From 28cd8da44dca3b45e6fa3def54603d1db33bdefc Mon Sep 17 00:00:00 2001 From: jeffrey Date: Tue, 17 Nov 2015 10:30:14 +0800 Subject: [PATCH] first commit --- component/common/api/at_cmd/atcmd_google.c | 193 + component/common/api/at_cmd/atcmd_sys.c | 512 + component/common/api/at_cmd/atcmd_sys.h | 5 + component/common/api/at_cmd/atcmd_wifi.c | 979 + component/common/api/at_cmd/atcmd_wifi.h | 67 + component/common/api/at_cmd/log_service.c | 323 + component/common/api/at_cmd/log_service.h | 95 + component/common/api/lwip_netconf.c | 354 + component/common/api/lwip_netconf.h | 74 + .../common/api/network/include/lwipopts.h | 236 + component/common/api/network/include/main.h | 68 + .../common/api/network/include/main_test.h | 21 + .../common/api/network/include/netconf.h | 50 + .../common/api/network/include/rtl8195a_it.h | 8 + component/common/api/network/include/util.h | 47 + .../common/api/network/include/wificonf.h | 88 + component/common/api/network/src/ping_test.c | 136 + .../common/api/network/src/rtl8195a_it.c | 110 + .../common/api/network/src/wlan_network.c | 65 + component/common/api/platform/dlist.h | 250 + .../common/api/platform/platform_stdlib.h | 145 + component/common/api/platform/stdlib_patch.c | 846 + .../wifi/rtw_wpa_supplicant/src/utils/os.h | 578 + .../src/utils/os_freertos.c | 100 + .../src/utils/rom/rom_wps_os.h | 24 + .../rtw_wpa_supplicant/src/wps/wps_defs.h | 319 + .../wpa_supplicant/wifi_wps_config.c | 528 + component/common/api/wifi/wifi_conf.c | 1609 + component/common/api/wifi/wifi_conf.h | 612 + component/common/api/wifi/wifi_ind.c | 236 + component/common/api/wifi/wifi_ind.h | 52 + component/common/api/wifi/wifi_promisc.c | 333 + .../common/api/wifi/wifi_simple_config.c | 602 + component/common/api/wifi/wifi_util.c | 933 + component/common/api/wifi/wifi_util.h | 64 + component/common/api/wifi_interactive_ext.h | 41 + component/common/api/wifi_interactive_mode.c | 1237 + .../common/application/google/google_nest.h | 23 + .../application/uart_adapter/uart_adapter.c | 1549 + .../application/uart_adapter/uart_adapter.h | 166 + .../drivers/wlan/realtek/include/autoconf.h | 347 + .../drivers/wlan/realtek/include/drv_conf.h | 106 + .../wlan/realtek/include/wifi_constants.h | 221 + .../wlan/realtek/include/wifi_structures.h | 134 + .../src/osdep/freertos/freertos_service.h | 285 + .../wlan/realtek/src/osdep/freertos/wrapper.h | 432 + .../wlan/realtek/src/osdep/lwip_intf.c | 222 + .../wlan/realtek/src/osdep/lwip_intf.h | 56 + .../wlan/realtek/src/osdep/osdep_service.h | 523 + .../drivers/wlan/realtek/src/osdep/skbuff.h | 54 + .../drivers/wlan/realtek/src/osdep/wireless.h | 1206 + .../wlan/realtek/src/osdep/wlan_intf.h | 66 + .../common/example/cJSON/cJSON_example.c | 84 + component/common/example/example_entry.c | 59 + component/common/example/example_entry.h | 8 + .../common/example/googlenest/example.html | 38 + .../example/googlenest/example_google.c | 185 + .../example/googlenest/example_google.h | 10 + .../common/example/mcast/example_mcast.c | 98 + .../common/example/mcast/example_mcast.h | 6 + component/common/example/mcast/readme.txt | 17 + component/common/example/mdns/example_mdns.c | 55 + component/common/example/mdns/example_mdns.h | 6 + .../example_wlan_fast_connect.c | 125 + .../example_wlan_fast_connect.h | 35 + component/common/mbed/api/error.h | 66 + component/common/mbed/api/mbed_assert.h | 50 + component/common/mbed/api/wait_api.h | 66 + component/common/mbed/common/.gitkeep | 0 component/common/mbed/common/Makefile | 52 + component/common/mbed/common/us_ticker_api.c | 118 + component/common/mbed/common/wait_api.c | 30 + component/common/mbed/hal/analogin_api.h | 39 + component/common/mbed/hal/analogout_api.h | 42 + component/common/mbed/hal/can_api.h | 80 + component/common/mbed/hal/ethernet_api.h | 63 + component/common/mbed/hal/gpio_api.h | 51 + component/common/mbed/hal/gpio_irq_api.h | 47 + component/common/mbed/hal/i2c_api.h | 58 + component/common/mbed/hal/pinmap.h | 43 + component/common/mbed/hal/port_api.h | 42 + component/common/mbed/hal/pwmout_api.h | 49 + component/common/mbed/hal/rtc_api.h | 42 + component/common/mbed/hal/serial_api.h | 78 + component/common/mbed/hal/sleep_api.h | 64 + component/common/mbed/hal/spi_api.h | 73 + component/common/mbed/hal/us_ticker_api.h | 51 + component/common/mbed/hal_ext/dma_api.h | 42 + component/common/mbed/hal_ext/flash_api.h | 45 + component/common/mbed/hal_ext/i2s_api.h | 76 + component/common/mbed/hal_ext/nfc_api.h | 70 + component/common/mbed/hal_ext/serial_ex_api.h | 42 + component/common/mbed/hal_ext/sleep_ex_api.h | 97 + component/common/mbed/hal_ext/spi_ex_api.h | 58 + component/common/mbed/hal_ext/sys_api.h | 50 + component/common/mbed/hal_ext/wdt_api.h | 61 + component/common/mbed/targets/cmsis/rtl8195a | 14 + .../targets/hal/rtl8195a/PeripheralNames.h | 94 + .../mbed/targets/hal/rtl8195a/PinNames.h | 212 + .../mbed/targets/hal/rtl8195a/PortNames.h | 38 + .../mbed/targets/hal/rtl8195a/analogin_api.c | 160 + .../common/mbed/targets/hal/rtl8195a/device.h | 48 + .../mbed/targets/hal/rtl8195a/dma_api.c | 77 + .../mbed/targets/hal/rtl8195a/flash_api.c | 321 + .../mbed/targets/hal/rtl8195a/gpio_api.c | 114 + .../mbed/targets/hal/rtl8195a/gpio_irq_api.c | 73 + .../mbed/targets/hal/rtl8195a/gpio_object.h | 39 + .../mbed/targets/hal/rtl8195a/i2c_api.c | 593 + .../mbed/targets/hal/rtl8195a/i2s_api.c | 244 + .../mbed/targets/hal/rtl8195a/nfc_api.c | 243 + .../mbed/targets/hal/rtl8195a/objects.h | 174 + .../common/mbed/targets/hal/rtl8195a/pinmap.c | 34 + .../mbed/targets/hal/rtl8195a/pinmap_common.c | 73 + .../mbed/targets/hal/rtl8195a/port_api.c | 150 + .../mbed/targets/hal/rtl8195a/pwmout_api.c | 136 + .../mbed/targets/hal/rtl8195a/rtc_api.c | 115 + .../mbed/targets/hal/rtl8195a/serial_api.c | 536 + .../common/mbed/targets/hal/rtl8195a/sleep.c | 245 + .../mbed/targets/hal/rtl8195a/spi_api.c | 681 + .../mbed/targets/hal/rtl8195a/sys_api.c | 180 + .../mbed/targets/hal/rtl8195a/timer_api.c | 135 + .../mbed/targets/hal/rtl8195a/timer_api.h | 38 + .../mbed/targets/hal/rtl8195a/us_ticker.c | 103 + .../mbed/targets/hal/rtl8195a/wdt_api.c | 94 + component/common/network/dhcp/dhcps.c | 618 + component/common/network/dhcp/dhcps.h | 123 + .../common/network/lwip/lwip_v1.3.2/CHANGELOG | 2419 + .../common/network/lwip/lwip_v1.3.2/COPYING | 33 + .../common/network/lwip/lwip_v1.3.2/FILES | 4 + .../common/network/lwip/lwip_v1.3.2/README | 89 + .../common/network/lwip/lwip_v1.3.2/doc/FILES | 6 + .../network/lwip/lwip_v1.3.2/doc/contrib.txt | 63 + .../network/lwip/lwip_v1.3.2/doc/rawapi.txt | 478 + .../network/lwip/lwip_v1.3.2/doc/savannah.txt | 135 + .../lwip/lwip_v1.3.2/doc/snmp_agent.txt | 181 + .../network/lwip/lwip_v1.3.2/doc/sys_arch.txt | 228 + .../lwip_v1.3.2/port/realtek/arch/bpstruct.h | 36 + .../lwip/lwip_v1.3.2/port/realtek/arch/cc.h | 100 + .../lwip/lwip_v1.3.2/port/realtek/arch/cpu.h | 37 + .../lwip_v1.3.2/port/realtek/arch/epstruct.h | 36 + .../lwip/lwip_v1.3.2/port/realtek/arch/init.h | 44 + .../lwip/lwip_v1.3.2/port/realtek/arch/lib.h | 38 + .../lwip/lwip_v1.3.2/port/realtek/arch/perf.h | 38 + .../lwip_v1.3.2/port/realtek/arch/sys_arch.h | 66 + .../port/realtek/freertos/ethernetif.c | 274 + .../port/realtek/freertos/ethernetif.h | 13 + .../port/realtek/freertos/sys_arch.c | 446 + .../port/realtek/freertos/sys_arch.h | 66 + .../port/stm32f2x7/arch/bpstruct.h | 36 + .../lwip/lwip_v1.3.2/port/stm32f2x7/arch/cc.h | 93 + .../lwip_v1.3.2/port/stm32f2x7/arch/cpu.h | 37 + .../port/stm32f2x7/arch/epstruct.h | 36 + .../lwip_v1.3.2/port/stm32f2x7/arch/init.h | 44 + .../lwip_v1.3.2/port/stm32f2x7/arch/lib.h | 38 + .../lwip_v1.3.2/port/stm32f2x7/arch/perf.h | 38 + .../port/stm32f2x7/arch/sys_arch.h | 66 + .../port/stm32f2x7/freertos/ethernetif.c | 649 + .../port/stm32f2x7/freertos/ethernetif.h | 13 + .../port/stm32f2x7/freertos/sys_arch.c | 446 + .../port/stm32f2x7/freertos/sys_arch.h | 66 + .../port/stm32f2x7/standalone/ethernetif.c | 304 + .../port/stm32f2x7/standalone/ethernetif.h | 11 + .../lwip/lwip_v1.3.2/src/api/api_lib.c | 568 + .../lwip/lwip_v1.3.2/src/api/api_msg.c | 1243 + .../network/lwip/lwip_v1.3.2/src/api/err.c | 74 + .../network/lwip/lwip_v1.3.2/src/api/netbuf.c | 240 + .../network/lwip/lwip_v1.3.2/src/api/netdb.c | 346 + .../lwip/lwip_v1.3.2/src/api/netifapi.c | 162 + .../lwip/lwip_v1.3.2/src/api/sockets.c | 1971 + .../network/lwip/lwip_v1.3.2/src/api/tcpip.c | 596 + .../network/lwip/lwip_v1.3.2/src/core/dhcp.c | 1778 + .../network/lwip/lwip_v1.3.2/src/core/dns.c | 980 + .../network/lwip/lwip_v1.3.2/src/core/init.c | 274 + .../lwip/lwip_v1.3.2/src/core/ipv4/autoip.c | 497 + .../lwip/lwip_v1.3.2/src/core/ipv4/icmp.c | 340 + .../lwip/lwip_v1.3.2/src/core/ipv4/igmp.c | 757 + .../lwip/lwip_v1.3.2/src/core/ipv4/inet.c | 278 + .../lwip_v1.3.2/src/core/ipv4/inet_chksum.c | 438 + .../lwip/lwip_v1.3.2/src/core/ipv4/ip.c | 768 + .../lwip/lwip_v1.3.2/src/core/ipv4/ip_addr.c | 84 + .../lwip/lwip_v1.3.2/src/core/ipv4/ip_frag.c | 792 + .../lwip/lwip_v1.3.2/src/core/ipv6/README | 1 + .../lwip/lwip_v1.3.2/src/core/ipv6/icmp6.c | 179 + .../lwip/lwip_v1.3.2/src/core/ipv6/inet6.c | 163 + .../lwip/lwip_v1.3.2/src/core/ipv6/ip6.c | 397 + .../lwip/lwip_v1.3.2/src/core/ipv6/ip6_addr.c | 72 + .../network/lwip/lwip_v1.3.2/src/core/mem.c | 633 + .../network/lwip/lwip_v1.3.2/src/core/memp.c | 386 + .../network/lwip/lwip_v1.3.2/src/core/netif.c | 687 + .../network/lwip/lwip_v1.3.2/src/core/pbuf.c | 929 + .../network/lwip/lwip_v1.3.2/src/core/raw.c | 353 + .../lwip/lwip_v1.3.2/src/core/snmp/asn1_dec.c | 657 + .../lwip/lwip_v1.3.2/src/core/snmp/asn1_enc.c | 611 + .../lwip/lwip_v1.3.2/src/core/snmp/mib2.c | 4128 + .../lwip_v1.3.2/src/core/snmp/mib_structs.c | 1183 + .../lwip/lwip_v1.3.2/src/core/snmp/msg_in.c | 1454 + .../lwip/lwip_v1.3.2/src/core/snmp/msg_out.c | 683 + .../network/lwip/lwip_v1.3.2/src/core/stats.c | 149 + .../network/lwip/lwip_v1.3.2/src/core/sys.c | 346 + .../network/lwip/lwip_v1.3.2/src/core/tcp.c | 1461 + .../lwip/lwip_v1.3.2/src/core/tcp_in.c | 1506 + .../lwip/lwip_v1.3.2/src/core/tcp_out.c | 1054 + .../network/lwip/lwip_v1.3.2/src/core/udp.c | 848 + .../src/include/ipv4/lwip/autoip.h | 116 + .../lwip_v1.3.2/src/include/ipv4/lwip/icmp.h | 111 + .../lwip_v1.3.2/src/include/ipv4/lwip/igmp.h | 162 + .../lwip_v1.3.2/src/include/ipv4/lwip/inet.h | 103 + .../src/include/ipv4/lwip/inet_chksum.h | 60 + .../lwip_v1.3.2/src/include/ipv4/lwip/ip.h | 198 + .../src/include/ipv4/lwip/ip_addr.h | 173 + .../src/include/ipv4/lwip/ip_frag.h | 76 + .../lwip_v1.3.2/src/include/ipv6/lwip/icmp.h | 100 + .../lwip_v1.3.2/src/include/ipv6/lwip/inet.h | 68 + .../lwip_v1.3.2/src/include/ipv6/lwip/ip.h | 130 + .../src/include/ipv6/lwip/ip_addr.h | 97 + .../lwip/lwip_v1.3.2/src/include/lwip/api.h | 229 + .../lwip_v1.3.2/src/include/lwip/api_msg.h | 162 + .../lwip/lwip_v1.3.2/src/include/lwip/arch.h | 233 + .../lwip/lwip_v1.3.2/src/include/lwip/debug.h | 102 + .../lwip/lwip_v1.3.2/src/include/lwip/def.h | 47 + .../lwip/lwip_v1.3.2/src/include/lwip/dhcp.h | 247 + .../lwip/lwip_v1.3.2/src/include/lwip/dns.h | 97 + .../lwip/lwip_v1.3.2/src/include/lwip/err.h | 87 + .../lwip/lwip_v1.3.2/src/include/lwip/init.h | 72 + .../lwip/lwip_v1.3.2/src/include/lwip/mem.h | 107 + .../lwip/lwip_v1.3.2/src/include/lwip/memp.h | 116 + .../lwip_v1.3.2/src/include/lwip/memp_std.h | 102 + .../lwip_v1.3.2/src/include/lwip/netbuf.h | 86 + .../lwip/lwip_v1.3.2/src/include/lwip/netdb.h | 111 + .../lwip/lwip_v1.3.2/src/include/lwip/netif.h | 269 + .../lwip_v1.3.2/src/include/lwip/netifapi.h | 105 + .../lwip/lwip_v1.3.2/src/include/lwip/opt.h | 1841 + .../lwip/lwip_v1.3.2/src/include/lwip/pbuf.h | 120 + .../lwip/lwip_v1.3.2/src/include/lwip/raw.h | 97 + .../lwip/lwip_v1.3.2/src/include/lwip/sio.h | 141 + .../lwip/lwip_v1.3.2/src/include/lwip/snmp.h | 364 + .../lwip_v1.3.2/src/include/lwip/snmp_asn1.h | 101 + .../lwip_v1.3.2/src/include/lwip/snmp_msg.h | 311 + .../src/include/lwip/snmp_structs.h | 262 + .../lwip_v1.3.2/src/include/lwip/sockets.h | 357 + .../lwip/lwip_v1.3.2/src/include/lwip/stats.h | 283 + .../lwip/lwip_v1.3.2/src/include/lwip/sys.h | 243 + .../lwip/lwip_v1.3.2/src/include/lwip/tcp.h | 707 + .../lwip/lwip_v1.3.2/src/include/lwip/tcpip.h | 141 + .../lwip/lwip_v1.3.2/src/include/lwip/udp.h | 153 + .../lwip_v1.3.2/src/include/netif/etharp.h | 192 + .../lwip_v1.3.2/src/include/netif/loopif.h | 53 + .../lwip_v1.3.2/src/include/netif/ppp_oe.h | 161 + .../lwip_v1.3.2/src/include/netif/slipif.h | 51 + .../network/lwip/lwip_v1.3.2/src/netif/FILES | 29 + .../lwip/lwip_v1.3.2/src/netif/etharp.c | 1235 + .../lwip/lwip_v1.3.2/src/netif/loopif.c | 66 + .../lwip/lwip_v1.3.2/src/netif/ppp/auth.c | 990 + .../lwip/lwip_v1.3.2/src/netif/ppp/auth.h | 111 + .../lwip/lwip_v1.3.2/src/netif/ppp/chap.c | 903 + .../lwip/lwip_v1.3.2/src/netif/ppp/chap.h | 166 + .../lwip/lwip_v1.3.2/src/netif/ppp/chpms.c | 399 + .../lwip/lwip_v1.3.2/src/netif/ppp/chpms.h | 64 + .../lwip/lwip_v1.3.2/src/netif/ppp/fsm.c | 908 + .../lwip/lwip_v1.3.2/src/netif/ppp/fsm.h | 169 + .../lwip/lwip_v1.3.2/src/netif/ppp/ipcp.c | 1427 + .../lwip/lwip_v1.3.2/src/netif/ppp/ipcp.h | 124 + .../lwip/lwip_v1.3.2/src/netif/ppp/lcp.c | 2035 + .../lwip/lwip_v1.3.2/src/netif/ppp/lcp.h | 167 + .../lwip/lwip_v1.3.2/src/netif/ppp/magic.c | 82 + .../lwip/lwip_v1.3.2/src/netif/ppp/magic.h | 67 + .../lwip/lwip_v1.3.2/src/netif/ppp/md5.c | 320 + .../lwip/lwip_v1.3.2/src/netif/ppp/md5.h | 55 + .../lwip/lwip_v1.3.2/src/netif/ppp/pap.c | 622 + .../lwip/lwip_v1.3.2/src/netif/ppp/pap.h | 131 + .../lwip/lwip_v1.3.2/src/netif/ppp/ppp.c | 1989 + .../lwip/lwip_v1.3.2/src/netif/ppp/ppp.h | 465 + .../lwip/lwip_v1.3.2/src/netif/ppp/ppp_oe.c | 1227 + .../lwip/lwip_v1.3.2/src/netif/ppp/pppdebug.h | 86 + .../lwip/lwip_v1.3.2/src/netif/ppp/randm.c | 249 + .../lwip/lwip_v1.3.2/src/netif/ppp/randm.h | 81 + .../lwip/lwip_v1.3.2/src/netif/ppp/vj.c | 660 + .../lwip/lwip_v1.3.2/src/netif/ppp/vj.h | 155 + .../lwip/lwip_v1.3.2/src/netif/ppp/vjbsdhdr.h | 75 + .../lwip/lwip_v1.3.2/src/netif/slipif.c | 367 + .../common/network/lwip/lwip_v1.4.1/CHANGELOG | 3349 + .../common/network/lwip/lwip_v1.4.1/COPYING | 33 + .../common/network/lwip/lwip_v1.4.1/FILES | 4 + .../common/network/lwip/lwip_v1.4.1/README | 89 + .../common/network/lwip/lwip_v1.4.1/UPGRADING | 144 + .../common/network/lwip/lwip_v1.4.1/doc/FILES | 6 + .../network/lwip/lwip_v1.4.1/doc/contrib.txt | 63 + .../network/lwip/lwip_v1.4.1/doc/rawapi.txt | 511 + .../network/lwip/lwip_v1.4.1/doc/savannah.txt | 135 + .../lwip/lwip_v1.4.1/doc/snmp_agent.txt | 181 + .../network/lwip/lwip_v1.4.1/doc/sys_arch.txt | 267 + .../lwip_v1.4.1/port/realtek/arch/bpstruct.h | 36 + .../lwip/lwip_v1.4.1/port/realtek/arch/cc.h | 100 + .../lwip/lwip_v1.4.1/port/realtek/arch/cpu.h | 37 + .../lwip_v1.4.1/port/realtek/arch/epstruct.h | 36 + .../lwip/lwip_v1.4.1/port/realtek/arch/init.h | 44 + .../lwip/lwip_v1.4.1/port/realtek/arch/lib.h | 38 + .../lwip/lwip_v1.4.1/port/realtek/arch/perf.h | 38 + .../lwip_v1.4.1/port/realtek/arch/sys_arch.h | 67 + .../port/realtek/freertos/ethernetif.c | 284 + .../port/realtek/freertos/ethernetif.h | 13 + .../port/realtek/freertos/sys_arch.c | 563 + .../port/realtek/freertos/sys_arch.h | 66 + .../port/stm32f2x7/arch/bpstruct.h | 36 + .../lwip/lwip_v1.4.1/port/stm32f2x7/arch/cc.h | 93 + .../lwip_v1.4.1/port/stm32f2x7/arch/cpu.h | 37 + .../port/stm32f2x7/arch/epstruct.h | 36 + .../lwip_v1.4.1/port/stm32f2x7/arch/init.h | 44 + .../lwip_v1.4.1/port/stm32f2x7/arch/lib.h | 38 + .../lwip_v1.4.1/port/stm32f2x7/arch/perf.h | 38 + .../port/stm32f2x7/arch/sys_arch.h | 67 + .../port/stm32f2x7/freertos/MFC6A0B.tmp | 489 + .../port/stm32f2x7/freertos/ethernetif.c | 653 + .../port/stm32f2x7/freertos/ethernetif.h | 13 + .../port/stm32f2x7/freertos/sys_arch.c | 515 + .../port/stm32f2x7/freertos/sys_arch.h | 66 + .../port/stm32f2x7/standalone/ethernetif.c | 366 + .../port/stm32f2x7/standalone/ethernetif.h | 11 + .../common/network/lwip/lwip_v1.4.1/src/FILES | 13 + .../lwip/lwip_v1.4.1/src/api/api_lib.c | 792 + .../lwip/lwip_v1.4.1/src/api/api_msg.c | 1565 + .../network/lwip/lwip_v1.4.1/src/api/err.c | 75 + .../network/lwip/lwip_v1.4.1/src/api/netbuf.c | 245 + .../network/lwip/lwip_v1.4.1/src/api/netdb.c | 353 + .../lwip/lwip_v1.4.1/src/api/netifapi.c | 160 + .../lwip/lwip_v1.4.1/src/api/sockets.c | 2444 + .../network/lwip/lwip_v1.4.1/src/api/tcpip.c | 511 + .../network/lwip/lwip_v1.4.1/src/core/def.c | 108 + .../network/lwip/lwip_v1.4.1/src/core/dhcp.c | 1827 + .../network/lwip/lwip_v1.4.1/src/core/dns.c | 970 + .../network/lwip/lwip_v1.4.1/src/core/init.c | 332 + .../lwip/lwip_v1.4.1/src/core/ipv4/autoip.c | 528 + .../lwip/lwip_v1.4.1/src/core/ipv4/icmp.c | 339 + .../lwip/lwip_v1.4.1/src/core/ipv4/igmp.c | 805 + .../lwip/lwip_v1.4.1/src/core/ipv4/inet.c | 42 + .../lwip_v1.4.1/src/core/ipv4/inet_chksum.c | 450 + .../lwip/lwip_v1.4.1/src/core/ipv4/ip.c | 924 + .../lwip/lwip_v1.4.1/src/core/ipv4/ip_addr.c | 312 + .../lwip/lwip_v1.4.1/src/core/ipv4/ip_frag.c | 863 + .../lwip/lwip_v1.4.1/src/core/ipv6/README | 1 + .../lwip/lwip_v1.4.1/src/core/ipv6/icmp6.c | 179 + .../lwip/lwip_v1.4.1/src/core/ipv6/inet6.c | 163 + .../lwip/lwip_v1.4.1/src/core/ipv6/ip6.c | 397 + .../lwip/lwip_v1.4.1/src/core/ipv6/ip6_addr.c | 72 + .../lwip/lwip_v1.4.1/src/core/lwip_timers.c | 487 + .../network/lwip/lwip_v1.4.1/src/core/mem.c | 659 + .../network/lwip/lwip_v1.4.1/src/core/memp.c | 470 + .../network/lwip/lwip_v1.4.1/src/core/netif.c | 774 + .../network/lwip/lwip_v1.4.1/src/core/pbuf.c | 1179 + .../network/lwip/lwip_v1.4.1/src/core/raw.c | 350 + .../lwip/lwip_v1.4.1/src/core/snmp/asn1_dec.c | 657 + .../lwip/lwip_v1.4.1/src/core/snmp/asn1_enc.c | 611 + .../lwip/lwip_v1.4.1/src/core/snmp/mib2.c | 4146 + .../lwip_v1.4.1/src/core/snmp/mib_structs.c | 1174 + .../lwip/lwip_v1.4.1/src/core/snmp/msg_in.c | 1453 + .../lwip/lwip_v1.4.1/src/core/snmp/msg_out.c | 674 + .../network/lwip/lwip_v1.4.1/src/core/stats.c | 176 + .../network/lwip/lwip_v1.4.1/src/core/sys.c | 68 + .../network/lwip/lwip_v1.4.1/src/core/tcp.c | 1742 + .../lwip/lwip_v1.4.1/src/core/tcp_in.c | 1619 + .../lwip/lwip_v1.4.1/src/core/tcp_out.c | 1485 + .../network/lwip/lwip_v1.4.1/src/core/udp.c | 1013 + .../src/include/ipv4/lwip/autoip.h | 118 + .../lwip_v1.4.1/src/include/ipv4/lwip/icmp.h | 111 + .../lwip_v1.4.1/src/include/ipv4/lwip/igmp.h | 106 + .../lwip_v1.4.1/src/include/ipv4/lwip/inet.h | 107 + .../src/include/ipv4/lwip/inet_chksum.h | 90 + .../lwip_v1.4.1/src/include/ipv4/lwip/ip.h | 223 + .../src/include/ipv4/lwip/ip_addr.h | 244 + .../src/include/ipv4/lwip/ip_frag.h | 88 + .../lwip_v1.4.1/src/include/ipv6/lwip/icmp.h | 100 + .../lwip_v1.4.1/src/include/ipv6/lwip/inet.h | 68 + .../lwip_v1.4.1/src/include/ipv6/lwip/ip.h | 130 + .../src/include/ipv6/lwip/ip_addr.h | 97 + .../lwip/lwip_v1.4.1/src/include/lwip/api.h | 305 + .../lwip_v1.4.1/src/include/lwip/api_msg.h | 177 + .../lwip/lwip_v1.4.1/src/include/lwip/arch.h | 217 + .../lwip/lwip_v1.4.1/src/include/lwip/debug.h | 102 + .../lwip/lwip_v1.4.1/src/include/lwip/def.h | 123 + .../lwip/lwip_v1.4.1/src/include/lwip/dhcp.h | 243 + .../lwip/lwip_v1.4.1/src/include/lwip/dns.h | 124 + .../lwip/lwip_v1.4.1/src/include/lwip/err.h | 85 + .../lwip/lwip_v1.4.1/src/include/lwip/init.h | 72 + .../src/include/lwip/lwip_timers.h | 108 + .../lwip/lwip_v1.4.1/src/include/lwip/mem.h | 123 + .../lwip/lwip_v1.4.1/src/include/lwip/memp.h | 116 + .../lwip_v1.4.1/src/include/lwip/memp_std.h | 122 + .../lwip_v1.4.1/src/include/lwip/netbuf.h | 101 + .../lwip/lwip_v1.4.1/src/include/lwip/netdb.h | 124 + .../lwip/lwip_v1.4.1/src/include/lwip/netif.h | 328 + .../lwip_v1.4.1/src/include/lwip/netifapi.h | 108 + .../lwip/lwip_v1.4.1/src/include/lwip/opt.h | 2134 + .../lwip/lwip_v1.4.1/src/include/lwip/pbuf.h | 178 + .../lwip/lwip_v1.4.1/src/include/lwip/raw.h | 98 + .../lwip/lwip_v1.4.1/src/include/lwip/sio.h | 141 + .../lwip/lwip_v1.4.1/src/include/lwip/snmp.h | 367 + .../lwip_v1.4.1/src/include/lwip/snmp_asn1.h | 101 + .../lwip_v1.4.1/src/include/lwip/snmp_msg.h | 315 + .../src/include/lwip/snmp_structs.h | 268 + .../lwip_v1.4.1/src/include/lwip/sockets.h | 379 + .../lwip/lwip_v1.4.1/src/include/lwip/stats.h | 292 + .../lwip/lwip_v1.4.1/src/include/lwip/sys.h | 337 + .../lwip/lwip_v1.4.1/src/include/lwip/tcp.h | 383 + .../lwip_v1.4.1/src/include/lwip/tcp_impl.h | 493 + .../lwip/lwip_v1.4.1/src/include/lwip/tcpip.h | 167 + .../lwip/lwip_v1.4.1/src/include/lwip/udp.h | 171 + .../lwip_v1.4.1/src/include/netif/etharp.h | 222 + .../lwip_v1.4.1/src/include/netif/loopif.h | 0 .../lwip_v1.4.1/src/include/netif/ppp_oe.h | 190 + .../lwip_v1.4.1/src/include/netif/slipif.h | 81 + .../lwip_v1.4.1/src/include/posix/netdb.h | 33 + .../src/include/posix/sys/socket.h | 33 + .../network/lwip/lwip_v1.4.1/src/netif/FILES | 29 + .../lwip/lwip_v1.4.1/src/netif/etharp.c | 1399 + .../lwip/lwip_v1.4.1/src/netif/ethernetif.c | 317 + .../lwip/lwip_v1.4.1/src/netif/ppp/auth.c | 1334 + .../lwip/lwip_v1.4.1/src/netif/ppp/auth.h | 111 + .../lwip/lwip_v1.4.1/src/netif/ppp/chap.c | 908 + .../lwip/lwip_v1.4.1/src/netif/ppp/chap.h | 150 + .../lwip/lwip_v1.4.1/src/netif/ppp/chpms.c | 396 + .../lwip/lwip_v1.4.1/src/netif/ppp/chpms.h | 64 + .../lwip/lwip_v1.4.1/src/netif/ppp/fsm.c | 890 + .../lwip/lwip_v1.4.1/src/netif/ppp/fsm.h | 157 + .../lwip/lwip_v1.4.1/src/netif/ppp/ipcp.c | 1411 + .../lwip/lwip_v1.4.1/src/netif/ppp/ipcp.h | 106 + .../lwip/lwip_v1.4.1/src/netif/ppp/lcp.c | 2066 + .../lwip/lwip_v1.4.1/src/netif/ppp/lcp.h | 151 + .../lwip/lwip_v1.4.1/src/netif/ppp/magic.c | 80 + .../lwip/lwip_v1.4.1/src/netif/ppp/magic.h | 63 + .../lwip/lwip_v1.4.1/src/netif/ppp/md5.c | 320 + .../lwip/lwip_v1.4.1/src/netif/ppp/md5.h | 55 + .../lwip/lwip_v1.4.1/src/netif/ppp/pap.c | 628 + .../lwip/lwip_v1.4.1/src/netif/ppp/pap.h | 118 + .../lwip/lwip_v1.4.1/src/netif/ppp/ppp.c | 2045 + .../lwip/lwip_v1.4.1/src/netif/ppp/ppp.h | 201 + .../lwip/lwip_v1.4.1/src/netif/ppp/ppp_impl.h | 363 + .../lwip/lwip_v1.4.1/src/netif/ppp/ppp_oe.c | 1132 + .../lwip/lwip_v1.4.1/src/netif/ppp/pppdebug.h | 73 + .../lwip/lwip_v1.4.1/src/netif/ppp/randm.c | 249 + .../lwip/lwip_v1.4.1/src/netif/ppp/randm.h | 81 + .../lwip/lwip_v1.4.1/src/netif/ppp/vj.c | 652 + .../lwip/lwip_v1.4.1/src/netif/ppp/vj.h | 156 + .../lwip/lwip_v1.4.1/src/netif/slipif.c | 510 + .../network/lwip/lwip_v1.5.0.beta/CHANGELOG | 3733 + .../network/lwip/lwip_v1.5.0.beta/COPYING | 33 + .../network/lwip/lwip_v1.5.0.beta/FILES | 4 + .../network/lwip/lwip_v1.5.0.beta/README | 89 + .../network/lwip/lwip_v1.5.0.beta/UPGRADING | 144 + .../network/lwip/lwip_v1.5.0.beta/doc/FILES | 6 + .../lwip/lwip_v1.5.0.beta/doc/contrib.txt | 59 + .../lwip/lwip_v1.5.0.beta/doc/rawapi.txt | 511 + .../lwip/lwip_v1.5.0.beta/doc/savannah.txt | 135 + .../lwip/lwip_v1.5.0.beta/doc/snmp_agent.txt | 181 + .../lwip/lwip_v1.5.0.beta/doc/sys_arch.txt | 267 + .../port/realtek/arch/bpstruct.h | 36 + .../lwip_v1.5.0.beta/port/realtek/arch/cc.h | 88 + .../lwip_v1.5.0.beta/port/realtek/arch/cpu.h | 37 + .../port/realtek/arch/epstruct.h | 36 + .../lwip_v1.5.0.beta/port/realtek/arch/init.h | 44 + .../lwip_v1.5.0.beta/port/realtek/arch/lib.h | 38 + .../lwip_v1.5.0.beta/port/realtek/arch/perf.h | 38 + .../port/realtek/arch/sys_arch.h | 67 + .../port/realtek/freertos/ethernetif.c | 280 + .../port/realtek/freertos/ethernetif.h | 13 + .../port/realtek/freertos/sys_arch.c | 517 + .../port/realtek/freertos/sys_arch.h | 66 + .../port/stm32f2x7/arch/bpstruct.h | 36 + .../lwip_v1.5.0.beta/port/stm32f2x7/arch/cc.h | 98 + .../port/stm32f2x7/arch/cpu.h | 37 + .../port/stm32f2x7/arch/epstruct.h | 36 + .../port/stm32f2x7/arch/init.h | 44 + .../port/stm32f2x7/arch/lib.h | 38 + .../port/stm32f2x7/arch/perf.h | 38 + .../port/stm32f2x7/arch/sys_arch.h | 67 + .../port/stm32f2x7/freertos/MFC6A0B.tmp | 489 + .../port/stm32f2x7/freertos/ethernetif.c | 656 + .../port/stm32f2x7/freertos/ethernetif.h | 13 + .../port/stm32f2x7/freertos/sys_arch.c | 515 + .../port/stm32f2x7/freertos/sys_arch.h | 66 + .../port/stm32f2x7/standalone/ethernetif.c | 366 + .../port/stm32f2x7/standalone/ethernetif.h | 11 + .../network/lwip/lwip_v1.5.0.beta/src/FILES | 13 + .../lwip/lwip_v1.5.0.beta/src/api/api_lib.c | 903 + .../lwip/lwip_v1.5.0.beta/src/api/api_msg.c | 1657 + .../lwip/lwip_v1.5.0.beta/src/api/err.c | 75 + .../lwip/lwip_v1.5.0.beta/src/api/netbuf.c | 245 + .../lwip/lwip_v1.5.0.beta/src/api/netdb.c | 349 + .../lwip/lwip_v1.5.0.beta/src/api/netifapi.c | 204 + .../lwip/lwip_v1.5.0.beta/src/api/pppapi.c | 439 + .../lwip/lwip_v1.5.0.beta/src/api/sockets.c | 2480 + .../lwip/lwip_v1.5.0.beta/src/api/tcpip.c | 571 + .../lwip/lwip_v1.5.0.beta/src/core/def.c | 108 + .../lwip/lwip_v1.5.0.beta/src/core/dhcp.c | 1844 + .../lwip/lwip_v1.5.0.beta/src/core/dns.c | 1302 + .../lwip_v1.5.0.beta/src/core/inet_chksum.c | 545 + .../lwip/lwip_v1.5.0.beta/src/core/init.c | 349 + .../lwip_v1.5.0.beta/src/core/ipv4/autoip.c | 528 + .../lwip_v1.5.0.beta/src/core/ipv4/icmp.c | 339 + .../lwip_v1.5.0.beta/src/core/ipv4/igmp.c | 816 + .../lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4.c | 972 + .../lwip_v1.5.0.beta/src/core/ipv4/ip4_addr.c | 312 + .../lwip_v1.5.0.beta/src/core/ipv4/ip_frag.c | 878 + .../lwip_v1.5.0.beta/src/core/ipv6/README | 1 + .../lwip_v1.5.0.beta/src/core/ipv6/dhcp6.c | 50 + .../lwip_v1.5.0.beta/src/core/ipv6/ethip6.c | 193 + .../lwip_v1.5.0.beta/src/core/ipv6/icmp6.c | 342 + .../lwip_v1.5.0.beta/src/core/ipv6/inet6.c | 51 + .../lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6.c | 1052 + .../lwip_v1.5.0.beta/src/core/ipv6/ip6_addr.c | 251 + .../lwip_v1.5.0.beta/src/core/ipv6/ip6_frag.c | 716 + .../lwip_v1.5.0.beta/src/core/ipv6/mld6.c | 586 + .../lwip/lwip_v1.5.0.beta/src/core/ipv6/nd6.c | 1793 + .../lwip_v1.5.0.beta/src/core/lwip_timers.c | 562 + .../lwip/lwip_v1.5.0.beta/src/core/mem.c | 677 + .../lwip/lwip_v1.5.0.beta/src/core/memp.c | 489 + .../lwip/lwip_v1.5.0.beta/src/core/netif.c | 940 + .../lwip/lwip_v1.5.0.beta/src/core/pbuf.c | 1329 + .../lwip/lwip_v1.5.0.beta/src/core/raw.c | 436 + .../lwip_v1.5.0.beta/src/core/snmp/asn1_dec.c | 657 + .../lwip_v1.5.0.beta/src/core/snmp/asn1_enc.c | 611 + .../lwip_v1.5.0.beta/src/core/snmp/mib2.c | 4160 + .../src/core/snmp/mib_structs.c | 1175 + .../lwip_v1.5.0.beta/src/core/snmp/msg_in.c | 1463 + .../lwip_v1.5.0.beta/src/core/snmp/msg_out.c | 684 + .../lwip/lwip_v1.5.0.beta/src/core/stats.c | 181 + .../lwip/lwip_v1.5.0.beta/src/core/sys.c | 68 + .../lwip/lwip_v1.5.0.beta/src/core/tcp.c | 1880 + .../lwip/lwip_v1.5.0.beta/src/core/tcp_in.c | 1770 + .../lwip/lwip_v1.5.0.beta/src/core/tcp_out.c | 1574 + .../lwip/lwip_v1.5.0.beta/src/core/udp.c | 1164 + .../lwip_v1.5.0.beta/src/include/lwip/api.h | 364 + .../src/include/lwip/api_msg.h | 204 + .../lwip_v1.5.0.beta/src/include/lwip/arch.h | 233 + .../src/include/lwip/autoip.h | 121 + .../lwip_v1.5.0.beta/src/include/lwip/debug.h | 102 + .../lwip_v1.5.0.beta/src/include/lwip/def.h | 123 + .../lwip_v1.5.0.beta/src/include/lwip/dhcp.h | 242 + .../lwip_v1.5.0.beta/src/include/lwip/dhcp6.h | 58 + .../lwip_v1.5.0.beta/src/include/lwip/dns.h | 117 + .../lwip_v1.5.0.beta/src/include/lwip/err.h | 87 + .../src/include/lwip/ethip6.h | 68 + .../lwip_v1.5.0.beta/src/include/lwip/icmp.h | 125 + .../lwip_v1.5.0.beta/src/include/lwip/icmp6.h | 152 + .../lwip_v1.5.0.beta/src/include/lwip/igmp.h | 106 + .../lwip_v1.5.0.beta/src/include/lwip/inet.h | 121 + .../lwip_v1.5.0.beta/src/include/lwip/inet6.h | 92 + .../src/include/lwip/inet_chksum.h | 112 + .../lwip_v1.5.0.beta/src/include/lwip/init.h | 72 + .../lwip_v1.5.0.beta/src/include/lwip/ip4.h | 150 + .../src/include/lwip/ip4_addr.h | 247 + .../lwip_v1.5.0.beta/src/include/lwip/ip6.h | 198 + .../src/include/lwip/ip6_addr.h | 289 + .../src/include/lwip/ip6_frag.h | 102 + .../src/include/lwip/ip_addr.h | 130 + .../src/include/lwip/ip_frag.h | 91 + .../src/include/lwip/lwip_ip.h | 259 + .../src/include/lwip/lwip_timers.h | 108 + .../lwip_v1.5.0.beta/src/include/lwip/mem.h | 123 + .../lwip_v1.5.0.beta/src/include/lwip/memp.h | 119 + .../src/include/lwip/memp_std.h | 154 + .../lwip_v1.5.0.beta/src/include/lwip/mld6.h | 118 + .../lwip_v1.5.0.beta/src/include/lwip/nd6.h | 362 + .../src/include/lwip/netbuf.h | 123 + .../lwip_v1.5.0.beta/src/include/lwip/netdb.h | 127 + .../lwip_v1.5.0.beta/src/include/lwip/netif.h | 401 + .../src/include/lwip/netifapi.h | 117 + .../lwip_v1.5.0.beta/src/include/lwip/opt.h | 2773 + .../lwip_v1.5.0.beta/src/include/lwip/pbuf.h | 193 + .../src/include/lwip/pppapi.h | 151 + .../lwip_v1.5.0.beta/src/include/lwip/raw.h | 136 + .../lwip_v1.5.0.beta/src/include/lwip/sio.h | 141 + .../lwip_v1.5.0.beta/src/include/lwip/snmp.h | 367 + .../src/include/lwip/snmp_asn1.h | 101 + .../src/include/lwip/snmp_msg.h | 315 + .../src/include/lwip/snmp_structs.h | 268 + .../src/include/lwip/sockets.h | 482 + .../lwip_v1.5.0.beta/src/include/lwip/stats.h | 347 + .../lwip_v1.5.0.beta/src/include/lwip/sys.h | 334 + .../lwip_v1.5.0.beta/src/include/lwip/tcp.h | 421 + .../src/include/lwip/tcp_impl.h | 538 + .../lwip_v1.5.0.beta/src/include/lwip/tcpip.h | 232 + .../lwip_v1.5.0.beta/src/include/lwip/udp.h | 221 + .../src/include/netif/etharp.h | 229 + .../src/include/netif/loopif.h | 0 .../src/include/netif/ppp/ccp.h | 57 + .../src/include/netif/ppp/chap-md5.h | 36 + .../src/include/netif/ppp/chap-new.h | 193 + .../src/include/netif/ppp/chap_ms.h | 115 + .../src/include/netif/ppp/eap.h | 168 + .../src/include/netif/ppp/ecp.h | 50 + .../src/include/netif/ppp/eui64.h | 94 + .../src/include/netif/ppp/fsm.h | 175 + .../src/include/netif/ppp/ipcp.h | 107 + .../src/include/netif/ppp/ipv6cp.h | 177 + .../src/include/netif/ppp/lcp.h | 176 + .../src/include/netif/ppp/magic.h | 119 + .../src/include/netif/ppp/polarssl/des.h | 92 + .../src/include/netif/ppp/polarssl/md4.h | 97 + .../src/include/netif/ppp/polarssl/md5.h | 96 + .../src/include/netif/ppp/polarssl/sha1.h | 96 + .../src/include/netif/ppp/ppp.h | 644 + .../src/include/netif/ppp/ppp_impl.h | 581 + .../src/include/netif/ppp/pppcrypt.h | 43 + .../src/include/netif/ppp/pppdebug.h | 80 + .../src/include/netif/ppp/pppoe.h | 183 + .../src/include/netif/ppp/pppol2tp.h | 217 + .../src/include/netif/ppp/upap.h | 123 + .../src/include/netif/ppp/vj.h | 161 + .../src/include/netif/slipif.h | 81 + .../src/include/posix/netdb.h | 33 + .../src/include/posix/sys/socket.h | 33 + .../lwip/lwip_v1.5.0.beta/src/netif/FILES | 29 + .../lwip/lwip_v1.5.0.beta/src/netif/etharp.c | 1484 + .../lwip_v1.5.0.beta/src/netif/ethernetif.c | 322 + .../src/netif/ppp/PPPD_FOLLOWUP | 382 + .../lwip_v1.5.0.beta/src/netif/ppp/auth.c | 2563 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/ccp.c | 1694 + .../lwip_v1.5.0.beta/src/netif/ppp/chap-md5.c | 124 + .../lwip_v1.5.0.beta/src/netif/ppp/chap-new.c | 670 + .../lwip_v1.5.0.beta/src/netif/ppp/chap_ms.c | 913 + .../lwip_v1.5.0.beta/src/netif/ppp/demand.c | 467 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/eap.c | 2455 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/ecp.c | 188 + .../lwip_v1.5.0.beta/src/netif/ppp/eui64.c | 56 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/fsm.c | 798 + .../lwip_v1.5.0.beta/src/netif/ppp/ipcp.c | 2274 + .../lwip_v1.5.0.beta/src/netif/ppp/ipv6cp.c | 1498 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/lcp.c | 2732 + .../lwip_v1.5.0.beta/src/netif/ppp/magic.c | 267 + .../src/netif/ppp/multilink.c | 609 + .../src/netif/ppp/polarssl/README | 34 + .../src/netif/ppp/polarssl/des.c | 422 + .../src/netif/ppp/polarssl/md4.c | 279 + .../src/netif/ppp/polarssl/md5.c | 298 + .../src/netif/ppp/polarssl/sha1.c | 333 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/ppp.c | 2606 + .../lwip_v1.5.0.beta/src/netif/ppp/pppcrypt.c | 66 + .../lwip_v1.5.0.beta/src/netif/ppp/pppoe.c | 1113 + .../lwip_v1.5.0.beta/src/netif/ppp/pppol2tp.c | 1016 + .../lwip_v1.5.0.beta/src/netif/ppp/upap.c | 682 + .../lwip_v1.5.0.beta/src/netif/ppp/utils.c | 964 + .../lwip/lwip_v1.5.0.beta/src/netif/ppp/vj.c | 657 + .../lwip/lwip_v1.5.0.beta/src/netif/slipif.c | 546 + component/common/network/mdns/mDNS.h | 26 + component/common/network/mdns/mDNSPlatform.c | 37 + .../ssl/polarssl-1.3.8/include/polarssl/aes.h | 264 + .../polarssl-1.3.8/include/polarssl/aesni.h | 107 + .../polarssl-1.3.8/include/polarssl/arc4.h | 117 + .../polarssl-1.3.8/include/polarssl/asn1.h | 347 + .../include/polarssl/asn1write.h | 243 + .../polarssl-1.3.8/include/polarssl/base64.h | 87 + .../polarssl-1.3.8/include/polarssl/bignum.h | 752 + .../include/polarssl/blowfish.h | 197 + .../polarssl-1.3.8/include/polarssl/bn_mul.h | 873 + .../include/polarssl/camellia.h | 229 + .../ssl/polarssl-1.3.8/include/polarssl/ccm.h | 134 + .../polarssl-1.3.8/include/polarssl/certs.h | 77 + .../include/polarssl/check_config.h | 326 + .../polarssl-1.3.8/include/polarssl/cipher.h | 760 + .../include/polarssl/cipher_wrap.h | 57 + .../include/polarssl/compat-1.2.h | 389 + .../polarssl-1.3.8/include/polarssl/config.h | 14 + .../include/polarssl/config_all.h | 2180 + .../include/polarssl/config_rsa.h | 2183 + .../include/polarssl/config_srp.h | 2194 + .../include/polarssl/ctr_drbg.h | 274 + .../polarssl-1.3.8/include/polarssl/debug.h | 152 + .../ssl/polarssl-1.3.8/include/polarssl/des.h | 300 + .../ssl/polarssl-1.3.8/include/polarssl/dhm.h | 311 + .../polarssl-1.3.8/include/polarssl/ecdh.h | 225 + .../polarssl-1.3.8/include/polarssl/ecdsa.h | 236 + .../ssl/polarssl-1.3.8/include/polarssl/ecp.h | 651 + .../polarssl-1.3.8/include/polarssl/entropy.h | 246 + .../include/polarssl/entropy_poll.h | 79 + .../polarssl-1.3.8/include/polarssl/error.h | 123 + .../ssl/polarssl-1.3.8/include/polarssl/gcm.h | 219 + .../polarssl-1.3.8/include/polarssl/havege.h | 78 + .../include/polarssl/hmac_drbg.h | 284 + .../ssl/polarssl-1.3.8/include/polarssl/md.h | 395 + .../ssl/polarssl-1.3.8/include/polarssl/md2.h | 194 + .../ssl/polarssl-1.3.8/include/polarssl/md4.h | 200 + .../ssl/polarssl-1.3.8/include/polarssl/md5.h | 200 + .../polarssl-1.3.8/include/polarssl/md_wrap.h | 71 + .../polarssl-1.3.8/include/polarssl/memory.h | 52 + .../include/polarssl/memory_buffer_alloc.h | 122 + .../ssl/polarssl-1.3.8/include/polarssl/net.h | 160 + .../ssl/polarssl-1.3.8/include/polarssl/oid.h | 570 + .../polarssl-1.3.8/include/polarssl/openssl.h | 140 + .../polarssl-1.3.8/include/polarssl/padlock.h | 109 + .../polarssl-1.3.8/include/polarssl/pbkdf2.h | 82 + .../ssl/polarssl-1.3.8/include/polarssl/pem.h | 133 + .../ssl/polarssl-1.3.8/include/polarssl/pk.h | 632 + .../polarssl-1.3.8/include/polarssl/pk_wrap.h | 63 + .../polarssl-1.3.8/include/polarssl/pkcs11.h | 174 + .../polarssl-1.3.8/include/polarssl/pkcs12.h | 123 + .../polarssl-1.3.8/include/polarssl/pkcs5.h | 104 + .../include/polarssl/platform.h | 128 + .../include/polarssl/ripemd160.h | 204 + .../ssl/polarssl-1.3.8/include/polarssl/rsa.h | 647 + .../polarssl-1.3.8/include/polarssl/sha1.h | 200 + .../polarssl-1.3.8/include/polarssl/sha256.h | 208 + .../polarssl-1.3.8/include/polarssl/sha512.h | 209 + .../ssl/polarssl-1.3.8/include/polarssl/ssl.h | 1809 + .../include/polarssl/ssl_cache.h | 147 + .../include/polarssl/ssl_ciphersuites.h | 293 + .../include/polarssl/threading.h | 86 + .../polarssl-1.3.8/include/polarssl/timing.h | 98 + .../polarssl-1.3.8/include/polarssl/version.h | 114 + .../polarssl-1.3.8/include/polarssl/x509.h | 317 + .../include/polarssl/x509_crl.h | 162 + .../include/polarssl/x509_crt.h | 558 + .../include/polarssl/x509_csr.h | 295 + .../polarssl-1.3.8/include/polarssl/xtea.h | 149 + .../ssl/polarssl-1.3.8/library/.gitignore | 2 + .../ssl/polarssl-1.3.8/library/CMakeLists.txt | 136 + .../ssl/polarssl-1.3.8/library/Makefile | 110 + .../network/ssl/polarssl-1.3.8/library/aes.c | 1554 + .../ssl/polarssl-1.3.8/library/aesni.c | 463 + .../network/ssl/polarssl-1.3.8/library/arc4.c | 208 + .../ssl/polarssl-1.3.8/library/asn1parse.c | 391 + .../ssl/polarssl-1.3.8/library/asn1write.c | 366 + .../ssl/polarssl-1.3.8/library/base64.c | 273 + .../ssl/polarssl-1.3.8/library/bignum.c | 2344 + .../ssl/polarssl-1.3.8/library/blowfish.c | 658 + .../ssl/polarssl-1.3.8/library/camellia.c | 1073 + .../network/ssl/polarssl-1.3.8/library/ccm.c | 456 + .../ssl/polarssl-1.3.8/library/certs.c | 310 + .../ssl/polarssl-1.3.8/library/cipher.c | 917 + .../ssl/polarssl-1.3.8/library/cipher_wrap.c | 1451 + .../ssl/polarssl-1.3.8/library/ctr_drbg.c | 549 + .../ssl/polarssl-1.3.8/library/debug.c | 351 + .../network/ssl/polarssl-1.3.8/library/des.c | 1156 + .../network/ssl/polarssl-1.3.8/library/dhm.c | 598 + .../network/ssl/polarssl-1.3.8/library/ecdh.c | 280 + .../ssl/polarssl-1.3.8/library/ecdsa.c | 503 + .../network/ssl/polarssl-1.3.8/library/ecp.c | 2029 + .../ssl/polarssl-1.3.8/library/ecp_curves.c | 1380 + .../ssl/polarssl-1.3.8/library/entropy.c | 477 + .../ssl/polarssl-1.3.8/library/entropy_poll.c | 140 + .../ssl/polarssl-1.3.8/library/error.c | 769 + .../network/ssl/polarssl-1.3.8/library/gcm.c | 948 + .../ssl/polarssl-1.3.8/library/havege.c | 247 + .../ssl/polarssl-1.3.8/library/hmac_drbg.c | 502 + .../network/ssl/polarssl-1.3.8/library/md.c | 341 + .../network/ssl/polarssl-1.3.8/library/md2.c | 398 + .../network/ssl/polarssl-1.3.8/library/md4.c | 494 + .../network/ssl/polarssl-1.3.8/library/md5.c | 615 + .../ssl/polarssl-1.3.8/library/md_wrap.c | 955 + .../library/memory_buffer_alloc.c | 589 + .../network/ssl/polarssl-1.3.8/library/net.c | 599 + .../network/ssl/polarssl-1.3.8/library/oid.c | 684 + .../ssl/polarssl-1.3.8/library/padlock.c | 168 + .../ssl/polarssl-1.3.8/library/pbkdf2.c | 64 + .../network/ssl/polarssl-1.3.8/library/pem.c | 445 + .../network/ssl/polarssl-1.3.8/library/pk.c | 351 + .../ssl/polarssl-1.3.8/library/pk_wrap.c | 452 + .../ssl/polarssl-1.3.8/library/pkcs11.c | 236 + .../ssl/polarssl-1.3.8/library/pkcs12.c | 360 + .../ssl/polarssl-1.3.8/library/pkcs5.c | 417 + .../ssl/polarssl-1.3.8/library/pkparse.c | 1256 + .../ssl/polarssl-1.3.8/library/pkwrite.c | 358 + .../ssl/polarssl-1.3.8/library/platform.c | 116 + .../ssl/polarssl-1.3.8/library/ripemd160.c | 653 + .../network/ssl/polarssl-1.3.8/library/rsa.c | 1657 + .../network/ssl/polarssl-1.3.8/library/sha1.c | 661 + .../ssl/polarssl-1.3.8/library/sha256.c | 742 + .../ssl/polarssl-1.3.8/library/sha512.c | 796 + .../ssl/polarssl-1.3.8/library/ssl_cache.c | 335 + .../polarssl-1.3.8/library/ssl_ciphersuites.c | 1843 + .../ssl/polarssl-1.3.8/library/ssl_cli.c | 2635 + .../ssl/polarssl-1.3.8/library/ssl_srv.c | 3269 + .../ssl/polarssl-1.3.8/library/ssl_tls.c | 4873 + .../ssl/polarssl-1.3.8/library/threading.c | 113 + .../ssl/polarssl-1.3.8/library/timing.c | 500 + .../ssl/polarssl-1.3.8/library/version.c | 56 + .../polarssl-1.3.8/library/version_features.c | 560 + .../network/ssl/polarssl-1.3.8/library/x509.c | 1102 + .../ssl/polarssl-1.3.8/library/x509_create.c | 318 + .../ssl/polarssl-1.3.8/library/x509_crl.c | 768 + .../ssl/polarssl-1.3.8/library/x509_crt.c | 2018 + .../ssl/polarssl-1.3.8/library/x509_csr.c | 465 + .../polarssl-1.3.8/library/x509write_crt.c | 452 + .../polarssl-1.3.8/library/x509write_csr.c | 260 + .../network/ssl/polarssl-1.3.8/library/xtea.c | 283 + component/common/test/wlan/wlan_test_inc.h | 23 + component/common/utilities/cJSON.c | 596 + component/common/utilities/cJSON.h | 145 + component/common/utilities/ssl_client.c | 283 + component/common/utilities/tcpecho.c | 121 + component/common/utilities/tcptest.c | 534 + component/common/utilities/udpecho.c | 92 + component/common/utilities/update.c | 971 + component/common/utilities/update.h | 10 + component/common/utilities/webserver.c | 1197 + component/common/utilities/webserver.h | 71 + component/os/freertos/cmsis_os.c | 1394 + component/os/freertos/cmsis_os.h | 816 + component/os/freertos/freertos_pmu.c | 96 + component/os/freertos/freertos_pmu.h | 25 + .../freertos_v8.1.2/Demo/Common/Full/BlockQ.c | 346 + .../freertos_v8.1.2/Demo/Common/Full/PollQ.c | 258 + .../Demo/Common/Full/comtest.c | 384 + .../freertos_v8.1.2/Demo/Common/Full/death.c | 241 + .../Demo/Common/Full/dynamic.c | 616 + .../freertos_v8.1.2/Demo/Common/Full/events.c | 406 + .../freertos_v8.1.2/Demo/Common/Full/flash.c | 166 + .../freertos_v8.1.2/Demo/Common/Full/flop.c | 369 + .../Demo/Common/Full/integer.c | 365 + .../freertos_v8.1.2/Demo/Common/Full/print.c | 144 + .../Demo/Common/Full/semtest.c | 323 + .../Demo/Common/Minimal/AltBlckQ.c | 332 + .../Demo/Common/Minimal/AltBlock.c | 549 + .../Demo/Common/Minimal/AltPollQ.c | 275 + .../Demo/Common/Minimal/AltQTest.c | 587 + .../Demo/Common/Minimal/BlockQ.c | 324 + .../Demo/Common/Minimal/EventGroupsDemo.c | 1074 + .../Demo/Common/Minimal/GenQTest.c | 857 + .../Demo/Common/Minimal/IntQueue.c | 760 + .../Demo/Common/Minimal/PollQ.c | 258 + .../Demo/Common/Minimal/QPeek.c | 474 + .../Demo/Common/Minimal/QueueOverwrite.c | 268 + .../Demo/Common/Minimal/QueueSet.c | 715 + .../Demo/Common/Minimal/TimerDemo.c | 1079 + .../Demo/Common/Minimal/blocktim.c | 505 + .../Demo/Common/Minimal/comtest.c | 303 + .../Demo/Common/Minimal/comtest_strings.c | 349 + .../Demo/Common/Minimal/countsem.c | 322 + .../Demo/Common/Minimal/crflash.c | 246 + .../Demo/Common/Minimal/crhook.c | 270 + .../Demo/Common/Minimal/death.c | 254 + .../Demo/Common/Minimal/dynamic.c | 511 + .../Demo/Common/Minimal/flash.c | 157 + .../Demo/Common/Minimal/flash_timer.c | 136 + .../Demo/Common/Minimal/flop.c | 383 + .../Demo/Common/Minimal/integer.c | 201 + .../Demo/Common/Minimal/recmutex.c | 440 + .../Demo/Common/Minimal/semtest.c | 298 + .../Demo/Common/Minimal/sp_flop.c | 365 + .../Demo/Common/include/AltBlckQ.h | 74 + .../Demo/Common/include/AltBlock.h | 74 + .../Demo/Common/include/AltPollQ.h | 74 + .../Demo/Common/include/AltQTest.h | 75 + .../Demo/Common/include/BlockQ.h | 74 + .../Demo/Common/include/EventGroupsDemo.h | 82 + .../Demo/Common/include/GenQTest.h | 76 + .../Demo/Common/include/IntQueue.h | 80 + .../Demo/Common/include/PollQ.h | 74 + .../Demo/Common/include/QPeek.h | 75 + .../Demo/Common/include/QueueOverwrite.h | 75 + .../Demo/Common/include/QueueSet.h | 75 + .../Demo/Common/include/TimerDemo.h | 76 + .../Demo/Common/include/blocktim.h | 74 + .../Demo/Common/include/comtest.h | 75 + .../Demo/Common/include/comtest2.h | 73 + .../Demo/Common/include/comtest_strings.h | 73 + .../Demo/Common/include/countsem.h | 73 + .../Demo/Common/include/crflash.h | 85 + .../Demo/Common/include/crhook.h | 81 + .../Demo/Common/include/death.h | 74 + .../Demo/Common/include/dynamic.h | 74 + .../Demo/Common/include/fileIO.h | 74 + .../Demo/Common/include/flash.h | 72 + .../Demo/Common/include/flash_timer.h | 79 + .../Demo/Common/include/flop.h | 74 + .../Demo/Common/include/integer.h | 74 + .../Demo/Common/include/mevents.h | 74 + .../Demo/Common/include/partest.h | 76 + .../Demo/Common/include/print.h | 75 + .../Demo/Common/include/recmutex.h | 73 + .../Demo/Common/include/semtest.h | 73 + .../Demo/Common/include/serial.h | 136 + .../freertos_v8.1.2/License/license.txt | 394 + .../freertos/freertos_v8.1.2/Source/Makefile | 54 + .../freertos_v8.1.2/Source/croutine.c | 386 + .../freertos_v8.1.2/Source/event_groups.c | 676 + .../freertos_v8.1.2/Source/include/FreeRTOS.h | 762 + .../Source/include/StackMacros.h | 180 + .../freertos_v8.1.2/Source/include/croutine.h | 758 + .../Source/include/event_groups.h | 726 + .../freertos_v8.1.2/Source/include/list.h | 403 + .../Source/include/mpu_wrappers.h | 153 + .../freertos_v8.1.2/Source/include/portable.h | 426 + .../freertos_v8.1.2/Source/include/projdefs.h | 94 + .../freertos_v8.1.2/Source/include/queue.h | 1687 + .../freertos_v8.1.2/Source/include/semphr.h | 840 + .../Source/include/stdint.readme | 27 + .../freertos_v8.1.2/Source/include/task.h | 1570 + .../freertos_v8.1.2/Source/include/timers.h | 1121 + .../os/freertos/freertos_v8.1.2/Source/list.c | 204 + .../Source/portable/GCC/ARM_CM3/Makefile | 32 + .../Source/portable/GCC/ARM_CM3/port.c | 745 + .../Source/portable/GCC/ARM_CM3/portmacro.h | 195 + .../Source/portable/GCC/ARM_CM3_MPU/port.c | 1246 + .../portable/GCC/ARM_CM3_MPU/portmacro.h | 218 + .../Source/portable/GCC/ARM_CM4F/port.c | 784 + .../Source/portable/GCC/ARM_CM4F/portmacro.h | 196 + .../Source/portable/IAR/ARM_CM3/port.c | 648 + .../Source/portable/IAR/ARM_CM3/portasm.s | 155 + .../Source/portable/IAR/ARM_CM3/portmacro.h | 208 + .../Source/portable/IAR/ARM_CM4F/port.c | 652 + .../Source/portable/IAR/ARM_CM4F/portasm.s | 195 + .../Source/portable/IAR/ARM_CM4F/portmacro.h | 193 + .../Source/portable/MemMang/Makefile | 32 + .../Source/portable/MemMang/heap_1.c | 170 + .../Source/portable/MemMang/heap_2.c | 299 + .../Source/portable/MemMang/heap_3.c | 131 + .../Source/portable/MemMang/heap_4.c | 489 + .../Source/portable/MemMang/heap_5.c | 567 + .../Source/portable/RVDS/ARM_CM3/port.c | 724 + .../Source/portable/RVDS/ARM_CM3/portmacro.h | 185 + .../Source/portable/RVDS/ARM_CM4F/port.c | 803 + .../Source/portable/RVDS/ARM_CM4F/portmacro.h | 186 + .../freertos/freertos_v8.1.2/Source/queue.c | 2439 + .../freertos_v8.1.2/Source/readme.txt | 17 + .../freertos/freertos_v8.1.2/Source/tasks.c | 3729 + .../freertos/freertos_v8.1.2/Source/timers.c | 885 + component/os/os_dep/include/mailbox.h | 127 + component/os/os_dep/include/os_support.h | 344 + component/os/os_dep/include/os_timer.h | 215 + component/os/os_dep/include/osdep_api.h | 561 + component/os/os_dep/mailbox.c | 574 + component/os/os_dep/osdep_api.c | 836 + component/soc/realtek/8195a/cmsis/core_cm3.h | 1661 + .../soc/realtek/8195a/cmsis/core_cmFunc.h | 636 + .../soc/realtek/8195a/cmsis/core_cmInstr.h | 688 + .../realtek/8195a/cmsis/device/app_start.c | 192 + .../soc/realtek/8195a/cmsis/device/cmsis.h | 38 + .../realtek/8195a/cmsis/device/cmsis_nvic.c | 55 + .../realtek/8195a/cmsis/device/cmsis_nvic.h | 54 + .../soc/realtek/8195a/cmsis/device/diag.h | 808 + .../soc/realtek/8195a/cmsis/device/rand.h | 15 + .../realtek/8195a/cmsis/device/rtl_stdlib.h | 41 + .../realtek/8195a/cmsis/device/rtl_utility.h | 70 + .../soc/realtek/8195a/cmsis/device/strproc.h | 104 + .../realtek/8195a/cmsis/device/system_8195a.c | 139 + .../realtek/8195a/cmsis/device/system_8195a.h | 77 + .../soc/realtek/8195a/cmsis/device/va_list.h | 37 + component/soc/realtek/8195a/fwlib/hal_adc.h | 317 + component/soc/realtek/8195a/fwlib/hal_api.h | 123 + .../soc/realtek/8195a/fwlib/hal_common.h | 17 + .../soc/realtek/8195a/fwlib/hal_crypto.h | 213 + component/soc/realtek/8195a/fwlib/hal_dac.h | 313 + component/soc/realtek/8195a/fwlib/hal_diag.h | 107 + component/soc/realtek/8195a/fwlib/hal_efuse.h | 28 + component/soc/realtek/8195a/fwlib/hal_gdma.h | 127 + component/soc/realtek/8195a/fwlib/hal_gpio.h | 236 + component/soc/realtek/8195a/fwlib/hal_i2c.h | 565 + component/soc/realtek/8195a/fwlib/hal_i2s.h | 333 + component/soc/realtek/8195a/fwlib/hal_irqn.h | 111 + component/soc/realtek/8195a/fwlib/hal_mii.h | 118 + component/soc/realtek/8195a/fwlib/hal_misc.h | 30 + component/soc/realtek/8195a/fwlib/hal_nfc.h | 22 + component/soc/realtek/8195a/fwlib/hal_pcm.h | 104 + .../soc/realtek/8195a/fwlib/hal_peri_on.h | 451 + .../soc/realtek/8195a/fwlib/hal_pinmux.h | 60 + .../soc/realtek/8195a/fwlib/hal_platform.h | 102 + component/soc/realtek/8195a/fwlib/hal_pwm.h | 57 + component/soc/realtek/8195a/fwlib/hal_sdio.h | 241 + .../realtek/8195a/fwlib/hal_sdr_controller.h | 188 + .../realtek/8195a/fwlib/hal_soc_ps_monitor.h | 254 + .../soc/realtek/8195a/fwlib/hal_spi_flash.h | 235 + component/soc/realtek/8195a/fwlib/hal_ssi.h | 303 + component/soc/realtek/8195a/fwlib/hal_timer.h | 57 + component/soc/realtek/8195a/fwlib/hal_uart.h | 145 + component/soc/realtek/8195a/fwlib/hal_util.h | 252 + .../realtek/8195a/fwlib/hal_vector_table.h | 53 + .../wlan_ram_map/rom/rom_wlan_ram_map.h | 9 + .../realtek/8195a/fwlib/rtl8195a/rtl8195a.h | 155 + .../8195a/fwlib/rtl8195a/rtl8195a_adc.h | 350 + .../8195a/fwlib/rtl8195a/rtl8195a_dac.h | 294 + .../8195a/fwlib/rtl8195a/rtl8195a_gdma.h | 486 + .../8195a/fwlib/rtl8195a/rtl8195a_gpio.h | 352 + .../8195a/fwlib/rtl8195a/rtl8195a_i2c.h | 844 + .../8195a/fwlib/rtl8195a/rtl8195a_i2s.h | 617 + .../8195a/fwlib/rtl8195a/rtl8195a_mii.h | 674 + .../8195a/fwlib/rtl8195a/rtl8195a_nfc.h | 153 + .../8195a/fwlib/rtl8195a/rtl8195a_pcm.h | 449 + .../8195a/fwlib/rtl8195a/rtl8195a_peri_on.h | 1251 + .../8195a/fwlib/rtl8195a/rtl8195a_pwm.h | 37 + .../8195a/fwlib/rtl8195a/rtl8195a_sdio.h | 946 + .../8195a/fwlib/rtl8195a/rtl8195a_sdio_host.h | 295 + .../8195a/fwlib/rtl8195a/rtl8195a_sdr.h | 379 + .../8195a/fwlib/rtl8195a/rtl8195a_spi_flash.h | 990 + .../8195a/fwlib/rtl8195a/rtl8195a_ssi.h | 495 + .../8195a/fwlib/rtl8195a/rtl8195a_sys_on.h | 1093 + .../8195a/fwlib/rtl8195a/rtl8195a_timer.h | 222 + .../8195a/fwlib/rtl8195a/rtl8195a_uart.h | 403 + .../8195a/fwlib/rtl8195a/rtl8195a_wdt.h | 86 + .../8195a/fwlib/rtl8195a/src/rtl8195a_adc.c | 380 + .../8195a/fwlib/rtl8195a/src/rtl8195a_dac.c | 269 + .../8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c | 243 + .../8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c | 53 + .../8195a/fwlib/rtl8195a/src/rtl8195a_i2s.c | 394 + .../8195a/fwlib/rtl8195a/src/rtl8195a_mii.c | 411 + .../8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c | 1919 + .../8195a/fwlib/rtl8195a/src/rtl8195a_pcm.c | 360 + .../8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c | 219 + .../fwlib/rtl8195a/src/rtl8195a_sdio_device.c | 2766 + .../8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c | 1188 + .../8195a/fwlib/rtl8195a/src/rtl8195a_timer.c | 317 + .../8195a/fwlib/rtl8195a/src/rtl8195a_uart.c | 399 + .../soc/realtek/8195a/fwlib/src/hal_32k.c | 293 + .../soc/realtek/8195a/fwlib/src/hal_adc.c | 1428 + .../soc/realtek/8195a/fwlib/src/hal_common.c | 23 + .../soc/realtek/8195a/fwlib/src/hal_dac.c | 1452 + .../soc/realtek/8195a/fwlib/src/hal_gdma.c | 358 + .../soc/realtek/8195a/fwlib/src/hal_gpio.c | 144 + .../soc/realtek/8195a/fwlib/src/hal_i2c.c | 2805 + .../soc/realtek/8195a/fwlib/src/hal_i2s.c | 415 + .../soc/realtek/8195a/fwlib/src/hal_mii.c | 43 + .../soc/realtek/8195a/fwlib/src/hal_nfc.c | 20 + .../soc/realtek/8195a/fwlib/src/hal_pcm.c | 28 + .../soc/realtek/8195a/fwlib/src/hal_pwm.c | 131 + .../8195a/fwlib/src/hal_sdr_controller.c | 962 + .../8195a/fwlib/src/hal_soc_ps_monitor.c | 2487 + .../soc/realtek/8195a/fwlib/src/hal_ssi.c | 281 + .../soc/realtek/8195a/fwlib/src/hal_timer.c | 32 + .../soc/realtek/8195a/fwlib/src/hal_uart.c | 332 + .../realtek/8195a/misc/bsp/image/ram_1.p.bin | Bin 0 -> 13052 bytes .../realtek/8195a/misc/bsp/image/ram_1.r.bin | Bin 0 -> 13020 bytes .../realtek/8195a/misc/bsp/lib/va0/lib_mdns.a | Bin 0 -> 665098 bytes .../8195a/misc/bsp/lib/va0/lib_platform.a | Bin 0 -> 480232 bytes .../8195a/misc/bsp/lib/va0/lib_rtlstd.a | Bin 0 -> 138684 bytes .../realtek/8195a/misc/bsp/lib/va0/lib_wlan.a | Bin 0 -> 7821634 bytes .../8195a/misc/bsp/lib/va0/lib_wlan_mp.a | Bin 0 -> 8026282 bytes .../realtek/8195a/misc/bsp/lib/va0/lib_wps.a | Bin 0 -> 966426 bytes .../soc/realtek/8195a/misc/bsp/lib/va0/rom.a | Bin 0 -> 2015854 bytes .../realtek/8195a/misc/driver/low_level_io.c | 39 + .../realtek/8195a/misc/driver/rtl_consol.c | 333 + .../realtek/8195a/misc/driver/rtl_consol.h | 133 + .../8195a/misc/iar_utility/common/8195a.ddf | 23 + .../iar_utility/common/dram/EM6A6165TS_7G.mac | 41 + .../misc/iar_utility/common/dram/common.mac | 4 + .../misc/iar_utility/common/dram/readme.txt | 4 + .../common/flashloader/FlashRTL8195aMP.board | 24 + .../common/flashloader/FlashRTL8195aMP.flash | 10 + .../common/flashloader/FlashRTL8195aMP.mac | 69 + .../common/flashloader/FlashRTL8195aMP.out | Bin 0 -> 107376 bytes .../flashloader/FlashRTL8195aMP_img1.board | 17 + .../flashloader/FlashRTL8195aMP_img2.board | 26 + .../common/flashloader/FlashRTL8195aQA.board | 30 + .../common/flashloader/FlashRTL8195aQA.flash | 10 + .../common/flashloader/FlashRTL8195aQA.mac | 60 + .../common/flashloader/FlashRTL8195aQA.out | Bin 0 -> 151784 bytes .../misc/iar_utility/common/gen_board.bat | 53 + .../iar_utility/common/gen_board_img2.bat | 57 + .../misc/iar_utility/common/postbuild.bat | 47 + .../misc/iar_utility/common/postbuild.vbs | 5 + .../iar_utility/common/postbuild_img1.bat | 30 + .../iar_utility/common/postbuild_img1.vbs | 5 + .../iar_utility/common/postbuild_img2.bat | 59 + .../iar_utility/common/postbuild_img2.vbs | 5 + .../misc/iar_utility/common/prebuild.bat | 17 + .../misc/iar_utility/common/prebuild.vbs | 5 + .../misc/iar_utility/common/preload.dap.mac | 372 + .../8195a/misc/iar_utility/common/preload.mac | 382 + .../iar_utility/common/tools/cyggcc_s-1.dll | Bin 0 -> 103453 bytes .../iar_utility/common/tools/cyggmp-10.dll | Bin 0 -> 506909 bytes .../iar_utility/common/tools/cygiconv-2.dll | Bin 0 -> 1008654 bytes .../iar_utility/common/tools/cygintl-8.dll | Bin 0 -> 35342 bytes .../iar_utility/common/tools/cygmpfr-4.dll | Bin 0 -> 351773 bytes .../common/tools/cygncursesw-10.dll | Bin 0 -> 249870 bytes .../iar_utility/common/tools/cygpcre-1.dll | Bin 0 -> 273949 bytes .../iar_utility/common/tools/cygreadline7.dll | Bin 0 -> 165902 bytes .../misc/iar_utility/common/tools/cygwin1.dll | Bin 0 -> 3193245 bytes .../misc/iar_utility/common/tools/cygz.dll | Bin 0 -> 74269 bytes .../misc/iar_utility/common/tools/gawk.exe | Bin 0 -> 539677 bytes .../misc/iar_utility/common/tools/grep.exe | Bin 0 -> 216605 bytes .../misc/iar_utility/common/tools/nm.exe | Bin 0 -> 869671 bytes .../misc/iar_utility/common/tools/objcopy.exe | Bin 0 -> 1068793 bytes .../misc/iar_utility/common/tools/objdump.exe | Bin 0 -> 1313676 bytes .../misc/iar_utility/common/tools/padding.exe | Bin 0 -> 189509 bytes .../misc/iar_utility/common/tools/pick.exe | Bin 0 -> 105492 bytes .../misc/iar_utility/common/tools/sort.exe | Bin 0 -> 78862 bytes .../misc/iar_utility/common/tools/strip.exe | Bin 0 -> 1068793 bytes .../soc/realtek/common/bsp/basic_types.h | 497 + .../soc/realtek/common/bsp/section_config.h | 239 + .../common/rtl_std_lib/include/rtl_lib.h | 141 + .../EWARM-RELEASE/Debug/Exe/application.asm | 190694 +++++++++++++++ .../EWARM-RELEASE/Debug/Exe/application.map | 12506 + .../EWARM-RELEASE/Debug/Exe/application.sim | Bin 0 -> 656347 bytes .../EWARM-RELEASE/Debug/Exe/ota.bin | Bin 0 -> 274104 bytes .../EWARM-RELEASE/Debug/Exe/ram_1.p.bin | Bin 0 -> 45056 bytes .../EWARM-RELEASE/Debug/Exe/ram_2.bin | Bin 0 -> 301752 bytes .../EWARM-RELEASE/Debug/Exe/ram_2.p.bin | Bin 0 -> 274104 bytes .../EWARM-RELEASE/Debug/Exe/ram_all.bin | Bin 0 -> 319160 bytes .../EWARM-RELEASE/Project.dep | 3147 + .../EWARM-RELEASE/Project.ewd | 2697 + .../EWARM-RELEASE/Project.ewp | 2909 + .../EWARM-RELEASE/Project.ewt | 871 + .../EWARM-RELEASE/Project.eww | 10 + .../EWARM-RELEASE/image2.icf | 190 + .../settings/Project.Debug.cspy.bat | 24 + .../EWARM-RELEASE/settings/Project.crun | 16 + .../EWARM-RELEASE/settings/Project.dbgdt | 5 + .../EWARM-RELEASE/settings/Project.dni | 49 + .../EWARM-RELEASE/settings/Project.wsdt | 77 + .../EWARM-RELEASE/settings/Project.wspos | 2 + .../EWARM-RELEASE/tmp.board | 21 + .../analogin_voltage/readme.txt | 23 + .../analogin_voltage/src/main.c | 85 + .../example_sources/crypto/readme.txt | 7 + .../example_sources/crypto/src/main.c | 264 + .../example_sources/flash/readme.txt | 8 + .../example_sources/flash/src/main.c | 32 + .../example_sources/gdma/src/main.c | 60 + .../example_sources/gpio/readme.txt | 14 + .../example_sources/gpio/src/main.c | 49 + .../example_sources/gpio_irq/readme.txt | 13 + .../example_sources/gpio_irq/src/main.c | 59 + .../example_sources/gpio_port/readme.txt | 9 + .../example_sources/gpio_port/src/main.c | 82 + .../example_sources/gtimer/readme.txt | 17 + .../example_sources/gtimer/src/main.c | 71 + .../example_sources/gtimer_rtc/readme.txt | 9 + .../example_sources/gtimer_rtc/src/main.c | 126 + .../example_sources/i2c-shtc1/readme.txt | 16 + .../example_sources/i2c-shtc1/src/main.c | 209 + .../example_sources/i2c/readme.txt | 14 + .../example_sources/i2c/src/main.c | 80 + .../i2c_epl2197_heartrate/inc/HRM_2197.h | 77 + .../inc/heart_interface.h | 39 + .../i2c_epl2197_heartrate/readme.txt | 16 + .../i2c_epl2197_heartrate/src/hr_library.a | Bin 0 -> 51302 bytes .../i2c_epl2197_heartrate/src/main.c | 162 + .../i2c_epl2590_light/readme.txt | 11 + .../i2c_epl2590_light/src/main.c | 108 + .../i2c_epl2590_proximity/readme.txt | 12 + .../i2c_epl2590_proximity/src/main.c | 115 + .../example_sources/i2s/readme.txt | 10 + .../example_sources/i2s/src/alc5651.c | 183 + .../i2s/src/birds_16000_2ch_16b.c | 11666 + .../i2s/src/birds_22050_2ch_16b.c | 16075 ++ .../i2s/src/birds_24000_2ch_16b.c | 17496 ++ .../i2s/src/birds_32000_2ch_16b.c | 23326 ++ .../i2s/src/birds_44100_2ch_16b.c | 32143 +++ .../i2s/src/birds_48000_2ch_16b.c | 34985 +++ .../i2s/src/birds_8000_2ch_16b.c | 5836 + .../example_sources/i2s/src/main.c | 326 + .../example_sources/nfc/readme.txt | 28 + .../example_sources/nfc/src/main.c | 214 + .../example_sources/pm_deepsleep/readme.txt | 18 + .../example_sources/pm_deepsleep/src/main.c | 67 + .../example_sources/pm_deepstandby/readme.txt | 18 + .../example_sources/pm_deepstandby/src/main.c | 61 + .../example_sources/pm_sleep/readme.txt | 18 + .../example_sources/pm_sleep/src/main.c | 81 + .../example_sources/pwm-buzzer/readme.txt | 10 + .../example_sources/pwm-buzzer/src/main.c | 63 + .../example_sources/pwm/readme.txt | 13 + .../example_sources/pwm/src/main.c | 94 + .../example_sources/spi/readme.txt | 23 + .../example_sources/spi/src/main.c | 127 + .../example_sources/spi_pl7223/readme.txt | 19 + .../example_sources/spi_pl7223/src/main.c | 298 + .../spi_stream_twoboard/readme.txt | 24 + .../spi_stream_twoboard/src/main.c | 176 + .../example_sources/spi_twoboard/readme.txt | 24 + .../example_sources/spi_twoboard/src/main.c | 64 + .../example_sources/uart/readme.txt | 19 + .../example_sources/uart/src/main.c | 46 + .../example_sources/uart_clock/readme.txt | 16 + .../example_sources/uart_clock/src/main.c | 94 + .../example_sources/uart_irq/readme.txt | 19 + .../example_sources/uart_irq/src/main.c | 65 + .../uart_stream_dma/readme.txt | 19 + .../uart_stream_dma/src/main.c | 92 + .../uart_stream_irq/readme.txt | 19 + .../uart_stream_irq/src/main.c | 89 + .../example_sources/watchdog/readme.txt | 10 + .../example_sources/watchdog/src/main.c | 61 + .../example_sources/wlan/readme.txt | 14 + .../example_sources/wlan/src/main.c | 50 + .../inc/FreeRTOSConfig.h | 191 + .../inc/build_info.h | 6 + project/realtek_ameba1_va0_example/inc/main.h | 65 + .../inc/platform_autoconf.h | 174 + .../inc/platform_opts.h | 102 + .../realtek_ameba1_va0_example/inc/ws2812b.h | 22 + project/realtek_ameba1_va0_example/src/main.c | 16 + .../realtek_ameba1_va0_example/src/ws2812b.c | 287 + 1181 files changed, 784669 insertions(+) create mode 100644 component/common/api/at_cmd/atcmd_google.c create mode 100644 component/common/api/at_cmd/atcmd_sys.c create mode 100644 component/common/api/at_cmd/atcmd_sys.h create mode 100644 component/common/api/at_cmd/atcmd_wifi.c create mode 100644 component/common/api/at_cmd/atcmd_wifi.h create mode 100644 component/common/api/at_cmd/log_service.c create mode 100644 component/common/api/at_cmd/log_service.h create mode 100644 component/common/api/lwip_netconf.c create mode 100644 component/common/api/lwip_netconf.h create mode 100644 component/common/api/network/include/lwipopts.h create mode 100644 component/common/api/network/include/main.h create mode 100644 component/common/api/network/include/main_test.h create mode 100644 component/common/api/network/include/netconf.h create mode 100644 component/common/api/network/include/rtl8195a_it.h create mode 100644 component/common/api/network/include/util.h create mode 100644 component/common/api/network/include/wificonf.h create mode 100644 component/common/api/network/src/ping_test.c create mode 100644 component/common/api/network/src/rtl8195a_it.c create mode 100644 component/common/api/network/src/wlan_network.c create mode 100644 component/common/api/platform/dlist.h create mode 100644 component/common/api/platform/platform_stdlib.h create mode 100644 component/common/api/platform/stdlib_patch.c create mode 100644 component/common/api/wifi/rtw_wpa_supplicant/src/utils/os.h create mode 100644 component/common/api/wifi/rtw_wpa_supplicant/src/utils/os_freertos.c create mode 100644 component/common/api/wifi/rtw_wpa_supplicant/src/utils/rom/rom_wps_os.h create mode 100644 component/common/api/wifi/rtw_wpa_supplicant/src/wps/wps_defs.h create mode 100644 component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_wps_config.c create mode 100644 component/common/api/wifi/wifi_conf.c create mode 100644 component/common/api/wifi/wifi_conf.h create mode 100644 component/common/api/wifi/wifi_ind.c create mode 100644 component/common/api/wifi/wifi_ind.h create mode 100644 component/common/api/wifi/wifi_promisc.c create mode 100644 component/common/api/wifi/wifi_simple_config.c create mode 100644 component/common/api/wifi/wifi_util.c create mode 100644 component/common/api/wifi/wifi_util.h create mode 100644 component/common/api/wifi_interactive_ext.h create mode 100644 component/common/api/wifi_interactive_mode.c create mode 100644 component/common/application/google/google_nest.h create mode 100644 component/common/application/uart_adapter/uart_adapter.c create mode 100644 component/common/application/uart_adapter/uart_adapter.h create mode 100644 component/common/drivers/wlan/realtek/include/autoconf.h create mode 100644 component/common/drivers/wlan/realtek/include/drv_conf.h create mode 100644 component/common/drivers/wlan/realtek/include/wifi_constants.h create mode 100644 component/common/drivers/wlan/realtek/include/wifi_structures.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/freertos/freertos_service.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/freertos/wrapper.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c create mode 100644 component/common/drivers/wlan/realtek/src/osdep/lwip_intf.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/osdep_service.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/skbuff.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/wireless.h create mode 100644 component/common/drivers/wlan/realtek/src/osdep/wlan_intf.h create mode 100644 component/common/example/cJSON/cJSON_example.c create mode 100644 component/common/example/example_entry.c create mode 100644 component/common/example/example_entry.h create mode 100644 component/common/example/googlenest/example.html create mode 100644 component/common/example/googlenest/example_google.c create mode 100644 component/common/example/googlenest/example_google.h create mode 100644 component/common/example/mcast/example_mcast.c create mode 100644 component/common/example/mcast/example_mcast.h create mode 100644 component/common/example/mcast/readme.txt create mode 100644 component/common/example/mdns/example_mdns.c create mode 100644 component/common/example/mdns/example_mdns.h create mode 100644 component/common/example/wlan_fast_connect/example_wlan_fast_connect.c create mode 100644 component/common/example/wlan_fast_connect/example_wlan_fast_connect.h create mode 100644 component/common/mbed/api/error.h create mode 100644 component/common/mbed/api/mbed_assert.h create mode 100644 component/common/mbed/api/wait_api.h create mode 100644 component/common/mbed/common/.gitkeep create mode 100644 component/common/mbed/common/Makefile create mode 100644 component/common/mbed/common/us_ticker_api.c create mode 100644 component/common/mbed/common/wait_api.c create mode 100644 component/common/mbed/hal/analogin_api.h create mode 100644 component/common/mbed/hal/analogout_api.h create mode 100644 component/common/mbed/hal/can_api.h create mode 100644 component/common/mbed/hal/ethernet_api.h create mode 100644 component/common/mbed/hal/gpio_api.h create mode 100644 component/common/mbed/hal/gpio_irq_api.h create mode 100644 component/common/mbed/hal/i2c_api.h create mode 100644 component/common/mbed/hal/pinmap.h create mode 100644 component/common/mbed/hal/port_api.h create mode 100644 component/common/mbed/hal/pwmout_api.h create mode 100644 component/common/mbed/hal/rtc_api.h create mode 100644 component/common/mbed/hal/serial_api.h create mode 100644 component/common/mbed/hal/sleep_api.h create mode 100644 component/common/mbed/hal/spi_api.h create mode 100644 component/common/mbed/hal/us_ticker_api.h create mode 100644 component/common/mbed/hal_ext/dma_api.h create mode 100644 component/common/mbed/hal_ext/flash_api.h create mode 100644 component/common/mbed/hal_ext/i2s_api.h create mode 100644 component/common/mbed/hal_ext/nfc_api.h create mode 100644 component/common/mbed/hal_ext/serial_ex_api.h create mode 100644 component/common/mbed/hal_ext/sleep_ex_api.h create mode 100644 component/common/mbed/hal_ext/spi_ex_api.h create mode 100644 component/common/mbed/hal_ext/sys_api.h create mode 100644 component/common/mbed/hal_ext/wdt_api.h create mode 100644 component/common/mbed/targets/cmsis/rtl8195a create mode 100644 component/common/mbed/targets/hal/rtl8195a/PeripheralNames.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/PinNames.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/PortNames.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/analogin_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/device.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/dma_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/flash_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/gpio_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/gpio_irq_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/gpio_object.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/i2c_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/i2s_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/nfc_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/objects.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/pinmap.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/pinmap_common.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/port_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/pwmout_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/rtc_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/serial_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/sleep.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/spi_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/sys_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/timer_api.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/timer_api.h create mode 100644 component/common/mbed/targets/hal/rtl8195a/us_ticker.c create mode 100644 component/common/mbed/targets/hal/rtl8195a/wdt_api.c create mode 100644 component/common/network/dhcp/dhcps.c create mode 100644 component/common/network/dhcp/dhcps.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/CHANGELOG create mode 100644 component/common/network/lwip/lwip_v1.3.2/COPYING create mode 100644 component/common/network/lwip/lwip_v1.3.2/FILES create mode 100644 component/common/network/lwip/lwip_v1.3.2/README create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/FILES create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/contrib.txt create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/rawapi.txt create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/savannah.txt create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/snmp_agent.txt create mode 100644 component/common/network/lwip/lwip_v1.3.2/doc/sys_arch.txt create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/api_lib.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/api_msg.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/err.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/netbuf.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/netdb.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/netifapi.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/sockets.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/api/tcpip.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/dhcp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/dns.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/init.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/autoip.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/icmp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/igmp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet_chksum.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_addr.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_frag.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/README create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/icmp6.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/inet6.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6_addr.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/mem.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/memp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/netif.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/pbuf.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/raw.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_dec.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_enc.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib2.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib_structs.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_in.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_out.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/stats.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/sys.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/tcp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/tcp_in.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/tcp_out.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/core/udp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/autoip.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/icmp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/igmp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet_chksum.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_addr.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_frag.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/icmp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/inet.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip_addr.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api_msg.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/arch.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/debug.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/def.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dhcp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dns.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/err.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/init.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/mem.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp_std.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netbuf.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netdb.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netifapi.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/opt.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/pbuf.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/raw.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sio.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_asn1.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_msg.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_structs.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sockets.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/stats.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sys.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcpip.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/lwip/udp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/netif/etharp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/netif/loopif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/netif/ppp_oe.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/include/netif/slipif.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/FILES create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/etharp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/loopif.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp_oe.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pppdebug.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.c create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vjbsdhdr.h create mode 100644 component/common/network/lwip/lwip_v1.3.2/src/netif/slipif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/CHANGELOG create mode 100644 component/common/network/lwip/lwip_v1.4.1/COPYING create mode 100644 component/common/network/lwip/lwip_v1.4.1/FILES create mode 100644 component/common/network/lwip/lwip_v1.4.1/README create mode 100644 component/common/network/lwip/lwip_v1.4.1/UPGRADING create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/FILES create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/contrib.txt create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/rawapi.txt create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/savannah.txt create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/snmp_agent.txt create mode 100644 component/common/network/lwip/lwip_v1.4.1/doc/sys_arch.txt create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/MFC6A0B.tmp create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/FILES create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/api_lib.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/api_msg.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/err.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/netbuf.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/netdb.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/netifapi.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/sockets.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/api/tcpip.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/def.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/dhcp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/dns.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/init.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/autoip.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/icmp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/igmp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet_chksum.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_addr.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_frag.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/README create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/icmp6.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/inet6.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6_addr.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/lwip_timers.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/mem.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/memp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/netif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/pbuf.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/raw.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_dec.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_enc.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib2.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib_structs.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_in.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_out.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/stats.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/sys.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/tcp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/tcp_in.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/tcp_out.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/core/udp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/autoip.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/icmp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/igmp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet_chksum.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_addr.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_frag.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/icmp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/inet.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip_addr.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api_msg.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/arch.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/debug.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/def.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dhcp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dns.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/err.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/init.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/lwip_timers.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/mem.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp_std.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netbuf.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netdb.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netifapi.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/opt.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/pbuf.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/raw.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sio.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_asn1.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_msg.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_structs.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sockets.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/stats.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sys.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp_impl.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcpip.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/lwip/udp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/netif/etharp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/netif/loopif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/netif/ppp_oe.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/netif/slipif.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/posix/netdb.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/include/posix/sys/socket.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/FILES create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/etharp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_impl.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_oe.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pppdebug.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.c create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.h create mode 100644 component/common/network/lwip/lwip_v1.4.1/src/netif/slipif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/CHANGELOG create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/COPYING create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/FILES create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/README create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/UPGRADING create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/FILES create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/contrib.txt create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/rawapi.txt create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/savannah.txt create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/snmp_agent.txt create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/doc/sys_arch.txt create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/bpstruct.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cc.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cpu.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/epstruct.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/init.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/lib.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/perf.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/MFC6A0B.tmp create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/FILES create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_lib.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_msg.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/err.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/netbuf.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/netdb.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/netifapi.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/pppapi.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/sockets.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/api/tcpip.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/def.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/dhcp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/dns.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/inet_chksum.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/init.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/autoip.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/icmp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/igmp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4_addr.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip_frag.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/README create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/dhcp6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ethip6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/icmp6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/inet6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_addr.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_frag.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/mld6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/nd6.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/lwip_timers.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/mem.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/memp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/netif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/pbuf.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/raw.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_dec.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_enc.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib2.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib_structs.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_in.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_out.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/stats.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/sys.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_in.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_out.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/core/udp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api_msg.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/arch.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/autoip.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/debug.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/def.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dns.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/err.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ethip6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/igmp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet_chksum.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/init.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4_addr.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_addr.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_frag.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_addr.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_frag.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_ip.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_timers.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mem.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp_std.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mld6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/nd6.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netbuf.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netdb.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netifapi.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/opt.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pbuf.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pppapi.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/raw.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sio.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_asn1.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_msg.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_structs.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sockets.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/stats.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sys.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp_impl.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcpip.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/udp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/etharp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/loopif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ccp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-md5.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-new.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap_ms.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eap.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ecp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eui64.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/fsm.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipcp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipv6cp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/lcp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/magic.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/des.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md4.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md5.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/sha1.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp_impl.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppcrypt.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppdebug.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppoe.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppol2tp.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/upap.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/vj.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/slipif.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/netdb.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/sys/socket.h create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/FILES create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/etharp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ethernetif.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/PPPD_FOLLOWUP create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/auth.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ccp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-md5.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-new.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap_ms.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/demand.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eap.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ecp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eui64.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/fsm.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipcp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipv6cp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/lcp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/magic.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/multilink.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/README create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/des.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md4.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md5.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/sha1.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ppp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppcrypt.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppoe.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppol2tp.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/upap.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/utils.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/vj.c create mode 100644 component/common/network/lwip/lwip_v1.5.0.beta/src/netif/slipif.c create mode 100644 component/common/network/mdns/mDNS.h create mode 100644 component/common/network/mdns/mDNSPlatform.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/aes.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/aesni.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/arc4.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1write.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/base64.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/bignum.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/blowfish.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/bn_mul.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/camellia.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ccm.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/certs.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/check_config.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher_wrap.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/compat-1.2.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/config.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_all.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_rsa.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_srp.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ctr_drbg.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/debug.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/des.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/dhm.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdh.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdsa.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecp.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy_poll.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/error.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/gcm.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/havege.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/hmac_drbg.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/md.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/md2.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/md4.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/md5.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/md_wrap.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory_buffer_alloc.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/net.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/oid.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/openssl.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/padlock.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pbkdf2.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pem.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk_wrap.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs11.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs12.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs5.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/platform.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ripemd160.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/rsa.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha1.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha256.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha512.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_cache.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_ciphersuites.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/threading.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/timing.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/version.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crl.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crt.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_csr.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/include/polarssl/xtea.h create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/.gitignore create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/CMakeLists.txt create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/Makefile create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/aes.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/aesni.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/arc4.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/asn1parse.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/asn1write.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/base64.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/bignum.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/blowfish.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/camellia.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ccm.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/certs.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/cipher.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/cipher_wrap.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ctr_drbg.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/debug.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/des.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/dhm.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ecdh.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ecdsa.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ecp.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ecp_curves.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/entropy.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/entropy_poll.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/error.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/gcm.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/havege.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/hmac_drbg.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/md.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/md2.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/md4.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/md5.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/md_wrap.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/memory_buffer_alloc.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/net.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/oid.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/padlock.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pbkdf2.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pem.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pk.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pk_wrap.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pkcs11.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pkcs12.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pkcs5.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pkparse.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/pkwrite.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/platform.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ripemd160.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/rsa.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/sha1.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/sha256.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/sha512.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ssl_cache.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ssl_ciphersuites.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ssl_cli.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ssl_srv.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/ssl_tls.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/threading.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/timing.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/version.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/version_features.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509_create.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509_crl.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509_crt.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509_csr.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509write_crt.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/x509write_csr.c create mode 100644 component/common/network/ssl/polarssl-1.3.8/library/xtea.c create mode 100644 component/common/test/wlan/wlan_test_inc.h create mode 100644 component/common/utilities/cJSON.c create mode 100644 component/common/utilities/cJSON.h create mode 100644 component/common/utilities/ssl_client.c create mode 100644 component/common/utilities/tcpecho.c create mode 100644 component/common/utilities/tcptest.c create mode 100644 component/common/utilities/udpecho.c create mode 100644 component/common/utilities/update.c create mode 100644 component/common/utilities/update.h create mode 100644 component/common/utilities/webserver.c create mode 100644 component/common/utilities/webserver.h create mode 100644 component/os/freertos/cmsis_os.c create mode 100644 component/os/freertos/cmsis_os.h create mode 100644 component/os/freertos/freertos_pmu.c create mode 100644 component/os/freertos/freertos_pmu.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/BlockQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/PollQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/comtest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/death.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/dynamic.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/events.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flash.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flop.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/integer.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/print.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Full/semtest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlckQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlock.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltPollQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltQTest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/BlockQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/EventGroupsDemo.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/GenQTest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/IntQueue.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/PollQ.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QPeek.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueOverwrite.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueSet.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/TimerDemo.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/blocktim.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest_strings.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/countsem.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crflash.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crhook.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/death.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/dynamic.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash_timer.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flop.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/integer.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/recmutex.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/semtest.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/sp_flop.c create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlckQ.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlock.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltPollQ.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltQTest.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/BlockQ.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/EventGroupsDemo.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/GenQTest.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/IntQueue.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/PollQ.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/QPeek.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueOverwrite.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueSet.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/TimerDemo.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/blocktim.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest2.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest_strings.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/countsem.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/crflash.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/crhook.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/death.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/dynamic.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/fileIO.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash_timer.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/flop.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/integer.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/mevents.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/partest.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/print.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/recmutex.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/semtest.h create mode 100644 component/os/freertos/freertos_v8.1.2/Demo/Common/include/serial.h create mode 100644 component/os/freertos/freertos_v8.1.2/License/license.txt create mode 100644 component/os/freertos/freertos_v8.1.2/Source/Makefile create mode 100644 component/os/freertos/freertos_v8.1.2/Source/croutine.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/event_groups.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/FreeRTOS.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/StackMacros.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/croutine.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/event_groups.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/list.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/mpu_wrappers.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/portable.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/projdefs.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/queue.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/semphr.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/stdint.readme create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/task.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/include/timers.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/list.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/Makefile create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portasm.s create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portasm.s create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/Makefile create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_1.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_2.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_3.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_4.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_5.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/port.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/portmacro.h create mode 100644 component/os/freertos/freertos_v8.1.2/Source/queue.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/readme.txt create mode 100644 component/os/freertos/freertos_v8.1.2/Source/tasks.c create mode 100644 component/os/freertos/freertos_v8.1.2/Source/timers.c create mode 100644 component/os/os_dep/include/mailbox.h create mode 100644 component/os/os_dep/include/os_support.h create mode 100644 component/os/os_dep/include/os_timer.h create mode 100644 component/os/os_dep/include/osdep_api.h create mode 100644 component/os/os_dep/mailbox.c create mode 100644 component/os/os_dep/osdep_api.c create mode 100644 component/soc/realtek/8195a/cmsis/core_cm3.h create mode 100644 component/soc/realtek/8195a/cmsis/core_cmFunc.h create mode 100644 component/soc/realtek/8195a/cmsis/core_cmInstr.h create mode 100644 component/soc/realtek/8195a/cmsis/device/app_start.c create mode 100644 component/soc/realtek/8195a/cmsis/device/cmsis.h create mode 100644 component/soc/realtek/8195a/cmsis/device/cmsis_nvic.c create mode 100644 component/soc/realtek/8195a/cmsis/device/cmsis_nvic.h create mode 100644 component/soc/realtek/8195a/cmsis/device/diag.h create mode 100644 component/soc/realtek/8195a/cmsis/device/rand.h create mode 100644 component/soc/realtek/8195a/cmsis/device/rtl_stdlib.h create mode 100644 component/soc/realtek/8195a/cmsis/device/rtl_utility.h create mode 100644 component/soc/realtek/8195a/cmsis/device/strproc.h create mode 100644 component/soc/realtek/8195a/cmsis/device/system_8195a.c create mode 100644 component/soc/realtek/8195a/cmsis/device/system_8195a.h create mode 100644 component/soc/realtek/8195a/cmsis/device/va_list.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_adc.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_api.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_common.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_crypto.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_dac.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_diag.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_efuse.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_gdma.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_gpio.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_i2c.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_i2s.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_irqn.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_mii.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_misc.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_nfc.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_pcm.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_peri_on.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_pinmux.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_platform.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_pwm.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_sdio.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_sdr_controller.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_soc_ps_monitor.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_spi_flash.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_ssi.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_timer.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_uart.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_util.h create mode 100644 component/soc/realtek/8195a/fwlib/hal_vector_table.h create mode 100644 component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom/rom_wlan_ram_map.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_adc.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_dac.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gdma.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gpio.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2c.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2s.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_mii.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_nfc.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pcm.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_peri_on.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pwm.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio_host.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdr.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_spi_flash.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_ssi.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sys_on.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_timer.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_uart.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_wdt.h create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_adc.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_dac.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_i2s.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_mii.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pcm.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_sdio_device.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_timer.c create mode 100644 component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_uart.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_32k.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_adc.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_common.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_dac.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_gdma.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_gpio.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_i2c.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_i2s.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_mii.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_nfc.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_pcm.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_pwm.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_sdr_controller.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_soc_ps_monitor.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_ssi.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_timer.c create mode 100644 component/soc/realtek/8195a/fwlib/src/hal_uart.c create mode 100644 component/soc/realtek/8195a/misc/bsp/image/ram_1.p.bin create mode 100644 component/soc/realtek/8195a/misc/bsp/image/ram_1.r.bin create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_mdns.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_platform.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_rtlstd.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wlan.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wlan_mp.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wps.a create mode 100644 component/soc/realtek/8195a/misc/bsp/lib/va0/rom.a create mode 100644 component/soc/realtek/8195a/misc/driver/low_level_io.c create mode 100644 component/soc/realtek/8195a/misc/driver/rtl_consol.c create mode 100644 component/soc/realtek/8195a/misc/driver/rtl_consol.h create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/8195a.ddf create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/dram/EM6A6165TS_7G.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/dram/common.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/dram/readme.txt create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.board create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.flash create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.out create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img1.board create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img2.board create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.board create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.flash create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.out create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/gen_board.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/gen_board_img2.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild.vbs create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.vbs create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.vbs create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/prebuild.bat create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/prebuild.vbs create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/preload.dap.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/preload.mac create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggcc_s-1.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggmp-10.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygiconv-2.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygintl-8.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygmpfr-4.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygncursesw-10.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygpcre-1.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygreadline7.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygwin1.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/cygz.dll create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/gawk.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/grep.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/nm.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/objcopy.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/objdump.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/padding.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/pick.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/sort.exe create mode 100644 component/soc/realtek/8195a/misc/iar_utility/common/tools/strip.exe create mode 100644 component/soc/realtek/common/bsp/basic_types.h create mode 100644 component/soc/realtek/common/bsp/section_config.h create mode 100644 component/soc/realtek/common/rtl_std_lib/include/rtl_lib.h create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/application.asm create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/application.map create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/application.sim create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/ota.bin create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/ram_1.p.bin create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/ram_2.bin create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/ram_2.p.bin create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Debug/Exe/ram_all.bin create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Project.dep create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Project.ewd create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Project.ewp create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Project.ewt create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/Project.eww create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/image2.icf create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.Debug.cspy.bat create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.crun create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.dbgdt create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.dni create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.wsdt create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/settings/Project.wspos create mode 100644 project/realtek_ameba1_va0_example/EWARM-RELEASE/tmp.board create mode 100644 project/realtek_ameba1_va0_example/example_sources/analogin_voltage/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/analogin_voltage/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/crypto/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/crypto/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/flash/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/flash/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gdma/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio_irq/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio_irq/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio_port/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/gpio_port/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gtimer/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/gtimer/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/gtimer_rtc/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/gtimer_rtc/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c-shtc1/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c-shtc1/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2197_heartrate/inc/HRM_2197.h create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2197_heartrate/inc/heart_interface.h create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2197_heartrate/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2197_heartrate/src/hr_library.a create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2197_heartrate/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2590_light/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2590_light/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2590_proximity/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2c_epl2590_proximity/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/alc5651.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_16000_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_22050_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_24000_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_32000_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_44100_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_48000_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/birds_8000_2ch_16b.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/i2s/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/nfc/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/nfc/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_deepsleep/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_deepsleep/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_deepstandby/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_deepstandby/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_sleep/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/pm_sleep/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/pwm-buzzer/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/pwm-buzzer/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/pwm/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/pwm/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_pl7223/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_pl7223/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_stream_twoboard/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_stream_twoboard/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_twoboard/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/spi_twoboard/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_clock/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_clock/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_irq/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_irq/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_stream_dma/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_stream_dma/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_stream_irq/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/uart_stream_irq/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/watchdog/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/watchdog/src/main.c create mode 100644 project/realtek_ameba1_va0_example/example_sources/wlan/readme.txt create mode 100644 project/realtek_ameba1_va0_example/example_sources/wlan/src/main.c create mode 100644 project/realtek_ameba1_va0_example/inc/FreeRTOSConfig.h create mode 100644 project/realtek_ameba1_va0_example/inc/build_info.h create mode 100644 project/realtek_ameba1_va0_example/inc/main.h create mode 100644 project/realtek_ameba1_va0_example/inc/platform_autoconf.h create mode 100644 project/realtek_ameba1_va0_example/inc/platform_opts.h create mode 100644 project/realtek_ameba1_va0_example/inc/ws2812b.h create mode 100644 project/realtek_ameba1_va0_example/src/main.c create mode 100644 project/realtek_ameba1_va0_example/src/ws2812b.c diff --git a/component/common/api/at_cmd/atcmd_google.c b/component/common/api/at_cmd/atcmd_google.c new file mode 100644 index 0000000..1daaf43 --- /dev/null +++ b/component/common/api/at_cmd/atcmd_google.c @@ -0,0 +1,193 @@ +#include +#include +#include "log_service.h" +#include "cmsis_os.h" +#include +#include +#include +#include "wifi_conf.h" +#include "google_nest.h" +#include + +#define GN_PORT 443 + + +#define _AT_GOOGLE_NEST_ "ATG0" + +//functions that using Google Nest's API +void google_data_retrieve_cb(char *response_buf); + +void googlenest_get(char *host_addr, char *host_file) +{ + unsigned char buffer[512]; + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + if(gn_get(&googlenest, googlenest_uri, buffer, sizeof(buffer)) == 0) + printf("\r\n\r\nGet data from googlenest: %s", buffer); + gn_close(&googlenest); + + } +} + +void google_data_retrieve_cb(char *response_buf) { + printf("\r\nResponse_buf:\r\n%s\r\n", response_buf); + +} + +void googlenest_stream(char *host_addr, char *host_file) +{ + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + google_retrieve_data_hook_callback(google_data_retrieve_cb); + gn_stream(&googlenest, googlenest_uri); + gn_close(&googlenest); + + } +} + +void googlenest_delete(char *host_addr, char *host_file) +{ + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + if(gn_delete(&googlenest, googlenest_uri) == 0) + printf("\r\n\r\nDelete the data is successful!"); + gn_close(&googlenest); + + } +} + +void googlenest_put(char *host_addr, char *host_file, char *data) +{ + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + if(gn_put(&googlenest, googlenest_uri, data) == 0) + printf("\r\n\r\nSaving data in firebase is successful!"); + gn_close(&googlenest); + } +} + +void googlenest_patch(char *host_addr, char *host_file, char *data) +{ + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + if(gn_patch(&googlenest, googlenest_uri, data) == 0) + printf("\r\n\r\nUpdating data in firebase is successful!"); + gn_close(&googlenest); + } +} + +void googlenest_post(char *host_addr, char *host_file, char *data) +{ + googlenest_context googlenest; + char *googlenest_host = host_addr; + char *googlenest_uri = host_file; + unsigned char buffer[64]; + + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + if(gn_post(&googlenest, googlenest_uri, data, buffer, sizeof(buffer)) == 0) + printf("\r\n\r\nInserting data to firebase is successful!\r\n\r\nThe unique name for this list of data is: %s", buffer); + gn_close(&googlenest); + } +} + +void cmd_googlenest(int argc, char **argv) +{ + if(strcmp(argv[1], "get") == 0) { + if(argc != 4) + printf("\n\rUsage: gn get address file"); + else { + googlenest_get(argv[2], argv[3]); + } + } + else if(strcmp(argv[1], "delete") ==0){ + if(argc != 4) + printf("\n\rUsage: gn delete address file"); + else { + googlenest_delete(argv[2], argv[3]); + } + } + else if(strcmp(argv[1], "put") ==0){ + if(argc != 5) + printf("\n\rUsage: gn put address file data"); + else { + googlenest_put(argv[2], argv[3], argv[4]); + } + } + else if(strcmp(argv[1], "patch") ==0){ + if(argc != 5) + printf("\n\rUsage: gn patch address file data"); + else { + googlenest_patch(argv[2], argv[3], argv[4]); + } + } + else if(strcmp(argv[1], "post") ==0){ + if(argc != 5) + printf("\n\rUsage: gn post address file data"); + else { + googlenest_post(argv[2], argv[3], argv[4]); + } + } + else if(strcmp(argv[1], "stream") ==0){ + if(argc != 4) + printf("\n\rUsage: gn stream address file"); + else { + googlenest_stream(argv[2], argv[3]); + } + } + else + printf("\n\rUsage: gn method addr file (data)"); +} + +//AT Command function + +void fATG0(void *arg){ + int argc; + char *argv[MAX_ARGC] = {0}; + printf("[ATG0]: _AT_WLAN_GOOGLENEST_\n\r"); + if(!arg){ + printf("[ATG0]Usage: ATWG=[method,address,file,data] or ATG0=[method,address,file]\n\r"); + return; + } + argv[0] = "gn"; + if((argc = parse_param(arg, argv)) > 1){ + cmd_googlenest(argc, argv); + } + else + printf("[ATG0]Usage: ATG0=[method,address,file,data] or ATG0=[method,address,file]\n\r"); +} + +log_item_t at_google_items[ ] = { + + {"ATG0", fATG0,} + +}; + +void at_google_init(void) +{ + log_service_add_table(at_google_items, sizeof(at_google_items)/sizeof(at_google_items[0])); +} + +log_module_init(at_google_init); diff --git a/component/common/api/at_cmd/atcmd_sys.c b/component/common/api/at_cmd/atcmd_sys.c new file mode 100644 index 0000000..cc198d2 --- /dev/null +++ b/component/common/api/at_cmd/atcmd_sys.c @@ -0,0 +1,512 @@ +#include +#include +#include +#include // mbed +#include +#include +#include +#include "analogin_api.h" +#include "log_service.h" +#include "atcmd_sys.h" + +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) +#include "freertos_pmu.h" +#endif + +extern u32 ConfigDebugErr; +extern u32 ConfigDebugInfo; +extern u32 ConfigDebugWarn; +extern u32 CmdDumpWord(IN u16 argc, IN u8 *argv[]); +extern u32 CmdWriteWord(IN u16 argc, IN u8 *argv[]); + +//-------- AT SYS commands --------------------------------------------------------------- + +void fATSD(void *arg) +{ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + + AT_DBG_MSG(AT_FLAG_DUMP, AT_DBG_ALWAYS, "[ATSD]: _AT_SYSTEM_DUMP_REGISTER_"); + if(!arg){ + AT_DBG_MSG(AT_FLAG_DUMP, AT_DBG_ALWAYS, "[ATSD] Usage: ATSD=REGISTER"); + return; + } + argc = parse_param(arg, argv); + if(argc == 2 || argc == 3) + CmdDumpWord(argc-1, (unsigned char**)(argv+1)); +} + +void fATSE(void *arg) +{ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + + AT_DBG_MSG(AT_FLAG_EDIT, AT_DBG_ALWAYS, "[ATSE]: _AT_SYSTEM_EDIT_REGISTER_"); + if(!arg){ + AT_DBG_MSG(AT_FLAG_EDIT, AT_DBG_ALWAYS, "[ATSE] Usage: ATSE=REGISTER[VALUE]"); + return; + } + argc = parse_param(arg, argv); + if(argc == 3) + CmdWriteWord(argc-1, (unsigned char**)(argv+1)); +} + +#if SUPPORT_MP_MODE +void fATSA(void *arg) +{ + u32 tConfigDebugInfo = ConfigDebugInfo; + int argc = 0, channel; + char *argv[MAX_ARGC] = {0}, *ptmp; + u16 offset, gain; + + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA]: _AT_SYSTEM_ADC_TEST_"); + if(!arg){ + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] Usage: ATSA=CHANNEL(0~2)"); + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] Usage: ATSA=k_get"); + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] Usage: ATSA=k_set[offet(hex),gain(hex)]"); + return; + } + + argc = parse_param(arg, argv); + if(strcmp(argv[1], "k_get") == 0){ + sys_adc_calibration(0, &offset, &gain); + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] offset = 0x%04X, gain = 0x%04X", offset, gain); + }else if(strcmp(argv[1], "k_set") == 0){ + if(argc != 4){ + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] Usage: ATSA=k_set[offet(hex),gain(hex)]"); + return; + } + offset = strtoul(argv[2], &ptmp, 16); + gain = strtoul(argv[3], &ptmp, 16); + sys_adc_calibration(1, &offset, &gain); + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] offset = 0x%04X, gain = 0x%04X", offset, gain); + }else{ + channel = atoi(argv[1]); + if(channel < 0 || channel > 2){ + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] Usage: ATSA=CHANNEL(0~2)"); + return; + } + analogin_t adc; + u16 adcdat; + + // Remove debug info massage + ConfigDebugInfo = 0; + if(channel == 0) + analogin_init(&adc, AD_1); + else if(channel == 1) + analogin_init(&adc, AD_2); + else + analogin_init(&adc, AD_3); + adcdat = analogin_read_u16(&adc)>>4; + // Recover debug info massage + ConfigDebugInfo = tConfigDebugInfo; + + AT_DBG_MSG(AT_FLAG_ADC, AT_DBG_ALWAYS, "[ATSA] A%d = 0x%04X", channel, adcdat); + } +} + +void fATSG(void *arg) +{ + gpio_t gpio_test; + int argc = 0, val; + char *argv[MAX_ARGC] = {0}, port, num; + PinName pin = NC; + u32 tConfigDebugInfo = ConfigDebugInfo; + + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSG]: _AT_SYSTEM_GPIO_TEST_"); + if(!arg){ + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSG] Usage: ATSG=PINNAME(ex:A0)"); + return; + }else{ + argc = parse_param(arg, argv); + if(argc != 2){ + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSG] Usage: ATSG=PINNAME(ex:A0)"); + return; + } + } + port = argv[1][0]; + num = argv[1][1]; + if(port >= 'a' && port <= 'z') + port -= ('a' - 'A'); + if(num >= 'a' && num <= 'z') + num -= ('a' - 'A'); + switch(port){ + case 'A': + switch(num){ + case '0': pin = PA_0; break; case '1': pin = PA_1; break; case '2': pin = PA_2; break; case '3': pin = PA_3; break; + case '4': pin = PA_4; break; case '5': pin = PA_5; break; case '6': pin = PA_6; break; case '7': pin = PA_7; break; + } + break; + case 'B': + switch(num){ + case '0': pin = PB_0; break; case '1': pin = PB_1; break; case '2': pin = PB_2; break; case '3': pin = PB_3; break; + case '4': pin = PB_4; break; case '5': pin = PB_5; break; case '6': pin = PB_6; break; case '7': pin = PB_7; break; + } + break; + case 'C': + switch(num){ + case '0': pin = PC_0; break; case '1': pin = PC_1; break; case '2': pin = PC_2; break; case '3': pin = PC_3; break; + case '4': pin = PC_4; break; case '5': pin = PC_5; break; case '6': pin = PC_6; break; case '7': pin = PC_7; break; + case '8': pin = PC_8; break; case '9': pin = PC_9; break; + } + break; + case 'D': + switch(num){ + case '0': pin = PD_0; break; case '1': pin = PD_1; break; case '2': pin = PD_2; break; case '3': pin = PD_3; break; + case '4': pin = PD_4; break; case '5': pin = PD_5; break; case '6': pin = PD_6; break; case '7': pin = PD_7; break; + case '8': pin = PD_8; break; case '9': pin = PD_9; break; + } + break; + case 'E': + switch(num){ + case '0': pin = PE_0; break; case '1': pin = PE_1; break; case '2': pin = PE_2; break; case '3': pin = PE_3; break; + case '4': pin = PE_4; break; case '5': pin = PE_5; break; case '6': pin = PE_6; break; case '7': pin = PE_7; break; + case '8': pin = PE_8; break; case '9': pin = PE_9; break; case 'A': pin = PE_A; break; + } + break; + case 'F': + switch(num){ + case '0': pin = PF_0; break; case '1': pin = PF_1; break; case '2': pin = PF_2; break; case '3': pin = PF_3; break; + case '4': pin = PF_4; break; case '5': pin = PF_5; break; + } + break; + case 'G': + switch(num){ + case '0': pin = PG_0; break; case '1': pin = PG_1; break; case '2': pin = PG_2; break; case '3': pin = PG_3; break; + case '4': pin = PG_4; break; case '5': pin = PG_5; break; case '6': pin = PG_6; break; case '7': pin = PG_7; break; + } + break; + case 'H': + switch(num){ + case '0': pin = PH_0; break; case '1': pin = PH_1; break; case '2': pin = PH_2; break; case '3': pin = PH_3; break; + case '4': pin = PH_4; break; case '5': pin = PH_5; break; case '6': pin = PH_6; break; case '7': pin = PH_7; break; + } + break; + case 'I': + switch(num){ + case '0': pin = PI_0; break; case '1': pin = PI_1; break; case '2': pin = PI_2; break; case '3': pin = PI_3; break; + case '4': pin = PI_4; break; case '5': pin = PI_5; break; case '6': pin = PI_6; break; case '7': pin = PI_7; break; + } + break; + case 'J': + switch(num){ + case '0': pin = PJ_0; break; case '1': pin = PJ_1; break; case '2': pin = PJ_2; break; case '3': pin = PJ_3; break; + case '4': pin = PJ_4; break; case '5': pin = PJ_5; break; case '6': pin = PJ_6; break; + } + break; + case 'K': + switch(num){ + case '0': pin = PK_0; break; case '1': pin = PK_1; break; case '2': pin = PK_2; break; case '3': pin = PK_3; break; + case '4': pin = PK_4; break; case '5': pin = PK_5; break; case '6': pin = PK_6; break; + } + break; + } + if(pin == NC){ + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSG]: Invalid Pin Name"); + return; + } + // Remove debug info massage + ConfigDebugInfo = 0; + // Initial input control pin + gpio_init(&gpio_test, pin); + gpio_dir(&gpio_test, PIN_INPUT); // Direction: Input + gpio_mode(&gpio_test, PullUp); // Pull-High + val = gpio_read(&gpio_test); + // Recover debug info massage + ConfigDebugInfo = tConfigDebugInfo; + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSG] %c%c = %d", port, num, val); +} + +void fATSC(void *arg) +{ + AT_DBG_MSG(AT_FLAG_OTA, AT_DBG_ALWAYS, "[ATSC]: _AT_SYSTEM_CLEAR_OTA_SIGNATURE_"); + sys_clear_ota_signature(); +} + +void fATSR(void *arg) +{ + AT_DBG_MSG(AT_FLAG_OTA, AT_DBG_ALWAYS, "[ATSR]: _AT_SYSTEM_RECOVER_OTA_SIGNATURE_"); + sys_recover_ota_signature(); +} + +void fATSP(void *arg) +{ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + + unsigned long timeout; // ms + unsigned long time_begin, time_current; + + gpio_t gpiob_1; + int val_old, val_new; + + int expected_zerocount, zerocount; + int test_result; + + // parameter check + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: _AT_SYSTEM_POWER_PIN_TEST_"); + if(!arg) { + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: Usage: ATSP=gpiob1[timeout,zerocount]"); + } else { + argc = parse_param(arg, argv); + if (argc < 2) { + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: Usage: ATSP=gpiob1[timeout,zerocount]"); + return; + } + } + + if ( strcmp(argv[1], "gpiob1" ) == 0 ) { + if (argc < 4) { + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: Usage: ATSP=gpiob1[timeout,zerocount]"); + return; + } + + // init gpiob1 test + test_result = 0; + timeout = strtoul(argv[2], NULL, 10); + expected_zerocount = atoi(argv[3]); + zerocount = 0; + val_old = 1; + + sys_log_uart_off(); + + gpio_init(&gpiob_1, PB_1); + gpio_dir(&gpiob_1, PIN_INPUT); + gpio_mode(&gpiob_1, PullDown); + + // gpiob1 test ++ + time_begin = time_current = xTaskGetTickCount(); + while (time_current < time_begin + timeout) { + val_new = gpio_read(&gpiob_1); + + if (val_new != val_old && val_new == 0) { + + zerocount ++; + if (zerocount == expected_zerocount) { + test_result = 1; + break; + } + } + + val_old = val_new; + time_current = xTaskGetTickCount(); + } + // gpio test -- + + sys_log_uart_on(); + + if (test_result == 1) { + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: success"); + } else { + AT_DBG_MSG(AT_FLAG_GPIO, AT_DBG_ALWAYS, "[ATSP]: fail, it only got %d zeros", zerocount); + } + } +} +#endif + +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) +void fATSL(void *arg) +{ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + + uint32_t lock_id; + + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL]: _AT_SYS_WAKELOCK_TEST_"); + + if (!arg) { + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] Usage ATSL=[a/r/?][bitmask]"); + return; + } else { + argc = parse_param(arg, argv); + if (argc < 2) { + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] Usage ATSL=[a/r/?][bitmask]"); + return; + } + } + + switch(argv[1][0]) { + case 'a': // acquire + { + if (argc == 3) { + lock_id = strtoul(argv[2], NULL, 16); + acquire_wakelock(lock_id); + } + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] wakelock:0x%08x", get_wakelock_status()); + break; + } + + case 'r': // release + { + if (argc == 3) { + lock_id = strtoul(argv[2], NULL, 16); + release_wakelock(lock_id); + } + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] wakelock:0x%08x", get_wakelock_status()); + break; + } + + case '?': // get status + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] wakelock:0x%08x", get_wakelock_status()); + break; + + default: + AT_DBG_MSG(AT_FLAG_OS, AT_DBG_ALWAYS, "[ATSL] Usage ATSL=[a/r/?][bitmask]"); + break; + } +} +#endif + +void fATSs(void *arg) +{ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + + AT_PRINTK("[ATS@]: _AT_SYSTEM_DBG_SETTING_"); + if(!arg){ + AT_PRINTK("[ATS@] Usage: ATS@=[LEVLE,FLAG]"); + }else{ + argc = parse_param(arg, argv); + if(argc == 3){ + char *ptmp; + gDbgLevel = atoi(argv[1]); + gDbgFlag = strtoul(argv[2], &ptmp, 16); + } + } + AT_PRINTK("[ATS@] level = %d, flag = 0x%08X", gDbgLevel, gDbgFlag); +} + +void fATSc(void *arg) +{ + int argc = 0, config = 0; + char *argv[MAX_ARGC] = {0}; + + AT_PRINTK("[ATS!]: _AT_SYSTEM_CONFIG_SETTING_"); + if(!arg){ + AT_PRINTK("[ATS!] Usage: ATS!=[CONFIG(0,1,2),FLAG]"); + }else{ + argc = parse_param(arg, argv); + if(argc == 3){ + char *ptmp; + config = atoi(argv[1]); + gDbgFlag = strtoul(argv[2], &ptmp, 16); + if(config == 0) + ConfigDebugErr = strtoul(argv[2], &ptmp, 16); + if(config == 1) + ConfigDebugInfo = strtoul(argv[2], &ptmp, 16); + if(config == 2) + ConfigDebugWarn = strtoul(argv[2], &ptmp, 16); + } + } + AT_PRINTK("[ATS!] ConfigDebugErr = 0x%08X", ConfigDebugErr); + AT_PRINTK("[ATS!] ConfigDebugInfo = 0x%08X", ConfigDebugInfo); + AT_PRINTK("[ATS!] ConfigDebugWarn = 0x%08X", ConfigDebugWarn); +} + +void fATSt(void *arg) +{ + AT_PRINTK("[ATS#]: _AT_SYSTEM_TEST_"); +} + +void fATSx(void *arg) +{ + AT_PRINTK("[ATS?]: _AT_SYSTEM_HELP_"); + AT_PRINTK("[ATS?]: COMPILE TIME: %s", RTL8195AFW_COMPILE_TIME); +} + + + +void fATGR(void *arg) { + + u32 val; + + printf("=== GPIO_REG_BASE ===\n"); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + gpio_t gpio; + + printf("PA_0\n"); + + printf("gpio_init\n"); + gpio_init(&gpio, PA_0); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + printf("gpio_dir PIN_OUTPUT\n"); + gpio_dir(&gpio, PIN_OUTPUT); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + printf("gpio_mode PullNone\n"); + gpio_mode(&gpio, PullNone); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + printf("gpio_write 1\n"); + gpio_write(&gpio, 1); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + printf("gpio_write 0\n"); + gpio_write(&gpio, 0); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + printf("gpio_write 1\n"); + HAL_WRITE32(GPIO_REG_BASE, GPIO_PORTA_DR, 0x00000001); + + printf("GPIO_PORTA_DR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DR)); + printf("GPIO_PORTA_DDR = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_DDR)); + printf("GPIO_PORTA_CTRL = 0x%08x\n", HAL_READ32(GPIO_REG_BASE, GPIO_PORTA_CTRL)); + + + +} + + + +log_item_t at_sys_items[] = { + {"ATSD", fATSD,}, // Dump register + {"ATSE", fATSE,}, // Edit register +#if SUPPORT_MP_MODE + {"ATSA", fATSA,}, // MP ADC test + {"ATSG", fATSG,}, // MP GPIO test + {"ATSC", fATSC,}, // Clear OTA signature + {"ATSR", fATSR,}, // Recover OTA signature + {"ATSP", fATSP,}, // MP Power related test +#endif +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) + {"ATSL", fATSL,}, // wakelock test +#endif + {"ATS@", fATSs,}, // Debug message setting + {"ATS!", fATSc,}, // Debug config setting + {"ATS#", fATSt,}, // test command + {"ATS?", fATSx,}, // Help + + + + {"ATGR", fATGR,}, // gpio register test + + + +}; + +void at_sys_init(void) +{ + log_service_add_table(at_sys_items, sizeof(at_sys_items)/sizeof(at_sys_items[0])); +} + +log_module_init(at_sys_init); diff --git a/component/common/api/at_cmd/atcmd_sys.h b/component/common/api/at_cmd/atcmd_sys.h new file mode 100644 index 0000000..3a1cf7a --- /dev/null +++ b/component/common/api/at_cmd/atcmd_sys.h @@ -0,0 +1,5 @@ +#ifndef __ATCMD_SYS_H__ +#define __ATCMD_SYS_H__ + + +#endif diff --git a/component/common/api/at_cmd/atcmd_wifi.c b/component/common/api/at_cmd/atcmd_wifi.c new file mode 100644 index 0000000..4675ae1 --- /dev/null +++ b/component/common/api/at_cmd/atcmd_wifi.c @@ -0,0 +1,979 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "log_service.h" +#include "atcmd_wifi.h" +#include +#include "tcpip.h" +#include +#include +#include +#include +#if SUPPORT_LOG_SERVICE +/******************************************************************************/ +#define _AT_WLAN_SET_SSID_ "ATW0" +#define _AT_WLAN_SET_PASSPHRASE_ "ATW1" +#define _AT_WLAN_SET_KEY_ID_ "ATW2" +#define _AT_WLAN_AP_SET_SSID_ "ATW3" +#define _AT_WLAN_AP_SET_SEC_KEY_ "ATW4" +#define _AT_WLAN_AP_SET_CHANNEL_ "ATW5" +#define _AT_WLAN_AP_ACTIVATE_ "ATWA" +#define _AT_WLAN_AP_STA_ACTIVATE_ "ATWB" +#define _AT_WLAN_JOIN_NET_ "ATWC" +#define _AT_WLAN_DISC_NET_ "ATWD" +#define _AT_WLAN_WEB_SERVER_ "ATWE" +#define _AT_WLAN_PING_TEST_ "ATWI" +#define _AT_WLAN_SSL_CLIENT_ "ATWL" +#define _AT_WLAN_WIFI_PROMISC_ "ATWM" +#define _AT_WLAN_POWER_ "ATWP" +#define _AT_WLAN_SIMPLE_CONFIG_ "ATWQ" +#define _AT_WLAN_GET_RSSI_ "ATWR" +#define _AT_WLAN_SCAN_ "ATWS" +#define _AT_WLAN_TCP_TEST_ "ATWT" +#define _AT_WLAN_UDP_TEST_ "ATWU" +#define _AT_WLAN_WPS_ "ATWW" +#define _AT_WLAN_IWPRIV_ "ATWZ" +#define _AT_WLAN_INFO_ "ATW?" + +#ifndef CONFIG_SSL_CLIENT +#define CONFIG_SSL_CLIENT 0 +#endif +#ifndef CONFIG_WEBSERVER +#define CONFIG_WEBSERVER 0 +#endif +#ifndef CONFIG_OTA_UPDATE +#define CONFIG_OTA_UPDATE 0 +#endif +#ifndef CONFIG_BSD_TCP +#define CONFIG_BSD_TCP 1 +#endif +#ifndef CONFIG_ENABLE_P2P +#define CONFIG_ENABLE_P2P 0 +#endif +#define SCAN_WITH_SSID 0 +#if CONFIG_WEBSERVER +#define CONFIG_READ_FLASH 1 +extern rtw_wifi_setting_t wifi_setting; +#endif +#if CONFIG_WLAN + +extern void cmd_promisc(int argc, char **argv); +extern void cmd_update(int argc, char **argv); +extern void cmd_tcp(int argc, char **argv); +extern void cmd_udp(int argc, char **argv); +extern void cmd_simple_config(int argc, char **argv); +#if CONFIG_ENABLE_WPS +extern void cmd_wps(int argc, char **argv); +#endif +extern void cmd_ssl_client(int argc, char **argv); +#ifdef CONFIG_WPS_AP +extern void cmd_ap_wps(int argc, char **argv); +extern int wpas_wps_dev_config(u8 *dev_addr, u8 bregistrar); +#endif //CONFIG_WPS_AP +#if CONFIG_ENABLE_P2P +extern void cmd_wifi_p2p_start(int argc, char **argv); +extern void cmd_wifi_p2p_stop(int argc, char **argv); +extern void cmd_p2p_listen(int argc, char **argv); +extern void cmd_p2p_find(int argc, char **argv); +extern void cmd_p2p_peers(int argc, char **argv); +extern void cmd_p2p_info(int argc, char **argv); +extern void cmd_p2p_disconnect(int argc, char **argv); +extern void cmd_p2p_connect(int argc, char **argv); +#endif //CONFIG_ENABLE_P2P +#if CONFIG_LWIP_LAYER +extern struct netif xnetif[NET_IF_NUM]; +#endif +static rtw_network_info_t wifi = {0}; +static rtw_ap_info_t ap = {0}; +static unsigned char password[65] = {0}; +static void init_wifi_struct(void) +{ + memset(wifi.ssid.val, 0, sizeof(wifi.ssid.val)); + memset(wifi.bssid.octet, 0, ETH_ALEN); + memset(password, 0, sizeof(password)); + wifi.ssid.len = 0; + wifi.password = NULL; + wifi.password_len = 0; + wifi.key_id = -1; + memset(ap.ssid.val, 0, sizeof(ap.ssid.val)); + ap.ssid.len = 0; + ap.password = NULL; + ap.password_len = 0; + ap.channel = 1; +} + +static void print_scan_result( rtw_scan_result_t* record ) +{ + RTW_API_INFO( ( "%s\t ", ( record->bss_type == RTW_BSS_TYPE_ADHOC ) ? "Adhoc" : "Infra" ) ); + RTW_API_INFO( ( MAC_FMT, MAC_ARG(record->BSSID.octet) ) ); + RTW_API_INFO( ( " %d\t ", record->signal_strength ) ); + RTW_API_INFO( ( " %d\t ", record->channel ) ); + RTW_API_INFO( ( " %d\t ", record->wps_type ) ); + RTW_API_INFO( ( "%s\t\t ", ( record->security == RTW_SECURITY_OPEN ) ? "Open" : + ( record->security == RTW_SECURITY_WEP_PSK ) ? "WEP" : + ( record->security == RTW_SECURITY_WPA_TKIP_PSK ) ? "WPA TKIP" : + ( record->security == RTW_SECURITY_WPA_AES_PSK ) ? "WPA AES" : + ( record->security == RTW_SECURITY_WPA2_AES_PSK ) ? "WPA2 AES" : + ( record->security == RTW_SECURITY_WPA2_TKIP_PSK ) ? "WPA2 TKIP" : + ( record->security == RTW_SECURITY_WPA2_MIXED_PSK ) ? "WPA2 Mixed" : + "Unknown" ) ); + + RTW_API_INFO( ( " %s ", record->SSID.val ) ); + RTW_API_INFO( ( "\r\n" ) ); +} + +static rtw_result_t app_scan_result_handler( rtw_scan_handler_result_t* malloced_scan_result ) +{ + static int ApNum = 0; + + if (malloced_scan_result->scan_complete != RTW_TRUE) { + rtw_scan_result_t* record = &malloced_scan_result->ap_details; + record->SSID.val[record->SSID.len] = 0; /* Ensure the SSID is null terminated */ + + RTW_API_INFO( ( "%d\t ", ++ApNum ) ); + + print_scan_result(record); + } else{ + ApNum = 0; + } + return RTW_SUCCESS; +} + +void fATW0(void *arg){ + if(!arg){ + printf("[ATW0]Usage: ATW0=SSID\n\r"); + return; + } + printf("[ATW0]: _AT_WLAN_SET_SSID_ [%s]\n\r", (char*)arg); + strcpy((char *)wifi.ssid.val, (char*)arg); + wifi.ssid.len = strlen((char*)arg); +} + +void fATW1(void *arg){ + printf("[ATW1]: _AT_WLAN_SET_PASSPHRASE_ [%s]\n\r", (char*)arg); + strcpy((char *)password, (char*)arg); + wifi.password = password; + wifi.password_len = strlen((char*)arg); + + /* + * note:rtw_wx_set_passphrase will clear psecuritypriv->wpa_passphrase when len=64 + * so psk_derive will return because passphrase[0] == 0, then psk_passphrase + * will not be set. Here set psk_passphrase64 to make restore_wifi_info_to_flash working + * + */ + extern unsigned char psk_passphrase64[64 + 1]; + if (wifi.password_len == 64) + strcpy(psk_passphrase64, wifi.password); +} + +void fATW2(void *arg){ + printf("[ATW2]: _AT_WLAN_SET_KEY_ID_ [%s]\n\r", (char*)arg); + if((strlen((const char *)arg) != 1 ) || (*(char*)arg <'0' ||*(char*)arg >'3')) { + printf("\n\rWrong WEP key id. Must be one of 0,1,2, or 3."); + return; + } + wifi.key_id = atoi((const char *)(arg)); +} + +void fATW3(void *arg){ + if(!arg){ + printf("[ATW3]Usage: ATW3=SSID\n\r"); + return; + } + + ap.ssid.len = strlen((char*)arg); + + if(ap.ssid.len > 32){ + printf("[ATW3]Error: SSID length can't exceed 32\n\r"); + return; + } + strcpy((char *)ap.ssid.val, (char*)arg); + + printf("[ATW3]: _AT_WLAN_AP_SET_SSID_ [%s]\n\r", ap.ssid.val); +} + +void fATW4(void *arg){ + strcpy((char *)password, (char*)arg); + ap.password = password; + ap.password_len = strlen((char*)arg); + printf("[ATW4]: _AT_WLAN_AP_SET_SEC_KEY_ [%s]\n\r", ap.password); +} + +void fATW5(void *arg){ + ap.channel = (unsigned char) atoi((const char *)arg); + printf("[ATW5]: _AT_WLAN_AP_SET_CHANNEL_ [channel %d]\n\r", ap.channel); +} + +void fATW6(void *arg){ + u32 mac[ETH_ALEN]; + u32 i; + if(!arg){ + printf("[ATW6]Usage: ATW6=BSSID\n\r"); + return; + } + printf("[ATW6]: _AT_WLAN_SET_BSSID_ [%s]\n\r", (char*)arg); + sscanf(arg, MAC_FMT, mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5); + for(i = 0; i < ETH_ALEN; i ++) + wifi.bssid.octet[i] = (u8)mac[i] & 0xFF; +} + +void fATWA(void *arg){ +#if CONFIG_LWIP_LAYER + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + struct netif * pnetif = &xnetif[0]; +#endif + int timeout = 20; + printf("[ATWA]: _AT_WLAN_AP_ACTIVATE_\n\r"); + if(ap.ssid.val[0] == 0){ + printf("[ATWA]Error: SSID can't be empty\n\r"); + return; + } + if(ap.password == NULL){ + ap.security_type = RTW_SECURITY_OPEN; + } + else{ + ap.security_type = RTW_SECURITY_WPA2_AES_PSK; + } + +#if CONFIG_WEBSERVER + //store into flash + memset(wifi_setting.ssid, 0, sizeof(wifi_setting.ssid));; + memcpy(wifi_setting.ssid, ap.ssid.val, strlen((char*)ap.ssid.val)); + wifi_setting.ssid[ap.ssid.len] = '\0'; + wifi_setting.security_type = ap.security_type; + if(ap.security_type !=0) + wifi_setting.security_type = 1; + else + wifi_setting.security_type = 0; + if (ap.password) + memcpy(wifi_setting.password, ap.password, strlen((char*)ap.password)); + else + memset(wifi_setting.password, 0, sizeof(wifi_setting.password));; + wifi_setting.channel = ap.channel; +#if CONFIG_READ_FLASH + StoreApInfo(); +#endif +#endif + +#if CONFIG_LWIP_LAYER + dhcps_deinit(); + IP4_ADDR(&ipaddr, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + netif_set_addr(pnetif, &ipaddr, &netmask,&gw); +#ifdef CONFIG_DONT_CARE_TP + pnetif->flags |= NETIF_FLAG_IPSWITCH; +#endif +#endif + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_AP) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } + printf("\n\rStarting AP ..."); + +#ifdef CONFIG_WPS_AP + wpas_wps_dev_config(pnetif->hwaddr, 1); +#endif + if(wifi_start_ap((char*)ap.ssid.val, ap.security_type, (char*)ap.password, ap.ssid.len, ap.password_len, ap.channel) < 0) { + printf("\n\rERROR: Operation failed!"); + return; + } + + while(1) { + char essid[33]; + + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) > 0) { + if(strcmp((const char *) essid, (const char *)ap.ssid.val) == 0) { + printf("\n\r%s started\n", ap.ssid.val); + break; + } + } + + if(timeout == 0) { + printf("\n\rERROR: Start AP timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } +#if CONFIG_LWIP_LAYER + //LwIP_UseStaticIP(pnetif); + dhcps_init(pnetif); +#endif + + init_wifi_struct( ); +} +void fATWC(void *arg){ + int mode, ret; + unsigned long tick1 = xTaskGetTickCount(); + unsigned long tick2, tick3; + char empty_bssid[6] = {0}, assoc_by_bssid = 0; + + printf("[ATWC]: _AT_WLAN_JOIN_NET_\n\r"); + if(memcmp (wifi.bssid.octet, empty_bssid, 6)) + assoc_by_bssid = 1; + else if(wifi.ssid.val[0] == 0){ + printf("[ATWC]Error: SSID can't be empty\n\r"); + goto EXIT; + } + if(wifi.password != NULL){ + if((wifi.key_id >= 0)&&(wifi.key_id <= 3)) { + wifi.security_type = RTW_SECURITY_WEP_PSK; + } + else{ + wifi.security_type = RTW_SECURITY_WPA2_AES_PSK; + } + } + else{ + wifi.security_type = RTW_SECURITY_OPEN; + } + //Check if in AP mode + wext_get_mode(WLAN0_NAME, &mode); + if(mode == IW_MODE_MASTER) { +#if CONFIG_LWIP_LAYER + dhcps_deinit(); +#endif + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_STA) < 0){ + printf("\n\rERROR: Wifi on failed!"); + goto EXIT; + } + } + if(assoc_by_bssid){ + printf("\n\rJoining BSS by BSSID "MAC_FMT" ...\n\r", MAC_ARG(wifi.bssid.octet)); + ret = wifi_connect_bssid(wifi.bssid.octet, (char*)wifi.ssid.val, wifi.security_type, (char*)wifi.password, + ETH_ALEN, wifi.ssid.len, wifi.password_len, wifi.key_id, NULL); + } else { + printf("\n\rJoining BSS by SSID %s...\n\r", (char*)wifi.ssid.val); + ret = wifi_connect((char*)wifi.ssid.val, wifi.security_type, (char*)wifi.password, wifi.ssid.len, + wifi.password_len, wifi.key_id, NULL); + } + + if(ret!= RTW_SUCCESS){ + printf("\n\rERROR: Operation failed!"); + goto EXIT; + } + tick2 = xTaskGetTickCount(); + printf("\r\nConnected after %dms.\n", (tick2-tick1)); +#if CONFIG_LWIP_LAYER + /* Start DHCPClient */ + LwIP_DHCP(0, DHCP_START); + tick3 = xTaskGetTickCount(); + printf("\r\n\nGot IP after %dms.\n", (tick3-tick1)); +#endif + printf("\n\r"); +EXIT: + init_wifi_struct( ); +} + +void fATWD(void *arg){ + int timeout = 20; + char essid[33]; + printf("[ATWD]: _AT_WLAN_DISC_NET_\n\r"); + printf("\n\rDeassociating AP ..."); + + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) < 0) { + printf("\n\rWIFI disconnected"); + return; + } + + if(wifi_disconnect() < 0) { + printf("\n\rERROR: Operation failed!"); + return; + } + + while(1) { + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) < 0) { + printf("\n\rWIFI disconnected"); + break; + } + + if(timeout == 0) { + printf("\n\rERROR: Deassoc timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } + printf("\n\r"); + init_wifi_struct( ); +} + +void fATWS(void *arg){ + char buf[32] = {0}; + u8 *channel_list = NULL; + u8 *pscan_config = NULL; + int num_channel = 0; + int i, argc = 0; + char *argv[MAX_ARGC] = {0}; + printf("[ATWS]: _AT_WLAN_SCAN_\n\r"); + if(arg){ + strcpy(buf, arg); + argc = parse_param(buf, argv); + if(argc < 2) + goto exit; + num_channel = atoi(argv[1]); + channel_list = (u8*)malloc(num_channel); + if(!channel_list){ + printf("[ATWS]ERROR: Can't malloc memory for channel list\n\r"); + goto exit; + } + pscan_config = (u8*)malloc(num_channel); + if(!pscan_config){ + printf("[ATWS]ERROR: Can't malloc memory for pscan_config\n\r"); + goto exit; + } + //parse command channel list + for(i = 2; i <= argc -1 ; i++){ + *(channel_list + i - 2) = (u8)atoi(argv[i]); + *(pscan_config + i - 2) = PSCAN_ENABLE; + } + + if(wifi_set_pscan_chan(channel_list, pscan_config, num_channel) < 0){ + printf("[ATWS]ERROR: wifi set partial scan channel fail\n\r"); + goto exit; + } + } + + if(wifi_scan_networks(app_scan_result_handler, NULL ) != RTW_SUCCESS){ + printf("[ATWS]ERROR: wifi scan failed\n\r"); + goto exit; + } +exit: + if(arg && channel_list) + free(channel_list); +} + +#if SCAN_WITH_SSID +void fATWs(void *arg){ + char buf[32] = {0}; + u8 *channel_list = NULL; + int scan_buf_len = 500; + int num_channel = 0; + int i, argc = 0; + char *argv[MAX_ARGC] = {0}; + printf("[ATWs]: _AT_WLAN_SCAN_WITH_SSID_ [%s]\n\r", (char*)wifi.ssid.val); + if(arg){ + strcpy(buf, arg); + argc = parse_param(buf, argv); + if(argc == 2){ + scan_buf_len = atoi(argv[1]); + }else if(argc > 2){ + num_channel = atoi(argv[1]); + channel_list = (u8*)malloc(num_channel); + if(!channel_list){ + printf("[ATWS]ERROR: Can't malloc memory for channel list\n\r"); + goto exit; + } + //parse command channel list + for(i = 2; i <= argc -1 ; i++){ + *(channel_list + i - 2) = (u8)atoi(argv[i]); + } + + if(wifi_set_pscan_chan(channel_list, num_channel) < 0){ + printf("[ATWS]ERROR: wifi set partial scan channel fail\n\r"); + goto exit; + } + } + }else{ + printf("[ATWs]For Scan all channel Usage: ATWs=BUFFER_LENGTH\n\r"); + printf("[ATWs]For Scan partial channel Usage: ATWs=num_channels[channel_num1, ...]\n\r"); + goto exit; + } + + if(wifi_scan_networks_with_ssid(NULL, &scan_buf_len, wifi.ssid.val, wifi.ssid.len) != RTW_SUCCESS){ + printf("[ATWS]ERROR: wifi scan failed\n\r"); + } +exit: + if(arg && channel_list) + free(channel_list); +} +#endif + +void fATWR(void *arg){ + int rssi = 0; + printf("[ATWR]: _AT_WLAN_GET_RSSI_\n\r"); + wifi_get_rssi(&rssi); + printf("\n\rwifi_get_rssi: rssi = %d", rssi); + printf("\n\r"); +} + +void fATWP(void *arg){ + unsigned int parm = atoi((const char *)(arg)); + printf("[ATWP]: _AT_WLAN_POWER_[%s]\n\r", parm?"ON":"OFF"); + if(parm == 1){ + if(wifi_on(RTW_MODE_STA)<0){ + printf("\n\rERROR: Wifi on failed!\n"); + } + } + else if(parm == 0) + { +#if CONFIG_WEBSERVER + stop_web_server(); +#endif + wifi_off(); + } + else + printf("[ATWP]Usage: ATWP=0/1\n\r"); +} +#ifdef CONFIG_CONCURRENT_MODE +void fATWB(void *arg) +{ + int timeout = 20;//, mode; +#if CONFIG_LWIP_LAYER + struct netif * pnetiff = (struct netif *)&xnetif[1]; +#endif + printf("[ATWB](_AT_WLAN_AP_STA_ACTIVATE_)\n\r"); + if(ap.ssid.val[0] == 0){ + printf("[ATWB]Error: SSID can't be empty\n\r"); + return; + } + if(ap.channel > 14){ + printf("[ATWB]Error:bad channel! channel is from 1 to 14\n\r"); + return; + } + if(ap.password == NULL){ + ap.security_type = RTW_SECURITY_OPEN; + } + else{ + ap.security_type = RTW_SECURITY_WPA2_AES_PSK; + } + +#if CONFIG_LWIP_LAYER + dhcps_deinit(); +#endif + + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_STA_AP) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } + + printf("\n\rStarting AP ..."); + if(wifi_start_ap((char*)ap.ssid.val, ap.security_type, (char*)ap.password, ap.ssid.len, ap.password_len, ap.channel) < 0) { + printf("\n\rERROR: Operation failed!"); + return; + } + while(1) { + char essid[33]; + + if(wext_get_ssid(WLAN1_NAME, (unsigned char *) essid) > 0) { + if(strcmp((const char *) essid, (const char *)ap.ssid.val) == 0) { + printf("\n\r%s started\n", ap.ssid.val); + break; + } + } + + if(timeout == 0) { + printf("\n\rERROR: Start AP timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } +#if CONFIG_LWIP_LAYER + LwIP_UseStaticIP(&xnetif[1]); +#ifdef CONFIG_DONT_CARE_TP + pnetiff->flags |= NETIF_FLAG_IPSWITCH; +#endif + dhcps_init(pnetiff); +#endif +} +#endif + +void fATWI(void *arg){ + int count, argc = 0; + char buf[32] = {0}; + char *argv[MAX_ARGC] = {0}; + printf("[ATWI]: _AT_WLAN_PING_TEST_\n\r"); + if(!arg){ + printf("[ATWI]Usage: ATWI=xxxx.xxxx.xxxx.xxxx[y/loop]\n\r"); + return; + } + strcpy(buf, arg); + argv[0] = "ping"; + argc = parse_param(buf, argv); + printf("[ATWI]Target address: %s\n\r", argv[1]); + if(argc == 2){ + printf("[ATWI]Repeat Count: 5\n\r"); + do_ping_call(argv[1], 0, 5); //Not loop, count=5 + }else{ + if(strcmp(argv[2], "loop") == 0){ + printf("[ATWI]Repeat Count: %s\n\r", "loop"); + do_ping_call(argv[1], 1, 0); //loop, no count + } + else{ + count = atoi(argv[2]); + printf("[ATWI]Repeat Count: %d\n\r", count); + do_ping_call(argv[1], 0, count); //Not loop, with count + } + } +} + +#if CONFIG_BSD_TCP +void fATWT(void *arg){ + int argc; + char *argv[MAX_ARGC] = {0}; + printf("[ATWT]: _AT_WLAN_TCP_TEST_\n\r"); + if(!arg){ + printf("[ATWT]Usage: ATWT=[-c/c,xxxx.xxxx.xxxx.xxxx,buf_len,count] or ATWT=[-s/s] or ATWT=[stop]\n\r"); + return; + } + argv[0] = "tcp"; + if((argc = parse_param(arg, argv)) > 1){ + cmd_tcp(argc, argv); + } + else + printf("[ATWT]Usage: ATWT=[-c/c,xxxx.xxxx.xxxx.xxxx,buf_len,count] or ATWT=[-s/s] or ATWT=[stop]\n\r"); +} + +void fATWU(void *arg){ + int argc; + char *argv[MAX_ARGC] = {0}; + printf("[ATWU]: _AT_WLAN_UDP_TEST_\n\r"); + if(!arg){ + printf("[ATWU]Usage: ATWU=[-c/c,xxxx.xxxx.xxxx.xxxx,buf_len,count] or ATWU=[-s/s] or ATWU=[stop]\n\r"); + return; + } + argv[0] = "ucp"; + if((argc = parse_param(arg, argv)) > 1){ + cmd_udp(argc, argv); + } + else + printf("[ATWU]Usage: ATWU=[-c/c,xxxx.xxxx.xxxx.xxxx,buf_len,count] or ATWU=[-s/s] or ATWU=[stop]\n\r"); +} +#endif + +void fATWM(void *arg){ + int argc; + char *argv[MAX_ARGC] = {0}; + argv[0] = "wifi_promisc"; + printf("[ATWM]: _AT_WLAN_PROMISC_\n\r"); + if(!arg){ + printf("[ATWM]Usage: ATWM=DURATION_SECONDS[with_len]"); + return; + } + if((argc = parse_param(arg, argv)) > 1){ + cmd_promisc(argc, argv); + } +} + +void fATWL(void *arg){ + int argc; + char *argv[MAX_ARGC] = {0}; + printf("[ATWL]: _AT_WLAN_SSL_CLIENT_\n\r"); + argv[0] = "ssl_client"; + if(!arg){ + printf("ATWL=SSL_SERVER_HOST[SRP_USER_NAME,SRP_PASSWORD]\n\r"); + return; + } + if((argc = parse_param(arg, argv)) > 1){ + cmd_ssl_client(argc, argv); + } +} + + +#if CONFIG_WEBSERVER +void fATWE(void *arg){ + printf("[ATWE]: _AT_WLAN_START_WEB_SERVER_\n\r"); + start_web_server(); +} +#endif + +void fATWQ(void *arg){ + int argc=0; + char *argv[2] = {0}; + printf("[ATWQ]: _AT_WLAN_SIMPLE_CONFIG_\n\r"); + argv[argc++] = "wifi_simple_config"; + if(arg){ + argv[argc++] = arg; + } + cmd_simple_config(argc, argv); +} +#if CONFIG_ENABLE_WPS +void fATWW(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWW]: _AT_WLAN_WPS_\n\r"); + if(!arg){ + printf("[ATWW]Usage: ATWW=pbc/pin\n\r"); + return; + } + argv[argc++] = "wifi_wps"; + argv[argc++] = arg; + cmd_wps(argc, argv); +} +#endif +void fATWw(void *arg){ +#ifdef CONFIG_WPS_AP + int argc = 0; + char *argv[4]; + printf("[ATWw]: _AT_WLAN_AP_WPS_\n\r"); + if(!arg){ + printf("[ATWw]Usage: ATWw=pbc/pin\n\r"); + return; + } + argv[argc++] = "wifi_ap_wps"; + argv[argc++] = arg; + cmd_ap_wps(argc, argv); +#endif +} + +#if CONFIG_ENABLE_P2P +void fATWG(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWa]: _AT_WLAN_P2P_START_\n\r"); + argv[argc++] = "p2p_start"; + cmd_wifi_p2p_start(argc, argv); +} +void fATWH(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWb]: _AT_WLAN_P2P_STOP_\n\r"); + argv[argc++] = "p2p_stop"; + cmd_wifi_p2p_stop(argc, argv); +} +void fATWJ(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWc]: _AT_WLAN_P2P_CONNECT_\n\r"); + argv[0] = "p2p_connect"; + if(!arg){ + printf("ATWc=[DEST_MAC,pbc/pin]\n\r"); + return; + } + if((argc = parse_param(arg, argv)) > 1){ + cmd_p2p_connect(argc, argv); + } +} +void fATWK(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWd]: _AT_WLAN_P2P_DISCONNECT_\n\r"); + argv[argc++] = "p2p_disconnect"; + cmd_p2p_disconnect(argc, argv); +} +void fATWN(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWe]: _AT_WLAN_P2P_INFO_\n\r"); + argv[argc++] = "p2p_info"; + cmd_p2p_info(argc, argv); +} +void fATWF(void *arg){ + int argc = 0; + char *argv[4]; + printf("[ATWf]: _AT_WLAN_P2P_FIND_\n\r"); + argv[argc++] = "p2p_find"; + cmd_p2p_find(argc, argv); +} +#endif +#if CONFIG_OTA_UPDATE +void fATWO(void *arg){ + int argc = 0; + char *argv[MAX_ARGC] = {0}; + printf("[ATWO]: _AT_WLAN_OTA_UPDATE_\n\r"); + if(!arg){ + printf("[ATWO]Usage: ATWO=IP[PORT] or ATWO= REPOSITORY[FILE_PATH]\n\r"); + return; + } + argv[0] = "update"; + if((argc = parse_param(arg, argv)) != 3){ + printf("[ATWO]Usage: ATWO=IP[PORT] or ATWO= REPOSITORY[FILE_PATH]\n\r"); + return; + } + cmd_update(argc, argv); +} +#endif +void fATWx(void *arg){ + int i = 0; +#if CONFIG_LWIP_LAYER + u8 *mac = LwIP_GetMAC(&xnetif[0]); + u8 *ip = LwIP_GetIP(&xnetif[0]); + u8 *gw = LwIP_GetGW(&xnetif[0]); +#endif + u8 *ifname[2] = {WLAN0_NAME,WLAN1_NAME}; + rtw_wifi_setting_t setting; + printf("[ATW?]: _AT_WLAN_INFO_\n\r"); + + for(i=0;i %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) ; + printf("\n\r\tIP => %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + printf("\n\r\tGW => %d.%d.%d.%d\n\r", gw[0], gw[1], gw[2], gw[3]); +#endif + if(setting.mode == RTW_MODE_AP || i == 1) + { + int client_number; + struct { + int count; + rtw_mac_t mac_list[3]; + } client_info; + + client_info.count = 3; + wifi_get_associated_client_list(&client_info, sizeof(client_info)); + + printf("\n\rAssociated Client List:"); + printf("\n\r=============================="); + + if(client_info.count == 0) + printf("\n\rClient Num: 0\n\r", client_info.count); + else + { + printf("\n\rClient Num: %d", client_info.count); + for( client_number=0; client_number < client_info.count; client_number++ ) + { + printf("\n\rClient %d:", client_number + 1); + printf("\n\r\tMAC => "MAC_FMT"", + MAC_ARG(client_info.mac_list[client_number].octet)); + } + printf("\n\r"); + } + } + } + } + +#if defined(configUSE_TRACE_FACILITY) && (configUSE_TRACE_FACILITY == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS == 1) + { + signed char pcWriteBuffer[1024]; + vTaskList((char*)pcWriteBuffer); + printf("\n\rTask List: \n\r%s", pcWriteBuffer); + } +#endif +} + +void fATWZ(void *arg){ + char buf[32] = {0}; + char *copy = buf; + int i = 0; + int len = 0; + printf("[ATWZ]: _AT_WLAN_IWPRIV_\n\r"); + if(!arg){ + printf("[ATWZ]Usage: ATWZ=COMMAND[PARAMETERS]\n\r"); + return; + } + strcpy(copy, arg); + len = strlen(copy); + do{ + if((*(copy+i)=='[')) + *(copy+i)=' '; + if((*(copy+i)==']')||(*(copy+i)=='\0')){ + *(copy+i)='\0'; + break; + } + }while((i++) < len); + wext_private_command(WLAN0_NAME, copy, 1); +} + +void print_wlan_help(void *arg){ + printf("\n\rWLAN AT COMMAND SET:"); + printf("\n\r=============================="); + printf("\n\r1. Wlan Scan for Network Access Point"); + printf("\n\r # ATWS"); + printf("\n\r2. Connect to an AES AP"); + printf("\n\r # ATW0=SSID"); + printf("\n\r # ATW1=PASSPHRASE"); + printf("\n\r # ATWC"); + printf("\n\r3. Create an AES AP"); + printf("\n\r # ATW3=SSID"); + printf("\n\r # ATW4=PASSPHRASE"); + printf("\n\r # ATW5=CHANNEL"); + printf("\n\r # ATWA"); + printf("\n\r4. Ping"); + printf("\n\r # ATWI=xxx.xxx.xxx.xxx"); +} + +log_item_t at_wifi_items[ ] = { + {"ATW0", fATW0,}, + {"ATW1", fATW1,}, + {"ATW2", fATW2,}, + {"ATW3", fATW3,}, + {"ATW4", fATW4,}, + {"ATW5", fATW5,}, + {"ATW6", fATW6,}, + {"ATWA", fATWA,}, +#ifdef CONFIG_CONCURRENT_MODE + {"ATWB", fATWB,}, +#endif + {"ATWC", fATWC,}, + {"ATWD", fATWD,}, + {"ATWP", fATWP,}, + {"ATWR", fATWR,}, + {"ATWS", fATWS,}, +#if SCAN_WITH_SSID + {"ATWs", fATWs,}, +#endif + {"ATWM", fATWM,}, + {"ATWZ", fATWZ,}, +#if CONFIG_OTA_UPDATE + {"ATWO", fATWO,}, +#endif +#if CONFIG_WEBSERVER + {"ATWE", fATWE,}, +#endif +#if (CONFIG_INCLUDE_SIMPLE_CONFIG) + {"ATWQ", fATWQ,}, +#endif +#ifdef CONFIG_WPS +#if CONFIG_ENABLE_WPS + {"ATWW", fATWW,}, +#endif + {"ATWw", fATWw,}, //wps registrar for softap +#if CONFIG_ENABLE_P2P + {"ATWG", fATWG,}, //p2p start + {"ATWH", fATWH,}, //p2p stop + {"ATWJ", fATWJ,}, //p2p connect + {"ATWK", fATWK,}, //p2p disconnect + {"ATWN", fATWN,}, //p2p info + {"ATWF", fATWF,}, //p2p find +#endif +#endif +#if CONFIG_SSL_CLIENT + {"ATWL", fATWL,}, +#endif +#if CONFIG_LWIP_LAYER + {"ATWI", fATWI,}, +#if CONFIG_BSD_TCP + {"ATWT", fATWT,}, + {"ATWU", fATWU,}, +#endif +#endif + {"ATW?", fATWx,}, + {"ATW+ABC", fATWx,} +}; + +void at_wifi_init(void) +{ + init_wifi_struct(); + log_service_add_table(at_wifi_items, sizeof(at_wifi_items)/sizeof(at_wifi_items[0])); +} + +log_module_init(at_wifi_init); + +#endif // end of #if CONFIG_WLAN +#endif //end of #if SUPPORT_LOG_SERVICE diff --git a/component/common/api/at_cmd/atcmd_wifi.h b/component/common/api/at_cmd/atcmd_wifi.h new file mode 100644 index 0000000..8e05312 --- /dev/null +++ b/component/common/api/at_cmd/atcmd_wifi.h @@ -0,0 +1,67 @@ +#ifndef __ATCMD_WIFI_H__ +#define __ATCMD_WIFI_H__ +#include "main.h" +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif +#ifndef WLAN1_NAME + #define WLAN1_NAME "wlan1" +#endif +/* Give default value if not defined */ +#ifndef NET_IF_NUM +#ifdef CONFIG_CONCURRENT_MODE +#define NET_IF_NUM 2 +#else +#define NET_IF_NUM 1 +#endif +#endif + +/*Static IP ADDRESS*/ +#ifndef IP_ADDR0 +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 1 +#define IP_ADDR3 80 +#endif + +/*NETMASK*/ +#ifndef NETMASK_ADDR0 +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef GW_ADDR0 +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 1 +#define GW_ADDR3 1 +#endif + +/*Static IP ADDRESS*/ +#ifndef AP_IP_ADDR0 +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 +#endif + +/*NETMASK*/ +#ifndef AP_NETMASK_ADDR0 +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef AP_GW_ADDR0 +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 +#endif + +#endif diff --git a/component/common/api/at_cmd/log_service.c b/component/common/api/at_cmd/log_service.c new file mode 100644 index 0000000..85e29ab --- /dev/null +++ b/component/common/api/at_cmd/log_service.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include "FreeRTOS.h" +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) +#include "freertos_pmu.h" +#endif +#include "log_service.h" +#include "task.h" +#include "semphr.h" +#include "main.h" +#include "wifi_util.h" + +#if SUPPORT_LOG_SERVICE +//====================================================== +struct list_head log_hash[ATC_INDEX_NUM]; + +extern void at_wifi_init(void); +extern void at_fs_init(void); +extern void at_sys_init(void); +//extern void at_app_init(void); +char log_buf[LOG_SERVICE_BUFLEN]; +#if CONFIG_LOG_HISTORY +char log_history[LOG_HISTORY_LEN][LOG_SERVICE_BUFLEN]; +static unsigned int log_history_count = 0; +#endif +xSemaphoreHandle log_rx_interrupt_sema = NULL; +extern xSemaphoreHandle uart_rx_interrupt_sema; + +#if defined (__ICCARM__) +#pragma section=".data.log_init" + +unsigned int __log_init_begin__; +unsigned int __log_init_end__; +#elif defined ( __CC_ARM ) +//#pragma section=".data.log_init" +log_init_t* __log_init_begin__; +log_init_t* __log_init_end__; +log_init_t log_init_table[] = { + at_wifi_init, + at_fs_init, + at_sys_init +// at_app_init +}; +#else +#error "not implement, add to linker script" +extern unsigned int __log_init_begin__; +extern unsigned int __log_init_end__; +#endif + +//====================================================== +int hash_index(char *str) +{ + unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. + unsigned int hash = 0; + + while (*str) + { + hash = hash * seed + (*str++); + } + + return (hash & 0x7FFFFFFF); +} + +void log_add_new_command(log_item_t *new) +{ + int index = hash_index(new->log_cmd)%ATC_INDEX_NUM; + + list_add(&new->node, &log_hash[index]); +} +void start_log_service(void); +void log_service_init(void) +{ + int i; + +#if defined (__ICCARM__) + log_init_t *log_init_table; + __log_init_begin__ = (unsigned int)__section_begin(".data.log_init"); + __log_init_end__ = (unsigned int)__section_end(".data.log_init"); + log_init_table = (log_init_t *)__log_init_begin__; +#elif defined(__CC_ARM) + __log_init_begin__ = log_init_table; + __log_init_end__ = log_init_table + sizeof(log_init_table); +#else + #error "not implement" +#endif + + + for(i=0;ilog_cmd, cmd) == 0){ + //printf("%s match %s, search cnt %d\n\r", cmd, item->log_cmd, search_cnt); + act = (void*)item->at_act; + break; + } + } + + return act; +} + +void* log_handler(char *cmd) +{ + log_act_t action=NULL; + char buf[100] = {0}; + char *copy=buf; + char *token = NULL; + char *param = NULL; + char tok[5] = {0};//'\0' +#if CONFIG_LOG_HISTORY + strcpy(log_history[((log_history_count++)%LOG_HISTORY_LEN)], log_buf); +#endif + strcpy(copy, cmd); + //token = _strsep(©, "="); + token = strtok(copy, "="); + if(token && (strlen(token) <= 4)) + strcpy(tok, token); + else{ + //printf("\n\rAT Cmd format error!\n"); + return NULL; + }; + //printf(" Command %s \n\r ", tok); + param = strtok(NULL, NULL); + //printf(" Param %s \n\r", param); + action = (log_act_t)log_action(tok); + + if(action){ + action(param); + } + return (void*)action; + +} + +int parse_param(char *buf, char **argv) +{ + int argc = 1; + while((argc < MAX_ARGC) && (*buf != '\0')) { + while((*buf == '[')||(*buf == ' ')) + buf ++; + if((*buf == ']')||(*buf == '\0')) + break; + argv[argc] = buf; + argc ++; + buf ++; + + while((*buf != ',')&&(*buf != '[')&&(*buf != ']')&&(*buf != '\0')) + buf ++; + + while((*buf == ',')||(*buf == '[')||(*buf == ']')) { + *buf = '\0'; + buf ++; + } + } + return argc; +} + +void at_set_debug_level(unsigned char newDbgLevel) +{ + gDbgLevel = newDbgLevel; +} + +void at_set_debug_mask(unsigned int newDbgFlag) +{ + gDbgFlag = newDbgFlag; +} + +#if SUPPORT_INTERACTIVE_MODE +extern char uart_buf[64]; +void legency_interactive_handler(unsigned char argc, unsigned char **argv) +{ +#if 0 //defined(CONFIG_PLATFORM_8195A) + if(argc<1) + { + DiagPrintf("Wrong argument number!\r\n"); + return; + } + + + DiagPrintf("Wlan Normal Mode\n"); + + WlanNormal( argc, argv); +#else + strncpy(uart_buf, log_buf, 63);//uart_buf[64] + xSemaphoreGive(uart_rx_interrupt_sema); +#endif +} +#endif + +#if CONFIG_WLAN +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif +#ifndef WLAN1_NAME + #define WLAN1_NAME "wlan1" +#endif +int mp_commnad_handler(char *cmd) +{ + char buf[64] = {0}; + char *token = NULL; + + strcpy(buf, cmd); + token = strtok(buf, " "); + if(token && (strcmp(buf, "iwpriv") == 0)){ + token = strtok(NULL, ""); + wext_private_command(WLAN0_NAME, token, 1); + return 0; + } + return -1; +} +#endif +void print_help_msg(void){ +#if CONFIG_WLAN +extern void print_wlan_help(void); + print_wlan_help(); +#endif +//add other help message print here +} + +int print_help_handler(char *cmd){ + if(strcmp(cmd, "help") == 0){ + print_help_msg(); + return 0; + } + return -1; +} + +void log_service(void *param) +{ + printf("\n\rStart LOG SERVICE MODE\n\r"); + printf("\n\r# "); + while(1){ + while(xSemaphoreTake(log_rx_interrupt_sema, portMAX_DELAY) != pdTRUE); + if(log_handler((char *)log_buf) == NULL){ +#if CONFIG_WLAN + if(mp_commnad_handler((char *)log_buf) < 0) +#endif + { + #if SUPPORT_INTERACTIVE_MODE + print_help_handler((char *)log_buf); + legency_interactive_handler(NULL, NULL); + continue; + #else + if(print_help_handler((char *)log_buf) < 0){ + printf("\n\runknown command '%s'", log_buf); + } + #endif + } + } + log_buf[0] = '\0'; + printf("\n\r[MEM] After do cmd, available heap %d\n\r", xPortGetFreeHeapSize()); + printf("\r\n\n# "); +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) + release_wakelock(WAKELOCK_LOGUART); +#endif + } +} + +#define STACKSIZE 1280 +void start_log_service(void) +{ + if(xTaskCreate(log_service, (char const*)"log_service", STACKSIZE, NULL, tskIDLE_PRIORITY + 5, NULL) != pdPASS) + printf("\n\r%s xTaskCreate failed", __FUNCTION__); +} +void fAT_exit(void *arg){ + printf("\n\rLeave LOG SERVICE"); + vTaskDelete(NULL); +} +#if CONFIG_LOG_HISTORY +void fAT_log(void *arg){ + int i = 0; + printf("[AT]log history:\n\n\r"); + if(log_history_count > LOG_HISTORY_LEN){ + for(i=0; i<4; i++) + printf(" %s\n\r", log_history[((log_history_count+i)%LOG_HISTORY_LEN)]); + } + else{ + for(i=0; i<(log_history_count-1); i++) + printf(" %s\n\r", log_history[i]); + } +} +#endif +log_item_t at_log_items[ ] = { + {"AT--", fAT_exit,}, +#if CONFIG_LOG_HISTORY + {"AT??", fAT_log,}, +#endif + {"ATxx", fAT_exit,} +}; +void at_log_init(void) +{ + log_service_add_table(at_log_items, sizeof(at_log_items)/sizeof(at_log_items[0])); +} +log_module_init(at_log_init); +#endif \ No newline at end of file diff --git a/component/common/api/at_cmd/log_service.h b/component/common/api/at_cmd/log_service.h new file mode 100644 index 0000000..2a27fee --- /dev/null +++ b/component/common/api/at_cmd/log_service.h @@ -0,0 +1,95 @@ +#ifndef LOG_SERVICE_H +#define LOG_SERVICE_H + +#include "dlist.h" +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ + #if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#include "platform_opts.h" +#endif + +#ifdef __ICCARM__ +#define STRINGIFY(s) #s +#define SECTION(_name) _Pragma( STRINGIFY(location=##_name##)) +#define log_module_init(fn) \ + SECTION(".data.log_init") __root static void* log_##fn = (void*)fn +#elif defined(__CC_ARM) +#define log_module_init(fn) \ +static void* log_##fn __attribute__((section(".data.log_init"))) = (void*)fn; +#define DiagPrintf printf +#else +#error "not implement" +#endif + +#define ATC_INDEX_NUM 32 + +#ifndef SUPPORT_LOG_SERVICE +#define SUPPORT_LOG_SERVICE 1 +#endif + +#if SUPPORT_LOG_SERVICE + +//LOG_SERVICE_BUFLEN: default, only 63 bytes could be used for keeping input +// cmd, the last byte is for string end ('\0'). +#ifndef LOG_SERVICE_BUFLEN +#define LOG_SERVICE_BUFLEN 64 +#endif + +#ifndef CONFIG_LOG_HISTORY +#define CONFIG_LOG_HISTORY 0 +#if CONFIG_LOG_HISTORY +#define LOG_HISTORY_LEN 5 +#endif +#endif //#ifndef CONFIG_LOG_HISTORY + +#ifndef MAX_ARGC +#define MAX_ARGC 6 +#endif + +#define AT_BIT(n) (1< + +/* Give default value if not defined */ +#ifndef NET_IF_NUM +#ifdef CONFIG_CONCURRENT_MODE +#define NET_IF_NUM 2 +#else +#define NET_IF_NUM 1 +#endif +#endif + +/*Static IP ADDRESS*/ +#ifndef IP_ADDR0 +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 1 +#define IP_ADDR3 80 +#endif + +/*NETMASK*/ +#ifndef NETMASK_ADDR0 +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef GW_ADDR0 +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 1 +#define GW_ADDR3 1 +#endif + +/*Static IP ADDRESS*/ +#ifndef AP_IP_ADDR0 +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 +#endif + +/*NETMASK*/ +#ifndef AP_NETMASK_ADDR0 +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef AP_GW_ADDR0 +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 +#endif + +_WEAK void user_wifi_beacon_hdl( char* buf, int buf_len, int flags, void* userdata) { + //printf("Beacon!\n"); +} + +/* Private define ------------------------------------------------------------*/ +#define MAX_DHCP_TRIES 5 + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ + +struct netif xnetif[NET_IF_NUM]; /* network interface structure */ +/* Private functions ---------------------------------------------------------*/ +/** + * @brief Initializes the lwIP stack + * @param None + * @retval None + */ +void LwIP_Init(void) +{ + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + int8_t idx = 0; + /* Create tcp_ip stack thread */ + tcpip_init( NULL, NULL ); + + /* - netif_add(struct netif *netif, struct ip_addr *ipaddr, + struct ip_addr *netmask, struct ip_addr *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use.*/ + //printf("NET_IF_NUM:%d\n\r",NET_IF_NUM); + for(idx=NET_IF_NUM - 1;idx>=0;idx--){ + if(idx==0){ + IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + } + else{ + IP4_ADDR(&ipaddr, AP_IP_ADDR0, AP_IP_ADDR1, AP_IP_ADDR2, AP_IP_ADDR3); + IP4_ADDR(&netmask, AP_NETMASK_ADDR0, AP_NETMASK_ADDR1 , AP_NETMASK_ADDR2, AP_NETMASK_ADDR3); + IP4_ADDR(&gw, AP_GW_ADDR0, AP_GW_ADDR1, AP_GW_ADDR2, AP_GW_ADDR3); + } + xnetif[idx].name[0] = 'r'; + xnetif[idx].name[1] = '0'+idx; + netif_add(&xnetif[idx], &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input); + } + + /* Registers the default network interface. */ + netif_set_default(&xnetif[0]); + + /* When the netif is fully configured this function must be called.*/ + for(idx = 0;idx < NET_IF_NUM;idx++) + netif_set_up(&xnetif[idx]); +} + +/** + * @brief LwIP_DHCP_Process_Handle + * @param None + * @retval None + */ +uint8_t LwIP_DHCP(uint8_t idx, uint8_t dhcp_state) +{ + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + uint32_t IPaddress; + uint8_t iptab[4]; + uint8_t DHCP_state; + int mscnt = 0; + struct netif *pnetif = NULL; + + DHCP_state = dhcp_state; + + if(idx > 1) + idx = 1; + pnetif = &xnetif[idx]; + if(DHCP_state == 0){ + pnetif->ip_addr.addr = 0; + pnetif->netmask.addr = 0; + pnetif->gw.addr = 0; + } + + for (;;) + { + //printf("\n\r ========DHCP_state:%d============\n\r",DHCP_state); + switch (DHCP_state) + { + case DHCP_START: + { + wifi_unreg_event_handler(WIFI_EVENT_BEACON_AFTER_DHCP, user_wifi_beacon_hdl); + dhcp_start(pnetif); + IPaddress = 0; + DHCP_state = DHCP_WAIT_ADDRESS; + } + break; + + case DHCP_WAIT_ADDRESS: + { + /* Read the new IP address */ + IPaddress = pnetif->ip_addr.addr; + + if (IPaddress!=0) + { + DHCP_state = DHCP_ADDRESS_ASSIGNED; + + wifi_reg_event_handler(WIFI_EVENT_BEACON_AFTER_DHCP, user_wifi_beacon_hdl, NULL); + + + /* Stop DHCP */ + dhcp_stop(pnetif); + + iptab[0] = (uint8_t)(IPaddress >> 24); + iptab[1] = (uint8_t)(IPaddress >> 16); + iptab[2] = (uint8_t)(IPaddress >> 8); + iptab[3] = (uint8_t)(IPaddress); + printf("\n\rIP address : %d.%d.%d.%d", iptab[3], iptab[2], iptab[1], iptab[0]); + return DHCP_ADDRESS_ASSIGNED; + } + else + { + /* DHCP timeout */ + if (pnetif->dhcp->tries > MAX_DHCP_TRIES) + { + DHCP_state = DHCP_TIMEOUT; + + /* Stop DHCP */ + dhcp_stop(pnetif); + + /* Static address used */ + IP4_ADDR(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 ); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + netif_set_addr(pnetif, &ipaddr , &netmask, &gw); + + iptab[0] = IP_ADDR3; + iptab[1] = IP_ADDR2; + iptab[2] = IP_ADDR1; + iptab[3] = IP_ADDR0; + printf("\n\rDHCP timeout"); + printf("\n\rStatic IP address : %d.%d.%d.%d", iptab[3], iptab[2], iptab[1], iptab[0]); + + return DHCP_TIMEOUT; + }else + { + //sys_msleep(DHCP_FINE_TIMER_MSECS); + vTaskDelay(DHCP_FINE_TIMER_MSECS); + dhcp_fine_tmr(); + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) + { + dhcp_coarse_tmr(); + mscnt = 0; + } + } + } + } + break; + case DHCP_RELEASE_IP: + wifi_unreg_event_handler(WIFI_EVENT_BEACON_AFTER_DHCP, user_wifi_beacon_hdl); + printf("\n\rLwIP_DHCP: Release ip"); + dhcp_release_unicast(pnetif); + return DHCP_RELEASE_IP; + case DHCP_STOP: + wifi_unreg_event_handler(WIFI_EVENT_BEACON_AFTER_DHCP, user_wifi_beacon_hdl); + printf("\n\rLwIP_DHCP: dhcp stop."); + dhcp_stop(pnetif); + return DHCP_STOP; + default: + break; + } + + } +} + +uint8_t* LwIP_GetMAC(struct netif *pnetif) +{ + return (uint8_t *) (pnetif->hwaddr); +} + +uint8_t* LwIP_GetIP(struct netif *pnetif) +{ + return (uint8_t *) &(pnetif->ip_addr); +} + +uint8_t* LwIP_GetGW(struct netif *pnetif) +{ + return (uint8_t *) &(pnetif->gw); +} + +uint8_t* LwIP_GetMASK(struct netif *pnetif) +{ + return (uint8_t *) &(pnetif->netmask); +} + +uint8_t* LwIP_GetBC(struct netif *pnetif) +{ + return (uint8_t *) &(pnetif->dhcp->offered_bc_addr); +} + +#if LWIP_DNS +void LwIP_GetDNS(struct ip_addr* dns) +{ + *dns = dns_getserver(0); +} + +void LwIP_SetDNS(struct ip_addr* dns) +{ + dns_setserver(0, dns); +} +#endif +void LwIP_UseStaticIP(struct netif *pnetif) +{ + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + + /* Static address used */ + if(pnetif->name[1] == '0'){ + IP4_ADDR(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 ); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + }else{ + IP4_ADDR(&ipaddr, AP_IP_ADDR0, AP_IP_ADDR1, AP_IP_ADDR2, AP_IP_ADDR3); + IP4_ADDR(&netmask, AP_NETMASK_ADDR0, AP_NETMASK_ADDR1 , AP_NETMASK_ADDR2, AP_NETMASK_ADDR3); + IP4_ADDR(&gw, AP_GW_ADDR0, AP_GW_ADDR1, AP_GW_ADDR2, AP_GW_ADDR3); + } + + netif_set_addr(pnetif, &ipaddr , &netmask, &gw); +} +#if LWIP_AUTOIP +#include + +void LwIP_AUTOIP(struct netif *pnetif) +{ + uint8_t *ip = LwIP_GetIP(pnetif); + + autoip_start(pnetif); + + while((pnetif->autoip->state == AUTOIP_STATE_PROBING) || (pnetif->autoip->state == AUTOIP_STATE_ANNOUNCING)) { + vTaskDelay(1000); + } + + if(*((uint32_t *) ip) == 0) { + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + + printf("AUTOIP timeout\n"); + + /* Static address used */ + IP4_ADDR(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 ); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + netif_set_addr(pnetif, &ipaddr , &netmask, &gw); + printf("Static IP address : %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + } + else { + printf("\nLink-local address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); + } +} +#endif +#if LWIP_IPV6 +void LwIP_AUTOIP_IPv6(struct netif *pnetif) +{ + uint8_t *ipv6 = (uint8_t *) &(pnetif->ip6_addr[0].addr[0]); + + netif_create_ip6_linklocal_address(pnetif, 1); + netif_ip6_addr_set_state(pnetif, 0, IP6_ADDR_TENTATIVE); + printf("\nIPv6 link-local address: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + ipv6[0], ipv6[1], ipv6[2], ipv6[3], ipv6[4], ipv6[5], ipv6[6], ipv6[7], + ipv6[8], ipv6[9], ipv6[10], ipv6[11], ipv6[12], ipv6[13], ipv6[14], ipv6[15]); +} +#endif diff --git a/component/common/api/lwip_netconf.h b/component/common/api/lwip_netconf.h new file mode 100644 index 0000000..cfec88a --- /dev/null +++ b/component/common/api/lwip_netconf.h @@ -0,0 +1,74 @@ +/** + ****************************************************************************** + * @file netconf.h + * @author MCD Application Team + * @version V1.1.0 + * @date 07-October-2011 + * @brief This file contains all the functions prototypes for the netconf.c + * file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __NETCONF_H +#define __NETCONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "tcpip.h" +/* Includes ------------------------------------------------------------------*/ +#include +/* Private typedef -----------------------------------------------------------*/ +typedef enum +{ + DHCP_START=0, + DHCP_WAIT_ADDRESS, + DHCP_ADDRESS_ASSIGNED, + DHCP_RELEASE_IP, + DHCP_STOP, + DHCP_TIMEOUT +} DHCP_State_TypeDef; + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +void LwIP_Init(void); +uint8_t LwIP_DHCP(uint8_t idx, uint8_t dhcp_state); +unsigned char* LwIP_GetMAC(struct netif *pnetif); +unsigned char* LwIP_GetIP(struct netif *pnetif); +unsigned char* LwIP_GetGW(struct netif *pnetif); +uint8_t* LwIP_GetMASK(struct netif *pnetif); +uint8_t* LwIP_GetBC(struct netif *pnetif); +#if LWIP_DNS +void LwIP_GetDNS(struct ip_addr* dns); +void LwIP_SetDNS(struct ip_addr* dns); +#endif +void LwIP_UseStaticIP(struct netif *pnetif); +#if LWIP_AUTOIP +void LwIP_AUTOIP(struct netif *pnetif); +#endif +#if LWIP_IPV6 +void LwIP_AUTOIP_IPv6(struct netif *pnetif); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* __NETCONF_H */ + + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/component/common/api/network/include/lwipopts.h b/component/common/api/network/include/lwipopts.h new file mode 100644 index 0000000..d704f8d --- /dev/null +++ b/component/common/api/network/include/lwipopts.h @@ -0,0 +1,236 @@ +/** + ****************************************************************************** + * @file lwipopts.h + * @author MCD Application Team + * @version V1.1.0 + * @date 07-October-2011 + * @brief lwIP Options Configuration. + * This file is based on Utilities\lwip_v1.3.2\src\include\lwip\opt.h + * and contains the lwIP configuration for the STM32F2x7 demonstration. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +#include + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT 1 + +/* Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#define LWIP_COMPAT_MUTEX 1 + +#define ETHARP_TRUST_IP_MAC 0 +#define IP_REASSEMBLY 1 +#define IP_FRAG 1 +#define ARP_QUEUEING 0 + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#define NO_SYS 0 + +/* ---------- Memory options ---------- */ +/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which + lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 + byte alignment -> define MEM_ALIGNMENT to 2. */ +#define MEM_ALIGNMENT 4 + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#define MEM_SIZE (5*1024) + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 100 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 6 +/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 10 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 5 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 20 +/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT 10 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE 20 + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 500 + + +/* ---------- TCP options ---------- */ +#define LWIP_TCP 1 +#define TCP_TTL 255 + +/* Controls if TCP should queue segments that arrive out of + order. Define to 0 if your device is low on memory. */ +#define TCP_QUEUE_OOSEQ 1 + +/* TCP Maximum segment size. */ +#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF (5*TCP_MSS) + +/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ + +#define TCP_SND_QUEUELEN (4* TCP_SND_BUF/TCP_MSS) + +/* TCP receive window. */ +#define TCP_WND (2*TCP_MSS) + + +/* ---------- ICMP options ---------- */ +#define LWIP_ICMP 1 + +/* ---------- ARP options ----------- */ +#define LWIP_ARP 1 + +/* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. DHCP is not implemented in lwIP 0.5.1, however, so + turning this on does currently not work. */ +#define LWIP_DHCP 1 + + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define UDP_TTL 255 + + +/* ---------- UPNP options --------- */ +#define LWIP_UPNP 0 + +/* Support Multicast */ +#define LWIP_IGMP 0 +#define LWIP_RAND() rand() + +#define LWIP_TCP_DELAY_DISABLE 0 + +/* ---------- Statistics options ---------- */ +#define LWIP_STATS 0 +#define LWIP_PROVIDE_ERRNO 1 + + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ + +/* +The STM32F2x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: + - To use this feature let the following define uncommented. + - To disable it and process by CPU comment the the checksum. +*/ +//Do checksum by lwip - WLAN nic does not support Checksum offload +//#define CHECKSUM_BY_HARDWARE + + +#ifdef CHECKSUM_BY_HARDWARE + /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 0 + /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 0 + /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 0 + /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 0 + /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 0 + /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 0 +#else + /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 1 + /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 1 + /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 1 + /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 1 + /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 1 + /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 1 +#endif + + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#define LWIP_NETCONN 1 + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#define LWIP_SOCKET 1 + +/* + ----------------------------------- + ---------- DEBUG options ---------- + ----------------------------------- +*/ + +#define LWIP_DEBUG 0 + + +/* + --------------------------------- + ---------- OS options ---------- + --------------------------------- +*/ + +#define TCPIP_THREAD_STACKSIZE 1000 +#define TCPIP_MBOX_SIZE 5 +#define DEFAULT_UDP_RECVMBOX_SIZE 2000 +#define DEFAULT_TCP_RECVMBOX_SIZE 2000 +#define DEFAULT_ACCEPTMBOX_SIZE 2000 +#define DEFAULT_THREAD_STACKSIZE 500 +#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2) + + + +#endif /* __LWIPOPTS_H__ */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/component/common/api/network/include/main.h b/component/common/api/network/include/main.h new file mode 100644 index 0000000..7c352c0 --- /dev/null +++ b/component/common/api/network/include/main.h @@ -0,0 +1,68 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +#define CONFIG_WLAN 1 + + +/* Header file declaration*/ +void wlan_network(); + + +/* Interactive Mode */ +#define SERIAL_DEBUG_RX 1 +#if defined(__ICCARM__) +static +#endif +char uart_buf[64]; + + +/* WLAN and Netork */ +#define STA_MODE_SSID "ap" /* Set SSID here */ +#define AP_MODE_SSID "wlan_ap_ssid" /* Set SSID here */ +#define AP_DEFAULT_CH 6 +#define WLAN0_NAME "wlan0" +#define WLAN1_NAME "wlan1" +#define WPA_PASSPHRASE "1234567890" /* Max 32 cahracters */ +#define WEP40_KEY {0x12, 0x34, 0x56, 0x78, 0x90} + +/*Static IP ADDRESS*/ +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 1 +#define IP_ADDR3 80 + +/*NETMASK*/ +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 1 +#define GW_ADDR3 1 + +/*******************************************/ + +/*Static IP ADDRESS*/ +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 + +/*NETMASK*/ +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 + +#endif diff --git a/component/common/api/network/include/main_test.h b/component/common/api/network/include/main_test.h new file mode 100644 index 0000000..c9f3227 --- /dev/null +++ b/component/common/api/network/include/main_test.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------// +#ifndef __MAIN_TEST_H +#define __MAIN_TEST_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported test functions ------------------------------------------------------- */ +void do_ping_test(char *ip, int size, int count, int interval); +void do_ping_call(char *ip, int loop, int count); +void interactive_question(char *question, char *choice, char *buf, int buf_size); +void start_interactive_mode(void); + +#ifdef __cplusplus + } +#endif + +#endif // __MAIN_TEST_H + +//----------------------------------------------------------------------------// diff --git a/component/common/api/network/include/netconf.h b/component/common/api/network/include/netconf.h new file mode 100644 index 0000000..600cd06 --- /dev/null +++ b/component/common/api/network/include/netconf.h @@ -0,0 +1,50 @@ +/** + ****************************************************************************** + * @file netconf.h + * @author MCD Application Team + * @version V1.1.0 + * @date 07-October-2011 + * @brief This file contains all the functions prototypes for the netconf.c + * file. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __NETCONF_H +#define __NETCONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +// TODO: remove this file +#include "lwip_netconf.h" +#if 0 +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +void LwIP_Init(void); +void LwIP_DHCP(void); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* __NETCONF_H */ + + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/component/common/api/network/include/rtl8195a_it.h b/component/common/api/network/include/rtl8195a_it.h new file mode 100644 index 0000000..8d5cf19 --- /dev/null +++ b/component/common/api/network/include/rtl8195a_it.h @@ -0,0 +1,8 @@ + +#ifndef __RTL8195A_IT_H_ +#define __RTL8195A_IT_H_ + + +int irq_alloc_wlan(void *contex); + +#endif //__RTL8195A_IT_H_ \ No newline at end of file diff --git a/component/common/api/network/include/util.h b/component/common/api/network/include/util.h new file mode 100644 index 0000000..9af5cbc --- /dev/null +++ b/component/common/api/network/include/util.h @@ -0,0 +1,47 @@ +#ifndef _UTIL_H +#define _UTIL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wifi_util.h" +#if 0 +typedef enum _WIFI_EVENT_INDICATE{ + WIFI_EVENT_CONNECT = 0, + WIFI_EVENT_DISCONNECT = 1, + WIFI_EVENT_FOURWAY_HANDSHAKE_DONE = 2, +}WIFI_EVENT_INDICATE; + +int wext_get_ssid(const char *ifname, __u8 *ssid); +int wext_set_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len); +int wext_set_auth_param(const char *ifname, __u16 idx, __u32 value); +int wext_set_key_ext(const char *ifname, __u16 alg, const __u8 *addr, int key_idx, int set_tx, const __u8 *seq, __u16 seq_len, __u8 *key, __u16 key_len); +int wext_get_enc_ext(const char *ifname, __u16 *alg); +int wext_set_passphrase(const char *ifname, const __u8 *passphrase, __u16 passphrase_len); +int wext_get_passphrase(const char *ifname, __u8 *passphrase); +int wext_disconnect(const char *ifname); +int wext_set_mode(const char *ifname, int mode); +int wext_get_mode(const char *ifname, int *mode); +int wext_set_ap_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len); +int wext_set_country(const char *ifname, char *country_code); +int wext_get_rssi(const char *ifname, int *rssi); +int wext_set_channel(const char *ifname, __u8 ch); +int wext_get_channel(const char *ifname, __u8 *ch); +int wext_set_scan(const char *ifname, char *buf, __u16 buf_len); +int wext_get_scan(const char *ifname, char *buf, __u16 buf_len); +int wext_mp_command(const char *ifname, char *cmd, int show_msg); +int wext_wifi_priv(const char *ifname, int argc, char **argv); +void wext_wlan_indicate(unsigned int cmd, union iwreq_data *wrqu, char *extra); +#endif + +#define wext_handshake_done rltk_wlan_handshake_done + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_H */ diff --git a/component/common/api/network/include/wificonf.h b/component/common/api/network/include/wificonf.h new file mode 100644 index 0000000..0538016 --- /dev/null +++ b/component/common/api/network/include/wificonf.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------// +#ifndef __WIFICONF_H +#define __WIFICONF_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "main.h" +#include "util.h" + +typedef enum _WIFI_SECURITY_TYPE{ + WIFI_SECURITY_OPEN = 0, + WIFI_SECURITY_WEP, + WIFI_SECURITY_WPA, + WIFI_SECURITY_WPA2 +}WIFI_SECURITY_TYPE; + +typedef enum _WIFI_COUNTRY_CODE{ + WIFI_COUNTRY_US = 0, + WIFI_COUNTRY_EU, + WIFI_COUNTRY_JP, + WIFI_COUNTRY_CN +}WIFI_COUNTRY_CODE; + +typedef enum _WIFI_MODE_TYPE{ + WIFI_MODE_STA = 0, + WIFI_MODE_AP +}WIFI_MODE_TYPE; + +typedef enum _WIFI_LINK_STATUS{ + WIFI_LINK_DISCONNECTED = 0, + WIFI_LINK_CONNECTED +}WIFI_LINK_STATUS; + +typedef struct _WIFI_AP{ + unsigned char *ssid; + WIFI_SECURITY_TYPE security_type; + unsigned char *password; + int ssid_len; + int password_len; + int channel; +}WIFI_AP; + +typedef struct _WIFI_NETWORK{ + unsigned char *ssid; + WIFI_SECURITY_TYPE security_type; + unsigned char *password; + int ssid_len; + int password_len; + int key_id; +}WIFI_NETWORK; + +typedef struct _WIFI_SETTING{ + WIFI_MODE_TYPE mode; + unsigned char ssid[33]; + unsigned char channel; + WIFI_SECURITY_TYPE security_type; + unsigned char password[33]; +}WIFI_SETTING; + +typedef enum _WIFI_NETWORK_MODE { + WIFI_NETWORK_B = 1, + WIFI_NETWORK_BG = 3, + WIFI_NETWORK_BGN = 11 +} WIFI_NETWORK_MODE; + +int wifi_connect(WIFI_NETWORK *pNetwork); +int wifi_disconnect(void); +WIFI_LINK_STATUS wifi_get_connect_status(WIFI_NETWORK *pWifi); +int wifi_set_country(WIFI_COUNTRY_CODE country_code); +int wifi_get_rssi(int *pRSSI); +int wifi_rf_on(void); +int wifi_rf_off(void); +int wifi_active_ap(WIFI_AP *pAP); +int wifi_scan(char *buf, int buf_len); +int wifi_get_setting(WIFI_SETTING *pSetting); +int wifi_show_setting(WIFI_SETTING *pSetting); +int wifi_set_network_mode(WIFI_NETWORK_MODE mode); +void wifi_indication( WIFI_EVENT_INDICATE event, char *buf, int buf_len); + +#ifdef __cplusplus + } +#endif + +#endif // __WIFICONF_H + +//----------------------------------------------------------------------------// diff --git a/component/common/api/network/src/ping_test.c b/component/common/api/network/src/ping_test.c new file mode 100644 index 0000000..887d129 --- /dev/null +++ b/component/common/api/network/src/ping_test.c @@ -0,0 +1,136 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" + +#include +#include +#include +#include +#include + +#include "diag.h" + +//#define PING_IP "192.168.0.1" +#define PING_IP "192.168.159.1" +#define PING_TO 1000 +#define PING_ID 0xABCD +#define BUF_SIZE 200 +#define STACKSIZE 1024 + +static unsigned short ping_seq = 0; +static int infinite_loop, ping_count, data_size, ping_interval, ping_call; +static char ping_ip[16]; + +static void generate_ping_echo(unsigned char *buf, int size) +{ + int i; + struct icmp_echo_hdr *pecho; + + for(i = 0; i < size; i ++) { + buf[sizeof(struct icmp_echo_hdr) + i] = (unsigned char) i; + } + + pecho = (struct icmp_echo_hdr *) buf; + ICMPH_TYPE_SET(pecho, ICMP_ECHO); + ICMPH_CODE_SET(pecho, 0); + pecho->chksum = 0; + pecho->id = PING_ID; + pecho->seqno = htons(++ ping_seq); + + //Checksum includes icmp header and data. Need to calculate after fill up icmp header + pecho->chksum = inet_chksum(pecho, sizeof(struct icmp_echo_hdr) + size); +} + +void ping_test(void *param) +//void ping_test() +{ + int i, ping_socket; + int pint_timeout = PING_TO; + struct sockaddr_in to_addr, from_addr; + int from_addr_len; + int ping_size, reply_size; + unsigned char ping_buf[BUF_SIZE], reply_buf[BUF_SIZE]; + unsigned int ping_time, reply_time; + struct ip_hdr *iphdr; + struct icmp_echo_hdr *pecho; + + //Ping size = icmp header(8 bytes) + data size + ping_size = sizeof(struct icmp_echo_hdr) + data_size; + printf("\n\r[%s] PING %s %d(%d) bytes of data\n", __FUNCTION__, ping_ip, data_size, sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr) + data_size); + + for(i = 0; (i < ping_count) || (infinite_loop == 1); i ++) { + ping_socket = socket(AF_INET, SOCK_RAW, IP_PROTO_ICMP); + setsockopt(ping_socket, SOL_SOCKET, SO_RCVTIMEO, &pint_timeout, sizeof(pint_timeout)); + + to_addr.sin_len = sizeof(to_addr); + to_addr.sin_family = AF_INET; + to_addr.sin_addr.s_addr = inet_addr(ping_ip); + + generate_ping_echo(ping_buf, data_size); + sendto(ping_socket, ping_buf, ping_size, 0, (struct sockaddr *) &to_addr, sizeof(to_addr)); + + ping_time = xTaskGetTickCount(); + if((reply_size = recvfrom(ping_socket, reply_buf, sizeof(reply_buf), 0, (struct sockaddr *) &from_addr, (socklen_t *) &from_addr_len)) + >= (int)(sizeof(struct ip_hdr) + sizeof(struct icmp_echo_hdr))) { + + reply_time = xTaskGetTickCount(); + iphdr = (struct ip_hdr *)reply_buf; + pecho = (struct icmp_echo_hdr *)(reply_buf + (IPH_HL(iphdr) * 4)); + + if((pecho->id == PING_ID) && (pecho->seqno == htons(ping_seq))) { + printf("\n\r[%s] %d bytes from %s: icmp_seq=%d time=%d ms", __FUNCTION__, reply_size - sizeof(struct ip_hdr), inet_ntoa(from_addr.sin_addr), htons(pecho->seqno), (reply_time - ping_time) * portTICK_RATE_MS); + } + } + else + printf("\n\r[%s] Request timeout for icmp_seq %d\n", __FUNCTION__, ping_seq); + + close(ping_socket); + vTaskDelay(ping_interval * configTICK_RATE_HZ); + } + + if(!ping_call) + vTaskDelete(NULL); +} + +void do_ping_call(char *ip, int loop, int count) +{ + ping_call = 1; + ping_seq = 0; + data_size = 120; + ping_interval = 1; + infinite_loop = loop; + ping_count = count; + strcpy(ping_ip, ip); + + ping_test(NULL); +} + +void do_ping_test(char *ip, int size, int count, int interval) +{ + if((sizeof(struct icmp_echo_hdr) + size) > BUF_SIZE) { + printf("\n\r%s BUF_SIZE(%d) is too small", __FUNCTION__, BUF_SIZE); + return; + } + + if(ip == NULL) + strcpy(ping_ip, PING_IP); + else + strcpy(ping_ip, ip); + + ping_call = 0; + ping_seq = 0; + data_size = size; + ping_interval = interval; + + if(count == 0) { + infinite_loop = 1; + ping_count = 0; + } + else { + infinite_loop = 0; + ping_count = count; + } + + if(xTaskCreate(ping_test, ((const signed char*)"ping_test"), STACKSIZE, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS) + printf("\n\r%s xTaskCreate failed", __FUNCTION__); +} diff --git a/component/common/api/network/src/rtl8195a_it.c b/component/common/api/network/src/rtl8195a_it.c new file mode 100644 index 0000000..91c0667 --- /dev/null +++ b/component/common/api/network/src/rtl8195a_it.c @@ -0,0 +1,110 @@ +#include "rtl8195a.h" +#include +#include "rtl8195a_it.h" + +/* os dependent*/ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + + +#include +#define printf DiagPrintf + +/*-----------------------------Global Variable ---------------------*/ +//#ifdef CONFIG_WLAN +//#ifdef CONFIG_ISR_THREAD_MODE_INTERRUPT +extern xSemaphoreHandle *pExportWlanIrqSemaphore; +//#endif +//#endif + + +#ifdef CONFIG_WLAN +#ifdef CONFIG_ISR_THREAD_MODE_INTERRUPT + +//TODO: chris +#define IRQ_HANDLED 1 +#define IRQ_NONE 0 + +int wlan_Interrupt ( + IN VOID* Data +) +{ +#if 1 + + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + printf("wlan interrupt\n"); + /* This semaphore is initialized once wlan interrupt handler thread is created and initialized*/ + if(!pExportWlanIrqSemaphore) + { + printf("%s(%d)\n", __FUNCTION__, __LINE__); + goto exit; + } + + printf("%s(%d)\n", __FUNCTION__, __LINE__); + xSemaphoreGiveFromISR( *pExportWlanIrqSemaphore, &xHigherPriorityTaskWoken ); + + printf("%s(%d)\n", __FUNCTION__, __LINE__); + /* Switch tasks if necessary. */ + if( xHigherPriorityTaskWoken != pdFALSE ) + { + printf("%s(%d)\n", __FUNCTION__, __LINE__); + portEND_SWITCHING_ISR( xHigherPriorityTaskWoken ); + } + +exit: + + return IRQ_HANDLED; +#else + struct dvobj_priv *dvobj = (struct dvobj_priv *)Data; + _adapter *adapter = dvobj->if1; + DBG_8192C("Dma isr\n"); + + if (dvobj->irq_enabled == 0) { + return IRQ_HANDLED; + } +DBG_871X("%s(%d)\n", __FUNCTION__, __LINE__); + if(rtw_hal_interrupt_handler(adapter) == _FAIL) + return IRQ_HANDLED; + //return IRQ_NONE; +DBG_871X("%s(%d)\n", __FUNCTION__, __LINE__); + return IRQ_HANDLED; +#endif + +} + + +VOID +lextra_bus_dma_Interrupt ( + IN VOID* Data +); + + +/* + * This function register interrupt handler and is called by wlan driver + * Return 0 if success, Others if fail + */ + +int irq_alloc_wlan(void *contex) +{ + int ret = 0; + IRQ_HANDLE IrqHandle = {0}; + + printf("Register Interrupt\n"); + IrqHandle.Data = (u32) (contex); + IrqHandle.IrqNum = WL_DMA_IRQ; + IrqHandle.IrqFun = (IRQ_FUN)wlan_Interrupt; + //IrqHandle.IrqFun = (IRQ_FUN)lextra_bus_dma_Interrupt; + IrqHandle.Priority = 0; + + InterruptRegister(&IrqHandle); + InterruptEn(&IrqHandle); + + return ret; + +} +#endif +#endif + diff --git a/component/common/api/network/src/wlan_network.c b/component/common/api/network/src/wlan_network.c new file mode 100644 index 0000000..ac66aea --- /dev/null +++ b/component/common/api/network/src/wlan_network.c @@ -0,0 +1,65 @@ +/* + * Hello World + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +#include "main.h" +#include "main_test.h" +#include "wifi_conf.h" +#include "wlan_intf.h" +#include "lwip_netconf.h" +#include "wifi_constants.h" + +#include + +#ifndef CONFIG_INIT_NET +#define CONFIG_INIT_NET 1 +#endif +#ifndef CONFIG_INTERACTIVE_MODE +#define CONFIG_INTERACTIVE_MODE 1 +#endif + +#define STACKSIZE (512 + 768) + +xSemaphoreHandle uart_rx_interrupt_sema = NULL; + +void init_thread(void *param) +{ + +#if CONFIG_INIT_NET +#if CONFIG_LWIP_LAYER + /* Initilaize the LwIP stack */ + LwIP_Init(); +#endif +#endif + +#if CONFIG_WLAN + wifi_on(RTW_MODE_STA); + + printf("\n\r%s(%d), Available heap 0x%x", __FUNCTION__, __LINE__, xPortGetFreeHeapSize()); +#endif + +#if CONFIG_INTERACTIVE_MODE + /* Initial uart rx swmaphore*/ + vSemaphoreCreateBinary(uart_rx_interrupt_sema); + xSemaphoreTake(uart_rx_interrupt_sema, 1/portTICK_RATE_MS); + start_interactive_mode(); +#endif + + /* Kill init thread after all init tasks done */ + vTaskDelete(NULL); +} + +void wlan_network() +{ + if(xTaskCreate(init_thread, ((const char*)"init"), STACKSIZE, NULL, tskIDLE_PRIORITY + 3 + PRIORITIE_OFFSET, NULL) != pdPASS) + printf("\n\r%s xTaskCreate(init_thread) failed", __FUNCTION__); +} diff --git a/component/common/api/platform/dlist.h b/component/common/api/platform/dlist.h new file mode 100644 index 0000000..28ec5a8 --- /dev/null +++ b/component/common/api/platform/dlist.h @@ -0,0 +1,250 @@ +#ifndef __LIST_H +#define __LIST_H + +#if defined ( __CC_ARM ) +#ifndef inline +#define inline __inline +#endif +#endif + +/* This file is from Linux Kernel (include/linux/list.h) + * and modified by simply removing hardware prefetching of list items. + * Here by copyright, credits attributed to wherever they belong. + * Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu) + */ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (void *) 0; + entry->prev = (void *) 0; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + + +#endif diff --git a/component/common/api/platform/platform_stdlib.h b/component/common/api/platform/platform_stdlib.h new file mode 100644 index 0000000..e53c0ca --- /dev/null +++ b/component/common/api/platform/platform_stdlib.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2014 Realtek Corporation. All rights reserved. + * + * + ******************************************************************************/ +#ifndef __PLATFORM_STDLIB_H__ +#define __PLATFORM_STDLIB_H__ + +#define USE_CLIB_PATCH 0 +#if defined (__GNUC__) +#define USE_RTL_ROM_CLIB 0 +#else +#define USE_RTL_ROM_CLIB 1 +#endif + +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#if defined (__IARSTDLIB__) + #include + #include + #include + #include + #include "diag.h" + + #define strsep(str, delim) _strsep(str, delim) +#else + #include + #include + #include + #include "diag.h" + #include "strproc.h" + #include "basic_types.h" + #include "hal_misc.h" + #if USE_RTL_ROM_CLIB + #include "rtl_lib.h" + #endif + + #undef printf + #undef sprintf + #undef snprintf + #undef atoi + #undef memcmp + #undef memcpy + #undef memset + #undef strcmp + #undef strcpy + #undef strlen + #undef strncmp + #undef strncpy + #undef strsep + #undef strtok + #if USE_RTL_ROM_CLIB + #undef memchr + #undef memmove + #undef strcat + #undef strchr + #undef strncat + #undef strstr + #endif + + #if USE_RTL_ROM_CLIB + #define printf rtl_printf + #define sprintf rtl_sprintf + #define snprintf rtl_snprintf + #define memchr rtl_memchr + #define memcmp rtl_memcmp + #define memcpy rtl_memcpy + #define memmove rtl_memmove + #define memset rtl_memset + #define strcat rtl_strcat + #define strchr rtl_strchr + #define strcmp(s1, s2) rtl_strcmp((const char *)s1, (const char *)s2) + #define strcpy rtl_strcpy + #define strlen(str) rtl_strlen((const char *)str) + #define strncat rtl_strncat + #define strncmp(s1, s2, n) rtl_strncmp((const char *)s1, (const char *)s2, n) + #define strncpy rtl_strncpy + #define strstr rtl_strstr + #define strsep rtl_strsep + #define strtok rtl_strtok + #else + #if USE_CLIB_PATCH + extern int DiagSscanfPatch(const char *buf, const char *fmt, ...); + extern char* DiagStrtokPatch(char *str, const char* delim); + extern char* DiagStrstrPatch(char *string, char *substring); + extern int DiagSnPrintfPatch(char *buf, size_t size, const char *fmt, ...); + extern u32 DiagPrintfPatch(const char *fmt, ...); + extern u32 DiagSPrintfPatch(u8 *buf, const char *fmt, ...); + #define printf DiagPrintfPatch + #define sprintf DiagSPrintfPatch + #define snprintf DiagSnPrintfPatch + #define strstr(a, b) DiagStrstrPatch((char *)(a), (char *)(b)) + #define strtok DiagStrtokPatch + #else + #define printf DiagPrintf + #define sprintf(fmt, arg...) DiagSPrintf((u8*)fmt, ##arg) + #if defined (__GNUC__) + #define snprintf DiagSnPrintf // NULL function + #define strstr(str1, str2) prvStrStr(str1, str2) // NULL function + #endif + #define strtok(str, delim) _strsep(str, delim) + #endif + #define memcmp(dst, src, sz) _memcmp(dst, src, sz) + #define memcpy(dst, src, sz) _memcpy(dst, src, sz) + #define memset(dst, val, sz) _memset(dst, val, sz) + #define strchr(s, c) _strchr(s, c) // for B-cut ROM + #define strcmp(str1, str2) prvStrCmp((const unsigned char *) str1, (const unsigned char *) str2) + #define strcpy(dest, src) _strcpy(dest, src) + #define strlen(str) prvStrLen((const unsigned char *) str) + #define strncmp(str1, str2, cnt) _strncmp(str1, str2, cnt) + #define strncpy(dest, src, count) _strncpy(dest, src, count) + #define strsep(str, delim) _strsep(str, delim) + #endif + + #define atoi(str) prvAtoi(str) + #define strpbrk(cs, ct) _strpbrk(cs, ct) // for B-cut ROM + + #if USE_CLIB_PATCH + #undef sscanf + #define sscanf DiagSscanfPatch + #else + #if defined (__GNUC__) + #undef sscanf + #define sscanf _sscanf + #endif + #endif +#endif // defined (__IARSTDLIB__) + +// +// memory management +// +extern void *pvPortMalloc( size_t xWantedSize ); +extern void vPortFree( void *pv ); +#define malloc pvPortMalloc +#define free vPortFree + +#elif defined(USE_STM322xG_EVAL) || defined(USE_STM324xG_EVAL) || defined(STM32F10X_XL) + #include + #include + #include + #include +#endif + + +#endif //__PLATFORM_STDLIB_H__ diff --git a/component/common/api/platform/stdlib_patch.c b/component/common/api/platform/stdlib_patch.c new file mode 100644 index 0000000..c86f4f9 --- /dev/null +++ b/component/common/api/platform/stdlib_patch.c @@ -0,0 +1,846 @@ + + +#include +#include +#include +#include + + +#define DiagPutChar HalSerialPutcRtl8195a + + + +#define u32 unsigned int +#define IN +#define u8 unsigned char +#define s16 signed short int +#define u16 unsigned short int +typedef signed int s32; +typedef unsigned char bool; +typedef unsigned long long u64; +typedef signed long long s64; +#define SIZE_T unsigned int + + + + + +#define in_range(c, lo, up) ((u8)c >= lo && (u8)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' || c == ',') +#define ULLONG_MAX (~0ULL) +#define USHRT_MAX ((u16)(~0U)) +#define KSTRTOX_OVERFLOW (1U << 31) +#define SHRT_MAX ((s16)(USHRT_MAX>>1)) + +static inline char _tolower(const char c) +{ + return c | 0x20; +} + + +extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); +extern s64 div_s64(s64 dividend, s32 divisor); +extern inline char _tolower(const char c); +extern u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder); +extern u64 div_u64(u64 dividend, u32 divisor); +extern unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p); +extern const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); +extern char *skip_spaces(const char *str); +extern int skip_atoi(const char **s); + + +static unsigned long long simple_strtoull_patch(const char *cp, char **endp, unsigned int base) +{ + unsigned long long result; + unsigned int rv; + + cp = _parse_integer_fixup_radix(cp, &base); + rv = _parse_integer(cp, base, &result); + + +//troy delete + /* FIXME */ +// cp += (rv & ~KSTRTOX_OVERFLOW); +// if(endp) +// *endp = (char *)cp; + + return result; +} + +static long long simple_strtoll_patch(const char *cp, char **endp, unsigned int base) +{ + if(*cp == '-') + return -simple_strtoull_patch(cp + 1, endp, base); + + return simple_strtoull_patch(cp, endp, base); +} +static unsigned long simple_strtoul_patch(const char *cp, char **endp, unsigned int base) +{ + return simple_strtoull_patch(cp, endp, base); +} + +static long simple_strtol_patch(const char *cp, char **endp, unsigned int base) +{ + if(*cp == '-') + return -simple_strtoul_patch(cp + 1, endp, base); + + return simple_strtoul_patch(cp, endp, base); +} + + + + +static int judge_digit_width(const char *str) +{ + + int width = 0; + while(isxdigit(*str)) { + width++; + str++; + } + return width; +} + + +static int _vsscanf_patch(const char *buf, const char *fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + int i =0; + u8 qualifier; + unsigned int base; + union { + long long s; + unsigned long long u; + } val; + s16 field_width; + bool is_sign; + + char str_store[20] = {0}; + + + + while(*fmt) { + /* skip any white space in format */ + /* white space in format matchs any amount of + * white space, including none, in the input. + */ + if(isspace(*fmt)) { + fmt = skip_spaces(++fmt); + str = skip_spaces(str); + } + + /* anything that is not a conversion must match exactly */ + if(*fmt != '%' && *fmt) { + if(*fmt++ != *str++) { + break; + } + + continue; + } + + if(!*fmt) { + break; + } + + ++fmt; + + /* skip this conversion. + * advance both strings to next white space + */ + if(*fmt == '*') { + if(!*str) { + break; + } + + while(!isspace(*fmt) && *fmt != '%' && *fmt) + fmt++; + + while(!isspace(*str) && *str) + str++; + + continue; + } + + /* get field width */ + field_width = -1; + + if(isdigit(*fmt)) { + + field_width = skip_atoi(&fmt); + + + + if(field_width <= 0) { + + break; + } + } + + /* get conversion qualifier */ + qualifier = -1; + + if(*fmt == 'h' || _tolower(*fmt) == 'l' || + _tolower(*fmt) == 'z') { + qualifier = *fmt++; + + if(qualifier == *fmt) { + if(qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if(qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + + if(!*fmt) { + break; + } + + if(*fmt == 'n') { + /* return number of characters read so far */ + *va_arg(args, int *) = str - buf; + ++fmt; + continue; + } + + if(!*str) { + break; + } + + base = 10; + is_sign = 0; + + switch(*fmt++) { + case 'c': { + char *s = (char *)va_arg(args, char*); + + if(field_width == -1) + field_width = 1; + + do { + *s++ = *str++; + } while(--field_width > 0 && *str); + + num++; + } + + continue; + + case 's': { + char *s = (char *)va_arg(args, char *); + + if(field_width == -1) + field_width = SHRT_MAX; + + /* first, skip leading white space in buffer */ + str = skip_spaces(str); + + /* now copy until next white space */ + while(*str && !isspace(*str) && field_width--) { + *s++ = *str++; + } + + *s = '\0'; + num++; + } + + continue; + + case 'o': + base = 8; + break; + + case 'x': + case 'X': + base = 16; + break; + + case 'i': + base = 0; + + case 'd': + is_sign = 1; + + case 'u': + break; + + case '%': + + /* looking for '%' in str */ + if(*str++ != '%') { + return num; + } + + continue; + + default: + /* invalid format; stop here */ + return num; + } + + /* have some sort of integer conversion. + * first, skip white space in buffer. + */ + str = skip_spaces(str); + + digit = *str; + + if(is_sign && digit == '-') + digit = *(str + 1); + + if(!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) { + break; + } + + //here problem ******************************************* + + + + //troy add ,fix support %2d, but not support %d + if(field_width <= 0) { + + field_width = judge_digit_width(str); + } + + + /////troy add, fix str passed inwidth wrong + for(i = 0; i 0 && next - str > field_width) { + if(base == 0) + _parse_integer_fixup_radix(str, &base); + + while(next - str > field_width) { + if(is_sign) { + val.s = div_s64(val.s, base); + } else { + val.u = div_u64(val.u, base); + } + + --next; + } + } + + switch(qualifier) { + case 'H': /* that's 'hh' in format */ + if(is_sign) + *va_arg(args, signed char *) = val.s; + else + *va_arg(args, unsigned char *) = val.u; + + break; + + case 'h': + if(is_sign) + *va_arg(args, short *) = val.s; + else + *va_arg(args, unsigned short *) = val.u; + + break; + + case 'l': + if(is_sign) + *va_arg(args, long *) = val.s; + else + *va_arg(args, unsigned long *) = val.u; + + break; + + case 'L': + if(is_sign) + *va_arg(args, long long *) = val.s; + else + *va_arg(args, unsigned long long *) = val.u; + + break; + + case 'Z': + case 'z': + *va_arg(args, size_t *) = val.u; + break; + + default: + if(is_sign) + *va_arg(args, int *) = val.s; + else + *va_arg(args, unsigned int *) = val.u; + + break; + } + + num++; + + if(!next) { + break; + } + + str = next; + } + + return num; +} + + +int DiagSscanfPatch(const char *buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = _vsscanf_patch(buf, fmt, args); + va_end(args); + + return i; +} + + + +/*********************************************************/ + + + +char* DiagStrtokPatch(char *str, const char* delim) { + static char* _buffer; + if(str != NULL) _buffer = str; + if(_buffer[0] == '\0') return NULL; + + char *ret = _buffer, *b; + const char *d; + + for(b = _buffer; *b !='\0'; b++) { + for(d = delim; *d != '\0'; d++) { + if(*b == *d) { + *b = '\0'; + _buffer = b+1; + + // skip the beginning delimiters + if(b == ret) { + ret++; + continue; + } + return ret; + } + } + } + + return ret; +} + + + +/*********************************************************/ + + + +char *DiagStrstrPatch(char *string, char *substring) +{ + register char *a, *b; + + /* First scan quickly through the two strings looking for a + * single-character match. When it's found, then compare the + * rest of the substring. + */ + + b = substring; + if (*b == 0) { + return string; + } + for ( ; *string != 0; string += 1) { + if (*string != *b) { + continue; + } + a = string; + while (1) { + if (*b == 0) { + return string; + } + if (*a++ != *b++) { + break; + } + } + b = substring; + } + return (char *) 0; +} + + + + + +/*********************************************************/ + + + + +int DiagSnPrintfPatch(char *buf, size_t size, const char *fmt, ...) +{ + + va_list ap; + char *p, *s, *buf_end = NULL; + const int *dp = ((const int *)&fmt)+1; + + if(buf == NULL) + return 0; + + + va_start(ap, fmt); + s = buf; + buf_end = size? (buf + size):(char*)~0; + for ( ; *fmt != '\0'; ++fmt) { + + if (*fmt != '%') { + *s++ = *fmt; + if(s >= buf_end){ + goto Exit; + } + continue; + } + + if (*++fmt == 's') { + for (p = (char *)*dp++; *p != '\0'; p++){ + *s++ = *p; + + if(s >= buf_end){ + goto Exit; + } + } + } + else { /* Length of item is bounded */ + char tmp[20], *q = tmp; + int alt = 0; + int shift = 0;// = 12; + const long *lpforchk = (const long *)dp; + + if ((*lpforchk) < 0x10) { + shift = 0; + } + else if (((*lpforchk) >= 0x10) && ((*lpforchk) < 0x100)) { + shift = 4; + } + else if (((*lpforchk) >= 0x100) && ((*lpforchk) < 0x1000)) { + shift = 8; + } + else if (((*lpforchk) >= 0x1000) && ((*lpforchk) < 0x10000)) { + shift = 12; + } + else if (((*lpforchk) >= 0x10000) && ((*lpforchk) < 0x100000)) { + shift = 16; + } + else if (((*lpforchk) >= 0x100000) && ((*lpforchk) < 0x1000000)) { + shift = 20; + } + else if (((*lpforchk) >= 0x1000000) && ((*lpforchk) < 0x10000000)) { + shift = 24; + } + else if ((*lpforchk) >= 0x10000000) { + shift = 28; + } + else { + shift = 28; + } + + if ((*fmt >= '0') && (*fmt <= '9')) + { + int width; + unsigned char fch = *fmt; + for (width=0; (fch>='0') && (fch<='9'); fch=*++fmt) + { width = width * 10 + fch - '0'; + } + shift=(width-1)*4; + } + + /* + * Before each format q points to tmp buffer + * After each format q points past end of item + */ + if ((*fmt == 'x')||(*fmt == 'X') || (*fmt == 'p') || (*fmt == 'P')) { + /* With x86 gcc, sizeof(long) == sizeof(int) */ + const long *lp = (const long *)dp; + long h = *lp++; + int hex_count = 0; + unsigned long h_back = h; + int ncase = (*fmt & 0x20); + dp = (const int *)lp; + if((*fmt == 'p') || (*fmt == 'P')) + alt=1; + if (alt) { + *q++ = '0'; + *q++ = 'X' | ncase; + } + while (h_back) { + hex_count += (h_back & 0xF) ? 1 : 0; + h_back = h_back >> 4; + } + + if (shift < (hex_count - 1)*4) + shift = (hex_count - 1)*4; + for ( ; shift >= 0; shift -= 4) + *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase; + } + else if (*fmt == 'd') { + int i = *dp++; + char *r; + int digit_space = 0; + + + if (i < 0) { + *q++ = '-'; + i = -i; + digit_space++; + } + p = q; /* save beginning of digits */ + + + do { + *q++ = '0' + (i % 10); + i /= 10; + digit_space++; + } while (i); + + + for ( ; shift >= 0; shift -= 4) { + + if(digit_space-- > 0) { + ; //do nothing + } else { + *q++ = '0'; + } + } + + /* reverse digits, stop in middle */ + r = q; /* don't alter q */ + while (--r > p) { + i = *r; + *r = *p; + *p++ = i; + } + } + else if (*fmt == 'c') + *q++ = *dp++; + else + *q++ = *fmt; + /* now output the saved string */ + for (p = tmp; p < q; ++p){ + *s++ = *p; + if(s >= buf_end){ + goto Exit; + } + } + } + } + +Exit: + if (buf) + *s = '\0'; + + va_end(ap); + return(s-buf); + +} + + + + + + +/*********************************************************/ + +static int VSprintfPatch(char *buf, const char *fmt, const int *dp) +{ + char *p, *s; + s = buf; + for ( ; *fmt != '\0'; ++fmt) { + if (*fmt != '%') { + buf ? *s++ = *fmt : DiagPutChar(*fmt); + continue; + } + if (*++fmt == 's') { + for (p = (char *)*dp++; *p != '\0'; p++) + buf ? *s++ = *p : DiagPutChar(*p); + } + else { /* Length of item is bounded */ + char tmp[20], *q = tmp; + int alt = 0; + int shift = 0;// = 12; + const long *lpforchk = (const long *)dp; + + if ((*lpforchk) < 0x10) { + shift = 0; + } + else if (((*lpforchk) >= 0x10) && ((*lpforchk) < 0x100)) { + shift = 4; + } + else if (((*lpforchk) >= 0x100) && ((*lpforchk) < 0x1000)) { + shift = 8; + } + else if (((*lpforchk) >= 0x1000) && ((*lpforchk) < 0x10000)) { + shift = 12; + } + else if (((*lpforchk) >= 0x10000) && ((*lpforchk) < 0x100000)) { + shift = 16; + } + else if (((*lpforchk) >= 0x100000) && ((*lpforchk) < 0x1000000)) { + shift = 20; + } + else if (((*lpforchk) >= 0x1000000) && ((*lpforchk) < 0x10000000)) { + shift = 24; + } + else if ((*lpforchk) >= 0x10000000) { + shift = 28; + } + else { + shift = 28; + } + +#if 1 //wei patch for %02x + if ((*fmt >= '0') && (*fmt <= '9')) + { + int width; + unsigned char fch = *fmt; + for (width=0; (fch>='0') && (fch<='9'); fch=*++fmt) + { width = width * 10 + fch - '0'; + } + shift=(width-1)*4; + } +#endif + + /* + * Before each format q points to tmp buffer + * After each format q points past end of item + */ + + if ((*fmt == 'x')||(*fmt == 'X') || (*fmt == 'p') || (*fmt == 'P')) { + /* With x86 gcc, sizeof(long) == sizeof(int) */ + const long *lp = (const long *)dp; + long h = *lp++; + int hex_count = 0; + unsigned long h_back = h; + int ncase = (*fmt & 0x20); + dp = (const int *)lp; + if((*fmt == 'p') || (*fmt == 'P')) + alt=1; + if (alt) { + *q++ = '0'; + *q++ = 'X' | ncase; + } + //hback 是实际得到的数æ®ï¼Œhex_count是统计数æ®çš„HEX字符个数 + while (h_back) { + hex_count += (h_back & 0xF) ? 1 : 0; + h_back = h_back >> 4; + } + //è¿™é‡Œä¿®å¤ example: 字符有4个,但是用了%02x导致字符被截断的情况 + if (shift < (hex_count - 1)*4) + shift = (hex_count - 1)*4; + //printf("(%d,%d)", hex_count, shift); + + for ( ; shift >= 0; shift -= 4) { + + *q++ = "0123456789ABCDEF"[(h >> shift) & 0xF] | ncase; + } + + } + else if (*fmt == 'd') { + int i = *dp++; + char *r; + int digit_space = 0; + if (i < 0) { + *q++ = '-'; + i = -i; + digit_space++; + } + p = q; /* save beginning of digits */ + do { + *q++ = '0' + (i % 10); + i /= 10; + digit_space++; + } while (i); + //è¿™é‡Œä¿®å¤ example:用了%08dåŽï¼Œåœ¨æ•°å­—å‰é¢æ²¡æœ‰0的情况 + for ( ; shift >= 0; shift -= 4) { + + if(digit_space-- > 0) { + ; //do nothing + } else { + *q++ = '0'; + } + } + /* reverse digits, stop in middle */ + r = q; /* don't alter q */ + while (--r > p) { + i = *r; + *r = *p; + *p++ = i; + } + } + else if (*fmt == 'c') + *q++ = *dp++; + else + *q++ = *fmt; + /* now output the saved string */ + for (p = tmp; p < q; ++p){ + buf ? *s++ = *p : DiagPutChar(*p); + if ((*p) == '\n') { + DiagPutChar('\r'); + } + } + } + } + if (buf) + *s = '\0'; + return (s - buf); +} + + +u32 DiagPrintfPatch( + IN const char *fmt, ... +) +{ + (void)VSprintfPatch(0, fmt, ((const int *)&fmt)+1); + return 1; +} + +u32 DiagSPrintfPatch( + IN u8 *buf, + IN const char *fmt, ... +) +{ + (void)VSprintfPatch((char*)buf, fmt, ((const int *)&fmt)+1); + return 1; +} + + + diff --git a/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os.h b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os.h new file mode 100644 index 0000000..4cbfb01 --- /dev/null +++ b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os.h @@ -0,0 +1,578 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OS_H +#define OS_H + +//#include "basic_types.h" +#include +#include "osdep_service.h" +#include "utils/rom/rom_wps_os.h" + +typedef void* xqueue_handle_t; + +typedef long os_time_t; + +typedef _timer os_timer; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + +/** + * os_get_time - Get current time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_time(struct os_time *t); + +int os_get_reltime(struct os_reltime *t); +/* Helper macros for handling struct os_time */ +/* (&timeout->time, &tmp->time) */ +#define os_time_before(a, b) \ + ((a)->sec < (b)->sec || \ + ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) + +#define os_time_sub(a, b, res) do { \ + (res)->sec = (a)->sec - (b)->sec; \ + (res)->usec = (a)->usec - (b)->usec; \ + if ((res)->usec < 0) { \ + (res)->sec--; \ + (res)->usec += 1000000; \ + } \ +} while (0) + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + * + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time + * which is used by POSIX mktime(). + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); + +/* Helpers for handling struct os_time */ + +/* Helpers for handling struct os_reltime */ + +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return os_time_before(a,b); +} + + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + os_time_sub(a,b,res); +} + + +static inline void os_reltime_age(struct os_reltime *start, + struct os_reltime *age) +{ + struct os_reltime now; + + os_get_time((struct os_time *)&now); + os_reltime_sub(&now, start, age); +} + + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + +/** + * os_daemonize - Run in the background (detach from the controlling terminal) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +#if 0 +/** + * os_zalloc - Allocate and zero memory + * @size: Number of bytes to allocate + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_zalloc(size_t size); + +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} +#endif + +/* + * The following functions are wrapper for standard ANSI C or POSIX functions. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifdef OS_NO_C_LIB_DEFINES + +/** + * os_malloc - Allocate dynamic memory + * @size: Size of the buffer to allocate + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_malloc(size_t size); + +/** + * os_realloc - Re-allocate dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc() + * @size: Size of the new buffer + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is + * not freed and caller is still responsible for freeing it. + */ +void * os_realloc(void *ptr, size_t size); + +/** + * os_free - Free dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL + */ +void os_free(void *ptr); + +/** + * os_memcpy - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst must not overlap. os_memmove() can be used with + * overlapping memory. + */ +void * os_memcpy(void *dest, const void *src, size_t n); + +/** + * os_memmove - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst may overlap. + */ +void *os_memmove(void *dest, const void *src, size_t n); + +/** + * os_memset - Fill memory with a constant byte + * @s: Memory area to be filled + * @c: Constant byte + * @n: Number of bytes started from s to fill with c + * Returns: s + */ +void *os_memset(void *s, int c, size_t n); + +/** + * os_memcmp - Compare memory areas + * @s1: First buffer + * @s2: Second buffer + * @n: Maximum numbers of octets to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_memcmp(const void *s1, const void *s2, size_t n); + +/** + * os_strdup - Duplicate a string + * @s: Source string + * Returns: Allocated buffer with the string copied into it or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char *os_strdup(const char *s); + +/** + * os_strlen - Calculate the length of a string + * @s: '\0' terminated string + * Returns: Number of characters in s (not counting the '\0' terminator) + */ +size_t os_strlen(const char *s); + +/** + * os_strcasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcasecmp(const char *s1, const char *s2); + +/** + * os_strncasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncasecmp(const char *s1, const char *s2, size_t n); + +/** + * os_strchr - Locate the first occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char *os_strchr(const char *s, int c); + +/** + * os_strrchr - Locate the last occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char *os_strrchr(const char *s, int c); + +/** + * os_strcmp - Compare two strings + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcmp(const char *s1, const char *s2); + +/** + * os_strncmp - Compare two strings + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncmp(const char *s1, const char *s2, size_t n); + +/** + * os_strncpy - Copy a string + * @dest: Destination + * @src: Source + * @n: Maximum number of characters to copy + * Returns: dest + */ +char *os_strncpy(char *dest, const char *src, size_t n); + +/** + * os_strstr - Locate a substring + * @haystack: String (haystack) to search from + * @needle: Needle to search from haystack + * Returns: Pointer to the beginning of the substring or %NULL if not found + */ +char *os_strstr(const char *haystack, const char *needle); + +/** + * os_snprintf - Print to a memory buffer + * @str: Memory buffer to print into + * @size: Maximum length of the str buffer + * @format: printf format + * Returns: Number of characters printed (not including trailing '\0'). + * + * If the output buffer is truncated, number of characters which would have + * been written is returned. Since some C libraries return -1 in such a case, + * the caller must be prepared on that value, too, to indicate truncation. + * + * Note: Some C library implementations of snprintf() may not guarantee null + * termination in case the output is truncated. The OS wrapper function of + * os_snprintf() should provide this guarantee, i.e., to null terminate the + * output buffer if a C library version of the function is used and if that + * function does not guarantee null termination. + * + * If the target system does not include snprintf(), see, e.g., + * http://www.ijs.si/software/snprintf/ for an example of a portable + * implementation of snprintf. + */ +int os_snprintf(char *str, size_t size, const char *format, ...); + +#else /* OS_NO_C_LIB_DEFINES */ + +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +#ifdef CONFIG_MEM_MONITOR + u8* os_malloc(u32 sz); + void os_mfree(u8 *pbuf, u32 sz); + #ifndef os_free + #define os_free(p, sz) os_mfree(((u8*)(p)), (sz)) + #endif +#else + #ifndef os_malloc + #define os_malloc(sz) _rtw_malloc(sz) + #endif + #ifndef os_free + #define os_free(p, sz) _rtw_mfree(((u8*)(p)), (sz)) + #endif +#endif +#endif + extern void *os_zalloc(size_t size); + extern char *os_strdup(const char *string_copy_from); + + #ifndef os_sleep + #define os_sleep(s, us) rtw_mdelay_os((s)*1000 + (us)/1000) + #endif + #ifndef os_memcpy + #define os_memcpy(d, s, n) rtw_memcpy((void*)(d), ((void*)(s)), (n)) + #endif + #ifndef os_memmove + #define os_memmove(d, s, n) memmove((d), (s), (n)) + #endif + #ifndef os_memset + #define os_memset(pbuf, c, sz) rtw_memset(pbuf, c, sz) + #endif + #ifndef os_memcmp + #define os_memcmp(s1, s2, n) rtw_memcmp(((void*)(s1)), ((void*)(s2)), (n)) + #endif + #ifndef os_memcmp_p2p + #define os_memcmp_p2p(s1, s2, n) memcmp((s1), (s2), (n)) + #endif + #ifndef os_get_random_bytes + #define os_get_random_bytes(d,sz) rtw_get_random_bytes(((void*)(d)), (sz)) + #endif + #ifndef os_strlen + #define os_strlen(s) strlen(s) + #endif + #ifndef os_strcasecmp + #ifdef _MSC_VER + #define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) + #else + #define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) + #endif + #endif + #ifndef os_strncasecmp + #ifdef _MSC_VER + #define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) + #else + #define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) + #endif + #endif + #ifndef os_init_timer + #define os_init_timer(t, p, f, x, n) rtw_init_timer((t), (p), (f), (x), (n)) + #endif + #ifndef os_set_timer + #define os_set_timer(t, d) rtw_set_timer((t), (d)) + #endif + #ifndef os_cancel_timer + #define os_cancel_timer(t) rtw_cancel_timer(t) + #endif + #ifndef os_del_timer + #define os_del_timer(t) rtw_del_timer(t) + #endif + #ifndef os_atoi + #define os_atoi(s) rtw_atoi(s) + #endif + +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strncpy +#define os_strncpy(d, s, n) strncpy((d), (s), (n)) +#endif +#ifndef os_strrchr +#define os_strrchr(s, c) strrchr((s), (c)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf + #ifdef _MSC_VER + #define os_snprintf _snprintf + #else + #define os_snprintf snprintf + #endif +#endif + +#endif /* OS_NO_C_LIB_DEFINES */ + + +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size, nmemb * size); +} + +void *os_xqueue_create(unsigned long uxQueueLength, unsigned long uxItemSize) ; + +int os_xqueue_receive(xqueue_handle_t xQueue, void * const pvBuffer, unsigned long xSecsToWait); + +void os_xqueue_delete(xqueue_handle_t xQueue ); + +int os_xqueue_send(xqueue_handle_t xQueue, const void * const pvItemToQueue, unsigned long xSecsToWait); + + +#endif /* OS_H */ diff --git a/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os_freertos.c b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os_freertos.c new file mode 100644 index 0000000..3d298e3 --- /dev/null +++ b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/os_freertos.c @@ -0,0 +1,100 @@ +/* + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#include +#include +#include +#include + +#ifdef CONFIG_WPS +#include "utils/os.h" + +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +#ifdef CONFIG_MEM_MONITOR +#if CONFIG_MEM_MONITOR & MEM_MONITOR_LEAK +_list wpa_mem_table; +int wpa_mem_used_num; +//int wpa_mem_used_size; +#endif +extern int min_free_heap_size; +u8* os_malloc(u32 sz) +{ + int free_heap_size = rtw_getFreeHeapSize(); + u8 *pbuf = _rtw_malloc(sz); +#if CONFIG_MEM_MONITOR & MEM_MONITOR_LEAK + add_mem_usage(&wpa_mem_table, pbuf, sz, &wpa_mem_used_num, MEM_MONITOR_FLAG_WPAS); +#else + add_mem_usage(NULL, pbuf, sz, NULL, MEM_MONITOR_FLAG_WPAS); +#endif + if(min_free_heap_size > free_heap_size) + min_free_heap_size = free_heap_size; + return pbuf; +} + +void os_mfree(u8 *pbuf, u32 sz) +{ + _rtw_mfree(pbuf, sz); +#if CONFIG_MEM_MONITOR & MEM_MONITOR_LEAK + del_mem_usage(&wpa_mem_table, pbuf, &wpa_mem_used_num, MEM_MONITOR_FLAG_WPAS); +#else + del_mem_usage(NULL, pbuf, NULL, MEM_MONITOR_FLAG_WPAS); +#endif +} +#endif//CONFIG_MEM_MONITOR + +#endif// !defined(CONFIG_PLATFORM_8195A) + +#ifndef OS_NO_C_LIB_DEFINES +char *os_strdup(const char *string_copy_from) +{ + char *string_copy_to = NULL; + string_copy_to = os_zalloc(strlen(string_copy_from) + 1); + os_memcpy((void *)string_copy_to, string_copy_from, strlen(string_copy_from)); + string_copy_to[strlen(string_copy_from)] = '\0'; + return string_copy_to; +} +#endif + +int os_get_random(unsigned char *buf, size_t len) +{ + //TODO implement it + rtw_get_random_bytes(buf, len); + return 0; +} + +int os_get_time(struct os_time *t){ + unsigned int tt = rtw_get_current_time(); + t->sec = (os_time_t) (tt / 1000); + t->usec = (os_time_t) (tt % 1000)*1000; + return 0; +} + +int os_get_reltime(struct os_reltime *t){ + os_get_time((struct os_time *)t); + return 0; +} + +void *os_xqueue_create(unsigned long uxQueueLength, unsigned long uxItemSize) +{ + return xQueueCreate( uxQueueLength, uxItemSize ); +} + +int os_xqueue_receive(xqueue_handle_t xQueue, void * const pvBuffer, unsigned long xSecsToWait) +{ + return xQueueReceive((xQueueHandle)xQueue, pvBuffer, (portTickType)(xSecsToWait*configTICK_RATE_HZ)); +} + +void os_xqueue_delete(xqueue_handle_t xQueue ) +{ + vQueueDelete((xQueueHandle)xQueue); +} + +int os_xqueue_send(xqueue_handle_t xQueue, const void * const pvItemToQueue, unsigned long xSecsToWait) +{ + return xQueueSendToBack((xQueueHandle)xQueue, pvItemToQueue, (portTickType)(xSecsToWait*configTICK_RATE_HZ)); +} +#endif diff --git a/component/common/api/wifi/rtw_wpa_supplicant/src/utils/rom/rom_wps_os.h b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/rom/rom_wps_os.h new file mode 100644 index 0000000..cd41061 --- /dev/null +++ b/component/common/api/wifi/rtw_wpa_supplicant/src/utils/rom/rom_wps_os.h @@ -0,0 +1,24 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ROM_WPS_OS_H +#define ROM_WPS_OS_H + +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) + +#include +extern struct _rom_wlan_ram_map rom_wlan_ram_map; +#define os_malloc(sz) rom_wlan_ram_map.rtw_malloc(sz) +#define os_free(p, sz) rom_wlan_ram_map.rtw_mfree(((u8*)(p)), (sz)) + +#endif + +extern u8 *WPS_realloc(u8 *old_buf, u32 old_sz, u32 new_sz); +#define os_realloc(p, os, ns) WPS_realloc(((u8*)(p)),(os),(ns)) + +#endif /* ROM_WPS_OS_H */ diff --git a/component/common/api/wifi/rtw_wpa_supplicant/src/wps/wps_defs.h b/component/common/api/wifi/rtw_wpa_supplicant/src/wps/wps_defs.h new file mode 100644 index 0000000..73d5940 --- /dev/null +++ b/component/common/api/wifi/rtw_wpa_supplicant/src/wps/wps_defs.h @@ -0,0 +1,319 @@ + +/* + * Wi-Fi Protected Setup - message definitions + * Copyright (c) 2008, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPS_DEFS_H +#define WPS_DEFS_H + + +/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */ +#define WPS_DH_GROUP (5) + +#define WPS_UUID_LEN (16) +#define WPS_NONCE_LEN (16) +#define WPS_AUTHENTICATOR_LEN (8) +#define WPS_AUTHKEY_LEN (32) +#define WPS_KEYWRAPKEY_LEN (16) +#define WPS_EMSK_LEN (32) +#define WPS_PSK_LEN (16) +#define WPS_SECRET_NONCE_LEN (16) +#define WPS_HASH_LEN (32) +#define WPS_KWA_LEN (8) +#define WPS_MGMTAUTHKEY_LEN (32) +#define WPS_MGMTENCKEY_LEN (16) +#define WPS_MGMT_KEY_ID_LEN (16) +#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN (16) +#define WPS_OOB_DEVICE_PASSWORD_LEN (32) +#define WPS_OOB_PUBKEY_HASH_LEN (20) + +/* Attribute Types */ +enum wps_attribute { + ATTR_AP_CHANNEL = 0x1001, + ATTR_ASSOC_STATE = 0x1002, + ATTR_AUTH_TYPE = 0x1003, + ATTR_AUTH_TYPE_FLAGS = 0x1004, + ATTR_AUTHENTICATOR = 0x1005, + ATTR_CONFIG_METHODS = 0x1008, + ATTR_CONFIG_ERROR = 0x1009, + ATTR_CONFIRM_URL4 = 0x100a, + ATTR_CONFIRM_URL6 = 0x100b, + ATTR_CONN_TYPE = 0x100c, + ATTR_CONN_TYPE_FLAGS = 0x100d, + ATTR_CRED = 0x100e, + ATTR_ENCR_TYPE = 0x100f, + ATTR_ENCR_TYPE_FLAGS = 0x1010, + ATTR_DEV_NAME = 0x1011, + ATTR_DEV_PASSWORD_ID = 0x1012, + ATTR_E_HASH1 = 0x1014, + ATTR_E_HASH2 = 0x1015, + ATTR_E_SNONCE1 = 0x1016, + ATTR_E_SNONCE2 = 0x1017, + ATTR_ENCR_SETTINGS = 0x1018, + ATTR_ENROLLEE_NONCE = 0x101a, + ATTR_FEATURE_ID = 0x101b, + ATTR_IDENTITY = 0x101c, + ATTR_IDENTITY_PROOF = 0x101d, + ATTR_KEY_WRAP_AUTH = 0x101e, + ATTR_KEY_ID = 0x101f, + ATTR_MAC_ADDR = 0x1020, + ATTR_MANUFACTURER = 0x1021, + ATTR_MSG_TYPE = 0x1022, + ATTR_MODEL_NAME = 0x1023, + ATTR_MODEL_NUMBER = 0x1024, + ATTR_NETWORK_INDEX = 0x1026, + ATTR_NETWORK_KEY = 0x1027, + ATTR_NETWORK_KEY_INDEX = 0x1028, + ATTR_NEW_DEVICE_NAME = 0x1029, + ATTR_NEW_PASSWORD = 0x102a, + ATTR_OOB_DEVICE_PASSWORD = 0x102c, + ATTR_OS_VERSION = 0x102d, + ATTR_POWER_LEVEL = 0x102f, + ATTR_PSK_CURRENT = 0x1030, + ATTR_PSK_MAX = 0x1031, + ATTR_PUBLIC_KEY = 0x1032, + ATTR_RADIO_ENABLE = 0x1033, + ATTR_REBOOT = 0x1034, + ATTR_REGISTRAR_CURRENT = 0x1035, + ATTR_REGISTRAR_ESTABLISHED = 0x1036, + ATTR_REGISTRAR_LIST = 0x1037, + ATTR_REGISTRAR_MAX = 0x1038, + ATTR_REGISTRAR_NONCE = 0x1039, + ATTR_REQUEST_TYPE = 0x103a, + ATTR_RESPONSE_TYPE = 0x103b, + ATTR_RF_BANDS = 0x103c, + ATTR_R_HASH1 = 0x103d, + ATTR_R_HASH2 = 0x103e, + ATTR_R_SNONCE1 = 0x103f, + ATTR_R_SNONCE2 = 0x1040, + ATTR_SELECTED_REGISTRAR = 0x1041, + ATTR_SERIAL_NUMBER = 0x1042, + ATTR_WPS_STATE = 0x1044, + ATTR_SSID = 0x1045, + ATTR_TOTAL_NETWORKS = 0x1046, + ATTR_UUID_E = 0x1047, + ATTR_UUID_R = 0x1048, + ATTR_VENDOR_EXT = 0x1049, + ATTR_VERSION = 0x104a, + ATTR_X509_CERT_REQ = 0x104b, + ATTR_X509_CERT = 0x104c, + ATTR_EAP_IDENTITY = 0x104d, + ATTR_MSG_COUNTER = 0x104e, + ATTR_PUBKEY_HASH = 0x104f, + ATTR_REKEY_KEY = 0x1050, + ATTR_KEY_LIFETIME = 0x1051, + ATTR_PERMITTED_CFG_METHODS = 0x1052, + ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053, + ATTR_PRIMARY_DEV_TYPE = 0x1054, + ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055, + ATTR_PORTABLE_DEV = 0x1056, + ATTR_AP_SETUP_LOCKED = 0x1057, + ATTR_APPLICATION_EXT = 0x1058, + ATTR_EAP_TYPE = 0x1059, + ATTR_IV = 0x1060, + ATTR_KEY_PROVIDED_AUTO = 0x1061, + ATTR_802_1X_ENABLED = 0x1062, + ATTR_APPSESSIONKEY = 0x1063, + ATTR_WEPTRANSMITKEY = 0x1064, + ATTR_REQUESTED_DEV_TYPE = 0x106a, + ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */ +}; + +#define WPS_VENDOR_ID_WFA 14122 + +/* WFA Vendor Extension subelements */ +enum { + WFA_ELEM_VERSION2 = 0x00, + WFA_ELEM_AUTHORIZEDMACS = 0x01, + WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02, + WFA_ELEM_REQUEST_TO_ENROLL = 0x03, + WFA_ELEM_SETTINGS_DELAY_TIME = 0x04 +}; + +/* Device Password ID */ +enum wps_dev_password_id { + DEV_PW_DEFAULT = 0x0000, + DEV_PW_USER_SPECIFIED = 0x0001, + DEV_PW_MACHINE_SPECIFIED = 0x0002, + DEV_PW_REKEY = 0x0003, + DEV_PW_PUSHBUTTON = 0x0004, + DEV_PW_REGISTRAR_SPECIFIED = 0x0005 +}; + +/* Message Type */ +enum wps_msg_type { + WPS_START = 0x00, + WPS_Beacon = 0x01, + WPS_ProbeRequest = 0x02, + WPS_ProbeResponse = 0x03, + WPS_M1 = 0x04, + WPS_M2 = 0x05, + WPS_M2D = 0x06, + WPS_M3 = 0x07, + WPS_M4 = 0x08, + WPS_M5 = 0x09, + WPS_M6 = 0x0a, + WPS_M7 = 0x0b, + WPS_M8 = 0x0c, + WPS_WSC_ACK = 0x0d, + WPS_WSC_NACK = 0x0e, + WPS_WSC_DONE = 0x0f +}; + +/* Authentication Type Flags */ +#define WPS_AUTH_OPEN 0x0001 +#define WPS_AUTH_WPAPSK 0x0002 +#define WPS_AUTH_SHARED 0x0004 +#define WPS_AUTH_WPA 0x0008 +#define WPS_AUTH_WPA2 0x0010 +#define WPS_AUTH_WPA2PSK 0x0020 +#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \ + WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK) + +/* Encryption Type Flags */ +#define WPS_ENCR_NONE 0x0001 +#define WPS_ENCR_WEP 0x0002 +#define WPS_ENCR_TKIP 0x0004 +#define WPS_ENCR_AES 0x0008 +#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \ + WPS_ENCR_AES) + +/* Configuration Error */ +enum wps_config_error { + WPS_CFG_NO_ERROR = 0, + WPS_CFG_OOB_IFACE_READ_ERROR = 1, + WPS_CFG_DECRYPTION_CRC_FAILURE = 2, + WPS_CFG_24_CHAN_NOT_SUPPORTED = 3, + WPS_CFG_50_CHAN_NOT_SUPPORTED = 4, + WPS_CFG_SIGNAL_TOO_WEAK = 5, + WPS_CFG_NETWORK_AUTH_FAILURE = 6, + WPS_CFG_NETWORK_ASSOC_FAILURE = 7, + WPS_CFG_NO_DHCP_RESPONSE = 8, + WPS_CFG_FAILED_DHCP_CONFIG = 9, + WPS_CFG_IP_ADDR_CONFLICT = 10, + WPS_CFG_NO_CONN_TO_REGISTRAR = 11, + WPS_CFG_MULTIPLE_PBC_DETECTED = 12, + WPS_CFG_ROGUE_SUSPECTED = 13, + WPS_CFG_DEVICE_BUSY = 14, + WPS_CFG_SETUP_LOCKED = 15, + WPS_CFG_MSG_TIMEOUT = 16, + WPS_CFG_REG_SESS_TIMEOUT = 17, + WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18 +}; + +/* RF Bands */ +#define WPS_RF_24GHZ (0x01) +#define WPS_RF_50GHZ (0x02) + +/* Config Methods */ +#define WPS_CONFIG_USBA (0x0001) +#define WPS_CONFIG_ETHERNET (0x0002) +#define WPS_CONFIG_LABEL (0x0004) +#define WPS_CONFIG_DISPLAY (0x0008) +#define WPS_CONFIG_EXT_NFC_TOKEN (0x0010) +#define WPS_CONFIG_INT_NFC_TOKEN (0x0020) +#define WPS_CONFIG_NFC_INTERFACE (0x0040) +#define WPS_CONFIG_PUSHBUTTON (0x0080) +#define WPS_CONFIG_KEYPAD (0x0100) + +#ifdef CONFIG_WPS2 +#define WPS_CONFIG_VIRT_PUSHBUTTON (0x0280) +#define WPS_CONFIG_PHY_PUSHBUTTON (0x0480) +#define WPS_CONFIG_VIRT_DISPLAY (0x2008) +#define WPS_CONFIG_PHY_DISPLAY (0x4008) +#endif /* CONFIG_WPS2 */ + +/* Connection Type Flags */ +#define WPS_CONN_ESS (0x01) +#define WPS_CONN_IBSS (0x02) + +/* Wi-Fi Protected Setup State */ +enum wps_state { + WPS_STATE_NOT_CONFIGURED = 1, + WPS_STATE_CONFIGURED = 2 +}; + +/* Association State */ +enum wps_assoc_state { + WPS_ASSOC_NOT_ASSOC = 0, + WPS_ASSOC_CONN_SUCCESS = 1, + WPS_ASSOC_CFG_FAILURE = 2, + WPS_ASSOC_FAILURE = 3, + WPS_ASSOC_IP_FAILURE = 4 +}; + + +#define WPS_DEV_OUI_WFA (0x0050f204) + +enum wps_dev_categ { + WPS_DEV_COMPUTER = 1, + WPS_DEV_INPUT = 2, + WPS_DEV_PRINTER = 3, + WPS_DEV_CAMERA = 4, + WPS_DEV_STORAGE = 5, + WPS_DEV_NETWORK_INFRA = 6, + WPS_DEV_DISPLAY = 7, + WPS_DEV_MULTIMEDIA = 8, + WPS_DEV_GAMING = 9, + WPS_DEV_PHONE = 10 +}; + +enum wps_dev_subcateg { + WPS_DEV_COMPUTER_PC = 1, + WPS_DEV_COMPUTER_SERVER = 2, + WPS_DEV_COMPUTER_MEDIA_CENTER = 3, + + WPS_DEV_PRINTER_PRINTER = 1, + WPS_DEV_PRINTER_SCANNER = 2, + + WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1, + + WPS_DEV_STORAGE_NAS = 1, + + WPS_DEV_NETWORK_INFRA_AP = 1, + WPS_DEV_NETWORK_INFRA_ROUTER = 2, + WPS_DEV_NETWORK_INFRA_SWITCH = 3, + + WPS_DEV_DISPLAY_TV = 1, + WPS_DEV_DISPLAY_PICTURE_FRAME = 2, + WPS_DEV_DISPLAY_PROJECTOR = 3, + + WPS_DEV_MULTIMEDIA_DAR = 1, + WPS_DEV_MULTIMEDIA_PVR = 2, + WPS_DEV_MULTIMEDIA_MCX = 3, + + WPS_DEV_GAMING_XBOX = 1, + WPS_DEV_GAMING_XBOX360 = 2, + WPS_DEV_GAMING_PLAYSTATION = 3, + + WPS_DEV_PHONE_WINDOWS_MOBILE = 1 +}; + + +/* Request Type */ +enum wps_request_type { + WPS_REQ_ENROLLEE_INFO = 0, + WPS_REQ_ENROLLEE = 1, + WPS_REQ_REGISTRAR = 2, + WPS_REQ_WLAN_MANAGER_REGISTRAR = 3 +}; + +/* Response Type */ +enum wps_response_type { + WPS_RESP_ENROLLEE_INFO = 0, + WPS_RESP_ENROLLEE = 1, + WPS_RESP_REGISTRAR = 2, + WPS_RESP_AP = 3 +}; + +/* Walk Time for push button configuration (in seconds) */ +#define WPS_PBC_WALK_TIME (120) + +#define WPS_MAX_AUTHORIZED_MACS (5) + +#endif /* WPS_DEFS_H */ + diff --git a/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_wps_config.c b/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_wps_config.c new file mode 100644 index 0000000..3ca472d --- /dev/null +++ b/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_wps_config.c @@ -0,0 +1,528 @@ +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" +#include "queue.h" +#include "utils/os.h" +#include +#include +#include "wifi/wifi_conf.h" +#include "wps/wps_defs.h" +#include + +/** + * struct wps_credential - WPS Credential + * @ssid: SSID + * @ssid_len: Length of SSID + * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags) + * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags) + * @key_idx: Key index + * @key: Key + * @key_len: Key length in octets + * @mac_addr: MAC address of the Credential receiver + * @cred_attr: Unparsed Credential attribute data (used only in cred_cb()); + * this may be %NULL, if not used + * @cred_attr_len: Length of cred_attr in octets + * @ap_channel: AP channel + */ +struct dev_credential { + u8 ssid[32]; + size_t ssid_len; + u16 auth_type; + u16 encr_type; + u8 key_idx; + u8 key[65]; + size_t key_len; + u8 mac_addr[6]; + const u8 *cred_attr; + size_t cred_attr_len; + u16 ap_channel; +}; + +typedef struct { + char *target_ssid; + u16 config_method; + _sema scan_sema; + int isoverlap; +} internal_wps_scan_handler_arg_t; + +#define WLAN0_NAME "wlan0" +#ifndef ENABLE +#define ENABLE (1) +#endif +#ifndef DISABLE +#define DISABLE (0) +#endif +#define STACKSIZE 512 + + +//static xSemaphoreHandle wps_reconnect_semaphore; +//static struct _WIFI_NETWORK wifi_get_from_certificate = {0}; + +#define WPS_AUTH_TYPE_OPEN (0x0001) +#define WPS_AUTH_TYPE_WPA_PERSONAL (0x0002) +#define WPS_AUTH_TYPE_WPA_ENTERPRISE (0x0008) +#define WPS_AUTH_TYPE_WPA2_PERSONAL (0x0010) +#define WPS_AUTH_TYPE_WPA2_ENTERPRISE (0x0020) + +#define WIFI_LINK_DISCONNECTED (0) +#define WIFI_LINK_CONNECTED (1) +#define SCAN_BUFFER_LENGTH (4096) + + +#ifdef CONFIG_WPS +#if CONFIG_ENABLE_WPS +xqueue_handle_t queue_for_credential; +char wps_pin_code[32]; +u16 config_method; +u8 wps_password_id; + +void wps_check_and_show_connection_info(void) +{ + rtw_wifi_setting_t setting; +#if CONFIG_LWIP_LAYER + /* Start DHCP Client */ + LwIP_DHCP(0, DHCP_START); +#endif + wifi_get_setting(WLAN0_NAME, &setting); + wifi_show_setting(WLAN0_NAME, &setting); +} + +static void wps_config_wifi_setting(rtw_network_info_t *wifi, struct dev_credential *dev_cred) +{ + printf("\r\nwps_config_wifi_setting\n"); + //memcpy((void *)wifi->ssid, (void *)dev_cred->ssid, dev_cred->ssid_len); + strcpy((char*)wifi->ssid.val, (char*)&dev_cred->ssid[0]); + printf("\r\nwps_wifi.ssid = %s\n", wifi->ssid.val); + wifi->ssid.len = dev_cred->ssid_len; + printf("\r\nwps_wifi.ssid_len = %d\n", wifi->ssid.len); + + switch(dev_cred->auth_type) { + case WPS_AUTH_TYPE_OPEN : + printf("\r\nsecurity_type = RTW_SECURITY_OPEN\n"); + wifi->security_type = RTW_SECURITY_OPEN; + break; + case WPS_AUTH_TYPE_WPA_PERSONAL : + case WPS_AUTH_TYPE_WPA_ENTERPRISE : + printf("\r\nsecurity_type = RTW_SECURITY_WPA_AES_PSK\n"); + wifi->security_type = RTW_SECURITY_WPA_AES_PSK; + break; + case WPS_AUTH_TYPE_WPA2_PERSONAL : + case WPS_AUTH_TYPE_WPA2_ENTERPRISE : + printf("\r\nsecurity_type = RTW_SECURITY_WPA2_AES_PSK\n"); + wifi->security_type = RTW_SECURITY_WPA2_AES_PSK; + break; + } + + printf("\r\nwps_wifi.security_type = %d\n", wifi->security_type); + + //memcpy(wifi->password, dev_cred->key, dev_cred->key_len); + wifi->password = dev_cred->key; + printf("\r\nwps_wifi.password = %s\n", wifi->password); + wifi->password_len = dev_cred->key_len; + printf("\r\nwps_wifi.password_len = %d", wifi->password_len); + //xSemaphoreGive(wps_reconnect_semaphore); + //printf("\r\nrelease wps_reconnect_semaphore"); +} + +static void wps_connect_to_AP_by_certificate(rtw_network_info_t *wifi) +{ +#define RETRY_COUNT 3 + int retry_count = RETRY_COUNT, ret; + + printf("\r\n=============== wifi_certificate_info ===============\n"); + printf("\r\nwps_wifi.ssid = %s\n", wifi->ssid.val); + printf("\r\nsecurity_type = %d\n", wifi->security_type); + printf("\r\nwps_wifi.password = %s\n", wifi->password); + printf("\r\nssid_len = %d\n", wifi->ssid.len); + printf("\r\npassword_len = %d\n", wifi->password_len); + while (1) { + ret = wifi_connect((char*)wifi->ssid.val, + wifi->security_type, + (char*)wifi->password, + wifi->ssid.len, + wifi->password_len, + wifi->key_id, + NULL); + if (ret == RTW_SUCCESS) { + if(retry_count == RETRY_COUNT) + rtw_msleep_os(1000); //When start wps with OPEN AP, AP will send a disassociate frame after STA connected, need reconnect here. + if(RTW_SUCCESS == wifi_is_ready_to_transceive(RTW_STA_INTERFACE)){ + //printf("\r\n[WPS]Ready to tranceive!!\n"); + wps_check_and_show_connection_info(); + break; + } + } + if (retry_count == 0) { + printf("\r\n[WPS]Join bss failed\n"); + break; + } + retry_count --; + } +} + +static int wps_connect_to_AP_by_open_system(char *target_ssid) +{ + int retry_count = 3, ret; + + if (target_ssid != NULL) { + rtw_msleep_os(500); //wait scan complete. + while (1) { + ret = wifi_connect(target_ssid, + RTW_SECURITY_OPEN, + NULL, + strlen(target_ssid), + 0, + 0, + NULL); + if (ret == RTW_SUCCESS) { + //wps_check_and_show_connection_info(); + break; + } + if (retry_count == 0) { + printf("\r\n[WPS]Join bss failed\n"); + return -1; + } + retry_count --; + } + // + } else { + printf("\r\n[WPS]Target SSID is NULL\n"); + } + + return 0; +} + +static void process_wps_scan_result( rtw_scan_result_t* record, void * user_data ) +{ + internal_wps_scan_handler_arg_t *wps_arg = (internal_wps_scan_handler_arg_t *)user_data; + + if (record->wps_type != 0xff) { + if (wps_arg->config_method == WPS_CONFIG_PUSHBUTTON) { + if (record->wps_type == 0x04) { + wps_password_id = record->wps_type; + if (++wps_arg->isoverlap == 0) { + memcpy(&wps_arg->target_ssid[0], record->SSID.val, record->SSID.len); + wps_arg->target_ssid[record->SSID.len] = '\0'; + printf("\r\n[pbc]Record first triger wps AP = %s\n", wps_arg->target_ssid); + } + } + } else if (wps_arg->config_method == WPS_CONFIG_DISPLAY) { + if (record->wps_type == 0x00) { + wps_arg->isoverlap = 0; + wps_password_id = record->wps_type; + memcpy(&wps_arg->target_ssid[0], record->SSID.val, record->SSID.len); + wps_arg->target_ssid[record->SSID.len] = '\0'; + printf("\r\n[pin]find out first triger wps AP = %s\n", wps_arg->target_ssid); + } + } + } +} + +static rtw_result_t wps_scan_result_handler( rtw_scan_handler_result_t* malloced_scan_result ) +{ + internal_wps_scan_handler_arg_t *wps_arg = (internal_wps_scan_handler_arg_t *)malloced_scan_result->user_data; + if (malloced_scan_result->scan_complete != RTW_TRUE) + { + rtw_scan_result_t* record = &malloced_scan_result->ap_details; + record->SSID.val[record->SSID.len] = 0; /* Ensure the SSID is null terminated */ + + process_wps_scan_result(record, malloced_scan_result->user_data); + } + else + { + printf("\r\nWPS scan done!\r\n"); + rtw_up_sema(&wps_arg->scan_sema); + } + return RTW_SUCCESS; +} + + +static int wps_find_out_triger_wps_AP(char *target_ssid, u16 config_method) +{ + internal_wps_scan_handler_arg_t wps_arg = {0}; + + wps_password_id = 0xFF; + + wps_arg.isoverlap = -1; + wps_arg.config_method = config_method; + wps_arg.target_ssid = target_ssid; + rtw_init_sema(&wps_arg.scan_sema, 0); + if(wps_arg.scan_sema == NULL) return RTW_ERROR; + + if(wifi_scan_networks(wps_scan_result_handler, &wps_arg ) != RTW_SUCCESS){ + printf("\n\rERROR: wifi scan failed"); + goto exit; + } + if(rtw_down_timeout_sema(&wps_arg.scan_sema, SCAN_LONGEST_WAIT_TIME) == RTW_FALSE){ + printf("\r\nWPS scan done early!\r\n"); + } + +exit: + rtw_free_sema(&wps_arg.scan_sema); + + return wps_arg.isoverlap; +} +extern void wpas_wps_notify_wps_finish_hdl(char *buf, int buf_len, int flags, void *userdata); +extern void wpas_wsc_eapol_recvd_hdl(char *buf, int buf_len, int flags, void* handler_user_data); +int wps_start(u16 wps_config, char *pin, u8 channel, char *ssid) +{ + struct dev_credential dev_cred; + rtw_network_info_t wifi = {0}; + char target_ssid[64]; + int is_overlap = -1; + u32 start_time = rtw_get_current_time(); + int ret = 0; + + memset(&dev_cred, 0, sizeof(struct dev_credential)); + memset(target_ssid, 0, 64); + if((wps_config != WPS_CONFIG_PUSHBUTTON) + && (wps_config != WPS_CONFIG_DISPLAY) + && (wps_config != WPS_CONFIG_KEYPAD)){ + printf("\n\rWPS: Wps method(%d) is wrong. Not triger WPS.\n", wps_config); + return -1; + } + config_method = wps_config; + + if(wps_config == WPS_CONFIG_DISPLAY + || wps_config == WPS_CONFIG_KEYPAD) { + if(pin) + strcpy(wps_pin_code, pin); + else{ + printf("\n\rWPS: PIN is NULL. Not triger WPS.\n"); + return -1; + } + } + + if(!ssid) { + while (1) { + unsigned int current_time = rtw_get_current_time(); + if (rtw_systime_to_sec(current_time - start_time) < 120) { + is_overlap = wps_find_out_triger_wps_AP(&target_ssid[0], wps_config); + if ((is_overlap == 0) || (is_overlap > 0)) + break; + } else { + printf("\r\nWPS: WPS Walking Time Out\n"); + return 0; + } + } + + if (is_overlap > 0) { + printf("\r\nWPS: WPS session overlap. Not triger WPS.\n"); + return 0; + } + }else{ + rtw_memcpy(target_ssid, ssid, strlen(ssid)); + } + + if (queue_for_credential != NULL) { + os_xqueue_delete(queue_for_credential); + queue_for_credential = NULL; + } + queue_for_credential = os_xqueue_create(1, sizeof(struct dev_credential)); + if(!queue_for_credential) + return -1; + + wifi_reg_event_handler(WIFI_EVENT_WPS_FINISH, wpas_wps_notify_wps_finish_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_EAPOL_RECVD, wpas_wsc_eapol_recvd_hdl, NULL); + + wifi_set_wps_phase(ENABLE); + ret = wps_connect_to_AP_by_open_system(target_ssid); + if(ret < 0){ + printf("\n\rWPS: WPS Fail!!\n"); + goto exit; + } + os_xqueue_receive(queue_for_credential, &dev_cred, 120); + if (dev_cred.ssid[0] != 0 && dev_cred.ssid_len <= 32) { + wps_config_wifi_setting(&wifi, &dev_cred); + wifi_set_wps_phase(DISABLE); + wps_connect_to_AP_by_certificate(&wifi); + goto exit1; + } else { + printf("\n\rWPS: WPS FAIL!!!\n"); + } +exit: + wifi_set_wps_phase(DISABLE); +exit1: + if (queue_for_credential != NULL) { + os_xqueue_delete(queue_for_credential); + queue_for_credential = NULL; + } + + wifi_unreg_event_handler(WIFI_EVENT_WPS_FINISH, wpas_wps_notify_wps_finish_hdl); + wifi_unreg_event_handler(WIFI_EVENT_EAPOL_RECVD, wpas_wsc_eapol_recvd_hdl); + + return 0; +} + +#ifdef CONFIG_WPS_AP +int ap_wps_start(u16 wps_config, char *pin) +{ + u8 authorized_mac[ETH_ALEN]; + int ret = 0; + u32 pin_val = 0; + + printf("\n\rWPS-AP: wps_config(%x).\n", wps_config); + if((wps_config != WPS_CONFIG_PUSHBUTTON) + && (wps_config != WPS_CONFIG_DISPLAY) + && (wps_config != WPS_CONFIG_KEYPAD)){ + printf("\n\rWPS-AP: Wps method(%d) is wrong. Not triger WPS.\n", wps_config); + return -1; + } + config_method = wps_config; + if(wps_config == WPS_CONFIG_DISPLAY + || wps_config == WPS_CONFIG_KEYPAD) { + if(pin) + strcpy(wps_pin_code, pin); + else{ + printf("\n\rWPS-AP: PIN is NULL. Not triger WPS.\n"); + return -1; + } + } + + if (queue_for_credential != NULL) { + os_xqueue_delete(queue_for_credential); + queue_for_credential = NULL; + } + + queue_for_credential = os_xqueue_create(1, sizeof(authorized_mac)); + if(!queue_for_credential) + return -1; + + wifi_set_wps_phase(1); + + if(wps_config == WPS_CONFIG_KEYPAD) + { + pin_val = atoi(pin); + if (!wps_pin_valid(pin_val)) { + printf("\n\rWPS-AP: Enter pin code is unvalid."); + goto exit; + } + ret = wpas_wps_registrar_add_pin((unsigned char const*)pin, strlen(pin)); + } + else if(wps_config == WPS_CONFIG_DISPLAY) + ret = wpas_wps_registrar_add_pin((unsigned char const*)pin, strlen(pin)); + else + ret = wpas_wps_registrar_button_pushed(); + + if(ret<0) + goto exit; + + printf("\n\rWPS-AP: wait for STA connect!\n"); + os_xqueue_receive(queue_for_credential, authorized_mac, 120); //max wait 2min + + if(!wpas_wps_registrar_check_done()) + { + ret = -1; + wpas_wps_registrar_wps_cancel(); + } + +exit: + wifi_set_wps_phase(0); + os_xqueue_delete(queue_for_credential); + queue_for_credential = NULL; + + return ret; +} +#endif //CONFIG_WPS_AP + +void wps_judge_staion_disconnect(void) +{ + int mode = 0; + unsigned char ssid[33]; + + wext_get_mode(WLAN0_NAME, &mode); + + switch(mode) { + case IW_MODE_MASTER: //In AP mode + rltk_wlan_deinit(); + rltk_wlan_init(0,RTW_MODE_STA); + rltk_wlan_start(0); + break; + case IW_MODE_INFRA: //In STA mode + if(wext_get_ssid(WLAN0_NAME, ssid) > 0) + wifi_disconnect(); + } +} + +void cmd_wps(int argc, char **argv) +{ + wps_judge_staion_disconnect(); + + if((argc == 2 || argc == 3 ) && (argv[1] != NULL)){ + if(strcmp(argv[1],"pin") == 0){ + unsigned int pin_val = 0; + /* start pin */ + if(argc == 2){ + char device_pin[10]; + pin_val = wps_generate_pin(); + sprintf(device_pin, "%08d", pin_val); + printf("\n\rWPS: Start WPS PIN Display. PIN: %s\n\r", device_pin); + wps_start(WPS_CONFIG_DISPLAY, (char*)device_pin, 0, NULL); + }else{ + pin_val = atoi(argv[2]); + if (!wps_pin_valid(pin_val)) { + printf("\n\rWPS: Device pin code is invalid. Not triger WPS.\n"); + return; + } + printf("\n\rWPS: Start WPS PIN Keypad.\n\r"); + wps_start(WPS_CONFIG_KEYPAD, argv[2], 0, NULL); + } + }else if(strcmp(argv[1],"pbc") == 0){ + /* start pbc */ + printf("\n\rWPS: Start WPS PBC.\n\r"); + wps_start(WPS_CONFIG_PUSHBUTTON, NULL, 0, NULL); + }else{ + printf("\n\rWPS: Wps Method is wrong. Not triger WPS.\n"); + return; + } + } +} + +#ifdef CONFIG_WPS_AP +/* +cmd_ap_wps for AP WSC setting. command style: +cmd_ap_wps pbc or cmd_ap_wps pin 12345678 +*/ +void cmd_ap_wps(int argc, char **argv) +{ + if(rltk_wlan_running(WLAN1_IDX)){ + printf("\n\rNot support con-current softAP WSC!\n\r"); + return; + } + + if((argc == 2 || argc == 3) && (argv[1] != NULL)) { + if (strcmp(argv[1],"pin") == 0 ) { + unsigned int pin_val = 0; + if(argc == 3){ + pin_val = atoi(argv[2]); + if (!wps_pin_valid(pin_val)) { + printf("\n\rWPS-AP: Device pin code is invalid. Not trigger WPS.\n\r"); + return; + } + printf("\n\rWPS-AP: Start AP WPS PIN Keypad.\n"); + ap_wps_start(WPS_CONFIG_KEYPAD, argv[2]); + }else{ + char device_pin[10]; + pin_val = wps_generate_pin(); + sprintf(device_pin, "%08d", pin_val); + printf("\n\rWPS: Start WPS PIN Display. PIN: %s\n\r", device_pin); + ap_wps_start(WPS_CONFIG_DISPLAY, (char*)device_pin); + } + }else if (strcmp(argv[1],"pbc") == 0) { + printf("\n\rWPS-AP: Start AP WPS PBC\n"); + ap_wps_start(WPS_CONFIG_PUSHBUTTON, NULL); + }else{ + printf("\n\rWPS-AP Usage:\"wifi_ap_wps pin [pin_code]\" or \"wifi_ap_wps pbc\"\n"); + return; + } + } else { + printf("\n\rWPS-AP Usage:\"wifi_ap_wps pin [pin_code]\" or \"wifi_ap_wps pbc\"\n"); + } + return; +} +#endif //CONFIG_WPS_AP +#endif //CONFIG_ENABLE_WPS +#endif //#ifdef CONFIG_WPS diff --git a/component/common/api/wifi/wifi_conf.c b/component/common/api/wifi/wifi_conf.c new file mode 100644 index 0000000..27743a1 --- /dev/null +++ b/component/common/api/wifi/wifi_conf.c @@ -0,0 +1,1609 @@ +//----------------------------------------------------------------------------// +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include +#include "main.h" +#include +//#include +#include +#include +#include +#include +#include "tcpip.h" +#include + +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT +#include "wlan_fast_connect/example_wlan_fast_connect.h" +#endif + +/****************************************************** + * Constants + ******************************************************/ +#define SCAN_USE_SEMAPHORE 0 + +#define RTW_JOIN_TIMEOUT 15000 + +#define JOIN_ASSOCIATED (uint32_t)(1 << 0) +#define JOIN_AUTHENTICATED (uint32_t)(1 << 1) +#define JOIN_LINK_READY (uint32_t)(1 << 2) +#define JOIN_SECURITY_COMPLETE (uint32_t)(1 << 3) +#define JOIN_COMPLETE (uint32_t)(1 << 4) +#define JOIN_NO_NETWORKS (uint32_t)(1 << 5) +#define JOIN_WRONG_SECURITY (uint32_t)(1 << 6) +#define JOIN_HANDSHAKE_DONE (uint32_t)(1 << 7) + +/****************************************************** + * Type Definitions + ******************************************************/ + +/****************************************************** + * Variables Declarations + ******************************************************/ + +extern struct netif xnetif[NET_IF_NUM]; + +/****************************************************** + * Variables Definitions + ******************************************************/ +static internal_scan_handler_t scan_result_handler_ptr = {0, 0, 0, RTW_FALSE, 0, 0, 0, 0, 0}; +static internal_join_result_t* join_user_data; +static rtw_mode_t wifi_mode; +static int error_flag; +uint32_t rtw_join_status; + +/****************************************************** + * Variables Definitions + ******************************************************/ + +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif +#ifndef WLAN1_NAME + #define WLAN1_NAME "wlan1" +#endif +/* Give default value if not defined */ +#ifndef NET_IF_NUM +#ifdef CONFIG_CONCURRENT_MODE +#define NET_IF_NUM 2 +#else +#define NET_IF_NUM 1 +#endif +#endif + +/*Static IP ADDRESS*/ +#ifndef IP_ADDR0 +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 1 +#define IP_ADDR3 80 +#endif + +/*NETMASK*/ +#ifndef NETMASK_ADDR0 +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef GW_ADDR0 +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 1 +#define GW_ADDR3 1 +#endif + +/*Static IP ADDRESS*/ +#ifndef AP_IP_ADDR0 +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 +#endif + +/*NETMASK*/ +#ifndef AP_NETMASK_ADDR0 +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef AP_GW_ADDR0 +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 +#endif + +/****************************************************** + * Function Definitions + ******************************************************/ + +#if CONFIG_WLAN +//----------------------------------------------------------------------------// +static int wifi_connect_local(rtw_network_info_t *pWifi) +{ + int ret = 0; + u8 bssid[12] = {0}; + + if(is_promisc_enabled()) + promisc_set(0, NULL, 0); + + if(!pWifi) return -1; + switch(pWifi->security_type){ + case RTW_SECURITY_OPEN: + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_NONE, NULL, 0, 0, 0, 0, NULL, 0); + break; + case RTW_SECURITY_WEP_PSK: + case RTW_SECURITY_WEP_SHARED: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_SHARED_KEY); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_WEP, NULL, pWifi->key_id, 1 /* set tx key */, 0, 0, pWifi->password, pWifi->password_len); + break; + case RTW_SECURITY_WPA_TKIP_PSK: + case RTW_SECURITY_WPA2_TKIP_PSK: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_OPEN_SYSTEM); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_TKIP, NULL, 0, 0, 0, 0, NULL, 0); + if(ret == 0) + ret = wext_set_passphrase(WLAN0_NAME, pWifi->password, pWifi->password_len); + break; + case RTW_SECURITY_WPA_AES_PSK: + case RTW_SECURITY_WPA2_AES_PSK: + case RTW_SECURITY_WPA2_MIXED_PSK: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_OPEN_SYSTEM); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_CCMP, NULL, 0, 0, 0, 0, NULL, 0); + if(ret == 0) + ret = wext_set_passphrase(WLAN0_NAME, pWifi->password, pWifi->password_len); + break; + default: + ret = -1; + printf("\n\rWIFICONF: security type(0x%x) is not supported.\n\r", pWifi->security_type); + break; + } + if(ret == 0) + ret = wext_set_ssid(WLAN0_NAME, pWifi->ssid.val, pWifi->ssid.len); + return ret; +} + +static int wifi_connect_bssid_local(rtw_network_info_t *pWifi) +{ + int ret = 0; + u8 bssid[12] = {0}; + + if(is_promisc_enabled()) + promisc_set(0, NULL, 0); + + if(!pWifi) return -1; + switch(pWifi->security_type){ + case RTW_SECURITY_OPEN: + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_NONE, NULL, 0, 0, 0, 0, NULL, 0); + break; + case RTW_SECURITY_WEP_PSK: + case RTW_SECURITY_WEP_SHARED: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_SHARED_KEY); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_WEP, NULL, pWifi->key_id, 1 /* set tx key */, 0, 0, pWifi->password, pWifi->password_len); + break; + case RTW_SECURITY_WPA_TKIP_PSK: + case RTW_SECURITY_WPA2_TKIP_PSK: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_OPEN_SYSTEM); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_TKIP, NULL, 0, 0, 0, 0, NULL, 0); + if(ret == 0) + ret = wext_set_passphrase(WLAN0_NAME, pWifi->password, pWifi->password_len); + break; + case RTW_SECURITY_WPA_AES_PSK: + case RTW_SECURITY_WPA2_AES_PSK: + case RTW_SECURITY_WPA2_MIXED_PSK: + ret = wext_set_auth_param(WLAN0_NAME, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_OPEN_SYSTEM); + if(ret == 0) + ret = wext_set_key_ext(WLAN0_NAME, IW_ENCODE_ALG_CCMP, NULL, 0, 0, 0, 0, NULL, 0); + if(ret == 0) + ret = wext_set_passphrase(WLAN0_NAME, pWifi->password, pWifi->password_len); + break; + default: + ret = -1; + printf("\n\rWIFICONF: security type(0x%x) is not supported.\n\r", pWifi->security_type); + break; + } + if(ret == 0){ + memcpy(bssid, pWifi->bssid.octet, ETH_ALEN); + if(pWifi->ssid.len){ + bssid[ETH_ALEN] = '#'; + bssid[ETH_ALEN + 1] = '@'; + memcpy(bssid + ETH_ALEN + 2, &pWifi, sizeof(pWifi)); + } + ret = wext_set_bssid(WLAN0_NAME, bssid); + } + return ret; +} + +static void wifi_no_network_hdl(char* buf, int buf_len, int flags, void* userdata) +{ + if(join_user_data!=NULL) + rtw_join_status = JOIN_NO_NETWORKS; +} + +static void wifi_connected_hdl( char* buf, int buf_len, int flags, void* userdata) +{ + if((join_user_data!=NULL)&&((join_user_data->network_info.security_type == RTW_SECURITY_OPEN) || + (join_user_data->network_info.security_type == RTW_SECURITY_WEP_PSK))){ + error_flag = RTW_NO_ERROR; + rtw_join_status = JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY; + rtw_up_sema(&join_user_data->join_sema); + }else if((join_user_data!=NULL)&&((join_user_data->network_info.security_type == RTW_SECURITY_WPA2_AES_PSK) )){ + rtw_join_status = JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY; + } +} +static void wifi_handshake_done_hdl( char* buf, int buf_len, int flags, void* userdata) +{ + error_flag = RTW_NO_ERROR; + rtw_join_status = JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY|JOIN_HANDSHAKE_DONE; + if(join_user_data != NULL) + rtw_up_sema(&join_user_data->join_sema); +} + +static void wifi_disconn_hdl( char* buf, int buf_len, int flags, void* userdata) +{ + if(join_user_data != NULL){ + if(join_user_data->network_info.security_type == RTW_SECURITY_OPEN){ + + if(rtw_join_status == JOIN_NO_NETWORKS) + error_flag = RTW_NONE_NETWORK; + else if(rtw_join_status ==JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY) + error_flag = RTW_DHCP_FAIL; + + }else if(join_user_data->network_info.security_type == RTW_SECURITY_WEP_PSK){ + if(rtw_join_status == JOIN_NO_NETWORKS) + error_flag = RTW_NONE_NETWORK; + else if(rtw_join_status == 0) + error_flag = RTW_CONNECT_FAIL; + else if(rtw_join_status ==JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY){ + error_flag = RTW_DHCP_FAIL; + printf("\r\n IN WEP SECURITY MODE,wrong password is the same to dhcp fail"); + } + }else if(join_user_data->network_info.security_type == RTW_SECURITY_WPA2_AES_PSK){ + if(rtw_join_status ==JOIN_NO_NETWORKS) + error_flag = RTW_NONE_NETWORK; + else if(rtw_join_status == 0) + error_flag = RTW_CONNECT_FAIL; + else if(rtw_join_status ==JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY) + error_flag = RTW_WRONG_PASSWORD; + else if(rtw_join_status == JOIN_COMPLETE | JOIN_SECURITY_COMPLETE | JOIN_ASSOCIATED | JOIN_AUTHENTICATED | JOIN_LINK_READY|JOIN_HANDSHAKE_DONE) + error_flag = RTW_DHCP_FAIL; + } + + }else{ + if(error_flag == RTW_NO_ERROR) //wifi_disconn_hdl will be dispatched one more time after join_user_data = NULL add by frankie + error_flag = RTW_DHCP_FAIL; + } + + if(join_user_data != NULL) + rtw_up_sema(&join_user_data->join_sema); + //printf("\r\nWiFi Disconnect. Error flag is %d.\n", error_flag); +} + +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT +#define WLAN0_NAME "wlan0" + +void restore_wifi_info_to_flash() +{ + + u8 * data_to_flash; + u32 data_to_flash_len = 0, channel = 0; + u8 index = 0; + u8 *ifname[1] = {WLAN0_NAME}; + rtw_wifi_setting_t setting; + //struct security_priv *psecuritypriv = &padapter->securitypriv; + //WLAN_BSSID_EX *pcur_bss = pmlmepriv->cur_network.network; + + // SSID + passphrase + psk + channel + data_to_flash_len = NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1) + A_SHA_DIGEST_LEN * 2 + 4; + data_to_flash = rtw_zmalloc(data_to_flash_len); + + if(data_to_flash && p_write_reconnect_ptr){ + if(wifi_get_setting((const char*)ifname[0],&setting) || setting.mode == RTW_MODE_AP){ + printf("\r\n %s():wifi_get_setting fail or ap mode", __func__); + return; + } + channel = setting.channel; + + rtw_memset(psk_essid[index], 0, sizeof(psk_essid[index])); + strncpy(psk_essid[index], setting.ssid, strlen(setting.ssid)); + switch(setting.security_type){ + case RTW_SECURITY_OPEN: + rtw_memset(psk_passphrase[index], 0, sizeof(psk_passphrase[index])); + rtw_memset(wpa_global_PSK[index], 0, sizeof(wpa_global_PSK[index])); + break; + case RTW_SECURITY_WEP_PSK: + channel |= (setting.key_idx) << 28; + rtw_memset(psk_passphrase[index], 0, sizeof(psk_passphrase[index])); + rtw_memset(wpa_global_PSK[index], 0, sizeof(wpa_global_PSK[index])); + rtw_memcpy(psk_passphrase[index], setting.password, sizeof(psk_passphrase[index])); + break; + case RTW_SECURITY_WPA_AES_PSK: + case RTW_SECURITY_WPA2_AES_PSK: + default: + break; + } + + memcpy(data_to_flash, psk_essid[index], NDIS_802_11_LENGTH_SSID + 4); + if (strlen(psk_passphrase64) == 64) { + memcpy(data_to_flash + NDIS_802_11_LENGTH_SSID + 4, psk_passphrase64, IW_PASSPHRASE_MAX_SIZE + 1); + } else { + memcpy(data_to_flash + NDIS_802_11_LENGTH_SSID + 4, psk_passphrase[index], IW_PASSPHRASE_MAX_SIZE + 1); + } + memcpy(data_to_flash + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1), wpa_global_PSK[index], A_SHA_DIGEST_LEN * 2); + memcpy(data_to_flash + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1) + A_SHA_DIGEST_LEN * 2, &channel, 4); + + //call callback function in user program + p_write_reconnect_ptr(data_to_flash, data_to_flash_len); + + if(data_to_flash) + rtw_free(data_to_flash); + } +} + +#endif + +//----------------------------------------------------------------------------// +int wifi_connect( + char *ssid, + rtw_security_t security_type, + char *password, + int ssid_len, + int password_len, + int key_id, + void *semaphore) +{ + xSemaphoreHandle join_semaphore; + rtw_result_t result = RTW_SUCCESS; + u8 wep_hex = 0; + u8 wep_pwd[14] = {0}; + + rtw_join_status = 0;//clear for last connect status + error_flag = 0 ;//clear for last connect status + internal_join_result_t *join_result = (internal_join_result_t *)rtw_zmalloc(sizeof(internal_join_result_t)); + if(!join_result) { + return RTW_NOMEM; + } + + join_result->network_info.ssid.len = ssid_len > 32 ? 32 : ssid_len; + rtw_memcpy(join_result->network_info.ssid.val, ssid, ssid_len); + + if ( ( ( ( password_len > RTW_MAX_PSK_LEN ) || + ( password_len < RTW_MIN_PSK_LEN ) ) && + ( ( security_type == RTW_SECURITY_WPA_TKIP_PSK ) || + ( security_type == RTW_SECURITY_WPA_AES_PSK ) || + ( security_type == RTW_SECURITY_WPA2_AES_PSK ) || + ( security_type == RTW_SECURITY_WPA2_TKIP_PSK ) || + ( security_type == RTW_SECURITY_WPA2_MIXED_PSK ) ) )) { + return RTW_INVALID_KEY; + } + + if ((security_type == RTW_SECURITY_WEP_PSK)|| + (security_type ==RTW_SECURITY_WEP_SHARED)) { + if ((password_len != 5) && (password_len != 13) && + (password_len != 10)&& (password_len != 26)) { + return RTW_INVALID_KEY; + } else { + + if(password_len == 10) { + + u32 p[5]; + u8 i = 0; + sscanf((const char*)password, "%02x%02x%02x%02x%02x", &p[0], &p[1], &p[2], &p[3], &p[4]); + for(i=0; i< 5; i++) + wep_pwd[i] = (u8)p[i]; + wep_pwd[5] = '\0'; + password_len = 5; + wep_hex = 1; + } else if (password_len == 26) { + u32 p[13]; + u8 i = 0; + sscanf((const char*)password, "%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x", &p[0], &p[1], &p[2], &p[3], &p[4],\ + &p[5], &p[6], &p[7], &p[8], &p[9], &p[10], &p[11], &p[12]); + for(i=0; i< 13; i++) + wep_pwd[i] = (u8)p[i]; + wep_pwd[13] = '\0'; + password_len = 13; + wep_hex = 1; + } + } + } + + + join_result->network_info.password_len = password_len; + if(password_len) { + /* add \0 to the end */ + join_result->network_info.password = rtw_zmalloc(password_len + 1); + if(!join_result->network_info.password) { + return RTW_NOMEM; + } + if (0 == wep_hex) + rtw_memcpy(join_result->network_info.password, password, password_len); + else + rtw_memcpy(join_result->network_info.password, wep_pwd, password_len); + + } + + join_result->network_info.security_type = security_type; + join_result->network_info.key_id = key_id; + + if(semaphore == NULL) { + rtw_init_sema( &join_result->join_sema, 0 ); + if(!join_result->join_sema) return RTW_NORESOURCE; + join_semaphore = join_result->join_sema; + } else { + join_result->join_sema = semaphore; + } + wifi_reg_event_handler(WIFI_EVENT_NO_NETWORK,wifi_no_network_hdl,NULL); + wifi_reg_event_handler(WIFI_EVENT_CONNECT, wifi_connected_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_DISCONNECT, wifi_disconn_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_FOURWAY_HANDSHAKE_DONE, wifi_handshake_done_hdl, NULL); + + wifi_connect_local(&join_result->network_info); + + join_user_data = join_result; + + if(semaphore == NULL) { + if(rtw_down_timeout_sema( &join_result->join_sema, RTW_JOIN_TIMEOUT ) == RTW_FALSE) { + printf("RTW API: Join bss timeout\r\n"); + if(password_len) { + rtw_free(join_result->network_info.password); + } + rtw_free((u8*)join_result); + rtw_free_sema( &join_semaphore); + result = RTW_TIMEOUT; + goto error; + } else { + rtw_free_sema( &join_semaphore ); + if(join_result->network_info.password_len) { + rtw_free(join_result->network_info.password); + } + rtw_free((u8*)join_result); + if(wifi_is_ready_to_transceive(RTW_STA_INTERFACE) != RTW_SUCCESS) { + result = RTW_ERROR; + goto error; + } + } + } + + result = RTW_SUCCESS; + +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT + restore_wifi_info_to_flash(); +#endif + +error: + join_user_data = NULL; + wifi_unreg_event_handler(WIFI_EVENT_CONNECT, wifi_connected_hdl); + wifi_unreg_event_handler(WIFI_EVENT_NO_NETWORK,wifi_no_network_hdl); + wifi_unreg_event_handler(WIFI_EVENT_FOURWAY_HANDSHAKE_DONE, wifi_handshake_done_hdl); + return result; +} + +int wifi_connect_bssid( + unsigned char bssid[ETH_ALEN], + char *ssid, + rtw_security_t security_type, + char *password, + int bssid_len, + int ssid_len, + int password_len, + int key_id, + void *semaphore) +{ + xSemaphoreHandle join_semaphore; + rtw_result_t result = RTW_SUCCESS; + + rtw_join_status = 0;//clear for last connect status + error_flag = 0 ;//clear for last connect status + internal_join_result_t *join_result = (internal_join_result_t *)rtw_zmalloc(sizeof(internal_join_result_t)); + if(!join_result) { + return RTW_NOMEM; + } + if(ssid_len && ssid){ + join_result->network_info.ssid.len = ssid_len > 32 ? 32 : ssid_len; + rtw_memcpy(join_result->network_info.ssid.val, ssid, ssid_len); + } + rtw_memcpy(join_result->network_info.bssid.octet, bssid, bssid_len); + + if ( ( ( ( password_len > RTW_MAX_PSK_LEN ) || + ( password_len < RTW_MIN_PSK_LEN ) ) && + ( ( security_type == RTW_SECURITY_WPA_TKIP_PSK ) || + ( security_type == RTW_SECURITY_WPA_AES_PSK ) || + ( security_type == RTW_SECURITY_WPA2_AES_PSK ) || + ( security_type == RTW_SECURITY_WPA2_TKIP_PSK ) || + ( security_type == RTW_SECURITY_WPA2_MIXED_PSK ) ) )|| + (((password_len != 5)&& (password_len != 13))&& + ((security_type == RTW_SECURITY_WEP_PSK)|| + (security_type ==RTW_SECURITY_WEP_SHARED ) ))) { + return RTW_INVALID_KEY; + } + join_result->network_info.password_len = password_len; + if(password_len) { + /* add \0 to the end */ + join_result->network_info.password = rtw_zmalloc(password_len + 1); + if(!join_result->network_info.password) { + return RTW_NOMEM; + } + rtw_memcpy(join_result->network_info.password, password, password_len); + } + + join_result->network_info.security_type = security_type; + join_result->network_info.key_id = key_id; + + if(semaphore == NULL) { + rtw_init_sema( &join_result->join_sema, 0 ); + if(!join_result->join_sema) return RTW_NORESOURCE; + join_semaphore = join_result->join_sema; + } else { + join_result->join_sema = semaphore; + } + wifi_reg_event_handler(WIFI_EVENT_NO_NETWORK,wifi_no_network_hdl,NULL); + wifi_reg_event_handler(WIFI_EVENT_CONNECT, wifi_connected_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_DISCONNECT, wifi_disconn_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_FOURWAY_HANDSHAKE_DONE, wifi_handshake_done_hdl, NULL); + + wifi_connect_bssid_local(&join_result->network_info); + + join_user_data = join_result; + + if(semaphore == NULL) { + if(rtw_down_timeout_sema( &join_result->join_sema, RTW_JOIN_TIMEOUT ) == RTW_FALSE) { + printf("RTW API: Join bss timeout\r\n"); + if(password_len) { + rtw_free(join_result->network_info.password); + } + rtw_free((u8*)join_result); + rtw_free_sema( &join_semaphore); + result = RTW_TIMEOUT; + goto error; + } else { + rtw_free_sema( &join_semaphore ); + if(join_result->network_info.password_len) { + rtw_free(join_result->network_info.password); + } + rtw_free((u8*)join_result); + if(wifi_is_ready_to_transceive(RTW_STA_INTERFACE) != RTW_SUCCESS) { + result = RTW_ERROR; + goto error; + } + } + } + + result = RTW_SUCCESS; + +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT + restore_wifi_info_to_flash(); +#endif + +error: + join_user_data = NULL; + wifi_unreg_event_handler(WIFI_EVENT_CONNECT, wifi_connected_hdl); + wifi_unreg_event_handler(WIFI_EVENT_NO_NETWORK,wifi_no_network_hdl); + wifi_unreg_event_handler(WIFI_EVENT_FOURWAY_HANDSHAKE_DONE, wifi_handshake_done_hdl); + return result; +} + +int wifi_disconnect(void) +{ + int ret = 0; + const __u8 null_bssid[ETH_ALEN] = {0, 0, 0, 0, 0, 1}; //set the last to 1 since driver will filter the mac with all 0x00 or 0xff + + if (wext_set_bssid(WLAN0_NAME, null_bssid) < 0){ + printf("\n\rWEXT: Failed to set bogus BSSID to disconnect"); + ret = -1; + } + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_is_up(rtw_interface_t interface) +{ + if(interface == RTW_AP_INTERFACE) { + if(wifi_mode == RTW_MODE_STA_AP) { + return rltk_wlan_running(WLAN1_IDX); + } + } + + return rltk_wlan_running(WLAN0_IDX); +} + +int wifi_is_ready_to_transceive(rtw_interface_t interface) +{ + switch ( interface ) + { + case RTW_AP_INTERFACE: + return ( wifi_is_up(interface) == RTW_TRUE ) ? RTW_SUCCESS : RTW_ERROR; + + case RTW_STA_INTERFACE: + switch ( rtw_join_status ) + { + case JOIN_NO_NETWORKS: + return RTW_NOTFOUND; + + case JOIN_AUTHENTICATED | JOIN_ASSOCIATED | JOIN_LINK_READY | JOIN_SECURITY_COMPLETE | JOIN_COMPLETE: + if(join_user_data->network_info.security_type == RTW_SECURITY_WPA2_AES_PSK) + return RTW_ERROR; + else + return RTW_SUCCESS; + case JOIN_AUTHENTICATED | JOIN_ASSOCIATED | JOIN_LINK_READY | JOIN_SECURITY_COMPLETE | JOIN_COMPLETE|JOIN_HANDSHAKE_DONE: + return RTW_SUCCESS; + case 0: + case JOIN_SECURITY_COMPLETE: /* For open/WEP */ + return RTW_NOT_AUTHENTICATED; + + case JOIN_AUTHENTICATED | JOIN_LINK_READY | JOIN_SECURITY_COMPLETE: + return RTW_UNFINISHED; + + case JOIN_AUTHENTICATED | JOIN_LINK_READY: + case JOIN_AUTHENTICATED | JOIN_LINK_READY | JOIN_COMPLETE: + return RTW_NOT_KEYED; + + default: + return RTW_ERROR; + } + default: + return RTW_ERROR; + } +} + +//----------------------------------------------------------------------------// +int wifi_set_mac_address(char * mac) +{ + char buf[13+17+1]; + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 13+17, "write_mac %s", mac); + return wext_private_command(WLAN0_NAME, buf, 0); +} + +int wifi_get_mac_address(char * mac) +{ + int ret = 0; + char buf[32]; + rtw_memset(buf, 0, sizeof(buf)); + rtw_memcpy(buf, "read_mac", 8); + ret = wext_private_command_with_retval(WLAN0_NAME, buf, buf, 32); + strcpy(mac, buf); + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_enable_powersave(void) +{ + return wext_enable_powersave(WLAN0_NAME); +} + +int wifi_disable_powersave(void) +{ + return wext_disable_powersave(WLAN0_NAME); +} + +#if 0 //Not ready +//----------------------------------------------------------------------------// +int wifi_get_txpower(int *poweridx) +{ + int ret = 0; + char buf[11]; + + rtw_memset(buf, 0, sizeof(buf)); + rtw_memcpy(buf, "txpower", 11); + ret = wext_private_command_with_retval(WLAN0_NAME, buf, buf, 11); + sscanf(buf, "%d", poweridx); + + return ret; +} + +int wifi_set_txpower(int poweridx) +{ + int ret = 0; + char buf[24]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 24, "txpower patha=%d", poweridx); + ret = wext_private_command(WLAN0_NAME, buf, 0); + + return ret; +} +#endif + +//----------------------------------------------------------------------------// +int wifi_get_associated_client_list(void * client_list_buffer, uint16_t buffer_length) +{ + const char * ifname = WLAN0_NAME; + int ret = 0; + char buf[25]; + + if(wifi_mode == RTW_MODE_STA_AP) { + ifname = WLAN1_NAME; + } + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 25, "get_client_list %x", client_list_buffer); + ret = wext_private_command(ifname, buf, 0); + + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_get_ap_info(rtw_bss_info_t * ap_info, rtw_security_t* security) +{ + const char * ifname = WLAN0_NAME; + int ret = 0; + char buf[24]; + + if(wifi_mode == RTW_MODE_STA_AP) { + ifname = WLAN1_NAME; + } + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 24, "get_ap_info %x", ap_info); + ret = wext_private_command(ifname, buf, 0); + + snprintf(buf, 24, "get_security"); + ret = wext_private_command_with_retval(ifname, buf, buf, 24); + sscanf(buf, "%d", security); + + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_set_country(rtw_country_code_t country_code) +{ + int ret; + switch(country_code){ + case RTW_COUNTRY_US: ret = wext_set_country(WLAN0_NAME, "US"); break; + case RTW_COUNTRY_EU: ret = wext_set_country(WLAN0_NAME, "EU"); break; + case RTW_COUNTRY_JP: ret = wext_set_country(WLAN0_NAME, "JP"); break; + case RTW_COUNTRY_CN: ret = wext_set_country(WLAN0_NAME, "CN"); break; + default: printf("\n\rUnknown country_code\n"); ret = -1; break; + } + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_get_rssi(int *pRSSI) +{ + return wext_get_rssi(WLAN0_NAME, pRSSI); +} + +//----------------------------------------------------------------------------// +int wifi_set_channel(int channel) +{ + return wext_set_channel(WLAN0_NAME, channel); +} + +int wifi_get_channel(int *channel) +{ + return wext_get_channel(WLAN0_NAME, (u8*)channel); +} + +//----------------------------------------------------------------------------// +int wifi_register_multicast_address(rtw_mac_t *mac) +{ + return wext_register_multicast_address(WLAN0_NAME, mac); +} + +int wifi_unregister_multicast_address(rtw_mac_t *mac) +{ + return wext_unregister_multicast_address(WLAN0_NAME, mac); +} + +//----------------------------------------------------------------------------// +int wifi_rf_on(void) +{ + int ret; + ret = rltk_wlan_rf_on(); + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_rf_off(void) +{ + int ret; + ret = rltk_wlan_rf_off(); + return ret; +} +int wifi_on(rtw_mode_t mode) +{ + int ret = 1; + int timeout = 20; + int idx; + int devnum = 1; + + wifi_mode = mode; + + init_event_callback_list(); + if(mode == RTW_MODE_STA_AP) + devnum = 2; + + if(rltk_wlan_running(WLAN0_IDX)) { + printf("\n\rWIFI is already running"); + return 1; + } + + printf("\n\rInitializing WIFI ..."); + for(idx=0;idx RTW_MAX_PSK_LEN) || + (password_len < RTW_MIN_PSK_LEN)) { + ret = -1; + printf("\nstart ap fail, please check password length\n"); + break; + } + + ret = wext_set_auth_param(ifname, IW_AUTH_80211_AUTH_ALG, IW_AUTH_ALG_OPEN_SYSTEM); + if(ret == 0) + ret = wext_set_key_ext(ifname, IW_ENCODE_ALG_CCMP, NULL, 0, 0, 0, 0, NULL, 0); + if(ret == 0) + ret = wext_set_passphrase(ifname, (u8*)password, password_len); + break; + default: + ret = -1; + printf("\n\rWIFICONF: security type is not supported"); + break; + } + if(ret < 0) goto exit; +#if 0 +//hide beacon SSID, add by serena_li + u8 value = 1; // 1: ssid = NUll, 2: ssid = ......, other: ssid = ssid + ret = set_hidden_ssid(ifname, value); + if(ret < 0) goto exit; +#endif + ret = wext_set_ap_ssid(ifname, (u8*)ssid, ssid_len); +#ifdef CONFIG_WPS //construct WPS IE +#if CONFIG_WPS_AP + wpas_wps_init(ifname); +#endif +#endif +exit: + return ret; +} + +static void wifi_scan_each_report_hdl( char* buf, int buf_len, int flags, void* userdata) +{ + int i =0; + int insert_pos = 0; + rtw_scan_result_t** result_ptr = (rtw_scan_result_t**)buf; + rtw_scan_result_t* temp = NULL; + + for(i=0; iBSSID.octet)){ + memset(*result_ptr, 0, sizeof(rtw_scan_result_t)); + return; + } + } + + scan_result_handler_ptr.scan_cnt++; + + if(scan_result_handler_ptr.scan_cnt > scan_result_handler_ptr.max_ap_size){ + scan_result_handler_ptr.scan_cnt = scan_result_handler_ptr.max_ap_size; + if((*result_ptr)->signal_strength > scan_result_handler_ptr.pap_details[scan_result_handler_ptr.max_ap_size-1]->signal_strength){ + rtw_memcpy(scan_result_handler_ptr.pap_details[scan_result_handler_ptr.max_ap_size-1], *result_ptr, sizeof(rtw_scan_result_t)); + temp = scan_result_handler_ptr.pap_details[scan_result_handler_ptr.max_ap_size -1]; + }else + return; + }else{ + rtw_memcpy(&scan_result_handler_ptr.ap_details[scan_result_handler_ptr.scan_cnt-1], *result_ptr, sizeof(rtw_scan_result_t)); + } + + for(i=0; i< scan_result_handler_ptr.scan_cnt-1; i++){ + if((*result_ptr)->signal_strength > scan_result_handler_ptr.pap_details[i]->signal_strength) + break; + } + insert_pos = i; + + for(i = scan_result_handler_ptr.scan_cnt-1; i>insert_pos; i--) + scan_result_handler_ptr.pap_details[i] = scan_result_handler_ptr.pap_details[i-1]; + + if(temp != NULL) + scan_result_handler_ptr.pap_details[insert_pos] = temp; + else + scan_result_handler_ptr.pap_details[insert_pos] = &scan_result_handler_ptr.ap_details[scan_result_handler_ptr.scan_cnt-1]; + rtw_memset(*result_ptr, 0, sizeof(rtw_scan_result_t)); +} + +static void wifi_scan_done_hdl( char* buf, int buf_len, int flags, void* userdata) +{ + int i = 0; + rtw_scan_handler_result_t scan_result_report; + + for(i=0; ibuf, pscan_buf->buf_len, flags); + }else{ + wifi_reg_event_handler(WIFI_EVENT_SCAN_RESULT_REPORT, wifi_scan_each_report_hdl, NULL); + wifi_reg_event_handler(WIFI_EVENT_SCAN_DONE, wifi_scan_done_hdl, NULL); + ret = wext_set_scan(WLAN0_NAME, NULL, 0, flags); + } + + if(ret == 0) { + if(result_ptr != NULL){ + ret = wext_get_scan(WLAN0_NAME, pscan_buf->buf, pscan_buf->buf_len); + } + } + return ret; +} +int wifi_scan_networks_with_ssid(rtw_scan_result_handler_t results_handler, void* user_data, char* ssid, int ssid_len){ + int scan_cnt = 0, add_cnt = 0; + scan_buf_arg scan_buf; + int ret; + + scan_buf.buf_len = *((int*)user_data); + scan_buf.buf = (char*)pvPortMalloc(scan_buf.buf_len); + if(!scan_buf.buf){ + printf("\n\rERROR: Can't malloc memory"); + return RTW_NOMEM; + } + //set ssid + memset(scan_buf.buf, 0, scan_buf.buf_len); + memcpy(scan_buf.buf, &ssid_len, sizeof(int)); + memcpy(scan_buf.buf+sizeof(int), ssid, ssid_len); + + //Scan channel + if(scan_cnt = (wifi_scan(RTW_SCAN_TYPE_ACTIVE, RTW_BSS_TYPE_ANY, &scan_buf)) < 0){ + printf("\n\rERROR: wifi scan failed"); + ret = RTW_ERROR; + }else{ + int plen = 0; + while(plen < scan_buf.buf_len){ + int len, rssi, ssid_len, i, security_mode; + int wps_password_id; + char *mac, *ssid; + //u8 *security_mode; + printf("\n\r"); + // len + len = (int)*(scan_buf.buf + plen); + printf("len = %d, ", len); + // check end + if(len == 0) break; + // mac + mac = scan_buf.buf + plen + 1; + printf("mac = "); + for(i=0; i<6; i++) + printf("%02x,", *(mac+i)); + // rssi + rssi = *(int*)(scan_buf.buf + plen + 1 + 6); + printf(" rssi = %d, ", rssi); + // security_mode + security_mode = (int)*(scan_buf.buf + plen + 1 + 6 + 4); + switch (security_mode) { + case RTW_SECURITY_OPEN: + printf("sec = open , "); + break; + case RTW_SECURITY_WEP_PSK: + printf("sec = wep , "); + break; + case RTW_SECURITY_WPA2_AES_PSK: + printf("sec = wpa/wpa2, "); + break; + } + // password id + wps_password_id = (int)*(scan_buf.buf + plen + 1 + 6 + 4 + 1); + printf("wps password id = %d, ", wps_password_id); + + printf("channel = %d ", *(scan_buf.buf + plen + 1 + 6 + 4 + 1 + 1)); + // ssid + ssid_len = len - 1 - 6 - 4 - 1 - 1 - 1; + ssid = scan_buf.buf + plen + 1 + 6 + 4 + 1 + 1 + 1; + printf("ssid = "); + for(i=0; i 0) + { + rtw_msleep_os(20); + count --; + } + if(count == 0){ + printf("\n\r[%d]WiFi: Scan is running. Wait 2s timeout.", rtw_get_current_time()); + return RTW_TIMEOUT; + } + } + scan_result_handler_ptr.scan_start_time = rtw_get_current_time(); + scan_result_handler_ptr.scan_running = 1; +#endif + + scan_result_handler_ptr.gscan_result_handler = results_handler; + + scan_result_handler_ptr.max_ap_size = max_ap_size; + scan_result_handler_ptr.ap_details = (rtw_scan_result_t*)rtw_zmalloc(max_ap_size*sizeof(rtw_scan_result_t)); + if(scan_result_handler_ptr.ap_details == NULL){ + goto error_with_result_ptr; + } + rtw_memset(scan_result_handler_ptr.ap_details, 0, max_ap_size*sizeof(rtw_scan_result_t)); + + scan_result_handler_ptr.pap_details = (rtw_scan_result_t**)rtw_zmalloc(max_ap_size*sizeof(rtw_scan_result_t*)); + if(scan_result_handler_ptr.pap_details == NULL) + return RTW_ERROR; + rtw_memset(scan_result_handler_ptr.pap_details, 0, max_ap_size); + + scan_result_handler_ptr.scan_cnt = 0; + + scan_result_handler_ptr.scan_complete = RTW_FALSE; + scan_result_handler_ptr.user_data = user_data; + + if (wifi_scan( RTW_SCAN_COMMAMD<<4 | RTW_SCAN_TYPE_ACTIVE, RTW_BSS_TYPE_ANY, NULL) != RTW_SUCCESS) + { + goto error_with_result_ptr; + } + + return RTW_SUCCESS; + +error_with_result_ptr: + rtw_free((u8*)scan_result_handler_ptr.pap_details); + scan_result_handler_ptr.pap_details = NULL; + return RTW_ERROR; +} +//----------------------------------------------------------------------------// +int wifi_set_pscan_chan(__u8 * channel_list,__u8 * pscan_config, __u8 length) +{ + if(channel_list) + return wext_set_pscan_channel(WLAN0_NAME, channel_list, pscan_config, length); + else + return -1; +} + +//----------------------------------------------------------------------------// +int wifi_get_setting(const char *ifname, rtw_wifi_setting_t *pSetting) +{ + int ret = 0; + int mode = 0; + unsigned short security = 0; + + memset(pSetting, 0, sizeof(rtw_wifi_setting_t)); + if(wext_get_mode(ifname, &mode) < 0) + ret = -1; + + switch(mode) { + case IW_MODE_MASTER: + pSetting->mode = RTW_MODE_AP; + break; + case IW_MODE_INFRA: + default: + pSetting->mode = RTW_MODE_STA; + break; + //default: + //printf("\r\n%s(): Unknown mode %d\n", __func__, mode); + //break; + } + + if(wext_get_ssid(ifname, pSetting->ssid) < 0) + ret = -1; + if(wext_get_channel(ifname, &pSetting->channel) < 0) + ret = -1; + if(wext_get_enc_ext(ifname, &security, &pSetting->key_idx, pSetting->password) < 0) + ret = -1; + + switch(security){ + case IW_ENCODE_ALG_NONE: + pSetting->security_type = RTW_SECURITY_OPEN; + break; + case IW_ENCODE_ALG_WEP: + pSetting->security_type = RTW_SECURITY_WEP_PSK; + break; + case IW_ENCODE_ALG_TKIP: + pSetting->security_type = RTW_SECURITY_WPA_TKIP_PSK; + break; + case IW_ENCODE_ALG_CCMP: + pSetting->security_type = RTW_SECURITY_WPA2_AES_PSK; + break; + default: + break; + } + + if(security == IW_ENCODE_ALG_TKIP || security == IW_ENCODE_ALG_CCMP) + if(wext_get_passphrase(ifname, pSetting->password) < 0) + ret = -1; + + return ret; +} +//----------------------------------------------------------------------------// +int wifi_show_setting(const char *ifname, rtw_wifi_setting_t *pSetting) +{ + int ret = 0; + + printf("\n\r\nWIFI %s Setting:",ifname); + printf("\n\r=============================="); + + switch(pSetting->mode) { + case RTW_MODE_AP: + printf("\n\r MODE => AP"); + break; + case RTW_MODE_STA: + printf("\n\r MODE => STATION"); + break; + default: + printf("\n\r MODE => UNKNOWN"); + } + + printf("\n\r SSID => %s", pSetting->ssid); + printf("\n\r CHANNEL => %d", pSetting->channel); + + switch(pSetting->security_type) { + case RTW_SECURITY_OPEN: + printf("\n\r SECURITY => OPEN"); + break; + case RTW_SECURITY_WEP_PSK: + printf("\n\r SECURITY => WEP"); + printf("\n\r KEY INDEX => %d", pSetting->key_idx); + break; + case RTW_SECURITY_WPA_TKIP_PSK: + printf("\n\r SECURITY => TKIP"); + break; + case RTW_SECURITY_WPA2_AES_PSK: + printf("\n\r SECURITY => AES"); + break; + default: + printf("\n\r SECURITY => UNKNOWN"); + } + + printf("\n\r PASSWORD => %s", pSetting->password); + printf("\n\r"); + + return ret; +} + +//----------------------------------------------------------------------------// +int wifi_set_network_mode(rtw_network_mode_t mode) +{ + if((mode == RTW_NETWORK_B) || (mode == RTW_NETWORK_BG) || (mode == RTW_NETWORK_BGN)) + return rltk_wlan_wireless_mode((unsigned char) mode); + + return -1; +} + +int wifi_set_wps_phase(unsigned char is_trigger_wps) +{ + return rltk_wlan_set_wps_phase(is_trigger_wps); +} + +//----------------------------------------------------------------------------// +int wifi_set_promisc(rtw_rcr_level_t enabled, void (*callback)(unsigned char*, unsigned int, void*), unsigned char len_used) +{ + return promisc_set(enabled, callback, len_used); +} + +void wifi_enter_promisc_mode(){ + int mode = 0; + unsigned char ssid[33]; + + if(wifi_mode == RTW_MODE_STA_AP){ + wifi_off(); + vTaskDelay(20); + wifi_on(RTW_MODE_PROMISC); + }else{ + wext_get_mode(WLAN0_NAME, &mode); + + switch(mode) { + case IW_MODE_MASTER: //In AP mode + rltk_wlan_deinit(); + vTaskDelay(20); + rltk_wlan_init(0, RTW_MODE_PROMISC); + rltk_wlan_start(0); + break; + case IW_MODE_INFRA: //In STA mode + if(wext_get_ssid(WLAN0_NAME, ssid) > 0) + wifi_disconnect(); + } + } +} + +int wifi_restart_ap( + unsigned char *ssid, + rtw_security_t security_type, + unsigned char *password, + int ssid_len, + int password_len, + int channel) +{ + unsigned char idx = 0; + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + struct netif * pnetif = &xnetif[0]; +#ifdef CONFIG_CONCURRENT_MODE + rtw_wifi_setting_t setting; + int sta_linked = 0; +#endif + + if(rltk_wlan_running(WLAN1_IDX)){ + idx = 1; + } + + // stop dhcp server + dhcps_deinit(); + +#ifdef CONFIG_CONCURRENT_MODE + if(idx > 0){ + sta_linked = wifi_get_setting(WLAN0_NAME, &setting); + wifi_off(); + vTaskDelay(20); + wifi_on(RTW_MODE_STA_AP); + } + else +#endif + { + IP4_ADDR(&ipaddr, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + netif_set_addr(pnetif, &ipaddr, &netmask,&gw); + wifi_off(); + vTaskDelay(20); + wifi_on(RTW_MODE_AP); + } + // start ap + if(wifi_start_ap((char*)ssid, security_type, (char*)password, ssid_len, password_len, channel) < 0) { + printf("\n\rERROR: Operation failed!"); + return -1; + } + +#if (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\r\nWebServer Thread: High Water Mark is %ld\n", uxTaskGetStackHighWaterMark(NULL)); +#endif +#ifdef CONFIG_CONCURRENT_MODE + // connect to ap if wlan0 was linked with ap + if(idx > 0 && sta_linked == 0){ + int ret; + printf("\r\nAP: ssid=%s", (char*)setting.ssid); + printf("\r\nAP: security_type=%d", setting.security_type); + printf("\r\nAP: password=%s", (char*)setting.password); + printf("\r\nAP: key_idx =%d\n", setting.key_idx); + ret = wifi_connect((char*)setting.ssid, + setting.security_type, + (char*)setting.password, + strlen((char*)setting.ssid), + strlen((char*)setting.password), + setting.key_idx, + NULL); +#if CONFIG_DHCP_CLIENT + if(ret == RTW_SUCCESS) { + /* Start DHCPClient */ + LwIP_DHCP(0, DHCP_START); + } +#endif + } +#endif +#if (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\r\nWebServer Thread: High Water Mark is %ld\n", uxTaskGetStackHighWaterMark(NULL)); +#endif + // start dhcp server + dhcps_init(&xnetif[idx]); + + return 0; +} + +#ifdef CONFIG_AUTO_RECONNECT +extern void (*p_wlan_autoreconnect_hdl)(rtw_security_t, char*, int, char*, int, int); + +struct wifi_autoreconnect_param { + rtw_security_t security_type; + char *ssid; + int ssid_len; + char *password; + int password_len; + int key_id; +}; + +static void wifi_autoreconnect_thread(void *param) +{ + int ret = RTW_ERROR; + struct wifi_autoreconnect_param *reconnect_param = (struct wifi_autoreconnect_param *) param; + ret = wifi_connect(reconnect_param->ssid, reconnect_param->security_type, reconnect_param->password, + reconnect_param->ssid_len, reconnect_param->password_len, reconnect_param->key_id, NULL); +#if CONFIG_LWIP_LAYER + if(ret == RTW_SUCCESS) + LwIP_DHCP(0, DHCP_START); +#endif + vTaskDelete(NULL); +} + +void wifi_autoreconnect_hdl(rtw_security_t security_type, + char *ssid, int ssid_len, + char *password, int password_len, + int key_id) +{ + static struct wifi_autoreconnect_param param; + param.security_type = security_type; + param.ssid = ssid; + param.ssid_len = ssid_len; + param.password = password; + param.password_len = password_len; + param.key_id = key_id; + xTaskCreate(wifi_autoreconnect_thread, (const char *)"wifi_autoreconnect", 512, ¶m, tskIDLE_PRIORITY + 1, NULL); +} + +int wifi_set_autoreconnect(__u8 mode) +{ + p_wlan_autoreconnect_hdl = wifi_autoreconnect_hdl; + return wext_set_autoreconnect(WLAN0_NAME, mode); +} + +int wifi_get_autoreconnect(__u8 *mode) +{ + return wext_get_autoreconnect(WLAN0_NAME, mode); +} +#endif + +#ifdef CONFIG_CUSTOM_IE +/* + * Example for custom ie + * + * u8 test_1[] = {221, 2, 2, 2}; + * u8 test_2[] = {221, 2, 1, 1}; + * cus_ie buf[2] = {{test_1, PROBE_REQ}, + * {test_2, PROBE_RSP | BEACON}}; + * u8 buf_test2[] = {221, 2, 1, 3} ; + * cus_ie buf_update = {buf_test2, PROBE_REQ}; + * + * add ie list + * static void cmd_add_ie(int argc, char **argv) + * { + * wifi_add_custom_ie((void *)buf, 2); + * } + * + * update current ie + * static void cmd_update_ie(int argc, char **argv) + * { + * wifi_update_custom_ie(&buf_update, 2); + * } + * + * delete all ie + * static void cmd_del_ie(int argc, char **argv) + * { + * wifi_del_custom_ie(); + * } + */ + +int wifi_add_custom_ie(void *cus_ie, int ie_num) +{ + return wext_add_custom_ie(WLAN0_NAME, cus_ie, ie_num); +} + + +int wifi_update_custom_ie(void *cus_ie, int ie_index) +{ + return wext_update_custom_ie(WLAN0_NAME, cus_ie, ie_index); +} + +int wifi_del_custom_ie() +{ + return wext_del_custom_ie(WLAN0_NAME); +} + +#endif + +#ifdef CONFIG_PROMISC +extern void promisc_init_packet_filter(void); +extern int promisc_add_packet_filter(u8 filter_id, rtw_packet_filter_pattern_t *patt, rtw_packet_filter_rule_e rule); +extern int promisc_enable_packet_filter(u8 filter_id); +extern int promisc_disable_packet_filter(u8 filter_id); +extern int promisc_remove_packet_filter(u8 filter_id); +void wifi_init_packet_filter() +{ + promisc_init_packet_filter(); +} + +int wifi_add_packet_filter(unsigned char filter_id, rtw_packet_filter_pattern_t *patt, rtw_packet_filter_rule_e rule) +{ + return promisc_add_packet_filter(filter_id, patt, rule); +} + +int wifi_enable_packet_filter(unsigned char filter_id) +{ + return promisc_enable_packet_filter(filter_id); +} + +int wifi_disable_packet_filter(unsigned char filter_id) +{ + return promisc_disable_packet_filter(filter_id); +} + +int wifi_remove_packet_filter(unsigned char filter_id) +{ + return promisc_remove_packet_filter(filter_id); +} +#endif + +#ifdef CONFIG_AP_MODE +int wifi_enable_forwarding(void) +{ + return wext_enable_forwarding(WLAN0_NAME); +} + +int wifi_disable_forwarding(void) +{ + return wext_disable_forwarding(WLAN0_NAME); +} +#endif + +//----------------------------------------------------------------------------// +#endif //#if CONFIG_WLAN diff --git a/component/common/api/wifi/wifi_conf.h b/component/common/api/wifi/wifi_conf.h new file mode 100644 index 0000000..7cd7abf --- /dev/null +++ b/component/common/api/wifi/wifi_conf.h @@ -0,0 +1,612 @@ +//----------------------------------------------------------------------------// +#ifndef __WIFI_API_H +#define __WIFI_API_H + +#include "FreeRTOS.h" +#include "wifi_constants.h" +#include "wifi_structures.h" +#include "wifi_util.h" +#include "wifi_ind.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/****************************************************** + * Macros + ******************************************************/ + +#define RTW_ENABLE_API_INFO + +#ifdef RTW_ENABLE_API_INFO + #define RTW_API_INFO(args) do {printf args;} while(0) +#else + #define RTW_API_INFO(args) +#endif + +#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5] +#define CMP_MAC( a, b ) (((a[0])==(b[0]))&& \ + ((a[1])==(b[1]))&& \ + ((a[2])==(b[2]))&& \ + ((a[3])==(b[3]))&& \ + ((a[4])==(b[4]))&& \ + ((a[5])==(b[5]))) + +/****************************************************** + * Constants + ******************************************************/ +#define SCAN_LONGEST_WAIT_TIME (4500) + + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" + +#define PSCAN_ENABLE 0x01 //enable for partial channel scan +#define PSCAN_FAST_SURVEY 0x02 //set to select scan time to FAST_SURVEY_TO, otherwise SURVEY_TO + + +/****************************************************** + * Type Definitions + ******************************************************/ + +/** Scan result callback function pointer type + * + * @param result_ptr : A pointer to the pointer that indicates where to put the next scan result + * @param user_data : User provided data + */ +typedef void (*rtw_scan_result_callback_t)( rtw_scan_result_t** result_ptr, void* user_data ); +typedef rtw_result_t (*rtw_scan_result_handler_t)( rtw_scan_handler_result_t* malloced_scan_result ); + +/****************************************************** + * Structures + ******************************************************/ +typedef enum _WIFI_LINK_STATUS{ + WIFI_LINK_DISCONNECTED = 0, + WIFI_LINK_CONNECTED +}WIFI_LINK_STATUS; + +typedef struct { + char *buf; + int buf_len; +} scan_buf_arg; + +/****************************************************** + * Structures + ******************************************************/ +typedef struct internal_scan_handler{ + rtw_scan_result_t** pap_details; + rtw_scan_result_t * ap_details; + int scan_cnt; + rtw_bool_t scan_complete; + unsigned char max_ap_size; + rtw_scan_result_handler_t gscan_result_handler; +#if SCAN_USE_SEMAPHORE + void *scan_semaphore; +#else + int scan_running; +#endif + void* user_data; + unsigned int scan_start_time; +} internal_scan_handler_t; + +typedef struct { + rtw_network_info_t network_info; + void *join_sema; +} internal_join_result_t; + +/****************************************************** + * Function Declarations + ******************************************************/ +/** + * Initialises Realtek WiFi API System + * + * - Initialises the required parts of the software platform + * i.e. worker, event registering, semaphore, etc. + * + * - Initialises the RTW API thread which handles the asynchronous event + * + * @return RTW_SUCCESS if initialization is successful, RTW_ERROR otherwise + */ +int wifi_manager_init(void); + +/** Joins a Wi-Fi network + * + * Scans for, associates and authenticates with a Wi-Fi network. + * On successful return, the system is ready to send data packets. + * + * @param[in] ssid : A null terminated string containing the SSID name of the network to join + * @param[in] security_type : Authentication type: + * - RTW_SECURITY_OPEN - Open Security + * - RTW_SECURITY_WEP_PSK - WEP Security with open authentication + * - RTW_SECURITY_WEP_SHARED - WEP Security with shared authentication + * - RTW_SECURITY_WPA_TKIP_PSK - WPA Security + * - RTW_SECURITY_WPA2_AES_PSK - WPA2 Security using AES cipher + * - RTW_SECURITY_WPA2_TKIP_PSK - WPA2 Security using TKIP cipher + * - RTW_SECURITY_WPA2_MIXED_PSK - WPA2 Security using AES and/or TKIP ciphers + * @param[in] password : A byte array containing either the + * cleartext security key for WPA/WPA2 + * secured networks, or a pointer to + * an array of rtw_wep_key_t + * structures for WEP secured networks + * @param[in] ssid_len : The length of the SSID in + * bytes. + * @param[in] password_len : The length of the security_key in + * bytes. + * @param[in] key_id : The index of the wep key. + * @param[in] semaphore : A user provided semaphore that is flagged when the join is complete + * + * @return RTW_SUCCESS : when the system is joined and ready + * to send data packets + * RTW_ERROR : if an error occurred + */ +int wifi_connect( + char *ssid, + rtw_security_t security_type, + char *password, + int ssid_len, + int password_len, + int key_id, + void *semaphore); + +int wifi_connect_bssid( + unsigned char bssid[ETH_ALEN], + char *ssid, + rtw_security_t security_type, + char *password, + int bssid_len, + int ssid_len, + int password_len, + int key_id, + void *semaphore); + +/** Disassociates from a Wi-Fi network. + * + * @return RTW_SUCCESS : On successful disassociation from + * the AP + * RTW_ERROR : If an error occurred +*/ +int wifi_disconnect(void); + +/** Check if the interface specified is up. + * + * @return RTW_TRUE : If it's up + * RTW_FALSE : If it's not +*/ +int wifi_is_up(rtw_interface_t interface); + +/** Determines if a particular interface is ready to transceive ethernet packets + * + * @param Radio interface to check, options are + * RTW_STA_INTERFACE, RTW_AP_INTERFACE + * @return RTW_SUCCESS : if the interface is ready to + * transceive ethernet packets + * @return RTW_NOTFOUND : no AP with a matching SSID was + * found + * @return RTW_NOT_AUTHENTICATED: a matching AP was found but + * it won't let you + * authenticate. This can + * occur if this device is + * in the block list on the + * AP. + * @return RTW_NOT_KEYED: the device has authenticated and + * associated but has not completed + * the key exchange. This can occur + * if the passphrase is incorrect. + * @return RTW_ERROR : if the interface is not ready to + * transceive ethernet packets + */ +int wifi_is_ready_to_transceive(rtw_interface_t interface); + +/** ---------------------------------------------------------------------- + * WARNING : This function is for internal use only! + * ---------------------------------------------------------------------- + * This function sets the current Media Access Control (MAC) address of the + * 802.11 device. + * + * @param[in] mac Wi-Fi MAC address + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_set_mac_address(char * mac); + +/** Retrieves the current Media Access Control (MAC) address + * (or Ethernet hardware address) of the 802.11 device + * + * @param mac Pointer to a variable that the current MAC address will be written to + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_get_mac_address(char * mac); + +/** Enables powersave mode + * + * @return @ref rtw_result_t + */ +int wifi_enable_powersave(void); + +/** Disables 802.11 power save mode + * + * @return RTW_SUCCESS : if power save mode was successfully + * disabled + * RTW_ERROR : if power save mode was not successfully + * disabled + */ +int wifi_disable_powersave(void); + +/** Gets the tx power in index units + * + * @param dbm : The variable to receive the tx power in index. + * + * @return RTW_SUCCESS : if successful + * RTW_ERROR : if not successful + */ +int wifi_get_txpower(int *poweridx); + +/** Sets the tx power in index units + * + * @param dbm : The desired tx power in index. + * + * @return RTW_SUCCESS : if tx power was successfully set + * RTW_ERROR : if tx power was not successfully set + */ +int wifi_set_txpower(int poweridx); + +/** Get the associated clients with SoftAP + * + * @param client_list_buffer : the location where the client + * list will be stored + * @param buffer_length : the buffer length. + * + * @return RTW_SUCCESS : if result was successfully get + * RTW_ERROR : if result was not successfully get + */ +int wifi_get_associated_client_list(void * client_list_buffer, unsigned short buffer_length); + +/** Get the SoftAP information + * + * @param ap_info : the location where the AP info will be + * stored + * @param security : the security type. + * + * @return RTW_SUCCESS : if result was successfully get + * RTW_ERROR : if result was not successfully get + */ +int wifi_get_ap_info(rtw_bss_info_t * ap_info, rtw_security_t* security); + +/** Set the country code to driver to determine the channel set + * + * @param country_code : the country code. + * + * @return RTW_SUCCESS : if result was successfully set + * RTW_ERROR : if result was not successfully set + */ +int wifi_set_country(rtw_country_code_t country_code); + +/** Retrieve the latest RSSI value + * + * @param rssi: The location where the RSSI value will be stored + * + * @return RTW_SUCCESS : if the RSSI was succesfully retrieved + * RTW_ERROR : if the RSSI was not retrieved + */ +int wifi_get_rssi(int *pRSSI); + +/** Set the current channel on STA interface + * + * @param channel : The desired channel + * + * @return RTW_SUCCESS : if the channel was successfully set + * RTW_ERROR : if the channel was not successfully + * set + */ +int wifi_set_channel(int channel); + +/** Get the current channel on STA interface + * + * @param channel : A pointer to the variable where the + * channel value will be written + * + * @return RTW_SUCCESS : if the channel was successfully read + * RTW_ERROR : if the channel was not successfully + * read + */ +int wifi_get_channel(int *channel); + +/** Registers interest in a multicast address + * Once a multicast address has been registered, all packets detected on the + * medium destined for that address are forwarded to the host. + * Otherwise they are ignored. + * + * @param mac: Ethernet MAC address + * + * @return RTW_SUCCESS : if the address was registered + * successfully + * RTW_ERROR : if the address was not registered + */ +int wifi_register_multicast_address(rtw_mac_t *mac); + +/** Unregisters interest in a multicast address + * Once a multicast address has been unregistered, all packets detected on the + * medium destined for that address are ignored. + * + * @param mac: Ethernet MAC address + * + * @return RTW_SUCCESS : if the address was unregistered + * successfully + * RTW_ERROR : if the address was not unregistered + */ +int wifi_unregister_multicast_address(rtw_mac_t *mac); + +int wifi_rf_on(void); +int wifi_rf_off(void); + +/** Turn on the Wi-Fi device + * + * - Bring the Wireless interface "Up" + * - Initialises the driver thread which arbitrates access + * to the SDIO/SPI bus + * + * @param mode: wifi work mode + * + * @return RTW_SUCCESS : if the WiFi chip was initialised + * successfully + * RTW_ERROR : if the WiFi chip was not initialised + * successfully + */ +int wifi_on(rtw_mode_t mode); + +/** + * Turn off the Wi-Fi device + * + * - Bring the Wireless interface "Down" + * - De-Initialises the driver thread which arbitrates access + * to the SDIO/SPI bus + * + * @return RTW_SUCCESS if deinitialization is successful, + * RTW_ERROR otherwise + */ +int wifi_off(void); + +/** Starts an infrastructure WiFi network + * + * @warning If a STA interface is active when this function is called, the softAP will\n + * start on the same channel as the STA. It will NOT use the channel provided! + * + * @param[in] ssid : A null terminated string containing + * the SSID name of the network to join + * @param[in] security_type : Authentication type: \n + * - RTW_SECURITY_OPEN - Open Security \n + * - RTW_SECURITY_WPA_TKIP_PSK - WPA Security \n + * - RTW_SECURITY_WPA2_AES_PSK - WPA2 Security using AES cipher \n + * - RTW_SECURITY_WPA2_MIXED_PSK - WPA2 Security using AES and/or TKIP ciphers \n + * - WEP security is NOT IMPLEMENTED. It is NOT SECURE! \n + * @param[in] password : A byte array containing the cleartext + * security key for the network + * @param[in] ssid_len : The length of the SSID in + * bytes. + * @param[in] password_len : The length of the security_key in + * bytes. + * @param[in] channel : 802.11 channel number + * + * @return RTW_SUCCESS : if successfully creates an AP + * RTW_ERROR : if an error occurred + */ +int wifi_start_ap( + char *ssid, + rtw_security_t security_type, + char *password, + int ssid_len, + int password_len, + int channel); + +/** Initiates a scan to search for 802.11 networks. + * + * The scan progressively accumulates results over time, and + * may take between 1 and 3 seconds to complete. The results of + * the scan will be individually provided to the callback + * function. Note: The callback function will be executed in + * the context of the RTW thread. + * + * @param[in] scan_type : Specifies whether the scan should + * be Active, Passive or scan Prohibited channels + * @param[in] bss_type : Specifies whether the scan should + * search for Infrastructure + * networks (those using an Access + * Point), Ad-hoc networks, or both + * types. + * @param callback[in] : the callback function which will + * receive and process the result data. + * @param result_ptr[in] : a pointer to a pointer to a result + * storage structure. + * @param user_data[in] : user specific data that will be + * passed directly to the callback function + * + * @note : When scanning specific channels, devices with a + * strong signal strength on nearby channels may be + * detected + * @note : Callback must not use blocking functions, since it is + * called from the context of the RTW thread. + * @note : The callback, result_ptr and user_data variables will + * be referenced after the function returns. Those + * variables must remain valid until the scan is + * complete. + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_scan(rtw_scan_type_t scan_type, + rtw_bss_type_t bss_type, + void* result_ptr); + +/** Initiates a scan to search for 802.11 networks, a higher + * level API based on wifi_scan to simplify the scan + * operation. + * + * The scan results will be list by the order of RSSI. + * It may demand hundreds bytes memory during scan + * processing according to the quantity of AP nearby. + * + * @param results_handler[in] : the callback function which + * will receive and process the result data. + * @param user_data[in] : user specific data that will be + * passed directly to the callback function + * + * @note : Callback must not use blocking functions, since it is + * called from the context of the RTW thread. + * @note : The callback, user_data variables will + * be referenced after the function returns. Those + * variables must remain valid until the scan is + * complete. + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_scan_networks(rtw_scan_result_handler_t results_handler, void* user_data); +int wifi_scan_networks_with_ssid(rtw_scan_result_handler_t results_handler, void* user_data, char* ssid, int ssid_len); + +/** Set the partical scan + * + * @param channel_list[in] : the channel set the scan will + * stay on + * @param pscan_config[in] : the pscan_config of the channel set + * + * @param length[in] : the channel list length + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_set_pscan_chan(__u8 * channel_list,__u8 * pscan_config, __u8 length); + +/** Get the network information + * + * @param ifname[in] : the name of the interface we are care + * @param pSetting[in] : the location where the network + * information will be stored + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_get_setting(const char *ifname,rtw_wifi_setting_t *pSetting); + +/** Show the network information + * + * @param ifname[in] : the name of the interface we are care + * @param pSetting[in] : the location where the network + * information was stored + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_show_setting(const char *ifname,rtw_wifi_setting_t *pSetting); + +/** Set the network mode according to the data rate it's + * supported + * + * @param mode[in] : the network mode + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_set_network_mode(rtw_network_mode_t mode); + +/** Set the chip to worke in the promisc mode + * + * @param enabled[in] : enabled can be set 0, 1 and 2. if enabled is zero, disable the promisc, else enable the promisc. + * 0 means disable the promisc + * 1 means enable the promisc + * 2 means enable the promisc special for length is used + * @param callback[in] : the callback function which will + * receive and process the netowork data. + * @param len_used[in] : specify if the the promisc length is + * used. + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_set_promisc(rtw_rcr_level_t enabled, void (*callback)(unsigned char*, unsigned int, void*), unsigned char len_used); + +/** Set the wps phase + * + * @param is_trigger_wps[in] : to trigger wps function or not + * + * @return RTW_SUCCESS or RTW_ERROR + */ +int wifi_set_wps_phase(unsigned char is_trigger_wps); + +/** Restarts an infrastructure WiFi network + * + * @warning If a STA interface is active when this function is called, the softAP will\n + * start on the same channel as the STA. It will NOT use the channel provided! + * + * @param[in] ssid : A null terminated string containing + * the SSID name of the network to join + * @param[in] security_type : Authentication type: \n + * - RTW_SECURITY_OPEN - Open Security \n + * - RTW_SECURITY_WPA_TKIP_PSK - WPA Security \n + * - RTW_SECURITY_WPA2_AES_PSK - WPA2 Security using AES cipher \n + * - RTW_SECURITY_WPA2_MIXED_PSK - WPA2 Security using AES and/or TKIP ciphers \n + * - WEP security is NOT IMPLEMENTED. It is NOT SECURE! \n + * @param[in] password : A byte array containing the cleartext + * security key for the network + * @param[in] ssid_len : The length of the SSID in + * bytes. + * @param[in] password_len : The length of the security_key in + * bytes. + * @param[in] channel : 802.11 channel number + * + * @return RTW_SUCCESS : if successfully creates an AP + * RTW_ERROR : if an error occurred + */ +int wifi_restart_ap( + unsigned char *ssid, + rtw_security_t security_type, + unsigned char *password, + int ssid_len, + int password_len, + int channel); + + +int wifi_set_autoreconnect(__u8 mode); +int wifi_get_autoreconnect(__u8 *mode); +int wifi_get_last_error( ); +#ifdef CONFIG_CUSTOM_IE +#ifndef BIT +#define BIT(x) ((__u32)1 << (x)) +#endif + +#ifndef _CUSTOM_IE_TYPE_ +#define _CUSTOM_IE_TYPE_ +enum CUSTOM_IE_TYPE{ + PROBE_REQ = BIT(0), + PROBE_RSP = BIT(1), + BEACON = BIT(2), +}; +#endif /* _CUSTOM_IE_TYPE_ */ + +/* ie format + * +-----------+--------+-----------------------+ + * |element ID | length | content in length byte| + * +-----------+--------+-----------------------+ + * + * type: refer to CUSTOM_IE_TYPE + */ +#ifndef _CUS_IE_ +#define _CUS_IE_ +typedef struct _cus_ie{ + __u8 *ie; + __u8 type; +}cus_ie, *p_cus_ie; +#endif /* _CUS_IE_ */ + +int wifi_add_custom_ie(void *cus_ie, int ie_num); + +int wifi_update_custom_ie(void *cus_ie, int ie_index); + +int wifi_del_custom_ie(void); +#endif + +#ifdef CONFIG_PROMISC +void wifi_init_packet_filter(void); +int wifi_add_packet_filter(unsigned char filter_id, rtw_packet_filter_pattern_t *patt, rtw_packet_filter_rule_e rule); +int wifi_enable_packet_filter(unsigned char filter_id); +int wifi_disable_packet_filter(unsigned char filter_id); +int wifi_remove_packet_filter(unsigned char filter_id); +#endif + +#ifdef __cplusplus + } +#endif + +#endif // __WIFI_API_H + +//----------------------------------------------------------------------------// diff --git a/component/common/api/wifi/wifi_ind.c b/component/common/api/wifi/wifi_ind.c new file mode 100644 index 0000000..b5ed305 --- /dev/null +++ b/component/common/api/wifi/wifi_ind.c @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------// +#include "wifi/wifi_ind.h" +#include "wifi/wifi_conf.h" +#include "osdep_service.h" + +/****************************************************** + * Constants + ******************************************************/ + +#define WIFI_INDICATE_MSG 0 +#define WIFI_MANAGER_STACKSIZE 128 +#define WIFI_MANAGER_PRIORITY (5) +#define WIFI_MANAGER_PRIO 2 + +#define INDICATE_USE_THREAD 0 +#define WIFI_EVENT_MAX_ROW 3 +/****************************************************** + * Globals + ******************************************************/ + +static event_list_elem_t event_callback_list[WIFI_EVENT_MAX][WIFI_EVENT_MAX_ROW]; +#if INDICATE_USE_THREAD +static rtw_worker_thread_t wifi_worker_thread; +#endif + +//----------------------------------------------------------------------------// +#if INDICATE_USE_THREAD +static rtw_result_t rtw_send_event_to_worker(int event_cmd, char *buf, int buf_len, int flags) +{ + rtw_event_message_t message; + int i; + rtw_result_t ret = RTW_SUCCESS; + + if(event_cmd >= WIFI_EVENT_MAX) + return RTW_BADARG; + for(i = 0; i < WIFI_EVENT_MAX_ROW; i++){ + if(event_callback_list[event_cmd][i].handler == NULL) + continue; + + message.function = (event_handler_t)event_callback_list[event_cmd][i].handler; + message.buf = buf; + message.buf_len = buf_len; + message.flags = flags; + message.user_data = event_callback_list[event_cmd][i].handler_user_data; + + ret = rtw_push_to_xqueue(&wifi_worker_thread->event_queue, &message, 0); + if(ret != RTW_SUCCESS) + break; + } + return ret; +} +#else +static rtw_result_t rtw_indicate_event_handle(int event_cmd, char *buf, int buf_len, int flags) +{ + rtw_event_handler_t handle = NULL; + int i; + + if(event_cmd >= WIFI_EVENT_MAX) + return RTW_BADARG; + + for(i = 0; i < WIFI_EVENT_MAX_ROW; i++){ + handle = event_callback_list[event_cmd][i].handler; + if(handle == NULL) + continue; + handle(buf, buf_len, flags, event_callback_list[event_cmd][i].handler_user_data); + } + + return RTW_SUCCESS; +} +#endif + +void wifi_indication( WIFI_EVENT_INDICATE event, char *buf, int buf_len, int flags) +{ + // + // If upper layer application triggers additional operations on receiving of wext_wlan_indicate, + // please strictly check current stack size usage (by using uxTaskGetStackHighWaterMark() ) + // , and tries not to share the same stack with wlan driver if remaining stack space is + // not available for the following operations. + // ex: using semaphore to notice another thread. + switch(event) + { + case WIFI_EVENT_DISCONNECT: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r %s():Disconnection indication received", __FUNCTION__); +#endif +#if CONFIG_INIC_EN + inic_indicate_event(WIFI_LINK_DISCONNECTED); +#endif//CONFIG_INIC_EN + break; + case WIFI_EVENT_CONNECT: + // For WPA/WPA2 mode, indication of connection does not mean data can be + // correctly transmitted or received. Data can be correctly transmitted or + // received only when 4-way handshake is done. + // Please check WIFI_EVENT_FOURWAY_HANDSHAKE_DONE event +#if(WIFI_INDICATE_MSG==1) + // Sample: return mac address + if(buf != NULL && buf_len == 6) + { + printf("\n\r%s():Connect indication received: %02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, + buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]); + } +#endif +#if CONFIG_INIC_EN + inic_indicate_event(WIFI_LINK_CONNECTED); +#endif//CONFIG_INIC_EN + break; + case WIFI_EVENT_FOURWAY_HANDSHAKE_DONE: +#if(WIFI_INDICATE_MSG==1) + if(buf != NULL) + { + if(buf_len == strlen(IW_EXT_STR_FOURWAY_DONE)) + printf("\n\r%s():%s", __FUNCTION__, buf); + } +#endif + break; + case WIFI_EVENT_SCAN_RESULT_REPORT: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_SCAN_RESULT_REPORT\n", __func__); +#endif + break; + case WIFI_EVENT_SCAN_DONE: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_SCAN_DONE\n", __func__); +#endif + break; + case WIFI_EVENT_RECONNECTION_FAIL: +#if(WIFI_INDICATE_MSG==1) + if(buf != NULL){ + if(buf_len == strlen(IW_EXT_STR_RECONNECTION_FAIL)) + printf("\n\r%s", buf); + } +#endif + break; + case WIFI_EVENT_NO_NETWORK: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_NO_NETWORK\n", __func__); +#endif + break; +#ifdef CONFIG_P2P_NEW + case WIFI_EVENT_SEND_ACTION_DONE: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_SEND_ACTION_DONE\n", __func__); +#endif + break; + case WIFI_EVENT_RX_MGNT: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_RX_MGNT\n", __func__); +#endif + break; + case WIFI_EVENT_STA_ASSOC: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_STA_ASSOC\n", __func__); +#endif + break; + case WIFI_EVENT_STA_DISASSOC: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_STA_DISASSOC\n", __func__); +#endif + break; +#endif //CONFIG_P2P_NEW +#ifdef CONFIG_WPS + case WIFI_EVENT_WPS_FINISH: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_WPS_FINISH\n", __func__); +#endif + break; + case WIFI_EVENT_EAPOL_RECVD: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_EAPOL_RECVD\n", __func__); +#endif + break; +#endif + case WIFI_EVENT_BEACON_AFTER_DHCP: +#if(WIFI_INDICATE_MSG==1) + printf("\n\r%s(): WIFI_EVENT_BEACON_AFTER_DHCP\n", __func__); +#endif + break; + } + +#if INDICATE_USE_THREAD + rtw_send_event_to_worker(event, buf, buf_len, flags); +#else + rtw_indicate_event_handle(event, buf, buf_len, flags); +#endif +} + +void wifi_reg_event_handler(unsigned int event_cmds, rtw_event_handler_t handler_func, void *handler_user_data) +{ + int i = 0; + if(event_cmds < WIFI_EVENT_MAX){ + for(i=0; i < WIFI_EVENT_MAX_ROW; i++){ + if(event_callback_list[event_cmds][i].handler == NULL){ + event_callback_list[event_cmds][i].handler = handler_func; + event_callback_list[event_cmds][i].handler_user_data = handler_user_data; + return; + } + } + } +} + +void wifi_unreg_event_handler(unsigned int event_cmds, rtw_event_handler_t handler_func) +{ + int i; + if(event_cmds < WIFI_EVENT_MAX){ + for(i = 0; i < WIFI_EVENT_MAX_ROW; i++){ + if(event_callback_list[event_cmds][i].handler == handler_func){ + event_callback_list[event_cmds][i].handler = NULL; + event_callback_list[event_cmds][i].handler_user_data = NULL; + return; + } + } + } +} + +void init_event_callback_list(){ + memset(event_callback_list, 0, sizeof(event_callback_list)); +} + +int wifi_manager_init() +{ +#if INDICATE_USE_THREAD + rtw_create_worker_thread(&wifi_worker_thread, + WIFI_MANAGER_PRIORITY, + WIFI_MANAGER_STACKSIZE, + WIFI_MANAGER_PRIO); +#endif + return 0; +} + +void rtw_wifi_manager_deinit() +{ +#if INDICATE_USE_THREAD + rtw_delete_worker_thread(&wifi_worker_thread); +#endif +} + diff --git a/component/common/api/wifi/wifi_ind.h b/component/common/api/wifi/wifi_ind.h new file mode 100644 index 0000000..be708ea --- /dev/null +++ b/component/common/api/wifi/wifi_ind.h @@ -0,0 +1,52 @@ +#ifndef _WIFI_INDICATE_H +#define _WIFI_INDICATE_H +#include "wifi_conf.h" + +typedef enum _WIFI_EVENT_INDICATE{ + WIFI_EVENT_CONNECT = 0, + WIFI_EVENT_DISCONNECT = 1, + WIFI_EVENT_FOURWAY_HANDSHAKE_DONE = 2, + WIFI_EVENT_SCAN_RESULT_REPORT = 3, + WIFI_EVENT_SCAN_DONE = 4, + WIFI_EVENT_RECONNECTION_FAIL = 5, + WIFI_EVENT_SEND_ACTION_DONE = 6, + WIFI_EVENT_RX_MGNT = 7, + WIFI_EVENT_STA_ASSOC = 8, + WIFI_EVENT_STA_DISASSOC = 9, + WIFI_EVENT_WPS_FINISH = 10, + WIFI_EVENT_EAPOL_RECVD = 11, + WIFI_EVENT_NO_NETWORK = 12, + WIFI_EVENT_BEACON_AFTER_DHCP = 13, + WIFI_EVENT_MAX, +}WIFI_EVENT_INDICATE; + +typedef void (*rtw_event_handler_t)(char *buf, int buf_len, int flags, void* handler_user_data ); + +typedef struct +{ +// WIFI_EVENT_INDICATE event_cmd; + rtw_event_handler_t handler; + void* handler_user_data; +} event_list_elem_t; + +void init_event_callback_list(void); +extern void wifi_indication( WIFI_EVENT_INDICATE event, char *buf, int buf_len, int flags); +/** Register the event listener + * + * @param[in] event_cmds : The event command number indicated + * @param[in] handler_func : the callback function which will + * receive and process the event + * @param[in] handler_user_data : user specific data that will be + * passed directly to the callback function + * + * @note : Set the same event_cmds with empty handler_func will + * unregister the event_cmds + * + * @return RTW_SUCCESS : if successfully registers the event + * RTW_ERROR : if an error occurred + */ +extern void wifi_reg_event_handler(unsigned int event_cmds, rtw_event_handler_t handler_func, void *handler_user_data); +extern void wifi_unreg_event_handler(unsigned int event_cmds, rtw_event_handler_t handler_func); + +#endif //_WIFI_INDICATE_H + diff --git a/component/common/api/wifi/wifi_promisc.c b/component/common/api/wifi/wifi_promisc.c new file mode 100644 index 0000000..51c0633 --- /dev/null +++ b/component/common/api/wifi/wifi_promisc.c @@ -0,0 +1,333 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" +#include "tcpip.h" +#include "wifi/wifi_conf.h" + +#ifndef CONFIG_WLAN +#define CONFIG_WLAN 1 +#endif + +#if CONFIG_WLAN +#include + +struct eth_frame { + struct eth_frame *prev; + struct eth_frame *next; + unsigned char da[6]; + unsigned char sa[6]; + unsigned int len; + unsigned char type; +}; + +struct eth_buffer { + struct eth_frame *head; + struct eth_frame *tail; +}; + +static struct eth_buffer eth_buffer; + +#ifdef CONFIG_PROMISC +#define MAX_PACKET_FILTER_INFO 5 +#define FILTER_ID_INIT_VALUE 10 +rtw_packet_filter_info_t paff_array[MAX_PACKET_FILTER_INFO]={0, 0, 0, 0, 0}; +static u8 packet_filter_enable_num = 0; + +void promisc_init_packet_filter() +{ + int i = 0; + for(i=0; ioffset; + paff_array[i].patt.mask_size = patt->mask_size; + paff_array[i].patt.mask = pvPortMalloc(patt->mask_size); + memcpy(paff_array[i].patt.mask, patt->mask, patt->mask_size); + paff_array[i].patt.pattern= pvPortMalloc(patt->mask_size); + memcpy(paff_array[i].patt.pattern, patt->pattern, patt->mask_size); + + paff_array[i].rule = rule; + paff_array[i].enable = 0; + + return 0; +} + +int promisc_enable_packet_filter(u8 filter_id) +{ + int i = 0; + while(i < MAX_PACKET_FILTER_INFO){ + if(paff_array[i].filter_id == filter_id) + break; + i++; + } + + if(i == MAX_PACKET_FILTER_INFO) + return -1; + + paff_array[i].enable = 1; + packet_filter_enable_num++; + return 0; +} + +int promisc_disable_packet_filter(u8 filter_id) +{ + int i = 0; + while(i < MAX_PACKET_FILTER_INFO){ + if(paff_array[i].filter_id == filter_id) + break; + i++; + } + + if(i == MAX_PACKET_FILTER_INFO) + return -1; + + paff_array[i].enable = 0; + packet_filter_enable_num--; + return 0; +} + +int promisc_remove_packet_filter(u8 filter_id) +{ + int i = 0; + while(i < MAX_PACKET_FILTER_INFO){ + if(paff_array[i].filter_id == filter_id) + break; + i++; + } + + if(i == MAX_PACKET_FILTER_INFO) + return -1; + + paff_array[i].filter_id = FILTER_ID_INIT_VALUE; + paff_array[i].enable = 0; + paff_array[i].patt.mask_size = 0; + paff_array[i].rule = 0; + if(paff_array[i].patt.mask){ + vPortFree(paff_array[i].patt.mask); + paff_array[i].patt.mask = NULL; + } + + if(paff_array[i].patt.pattern){ + vPortFree(paff_array[i].patt.pattern); + paff_array[i].patt.pattern = NULL; + } + return 0; +} +#endif + +/* Make callback simple to prevent latency to wlan rx when promiscuous mode */ +static void promisc_callback(unsigned char *buf, unsigned int len, void* userdata) +{ + struct eth_frame *frame = (struct eth_frame *) pvPortMalloc(sizeof(struct eth_frame)); + + if(frame) { + frame->prev = NULL; + frame->next = NULL; + memcpy(frame->da, buf, 6); + memcpy(frame->sa, buf+6, 6); + frame->len = len; + + taskENTER_CRITICAL(); + + if(eth_buffer.tail) { + eth_buffer.tail->next = frame; + frame->prev = eth_buffer.tail; + eth_buffer.tail = frame; + } + else { + eth_buffer.head = frame; + eth_buffer.tail = frame; + } + + taskEXIT_CRITICAL(); + } +} + +struct eth_frame* retrieve_frame(void) +{ + struct eth_frame *frame = NULL; + + taskENTER_CRITICAL(); + + if(eth_buffer.head) { + frame = eth_buffer.head; + + if(eth_buffer.head->next) { + eth_buffer.head = eth_buffer.head->next; + eth_buffer.head->prev = NULL; + } + else { + eth_buffer.head = NULL; + eth_buffer.tail = NULL; + } + } + + taskEXIT_CRITICAL(); + + return frame; +} + +static void promisc_test(int duration, unsigned char len_used) +{ + int ch; + unsigned int start_time; + struct eth_frame *frame; + eth_buffer.head = NULL; + eth_buffer.tail = NULL; + + wifi_enter_promisc_mode(); + wifi_set_promisc(RTW_PROMISC_ENABLE, promisc_callback, len_used); + + for(ch = 1; ch <= 13; ch ++) { + if(wifi_set_channel(ch) == 0) + printf("\n\n\rSwitch to channel(%d)", ch); + + start_time = xTaskGetTickCount(); + + while(1) { + unsigned int current_time = xTaskGetTickCount(); + + if((current_time - start_time) < (duration * configTICK_RATE_HZ)) { + frame = retrieve_frame(); + + if(frame) { + int i; + printf("\n\rDA:"); + for(i = 0; i < 6; i ++) + printf(" %02x", frame->da[i]); + printf(", SA:"); + for(i = 0; i < 6; i ++) + printf(" %02x", frame->sa[i]); + printf(", len=%d", frame->len); + + vPortFree((void *) frame); + } + else + vTaskDelay(1); //delay 1 tick + } + else + break; + } + } + + wifi_set_promisc(RTW_PROMISC_DISABLE, NULL, 0); + + while((frame = retrieve_frame()) != NULL) + vPortFree((void *) frame); +} + +static void promisc_callback_all(unsigned char *buf, unsigned int len, void* userdata) +{ + struct eth_frame *frame = (struct eth_frame *) pvPortMalloc(sizeof(struct eth_frame)); + + if(frame) { + frame->prev = NULL; + frame->next = NULL; + memcpy(frame->da, buf+4, 6); + memcpy(frame->sa, buf+10, 6); + frame->len = len; + frame->type = *buf; + + taskENTER_CRITICAL(); + + if(eth_buffer.tail) { + eth_buffer.tail->next = frame; + frame->prev = eth_buffer.tail; + eth_buffer.tail = frame; + } + else { + eth_buffer.head = frame; + eth_buffer.tail = frame; + } + + taskEXIT_CRITICAL(); + } +} +static void promisc_test_all(int duration, unsigned char len_used) +{ + int ch; + unsigned int start_time; + struct eth_frame *frame; + eth_buffer.head = NULL; + eth_buffer.tail = NULL; + + wifi_enter_promisc_mode(); + wifi_set_promisc(RTW_PROMISC_ENABLE_2, promisc_callback_all, len_used); + + for(ch = 1; ch <= 13; ch ++) { + if(wifi_set_channel(ch) == 0) + printf("\n\n\rSwitch to channel(%d)", ch); + + start_time = xTaskGetTickCount(); + + while(1) { + unsigned int current_time = xTaskGetTickCount(); + + if((current_time - start_time) < (duration * configTICK_RATE_HZ)) { + frame = retrieve_frame(); + + if(frame) { + int i; + printf("\n\rTYPE: 0x%x, ", frame->type); + printf("DA:"); + for(i = 0; i < 6; i ++) + printf(" %02x", frame->da[i]); + printf(", SA:"); + for(i = 0; i < 6; i ++) + printf(" %02x", frame->sa[i]); + printf(", len=%d", frame->len); + + vPortFree((void *) frame); + } + else + vTaskDelay(1); //delay 1 tick + } + else + break; + } + } + + wifi_set_promisc(RTW_PROMISC_DISABLE, NULL, 0); + + while((frame = retrieve_frame()) != NULL) + vPortFree((void *) frame); +} + +void cmd_promisc(int argc, char **argv) +{ + int duration; + #ifdef CONFIG_PROMISC + wifi_init_packet_filter(); + #endif + if((argc == 2) && ((duration = atoi(argv[1])) > 0)) + //promisc_test(duration, 0); + promisc_test_all(duration, 0); + else if((argc == 3) && ((duration = atoi(argv[1])) > 0) && (strcmp(argv[2], "with_len") == 0)) + promisc_test(duration, 1); + else + printf("\n\rUsage: %s DURATION_SECONDS [with_len]", argv[0]); +} +#endif //#if CONFIG_WLAN diff --git a/component/common/api/wifi/wifi_simple_config.c b/component/common/api/wifi/wifi_simple_config.c new file mode 100644 index 0000000..a34bcc6 --- /dev/null +++ b/component/common/api/wifi/wifi_simple_config.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" +#include "udp.h" +#include +#include +#include +#include "platform_stdlib.h" + +#define STACKSIZE 512 + + +#if CONFIG_WLAN +#if (CONFIG_INCLUDE_SIMPLE_CONFIG) +#include "wifi/wifi_conf.h" +int is_promisc_callback_unlock = 0; +static int is_fixed_channel; +int fixed_channel_num; +unsigned char g_ssid[32]; +int g_ssid_len; +extern unsigned char g_bssid[]; +extern unsigned char g_security_mode; +extern int promisc_get_fixed_channel( void *, u8 *, int* ); +struct pattern_test_ops; +struct rtk_test_sc; + +typedef int (*sc_test_check_pattern_call_back)(struct pattern_test_ops *pp, struct rtk_test_sc *pSc); +typedef int (*sc_test_get_cipher_info_call_back)(struct pattern_test_ops *pp, struct rtk_test_sc *pSc); +typedef int (*sc_test_generate_key_call_back)(struct pattern_test_ops *pp, struct rtk_test_sc *pSc); +typedef int (*sc_test_decode_profile_call_back)(struct pattern_test_ops *pp, struct rtk_test_sc *pSc); +typedef int (*sc_test_get_tlv_info_call_back)(struct pattern_test_ops *pp, struct rtk_test_sc *pSc); + +struct pattern_test_ops { + unsigned int index; + unsigned flag; + unsigned char name[64]; + sc_test_check_pattern_call_back check_pattern; + sc_test_get_cipher_info_call_back get_cipher_info; + sc_test_generate_key_call_back generate_key; + sc_test_decode_profile_call_back decode_profile; + sc_test_get_tlv_info_call_back get_tlv_info; +}; + + +struct rtk_test_sc { + int pattern_type; + unsigned char smac[6]; /* the source mac of the profile packet, it maybe the Phone MAC */ + unsigned char dmac[6]; /* the destination mac, it is the DUT wlan MAC for send ack packet */ + unsigned char ssid[32]; + unsigned char password[65]; + unsigned int ip_addr; + unsigned char sync_pkt[9][6]; /* parser syn start */ + unsigned char profile_pkt[256][6]; + unsigned int profile_pkt_len; + unsigned char plain_buf[256]; + unsigned int plain_len; + unsigned char key_buf[32]; /* kek */ + unsigned int key_len; + unsigned char crypt_buf[256]; + unsigned int crypt_len; + int pattern_index; /* pattern start index */ + struct pattern_ops *pattern[64]; /* pattern array */ + int max_pattern_num; /* total register pattern num */ + unsigned char pin[65]; + unsigned char default_pin[65]; + unsigned char have_pin; + unsigned short device_type; + unsigned char device_name[64]; +}; + +enum sc_result { + SC_ERROR = -1, /* default error code*/ + SC_NO_CONTROLLER_FOUND = 1, /* cannot get sta(controller) in the air which starts a simple config session */ + SC_CONTROLLER_INFO_PARSE_FAIL, /* cannot parse the sta's info */ + SC_TARGET_CHANNEL_SCAN_FAIL, /* cannot scan the target channel */ + SC_JOIN_BSS_FAIL, /* fail to connect to target ap */ + SC_DHCP_FAIL, /* fail to get ip address from target ap */ + /* fail to create udp socket to send info to controller. note that client isolation + must be turned off in ap. we cannot know if ap has configured this */ + SC_UDP_SOCKET_CREATE_FAIL, + SC_SUCCESS, /* default success code */ + +}; + +#ifdef PACK_STRUCT_USE_INCLUDES +#include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ack_msg { + PACK_STRUCT_FIELD(u8_t flag); + PACK_STRUCT_FIELD(u16_t length); + PACK_STRUCT_FIELD(u8_t smac[6]); + PACK_STRUCT_FIELD(u8_t status); + PACK_STRUCT_FIELD(u16_t device_type); + PACK_STRUCT_FIELD(u32_t device_ip); + PACK_STRUCT_FIELD(u8_t device_name[64]); +};PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +#include "arch/epstruct.h" +#endif + + + +#define MULCAST_PORT (8864) + +#define WIFI_LINK_CONNECTED (1) + +#define SCAN_BUFFER_LENGTH (1024) + +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif + +int simple_config_result; +static struct ack_msg *ack_content; +static struct rtk_test_sc *backup_sc_ctx; +extern struct netif xnetif[NET_IF_NUM]; +void SC_set_ack_content() +{ + memset(ack_content, 0, sizeof(struct ack_msg)); + ack_content->flag = 0x20; + ack_content->length = htons(sizeof(struct ack_msg)-3); + memcpy(ack_content->smac, xnetif[0].hwaddr, 6); + ack_content->status = 0; + ack_content->device_type = 0; + ack_content->device_ip = xnetif[0].ip_addr.addr; + memset(ack_content->device_name, 0, 64); +} + +int SC_send_simple_config_ack() +{ + + int ack_transmit_round, ack_num_each_sec; + int ack_socket; + //int sended_data = 0; + struct sockaddr_in to_addr; + + SC_set_ack_content(); + + ack_socket = socket(PF_INET, SOCK_DGRAM, IP_PROTO_UDP); + if (ack_socket == -1) { + return -1; + } + + FD_ZERO(&to_addr); + to_addr.sin_family = AF_INET; + to_addr.sin_port = htons(8864); + to_addr.sin_addr.s_addr = (backup_sc_ctx->ip_addr); + + for (ack_transmit_round = 0;ack_transmit_round < 10; ack_transmit_round++) { + for (ack_num_each_sec = 0;ack_num_each_sec < 20; ack_num_each_sec++) { + //sended_data = + sendto(ack_socket, (unsigned char *)ack_content, sizeof(struct ack_msg), 0, (struct sockaddr *) &to_addr, sizeof(struct sockaddr)); + //printf("\r\nAlready send %d bytes data\n", sended_data); + vTaskDelay(50); /* delay 50 ms */ + } + } + close(ack_socket); + return 0; +} + + + +static int SC_check_and_show_connection_info(void) +{ + rtw_wifi_setting_t setting; + int ret = -1; + + /* If not rise priority, LwIP DHCP may timeout */ + vTaskPrioritySet(NULL, tskIDLE_PRIORITY + 3); + /* Start DHCP Client */ + ret = LwIP_DHCP(0, DHCP_START); + vTaskPrioritySet(NULL, tskIDLE_PRIORITY + 1); + + wifi_get_setting(WLAN0_NAME, &setting); + wifi_show_setting(WLAN0_NAME, &setting); + + if (ret == DHCP_ADDRESS_ASSIGNED) + return SC_SUCCESS; + else + return SC_DHCP_FAIL; +} + +static void check_and_set_security_in_connection(rtw_security_t security_mode, rtw_network_info_t *wifi) +{ + + if (security_mode == RTW_SECURITY_WPA2_AES_PSK) { + printf("\r\nwifi->security_type = RTW_SECURITY_WPA2_AES_PSK\n"); + wifi->security_type = RTW_SECURITY_WPA2_AES_PSK; + } else if (security_mode == RTW_SECURITY_WEP_PSK) { + printf("\r\nwifi->security_type = RTW_SECURITY_WEP_PSK\n"); + wifi->security_type = RTW_SECURITY_WEP_PSK; + wifi->key_id = 0; + } else if (security_mode == RTW_SECURITY_WPA_AES_PSK) { + printf("\r\nwifi->security_type = RTW_SECURITY_WPA_AES_PSK\n"); + wifi->security_type = RTW_SECURITY_WPA_AES_PSK; + } else { + printf("\r\nwifi->security_type = RTW_SECURITY_OPEN\n"); + wifi->security_type = RTW_SECURITY_OPEN; + } +} + +static int get_connection_info_from_profile(rtw_security_t security_mode, rtw_network_info_t *wifi) +{ + + printf("\r\n======= Connection Information =======\n"); + check_and_set_security_in_connection(security_mode, wifi); + + wifi->password = backup_sc_ctx->password; + wifi->password_len = (int)strlen((char const *)backup_sc_ctx->password); + + if ( g_ssid_len > 0 && g_ssid_len < 33){ + wifi->ssid.len = g_ssid_len; + rtw_memcpy(wifi->ssid.val, g_ssid, wifi->ssid.len); + }else if(strlen(backup_sc_ctx->ssid) > 0 && strlen(backup_sc_ctx->ssid) < 33){ + wifi->ssid.len = strlen(backup_sc_ctx->ssid); + rtw_memcpy(wifi->ssid.val, backup_sc_ctx->ssid, wifi->ssid.len); + }else{ + printf("\r\n SSID is NULL or SSID length is over 32!!"); + return -1; + } + + if(wifi->security_type == RTW_SECURITY_WEP_PSK) + { + if(wifi->password_len == 10) + { + u32 p[5]; + u8 pwd[6], i = 0; + sscanf((const char*)backup_sc_ctx->password, "%02x%02x%02x%02x%02x", &p[0], &p[1], &p[2], &p[3], &p[4]); + for(i=0; i< 5; i++) + pwd[i] = (u8)p[i]; + pwd[5] = '\0'; + memset(backup_sc_ctx->password, 0, 65); + strcpy((char*)backup_sc_ctx->password, (char*)pwd); + wifi->password_len = 5; + }else if(wifi->password_len == 26){ + u32 p[13]; + u8 pwd[14], i = 0; + sscanf((const char*)backup_sc_ctx->password, "%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x", &p[0], &p[1], &p[2], &p[3], &p[4],\ + &p[5], &p[6], &p[7], &p[8], &p[9], &p[10], &p[11], &p[12]); + for(i=0; i< 13; i++) + pwd[i] = (u8)p[i]; + pwd[13] = '\0'; + memset(backup_sc_ctx->password, 0, 64); + strcpy((char*)backup_sc_ctx->password, (char*)pwd); + wifi->password_len = 13; + } + } + printf("\r\nwifi.password = %s\n", wifi->password); + printf("\r\nwifi.password_len = %d\n", wifi->password_len); + printf("\r\nwifi.ssid = %s\n", wifi->ssid.val); + printf("\r\nwifi.ssid_len = %d\n", wifi->ssid.len); + printf("\r\nwifi.channel = %d\n", fixed_channel_num); + printf("\r\n===== start to connect target AP =====\n"); + return 0; +} + +int SC_connect_to_AP(void) +{ + int ret = SC_ERROR; + u8 scan_channel; + u8 pscan_config; + int retry = 3; + rtw_security_t security_mode; + rtw_network_info_t wifi = {0}; + if(!(fixed_channel_num == 0)){ + scan_channel = fixed_channel_num; + } + pscan_config = PSCAN_ENABLE; + switch(g_security_mode){ + case RTW_ENCRYPTION_OPEN: + security_mode = RTW_SECURITY_OPEN; + break; + case RTW_ENCRYPTION_WEP40: + case RTW_ENCRYPTION_WEP104: + security_mode = RTW_SECURITY_WEP_PSK; + break; + case RTW_ENCRYPTION_WPA_TKIP: + case RTW_ENCRYPTION_WPA_AES: + case RTW_ENCRYPTION_WPA2_TKIP: + case RTW_ENCRYPTION_WPA2_AES: + case RTW_ENCRYPTION_WPA2_MIXED: + security_mode = RTW_SECURITY_WPA2_AES_PSK; + break; + case RTW_ENCRYPTION_UNKNOWN: + case RTW_ENCRYPTION_UNDEF: + default: + printf("\r\n unknow security mode,connect fail!"); + return ret; + } + g_security_mode = 0xff;//clear it + + if (-1 == get_connection_info_from_profile(security_mode, &wifi)) { + ret = SC_CONTROLLER_INFO_PARSE_FAIL; + return ret; + } + + while (1) { + if(wifi_set_pscan_chan(&scan_channel, &pscan_config, 1) < 0){ + printf("\n\rERROR: wifi set partial scan channel fail"); + ret = SC_TARGET_CHANNEL_SCAN_FAIL; + return ret; + } +#if 0 + ret = wifi_connect((char*)wifi.ssid.val, + wifi.security_type, + (char*)wifi.password, + wifi.ssid.len, + wifi.password_len, + wifi.key_id, + NULL); +#else + ret = wifi_connect_bssid(g_bssid, + (char*)wifi.ssid.val, + wifi.security_type, + (char*)wifi.password, + 6, + wifi.ssid.len, + wifi.password_len, + wifi.key_id, + NULL); +#endif + if (ret == RTW_SUCCESS) { + ret = SC_check_and_show_connection_info(); + break; + } + + if (retry == 0) { + ret = SC_JOIN_BSS_FAIL; + break; + } + retry --; + } + + return ret; +} +/* Make callback one by one to wlan rx when promiscuous mode */ +extern int rtk_start_parse_packet(unsigned char *da, unsigned char *sa, int len, void *user_data, struct rtk_test_sc *backup_sc_ctx); +void simple_config_callback(unsigned char *buf, unsigned int len, void* userdata) +{ + unsigned char * da = buf; + unsigned char * sa = buf + ETH_ALEN; + taskENTER_CRITICAL(); + if (is_promisc_callback_unlock == 1) { + simple_config_result = rtk_start_parse_packet(da, sa, len, userdata, backup_sc_ctx); + //printf("\r\nresult in callback function = %d\n",simple_config_result); + } + taskEXIT_CRITICAL(); + +} + +static unsigned int simple_config_cmd_start_time; +static unsigned int simple_config_cmd_current_time; +extern int simple_config_status; +extern void rtk_restart_simple_config(void); + +extern int rtk_sc_init(char *custom_pin_code); +extern void rtk_sc_deinit(void); + +int init_test_data(char *custom_pin_code) +{ +#if (CONFIG_INCLUDE_SIMPLE_CONFIG) + is_promisc_callback_unlock = 1; + is_fixed_channel = 0; + fixed_channel_num = 0; + simple_config_result = 0; + rtw_memset(g_ssid, 0, 32); + g_ssid_len = 0; + simple_config_cmd_start_time = xTaskGetTickCount(); + + if (ack_content != NULL) { + vPortFree(ack_content); + ack_content = NULL; + } + ack_content = pvPortMalloc(sizeof(struct ack_msg)); + if (!ack_content) { + printf("\n\rrtk_sc_init fail by allocate ack\n"); + } + memset(ack_content, 0, sizeof(struct ack_msg)); + + + if(custom_pin_code) { + backup_sc_ctx = pvPortMalloc(sizeof(struct rtk_test_sc)); + if (!backup_sc_ctx) { + printf("\n\r[Mem]malloc SC context fail\n"); + } else { + memset(backup_sc_ctx, 0, sizeof(struct rtk_test_sc)); + if (rtk_sc_init(custom_pin_code) < 0) { + printf("\n\rRtk_sc_init fail\n"); + } else { + return 0; + } + } + } else{ + backup_sc_ctx = pvPortMalloc(sizeof(struct rtk_test_sc)); + if (!backup_sc_ctx) { + printf("\n\r[Mem]malloc SC context fail\n"); + } else { + memset(backup_sc_ctx, 0, sizeof(struct rtk_test_sc)); + if (rtk_sc_init(NULL) < 0){ + printf("\n\rRtk_sc_init fail\n"); + } else { + return 0; + } + } + } +#else + printf("\n\rPlatform no include simple config now\n"); +#endif + return -1; +} + +void deinit_test_data(){ +#if (CONFIG_INCLUDE_SIMPLE_CONFIG) + rtk_sc_deinit(); + if (backup_sc_ctx != NULL) { + vPortFree(backup_sc_ctx); + backup_sc_ctx = NULL; + } + if (ack_content != NULL) { + vPortFree(ack_content); + ack_content = NULL; + } +#endif +} + +int simple_config_test(void) +{ + int channel = 1; + int ret = SC_SUCCESS; + unsigned int start_time; + int is_need_connect_to_AP = 0; + int fix_channel = 0; + int delta_time = 0; + wifi_set_promisc(RTW_PROMISC_ENABLE, simple_config_callback, 1); + start_time = xTaskGetTickCount(); + printf("\n\r"); + wifi_set_channel(channel); + while (1) { + vTaskDelay(50); //delay 0.5s to release CPU usage + simple_config_cmd_current_time = xTaskGetTickCount(); + if (simple_config_cmd_current_time - simple_config_cmd_start_time < ((120 + delta_time)*configTICK_RATE_HZ)) { + unsigned int current_time = xTaskGetTickCount(); + if (((current_time - start_time)*1000 /configTICK_RATE_HZ < 100) + || (is_fixed_channel == 1)) { + if((is_fixed_channel == 0)&&(!((fix_channel = promisc_get_fixed_channel(g_bssid,g_ssid,&g_ssid_len))== 0))){ + //printf("\r\n in simple_config_test fix channel = %d ",fix_channel); + is_fixed_channel = 1; + fixed_channel_num = fix_channel; + wifi_set_channel(fix_channel); + } + if (simple_config_result == 1) { + is_need_connect_to_AP = 1; + is_fixed_channel = 0; + break; + } + if (simple_config_result == -1) { + printf("\r\nsimple_config_test restart for result = -1"); + delta_time = 60; + wifi_set_channel(1); + is_need_connect_to_AP = 0; + is_fixed_channel = 0; + fixed_channel_num = 0; + memset(g_ssid, 0, 32); + g_ssid_len = 0; + simple_config_result = 0; + g_security_mode = 0xff; + rtk_restart_simple_config(); + } + if (simple_config_result == -2) { + printf("\n\rThe APP or client must have pin!\n"); + break; + } + } else { + channel++; + if ((1 <= channel) && (channel <= 13)) { + if (wifi_set_channel(channel) == 0) { + start_time = xTaskGetTickCount(); + printf("\n\rSwitch to channel(%d)\n", channel); + } + } else { + channel = 1; + if (wifi_set_channel(channel) == 0) { + start_time = xTaskGetTickCount(); + printf("\n\rSwitch to channel(%d)\n", channel); + } + } + + } + } else { + ret = SC_NO_CONTROLLER_FOUND; + break; + } + } + wifi_set_promisc(RTW_PROMISC_DISABLE, NULL, 0); + if (is_need_connect_to_AP == 1) { + int tmp_res = SC_connect_to_AP(); + if (SC_SUCCESS == tmp_res) { + if(-1 == SC_send_simple_config_ack()) + ret = SC_UDP_SOCKET_CREATE_FAIL; + } else { + return tmp_res; + } + + } else { + ret = SC_NO_CONTROLLER_FOUND; + } + + deinit_test_data(); + return ret; +} + +//Filter packet da[] = {0x01, 0x00, 0x5e} +#define MASK_SIZE 3 +void filter_add_enable(){ + u8 mask[MASK_SIZE]={0xFF,0xFF,0xFF}; + u8 pattern[MASK_SIZE]={0x01,0x00,0x5e}; + + rtw_packet_filter_pattern_t packet_filter; + rtw_packet_filter_rule_e rule; + + packet_filter.offset = 0; + packet_filter.mask_size = 3; + packet_filter.mask = mask; + packet_filter.pattern = pattern; + rule = RTW_POSITIVE_MATCHING; + + wifi_init_packet_filter(); + wifi_add_packet_filter(1, &packet_filter,rule); + wifi_enable_packet_filter(1); +} +void remove_filter(){ + wifi_disable_packet_filter(1); + wifi_remove_packet_filter(1); +} + +void print_simple_config_result(enum sc_result sc_code) +{ + printf("\r\n"); + switch (sc_code) { + case SC_NO_CONTROLLER_FOUND: + printf("Simple Config timeout!! Can't get Ap profile. Please try again\n"); + break; + case SC_CONTROLLER_INFO_PARSE_FAIL: + printf("Simple Config fail, cannot parse target ap info from controller\n"); + break; + case SC_TARGET_CHANNEL_SCAN_FAIL: + printf("Simple Config cannot scan the target channel\n"); + break; + case SC_JOIN_BSS_FAIL: + printf("Simple Config Join bss failed\n"); + break; + case SC_DHCP_FAIL: + printf("Simple Config fail, cannot get dhcp ip address\n"); + break; + case SC_UDP_SOCKET_CREATE_FAIL: + printf("Simple Config Ack socket create fail!!!\n"); + break; + case SC_SUCCESS: + printf("Simple Config success\n"); + break; + + case SC_ERROR: + default: + printf("unknown error when simple config!\n"); + + } + +} + + + + +#endif //CONFIG_INCLUDE_SIMPLE_CONFIG + +void cmd_simple_config(int argc, char **argv){ +#if CONFIG_INCLUDE_SIMPLE_CONFIG + char *custom_pin_code = NULL; + int ret = SC_ERROR; + + if(argc > 2){ + printf("\n\rInput Error!"); + } + + if(argc == 2) + custom_pin_code = (argv[1]); + + wifi_enter_promisc_mode(); + if(init_test_data(custom_pin_code) == 0){ + filter_add_enable(); + ret = simple_config_test(); + print_simple_config_result(ret); + remove_filter(); + } +#endif +} +#endif //#if CONFIG_WLAN diff --git a/component/common/api/wifi/wifi_util.c b/component/common/api/wifi/wifi_util.c new file mode 100644 index 0000000..2031d77 --- /dev/null +++ b/component/common/api/wifi/wifi_util.c @@ -0,0 +1,933 @@ +#include +#include +#include "FreeRTOS.h" +#include +#include +#include +#include + +int iw_ioctl(const char * ifname, unsigned long request, struct iwreq * pwrq) +{ + memcpy(pwrq->ifr_name, ifname, 5); + return rltk_wlan_control(request, (void *) pwrq); +} + +int wext_get_ssid(const char *ifname, __u8 *ssid) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.essid.pointer = ssid; + iwr.u.essid.length = 32; + + if (iw_ioctl(ifname, SIOCGIWESSID, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWESSID] ssid = NULL, not connected"); //do not use perror + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0') + ret--; + ssid[ret] = '\0'; + } + + return ret; +} + +int wext_set_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.essid.pointer = (void *) ssid; + iwr.u.essid.length = ssid_len; + iwr.u.essid.flags = (ssid_len != 0); + + if (iw_ioctl(ifname, SIOCSIWESSID, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWESSID] error"); + ret = -1; + } + + return ret; +} + +int wext_set_bssid(const char *ifname, const __u8 *bssid) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + + if(bssid[ETH_ALEN]=='#' && bssid[ETH_ALEN + 1]=='@'){ + memcpy(iwr.u.ap_addr.sa_data + ETH_ALEN, bssid + ETH_ALEN, 6); + } + + if (iw_ioctl(ifname, SIOCSIWAP, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWAP] error"); + ret = -1; + } + + return ret; +} + +int is_broadcast_ether_addr(const unsigned char *addr) +{ + return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff; +} + +int wext_set_auth_param(const char *ifname, __u16 idx, __u32 value) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (iw_ioctl(ifname, SIOCSIWAUTH, &iwr) < 0) { + printf("\n\rWEXT: SIOCSIWAUTH(param %d value 0x%x) failed)", idx, value); + } + + return ret; +} + +int wext_set_key_ext(const char *ifname, __u16 alg, const __u8 *addr, int key_idx, int set_tx, const __u8 *seq, __u16 seq_len, __u8 *key, __u16 key_len) +{ + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + ext = (struct iw_encode_ext *) malloc(sizeof(struct iw_encode_ext) + key_len); + if (ext == NULL) + return -1; + else + memset(ext, 0, sizeof(struct iw_encode_ext) + key_len); + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + iwr.u.encoding.pointer = ext; + iwr.u.encoding.length = sizeof(struct iw_encode_ext) + key_len; + + if (alg == IW_ENCODE_DISABLED) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + + if (addr == NULL || is_broadcast_ether_addr(addr)) + ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; + + if (set_tx) + ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; + + ext->addr.sa_family = ARPHRD_ETHER; + + if (addr) + memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + memset(ext->addr.sa_data, 0xff, ETH_ALEN); + + if (key && key_len) { + memcpy(ext->key, key, key_len); + ext->key_len = key_len; + } + + ext->alg = alg; + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + memcpy(ext->rx_seq, seq, seq_len); + } + + if (iw_ioctl(ifname, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = -2; + printf("\n\rioctl[SIOCSIWENCODEEXT] set key fail"); + } + + if(ext != NULL) + free(ext); + + return ret; +} + +int wext_get_enc_ext(const char *ifname, __u16 *alg, __u8 *key_idx, __u8 *passphrase) +{ + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + ext = (struct iw_encode_ext *) malloc(sizeof(struct iw_encode_ext) + 16); + if (ext == NULL) + return -1; + else + memset(ext, 0, sizeof(struct iw_encode_ext) + 16); + + iwr.u.encoding.pointer = ext; + + if (iw_ioctl(ifname, SIOCGIWENCODEEXT, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWENCODEEXT] error"); + ret = -1; + } + else + { + *alg = ext->alg; + if(key_idx) + *key_idx = (__u8)iwr.u.encoding.flags; + if(passphrase) + memcpy(passphrase, ext->key, ext->key_len); + } + + if(ext != NULL) + free(ext); + + return ret; +} + +int wext_set_passphrase(const char *ifname, const __u8 *passphrase, __u16 passphrase_len) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.passphrase.pointer = (void *) passphrase; + iwr.u.passphrase.length = passphrase_len; + iwr.u.passphrase.flags = (passphrase_len != 0); + + if (iw_ioctl(ifname, SIOCSIWPRIVPASSPHRASE, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWESSID+0x1f] error"); + ret = -1; + } + + return ret; +} + +int wext_get_passphrase(const char *ifname, __u8 *passphrase) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.passphrase.pointer = (void *) passphrase; + + if (iw_ioctl(ifname, SIOCGIWPRIVPASSPHRASE, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWPRIVPASSPHRASE] error"); + ret = -1; + } + else { + ret = iwr.u.passphrase.length; + passphrase[ret] = '\0'; + } + + return ret; +} + +#if 0 +int wext_disconnect(const char *ifname) +{ + int ret = 0; + const __u8 null_bssid[ETH_ALEN] = {0, 0, 0, 0, 0, 1}; //set the last to 1 since driver will filter the mac with all 0x00 or 0xff + + if (wext_set_bssid(ifname, null_bssid) < 0){ + printf("\n\rWEXT: Failed to set bogus BSSID to disconnect"); + ret = -1; + } + return ret; +} + +int wext_set_mac_address(const char *ifname, char * mac) +{ + char buf[13+17+1]; + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 13+17, "write_mac %s", mac); + return wext_private_command(ifname, buf, 0); +} + +int wext_get_mac_address(const char *ifname, char * mac) +{ + int ret = 0; + char buf[32]; + + rtw_memset(buf, 0, sizeof(buf)); + rtw_memcpy(buf, "read_mac", 8); + ret = wext_private_command_with_retval(ifname, buf, buf, 32); + strcpy(mac, buf); + return ret; +} +#endif + +int wext_enable_powersave(const char *ifname) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("pm_set"); + para = pvPortMalloc(24); + + snprintf((char*)para, cmd_len, "pm_set"); + *(para+cmd_len) = 1; //ips=1; + *(para+cmd_len+1) = 1;//lps=1 + + iwr.u.essid.pointer = para; + iwr.u.essid.length = cmd_len + 2; + + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWPRIVAPESSID] error"); + ret = -1; + } + + vPortFree(para); + return ret; +} + +int wext_disable_powersave(const char *ifname) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("pm_set"); + para = pvPortMalloc(24); + + snprintf((char*)para, cmd_len, "pm_set"); + *(para+cmd_len) = 0; //ips=0; + *(para+cmd_len+1) = 0;//lps=0 + + iwr.u.essid.pointer = para; + iwr.u.essid.length = cmd_len + 2; + + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWPRIVAPESSID] error"); + ret = -1; + } + + vPortFree(para); + return ret; + +} + +#if 0 +int wext_get_txpower(const char *ifname, int *poweridx) +{ + int ret = 0; + char buf[11]; + + rtw_memset(buf, 0, sizeof(buf)); + rtw_memcpy(buf, "txpower", 11); + ret = wext_private_command_with_retval(ifname, buf, buf, 11); + sscanf(buf, "%d", poweridx); + + return ret; +} + +int wext_set_txpower(const char *ifname, int poweridx) +{ + int ret = 0; + char buf[24]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 24, "txpower patha=%d", poweridx); + ret = wext_private_command(ifname, buf, 0); + + return ret; +} + +int wext_get_associated_client_list(const char *ifname, void * client_list_buffer, uint16_t buffer_length) +{ + int ret = 0; + char buf[25]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 25, "get_client_list %x", client_list_buffer); + ret = wext_private_command(ifname, buf, 0); + + return ret; +} + +int wext_get_ap_info(const char *ifname, rtw_bss_info_t * ap_info, rtw_security_t* security) +{ + int ret = 0; + char buf[24]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 24, "get_ap_info %x", ap_info); + ret = wext_private_command(ifname, buf, 0); + + snprintf(buf, 24, "get_security"); + ret = wext_private_command_with_retval(ifname, buf, buf, 24); + sscanf(buf, "%d", security); + + return ret; +} +#endif + +int wext_set_mode(const char *ifname, int mode) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.mode = mode; + if (iw_ioctl(ifname, SIOCSIWMODE, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWMODE] error"); + ret = -1; + } + + return ret; +} + +int wext_get_mode(const char *ifname, int *mode) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + + if (iw_ioctl(ifname, SIOCGIWMODE, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWMODE] error"); + ret = -1; + } + else + *mode = iwr.u.mode; + + return ret; +} + +int wext_set_ap_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.essid.pointer = (void *) ssid; + iwr.u.essid.length = ssid_len; + iwr.u.essid.flags = (ssid_len != 0); + + if (iw_ioctl(ifname, SIOCSIWPRIVAPESSID, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWPRIVAPESSID] error"); + ret = -1; + } + + return ret; +} + +int wext_set_country(const char *ifname, char *country_code) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + + iwr.u.data.pointer = country_code; + if (iw_ioctl(ifname, SIOCSIWPRIVCOUNTRY, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWPRIVCOUNTRY] error"); + ret = -1; + } + return ret; +} + +int wext_get_rssi(const char *ifname, int *rssi) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + + if (iw_ioctl(ifname, SIOCGIWSENS, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWSENS] error"); + ret = -1; + } else { + *rssi = 0 - iwr.u.sens.value; + } + return ret; +} + +int wext_set_pscan_channel(const char *ifname, __u8 *ch, __u8 *pscan_config, __u8 length) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int i =0; + + memset(&iwr, 0, sizeof(iwr)); + //Format of para:function_name num_channel chan1... pscan_config1 ... + para = pvPortMalloc((length + length + 1) + 12);//size:num_chan + num_time + length + function_name + //Cmd + snprintf((char*)para, 12, "PartialScan"); + //length + *(para+12) = length; + for(i = 0; i < length; i++){ + *(para + 13 + i)= *(ch + i); + *((__u16*) (para + 13 + length + i))= *(pscan_config + i); + } + + iwr.u.data.pointer = para; + iwr.u.data.length = (length + length + 1) + 12; + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_set_pscan_channel():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + vPortFree(para); + return ret; +} +int wext_set_channel(const char *ifname, __u8 ch) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.freq.m = 0; + iwr.u.freq.e = 0; + iwr.u.freq.i = ch; + + if (iw_ioctl(ifname, SIOCSIWFREQ, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWFREQ] error"); + ret = -1; + } + + return ret; +} + +int wext_get_channel(const char *ifname, __u8 *ch) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + + if (iw_ioctl(ifname, SIOCGIWFREQ, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWFREQ] error"); + ret = -1; + } + else + *ch = iwr.u.freq.i; + + return ret; +} + +int wext_register_multicast_address(const char *ifname, rtw_mac_t *mac) +{ + int ret = 0; + char buf[32]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 32, "reg_multicast "MAC_FMT, MAC_ARG(mac->octet)); + ret = wext_private_command(ifname, buf, 0); + + return ret; +} + +int wext_unregister_multicast_address(const char *ifname, rtw_mac_t *mac) +{ + int ret = 0; + char buf[35]; + + rtw_memset(buf, 0, sizeof(buf)); + snprintf(buf, 35, "reg_multicast -d "MAC_FMT, MAC_ARG(mac->octet)); + ret = wext_private_command(ifname, buf, 0); + + return ret; +} + +int wext_set_scan(const char *ifname, char *buf, __u16 buf_len, __u16 flags) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); +#if 0 //for scan_with_ssid + if(buf) + memset(buf, 0, buf_len); +#endif + iwr.u.data.pointer = buf; + iwr.u.data.flags = flags; + iwr.u.data.length = buf_len; + if (iw_ioctl(ifname, SIOCSIWSCAN, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWSCAN] error"); + ret = -1; + } + return ret; +} + +int wext_get_scan(const char *ifname, char *buf, __u16 buf_len) +{ + struct iwreq iwr; + int ret = 0; + + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + if (iw_ioctl(ifname, SIOCGIWSCAN, &iwr) < 0) { + printf("\n\rioctl[SIOCGIWSCAN] error"); + ret = -1; + }else + ret = iwr.u.data.flags; + return ret; +} + +int wext_private_command_with_retval(const char *ifname, char *cmd, char *ret_buf, int ret_len) +{ + struct iwreq iwr; + int ret = 0, buf_size; + char *buf; + + buf_size = 128; + if(strlen(cmd) >= buf_size) + buf_size = strlen(cmd) + 1; // 1 : '\0' + buf = (char*)pvPortMalloc(buf_size); + if(!buf){ + printf("\n\rWEXT: Can't malloc memory"); + return -1; + } + memset(buf, 0, buf_size); + strcpy(buf, cmd); + memset(&iwr, 0, sizeof(iwr)); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_size; + iwr.u.data.flags = 0; + + if ((ret = iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr)) < 0) { + printf("\n\rioctl[SIOCDEVPRIVATE] error. ret=%d\n", ret); + } + if(ret_buf){ + if(ret_len > iwr.u.data.length) + ret_len = iwr.u.data.length; + rtw_memcpy(ret_buf, (char *) iwr.u.data.pointer, ret_len); + } + vPortFree(buf); + return ret; +} + +int wext_private_command(const char *ifname, char *cmd, int show_msg) +{ + struct iwreq iwr; + int ret = 0, buf_size; + char *buf; + + buf_size = 2048; // 2048 for efuse_get realmap + if(strlen(cmd) >= buf_size) + buf_size = strlen(cmd) + 1; // 1 : '\0' + buf = (char*)pvPortMalloc(buf_size); + if(!buf){ + printf("\n\rWEXT: Can't malloc memory"); + return -1; + } + memset(buf, 0, buf_size); + strcpy(buf, cmd); + memset(&iwr, 0, sizeof(iwr)); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_size; + iwr.u.data.flags = 0; + + if ((ret = iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr)) < 0) { + printf("\n\rioctl[SIOCDEVPRIVATE] error. ret=%d\n", ret); + } + if (show_msg && iwr.u.data.length) { + if(iwr.u.data.length > buf_size) + printf("\n\rWEXT: Malloc memory is not enough"); + printf("\n\rPrivate Message: %s", (char *) iwr.u.data.pointer); + } + vPortFree(buf); + return ret; +} + +void wext_wlan_indicate(unsigned int cmd, union iwreq_data *wrqu, char *extra) +{ + unsigned char null_mac[6] = {0}; + + switch(cmd) + { + case SIOCGIWAP: + if(wrqu->ap_addr.sa_family == ARPHRD_ETHER) + { + if(!memcmp(wrqu->ap_addr.sa_data, null_mac, sizeof(null_mac))) + wifi_indication(WIFI_EVENT_DISCONNECT, NULL, 0, 0); + else + wifi_indication(WIFI_EVENT_CONNECT, wrqu->ap_addr.sa_data, sizeof(null_mac), 0); + } + break; + + case IWEVCUSTOM: + if(extra) + { + if(!memcmp(IW_EXT_STR_FOURWAY_DONE, extra, strlen(IW_EXT_STR_FOURWAY_DONE))) + wifi_indication(WIFI_EVENT_FOURWAY_HANDSHAKE_DONE, extra, strlen(IW_EXT_STR_FOURWAY_DONE), 0); + else if(!memcmp(IW_EXT_STR_RECONNECTION_FAIL, extra, strlen(IW_EXT_STR_RECONNECTION_FAIL))) + wifi_indication(WIFI_EVENT_RECONNECTION_FAIL, extra, strlen(IW_EXT_STR_RECONNECTION_FAIL), 0); + else if(!memcmp(IW_EVT_STR_NO_NETWORK, extra, strlen(IW_EVT_STR_NO_NETWORK))) + wifi_indication(WIFI_EVENT_NO_NETWORK, extra, strlen(IW_EVT_STR_NO_NETWORK), 0); +#ifdef CONFIG_P2P_NEW + else if(!memcmp(IW_EVT_STR_STA_ASSOC, extra, strlen(IW_EVT_STR_STA_ASSOC))) + wifi_indication(WIFI_EVENT_STA_ASSOC, wrqu->data.pointer, wrqu->data.length, 0); + else if(!memcmp(IW_EVT_STR_STA_DISASSOC, extra, strlen(IW_EVT_STR_STA_DISASSOC))) + wifi_indication(WIFI_EVENT_STA_DISASSOC, wrqu->addr.sa_data, sizeof(null_mac), 0); + else if(!memcmp(IW_EVT_STR_SEND_ACTION_DONE, extra, strlen(IW_EVT_STR_SEND_ACTION_DONE))) + wifi_indication(WIFI_EVENT_SEND_ACTION_DONE, NULL, 0, wrqu->data.flags); +#endif + } + break; + case SIOCGIWSCAN: + if(wrqu->data.pointer == NULL) + wifi_indication(WIFI_EVENT_SCAN_DONE, wrqu->data.pointer, 0, 0); + else + wifi_indication(WIFI_EVENT_SCAN_RESULT_REPORT, wrqu->data.pointer, 0, 0); + break; +#ifdef CONFIG_P2P_NEW + case IWEVMGNTRECV: + wifi_indication(WIFI_EVENT_RX_MGNT, wrqu->data.pointer, wrqu->data.length, wrqu->data.flags); + break; +#endif + default: + break; + + } + +} + +#ifdef CONFIG_P2P_NEW +int wext_send_mgnt(const char *ifname, char *buf, __u16 buf_len, __u16 flags) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + iwr.u.data.flags = flags; + if (iw_ioctl(ifname, SIOCSIWMGNTSEND, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWMGNTSEND] error"); + ret = -1; + } + return ret; +} +#endif + +int wext_set_gen_ie(const char *ifname, char *buf, __u16 buf_len, __u16 flags) +{ + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + iwr.u.data.flags = flags; + if (iw_ioctl(ifname, SIOCSIWGENIE, &iwr) < 0) { + printf("\n\rioctl[SIOCSIWGENIE] error"); + ret = -1; + } + return ret; +} + +int wext_set_autoreconnect(const char *ifname, __u8 mode) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("SetAutoRecnt"); + para = pvPortMalloc((1) + cmd_len);//size:para_len+cmd_len + //Cmd + snprintf((char*)para, cmd_len, "SetAutoRecnt"); + //length + *(para+cmd_len) = mode; //para + + iwr.u.data.pointer = para; + iwr.u.data.length = (1) + cmd_len; + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_set_autoreconnect():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + vPortFree(para); + return ret; +} + +int wext_get_autoreconnect(const char *ifname, __u8 *mode) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("GetAutoRecnt"); + //para = pvPortMalloc((1) + cmd_len);//size:para_len+cmd_len + para = pvPortMalloc(cmd_len);//size:para_len+cmd_len + //Cmd + snprintf((char*)para, cmd_len, "GetAutoRecnt"); + //length + //*(para+cmd_len) = *mode; //para + + iwr.u.data.pointer = para; + //iwr.u.data.length = (1) + cmd_len; + iwr.u.data.length = cmd_len; + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_set_autoreconnect():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + *mode = *(__u8 *)(iwr.u.data.pointer); + vPortFree(para); + return ret; +} + +#ifdef CONFIG_CUSTOM_IE +int wext_add_custom_ie(const char *ifname, void *cus_ie, int ie_num) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + if(ie_num <= 0 || !cus_ie){ + printf("\n\rwext_add_custom_ie():wrong parameter"); + ret = -1; + return ret; + } + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("SetCusIE"); + para = pvPortMalloc((4)* 2 + cmd_len);//size:addr len+cmd_len + //Cmd + snprintf(para, cmd_len, "SetCusIE"); + //addr length + *(__u32 *)(para + cmd_len) = (__u32)cus_ie; //ie addr + //ie_num + *(__u32 *)(para + cmd_len + 4) = ie_num; //num of ie + + iwr.u.data.pointer = para; + iwr.u.data.length = (4)* 2 + cmd_len;// 2 input + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_add_custom_ie():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + vPortFree(para); + + return ret; +} + +int wext_update_custom_ie(const char *ifname, void * cus_ie, int ie_index) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + if(ie_index <= 0 || !cus_ie){ + printf("\n\rwext_update_custom_ie():wrong parameter"); + ret = -1; + return ret; + } + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("UpdateIE"); + para = pvPortMalloc((4)* 2 + cmd_len);//size:addr len+cmd_len + //Cmd + snprintf(para, cmd_len, "UpdateIE"); + //addr length + *(__u32 *)(para + cmd_len) = (__u32)cus_ie; //ie addr + //ie_index + *(__u32 *)(para + cmd_len + 4) = ie_index; //num of ie + + iwr.u.data.pointer = para; + iwr.u.data.length = (4)* 2 + cmd_len;// 2 input + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_update_custom_ie():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + vPortFree(para); + + return ret; + +} + +int wext_del_custom_ie(const char *ifname) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("DelIE"); + para = pvPortMalloc(cmd_len);//size:addr len+cmd_len + //Cmd + snprintf(para, cmd_len, "DelIE"); + + iwr.u.data.pointer = para; + iwr.u.data.length = cmd_len; + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_del_custom_ie():ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + vPortFree(para); + + return ret; + + +} + +#endif + +#ifdef CONFIG_AP_MODE +int wext_enable_forwarding(const char *ifname) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("forwarding_set"); + para = pvPortMalloc(cmd_len + 1); + + // forwarding_set 1 + snprintf((char *) para, cmd_len, "forwarding_set"); + *(para + cmd_len) = '1'; + + iwr.u.essid.pointer = para; + iwr.u.essid.length = cmd_len + 1; + + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_enable_forwarding(): ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + + vPortFree(para); + return ret; +} + +int wext_disable_forwarding(const char *ifname) +{ + struct iwreq iwr; + int ret = 0; + __u8 *para = NULL; + int cmd_len = 0; + + memset(&iwr, 0, sizeof(iwr)); + cmd_len = sizeof("forwarding_set"); + para = pvPortMalloc(cmd_len + 1); + + // forwarding_set 0 + snprintf((char *) para, cmd_len, "forwarding_set"); + *(para + cmd_len) = '0'; + + iwr.u.essid.pointer = para; + iwr.u.essid.length = cmd_len + 1; + + if (iw_ioctl(ifname, SIOCDEVPRIVATE, &iwr) < 0) { + printf("\n\rwext_disable_forwarding(): ioctl[SIOCDEVPRIVATE] error"); + ret = -1; + } + + vPortFree(para); + return ret; + +} +#endif diff --git a/component/common/api/wifi/wifi_util.h b/component/common/api/wifi/wifi_util.h new file mode 100644 index 0000000..27645ba --- /dev/null +++ b/component/common/api/wifi/wifi_util.h @@ -0,0 +1,64 @@ +#ifndef _UTIL_H +#define _UTIL_H + +#include +#include +#include +#include "wifi_structures.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int wext_get_ssid(const char *ifname, __u8 *ssid); +int wext_set_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len); +int wext_set_auth_param(const char *ifname, __u16 idx, __u32 value); +int wext_set_key_ext(const char *ifname, __u16 alg, const __u8 *addr, int key_idx, int set_tx, const __u8 *seq, __u16 seq_len, __u8 *key, __u16 key_len); +int wext_get_enc_ext(const char *ifname, __u16 *alg, __u8 *key_idx, __u8 *passphrase); +int wext_set_passphrase(const char *ifname, const __u8 *passphrase, __u16 passphrase_len); +int wext_get_passphrase(const char *ifname, __u8 *passphrase); +int wext_disconnect(const char *ifname); +int wext_set_mode(const char *ifname, int mode); +int wext_get_mode(const char *ifname, int *mode); +int wext_set_ap_ssid(const char *ifname, const __u8 *ssid, __u16 ssid_len); +int wext_set_country(const char *ifname, char *country_code); +int wext_get_rssi(const char *ifname, int *rssi); +int wext_set_channel(const char *ifname, __u8 ch); +int wext_get_channel(const char *ifname, __u8 *ch); +int wext_register_multicast_address(const char *ifname, rtw_mac_t *mac); +int wext_unregister_multicast_address(const char *ifname, rtw_mac_t *mac); +int wext_set_scan(const char *ifname, char *buf, __u16 buf_len, __u16 flags); +int wext_get_scan(const char *ifname, char *buf, __u16 buf_len); +int wext_set_mac_address(const char *ifname, char * mac); +int wext_get_mac_address(const char *ifname, char * mac); +int wext_enable_powersave(const char *ifname); +int wext_disable_powersave(const char *ifname); +int wext_get_txpower(const char *ifname, int *poweridx); +int wext_set_txpower(const char *ifname, int poweridx); +int wext_get_associated_client_list(const char *ifname, void * client_list_buffer, __u16 buffer_length); +int wext_get_ap_info(const char *ifname, rtw_bss_info_t * ap_info, rtw_security_t* security); +int wext_mp_command(const char *ifname, char *cmd, int show_msg); +int wext_private_command(const char *ifname, char *cmd, int show_msg); +int wext_private_command_with_retval(const char *ifname, char *cmd, char *ret_buf, int ret_len); +void wext_wlan_indicate(unsigned int cmd, union iwreq_data *wrqu, char *extra); +int wext_set_pscan_channel(const char *ifname, __u8 *ch, __u8 *pscan_config, __u8 length); +int wext_set_autoreconnect(const char *ifname, __u8 mode); +int wext_get_autoreconnect(const char *ifname, __u8 *mode); +#ifdef CONFIG_CUSTOM_IE +int wext_add_custom_ie(const char *ifname, void * cus_ie, int ie_num); +int wext_update_custom_ie(const char *ifname, void * cus_ie, int ie_index); +int wext_del_custom_ie(const char *ifname); +#endif + +#define wext_handshake_done rltk_wlan_handshake_done + +#ifdef CONFIG_P2P_NEW +int wext_send_mgnt(const char *ifname, char *buf, __u16 buf_len, __u16 flags); +#endif +int wext_set_gen_ie(const char *ifname, char *buf, __u16 buf_len, __u16 flags); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_H */ diff --git a/component/common/api/wifi_interactive_ext.h b/component/common/api/wifi_interactive_ext.h new file mode 100644 index 0000000..774b482 --- /dev/null +++ b/component/common/api/wifi_interactive_ext.h @@ -0,0 +1,41 @@ +#define CONFIG_EXTERN_TEST 0 +#define CONFIG_EXTERN_HW 0 +#define CONFIG_EXTERN_CLOUD 0 +#define CONFIG_TTCP 0 + +/* External Function */ +#if CONFIG_EXTERN_TEST +extern void cmd_tcpecho(int argc, char **argv); +#endif +#if CONFIG_EXTERN_HW +extern void cmd_led(int argc, char **argv); +extern void cmd_tmp75(int argc, char **argv); +#endif +#if CONFIG_EXTERN_CLOUD +extern void cmd_cloud(int argc, char **argv); +extern void cmd_reboot(int argc, char **argv); +extern void cmd_config(int argc, char **argv); +#endif + +#if CONFIG_TTCP +extern void cmd_ttcp(int argc, char **argv); +#endif + +static const cmd_entry ext_cmd_table[] = { +#if CONFIG_EXTERN_TEST + {"tcpecho", cmd_tcpecho}, +#endif +#if CONFIG_EXTERN_HW + {"led", cmd_led}, + {"tmp75", cmd_tmp75}, +#endif +#if CONFIG_EXTERN_CLOUD + {"cloud", cmd_cloud}, + {"reboot", cmd_reboot}, + {"config", cmd_config}, +#endif +#if CONFIG_TTCP + {"ttcp", cmd_ttcp}, +#endif + {"", NULL} +}; diff --git a/component/common/api/wifi_interactive_mode.c b/component/common/api/wifi_interactive_mode.c new file mode 100644 index 0000000..adc8941 --- /dev/null +++ b/component/common/api/wifi_interactive_mode.c @@ -0,0 +1,1237 @@ + +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "main.h" +#include +#include "tcpip.h" +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#include "platform_opts.h" +#endif + +#ifndef CONFIG_INTERACTIVE_EXT +#define CONFIG_INTERACTIVE_EXT 0 +#endif +#ifndef CONFIG_SSL_CLIENT +#if defined(CONFIG_PLATFORM_8711B) +#define CONFIG_SSL_CLIENT 0 +#else +#define CONFIG_SSL_CLIENT 1 +#endif +#endif + +#ifndef CONFIG_GOOGLENEST +#define CONFIG_GOOGLENEST 0 +#endif +#if CONFIG_LWIP_LAYER +#ifndef CONFIG_WEBSERVER +#define CONFIG_WEBSERVER 0 +#endif +#endif +#ifndef CONFIG_OTA_UPDATE +#define CONFIG_OTA_UPDATE 0 +#endif +#ifndef CONFIG_BSD_TCP +#define CONFIG_BSD_TCP 0 +#endif +#define CONFIG_JD_SMART 0 +#ifndef CONFIG_ENABLE_P2P +#define CONFIG_ENABLE_P2P 0 +#endif +#define SCAN_WITH_SSID 0 + +#ifdef CONFIG_WPS +#define STACKSIZE 1280 +#else +#define STACKSIZE 1024 +#endif + +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif +#ifndef WLAN1_NAME + #define WLAN1_NAME "wlan1" +#endif +/* Give default value if not defined */ +#ifndef NET_IF_NUM +#ifdef CONFIG_CONCURRENT_MODE +#define NET_IF_NUM 2 +#else +#define NET_IF_NUM 1 +#endif +#endif + +/*Static IP ADDRESS*/ +#ifndef IP_ADDR0 +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 1 +#define IP_ADDR3 80 +#endif + +/*NETMASK*/ +#ifndef NETMASK_ADDR0 +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef GW_ADDR0 +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 1 +#define GW_ADDR3 1 +#endif + +/*Static IP ADDRESS*/ +#ifndef AP_IP_ADDR0 +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 +#endif + +/*NETMASK*/ +#ifndef AP_NETMASK_ADDR0 +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 +#endif + +/*Gateway Address*/ +#ifndef AP_GW_ADDR0 +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 +#endif + +static void cmd_help(int argc, char **argv); +#if CONFIG_SSL_CLIENT +extern void cmd_ssl_client(int argc, char **argv); +#endif + +#if CONFIG_GOOGLENEST +extern void cmd_googlenest(int argc, char **argv); +#endif +#if CONFIG_JD_SMART +extern void cmd_jd_smart(int argc, char **argv); +#endif +#if CONFIG_WLAN +static void cmd_wifi_on(int argc, char **argv); +static void cmd_wifi_off(int argc, char **argv); +static void cmd_wifi_disconnect(int argc, char **argv); +extern void cmd_promisc(int argc, char **argv); +extern void cmd_simple_config(int argc, char **argv); + +#if CONFIG_OTA_UPDATE +extern void cmd_update(int argc, char **argv); +#endif +#if CONFIG_BSD_TCP +extern void cmd_tcp(int argc, char **argv); +extern void cmd_udp(int argc, char **argv); +#endif +#if CONFIG_WEBSERVER +extern void start_web_server(void); +extern void stop_web_server(void); +#endif +extern void cmd_app(int argc, char **argv); + +#if CONFIG_EXAMPLE_UART_ADAPTER +extern void cmd_uart_adapter(int argc, char **argv); +#endif + +#ifdef CONFIG_WPS +#if CONFIG_ENABLE_WPS +extern void cmd_wps(int argc, char **argv); +#endif +#ifdef CONFIG_WPS_AP +extern void cmd_ap_wps(int argc, char **argv); +extern int wpas_wps_dev_config(u8 *dev_addr, u8 bregistrar); +#endif //CONFIG_WPS_AP +#endif //CONFIG_WPS +#if CONFIG_ENABLE_P2P +extern void cmd_wifi_p2p_start(int argc, char **argv); +extern void cmd_wifi_p2p_stop(int argc, char **argv); +extern void cmd_p2p_listen(int argc, char **argv); +extern void cmd_p2p_find(int argc, char **argv); +extern void cmd_p2p_peers(int argc, char **argv); +extern void cmd_p2p_info(int argc, char **argv); +extern void cmd_p2p_disconnect(int argc, char **argv); +extern void cmd_p2p_connect(int argc, char **argv); +#endif //CONFIG_ENABLE_P2P +#if defined(CONFIG_RTL8195A) || defined(CONFIG_RTL8711B) +extern u32 CmdDumpWord(IN u16 argc, IN u8 *argv[]); +extern u32 CmdWriteWord(IN u16 argc, IN u8 *argv[]); +#endif +#if CONFIG_LWIP_LAYER +extern struct netif xnetif[NET_IF_NUM]; +#endif +#ifdef CONFIG_CONCURRENT_MODE +static void cmd_wifi_sta_and_ap(int argc, char **argv) +{ + int timeout = 20;//, mode; +#if CONFIG_LWIP_LAYER + struct netif * pnetiff = (struct netif *)&xnetif[1]; +#endif + int channel; + + if((argc != 3) && (argc != 4)) { + printf("\n\rUsage: wifi_ap SSID CHANNEL [PASSWORD]"); + return; + } + + if(atoi((const char *)argv[2]) > 14){ + printf("\n\r bad channel!Usage: wifi_ap SSID CHANNEL [PASSWORD]"); + return; + } +#if CONFIG_LWIP_LAYER + dhcps_deinit(); +#endif + +#if 0 + //Check mode + wext_get_mode(WLAN0_NAME, &mode); + + switch(mode) { + case IW_MODE_MASTER: //In AP mode + cmd_wifi_off(0, NULL); + cmd_wifi_on(0, NULL); + break; + case IW_MODE_INFRA: //In STA mode + if(wext_get_ssid(WLAN0_NAME, ssid) > 0) + cmd_wifi_disconnect(0, NULL); + } +#endif + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_STA_AP) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } + + printf("\n\rStarting AP ..."); + channel = atoi((const char *)argv[2]); + if(channel > 13){ + printf("\n\rChannel is from 1 to 13. Set channel 1 as default!\n"); + channel = 1; + } + + if(argc == 4) { + if(wifi_start_ap(argv[1], + RTW_SECURITY_WPA2_AES_PSK, + argv[3], + strlen((const char *)argv[1]), + strlen((const char *)argv[3]), + channel + ) != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } + } + else { + if(wifi_start_ap(argv[1], + RTW_SECURITY_OPEN, + NULL, + strlen((const char *)argv[1]), + 0, + channel + ) != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } + } + + while(1) { + char essid[33]; + + if(wext_get_ssid(WLAN1_NAME, (unsigned char *) essid) > 0) { + if(strcmp((const char *) essid, (const char *)argv[1]) == 0) { + printf("\n\r%s started", argv[1]); + break; + } + } + + if(timeout == 0) { + printf("\n\rERROR: Start AP timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } +#if CONFIG_LWIP_LAYER + LwIP_UseStaticIP(&xnetif[1]); +#ifdef CONFIG_DONT_CARE_TP + pnetiff->flags |= NETIF_FLAG_IPSWITCH; +#endif + dhcps_init(pnetiff); +#endif +} +#endif + +static void cmd_wifi_ap(int argc, char **argv) +{ + int timeout = 20; +#if CONFIG_LWIP_LAYER + struct ip_addr ipaddr; + struct ip_addr netmask; + struct ip_addr gw; + struct netif * pnetif = &xnetif[0]; +#endif + int channel; + + if((argc != 3) && (argc != 4)) { + printf("\n\rUsage: wifi_ap SSID CHANNEL [PASSWORD]"); + return; + } +#if CONFIG_LWIP_LAYER + dhcps_deinit(); + IP4_ADDR(&ipaddr, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3); + IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); + netif_set_addr(pnetif, &ipaddr, &netmask,&gw); +#ifdef CONFIG_DONT_CARE_TP + pnetif->flags |= NETIF_FLAG_IPSWITCH; +#endif +#endif +#if 0 + //Check mode + wext_get_mode(WLAN0_NAME, &mode); + + switch(mode) { + case IW_MODE_MASTER: //In AP mode + wifi_off(); + wifi_on(1); + break; + case IW_MODE_INFRA: //In STA mode + if(wext_get_ssid(WLAN0_NAME, ssid) > 0) + cmd_wifi_disconnect(0, NULL); + } +#else + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_AP) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } +#endif + + printf("\n\rStarting AP ..."); + channel = atoi((const char *)argv[2]); + if(channel > 13){ + printf("\n\rChannel is from 1 to 13. Set channel 1 as default!\n"); + channel = 1; + } +#ifdef CONFIG_WPS_AP + wpas_wps_dev_config(pnetif->hwaddr, 1); +#endif + + if(argc == 4) { + if(wifi_start_ap(argv[1], + RTW_SECURITY_WPA2_AES_PSK, + argv[3], + strlen((const char *)argv[1]), + strlen((const char *)argv[3]), + channel + ) != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } + } + else { + if(wifi_start_ap(argv[1], + RTW_SECURITY_OPEN, + NULL, + strlen((const char *)argv[1]), + 0, + channel + ) != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } + } + + while(1) { + char essid[33]; + + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) > 0) { + if(strcmp((const char *) essid, (const char *)argv[1]) == 0) { + printf("\n\r%s started\n", argv[1]); + break; + } + } + + if(timeout == 0) { + printf("\n\rERROR: Start AP timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } +#if CONFIG_LWIP_LAYER + //LwIP_UseStaticIP(pnetif); + dhcps_init(pnetif); +#endif +} + +static void cmd_wifi_connect(int argc, char **argv) +{ + int ret = RTW_ERROR; + unsigned long tick1 = xTaskGetTickCount(); + unsigned long tick2, tick3; + int mode; + char *ssid; + rtw_security_t security_type; + char *password; + int ssid_len; + int password_len; + int key_id; + void *semaphore; + + if((argc != 2) && (argc != 3) && (argc != 4)) { + printf("\n\rUsage: wifi_connect SSID [WPA PASSWORD / (5 or 13) ASCII WEP KEY] [WEP KEY ID 0/1/2/3]"); + return; + } + + //Check if in AP mode + wext_get_mode(WLAN0_NAME, &mode); + + if(mode == IW_MODE_MASTER) { +#if CONFIG_LWIP_LAYER + dhcps_deinit(); +#endif + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_STA) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } + } + + ssid = argv[1]; + if(argc == 2){ + security_type = RTW_SECURITY_OPEN; + password = NULL; + ssid_len = strlen((const char *)argv[1]); + password_len = 0; + key_id = 0; + semaphore = NULL; + }else if(argc ==3){ + security_type = RTW_SECURITY_WPA2_AES_PSK; + password = argv[2]; + ssid_len = strlen((const char *)argv[1]); + password_len = strlen((const char *)argv[2]); + key_id = 0; + semaphore = NULL; + }else{ + security_type = RTW_SECURITY_WEP_PSK; + password = argv[2]; + ssid_len = strlen((const char *)argv[1]); + password_len = strlen((const char *)argv[2]); + key_id = atoi(argv[3]); + if(( password_len != 5) && (password_len != 13)) { + printf("\n\rWrong WEP key length. Must be 5 or 13 ASCII characters."); + return; + } + if((key_id < 0) || (key_id > 3)) { + printf("\n\rWrong WEP key id. Must be one of 0,1,2, or 3."); + return; + } + semaphore = NULL; + } + + ret = wifi_connect(ssid, + security_type, + password, + ssid_len, + password_len, + key_id, + semaphore); + + tick2 = xTaskGetTickCount(); + printf("\r\nConnected after %dms.\n", (tick2-tick1)); + if(ret != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } else { +#if CONFIG_LWIP_LAYER + /* Start DHCPClient */ + LwIP_DHCP(0, DHCP_START); +#endif + } + tick3 = xTaskGetTickCount(); + printf("\r\n\nGot IP after %dms.\n", (tick3-tick1)); +} + +static void cmd_wifi_connect_bssid(int argc, char **argv) +{ + int ret = RTW_ERROR; + unsigned long tick1 = xTaskGetTickCount(); + unsigned long tick2, tick3; + int mode; + unsigned char bssid[ETH_ALEN]; + char *ssid = NULL; + rtw_security_t security_type; + char *password; + int bssid_len; + int ssid_len = 0; + int password_len; + int key_id; + void *semaphore; + u32 mac[ETH_ALEN]; + u32 i; + u32 index = 0; + + if((argc != 3) && (argc != 4) && (argc != 5) && (argc != 6)) { + printf("\n\rUsage: wifi_connect_bssid 0/1 [SSID] BSSID / xx:xx:xx:xx:xx:xx [WPA PASSWORD / (5 or 13) ASCII WEP KEY] [WEP KEY ID 0/1/2/3]"); + return; + } + + //Check if in AP mode + wext_get_mode(WLAN0_NAME, &mode); + + if(mode == IW_MODE_MASTER) { +#if CONFIG_LWIP_LAYER + dhcps_deinit(); +#endif + wifi_off(); + vTaskDelay(20); + if (wifi_on(RTW_MODE_STA) < 0){ + printf("\n\rERROR: Wifi on failed!"); + return; + } + } + //check ssid + if(memcmp(argv[1], "0", 1)){ + index = 1; + ssid_len = strlen((const char *)argv[2]); + if((ssid_len <= 0) || (ssid_len > 32)) { + printf("\n\rWrong ssid. Length must be less than 32."); + return; + } + ssid = argv[2]; + } + sscanf(argv[2 + index], MAC_FMT, mac, mac + 1, mac + 2, mac + 3, mac + 4, mac + 5); + for(i=0; i 3)) { + printf("\n\rWrong WEP key id. Must be one of 0,1,2, or 3."); + return; + } + semaphore = NULL; + } + + ret = wifi_connect_bssid(bssid, + ssid, + security_type, + password, + bssid_len, + ssid_len, + password_len, + key_id, + semaphore); + + tick2 = xTaskGetTickCount(); + printf("\r\nConnected after %dms.\n", (tick2-tick1)); + if(ret != RTW_SUCCESS) { + printf("\n\rERROR: Operation failed!"); + return; + } else { +#if CONFIG_LWIP_LAYER + /* Start DHCPClient */ + LwIP_DHCP(0, DHCP_START); +#endif + } + tick3 = xTaskGetTickCount(); + printf("\r\n\nGot IP after %dms.\n", (tick3-tick1)); +} + +static void cmd_wifi_disconnect(int argc, char **argv) +{ + int timeout = 20; + char essid[33]; + + printf("\n\rDeassociating AP ..."); + + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) < 0) { + printf("\n\rWIFI disconnected"); + return; + } + + if(wifi_disconnect() < 0) { + printf("\n\rERROR: Operation failed!"); + return; + } + + while(1) { + if(wext_get_ssid(WLAN0_NAME, (unsigned char *) essid) < 0) { + printf("\n\rWIFI disconnected"); + break; + } + + if(timeout == 0) { + printf("\n\rERROR: Deassoc timeout!"); + break; + } + + vTaskDelay(1 * configTICK_RATE_HZ); + timeout --; + } +} + +static void cmd_wifi_info(int argc, char **argv) +{ + int i = 0; +#if CONFIG_LWIP_LAYER + u8 *mac = LwIP_GetMAC(&xnetif[0]); + u8 *ip = LwIP_GetIP(&xnetif[0]); + u8 *gw = LwIP_GetGW(&xnetif[0]); +#endif + u8 *ifname[2] = {WLAN0_NAME,WLAN1_NAME}; +#ifdef CONFIG_MEM_MONITOR + extern int min_free_heap_size; +#endif + + rtw_wifi_setting_t setting; + for(i=0;i %02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) ; + printf("\n\r\tIP => %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + printf("\n\r\tGW => %d.%d.%d.%d\n\r", gw[0], gw[1], gw[2], gw[3]); +#endif + if(setting.mode == RTW_MODE_AP || i == 1) + { + int client_number; + struct { + int count; + rtw_mac_t mac_list[3]; + } client_info; + + client_info.count = 3; + wifi_get_associated_client_list(&client_info, sizeof(client_info)); + + printf("\n\rAssociated Client List:"); + printf("\n\r=============================="); + + if(client_info.count == 0) + printf("\n\rClient Num: 0\n\r"); + else + { + printf("\n\rClient Num: %d", client_info.count); + for( client_number=0; client_number < client_info.count; client_number++ ) + { + printf("\n\rClient [%d]:", client_number); + printf("\n\r\tMAC => "MAC_FMT"", + MAC_ARG(client_info.mac_list[client_number].octet)); + } + printf("\n\r"); + } + } + + { + int error = wifi_get_last_error(); + printf("\n\rLast Link Error"); + printf("\n\r=============================="); + switch(error) + { + case RTW_NO_ERROR: + printf("\n\r\tNo Error"); + break; + case RTW_NONE_NETWORK: + printf("\n\r\tTarget AP Not Found"); + break; + case RTW_CONNECT_FAIL: + printf("\n\r\tAssociation Failed"); + break; + case RTW_WRONG_PASSWORD: + printf("\n\r\tWrong Password"); + break; + case RTW_DHCP_FAIL: + printf("\n\r\tDHCP Failed"); + break; + default: + printf("\n\r\tUnknown Error(%d)", error); + } + printf("\n\r"); + } + } + } + +#if defined(configUSE_TRACE_FACILITY) && (configUSE_TRACE_FACILITY == 1) + { + signed char pcWriteBuffer[1024]; + vTaskList((char*)pcWriteBuffer); + printf("\n\rTask List: \n%s", pcWriteBuffer); + } +#endif +#ifdef CONFIG_MEM_MONITOR + printf("\n\rMemory Usage"); + printf("\n\r=============================="); + printf("\r\nMin Free Heap Size: %d", min_free_heap_size); + printf("\r\nCur Free Heap Size: %d\n", xPortGetFreeHeapSize()); +#endif +} + +static void cmd_wifi_on(int argc, char **argv) +{ + if(wifi_on(RTW_MODE_STA)<0){ + printf("\n\rERROR: Wifi on failed!\n"); + } +} + +static void cmd_wifi_off(int argc, char **argv) +{ +#if CONFIG_WEBSERVER + stop_web_server(); +#endif +#if CONFIG_ENABLE_P2P + cmd_wifi_p2p_stop(0, NULL); +#else + wifi_off(); +#endif +} + +static void print_scan_result( rtw_scan_result_t* record ) +{ + RTW_API_INFO( ( "%s\t ", ( record->bss_type == RTW_BSS_TYPE_ADHOC ) ? "Adhoc" : "Infra" ) ); + RTW_API_INFO( ( MAC_FMT, MAC_ARG(record->BSSID.octet) ) ); + RTW_API_INFO( ( " %d\t ", record->signal_strength ) ); + RTW_API_INFO( ( " %d\t ", record->channel ) ); + RTW_API_INFO( ( " %d\t ", record->wps_type ) ); + RTW_API_INFO( ( "%s\t\t ", ( record->security == RTW_SECURITY_OPEN ) ? "Open" : + ( record->security == RTW_SECURITY_WEP_PSK ) ? "WEP" : + ( record->security == RTW_SECURITY_WPA_TKIP_PSK ) ? "WPA TKIP" : + ( record->security == RTW_SECURITY_WPA_AES_PSK ) ? "WPA AES" : + ( record->security == RTW_SECURITY_WPA2_AES_PSK ) ? "WPA2 AES" : + ( record->security == RTW_SECURITY_WPA2_TKIP_PSK ) ? "WPA2 TKIP" : + ( record->security == RTW_SECURITY_WPA2_MIXED_PSK ) ? "WPA2 Mixed" : + "Unknown" ) ); + + RTW_API_INFO( ( " %s ", record->SSID.val ) ); + RTW_API_INFO( ( "\r\n" ) ); +} + +static rtw_result_t app_scan_result_handler( rtw_scan_handler_result_t* malloced_scan_result ) +{ + static int ApNum = 0; + + if (malloced_scan_result->scan_complete != RTW_TRUE) { + rtw_scan_result_t* record = &malloced_scan_result->ap_details; + record->SSID.val[record->SSID.len] = 0; /* Ensure the SSID is null terminated */ + + RTW_API_INFO( ( "%d\t ", ++ApNum ) ); + + print_scan_result(record); + } else{ + ApNum = 0; + } + return RTW_SUCCESS; +} +#if SCAN_WITH_SSID +static void cmd_wifi_scan_with_ssid(int argc, char **argv) +{ + + u8 *channel_list = NULL; + char *ssid = NULL; + int ssid_len; + //Fully scan + int scan_buf_len = 500; + if(argc == 3 && argv[1] && argv[2]){ + ssid = argv[1]; + ssid_len = strlen((const char *)argv[1]); + scan_buf_len = atoi(argv[2]); + }else if(argc > 3){ + int i = 0; + int num_channel = atoi(argv[2]); + ssid = argv[1]; + ssid_len = strlen((const char *)argv[1]); + channel_list = (u8*)pvPortMalloc(num_channel); + if(!channel_list){ + printf("\n\r ERROR: Can't malloc memory for channel list"); + goto exit; + } + //parse command channel list + for(i = 3; i <= argc -1 ; i++){ + *(channel_list + i - 3) = (u8)atoi(argv[i]); + } + if(wifi_set_pscan_chan(channel_list,num_channel) < 0){ + printf("\n\rERROR: wifi set partial scan channel fail"); + goto exit; + } + }else{ + printf("\n\r For Scan all channel Usage: wifi_scan_with_ssid ssid BUFFER_LENGTH"); + printf("\n\r For Scan partial channel Usage: wifi_scan_with_ssid ssid num_channels channel_num1 ..."); + return; + } + + if(wifi_scan_networks_with_ssid(NULL, &scan_buf_len, ssid, ssid_len) != RTW_SUCCESS){ + printf("\n\rERROR: wifi scan failed"); + goto exit; + } + +exit: + if(argc > 2 && channel_list) + vPortFree(channel_list); + +} +#endif +static void cmd_wifi_scan(int argc, char **argv) +{ + + u8 *channel_list = NULL; + u8 *pscan_config = NULL; + + if(argc > 2){ + int i = 0; + int num_channel = atoi(argv[1]); + + channel_list = (u8*)pvPortMalloc(num_channel); + if(!channel_list){ + printf("\n\r ERROR: Can't malloc memory for channel list"); + goto exit; + } + pscan_config = (u8*)pvPortMalloc(num_channel); + if(!pscan_config){ + printf("\n\r ERROR: Can't malloc memory for pscan_config"); + goto exit; + } + //parse command channel list + for(i = 2; i <= argc -1 ; i++){ + *(channel_list + i - 2) = (u8)atoi(argv[i]); + *(pscan_config + i - 2) = PSCAN_ENABLE; + } + + if(wifi_set_pscan_chan(channel_list, pscan_config, num_channel) < 0){ + printf("\n\rERROR: wifi set partial scan channel fail"); + goto exit; + } + + } + + if(wifi_scan_networks(app_scan_result_handler, NULL ) != RTW_SUCCESS){ + printf("\n\rERROR: wifi scan failed"); + goto exit; + } +exit: + if(argc > 2 && channel_list) + vPortFree(channel_list); + +} + +#if CONFIG_WEBSERVER +static void cmd_wifi_start_webserver(int argc, char **argv) +{ + start_web_server(); +} +#endif + +static void cmd_wifi_iwpriv(int argc, char **argv) +{ + if(argc == 2 && argv[1]) { + wext_private_command(WLAN0_NAME, argv[1], 1); + } + else { + printf("\n\rUsage: iwpriv COMMAND PARAMETERS"); + } +} +#endif //#if CONFIG_WLAN + +static void cmd_ping(int argc, char **argv) +{ + if(argc == 2) { + do_ping_call(argv[1], 0, 5); //Not loop, count=5 + } + else if(argc == 3) { + if(strcmp(argv[2], "loop") == 0) + do_ping_call(argv[1], 1, 0); //loop, no count + else + do_ping_call(argv[1], 0, atoi(argv[2])); //Not loop, with count + } + else { + printf("\n\rUsage: ping IP [COUNT/loop]"); + } +} +#if ( configGENERATE_RUN_TIME_STATS == 1 ) +static char cBuffer[ 512 ]; +static void cmd_cpustat(int argc, char **argv) +{ + vTaskGetRunTimeStats( ( char * ) cBuffer ); + printf( cBuffer ); +} +#endif +#if defined(CONFIG_RTL8195A) || defined(CONFIG_RTL8711B) +static void cmd_dump_reg(int argc, char **argv) +{ + CmdDumpWord(argc-1, (u8**)(argv+1)); +} +static void cmd_edit_reg(int argc, char **argv) +{ + CmdWriteWord(argc-1, (u8**)(argv+1)); +} +#endif +static void cmd_exit(int argc, char **argv) +{ + printf("\n\rLeave INTERACTIVE MODE"); + vTaskDelete(NULL); +} + +static void cmd_debug(int argc, char **argv) +{ + if(strcmp(argv[1], "ready_trx") == 0) { + printf("\r\n%d", wifi_is_ready_to_transceive((rtw_interface_t)rtw_atoi((u8*)argv[2]))); + } else if(strcmp(argv[1], "is_up") == 0) { + printf("\r\n%d", wifi_is_up((rtw_interface_t)rtw_atoi((u8*)argv[2]))); + } else if(strcmp(argv[1], "set_mac") == 0) { + printf("\r\n%d", wifi_set_mac_address(argv[2])); + } else if(strcmp(argv[1], "get_mac") == 0) { + u8 mac[18] = {0}; + wifi_get_mac_address((char*)mac); + printf("\r\n%s", mac); + } else if(strcmp(argv[1], "ps_on") == 0) { + printf("\r\n%d", wifi_enable_powersave()); + } else if(strcmp(argv[1], "ps_off") == 0) { + printf("\r\n%d", wifi_disable_powersave()); +#if 0 //TODO + } else if(strcmp(argv[1], "get_txpwr") == 0) { + int idx; + wifi_get_txpower(&idx); + printf("\r\n%d", idx); + } else if(strcmp(argv[1], "set_txpwr") == 0) { + printf("\r\n%d", wifi_set_txpower(rtw_atoi((u8*)argv[2]))); +#endif + } else if(strcmp(argv[1], "get_clientlist") == 0) { + int client_number; + struct { + int count; + rtw_mac_t mac_list[3]; + } client_info; + + client_info.count = 3; + + printf("\r\n%d\r\n", wifi_get_associated_client_list(&client_info, sizeof(client_info))); + + if( client_info.count == 0 ) + { + RTW_API_INFO(("Clients connected 0..\r\n")); + } + else + { + RTW_API_INFO(("Clients connected %d..\r\n", client_info.count)); + for( client_number=0; client_number < client_info.count; client_number++ ) + { + RTW_API_INFO(("------------------------------------\r\n")); + RTW_API_INFO(("| %d | "MAC_FMT" |\r\n", + client_number, + MAC_ARG(client_info.mac_list[client_number].octet) + )); + } + RTW_API_INFO(("------------------------------------\r\n")); + } + } else if(strcmp(argv[1], "get_apinfo") == 0) { + rtw_bss_info_t ap_info; + rtw_security_t sec; + if(wifi_get_ap_info(&ap_info, &sec) == RTW_SUCCESS) { + RTW_API_INFO( ("\r\nSSID : %s\r\n", (char*)ap_info.SSID ) ); + RTW_API_INFO( ("BSSID : "MAC_FMT"\r\n", MAC_ARG(ap_info.BSSID.octet)) ); + RTW_API_INFO( ("RSSI : %d\r\n", ap_info.RSSI) ); + //RTW_API_INFO( ("SNR : %d\r\n", ap_info.SNR) ); + RTW_API_INFO( ("Beacon period : %d\r\n", ap_info.beacon_period) ); + RTW_API_INFO( ( "Security : %s\r\n", ( sec == RTW_SECURITY_OPEN ) ? "Open" : + ( sec == RTW_SECURITY_WEP_PSK ) ? "WEP" : + ( sec == RTW_SECURITY_WPA_TKIP_PSK ) ? "WPA TKIP" : + ( sec == RTW_SECURITY_WPA_AES_PSK ) ? "WPA AES" : + ( sec == RTW_SECURITY_WPA2_AES_PSK ) ? "WPA2 AES" : + ( sec == RTW_SECURITY_WPA2_TKIP_PSK ) ? "WPA2 TKIP" : + ( sec == RTW_SECURITY_WPA2_MIXED_PSK ) ? "WPA2 Mixed" : + "Unknown" ) ); + } + } else if(strcmp(argv[1], "reg_mc") == 0) { + rtw_mac_t mac; + sscanf(argv[2], MAC_FMT, (int*)(mac.octet+0), (int*)(mac.octet+1), (int*)(mac.octet+2), (int*)(mac.octet+3), (int*)(mac.octet+4), (int*)(mac.octet+5)); + printf("\r\n%d", wifi_register_multicast_address(&mac)); + } else if(strcmp(argv[1], "unreg_mc") == 0) { + rtw_mac_t mac; + sscanf(argv[2], MAC_FMT, (int*)(mac.octet+0), (int*)(mac.octet+1), (int*)(mac.octet+2), (int*)(mac.octet+3), (int*)(mac.octet+4), (int*)(mac.octet+5)); + printf("\r\n%d", wifi_unregister_multicast_address(&mac)); + } else if(strcmp(argv[1], "get_rssi") == 0) { + int rssi = 0; + wifi_get_rssi(&rssi); + printf("\n\rwifi_get_rssi: rssi = %d", rssi); + } else { + printf("\r\nUnknown CMD\r\n"); + } +} + +typedef struct _cmd_entry { + char *command; + void (*function)(int, char **); +} cmd_entry; + +static const cmd_entry cmd_table[] = { +#if CONFIG_WLAN + {"wifi_connect", cmd_wifi_connect}, + {"wifi_connect_bssid", cmd_wifi_connect_bssid}, + {"wifi_disconnect", cmd_wifi_disconnect}, + {"wifi_info", cmd_wifi_info}, + {"wifi_on", cmd_wifi_on}, + {"wifi_off", cmd_wifi_off}, + {"wifi_ap", cmd_wifi_ap}, + {"wifi_scan", cmd_wifi_scan}, +#if SCAN_WITH_SSID + {"wifi_scan_with_ssid", cmd_wifi_scan_with_ssid}, +#endif + {"iwpriv", cmd_wifi_iwpriv}, + {"wifi_promisc", cmd_promisc}, +#if CONFIG_OTA_UPDATE + {"wifi_update", cmd_update}, +#endif +#if CONFIG_WEBSERVER + {"wifi_start_webserver", cmd_wifi_start_webserver}, +#endif +#if (CONFIG_INCLUDE_SIMPLE_CONFIG) + {"wifi_simple_config", cmd_simple_config}, +#endif +#ifdef CONFIG_WPS +#if CONFIG_ENABLE_WPS + {"wifi_wps", cmd_wps}, +#endif +#ifdef CONFIG_WPS_AP + {"wifi_ap_wps", cmd_ap_wps}, +#endif +#if CONFIG_ENABLE_P2P + {"wifi_p2p_start", cmd_wifi_p2p_start}, + {"wifi_p2p_stop", cmd_wifi_p2p_stop}, + {"p2p_find", cmd_p2p_find}, + {"p2p_info", cmd_p2p_info}, + {"p2p_disconnect", cmd_p2p_disconnect}, + {"p2p_connect", cmd_p2p_connect}, +#endif +#endif +#ifdef CONFIG_CONCURRENT_MODE + {"wifi_sta_ap",cmd_wifi_sta_and_ap}, +#endif + +#if CONFIG_SSL_CLIENT + {"ssl_client", cmd_ssl_client}, +#endif +#if CONFIG_GOOGLENEST + {"gn", cmd_googlenest}, +#endif +#if CONFIG_EXAMPLE_UART_ADAPTER + {"ut", cmd_uart_adapter}, +#endif +#endif + +#if CONFIG_LWIP_LAYER +// {"app", cmd_app}, + {"wifi_debug", cmd_debug}, +#if CONFIG_BSD_TCP + {"tcp", cmd_tcp}, + {"udp", cmd_udp}, +#endif +#if CONFIG_JD_SMART + {"jd_smart", cmd_jd_smart}, +#endif + {"ping", cmd_ping}, +#endif +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + {"cpu", cmd_cpustat}, +#endif +#if defined(CONFIG_RTL8195A) || defined(CONFIG_RTL8711B) + {"dw", cmd_dump_reg}, + {"ew", cmd_edit_reg}, +#endif + {"exit", cmd_exit}, + {"help", cmd_help} +}; + +#if CONFIG_INTERACTIVE_EXT +/* must include here, ext_cmd_table in wifi_interactive_ext.h uses struct cmd_entry */ +#include +#endif + +static void cmd_help(int argc, char **argv) +{ + int i; + + printf("\n\rCOMMAND LIST:"); + printf("\n\r=============================="); + + for(i = 0; i < sizeof(cmd_table) / sizeof(cmd_table[0]); i ++) + printf("\n\r %s", cmd_table[i].command); +#if CONFIG_INTERACTIVE_EXT + for(i = 0; i < sizeof(ext_cmd_table) / sizeof(ext_cmd_table[0]); i ++) + printf("\n\r %s", ext_cmd_table[i].command); +#endif +} + +#define MAX_ARGC 6 + +static int parse_cmd(char *buf, char **argv) +{ + int argc = 0; + + memset(argv, 0, sizeof(argv)*MAX_ARGC); + while((argc < MAX_ARGC) && (*buf != '\0')) { + argv[argc] = buf; + argc ++; + buf ++; + + while((*buf != ' ') && (*buf != '\0')) + buf ++; + + while(*buf == ' ') { + *buf = '\0'; + buf ++; + } + // Don't replace space + if(argc == 1){ + if(strcmp(argv[0], "iwpriv") == 0){ + if(*buf != '\0'){ + argv[1] = buf; + argc ++; + } + break; + } + } + } + + return argc; +} + +char uart_buf[64]; +void interactive_mode(void *param) +{ + int i, argc; + char *argv[MAX_ARGC]; + extern xSemaphoreHandle uart_rx_interrupt_sema; + + printf("\n\rEnter INTERACTIVE MODE\n\r"); + printf("\n\r# "); + + while(1){ + while(xSemaphoreTake(uart_rx_interrupt_sema, portMAX_DELAY) != pdTRUE); + + if((argc = parse_cmd(uart_buf, argv)) > 0) { + int found = 0; + + for(i = 0; i < sizeof(cmd_table) / sizeof(cmd_table[0]); i ++) { + if(strcmp((const char *)argv[0], (const char *)(cmd_table[i].command)) == 0) { + cmd_table[i].function(argc, argv); + found = 1; + break; + } + } +#if CONFIG_INTERACTIVE_EXT + if(!found) { + for(i = 0; i < sizeof(ext_cmd_table) / sizeof(ext_cmd_table[0]); i ++) { + if(strcmp(argv[0], ext_cmd_table[i].command) == 0) { + ext_cmd_table[i].function(argc, argv); + found = 1; + break; + } + } + } +#endif + if(!found) + printf("\n\runknown command '%s'", argv[0]); + printf("\n\r[MEM] After do cmd, available heap %d\n\r", xPortGetFreeHeapSize()); + } + + printf("\r\n\n# "); + uart_buf[0] = '\0'; + } +} + +void start_interactive_mode(void) +{ +#ifdef SERIAL_DEBUG_RX + if(xTaskCreate(interactive_mode, (char const *)"interactive_mode", STACKSIZE, NULL, tskIDLE_PRIORITY + 4, NULL) != pdPASS) + printf("\n\r%s xTaskCreate failed", __FUNCTION__); +#else + printf("\n\rERROR: No SERIAL_DEBUG_RX to support interactive mode!"); +#endif +} + +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +VOID WlanNormal( IN u16 argc, IN u8 *argv[]) +{ + u8 i, j= 0; + u8* pbuf = (u8*)uart_buf; + extern xSemaphoreHandle uart_rx_interrupt_sema; + + memset(uart_buf, 0, sizeof(uart_buf)); + + printf("argc=%d\n", argc); + for(i = 0 ; i < argc ; i++) + { + printf("command element [%d] = %s\n", i, argv[i]); + for(j = 0; j + +typedef struct { + int socket; + char *host; + ssl_context ssl; +} googlenest_context; + +int gn_connect(googlenest_context *googlenest, char *host, int port); +void gn_close(googlenest_context *googlenest); +int gn_put(googlenest_context *googlenest, char *uri, char *content); +int gn_patch(googlenest_context *googlenest, char *uri, char *content); +int gn_post(googlenest_context *googlenest, char *uri, char *content, unsigned char *out_buffer, size_t out_len); +int gn_get(googlenest_context *googlenest, char *uri, unsigned char *out_buffer, size_t out_len); +int gn_delete(googlenest_context *googlenest, char *uri); +int gn_stream(googlenest_context *googlenest, char *uri); +void google_retrieve_data_hook_callback(void (*callback)(char *)); + +#endif + diff --git a/component/common/application/uart_adapter/uart_adapter.c b/component/common/application/uart_adapter/uart_adapter.c new file mode 100644 index 0000000..ee11102 --- /dev/null +++ b/component/common/application/uart_adapter/uart_adapter.c @@ -0,0 +1,1549 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" + +#include +#include +#include +#include +#include + +#include + +#include "stdlib.h" +#include "string.h" +#include +#include "autoconf.h" +#include "PinNames.h" + +#include "hal_platform.h" +#include "serial_api.h" +#include "serial_ex_api.h" +#include "lwip/api.h" +#include "uart_adapter.h" +#include "wifi_conf.h" +#include "wifi_util.h" +#include "gpio_api.h" // mbed +#include "gpio_irq_api.h" // mbed +#include "osdep_service.h" +//#include "utils/common.h" +#include + +#include "flash_api.h" +#include + +#include +/*********************************************************************** + * Macros * + ***********************************************************************/ +#define UA_TCP_SERVER_FD_NUM 1 +#define UA_TCP_CLIENT_FD_NUM 1 + +#define UA_UART_RECV_BUFFER_LEN 8192 +#define UA_UART_FRAME_LEN 1400 +#define UA_UART_MAX_DELAY_TIME 100 + +#define UA_DATA_SOCKET_PORT 5001 +#define UA_CONTROL_SOCKET_PORT 6001 + +//in 8711am,there are 1 uart +#define UA_MAX_UART_NUM 1 + +#define UA_GPIO_LED_PIN PC_5 +#define UA_GPIO_IRQ_PIN PC_4 + +#define UA_CONTROL_PREFIX "AMEBA_UART" + +/*********************************************************************** + * Variables Declarations * + ***********************************************************************/ +unsigned char ua_work_thread_terminate = 0; +unsigned char ua_work_thread_suspend = 0; + +int ua_tcp_data_fd_list[UA_TCP_SERVER_FD_NUM] = {-1}; +int ua_tcp_control_fd_list[UA_TCP_CLIENT_FD_NUM] = {-1}; +int ua_tcp_data_client_fd = -1; + +int ua_tcp_data_server_listen_fd = -1; +int ua_tcp_control_server_listen_fd = -1; + +char ua_tcp_server_ip[16]; + +char ua_uart_recv_buf[UA_UART_RECV_BUFFER_LEN] = {0}; +int ua_uart_recv_bytes = 0; +static int ua_pread = 0, ua_pwrite= 0; +static char ua_overlap = 0; + +static volatile char ua_rcv_ch=0; +unsigned int ua_tick_last_update = 0; +unsigned int ua_tick_current = 0; + +xSemaphoreHandle ua_uart_action_sema = NULL; +xSemaphoreHandle ua_tcp_sema = NULL; +xSemaphoreHandle ua_work_thread_sema = NULL; +xSemaphoreHandle ua_main_sema = NULL; + +int ua_gpio_irq_happen = 0; +//int ua_gpio_irq_happen = 0; + +long irq_rx_cnt = 0; +long tcp_tx_cnt = 0; +long irq_tx_cnt = 0; +long irq_miss_cnt = 0; + +int ua_debug_print_en = 0; +int ua_tcp_send_flag = 0; + +serial_t ua_uart_config[UA_MAX_UART_NUM]; //gloable uart_config +serial_t ua_sobj; + +static gpio_t gpio_led; +static gpio_irq_t gpio_btn; + +struct ua_tcp_rx_buffer +{ + char data[UA_UART_FRAME_LEN]; + int data_len; +}; +sys_mbox_t mbox_for_uart_tx; + +rtw_wifi_setting_t ua_wifi_setting = {RTW_MODE_NONE, {0}, 0, RTW_SECURITY_OPEN, {0}}; + +/************************************************************************ + * extern variables * + ************************************************************************/ +extern struct netif xnetif[NET_IF_NUM]; + +//static int is_fixed_channel; +//extern int fixed_channel_num; +//extern unsigned char g_ssid[32]; +//extern int g_ssid_len; +//extern unsigned char g_bssid[]; +//extern unsigned char g_security_mode; +//extern int simple_config_result; +//extern unsigned int simple_config_cmd_start_time; +enum ua_sc_result { + SC_ERROR = -1, /* default error code*/ + SC_NO_CONTROLLER_FOUND = 1, /* cannot get sta(controller) in the air which starts a simple config session */ + SC_CONTROLLER_INFO_PARSE_FAIL, /* cannot parse the sta's info */ + SC_TARGET_CHANNEL_SCAN_FAIL, /* cannot scan the target channel */ + SC_JOIN_BSS_FAIL, /* fail to connect to target ap */ + SC_DHCP_FAIL, /* fail to get ip address from target ap */ + /* fail to create udp socket to send info to controller. note that client isolation + must be turned off in ap. we cannot know if ap has configured this */ + SC_UDP_SOCKET_CREATE_FAIL, + SC_SUCCESS, /* default success code */ +}; + +//typedef int (*wlan_init_done_ptr)(void); +extern unsigned char psk_essid[NET_IF_NUM][NDIS_802_11_LENGTH_SSID+4]; +extern unsigned char psk_passphrase[NET_IF_NUM][IW_PASSPHRASE_MAX_SIZE + 1]; +extern unsigned char wpa_global_PSK[NET_IF_NUM][A_SHA_DIGEST_LEN * 2]; + +extern wlan_init_done_ptr p_wlan_uart_adapter_callback; + +/************************************************************************ + * extern funtions * + ************************************************************************/ +#if CONFIG_INCLUDE_SIMPLE_CONFIG +//extern void simple_config_callback(unsigned char *buf, unsigned int len, void* userdata); +//extern int SC_connect_to_AP(void); +//extern int SC_send_simple_config_ack(void); +//extern void SC_check_and_show_connection_info(void); +extern int simple_config_test(void); +//extern void print_simple_config_result(enum sc_result sc_code); +extern int init_test_data(char *custom_pin_code); +extern void deinit_test_data(void); +extern void filter_add_enable(); +extern void remove_filter(); +//extern void rtk_restart_simple_config(void); +//extern int promisc_fixed_channel_num(void * fixed_bssid, u8 *ssid, int * ssid_length); +//extern int promisc_get_fixed_channel(void * fixed_bssid, u8 * ssid, int * ssid_length); +//extern void get_connection_info_from_profile(rtw_security_t security_mode, rtw_network_info_t *wifi); +extern void wifi_enter_promisc_mode(); +#endif +/************************************************************************* +* uart releated * +*************************************************************************/ +#define UA_PRINT_DATA(_HexData, _HexDataLen) \ + if(UA_DEBUG_LEVEL == UA_DEBUG) \ + { \ + int __i; \ + u8 *ptr = (u8 *)_HexData; \ + printf("--------Len=%d\n\r", _HexDataLen); \ + for( __i=0; __i<(int)_HexDataLen; __i++ ) \ + { \ + printf("%02X%s", ptr[__i], (((__i + 1) % 4) == 0)?" ":" "); \ + if (((__i + 1) % 16) == 0) printf("\n\r"); \ + } \ + printf("\n\r"); \ + } + +#define ____________UART__RELATED____________________ +static void uartadapter_uart_irq(uint32_t id, SerialIrq event) +{ + portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; + + if(event == RxIrq) { + ua_rcv_ch = serial_getc(&ua_sobj); + ua_uart_recv_buf[ua_pwrite] = ua_rcv_ch; + ua_pwrite++; //point to uart data recved + xSemaphoreGiveFromISR( ua_uart_action_sema, &xHigherPriorityTaskWoken ); //up action semaphore + + if(ua_pwrite > (UA_UART_RECV_BUFFER_LEN -1)){ //restart from head if reach tail + ua_pwrite = 0; + ua_overlap = 1; + } + + if(ua_overlap && (ua_pwrite - 1) >= ua_pread ){ + //ua_printf(UA_ERROR, "IRQ missing data %d byte!", ua_pread - ua_pwrite + 1); + irq_miss_cnt ++; + ua_pread = ua_pwrite; //if pwrite overhead pread ,pread is always flow rwrite + } + ua_tick_last_update = xTaskGetTickCountFromISR(); // update tick everytime recved data + irq_rx_cnt ++; + } +} + +static int uartadapter_uart_recv_data(void) +{ + //int ret = 0; + int uart_recv_len = 0; + + ua_tick_current = xTaskGetTickCount(); + while((ua_tick_current -ua_tick_last_update) < (UA_UART_MAX_DELAY_TIME/portTICK_RATE_MS) + || ua_tick_current <= ua_tick_last_update){ + if(!ua_overlap){ + uart_recv_len = ua_pwrite - ua_pread; + }else{ + uart_recv_len = (UA_UART_RECV_BUFFER_LEN - ua_pread) + ua_pwrite; + } + + if(uart_recv_len >= UA_UART_FRAME_LEN){ + return 2; + } + ua_tick_current = xTaskGetTickCount(); + } + + return 1; +} + +int uartadapter_uart_read(void *read_buf, size_t size) +{ + /*the same as socket*/ + int ret = 0; + int read_bytes; + int pread_local,pwrite_local; + char *ptr; + ua_printf(UA_DEBUG, "==>uart adapter read uart"); + if(!size || !read_buf){ + ua_printf(UA_ERROR, "inpua error,size should not be null"); + ret = -1; + return ret; + } + + pread_local = ua_pread; + pwrite_local = ua_pwrite; + ptr = (char *)read_buf; + + /*calculate how much data not read */ + if(!ua_overlap){ + ua_uart_recv_bytes = pwrite_local - pread_local; + }else{ + ua_uart_recv_bytes = (UA_UART_RECV_BUFFER_LEN - pread_local) + pwrite_local; + } + + /*decide how much data shoule copy to application*/ + if(size >= ua_uart_recv_bytes ){ + read_bytes = ua_uart_recv_bytes; + ret = ua_uart_recv_bytes; + }else{ + read_bytes = size; + ret = size; + } + + if(!ua_overlap){ + memcpy(ptr, (ua_uart_recv_buf+ pread_local), read_bytes ); + }else { + ua_printf(UA_DEBUG, "uart recv buf is write overlap!!"); + if((pread_local + read_bytes) > UA_UART_RECV_BUFFER_LEN){ + memcpy(ptr,(ua_uart_recv_buf+ pread_local),(UA_UART_RECV_BUFFER_LEN-pread_local)); + memcpy(ptr+(UA_UART_RECV_BUFFER_LEN-pread_local),ua_uart_recv_buf,read_bytes-(UA_UART_RECV_BUFFER_LEN- pread_local)); + }else{ + memcpy(ptr,(ua_uart_recv_buf+ pread_local),read_bytes); + } + } + + ua_uart_recv_bytes = 0; + if((pread_local + read_bytes) >= UA_UART_RECV_BUFFER_LEN){ //update pread + ua_pread = (pread_local + read_bytes) - UA_UART_RECV_BUFFER_LEN; + ua_overlap = 0; //clean overlap flags + }else{ + ua_pread = pread_local + read_bytes; + } + + return ret; +} + + +int uartadapter_uart_write(char *pbuf, size_t size) +{ + /*the same as socket*/ + int ret = 0; + //int cnt = 0; + + if(!size || !pbuf){ + //ua_printf(UA_ERROR, "inpua error,please check!"); + ret = -1; + return ret; + } + +#if 1 + do{ + ret = serial_send_stream_dma(&ua_sobj, pbuf, size); + }while(ret != HAL_OK); +#else + while (size){ + serial_putc(&ua_sobj, *pbuf); + //printf("uart write %d \n", *pbuf); + size--; + pbuf++; + } +#endif + return ret; +} + +static void uartadapter_uart_rx_handle(void* param) +{ + char *rxbuf = NULL; + int ret =0; + int read_len = 0; + + rxbuf = pvPortMalloc(UA_UART_FRAME_LEN); + if(NULL == rxbuf){ + ua_printf(UA_ERROR, "TCP: Allocate rx buffer failed.\n"); + return; + } + + while(xSemaphoreTake(ua_uart_action_sema, portMAX_DELAY) == pdTRUE){ + if(ua_debug_print_en) { + //ua_printf(UA_INFO, "uartadapter_uart_thread_handle loop!"); + } + + ret = uartadapter_uart_recv_data(); + if(ret == -1){ + ua_printf(UA_ERROR, "uart recv data error!"); + }else{ + read_len = uartadapter_uart_read(rxbuf, UA_UART_FRAME_LEN); + if(read_len > 0){ + uartadapter_tcpsend(rxbuf, read_len, 0); + }else if(read_len < 0){ + ua_printf(UA_ERROR, "tcp send read_len = %d", read_len); + } + } + } +} + +static void uartadapter_uart_tx_handle(void* param) +{ + struct ua_tcp_rx_buffer *uart_tx; + //int ret; + + while(1) + { + sys_mbox_fetch(&mbox_for_uart_tx, (void *)&uart_tx); + uartadapter_uart_write(uart_tx->data, uart_tx->data_len); + vPortFree(uart_tx); + } +} + +int uartadapter_uart_open(char *uartname, ua_uart_set_str *puartpara) +{ + PinName uart_tx,uart_rx; + + if(!strcmp("uart0", uartname)){ + uart_tx = PA_7; + uart_rx = PA_6; + ua_uart_config[0].hal_uart_adp.BaudRate = puartpara->BaudRate; + ua_uart_config[0].hal_uart_adp.FlowControl = puartpara->FlowControl; + ua_uart_config[0].hal_uart_adp.WordLen = puartpara->number; + ua_uart_config[0].hal_uart_adp.Parity = puartpara->parity; + ua_uart_config[0].hal_uart_adp.StopBit = puartpara->StopBits; + }else{ + ua_printf(UA_ERROR, "please check uart name!"); + return RTW_ERROR; + } + + /*initial uart */ + serial_init(&ua_sobj,uart_tx,uart_rx); + serial_baud(&ua_sobj,puartpara->BaudRate); + serial_format(&ua_sobj, puartpara->number, (SerialParity)puartpara->parity, puartpara->StopBits); + + /*uart irq handle*/ + serial_irq_handler(&ua_sobj, uartadapter_uart_irq, (uint32_t)&ua_sobj); + serial_irq_set(&ua_sobj, RxIrq, 1); + serial_irq_set(&ua_sobj, TxIrq, 1); + + return 0; +} + +int uartadapter_uart_baud(int baud_rate) +{ + int ret = 0; + serial_baud(&ua_sobj, baud_rate); + + return ret; +} + +int uartadapter_uart_para(int word_len, int parity, int stop_bits) +{ + int ret = 0; + + serial_format(&ua_sobj, word_len, (SerialParity)parity, stop_bits); + + return ret; +} + +int uartadapter_uart_getnum(char *uartname) +{ + /*only uart 0 can used*/ + strcpy(uartname, "uart0\\"); + return UA_MAX_UART_NUM; +} + +int uartadapter_uart_getpara(char *uart_name, ua_uart_get_str *uart_para) +{ + int ret = 0; + + /*get the uart para according to uart_name*/ + if(!strcmp("uart0", uart_name)){ + uart_para->BaudRate =ua_uart_config[0].hal_uart_adp.BaudRate; + uart_para->FlowControl =ua_uart_config[0].hal_uart_adp.FlowControl; + uart_para->number = ua_uart_config[0].hal_uart_adp.WordLen; + uart_para->parity = ua_uart_config[0].hal_uart_adp.Parity; + uart_para->StopBits = ua_uart_config[0].hal_uart_adp.StopBit; + }else{ + ua_printf(UA_ERROR, "please check uart name!"); + return -1; + } + + return ret; +} +void uartadapter_uart_init() +{ + ua_uart_set_str uartset; + ua_uart_get_str uartget; + int uartnum; + char uarttest[]="uart0"; + char uartname[32] = {0}; + + uartset.BaudRate = 9600; + uartset.number = 8; + uartset.StopBits = 0; + uartset.FlowControl = 0; + uartset.parity = 0; + strcpy(uartset.UartName,uarttest); + + uartnum = uartadapter_uart_getnum(uartname); + ua_printf(UA_DEBUG, "there is %d uart on this platform",uartnum); + ua_printf(UA_DEBUG, "uartname = %s",uartname); + + uartadapter_uart_open("uart0", &uartset); + + if(uartadapter_uart_getpara("uart0", &uartget)) + ua_printf(UA_ERROR, "get uart failed!"); + else + ua_printf(UA_DEBUG,"uart pata:\r\n"\ + "uart->BaudRate = %d\r\n"\ + "uart->number = %d\r\n"\ + "uart->FlowControl = %d\r\n"\ + "uart->parity = %d\r\n"\ + "uart->StopBits = %d\r\n"\ + "\r\n",\ + uartget.BaudRate,\ + uartget.number,\ + uartget.FlowControl,\ + uartget.parity,\ + uartget.StopBits\ + ); +} + +#define _________FLASH___RELATED________________________ + +int uartadapter_flashread(int flashadd, char *pbuf, int len) +{ + int ret = 0; + flash_t flash; + + if( len == 0){ + ua_printf(UA_ERROR, "inpua error,data length should not be null!"); + ret = -1; + return ret; + }else //as 8711am only canbe r/w in words.so make len is 4-bytes aligmented. + len += 4 - ((len%4)==0 ? 4 : (len%4)); + + while(len){ + if(flash_read_word(&flash, flashadd, (unsigned int *)pbuf) !=1 ){ + ua_printf(UA_ERROR, "read flash error!"); + ret = -1; + return ret; + } + len -= 4; + pbuf += 4; + flashadd += 4; + } + + return len; +} + +int uartadapter_flashwrite(int flashadd, char *pbuf, int len) +{ + int ret = 0; + flash_t flash; + + if( len == 0){ + ua_printf(UA_ERROR, "inpua error,data length should not be null!"); + ret = -1; + return ret; + } + else //as 8711am only canbe r/w in words.so make len is 4-bytes aligmented. + len += 4 - ((len%4)==0 ? 4 : (len%4)); + + while(len){ + if(flash_write_word(&flash, flashadd, *(unsigned int *)pbuf) !=1 ){ + ua_printf(UA_ERROR, "write flash error!"); + ret = -1; + return ret; + } + len -= 4; + pbuf += 4; + flashadd += 4; + } + + return ret; +} + +int uartadapter_flasherase(int flashadd, int erase_bytelen) +{ + int ret = 0; + flash_t flash; + + flash_erase_sector(&flash, flashadd); + + return ret; +} + +#define _________GPIO___RELATED________________________ +void uartadapter_systemreload(void) +{ + // Cortex-M3 SCB->AIRCR + HAL_WRITE32(0xE000ED00, 0x0C, (0x5FA << 16) | // VECTKEY + (HAL_READ32(0xE000ED00, 0x0C) & (7 << 8)) | // PRIGROUP + (1 << 2)); // SYSRESETREQ +} +void uartadapter_gpio_irq (uint32_t id, gpio_irq_event event) +{ + //int ret = 0; + //int address = FAST_RECONNECT_DATA; + + ua_printf(UA_DEBUG, "GPIO push button!!"); + + + ua_gpio_irq_happen = 1; + xSemaphoreGive(ua_main_sema); + + +} + +void uartadapter_gpio_init() +{ + gpio_init(&gpio_led, UA_GPIO_IRQ_PIN); + gpio_dir(&gpio_led, PIN_INPUT); // Direction: Output + gpio_mode(&gpio_led, PullNone); // No pull + + gpio_irq_init(&gpio_btn, UA_GPIO_IRQ_PIN, uartadapter_gpio_irq, (uint32_t)(&gpio_led)); + gpio_irq_set(&gpio_btn, IRQ_FALL, 1); // Falling Edge Trigger + gpio_irq_enable(&gpio_btn); +} + + +#define _________CONTROL__DATA__RELATED________________________ +int uartadapter_strncmp(char *cs, char *ct, size_t count) +{ + unsigned char c1, c2; + + while (count) { + c1 = *cs++; + c2 = *ct++; + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + count--; + } + + return 0; +} +int uartadapter_control_set_req_handle(u8 *pbuf, u32 sz) +{ + u8 *p = pbuf; + u8 type = 0, len = 0; + + ua_printf(UA_DEBUG, "\n===>uartadapter_control_set_req_handle()"); + UA_PRINT_DATA(pbuf, sz); + + while(p < (pbuf+sz)){ + type = *p++; + len = *p++; + ua_printf(UA_DEBUG, "type=%d len=%d\n", type, len); + switch(type) + { + case UART_CTRL_TYPE_BAUD_RATE: + ua_uart_config[0].hal_uart_adp.BaudRate = *(u32 *)p; + ua_printf(UA_INFO, "SET UART BAUD_RATE to %d.\n", ua_uart_config[0].hal_uart_adp.BaudRate); + serial_baud(&ua_sobj, ua_uart_config[0].hal_uart_adp.BaudRate); + break; + case UART_CTRL_TYPE_WORD_LEN: + ua_uart_config[0].hal_uart_adp.WordLen = *p; + ua_printf(UA_INFO, "SET UART WORD_LEN to %d.\n", ua_uart_config[0].hal_uart_adp.WordLen); + serial_format(&ua_sobj, + ua_uart_config[0].hal_uart_adp.WordLen, + (SerialParity)ua_uart_config[0].hal_uart_adp.Parity, + ua_uart_config[0].hal_uart_adp.StopBit); + break; + case UART_CTRL_TYPE_PARITY: + ua_uart_config[0].hal_uart_adp.Parity = *p; + ua_printf(UA_INFO, "SET UART PARITY to %d.\n", ua_uart_config[0].hal_uart_adp.Parity); + serial_format(&ua_sobj, + ua_uart_config[0].hal_uart_adp.WordLen, + (SerialParity)ua_uart_config[0].hal_uart_adp.Parity, + ua_uart_config[0].hal_uart_adp.StopBit); + break; + case UART_CTRL_TYPE_STOP_BIT: + ua_uart_config[0].hal_uart_adp.StopBit = *p; + ua_printf(UA_INFO, "SET UART STOP_BIT to %d.\n", ua_uart_config[0].hal_uart_adp.StopBit); + serial_format(&ua_sobj, + ua_uart_config[0].hal_uart_adp.WordLen, + (SerialParity)ua_uart_config[0].hal_uart_adp.Parity, + ua_uart_config[0].hal_uart_adp.StopBit); + break; + case UART_CTRL_TYPE_FLOW_CTRL: + ua_uart_config[0].hal_uart_adp.FlowControl = *p; + ua_printf(UA_INFO, "SET UART FLOW_CTRL to %d.\n", ua_uart_config[0].hal_uart_adp.FlowControl); + ua_printf(UA_INFO, "SET UART FLOW_CTRL not support now.\n"); + //TODO + break; + } + p += len; + } + return 0; +} + +int uartadapter_control_get_req_handle(u8 type, u8 *prsp, u32 *sz) +{ + u8 *p = prsp; + + ua_printf(UA_DEBUG, "===>uartadapter_control_get_req_handle()"); + sprintf((char *)p, UA_CONTROL_PREFIX); + p += strlen(UA_CONTROL_PREFIX); + *p++ = UART_CTRL_MODE_GET_RSP; + + if(type & UART_CTRL_TYPE_BAUD_RATE){ + *p++ = UART_CTRL_TYPE_BAUD_RATE; + *p++ = 4; + *(u32*)p = ua_uart_config[0].hal_uart_adp.BaudRate; + p += 4; + } + if(type & UART_CTRL_TYPE_WORD_LEN){ + *p++ = UART_CTRL_TYPE_WORD_LEN; + *p++ = 1; + *p = ua_uart_config[0].hal_uart_adp.WordLen; + p += 1; + } + if(type & UART_CTRL_TYPE_PARITY){ + *p++ = UART_CTRL_TYPE_PARITY; + *p++ = 1; + *p = ua_uart_config[0].hal_uart_adp.Parity; + p += 1; + } + if(type & UART_CTRL_TYPE_STOP_BIT){ + *p++ = UART_CTRL_TYPE_STOP_BIT; + *p++ = 1; + *p = ua_uart_config[0].hal_uart_adp.StopBit; + p += 1; + } + if(type & UART_CTRL_TYPE_FLOW_CTRL){ + *p++ = UART_CTRL_TYPE_FLOW_CTRL; + *p++ = 1; + *p = ua_uart_config[0].hal_uart_adp.FlowControl; + p += 1; + } + *sz = p - prsp; + + UA_PRINT_DATA(prsp, *sz); + return 0; +} + +int uartadapter_control_process(int fd, char *pbuf, size_t size) +{ + /*the same as socket*/ + int ret = 0; + + if(!size || !pbuf){ + //ua_printf(UA_ERROR, "control data input error,please check!"); + ret = -1; + return ret; + } + + UA_PRINT_DATA(pbuf, size); + + if(uartadapter_strncmp(pbuf, UA_CONTROL_PREFIX, 10) != 0) + { + ua_printf(UA_ERROR, "control data prefix wrong!"); + return -1; + } + else + { + u8 *p = (u8*)pbuf + strlen(UA_CONTROL_PREFIX); + u8 mode = *p++; + switch(mode) + { + case UART_CTRL_MODE_SET_REQ: //AMEBA_UART-MODE-TYPE-LEN-VAL-TYPE-LEN-VAL... + { + char rsp[32] = {0}; //AMEBA_UART-MODE + u32 sz = strlen(UA_CONTROL_PREFIX); + uartadapter_control_set_req_handle(p, (size - strlen(UA_CONTROL_PREFIX))); + sprintf(rsp, UA_CONTROL_PREFIX); + *(rsp + sz) = UART_CTRL_MODE_SET_RSP; + sz ++; + sprintf(rsp + sz, "\n"); + sz ++; + uartadapter_tcpsend(rsp, sz, 1); + break; + } + case UART_CTRL_MODE_GET_REQ: //AMEBA_UART-MODE-TYPE + { + char rsp[128] = {0}; + u32 sz = 0; + u8 type = *p; + uartadapter_control_get_req_handle(type, (u8*)rsp, &sz); + sprintf(rsp + sz, "\n"); + sz ++; + uartadapter_tcpsend(rsp, sz, 1); + break; + } + default: + ua_printf(UA_ERROR, UA_CONTROL_PREFIX": Mode (%d) not support!", mode); + break; + } + } + return 0; +} + +#define _________TCP__RELATED________________________ +int uartadapter_tcpclient(const char *host_ip, unsigned short usPort) +{ + int iAddrSize; + int iSockFD = -1; + int iStatus; + //int enable = 1; + struct sockaddr_in sAddr; + + FD_ZERO(&sAddr); + sAddr.sin_family = AF_INET; + sAddr.sin_port = htons(usPort); + sAddr.sin_addr.s_addr = inet_addr(host_ip); + + iAddrSize = sizeof(struct sockaddr_in); + + iSockFD = socket(AF_INET, SOCK_STREAM, 0); + if( iSockFD < 0 ) { + ua_printf(UA_ERROR, "TCP ERROR: create tcp client socket fd error!"); + return 0; + } + + ua_printf(UA_DEBUG, "TCP: ServerIP=%s port=%d.", host_ip, usPort); + ua_printf(UA_DEBUG, "TCP: Create socket %d.", iSockFD); + // connecting to TCP server + iStatus = connect(iSockFD, (struct sockaddr *)&sAddr, iAddrSize); + if (iStatus < 0) { + ua_printf(UA_ERROR, "TCP ERROR: tcp client connect server error! "); + goto Exit; + } + +#if 0 + iStatus = setsockopt(iSockFD, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + if (iStatus < 0) { + printf("\n\rTCP ERROR: tcp client socket set opt error! "); + goto Exit; + } +#endif + + ua_printf(UA_INFO, "TCP: Connect server successfully."); +#if 1 + ua_work_thread_suspend = 1; + vTaskDelay(1500); + if(ua_tcp_data_fd_list[0] != -1){ + ua_printf(UA_INFO, "TCP: Close old data client fd %d.", ua_tcp_data_fd_list[0]); + close(ua_tcp_data_fd_list[0]); + } + ua_tcp_data_fd_list[0] = iSockFD; + ua_printf(UA_INFO, "connect new data socket %d successfully.", iSockFD); + ua_work_thread_suspend = 0; + xSemaphoreGive(ua_work_thread_sema); +#else + ua_tcp_data_client_fd = iSockFD; +#endif + + return 0; + +Exit: + //ua_printf(UA_ERROR, "TCP client fd list exceed."); + close(iSockFD); + return 0; +} + + +int uartadapter_tcpserver(unsigned short usPort, u8 isctrl) +{ + struct sockaddr_in sLocalAddr; + int iAddrSize; + int iSockFD; + int iStatus; + + iSockFD = socket(AF_INET, SOCK_STREAM, 0); + if( iSockFD < 0 ) { + ua_printf(UA_ERROR, "create server_socket error!"); + goto Exit; + } + + ua_printf(UA_DEBUG, "TCP: Create Tcp server socket %d", iSockFD); + + //filling the TCP server socket address + memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr)); + sLocalAddr.sin_family = AF_INET; + sLocalAddr.sin_len = sizeof(sLocalAddr); + sLocalAddr.sin_port = htons(usPort); + sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + iAddrSize = sizeof(sLocalAddr); + + iStatus = bind(iSockFD, (struct sockaddr *)&sLocalAddr, iAddrSize); + if( iStatus < 0 ) { + ua_printf(UA_ERROR, "bind tcp server socket fd error! "); + goto Exit; + } + ua_printf(UA_DEBUG, "TCP: Bind successfully."); + + iStatus = listen(iSockFD, 10); + if( iStatus != 0 ) { + ua_printf(UA_ERROR, "listen tcp server socket fd error!"); + goto Exit; + } + ua_printf(UA_INFO, "TCP Server: Listen on port %d", usPort); + + if(isctrl) + ua_tcp_control_server_listen_fd = iSockFD; + else + ua_tcp_data_server_listen_fd = iSockFD; + + return 0; + +Exit: + close(iSockFD); + ua_printf(UA_INFO, "Tcp server listen on port %d closed!", usPort); + return 0; +} + +void uartadapter_tcp_data_server_handler(void *param) +{ + unsigned short port = UA_DATA_SOCKET_PORT; + + ua_printf(UA_DEBUG, "Uart Adapter: Start Tcp Data Server!"); + uartadapter_tcpserver(port, 0); + +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + ua_printf(UA_DEBUG, "Min available stack size of %s = %d * %d bytes\n\r", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + ua_printf(UA_DEBUG, "TCP: Tcp data server stopped!"); + vTaskDelete(NULL); +} + +void uartadapter_tcp_data_client_handler(void *param) +{ + unsigned short port = UA_DATA_SOCKET_PORT; + + ua_printf(UA_DEBUG, "Uart Adapter: Start Tcp Data Client!"); + uartadapter_tcpclient(ua_tcp_server_ip, port); + +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + ua_printf(UA_DEBUG, "Min available stack size of %s = %d * %d bytes\n\r", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + ua_printf(UA_DEBUG, "TCP: Tcp data client stopped!"); + vTaskDelete(NULL); +} + +void uartadapter_tcp_control_server_handler(void *param) +{ + unsigned short port = UA_CONTROL_SOCKET_PORT; + + ua_printf(UA_DEBUG, "Uart Adapter: Start Tcp Control Server!"); + uartadapter_tcpserver(port, 1); + +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + ua_printf(UA_DEBUG, "Min available stack size of %s = %d * %d bytes", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + ua_printf(UA_DEBUG, "TCP: Tcp control server stopped!"); + vTaskDelete(NULL); +} + +void uartadapter_tcpsend(char *buffer, int size, u8 isctrl) +{ + int iStatus; + + ua_tcp_send_flag = 1; + + if(!isctrl){ + if(ua_tcp_data_fd_list[0] != -1){ + //xSemaphoreTake(ua_tcp_sema, portMAX_DELAY); + iStatus = send(ua_tcp_data_fd_list[0], buffer, size, 0 ); + //xSemaphoreGive(ua_tcp_sema); + + if( iStatus <= 0 ){ + ua_printf(UA_ERROR, "tcp data socket send data error! iStatus:%d!", iStatus); + //goto Exit; + }else if(iStatus != size){ + ua_printf(UA_DEBUG, "uart tcp data socket send: size %d, ret = %d!", size, iStatus); + } + + if(ua_debug_print_en){ + ua_printf(UA_INFO, "uart tcp data socket send %d bytes, ret %d!", size, iStatus); + } + } + } + else{ + if(ua_tcp_control_fd_list[0] != -1){ + //xSemaphoreTake(ua_tcp_sema, portMAX_DELAY); + iStatus = send(ua_tcp_control_fd_list[0], buffer, size, 0 ); + //xSemaphoreGive(ua_tcp_sema); + + if( iStatus <= 0 ){ + ua_printf(UA_ERROR,"tcp control socket send data error! iStatus:%d!", iStatus); + goto Exit; + }else if(iStatus != size){ + ua_printf(UA_DEBUG,"uart tcp control socket send: size %d, ret = %d!", size, iStatus); + } + + if(ua_debug_print_en){ + ua_printf(UA_INFO,"uart tcp control socket send %d bytes, ret %d!", size, iStatus); + } + } + } + + ua_tcp_send_flag = 0; + +Exit: + + return; +} + +void uartadapter_tcp_data_fd_handler(char *tcp_rxbuf) +{ + int recv_len; + struct ua_tcp_rx_buffer *rx_buffer; + rx_buffer = pvPortMalloc(sizeof(struct ua_tcp_rx_buffer)); + if(NULL == rx_buffer){ + ua_printf(UA_ERROR, "Allocate tcp data buffer failed.\n"); + return; + } + + //xSemaphoreTake(ua_tcp_sema, portMAX_DELAY); + recv_len = recv(ua_tcp_data_fd_list[0], tcp_rxbuf, UA_UART_FRAME_LEN, 0); + //xSemaphoreGive(ua_tcp_sema); + + if(recv_len < 0){ + ua_printf(UA_ERROR, "Tcp Data Socket %d Recv Error", ua_tcp_data_fd_list[0]); + //goto EXIT; + } + ua_printf(UA_DEBUG, "Tcp Data Socket %d Recv %d Data", ua_tcp_data_fd_list[0], recv_len); + +#if 1 + if(recv_len > 0){ + memcpy(rx_buffer->data, tcp_rxbuf, recv_len); + rx_buffer->data_len = recv_len; + sys_mbox_post(&mbox_for_uart_tx, (void *)rx_buffer); + }else{ + vPortFree(rx_buffer); + } +#else + uartadapter_uart_write(tcp_rxbuf, recv_len); +#endif + + tcp_tx_cnt += recv_len; + + return; +} + +void uartadapter_tcp_control_fd_handler() +{ + char tcp_rxbuf[UA_UART_FRAME_LEN]; + int recv_len; + + //xSemaphoreTake(ua_tcp_sema, portMAX_DELAY); + recv_len = recv(ua_tcp_control_fd_list[0], tcp_rxbuf, UA_UART_FRAME_LEN, 0); //MSG_DONTWAIT MSG_WAITALL + //xSemaphoreGive(ua_tcp_sema); + + if(recv_len<0){ + ua_printf(UA_ERROR, "Tcp Control Socket %d Recv Error", ua_tcp_control_fd_list[0]); + //goto EXIT; + } + ua_printf(UA_DEBUG, "Tcp Control Socket %d Recv %d Data", ua_tcp_control_fd_list[0], recv_len); + + uartadapter_control_process(ua_tcp_control_fd_list[0], (void*)tcp_rxbuf, recv_len); + + return; + +} + +void uartadapter_tcp_data_listen_fd_handler(int old_data_fd) +{ + struct sockaddr_in sAddr; + int addrlen = sizeof(sAddr); + + ua_tcp_data_fd_list[0] = accept(ua_tcp_data_server_listen_fd, (struct sockaddr *)&sAddr, (socklen_t*)&addrlen); + if( ua_tcp_data_fd_list[0] < 0 ) { + ua_printf(UA_ERROR, "Accept tcp data client socket fd error"); + goto EXIT; + } + ua_printf(UA_INFO, "Accept new data socket %d on port %d successfully.", ua_tcp_data_fd_list[0], sAddr.sin_port); + if(old_data_fd != -1) + { + shutdown(old_data_fd, 2); + close(old_data_fd); + ua_printf(UA_INFO, "Close old data socket %d.", old_data_fd); + old_data_fd = -1; + } + + return; + +EXIT: + if(ua_tcp_data_server_listen_fd != -1){ + close(ua_tcp_data_server_listen_fd); + ua_tcp_data_server_listen_fd = -1; + } +} + +void uartadapter_tcp_control_listen_fd_handler(int old_control_fd) +{ + struct sockaddr_in sAddr; + int addrlen = sizeof(sAddr); + + ua_tcp_control_fd_list[0] = accept(ua_tcp_control_server_listen_fd, (struct sockaddr *)&sAddr, (socklen_t*)&addrlen); + if( ua_tcp_control_fd_list[0] < 0 ) { + ua_printf(UA_ERROR, "Accept tcp control client socket fd error"); + goto EXIT; + } + ua_printf(UA_INFO, "Accept new control socket %d on port %d successfully.", ua_tcp_control_fd_list[0], sAddr.sin_port); + if(old_control_fd != -1) + { + close(old_control_fd); + ua_printf(UA_INFO, "Close old control socket %d.", old_control_fd); + old_control_fd = -1; + } + + return; + +EXIT: + if(ua_tcp_data_server_listen_fd != -1){ + close(ua_tcp_data_server_listen_fd); + ua_tcp_data_server_listen_fd = -1; + } +} + +void uartadapter_tcp_select(void *param) +{ + int max_fd; + struct timeval tv; + fd_set readfds; + int ret = 0; + char *tcp_rxbuf; + tcp_rxbuf = pvPortMalloc(UA_UART_FRAME_LEN); + if(NULL == tcp_rxbuf){ + printf("\n\rTCP: Allocate client buffer failed.\n"); + return; + } + + + while(1){ + if(ua_work_thread_suspend){ + ua_printf(UA_DEBUG, "uart adapter test thread suspended!"); + if(xSemaphoreTake(ua_work_thread_sema, portMAX_DELAY) == TRUE) + ua_printf(UA_DEBUG, "uart adapter test thread take semaphore!"); + } + + FD_ZERO(&readfds); + max_fd = -1; + + if(ua_tcp_data_fd_list[0] != -1){ + FD_SET(ua_tcp_data_fd_list[0], &readfds); + if(ua_tcp_data_fd_list[0] > max_fd) + max_fd = ua_tcp_data_fd_list[0]; + } + + if(ua_tcp_control_fd_list[0] != -1){ + FD_SET(ua_tcp_control_fd_list[0], &readfds); + if(ua_tcp_control_fd_list[0] > max_fd) + max_fd = ua_tcp_control_fd_list[0]; + } + + if(ua_tcp_control_server_listen_fd != -1){ + FD_SET(ua_tcp_control_server_listen_fd, &readfds); + if(ua_tcp_control_server_listen_fd > max_fd) + max_fd = ua_tcp_control_server_listen_fd; + } + + if(ua_tcp_data_server_listen_fd != -1){ + FD_SET(ua_tcp_data_server_listen_fd, &readfds); + if(ua_tcp_data_server_listen_fd > max_fd) + max_fd = ua_tcp_data_server_listen_fd; + } + + tv.tv_sec = 1; + tv.tv_usec = 0; + + ret = select(max_fd + 1, &readfds, NULL, NULL, &tv); + + if(ua_debug_print_en){ + ua_printf(UA_INFO, "uart adapter test select ret = %x",ret); + } + if(ret > 0){ + if(ua_tcp_data_fd_list[0] != -1 && FD_ISSET(ua_tcp_data_fd_list[0], &readfds)){ + uartadapter_tcp_data_fd_handler(tcp_rxbuf); + } + + if(ua_tcp_control_fd_list[0] != -1 && FD_ISSET(ua_tcp_control_fd_list[0], &readfds)){ + uartadapter_tcp_control_fd_handler(); + } + + if(ua_tcp_data_server_listen_fd != -1 && FD_ISSET(ua_tcp_data_server_listen_fd, &readfds)){ + uartadapter_tcp_data_listen_fd_handler(ua_tcp_data_fd_list[0]); + } + + if(ua_tcp_control_server_listen_fd != -1 && FD_ISSET(ua_tcp_control_server_listen_fd, &readfds)){ + uartadapter_tcp_control_listen_fd_handler(ua_tcp_control_fd_list[0]); + } + } + } + + //vTaskDelete(NULL); +} + + +#define _________WIFI__RELATED________________________ + +int uartadapter_connect_wifi(rtw_network_info_t *p_wifi, uint32_t channel, uint8_t pscan_config) +{ + int retry = 3; + rtw_wifi_setting_t setting; + int ret; + while (1) { + if(wifi_set_pscan_chan((uint8_t *)&channel, &pscan_config, 1) < 0){ + printf("\n\rERROR: wifi set partial scan channel fail"); + ret = SC_TARGET_CHANNEL_SCAN_FAIL; + return ret; + } + + ret = wifi_connect((char*)p_wifi->ssid.val, + p_wifi->security_type, + (char*)p_wifi->password, + p_wifi->ssid.len, + p_wifi->password_len, + p_wifi->key_id, + NULL); + + if (ret == RTW_SUCCESS) { + ret = LwIP_DHCP(0, DHCP_START); + wifi_get_setting(WLAN0_NAME, &setting); + wifi_show_setting(WLAN0_NAME, &setting); + if (ret == DHCP_ADDRESS_ASSIGNED) + return SC_SUCCESS; + else + return SC_DHCP_FAIL; + } + + if (retry == 0) { + ret = SC_JOIN_BSS_FAIL; + break; + } + retry --; + } + + return ret; +} + + +static int uartadapter_load_wifi_config() +{ + flash_t flash; + uint8_t *data; + uint32_t channel; + uint8_t pscan_config; + char key_id; + rtw_network_info_t wifi = {0}; + int ret = SC_SUCCESS; + + + data = (uint8_t *)rtw_zmalloc(FAST_RECONNECT_DATA_LEN); + flash_stream_read(&flash, FAST_RECONNECT_DATA, FAST_RECONNECT_DATA_LEN, (uint8_t *)data); + if(*((uint32_t *) data) != ~0x0) + { + ua_printf(UA_INFO, "AP Profile read from FLASH, try to connect"); + memcpy(psk_essid, (uint8_t *)data, NDIS_802_11_LENGTH_SSID + 4); + memcpy(psk_passphrase, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4, (IW_PASSPHRASE_MAX_SIZE + 1)); + memcpy(wpa_global_PSK, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1), A_SHA_DIGEST_LEN * 2); + memcpy(&channel, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1) + A_SHA_DIGEST_LEN * 2, 4); + sprintf(&key_id,"%d",(channel >> 28)); + channel &= 0xff; + pscan_config = PSCAN_ENABLE | PSCAN_FAST_SURVEY; + //set partial scan for entering to listen beacon quickly + //wifi_set_pscan_chan((uint8_t *)&channel, &pscan_config, 1); + +#ifdef CONFIG_AUTO_RECONNECT + wifi_set_autoreconnect(1); +#endif + //set wifi connect + wifi.ssid.len = (int)strlen((char*)psk_essid); + memcpy(wifi.ssid.val, psk_essid, wifi.ssid.len); + wifi.key_id = key_id; + + //open mode + if(!strlen((char*)psk_passphrase)){ + wifi.security_type = RTW_SECURITY_OPEN; + } + //wep mode + else if( strlen((char*)psk_passphrase) == 5 || strlen((char*)psk_passphrase) == 13){ + wifi.security_type = RTW_SECURITY_WEP_PSK; + wifi.password = (unsigned char *)psk_passphrase; + wifi.password_len = (int)strlen((char const *)psk_passphrase); + } + //WPA/WPA2 + else{ + wifi.security_type = RTW_SECURITY_WPA2_AES_PSK; + wifi.password = (unsigned char *)psk_passphrase; + wifi.password_len = (int)strlen((char const *)psk_passphrase); + } + + ret = uartadapter_connect_wifi(&wifi, channel, pscan_config); + + //print_simple_config_result((enum ua_sc_result)ret); + + if(data) + rtw_mfree(data, FAST_RECONNECT_DATA_LEN); + + if(ret == SC_SUCCESS) + return RTW_SUCCESS; + else + return RTW_ERROR; + }else{ + ua_printf(UA_INFO, "No AP Profile read from FLASH, start simple configure"); + + if(data) + rtw_mfree(data, FAST_RECONNECT_DATA_LEN); + + return RTW_ERROR; + } +} + +int uartadapter_simple_config(char *pin_code){ +#if CONFIG_INCLUDE_SIMPLE_CONFIG + int ret = SC_SUCCESS; + wifi_enter_promisc_mode(); + if(init_test_data(pin_code) == 0){ + filter_add_enable(); + //uartadapter_simple_config_handler(); + ret = simple_config_test(); + //print_simple_config_result(ret); + remove_filter(); + if(ret == SC_SUCCESS) + return RTW_SUCCESS; + else + return RTW_ERROR; + }else{ + return RTW_ERROR; + } +#endif +} + +#define _________MDNS__RELATED________________________ +static void uartadapter_mdns_thread(void *param) +{ + DNSServiceRef dnsServiceRef = NULL; + DNSServiceRef dnsServiceRef2 = NULL; + //TXTRecordRef txtRecord; + //unsigned char txt_buf[100]; // use fixed buffer for text record to prevent malloc/free + //TXTRecordCreate(&txtRecord, sizeof(txt_buf), txt_buf); + //TXTRecordSetValue(&txtRecord, "text1", strlen("text1_content_new"), "text1_content_new"); + + // Delay to wait for IP by DHCP + //vTaskDelay(5000); + struct netif * pnetif = &xnetif[0]; + + ua_printf(UA_DEBUG, "Uart Adapter mDNS Init"); + + if(mDNSResponderInit() == 0) { + ua_printf(UA_INFO, "mDNS Register service"); + char hostname[32] = {0}; //AMEBA_UART-MODE + u32 prefix_len = strlen("AMEBA_"); + sprintf(hostname, "AMEBA_"); + sprintf(hostname+prefix_len, "%02x%02x%02x%02x%02x%02x", + pnetif->hwaddr[0], pnetif->hwaddr[1], pnetif->hwaddr[2], + pnetif->hwaddr[3], pnetif->hwaddr[4], pnetif->hwaddr[5]); + + //TXTRecordCreate(&txtRecord, sizeof(txt_buf), txt_buf); + //TXTRecordSetValue(&txtRecord, "text1", strlen("text1_content"), "text1_content"); + //TXTRecordSetValue(&txtRecord, "text2", strlen("text2_content"), "text2_content"); + dnsServiceRef = mDNSRegisterService(hostname, "_uart_data._tcp", "local", 5001, NULL); + if(dnsServiceRef == NULL) + ua_printf(UA_ERROR, "mdns data service register failed!"); + dnsServiceRef2 = mDNSRegisterService(hostname, "_uart_control._tcp", "local", 6001, NULL); + if(dnsServiceRef2 == NULL) + ua_printf(UA_ERROR, "mdns control service register failed!"); + + }else{ + ua_printf(UA_INFO, "mDNS Init Failed"); + } + + //vTaskDelete(NULL); +} + +#define _________INIT__RELATED________________________ +void uartadapter_auto_connect(void *param) +{ + int ret = 0; + + if(wifi_is_ready_to_transceive(RTW_STA_INTERFACE) == RTW_SUCCESS) { + ua_printf(UA_INFO, "wifi connect successfully!"); + goto START; + }else{ +RETRY: + ret = uartadapter_load_wifi_config(); + if(ret != RTW_SUCCESS){ + ret = uartadapter_simple_config(NULL); + if(ret != RTW_SUCCESS){ + ua_printf(UA_INFO, "Simple configure connect failed, try again!"); + goto RETRY; + } + } + } + +START: + if(!sys_thread_new("tcp data server", uartadapter_tcp_data_server_handler, NULL, 256, UA_UART_THREAD_PRIORITY)) + ua_printf(UA_ERROR, "%s sys_thread_new data server failed\n", __FUNCTION__); + + vTaskDelay(50); + if(!sys_thread_new("tcp control server", uartadapter_tcp_control_server_handler, NULL, 256, UA_UART_THREAD_PRIORITY)) + ua_printf(UA_ERROR, "%s sys_thread_new control server failed\n", __FUNCTION__); + + vTaskDelay(50); + + if(!sys_thread_new("tcp control server", uartadapter_tcp_select, NULL, 1024, UA_UART_THREAD_PRIORITY)) + ua_printf(UA_ERROR, "%s sys_thread_new tcp select failed\n", __FUNCTION__); + + + vTaskDelay(50); + + //if(!sys_thread_new("MDNS thread", uartadapter_mdns_thread, NULL, 768, 6)) + // ua_printf(UA_ERROR, "%s sys_thread_new serial failed\n", __FUNCTION__); + + uartadapter_mdns_thread(NULL); + + vTaskDelay(50); + + ua_printf(UA_INFO, "[MEM] After auao connect, available heap %d", xPortGetFreeHeapSize()); + + /* Kill init thread after all init tasks done */ + vTaskDelete(NULL); + //auao_config = NULL; +} + +void uartadapter_main(void *param) +{ + int ret = 0; + unsigned int tick_start; + unsigned int tick_current; + int pin_high = 0; + int address = FAST_RECONNECT_DATA; + + while(xSemaphoreTake(ua_main_sema, portMAX_DELAY) == pdTRUE){ + if(ua_gpio_irq_happen){ + pin_high = 0; + tick_start = xTaskGetTickCount(); + tick_current = xTaskGetTickCount(); + while(tick_current - tick_start < 3000){ + if (gpio_read(&gpio_led)){ + pin_high = 1; + break; + }else{ + tick_current = xTaskGetTickCount(); + } + vTaskDelay(10); + } + + ua_gpio_irq_happen = 0; + if(!pin_high){ + ret = uartadapter_flasherase(address, sizeof(rtw_wifi_config_t)); + if(ret < 0){ + ua_printf(UA_ERROR, "flash erase error!"); + } + + uartadapter_systemreload(); + } + + } + + + + } + /* Kill init thread after all init tasks done */ + vTaskDelete(NULL); + //auao_config = NULL; +} + + +int uartadapter_init() +{ + int ret = 0; + + ua_printf(UA_INFO, "==============>%s()\n", __func__); + + uartadapter_uart_init(); + uartadapter_gpio_init(); + + ua_uart_action_sema = xSemaphoreCreateCounting(300, 0); + + vSemaphoreCreateBinary(ua_tcp_sema); + + vSemaphoreCreateBinary(ua_work_thread_sema); + xSemaphoreTake(ua_work_thread_sema, 1/portTICK_RATE_MS); + + vSemaphoreCreateBinary(ua_main_sema); + xSemaphoreTake(ua_main_sema, 1/portTICK_RATE_MS); + + + ret= sys_mbox_new(&mbox_for_uart_tx, sizeof(struct ua_tcp_rx_buffer)); + if(ret != ERR_OK) + { + ua_printf(UA_ERROR, "mbox_for_uart_tx create failed"); + goto Exit; + } + + //wifi_reg_event_handler(WIFI_EVENT_DISCONNECT, _uartadapter_event_handler, NULL); + + /*create uart_thread to handle send&recv data*/ + if(xTaskCreate(uartadapter_uart_rx_handle, ((const char*)"uart_rx"), 768, NULL, UA_UART_THREAD_PRIORITY, NULL) != pdPASS) + ua_printf(UA_ERROR, "%s xTaskCreate(uart_rx) failed", __FUNCTION__); + + vTaskDelay(50); + + /*create uart_thread to handle send&recv data*/ + if(xTaskCreate(uartadapter_uart_tx_handle, ((const char*)"uart_tx"), 512, NULL, UA_UART_THREAD_PRIORITY, NULL) != pdPASS) + ua_printf(UA_ERROR, "%s xTaskCreate(uart_tx) failed", __FUNCTION__); + + vTaskDelay(50); + + if(xTaskCreate(uartadapter_auto_connect, ((const char*)"auto connnect"), 1024, NULL, UA_UART_THREAD_PRIORITY, NULL) != pdPASS) + ua_printf(UA_ERROR, "%s xTaskCreate(auao connnect) failed", __FUNCTION__); + + if(xTaskCreate(uartadapter_main, ((const char*)"uart main"), 256, NULL, UA_UART_THREAD_PRIORITY, NULL) != pdPASS) + ua_printf(UA_ERROR, "%s xTaskCreate(auao connnect) failed", __FUNCTION__); + + + return 0; +Exit: + ua_printf(UA_ERROR, "%s(): Initialization failed!", __func__); + return ret; +} + +void example_uart_adapter_init() +{ + // Call back from wlan driver after wlan init done + p_wlan_uart_adapter_callback = uartadapter_init; +} + +#define _________CMD__RELATED________________________ +void uartadapter_print_irq_rx_count() +{ + ua_printf(UA_INFO, "ua_tick_last_update: %d!\n", ua_tick_last_update); + ua_printf(UA_INFO, "ua_tick_current: %d!\n", ua_tick_current); + ua_printf(UA_INFO, "ua current tick: %d!\n", xTaskGetTickCount()); + ua_printf(UA_INFO, "ua_pwrite: %d!\n", ua_pwrite); + ua_printf(UA_INFO, "ua_pread: %d!\n", ua_pread); + ua_printf(UA_INFO, "ua_overlap: %d!\n", ua_overlap); + ua_printf(UA_INFO, "ua_rcv_ch: %d!\n", ua_rcv_ch); + ua_printf(UA_INFO, "ua_uart_recv_bytes: %d!\n", ua_uart_recv_bytes); + ua_printf(UA_INFO, "ua_rcv_ch: %d!\n", ua_rcv_ch); + ua_printf(UA_INFO, "irq_rx_cnt: %d!\n", irq_rx_cnt); + ua_printf(UA_INFO, "irq_tx_cnt: %d!\n", irq_tx_cnt); + ua_printf(UA_INFO, "irq_miss_cnt: %d!\n", irq_miss_cnt); + ua_printf(UA_INFO, "tcp_tx_cnt: %d!\n", tcp_tx_cnt); + ua_printf(UA_INFO, "tcp_send_flag: %d!\n", ua_tcp_send_flag); + ua_printf(UA_INFO, "ua_tcp_data_fd_list: %d!\n", ua_tcp_data_fd_list[0]); + ua_printf(UA_INFO, "ua_tcp_control_fd_list: %d!\n", ua_tcp_control_fd_list[0]); + ua_printf(UA_INFO, "ua_tcp_data_server_listen_fd: %d!\n", ua_tcp_data_server_listen_fd); + ua_printf(UA_INFO, "ua_tcp_control_server_listen_fd: %d!\n", ua_tcp_control_server_listen_fd); + +} + +void uartadapter_reset_irq_rx_count() +{ + irq_rx_cnt = 0; + irq_tx_cnt = 0; + tcp_tx_cnt = 0; + irq_miss_cnt = 0; +} + +void uartadapter_set_debug_print(bool enable) +{ + ua_debug_print_en = enable; +} + +void cmd_uart_adapter(int argc, char **argv) +{ + if(argc < 2) { + printf("\n\r inpua error\n"); + return; + } + + //printf("\n\r haier_test: argv[1]=%s\n", argv[1]); + + if(strcmp(argv[1], "help") == 0){ + printf("\r\nUART THROUGH COMMAND LIST:"); + printf("\r\n=============================="); + printf("\r\n\tuart_baud"); + printf("\r\n"); + }else if(strcmp(argv[1], "irq_rx_get") == 0){ + uartadapter_print_irq_rx_count(); + }else if(strcmp(argv[1], "debug_print_en") == 0){ + uartadapter_set_debug_print(TRUE); + }else if(strcmp(argv[1], "debug_print_dis") == 0){ + uartadapter_set_debug_print(FALSE); + }else if(strcmp(argv[1], "irq_rx_reset") == 0){ + uartadapter_reset_irq_rx_count(); + }else if(strcmp(argv[1], "tcp") == 0){ + if(strcmp(argv[2], "-c") == 0 || strcmp(argv[2], "c") == 0){ + strncpy(ua_tcp_server_ip, argv[3], (strlen(argv[3])>16)?16:strlen(argv[3])); + if(!sys_thread_new("tcp dclient", uartadapter_tcp_data_client_handler, NULL, 768, UA_UART_THREAD_PRIORITY)) + printf("%s sys_thread_new serial failed\n", __FUNCTION__); + }else if(strcmp(argv[2], "-s") == 0 || strcmp(argv[2], "s") == 0){ + if(!sys_thread_new("tcp dserver", uartadapter_tcp_data_server_handler, NULL, 768, UA_UART_THREAD_PRIORITY)) + printf("%s sys_thread_new serial failed\n", __FUNCTION__); + } + }else if(strcmp(argv[1], "uart_baud") == 0){ + uartadapter_uart_baud(atoi(argv[2])); + }else if(strcmp(argv[1], "uart_para") == 0){ + uartadapter_uart_para(atoi(argv[2]), atoi(argv[3]), atoi(argv[4])); + }else{ + printf("\n\rCan not find the inpua commond!"); + } +} + diff --git a/component/common/application/uart_adapter/uart_adapter.h b/component/common/application/uart_adapter/uart_adapter.h new file mode 100644 index 0000000..80a3e24 --- /dev/null +++ b/component/common/application/uart_adapter/uart_adapter.h @@ -0,0 +1,166 @@ +#include +#include "gpio_api.h" // mbed +#include "gpio_irq_api.h" // mbed +/****************************************************** + * Macros + ******************************************************/ +#define UA_ERROR 0 +#define UA_WARNING 1 +#define UA_INFO 2 +#define UA_DEBUG 3 +#define UA_NONE 0xFF +#define UA_DEBUG_LEVEL UA_INFO + +#define UA_UART_THREAD_PRIORITY 5 +#define UA_UART_THREAD_STACKSIZE 512 + +#if (UA_DEBUG_LEVEL== UA_NONE) +#define ua_printf(level, fmt, arg...) +#else +#define ua_printf(level, fmt, arg...) \ +do {\ + if (level <= UA_DEBUG_LEVEL) {\ + if (level <= UA_ERROR) {\ + printf("\r\nERROR: " fmt, ##arg);\ + } \ + else {\ + printf("\r\n"fmt, ##arg);\ + } \ + }\ +}while(0) +#endif + +/****************************************************** + * Constants + ******************************************************/ +typedef enum +{ + UART_CTRL_MODE_SET_REQ = 0, + UART_CTRL_MODE_SET_RSP = 1, + UART_CTRL_MODE_GET_REQ = 2, + UART_CTRL_MODE_GET_RSP = 3, +}ua_ctrl_mode_t; + +typedef enum +{ + UART_CTRL_TYPE_BAUD_RATE = 0x01, + UART_CTRL_TYPE_WORD_LEN = 0x02, + UART_CTRL_TYPE_PARITY = 0x04, + UART_CTRL_TYPE_STOP_BIT = 0x08, + UART_CTRL_TYPE_FLOW_CTRL = 0x10, +}ua_ctrl_type_t; + +/****************************************************** + * Structures + ******************************************************/ +typedef long time_t; + +typedef struct _uartadapter_timeval_st{ + long tv_sec; //Ãë + long tv_hmsec; //°ÙºÁÃë +}UARTTHROUGH_TIMEVAL_st; + +//»ñÈ¡ÍøÂçDHCP²ÎÊýʱ´æ´¢·µ»ØµÄIP£¬MAC,GW£¬MASK£¬DNSµÈ +typedef struct _ua_net_para { + char dhcp; + char ip[16]; // such as string "192.168.1.1" + char gate[16]; + char mask[16]; + char dns[16]; + char mac[16]; // such as string "7E0000001111" + char broadcastip[16]; +} ua_net_para_st; + +//softAPģʽʱ´æ´¢ËÑË÷µ½µÄ APÁбíÐÅÏ¢ +typedef struct _ua_ApList_str +{ + char ssid[32]; + char ApPower; // min:0, max:100 + char channel; + char encryption; +}ua_ApList_str; + +//softAPģʽʱ´æ´¢ËÑË÷µ½µÄ APÁбíÐÅÏ¢ +typedef struct _ua_UwtPara_str +{ + char ApNum; //AP number + ua_ApList_str * ApList; +} ua_UwtPara_str; + + +//»ñÈ¡UARTÅäÖÃÐÅϢʱ£¬´æ´¢»ñµÃµÄ´®¿ÚÅäÖÃÐÅÏ¢ +typedef struct _ua_uart_get_str +{ + int BaudRate; //The baud rate + char number; //The number of data bits + char parity; //The parity(0: none, 1:odd, 2:evn, default:0) + char StopBits; //The number of stop bits + char FlowControl; //support flow control is 1 +}ua_uart_get_str; +//ÅäÖÃUART²ÎÊýʱ£¬´æ´¢Ïà¹ØµÄÅäÖÃÐÅÏ¢ +typedef struct _ua_uart_set_str +{ + char UartName[8]; // the name of uart + int BaudRate; //The baud rate + char number; //The number of data bits + char parity; //The parity(default NONE) + char StopBits; //The number of stop bits + char FlowControl; //support flow control is 1 +}ua_uart_set_str; + + + +//Æô¶¯wifiÁ¬½Óʱ´æ´¢WIFIµÄÅäÖÃÐÅÏ¢ +typedef struct _ua_network_InitTypeDef_st +{ + char wifi_mode; // SoftAp(0)station(1) + char wifi_ssid[32]; + char wifi_key[32]; + char local_ip_addr[16]; + char net_mask[16]; + char gateway_ip_addr[16]; + char dnsServer_ip_addr[16]; + char dhcpMode; // disable(0), client mode(1), server mode(2) + char address_pool_start[16]; + char address_pool_end[16]; + int wifi_retry_interval;//sta reconnect interval, ms + char channel; + char encryption; +} ua_network_InitTypeDef_st; + +#pragma pack(1) +struct ua_ieee80211_frame +{ + unsigned char i_fc[2]; + unsigned char i_dur[2]; + unsigned char i_addr1[6]; + unsigned char i_addr2[6]; + unsigned char i_addr3[6]; + unsigned char i_seq[2]; +}; +#pragma pack() + +//extern void uartadapter_netcallback(ua_net_para_st *pnet); +//extern void uartadapter_wifistatushandler(int status); +extern int uartadapter_init(); +//extern void uartadapter_deinit(); +//extern void uartadapter_wifipoweron(void); +//extern void uartadapter_wifipoweroff(void); + +//extern int uartadapter_startnetwork(ua_network_InitTypeDef_st* pNetworkInitPara); +//extern void uartadapter_wifidisconnect (void); +extern int uartadapter_getnetpara(ua_net_para_st * pnetpara); + +//extern int uartadapter_sethostname(char* name); + +extern int uartadapter_readuart(int fd, void *read_buf, size_t size); +void uartadapter_tcp_data_fd_handler(); +extern void uartadapter_tcpsend(char *buffer, int size, u8 isctrl); +//extern void uartadapter_test(void *param); +//extern void uartadapter_tcp_control_server_handler(void *param); +//extern void uartadapter_tcp_data_server_handler(void *param); +//extern void uartadapter_gpio_irq (uint32_t id, gpio_irq_event event); +void example_uart_adapter_init(); +extern void cmd_uart_adapter(int argc, char **argv); +//#define cmd_uart_adapter cmd_ua + diff --git a/component/common/drivers/wlan/realtek/include/autoconf.h b/component/common/drivers/wlan/realtek/include/autoconf.h new file mode 100644 index 0000000..d88f594 --- /dev/null +++ b/component/common/drivers/wlan/realtek/include/autoconf.h @@ -0,0 +1,347 @@ +#ifndef WLANCONFIG_H +#define WLANCONFIG_H + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ + #if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#include "platform_opts.h" +#endif + +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#define CONFIG_PLATFORM_AMEBA_X +#endif + +#if !defined(CONFIG_PLATFORM_AMEBA_X) +#define PLATFORM_FREERTOS 1 +#define CONFIG_GSPI_HCI +#else +#define CONFIG_LX_HCI +#endif + +#ifndef CONFIG_INIC_EN +#define CONFIG_INIC_EN 0 //For iNIC project +#if CONFIG_INIC_EN +#define CONFIG_LWIP_LAYER 0 +#endif +#endif + +#define CONFIG_LITTLE_ENDIAN +#define CONFIG_80211N_HT +//#define CONFIG_RECV_REORDERING_CTRL +#define RTW_NOTCH_FILTER 0 +#define CONFIG_EMBEDDED_FWIMG 1 +#define CONFIG_PHY_SETTING_WITH_ODM +#if !defined(CONFIG_PLATFORM_AMEBA_X) +#define CONFIG_ODM_REFRESH_RAMASK +#define HAL_MAC_ENABLE 1 +#define HAL_BB_ENABLE 1 +#define HAL_RF_ENABLE 1 +#endif +#if defined(CONFIG_PLATFORM_AMEBA_X) +/* Patch when dynamic mechanism is not ready */ +//#define CONFIG_DM_PATCH +#endif + +//#define CONFIG_DEBUG +//#define CONFIG_DEBUG_RTL871X +#if defined(CONFIG_PLATFORM_AMEBA_X) + #define CONFIG_MEM_MONITOR MEM_MONITOR_SIMPLE + #define WLAN_INTF_DBG 0 + #define CONFIG_DEBUG_DYNAMIC + //#define DBG_TX 1 + //#define DBG_XMIT_BUF 1 + //#define DBG_XMIT_BUF_EXT 1 + #define DBG_TX_DROP_FRAME + /* For DM debug*/ + #define DBG_PWR_TRACKING 0 + #define DBG_PWR_INDEX 0 + #define DBG_DM_RA 0 + #define DBG_DM_DIG 0 +#else + #define CONFIG_MEM_MONITOR MEM_MONITOR_LEAK + //#define CONFIG_TRACE_SKB + //#define WLAN_INTF_DBG +#endif // CONFIG_PLATFORM_AMEBA_X + +//#define CONFIG_DONT_CARE_TP +//#define CONFIG_MEMORY_ACCESS_ALIGNED +#define CONFIG_POWER_SAVING +#ifdef CONFIG_POWER_SAVING +#define CONFIG_LPS +#define CONFIG_IPS +//#define CONFIG_LPS_LCLK +#define CONFIG_WAIT_PS_ACK +#endif + +#if defined(CONFIG_PLATFORM_AMEBA_X) + #if !defined(CONFIG_PLATFORM_8711B) + #define CONFIG_USE_TCM_HEAP 1 /* USE TCM HEAP */ + #endif + #define CONFIG_RECV_TASKLET_THREAD + #define CONFIG_XMIT_TASKLET_THREAD +#else + #define CONFIG_XMIT_THREAD_MODE +#endif // CONFIG_PLATFORM_AMEBA_X +//#define CONFIG_RECV_THREAD_MODE /* Wlan IRQ Polling Mode*/ +//#define CONFIG_ISR_THREAD_MODE_POLLING /* Wlan IRQ Polling Mode*/ + +//1 Chris +#ifndef CONFIG_SDIO_HCI +#define CONFIG_ISR_THREAD_MODE_INTERRUPT /* Wlan IRQ Interrupt Mode*/ +#endif + +#if defined(CONFIG_ISR_THREAD_MODE_POLLING) && defined(CONFIG_ISR_THREAD_MODE_INTERRUPT) +#error "CONFIG_ISR_THREAD_MODE_POLLING and CONFIG_ISR_THREAD_MODE_INTERRUPT are mutually exclusive. " +#endif + +#if defined(CONFIG_PLATFORM_AMEBA_X) +/* CRC DMEM optimized mode consume 1k less SRM memory consumption */ +#define CRC_IMPLEMENTATION_MODE CRC_IMPLEMENTATION_DMEM_OPTIMIZED +#endif + +/* AES DMEM optimized mode comsume 10k less memory compare to + IMEM optimized mode AES_IMPLEMENTATION_IMEM_OPTIMIZED */ +#define AES_IMPLEMENTATION_MODE AES_IMPLEMENTATION_DMEM_OPTIMIZED + +#define USE_SKB_AS_XMITBUF 1 +#if defined(CONFIG_PLATFORM_AMEBA_X) +#define USE_XMIT_EXTBUFF 1 +#else +#define USE_XMIT_EXTBUFF 0 +#endif +#define USE_MUTEX_FOR_SPINLOCK 1 +#define SUPPORT_5G_CHANNEL 0 +#define SUPPORT_FAKE_EFUSE 0 + +#define CONFIG_AUTO_RECONNECT 1 +#define ENABLE_HWPDN_PIN +#define SUPPORT_SCAN_BUF 1 +#if !defined(CONFIG_PLATFORM_AMEBA_X) +#define BE_I_CUT 1 +#endif + +/* For WPA2 */ +#define CONFIG_INCLUDE_WPA_PSK +#ifdef CONFIG_INCLUDE_WPA_PSK +#define CONFIG_MULTIPLE_WPA_STA +//#define CONFIG_WPA2_PREAUTH +#define PSK_SUPPORT_TKIP 1 +#endif + +/* For promiscuous mode */ +#define CONFIG_PROMISC + +#define PROMISC_DENY_PAIRWISE 0 + +/* For Simple Link */ +#ifndef CONFIG_INCLUDE_SIMPLE_CONFIG +//#define CONFIG_INCLUDE_SIMPLE_CONFIG 1 +#endif + +// for probe request with custom vendor specific IE +#define CONFIG_CUSTOM_IE + +#if !defined(CONFIG_PLATFORM_AMEBA_X) +/* For multicast */ +#define CONFIG_MULTICAST +#endif + +/* For STA+AP Concurrent MODE */ +#if !defined(CONFIG_PLATFORM_8711B) +#define CONFIG_CONCURRENT_MODE +#endif +#ifdef CONFIG_CONCURRENT_MODE +#if defined(CONFIG_PLATFORM_8195A) +#define CONFIG_RUNTIME_PORT_SWITCH +#endif +#define NET_IF_NUM 2 +#else +#define NET_IF_NUM 1 +#endif + + +/* For WPS and P2P */ +#ifndef CONFIG_WPS +#define CONFIG_WPS +#if defined(CONFIG_WPS) +#define CONFIG_ENABLE_WPS 1 +#endif +#if 0//def CONFIG_WPS +#define CONFIG_WPS_AP +#define CONFIG_P2P_NEW +#if (!defined(SUPPORT_SCAN_BUF)||!defined(CONFIG_WPS_AP)) && defined(CONFIG_P2P_NEW) +#error "If CONFIG_P2P_NEW, need to SUPPORT_SCAN_BUF" +#endif +#endif +#endif + +#if !defined(CONFIG_PLATFORM_AMEBA_X) +#define CONFIG_NEW_SIGNAL_STAT_PROCESS +#endif + +/* For AP_MODE */ +#define CONFIG_AP_MODE +#if defined(CONFIG_PLATFORM_AMEBA_X) +#define AP_STA_NUM 3 //2014/10/27 modify to 3 +#define USE_DEDICATED_BCN_TX 0 +#if USE_DEDICATED_BCN_TX +#error "WLAN driver for Ameba should not enable USE_DEDICATED_BCN_TX" +#endif +#else +extern unsigned int g_ap_sta_num; +#define AP_STA_NUM g_ap_sta_num +#endif +#ifdef CONFIG_AP_MODE + #define CONFIG_NATIVEAP_MLME +#if defined(CONFIG_PLATFORM_AMEBA_X) + #define CONFIG_INTERRUPT_BASED_TXBCN +#endif + #ifdef CONFIG_INTERRUPT_BASED_TXBCN + //#define CONFIG_INTERRUPT_BASED_TXBCN_EARLY_INT + #define CONFIG_INTERRUPT_BASED_TXBCN_BCN_OK_ERR + #endif +// #define CONFIG_GK_REKEY +#if !defined(CONFIG_PLATFORM_AMEBA_X) + #define USE_DEDICATED_BCN_TX 1 +#endif +#else +#if !defined(CONFIG_PLATFORM_AMEBA_X) + #define USE_DEDICATED_BCN_TX 0 +#endif +#endif + +#if defined(CONFIG_AP_MODE) && defined(CONFIG_GK_REKEY) && !defined(CONFIG_MULTIPLE_WPA_STA) +#error "If CONFIG_GK_REKEY when CONFIG_AP_MODE, need to CONFIG_MULTIPLE_WPA_STA" +#endif + +#if !defined(CONFIG_PLATFORM_AMEBA_X) +#if !defined(CONFIG_AP_MODE) && defined(CONFIG_CONCURRENT_MODE) +#error "If CONFIG_CONCURRENT_MODEE, need to CONFIG_AP_MODE" +#endif +#endif + +/* For efuse or flash config */ +#if defined(CONFIG_PLATFORM_AMEBA_X) + #define CONFIG_RW_PHYSICAL_EFUSE 1 + #define CONFIG_HIDE_PROTECT_EFUSE 1 + #define CONFIG_ADAPTOR_INFO_CACHING_FLASH 1 + #define CHECK_FLASH_VALID_MASK 1 + /* For K-free */ + #if !defined(CONFIG_PLATFORM_8711B) + #define CONFIG_RF_GAIN_OFFSET + #endif +#endif // CONFIG_PLATFORM_AMEBA_X + +/* For MP_MODE */ +//#define CONFIG_MP_INCLUDED +#ifdef CONFIG_MP_INCLUDED + #define MP_DRIVER 1 + #define CONFIG_MP_IWPRIV_SUPPORT +// #define HAL_EFUSE_MEMORY + #if defined(CONFIG_PLATFORM_AMEBA_X) + #define MP_REG_TEST + #define MP_DISABLE_SDR + #endif +#else + #define MP_DRIVER 0 + #if defined(CONFIG_PLATFORM_AMEBA_X) + //Control wifi mcu function + #define CONFIG_LITTLE_WIFI_MCU_FUNCTION_THREAD + #define CONFIG_ODM_REFRESH_RAMASK + #endif +#endif // #ifdef CONFIG_MP_INCLUDED + +#if defined(CONFIG_PLATFORM_AMEBA_X) +#if defined(CONFIG_PLATFORM_8195A) +#define CONFIG_RTL8195A +#endif +#if defined(CONFIG_PLATFORM_8711B) +#define CONFIG_RTL8711B +#endif +#else +#define CONFIG_RTL8188E +#endif +#define RTL8192C_SUPPORT 0 +#define RTL8192CE_SUPPORT 0 +#define RTL8192CU_SUPPORT 0 +#define RTL8192D_SUPPORT 0 +#define RTL8192DE_SUPPORT 0 +#define RTL8192DU_SUPPORT 0 +#define RTL8723A_SUPPORT 0 +#define RTL8723AU_SUPPORT 0 +#define RTL8723AS_SUPPORT 0 +#define RTL8192E_SUPPORT 0 +#define RTL8812A_SUPPORT 0 +#define RTL8821A_SUPPORT 0 +#define RTL8723B_SUPPORT 0 +#if defined(CONFIG_PLATFORM_AMEBA_X) +#define RTL8195A_SUPPORT 1 +#define RTL8188E_SUPPORT 0 +#else +#define RTL8188E_SUPPORT 1 +#define RTL8195A_SUPPORT 0 +#endif +#define TEST_CHIP_SUPPORT 0 + +#define RTL8188E_FOR_TEST_CHIP 0 +#define RTL8188E_FPGA_TRUE_PHY_VERIFICATION 0 +#define DBG 0 + +/* For DM support */ +#define RATE_ADAPTIVE_SUPPORT 1 +#if defined(CONFIG_PLATFORM_AMEBA_X) + #define CONFIG_RTW_ADAPTIVITY_EN 0 + #define CONFIG_POWER_TRAINING_WIL 0 // in RA +#else + #define CONFIG_RTW_ADAPTIVITY_EN 1 + #define POWER_BY_RATE_SUPPORT 0 +#endif + +#if defined(CONFIG_PLATFORM_AMEBA_X) +#define RTL8195A_FOR_TEST_CHIP 0 + +//#define CONFIG_WIFI_TEST 1 +//#define CONFIG_MAC_LOOPBACK_DRIVER 1 +//#define CONFIG_WLAN_HAL_TEST 1 +//#define SKB_PRE_ALLOCATE_TX 1 +#define SKB_PRE_ALLOCATE_RX 1 +#define TX_CHECK_DSEC_ALWAYS 1 +#define EXCHANGE_LUBUX_RX_SKB 0 +#define CONFIG_DBG_DISABLE_RDU_INTERRUPT +//#define CONFIG_WLAN_HAL_RX_TASK + +//Enable mac loopback for test mode (Ameba) +//#define ENABLE_MAC_LB_FOR_TEST_MODE // for test mode +#ifdef ENABLE_MAC_LB_FOR_TEST_MODE + #define CONFIG_SUDO_PHY_SETTING + #define INT_HANDLE_IN_ISR 1 + #define CONFIG_LWIP_LAYER 0 + #define CONFIG_WLAN_HAL_TEST + #define CONFIG_WLAN_HAL_RX_TASK + #define CONFIG_MAC_LOOPBACK_DRIVER_RTL8195A 1 + //#define CONFIG_TWO_MAC_TEST_MODE + #define DISABLE_BB_RF 1 +#else + //#define CONFIG_TWO_MAC_DRIVER //for mornal driver; two mac + #ifdef CONFIG_TWO_MAC_DRIVER + #define CONFIG_SUDO_PHY_SETTING + #define DISABLE_BB_RF 1 + #else + #define HAL_MAC_ENABLE 1 + #define HAL_BB_ENABLE 1 + #define HAL_RF_ENABLE 1 + #define DISABLE_BB_RF 0 + #endif + //#define INT_HANDLE_IN_ISR 1 +#endif +#endif // CONFIG_PLATFORM_AMEBA_X + +#ifndef CONFIG_LWIP_LAYER +#define CONFIG_LWIP_LAYER 1 +#endif +#define CONFIG_MAC_ADDRESS 0 +//fast reconnection +//#define CONFIG_FAST_RECONNECTION 1 +#endif //WLANCONFIG_H diff --git a/component/common/drivers/wlan/realtek/include/drv_conf.h b/component/common/drivers/wlan/realtek/include/drv_conf.h new file mode 100644 index 0000000..115bb54 --- /dev/null +++ b/component/common/drivers/wlan/realtek/include/drv_conf.h @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#ifndef __DRV_CONF_H__ +#define __DRV_CONF_H__ + +#include "autoconf.h" +#if (RTL8195A_SUPPORT==1) +#include "platform_autoconf.h" +#endif + +#if defined (PLATFORM_LINUX) && defined (PLATFORM_WINDOWS) + +#error "Shall be Linux or Windows, but not both!\n" + +#endif + +//Older Android kernel doesn't has CONFIG_ANDROID defined, +//add this to force CONFIG_ANDROID defined +#ifdef CONFIG_PLATFORM_ANDROID +#define CONFIG_ANDROID +#endif + +#ifdef CONFIG_ANDROID +//Some Android build will restart the UI while non-printable ascii is passed +//between java and c/c++ layer (JNI). We force CONFIG_VALIDATE_SSID +//for Android here. If you are sure there is no risk on your system about this, +//mask this macro define to support non-printable ascii ssid. +//#define CONFIG_VALIDATE_SSID +#ifdef CONFIG_PLATFORM_ARM_SUNxI + #ifdef CONFIG_VALIDATE_SSID + #undef CONFIG_VALIDATE_SSID + #endif +#endif + +//Android expect dbm as the rx signal strength unit +#define CONFIG_SIGNAL_DISPLAY_DBM +#endif + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined (CONFIG_RESUME_IN_WORKQUEUE) + #warning "You have CONFIG_HAS_EARLYSUSPEND enabled in your system, we disable CONFIG_RESUME_IN_WORKQUEUE automatically" + #undef CONFIG_RESUME_IN_WORKQUEUE +#endif + +#if defined(CONFIG_ANDROID_POWER) && defined (CONFIG_RESUME_IN_WORKQUEUE) + #warning "You have CONFIG_ANDROID_POWER enabled in your system, we disable CONFIG_RESUME_IN_WORKQUEUE automatically" + #undef CONFIG_RESUME_IN_WORKQUEUE +#endif + +#ifdef CONFIG_RESUME_IN_WORKQUEUE //this can be removed, because there is no case for this... + #if !defined( CONFIG_WAKELOCK) && !defined(CONFIG_ANDROID_POWER) + #error "enable CONFIG_RESUME_IN_WORKQUEUE without CONFIG_WAKELOCK or CONFIG_ANDROID_POWER will suffer from the danger of wifi's unfunctionality..." + #error "If you still want to enable CONFIG_RESUME_IN_WORKQUEUE in this case, mask this preprossor checking and GOOD LUCK..." + #endif +#endif + +//About USB VENDOR REQ +#if defined(CONFIG_USB_VENDOR_REQ_BUFFER_PREALLOC) && !defined(CONFIG_USB_VENDOR_REQ_MUTEX) + #warning "define CONFIG_USB_VENDOR_REQ_MUTEX for CONFIG_USB_VENDOR_REQ_BUFFER_PREALLOC automatically" + #define CONFIG_USB_VENDOR_REQ_MUTEX +#endif +#if defined(CONFIG_VENDOR_REQ_RETRY) && !defined(CONFIG_USB_VENDOR_REQ_MUTEX) + #warning "define CONFIG_USB_VENDOR_REQ_MUTEX for CONFIG_VENDOR_REQ_RETRY automatically" + #define CONFIG_USB_VENDOR_REQ_MUTEX +#endif + +#ifndef CONFIG_RTW_ADAPTIVITY_EN + #define CONFIG_RTW_ADAPTIVITY_EN 0 +#endif + +#ifndef CONFIG_RTW_ADAPTIVITY_MODE + #define CONFIG_RTW_ADAPTIVITY_MODE 0 +#endif + +#ifndef CONFIG_RTW_ADAPTIVITY_DML + #define CONFIG_RTW_ADAPTIVITY_DML 0 +#endif + +#ifndef CONFIG_RTW_ADAPTIVITY_DC_BACKOFF + #define CONFIG_RTW_ADAPTIVITY_DC_BACKOFF 4 +#endif + +#ifndef CONFIG_RTW_NHM_EN + #define CONFIG_RTW_NHM_EN 0 +#endif + +//#include + +#endif // __DRV_CONF_H__ + diff --git a/component/common/drivers/wlan/realtek/include/wifi_constants.h b/component/common/drivers/wlan/realtek/include/wifi_constants.h new file mode 100644 index 0000000..6469a0f --- /dev/null +++ b/component/common/drivers/wlan/realtek/include/wifi_constants.h @@ -0,0 +1,221 @@ +#ifndef _WIFI_CONSTANTS_H +#define _WIFI_CONSTANTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define WEP_ENABLED 0x0001 +#define TKIP_ENABLED 0x0002 +#define AES_ENABLED 0x0004 +#define WSEC_SWFLAG 0x0008 + +#define SHARED_ENABLED 0x00008000 +#define WPA_SECURITY 0x00200000 +#define WPA2_SECURITY 0x00400000 +#define WPS_ENABLED 0x10000000 + +#define RTW_MAX_PSK_LEN (64) +#define RTW_MIN_PSK_LEN (8) + +#define MCSSET_LEN 16 + +typedef enum +{ + RTW_SUCCESS = 0, /**< Success */ + RTW_PENDING = 1, /**< Pending */ + RTW_TIMEOUT = 2, /**< Timeout */ + RTW_PARTIAL_RESULTS = 3, /**< Partial results */ + RTW_INVALID_KEY = 4, /**< Invalid key */ + RTW_DOES_NOT_EXIST = 5, /**< Does not exist */ + RTW_NOT_AUTHENTICATED = 6, /**< Not authenticated */ + RTW_NOT_KEYED = 7, /**< Not keyed */ + RTW_IOCTL_FAIL = 8, /**< IOCTL fail */ + RTW_BUFFER_UNAVAILABLE_TEMPORARY = 9, /**< Buffer unavailable temporarily */ + RTW_BUFFER_UNAVAILABLE_PERMANENT = 10, /**< Buffer unavailable permanently */ + RTW_WPS_PBC_OVERLAP = 11, /**< WPS PBC overlap */ + RTW_CONNECTION_LOST = 12, /**< Connection lost */ + + RTW_ERROR = -1, /**< Generic Error */ + RTW_BADARG = -2, /**< Bad Argument */ + RTW_BADOPTION = -3, /**< Bad option */ + RTW_NOTUP = -4, /**< Not up */ + RTW_NOTDOWN = -5, /**< Not down */ + RTW_NOTAP = -6, /**< Not AP */ + RTW_NOTSTA = -7, /**< Not STA */ + RTW_BADKEYIDX = -8, /**< BAD Key Index */ + RTW_RADIOOFF = -9, /**< Radio Off */ + RTW_NOTBANDLOCKED = -10, /**< Not band locked */ + RTW_NOCLK = -11, /**< No Clock */ + RTW_BADRATESET = -12, /**< BAD Rate valueset */ + RTW_BADBAND = -13, /**< BAD Band */ + RTW_BUFTOOSHORT = -14, /**< Buffer too short */ + RTW_BUFTOOLONG = -15, /**< Buffer too long */ + RTW_BUSY = -16, /**< Busy */ + RTW_NOTASSOCIATED = -17, /**< Not Associated */ + RTW_BADSSIDLEN = -18, /**< Bad SSID len */ + RTW_OUTOFRANGECHAN = -19, /**< Out of Range Channel */ + RTW_BADCHAN = -20, /**< Bad Channel */ + RTW_BADADDR = -21, /**< Bad Address */ + RTW_NORESOURCE = -22, /**< Not Enough Resources */ + RTW_UNSUPPORTED = -23, /**< Unsupported */ + RTW_BADLEN = -24, /**< Bad length */ + RTW_NOTREADY = -25, /**< Not Ready */ + RTW_EPERM = -26, /**< Not Permitted */ + RTW_NOMEM = -27, /**< No Memory */ + RTW_ASSOCIATED = -28, /**< Associated */ + RTW_RANGE = -29, /**< Not In Range */ + RTW_NOTFOUND = -30, /**< Not Found */ + RTW_WME_NOT_ENABLED = -31, /**< WME Not Enabled */ + RTW_TSPEC_NOTFOUND = -32, /**< TSPEC Not Found */ + RTW_ACM_NOTSUPPORTED = -33, /**< ACM Not Supported */ + RTW_NOT_WME_ASSOCIATION = -34, /**< Not WME Association */ + RTW_SDIO_ERROR = -35, /**< SDIO Bus Error */ + RTW_WLAN_DOWN = -36, /**< WLAN Not Accessible */ + RTW_BAD_VERSION = -37, /**< Incorrect version */ + RTW_TXFAIL = -38, /**< TX failure */ + RTW_RXFAIL = -39, /**< RX failure */ + RTW_NODEVICE = -40, /**< Device not present */ + RTW_UNFINISHED = -41, /**< To be finished */ + RTW_NONRESIDENT = -42, /**< access to nonresident overlay */ + RTW_DISABLED = -43 /**< Disabled in this build */ +} rtw_result_t; + +typedef enum { + RTW_SECURITY_OPEN = 0, /**< Open security */ + RTW_SECURITY_WEP_PSK = WEP_ENABLED, /**< WEP Security with open authentication */ + RTW_SECURITY_WEP_SHARED = ( WEP_ENABLED | SHARED_ENABLED ), /**< WEP Security with shared authentication */ + RTW_SECURITY_WPA_TKIP_PSK = ( WPA_SECURITY | TKIP_ENABLED ), /**< WPA Security with TKIP */ + RTW_SECURITY_WPA_AES_PSK = ( WPA_SECURITY | AES_ENABLED ), /**< WPA Security with AES */ + RTW_SECURITY_WPA2_AES_PSK = ( WPA2_SECURITY | AES_ENABLED ), /**< WPA2 Security with AES */ + RTW_SECURITY_WPA2_TKIP_PSK = ( WPA2_SECURITY | TKIP_ENABLED ), /**< WPA2 Security with TKIP */ + RTW_SECURITY_WPA2_MIXED_PSK = ( WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED ), /**< WPA2 Security with AES & TKIP */ + + RTW_SECURITY_WPS_OPEN = WPS_ENABLED, /**< WPS with open security */ + RTW_SECURITY_WPS_SECURE = (WPS_ENABLED | AES_ENABLED), /**< WPS with AES security */ + + RTW_SECURITY_UNKNOWN = -1, /**< May be returned by scan function if security is unknown. Do not pass this to the join function! */ + + RTW_SECURITY_FORCE_32_BIT = 0x7fffffff /**< Exists only to force rtw_security_t type to 32 bits */ +} rtw_security_t; + +typedef enum { + RTW_ENCRYPTION_UNKNOWN = 0, + RTW_ENCRYPTION_OPEN = 1, + RTW_ENCRYPTION_WEP40 = 2, + RTW_ENCRYPTION_WPA_TKIP = 3, + RTW_ENCRYPTION_WPA_AES = 4, + RTW_ENCRYPTION_WPA2_TKIP = 5, + RTW_ENCRYPTION_WPA2_AES = 6, + RTW_ENCRYPTION_WPA2_MIXED = 7, + RTW_ENCRYPTION_WEP104 = 9, + RTW_ENCRYPTION_UNDEF = 0xFF, +} rtw_encryption_t; + +typedef enum { + RTW_FALSE = 0, + RTW_TRUE = 1 +} rtw_bool_t; + +typedef enum { + RTW_802_11_BAND_5GHZ = 0, /**< Denotes 5GHz radio band */ + RTW_802_11_BAND_2_4GHZ = 1 /**< Denotes 2.4GHz radio band */ +} rtw_802_11_band_t; + +typedef enum { + RTW_COUNTRY_US = 0, + RTW_COUNTRY_EU, + RTW_COUNTRY_JP, + RTW_COUNTRY_CN +}rtw_country_code_t; + +typedef enum { + RTW_MODE_NONE = 0, + RTW_MODE_STA, + RTW_MODE_AP, + RTW_MODE_STA_AP, + RTW_MODE_PROMISC, + RTW_MODE_P2P +}rtw_mode_t; + +typedef enum { + RTW_SCAN_FULL = 0, + RTW_SCAN_SOCIAL, + RTW_SCAN_ONE +}rtw_scan_mode_t; + +typedef enum { + RTW_LINK_DISCONNECTED = 0, + RTW_LINK_CONNECTED +} rtw_link_status_t; + +typedef enum { + RTW_SCAN_TYPE_ACTIVE = 0x00, /**< Actively scan a network by sending 802.11 probe(s) */ + RTW_SCAN_TYPE_PASSIVE = 0x01, /**< Passively scan a network by listening for beacons from APs */ + RTW_SCAN_TYPE_PROHIBITED_CHANNELS = 0x04 /**< Passively scan on channels not enabled by the country code */ +} rtw_scan_type_t; + +typedef enum { + RTW_BSS_TYPE_INFRASTRUCTURE = 0, /**< Denotes infrastructure network */ + RTW_BSS_TYPE_ADHOC = 1, /**< Denotes an 802.11 ad-hoc IBSS network */ + RTW_BSS_TYPE_ANY = 2, /**< Denotes either infrastructure or ad-hoc network */ + + RTW_BSS_TYPE_UNKNOWN = -1 /**< May be returned by scan function if BSS type is unknown. Do not pass this to the Join function */ +} rtw_bss_type_t; + +typedef enum { + RTW_SCAN_COMMAMD = 0x01 +} rtw_scan_command_t; + +typedef enum{ + COMMAND1 = 0x01 +}rtw_command_type; + +typedef enum { + RTW_WPS_TYPE_DEFAULT = 0x0000, + RTW_WPS_TYPE_USER_SPECIFIED = 0x0001, + RTW_WPS_TYPE_MACHINE_SPECIFIED = 0x0002, + RTW_WPS_TYPE_REKEY = 0x0003, + RTW_WPS_TYPE_PUSHBUTTON = 0x0004, + RTW_WPS_TYPE_REGISTRAR_SPECIFIED = 0x0005, + RTW_WPS_TYPE_NONE = 0x0006 +} rtw_wps_type_t; + +typedef enum { + RTW_NETWORK_B = 1, + RTW_NETWORK_BG = 3, + RTW_NETWORK_BGN = 11 +} rtw_network_mode_t; + +typedef enum { + RTW_STA_INTERFACE = 0, /**< STA or Client Interface */ + RTW_AP_INTERFACE = 1, /**< softAP Interface */ +} rtw_interface_t; + +/** + * Enumeration of packet filter rules + */ +typedef enum { + RTW_POSITIVE_MATCHING = 0, /**< Specifies that a filter should match a given pattern */ + RTW_NEGATIVE_MATCHING = 1 /**< Specifies that a filter should NOT match a given pattern */ +} rtw_packet_filter_rule_e; + +typedef enum { + RTW_PROMISC_DISABLE = 0, /**< disable the promisc */ + RTW_PROMISC_ENABLE = 1, /**< enable the promisc */ + RTW_PROMISC_ENABLE_1 = 2, /**< enable the promisc special for length is used */ + RTW_PROMISC_ENABLE_2 = 3, /**< fetch all packets*/ +} rtw_rcr_level_t; + +typedef enum{ + RTW_NO_ERROR = 0, + RTW_NONE_NETWORK = 1, + RTW_CONNECT_FAIL = 2, + RTW_WRONG_PASSWORD = 3 , + RTW_DHCP_FAIL = 4, +}rtw_connect_error_flag_t; + +#ifdef __cplusplus +} +#endif +#endif /* _WIFI_CONSTANTS_H */ diff --git a/component/common/drivers/wlan/realtek/include/wifi_structures.h b/component/common/drivers/wlan/realtek/include/wifi_structures.h new file mode 100644 index 0000000..079f1b6 --- /dev/null +++ b/component/common/drivers/wlan/realtek/include/wifi_structures.h @@ -0,0 +1,134 @@ +#ifndef _WIFI_STRUCTURES_H +#define _WIFI_STRUCTURES_H + +//#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct rtw_ssid { + unsigned char len; /**< SSID length */ + unsigned char val[33]; /**< SSID name (AP name) */ +} rtw_ssid_t; + +typedef struct rtw_mac { + unsigned char octet[6]; /**< Unique 6-byte MAC address */ +} rtw_mac_t; + +typedef struct rtw_ap_info { + rtw_ssid_t ssid; + rtw_security_t security_type; + unsigned char *password; + int password_len; + int channel; +}rtw_ap_info_t; + +typedef struct rtw_network_info { + rtw_ssid_t ssid; + rtw_mac_t bssid; + rtw_security_t security_type; + unsigned char *password; + int password_len; + int key_id; +}rtw_network_info_t; + +typedef struct rtw_scan_result { + rtw_ssid_t SSID; /**< Service Set Identification (i.e. Name of Access Point) */ + rtw_mac_t BSSID; /**< Basic Service Set Identification (i.e. MAC address of Access Point) */ + signed short signal_strength; /**< Receive Signal Strength Indication in dBm. <-90=Very poor, >-30=Excellent */ + rtw_bss_type_t bss_type; /**< Network type */ + rtw_security_t security; /**< Security type */ + rtw_wps_type_t wps_type; /**< WPS type */ + unsigned char channel; /**< Radio channel that the AP beacon was received on */ + rtw_802_11_band_t band; /**< Radio band */ +} rtw_scan_result_t; + +typedef struct rtw_scan_handler_result { + rtw_scan_result_t ap_details; + rtw_bool_t scan_complete; + void* user_data; + +} rtw_scan_handler_result_t; + +typedef struct rtw_wifi_setting { + rtw_mode_t mode; + unsigned char ssid[33]; + unsigned char channel; + rtw_security_t security_type; + unsigned char password[65]; + unsigned char key_idx; +}rtw_wifi_setting_t; + +typedef struct rtw_wifi_config { + unsigned int boot_mode; + unsigned char ssid[32]; + unsigned char ssid_len; + unsigned char security_type; + unsigned char password[65]; + unsigned char password_len; + unsigned char channel; +} rtw_wifi_config_t; + +typedef struct +{ + unsigned int count; /**< Number of MAC addresses in the list */ + rtw_mac_t mac_list[1]; /**< Variable length array of MAC addresses */ +} rtw_maclist_t; + +typedef struct { + unsigned int version; /* version field */ + unsigned int length; /* byte length of data in this record, */ + /* starting at version and including IEs */ + rtw_mac_t BSSID; + unsigned short beacon_period; /* units are Kusec */ + unsigned short capability; /* Capability information */ + unsigned char SSID_len; + unsigned char SSID[32]; + unsigned char channel; +// struct { +// uint32_t count; /* # rates in this set */ +// uint8_t rates[16]; /* rates in 500kbps units w/hi bit set if basic */ +// } rateset; /* supported rates */ +// rtw_chanspec_t chanspec; /* chanspec for bss */ + unsigned short atim_window; /* units are Kusec */ + unsigned char dtim_period; /* DTIM period */ + signed short RSSI; /* receive signal strength (in dBm) */ + + unsigned char n_cap; /* BSS is 802.11N Capable */ + unsigned int nbss_cap; /* 802.11N BSS Capabilities (based on HT_CAP_*) */ + unsigned char basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + + unsigned short ie_offset; /* offset at which IEs start, from beginning */ + unsigned int ie_length; /* byte length of Information Elements */ +} rtw_bss_info_t; + +typedef struct { + unsigned short offset; /**< Offset in bytes to start filtering (referenced to the start of the ethernet packet) */ + unsigned short mask_size; /**< Size of the mask in bytes */ + unsigned char* mask; /**< Pattern mask bytes to be ANDed with the pattern eg. "\xff00" (must be in network byte order) */ + unsigned char* pattern; /**< Pattern bytes used to filter eg. "\x0800" (must be in network byte order) */ +} rtw_packet_filter_pattern_t; + +typedef struct ieee80211_frame_info{ + unsigned short i_fc; + unsigned short i_dur; + unsigned char i_addr1[6]; + unsigned char i_addr2[6]; + unsigned char i_addr3[6]; + unsigned short i_seq; + unsigned char bssid[6]; + unsigned char encrypt; +}ieee80211_frame_info_t; + +typedef struct { + char filter_id; + rtw_packet_filter_pattern_t patt; + rtw_packet_filter_rule_e rule; + unsigned char enable; +}rtw_packet_filter_info_t; +#ifdef __cplusplus +} +#endif + +#endif /* _WIFI_STRUCTURES_H */ diff --git a/component/common/drivers/wlan/realtek/src/osdep/freertos/freertos_service.h b/component/common/drivers/wlan/realtek/src/osdep/freertos/freertos_service.h new file mode 100644 index 0000000..be7b54f --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/freertos/freertos_service.h @@ -0,0 +1,285 @@ +#ifndef _FREERTOS_SERVICE_H_ +#define _FREERTOS_SERVICE_H_ + +//----- ------------------------------------------------------------------ +// Include Files +//----- ------------------------------------------------------------------ +#include "wireless.h" + +// -------------------------------------------- +// Platform dependent include file +// -------------------------------------------- +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#include "platform/platform_stdlib.h" +extern VOID RtlUdelayOS(u32 us); +#else +// other MCU may use standard library +#include +#endif + + +#if (defined CONFIG_GSPI_HCI || defined CONFIG_SDIO_HCI) || defined(CONFIG_LX_HCI) +/* For SPI interface transfer and us delay implementation */ +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +#include +#endif +#endif + + +// -------------------------------------------- +// Platform dependent type define +// -------------------------------------------- +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; +typedef unsigned long long u64; +typedef unsigned int uint; +typedef signed int sint; + +#ifndef bool +typedef int bool; +#define true 1 +#define false 0 +#endif + +#define IN +#define OUT +#define VOID void +#define NDIS_OID uint +#define NDIS_STATUS uint +#ifndef PVOID +typedef void * PVOID; +#endif + +typedef unsigned int __kernel_size_t; +typedef int __kernel_ssize_t; +typedef __kernel_size_t SIZE_T; +typedef __kernel_ssize_t SSIZE_T; + +#endif //CONFIG_PLATFORM_8195A + +#define FIELD_OFFSET(s,field) ((SSIZE_T)&((s*)(0))->field) + +// os types +typedef char osdepCHAR; +typedef float osdepFLOAT; +typedef double osdepDOUBLE; +typedef long osdepLONG; +typedef short osdepSHORT; +typedef unsigned long osdepSTACK_TYPE; +typedef long osdepBASE_TYPE; +typedef unsigned long osdepTickType; + +typedef void* _timerHandle; +typedef void* _sema; +typedef void* _mutex; +typedef void* _lock; +typedef void* _queueHandle; +typedef void* _xqueue; +typedef struct timer_list _timer; + +typedef struct sk_buff _pkt; +typedef unsigned char _buffer; + +struct list_head { + struct list_head *next, *prev; +}; + +struct __queue { + struct list_head queue; + _lock lock; +}; + +typedef struct __queue _queue; +typedef struct list_head _list; +typedef unsigned long _irqL; + +typedef void* _thread_hdl_; +typedef void thread_return; +typedef void* thread_context; + +#define ATOMIC_T atomic_t +#define HZ configTICK_RATE_HZ + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +/* emulate a modern version */ +#define LINUX_VERSION_CODE KERNEL_VERSION(2, 6, 17) + +static __inline _list *get_next(_list *list) +{ + return list->next; +} + +static __inline _list *get_list_head(_queue *queue) +{ + return (&(queue->queue)); +} + +#define LIST_CONTAINOR(ptr, type, member) \ + ((type *)((char *)(ptr)-(SIZE_T)((char *)&((type *)ptr)->member - (char *)ptr))) +#define container_of(p,t,n) (t*)((p)-&(((t*)0)->n)) + +#define TASK_PRORITY_LOW 1 +#define TASK_PRORITY_MIDDLE 2 +#define TASK_PRORITY_HIGH 3 +#define TASK_PRORITY_SUPER 4 + +#define TIMER_MAX_DELAY 0xFFFFFFFF + +// rtl8195a uses receive_tasklet for wps +// 8189em uses interrupt_thread for wps +#if defined(CONFIG_WPS) +#define RECV_STACK_FOR_WPS 448//512//384 //Change to 512 for WPS (IAR STM32) stack overflow +#else +#define RECV_STACK_FOR_WPS 0 +#endif + +#ifdef CONFIG_DONT_CARE_TP +#define XMIT_STACKSIZE 192 //256 +#define CMD_STACKSIZE 384 //512 +#else +#define XMIT_STACKSIZE 256 +#define CMD_STACKSIZE 512 //1024 +#endif //CONFIG_DONT_CARE_TP + +#ifdef CONFIG_PLATFORM_8195A +#define RECV_STACKSIZE 256 +#else //CONFIG_PLATFORM_8195A +#ifdef CONFIG_INCLUDE_WPA_PSK +#if PSK_SUPPORT_TKIP +#define RECV_STACKSIZE (512 + 256 + 128 + RECV_STACK_FOR_WPS) +#else +#define RECV_STACKSIZE (512 + 256 + RECV_STACK_FOR_WPS ) +#endif +#else +#define RECV_STACKSIZE (512 + 256 + RECV_STACK_FOR_WPS) //Can be reduced +#endif +#endif //CONFIG_PLATFORM_8195A + +#define XMIT_TASKLET_STACKSIZE 256 +#define RECV_TASKLET_STACKSIZE (1024 + RECV_STACK_FOR_WPS) +#define SDIOXMIT_STACKSIZE 256 + + +struct rtw_netdev_priv_indicator { + void *priv; + u32 sizeof_priv; +}; + +#define rtw_netdev_priv(netdev) ( ((struct rtw_netdev_priv_indicator *)netdev_priv(netdev))->priv ) + +#define ADPT_FMT "%s" +#define ADPT_ARG(adapter) adapter->pnetdev->name +#define FUNC_NDEV_FMT "%s" +#define FUNC_NDEV_ARG(ndev) __func__ +#define FUNC_ADPT_FMT "%s(%s)" +#define FUNC_ADPT_ARG(adapter) __func__, adapter->pnetdev->name + +void save_and_cli(void); +void restore_flags(void); +void cli(void); + +//----- ------------------------------------------------------------------ +// Common Definition +//----- ------------------------------------------------------------------ + +#define __init +#define __exit +#define __devinit +#define __devexit + +#define KERN_ERR +#define KERN_INFO +#define KERN_NOTICE + +#define GFP_KERNEL 1 +#define GFP_ATOMIC 1 + +#define SET_MODULE_OWNER(some_struct) do { } while (0) +#define SET_NETDEV_DEV(dev, obj) do { } while (0) +#define register_netdev(dev) (0) +#define unregister_netdev(dev) do { } while (0) +#define netif_queue_stopped(dev) (0) +#define netif_wake_queue(dev) do { } while (0) +#define printk printf + +#define DBG_ERR(fmt, args...) printf("\n\r[%s] " fmt, __FUNCTION__, ## args) +#if WLAN_INTF_DBG +#define DBG_TRACE(fmt, args...) printf("\n\r[%s] " fmt, __FUNCTION__, ## args) +#define DBG_INFO(fmt, args...) printf("\n\r[%s] " fmt, __FUNCTION__, ## args) +#else +#define DBG_TRACE(fmt, args...) +#define DBG_INFO(fmt, args...) +#endif +#define HALT() do { cli(); for(;;);} while(0) +#define ASSERT(x) do { \ + if((x) == 0) \ + printf("\n\rAssert(" #x ") failed on line %d in file %s", __LINE__, __FILE__); \ + HALT(); \ + } while(0) + +#undef DBG_ASSERT +#define DBG_ASSERT(x, msg) do { \ + if((x) == 0) \ + printf("\n\r%s, Assert(" #x ") failed on line %d in file %s", msg, __LINE__, __FILE__); \ + } while(0) + +//----- ------------------------------------------------------------------ +// Atomic Operation +//----- ------------------------------------------------------------------ +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) // for 8195A, it is defined in ..system../basic_types.h +typedef struct { volatile int counter; } atomic_t; +#endif + + +/* + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/* + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) ((v)->counter = (i)) + + /* + * These inlines deal with timer wrapping correctly. You are + * strongly encouraged to use them + * 1. Because people otherwise forget + * 2. Because if the timer wrap changes in future you wont have to + * alter your driver code. + * + * time_after(a,b) returns true if the time a is after time b. + * + * Do this with "<0" and ">=0" to only test the sign of the result. A + * good compiler would generate better code (and a really good compiler + * wouldn't care). Gcc is currently neither. + */ + #define time_after(a,b) ((long)(b) - (long)(a) < 0) + #define time_before(a,b) time_after(b,a) + + #define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) + #define time_before_eq(a,b) time_after_eq(b,a) + + +extern void rtw_init_listhead(_list *list); +extern u32 rtw_is_list_empty(_list *phead); +extern void rtw_list_insert_head(_list *plist, _list *phead); +extern void rtw_list_insert_tail(_list *plist, _list *phead); +extern void rtw_list_delete(_list *plist); + +#endif /* _FREERTOS_SERVICE_H_ */ diff --git a/component/common/drivers/wlan/realtek/src/osdep/freertos/wrapper.h b/component/common/drivers/wlan/realtek/src/osdep/freertos/wrapper.h new file mode 100644 index 0000000..1d8cce1 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/freertos/wrapper.h @@ -0,0 +1,432 @@ +#ifndef __WRAPPER_H__ +#define __WRAPPER_H__ +/************************************************************************** + * Wrapper provide a linux-like interface + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + ************************************************************************/ + +//----- ------------------------------------------------------------------ +// Include Files +//----- ------------------------------------------------------------------ +#include +#include +#include "wireless.h" +#include +#include "freertos_service.h" + + +//----- ------------------------------------------------------------------ +// Linled List +//----- ------------------------------------------------------------------ +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +// struct list_head { +// struct list_head *next, *prev; +// }; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +void list_add(struct list_head *new, struct list_head *head); +void list_add_tail(struct list_head *new, struct list_head *head); + +extern void save_and_cli(void); +extern void restore_flags(void); +//----- ------------------------------------------------------------------ +// SKB Operation +//----- ------------------------------------------------------------------ + +#define SMP_CACHE_BYTES 4 +#define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & ~(SMP_CACHE_BYTES - 1)) + +// Consideration for SKB size +// Tx: [INTF_CMD][TX_DESC][WLAN_HDR][QoS][IV][SNAP][Data][MIC][ICV][INTF_STATUS] +// Since SKB is used to accept ethernet packet from upper layer, SKB length of WLAN_MAX_ETHFRM_LEN +// (= 1514) is enough. But since SKB is also used to get spi receive packet, overall buffer space +// should be taken into consideration. +// RX: [INTF_CMD][RX_DESC][Drv_Info][WLAN_HDR][QoS][IV][SNAP][Data][MIC][ICV][CRC][INTF_STATUS] +// +// 32: Driver_Info that carry phy related information for each packets. Required only for receive case. +// WLAN_MAX_ETHFRM_LEN : May not be required because WLAN_HEADER +SNAP can totally +// cover ethernet header. Keep in only for safety. +// +// **Notes** SDIO requires 512 blocks r/w, so 512*4 = 2048 is required. +// 2003/12/26. The value is reduced from 2048 to 1658 for GSPI +// 2014/02/05. The value is 1650 for 8195A LX_BUS +#define SKB_RESERVED_FOR_SAFETY 0 +#define SKB_WLAN_TX_EXTRA_LEN (TXDESC_SIZE + WLAN_HDR_A4_QOS_LEN + WLAN_MAX_IV_LEN + WLAN_SNAP_HEADER - WLAN_ETHHDR_LEN) +#define RX_DRIVER_INFO 32 + +#if (defined CONFIG_GSPI_HCI || defined CONFIG_SDIO_HCI) +#define HAL_INTERFACE_OVERHEAD_SKB_DATA 12 //HAL_INTERFACE_CMD (4) + HAL_INTERFACE_STATUS (8) +#elif defined(CONFIG_LX_HCI) +#define HAL_INTERFACE_OVERHEAD_SKB_DATA 0 +#endif + +#if defined CONFIG_GSPI_HCI || defined CONFIG_SDIO_HCI || defined(CONFIG_LX_HCI) + #if defined(CONFIG_RTL8195A) || defined(CONFIG_RTL8711B) + #if defined(CONFIG_MP_INCLUDED) + #ifdef CONFIG_DONT_CARE_TP + #define MAX_RX_PKT_LIMIT ((WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_RX_ETHFRM_LEN + 511) / 512) // 4, for lxbus + #else + #define MAX_RX_PKT_LIMIT ((WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_ETHFRM_LEN + 511) / 512) // 4, for lxbus + #endif + #define MAX_RX_PKT_SIZE MAX_RX_PKT_LIMIT*512 // MAX_SKB_BUF_SIZE = 0+32+40+512*4+0 = 2120 + #else + #define MAX_RX_PKT_LIMIT 4 // MAX_SKB_BUF_SIZE = 0+32+40+64+1514+0 = 1650 + #ifdef CONFIG_DONT_CARE_TP + #define MAX_RX_PKT_SIZE WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_RX_ETHFRM_LEN + #else + #define MAX_RX_PKT_SIZE WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_ETHFRM_LEN + #endif + #endif + #else + #ifdef CONFIG_DONT_CARE_TP + #define MAX_RX_PKT_SIZE WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_RX_ETHFRM_LEN + #else + #define MAX_RX_PKT_SIZE WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_ETHFRM_LEN + #endif + #endif + + #ifdef CONFIG_DONT_CARE_TP + #define MAX_TX_SKB_BUF_SIZE (HAL_INTERFACE_OVERHEAD_SKB_DATA+RX_DRIVER_INFO+\ + ((TXDESC_SIZE>RXDESC_SIZE)? TXDESC_SIZE:RXDESC_SIZE) +\ + WLAN_MAX_PROTOCOL_OVERHEAD + WLAN_MAX_TX_ETHFRM_LEN +\ + SKB_RESERVED_FOR_SAFETY) + #define MAX_RX_SKB_BUF_SIZE (HAL_INTERFACE_OVERHEAD_SKB_DATA+RX_DRIVER_INFO+\ + ((TXDESC_SIZE>RXDESC_SIZE)? TXDESC_SIZE:RXDESC_SIZE) +\ + MAX_RX_PKT_SIZE +\ + SKB_RESERVED_FOR_SAFETY) + #else + #define MAX_SKB_BUF_SIZE (HAL_INTERFACE_OVERHEAD_SKB_DATA+RX_DRIVER_INFO+\ + ((TXDESC_SIZE>RXDESC_SIZE)? TXDESC_SIZE:RXDESC_SIZE) +\ + MAX_RX_PKT_SIZE +\ + SKB_RESERVED_FOR_SAFETY) + #endif +#else +#define MAX_SKB_BUF_SIZE 2048 +#endif + +#if 0 +struct sk_buff_head { + struct list_head *next, *prev; + u32 qlen; +}; + +struct sk_buff { + /* These two members must be first. */ + struct sk_buff *next; /* Next buffer in list */ + struct sk_buff *prev; /* Previous buffer in list */ + + struct sk_buff_head *list; /* List we are on */ + unsigned char *head; /* Head of buffer */ + unsigned char *data; /* Data head pointer */ + unsigned char *tail; /* Tail pointer */ + unsigned char *end; /* End pointer */ + struct net_device *dev; /* Device we arrived on/are leaving by */ + unsigned int len; /* Length of actual data */ +}; + +/** + * skb_put - add data to a buffer + * @skb: buffer to use + * @len: amount of data to add + * + * This function extends the used data area of the buffer. If this would + * exceed the total buffer size the kernel will panic. A pointer to the + * first byte of the extra data is returned. + */ + +static __inline__ unsigned char *skb_put(struct sk_buff *skb, unsigned int len) +{ + unsigned char *tmp=skb->tail; + skb->tail+=len; + skb->len+=len; + if(skb->tail>skb->end) { + ASSERT(0); + } + + return tmp; +} + +static __inline__ unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) +{ + skb->len-=len; + skb->data = (unsigned char *)(((unsigned int)skb->data) + len); + + return skb->data; +} + +/** + * skb_reserve - adjust headroom + * @skb: buffer to alter + * @len: bytes to move + * + * Increase the headroom of an empty &sk_buff by reducing the tail + * room. This is only allowed for an empty buffer. + */ + +static __inline__ void skb_reserve(struct sk_buff *skb, unsigned int len) +{ + skb->data+=len; + skb->tail+=len; +} + +static __inline__ void skb_queue_head_init(struct sk_buff_head *list) +{ + list->prev = (struct list_head *)list; + list->next = (struct list_head *)list; + list->qlen = 0; +} + +/** + * __skb_queue_tail - queue a buffer at the list tail + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the end of a list. This function takes no locks + * and you must therefore hold required locks before calling it. + * + * A buffer cannot be placed on two lists at the same time. + */ + +static __inline__ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) +{ + struct sk_buff *prev, *next; + + newsk->list = list; + list->qlen++; + next = (struct sk_buff *)list; + prev = next->prev; + newsk->next = next; + newsk->prev = prev; + next->prev = newsk; + prev->next = newsk; +} + +/** + * skb_queue_tail - queue a buffer at the list tail + * @list: list to use + * @newsk: buffer to queue + * + * Queue a buffer at the tail of the list. This function takes the + * list lock and can be used safely with other locking &sk_buff functions + * safely. + * + * A buffer cannot be placed on two lists at the same time. + */ + +static __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) +{ + save_and_cli(); + __skb_queue_tail(list, newsk); + restore_flags(); +} + +static __inline__ void skb_assign_buf(struct sk_buff *skb, unsigned char *buf, unsigned int len) +{ + skb->head = buf; + skb->data = buf; + skb->tail = buf; + skb->end = buf + len; +} + +static __inline__ unsigned char *skb_tail_pointer(const struct sk_buff *skb) +{ + return skb->tail; +} + +static __inline__ void skb_reset_tail_pointer(struct sk_buff *skb) +{ + skb->tail = skb->data; +} + +static __inline__ void skb_set_tail_pointer(struct sk_buff *skb, const int offset) +{ + skb->tail = skb->data + offset; +} + +static __inline__ unsigned char *skb_end_pointer(const struct sk_buff *skb) +{ + return skb->end; +} +#endif +/* + * External functions + */ +struct net_device; +extern void kfree_skb_chk_key(struct sk_buff *skb, struct net_device *root_dev); +#ifdef CONFIG_TRACE_SKB +extern void show_skb(void); +extern int _set_skb_list_flag(struct sk_buff *skb, unsigned int queueflag); +extern void dump_skb_list(void); +#define set_skb_list_flag(skb, queueflag) \ + (\ + _set_skb_list_flag((skb), queueflag), \ + (skb) ? (skb)->funcname[(skb)->list_idx] = __FUNCTION__:NULL \ + ) +extern int _clear_skb_list_flag(struct sk_buff *skb, unsigned int queueflag); +#define clear_skb_list_flag(skb, queueflag) \ + (\ + _clear_skb_list_flag((skb), queueflag), \ + (skb) ? (skb)->funcname[(skb)->list_idx] = __FUNCTION__ : NULL \ + ) +#define dev_kfree_skb_any(trx, holder, skb) \ + do{\ + clear_skb_list_flag(skb, SKBLIST_##trx##holder##_MASK);\ + set_skb_list_flag(skb, SKBLIST_POOL);\ + kfree_skb_chk_key(skb, skb->dev);\ + }while (0) +#else +#define dev_kfree_skb_any(skb) kfree_skb_chk_key(skb, skb->dev) +#endif +extern struct sk_buff *dev_alloc_skb(unsigned int length, unsigned int reserve_len); +extern struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask); +extern struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask, unsigned int reserve_len); +extern unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); + +//----- ------------------------------------------------------------------ +// Device structure +//----- ------------------------------------------------------------------ +struct net_device_stats { + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_dropped; /* no space in linux buffers */ + unsigned long tx_dropped; /* no space available in linux */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ +}; + +struct net_device { + char name[16]; + void *priv; /* pointer to private data */ + unsigned char dev_addr[6]; /* set during bootup */ + int (*init)(void); + int (*open)(struct net_device *dev); + int (*stop)(struct net_device *dev); + int (*hard_start_xmit)(struct sk_buff *skb, struct net_device *dev); + int (*do_ioctl)(struct net_device *dev, struct iwreq *ifr, int cmd); + struct net_device_stats* (*get_stats)(struct net_device *dev); +}; + +typedef struct { + struct net_device *dev; /* Binding wlan driver netdev */ + void *skb; /* pending Rx packet */ + unsigned int tx_busy; + unsigned int rx_busy; + unsigned char enable; + unsigned char mac[6]; +} Rltk_wlan_t; + +#define netdev_priv(dev) dev->priv + +extern struct net_device *alloc_etherdev(int sizeof_priv); +void free_netdev(struct net_device *dev); +int dev_alloc_name(struct net_device *net_dev, const char *ifname); + + +//----- ------------------------------------------------------------------ +// Timer Operation +//----- ------------------------------------------------------------------ +void init_timer(struct timer_list *timer); +void mod_timer(struct timer_list *timer, u32 delay_time_ms); +void cancel_timer_ex(struct timer_list * timer); +void del_timer_sync(struct timer_list * timer); +void init_timer_wrapper(void); +void deinit_timer_wrapper(void); + +#endif //__WRAPPER_H__ + + + diff --git a/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c b/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c new file mode 100644 index 0000000..8a0b776 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c @@ -0,0 +1,222 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +//#define _LWIP_INTF_C_ + +#include +#include +#include +#include +#include +#include +#include +//----- ------------------------------------------------------------------ +// External Reference +//----- ------------------------------------------------------------------ +#if (CONFIG_LWIP_LAYER == 1) +extern struct netif xnetif[]; //LWIP netif +#endif + +/** + * rltk_wlan_set_netif_info - set netif hw address and register dev pointer to netif device + * @idx_wlan: netif index + * 0 for STA only or SoftAP only or STA in STA+SoftAP concurrent mode, + * 1 for SoftAP in STA+SoftAP concurrent mode + * @dev: register netdev pointer to LWIP. Reserved. + * @dev_addr: set netif hw address + * + * Return Value: None + */ +void rltk_wlan_set_netif_info(int idx_wlan, void * dev, unsigned char * dev_addr) +{ +#if (CONFIG_LWIP_LAYER == 1) + rtw_memcpy(xnetif[idx_wlan].hwaddr, dev_addr, 6); + xnetif[idx_wlan].state = dev; +#endif +} + +/** + * rltk_wlan_send - send IP packets to WLAN. Called by low_level_output(). + * @idx: netif index + * @sg_list: data buffer list + * @sg_len: size of each data buffer + * @total_len: total data len + * + * Return Value: None + */ +int rltk_wlan_send(int idx, struct eth_drv_sg *sg_list, int sg_len, int total_len) +{ + struct eth_drv_sg *last_sg; + struct sk_buff *skb = NULL; + int ret = 0; + + if(idx == -1){ + DBG_ERR("netif is DOWN"); + return -1; + } + DBG_TRACE("%s is called", __FUNCTION__); + + save_and_cli(); + if(rltk_wlan_check_isup(idx)) + rltk_wlan_tx_inc(idx); + else { + DBG_ERR("netif is DOWN"); + restore_flags(); + return -1; + } + restore_flags(); + + skb = rltk_wlan_alloc_skb(total_len); + if (skb == NULL) { + DBG_ERR("rltk_wlan_alloc_skb() for data len=%d failed!", total_len); + ret = -1; + goto exit; + } + + for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { + rtw_memcpy(skb->tail, (void *)(sg_list->buf), sg_list->len); + skb_put(skb, sg_list->len); + } + + rltk_wlan_send_skb(idx, skb); + +exit: + save_and_cli(); + rltk_wlan_tx_dec(idx); + restore_flags(); + return ret; +} + +/** + * rltk_wlan_recv - indicate packets to LWIP. Called by ethernetif_recv(). + * @idx: netif index + * @sg_list: data buffer list + * @sg_len: size of each data buffer + * + * Return Value: None + */ +void rltk_wlan_recv(int idx, struct eth_drv_sg *sg_list, int sg_len) +{ + struct eth_drv_sg *last_sg; + struct sk_buff *skb; + + DBG_TRACE("%s is called", __FUNCTION__); + if(idx == -1){ + DBG_ERR("skb is NULL"); + return; + } + skb = rltk_wlan_get_recv_skb(idx); + DBG_ASSERT(skb, "No pending rx skb"); + + for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { + if (sg_list->buf != 0) { + rtw_memcpy((void *)(sg_list->buf), skb->data, sg_list->len); + skb_pull(skb, sg_list->len); + } + } +} + +int netif_is_valid_IP(int idx, unsigned char *ip_dest) +{ +#if CONFIG_LWIP_LAYER == 1 + struct netif * pnetif = &xnetif[idx]; + struct ip_addr addr = { 0 }; +#ifdef CONFIG_MEMORY_ACCESS_ALIGNED + unsigned int temp; + memcpy(&temp, ip_dest, sizeof(unsigned int)); + u32_t *ip_dest_addr = &temp; +#else + u32_t *ip_dest_addr = (u32_t*)ip_dest; +#endif + addr.addr = *ip_dest_addr; + + if(pnetif->ip_addr.addr == 0) + return 1; + + if(ip_addr_ismulticast(&addr) || ip_addr_isbroadcast(&addr,pnetif)){ + return 1; + } + + //if(ip_addr_netcmp(&(pnetif->ip_addr), &addr, &(pnetif->netmask))) //addr&netmask + // return 1; + + if(ip_addr_cmp(&(pnetif->ip_addr),&addr)) + return 1; + + DBG_TRACE("invalid IP: %d.%d.%d.%d ",ip_dest[0],ip_dest[1],ip_dest[2],ip_dest[3]); +#endif +#ifdef CONFIG_DONT_CARE_TP + if(pnetif->flags & NETIF_FLAG_IPSWITCH) + return 1; + else +#endif + return 0; +} + +int netif_get_idx(struct netif* pnetif) +{ +#if CONFIG_LWIP_LAYER == 1 + int idx = pnetif - xnetif; + + switch(idx) { + case 0: + return 0; + case 1: + return 1; + default: + return -1; + } +#else + return -1; +#endif +} + +unsigned char *netif_get_hwaddr(int idx_wlan) +{ +#if (CONFIG_LWIP_LAYER == 1) + return xnetif[idx_wlan].hwaddr; +#else + return NULL; +#endif +} + +void netif_rx(int idx, unsigned int len) +{ +#if (CONFIG_LWIP_LAYER == 1) + ethernetif_recv(&xnetif[idx], len); +#endif +#if (CONFIG_INIC_EN == 1) + inic_netif_rx(idx, len); +#endif +} + +void netif_post_sleep_processing(void) +{ +#if (CONFIG_LWIP_LAYER == 1) + lwip_POST_SLEEP_PROCESSING(); //For FreeRTOS tickless to enable Lwip ARP timer when leaving IPS - Alex Fang +#endif +} + +void netif_pre_sleep_processing(void) +{ +#if (CONFIG_LWIP_LAYER == 1) + lwip_PRE_SLEEP_PROCESSING(); +#endif +} + diff --git a/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.h b/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.h new file mode 100644 index 0000000..3478de3 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.h @@ -0,0 +1,56 @@ +#ifndef __LWIP_INTF_H__ +#define __LWIP_INTF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +//----- ------------------------------------------------------------------ +// Ethernet Buffer +//----- ------------------------------------------------------------------ +struct eth_drv_sg { + unsigned int buf; + unsigned int len; +}; + +#define MAX_ETH_DRV_SG 32 +#define MAX_ETH_MSG 1540 + +//----- ------------------------------------------------------------------ +// Wlan Interface Provided +//----- ------------------------------------------------------------------ +unsigned char rltk_wlan_check_isup(int idx); +void rltk_wlan_tx_inc(int idx); +void rltk_wlan_tx_dec(int idx); +struct sk_buff * rltk_wlan_get_recv_skb(int idx); +struct sk_buff * rltk_wlan_alloc_skb(unsigned int total_len); +void rltk_wlan_set_netif_info(int idx_wlan, void * dev, unsigned char * dev_addr); +void rltk_wlan_send_skb(int idx, struct sk_buff *skb); //struct sk_buff as defined above comment line +int rltk_wlan_send(int idx, struct eth_drv_sg *sg_list, int sg_len, int total_len); +void rltk_wlan_recv(int idx, struct eth_drv_sg *sg_list, int sg_len); +unsigned char rltk_wlan_running(unsigned char idx); // interface is up. 0: interface is down + +//----- ------------------------------------------------------------------ +// Network Interface provided +//----- ------------------------------------------------------------------ +struct netif; +int netif_is_valid_IP(int idx,unsigned char * ip_dest); +int netif_get_idx(struct netif *pnetif); +unsigned char *netif_get_hwaddr(int idx_wlan); +void netif_rx(int idx, unsigned int len); +void netif_post_sleep_processing(void); +void netif_pre_sleep_processing(void); +#if (CONFIG_LWIP_LAYER == 1) +extern void ethernetif_recv(struct netif *netif, int total_len); +extern void lwip_PRE_SLEEP_PROCESSING(void); +extern void lwip_POST_SLEEP_PROCESSING(void); +#endif //CONFIG_LWIP_LAYER == 1 + +#ifdef __cplusplus +} +#endif + +#endif //#ifndef __LWIP_INTF_H__ diff --git a/component/common/drivers/wlan/realtek/src/osdep/osdep_service.h b/component/common/drivers/wlan/realtek/src/osdep/osdep_service.h new file mode 100644 index 0000000..8541247 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/osdep_service.h @@ -0,0 +1,523 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#ifndef __OSDEP_SERVICE_H_ +#define __OSDEP_SERVICE_H_ + +/* Define compilor specific symbol */ +// +// inline function +// + +#if defined ( __ICCARM__ ) +#define __inline__ inline +#define __inline inline +#define __inline_definition //In dialect C99, inline means that a function's definition is provided + //only for inlining, and that there is another definition + //(without inline) somewhere else in the program. + //That means that this program is incomplete, because if + //add isn't inlined (for example, when compiling without optimization), + //then main will have an unresolved reference to that other definition. + + // Do not inline function is the function body is defined .c file and this + // function will be called somewhere else, otherwise there is compile error +#elif defined ( __CC_ARM ) +#define __inline__ __inline //__linine__ is not supported in keil compilor, use __inline instead +#define inline __inline +#define __inline_definition // for dialect C99 +#elif defined ( __GNUC__ ) +#define __inline__ inline +#define __inline inline +#define __inline_definition inline +#endif + +#include +#include +#if defined( PLATFORM_FREERTOS) +#include "freertos/freertos_service.h" +#elif defined( PLATFORM_ECOS) +#include "ecos/ecos_service.h" +#endif + +#include "wifi_constants.h" +#include "wifi_structures.h" + +#define RTW_MAX_DELAY 0xFFFFFFFF +#define RTW_WAIT_FOREVER 0xFFFFFFFF + +struct timer_list { + _timerHandle timer_hdl; + unsigned long data; + void (*function)(void *); +}; + +typedef thread_return (*thread_func_t)(thread_context context); +typedef void (*TIMER_FUN)(void *context); +typedef int (*event_handler_t)(char *buf, int buf_len, int flags, void *user_data); + +#define CONFIG_THREAD_COMM_SEMA +struct task_struct { + const char *task_name; + _thread_hdl_ task; /* I: workqueue thread */ + +#ifdef CONFIG_THREAD_COMM_SIGNAL + const char *name; /* I: workqueue thread name */ + u32 queue_num; /* total signal num */ + u32 cur_queue_num; /* cur signal num should < queue_num */ +#elif defined(CONFIG_THREAD_COMM_SEMA) + _sema wakeup_sema; + _sema terminate_sema; +// _queue work_queue; //TODO +#endif + u32 blocked; + u32 callback_running; +}; + +typedef struct { + _xqueue event_queue; + struct task_struct thread; +}rtw_worker_thread_t; + +typedef struct +{ + event_handler_t function; + char *buf; + int buf_len; + int flags; + void *user_data; +} rtw_event_message_t; + +struct worker_timer_entry { + struct list_head list; + _timerHandle timer_hdl; + rtw_event_message_t message; + rtw_worker_thread_t *worker_thread; + u32 timeout; +}; +#ifdef CONFIG_THREAD_COMM_SIGNAL +struct work_struct; +typedef void (*work_func_t)(void *context); +struct work_struct { + _list list; + u32 data; + work_func_t func; + void *context; + struct task_struct *used_wq; +}; + +struct delayed_work { + struct work_struct work; + struct timer_list timer; +}; +#endif + +#ifdef CONFIG_MEM_MONITOR +//----- ------------------------------------------------------------------ +// Memory Monitor +//----- ------------------------------------------------------------------ +#define MEM_MONITOR_SIMPLE 0x1 +#define MEM_MONITOR_LEAK 0x2 + +#define MEM_MONITOR_FLAG_WIFI_DRV 0x1 +#define MEM_MONITOR_FLAG_WPAS 0x2 +#if CONFIG_MEM_MONITOR & MEM_MONITOR_LEAK +struct mem_entry { + struct list_head list; + int size; + void *ptr; +}; +#endif + +void init_mem_monitor(_list *pmem_table, int *used_num); +void deinit_mem_monitor(_list *pmem_table, int *used_num); +void add_mem_usage(_list *pmem_table, void *ptr, int size, int *used_num, int flag); +void del_mem_usage(_list *pmem_table, void *ptr, int *used_num, int flag); +int get_mem_usage(_list *pmem_table); +#endif + +/*********************************** OSDEP API *****************************************/ +u8* _rtw_vmalloc(u32 sz); +u8* _rtw_zvmalloc(u32 sz); +void _rtw_vmfree(u8 *pbuf, u32 sz); +u8* _rtw_zmalloc(u32 sz); +u8* _rtw_malloc(u32 sz); +void _rtw_mfree(u8 *pbuf, u32 sz); +#ifdef CONFIG_MEM_MONITOR +u8* rtw_vmalloc(u32 sz); +u8* rtw_zvmalloc(u32 sz); +void rtw_vmfree(u8 *pbuf, u32 sz); +u8* rtw_zmalloc(u32 sz); +u8* rtw_malloc(u32 sz); +void rtw_mfree(u8 *pbuf, u32 sz); +#else +#define rtw_vmalloc _rtw_vmalloc +#define rtw_zvmalloc _rtw_zvmalloc +#define rtw_vmfree _rtw_vmfree +#define rtw_zmalloc _rtw_zmalloc +#define rtw_malloc _rtw_malloc +#define rtw_mfree _rtw_mfree +#endif +#define rtw_free(buf) rtw_mfree((u8 *)buf, 0) +void* rtw_malloc2d(int h, int w, int size); +void rtw_mfree2d(void *pbuf, int h, int w, int size); +void rtw_memcpy(void* dst, void* src, u32 sz); +int rtw_memcmp(void *dst, void *src, u32 sz); +void rtw_memset(void *pbuf, int c, u32 sz); + +void rtw_init_listhead(_list *list); +u32 rtw_is_list_empty(_list *phead); +void rtw_list_insert_head(_list *plist, _list *phead); +void rtw_list_insert_tail(_list *plist, _list *phead); +void rtw_list_delete(_list *plist); + +void rtw_init_sema(_sema *sema, int init_val); +void rtw_free_sema(_sema *sema); +void rtw_up_sema(_sema *sema); +u32 rtw_down_sema(_sema *sema); +u32 rtw_down_timeout_sema(_sema *sema, u32 timeout); +void rtw_mutex_init(_mutex *pmutex); +void rtw_mutex_free(_mutex *pmutex); +void rtw_mutex_put(_mutex *pmutex); +void rtw_mutex_get(_mutex *pmutex); +void rtw_enter_critical(_lock *plock, _irqL *pirqL); +void rtw_exit_critical(_lock *plock, _irqL *pirqL); +void rtw_enter_critical_bh(_lock *plock, _irqL *pirqL); +void rtw_exit_critical_bh(_lock *plock, _irqL *pirqL); +int rtw_enter_critical_mutex(_mutex *pmutex, _irqL *pirqL); +void rtw_exit_critical_mutex(_mutex *pmutex, _irqL *pirqL); +void rtw_spinlock_init(_lock *plock); +void rtw_spinlock_free(_lock *plock); +void rtw_spinlock_init(_lock *plock); +void rtw_spinlock_free(_lock *plock); +void rtw_spin_lock(_lock *plock); +void rtw_spin_unlock(_lock *plock); +void rtw_spinlock_irqsave(_lock *plock, _irqL *irqL); +void rtw_spinunlock_irqsave(_lock *plock, _irqL *irqL); + +rtw_result_t rtw_init_xqueue( _xqueue* queue, const char* name, u32 message_size, u32 number_of_messages ); +rtw_result_t rtw_push_to_xqueue( _xqueue* queue, void* message, u32 timeout_ms ); +rtw_result_t rtw_pop_from_xqueue( _xqueue* queue, void* message, u32 timeout_ms ); +rtw_result_t rtw_deinit_xqueue( _xqueue* queue ); + +void rtw_init_queue(_queue *pqueue); +void rtw_deinit_queue(_queue *pqueue); +u32 rtw_is_queue_empty(_queue *pqueue); +u32 rtw_queue_empty(_queue *pqueue); +u32 rtw_end_of_queue_search(_list *queue, _list *pelement); +_list* rtw_get_queue_head(_queue *queue); + +u32 rtw_get_current_time(void); +u32 rtw_systime_to_ms(u32 systime); +u32 rtw_systime_to_sec(u32 systime); +u32 rtw_ms_to_systime(u32 ms); +u32 rtw_sec_to_systime(u32 sec); +s32 rtw_get_passing_time_ms(u32 start); +s32 rtw_get_time_interval_ms(u32 start, u32 end); + +void rtw_msleep_os(int ms); +void rtw_usleep_os(int us); +u32 rtw_atoi(u8* s); +void rtw_mdelay_os(int ms); +void rtw_udelay_os(int us); +void rtw_yield_os(void); + +void rtw_init_timer(_timer *ptimer, void *adapter, TIMER_FUN pfunc,void* cntx, const char *name); +void rtw_set_timer(_timer *ptimer,u32 delay_time); +u8 rtw_cancel_timer(_timer *ptimer); +void rtw_del_timer(_timer *ptimer); + +//Atomic integer operations +void ATOMIC_SET(ATOMIC_T *v, int i); +int ATOMIC_READ(ATOMIC_T *v); +void ATOMIC_ADD(ATOMIC_T *v, int i); +void ATOMIC_SUB(ATOMIC_T *v, int i); +void ATOMIC_INC(ATOMIC_T *v); +void ATOMIC_DEC(ATOMIC_T *v); +int ATOMIC_ADD_RETURN(ATOMIC_T *v, int i); +int ATOMIC_SUB_RETURN(ATOMIC_T *v, int i); +int ATOMIC_INC_RETURN(ATOMIC_T *v); +int ATOMIC_DEC_RETURN(ATOMIC_T *v); +int ATOMIC_DEC_AND_TEST(ATOMIC_T *v); + +u64 rtw_modular64(u64 x, u64 y); +int rtw_get_random_bytes(void* dst, u32 size); +u32 rtw_getFreeHeapSize(void); +void flush_signals_thread(void); + +/*********************************** Thread related *****************************************/ +int rtw_create_task(struct task_struct *task, const char *name, u32 stack_size, u32 priority, thread_func_t func, void *thctx); +void rtw_delete_task(struct task_struct * task); +void rtw_wakeup_task(struct task_struct *task); +rtw_result_t rtw_create_worker_thread( rtw_worker_thread_t* worker_thread, u8 priority, u32 stack_size, u32 event_queue_size ); +rtw_result_t rtw_delete_worker_thread( rtw_worker_thread_t* worker_thread ); + +#if 0 //TODO +void rtw_init_delayed_work(struct delayed_work *dwork, work_func_t func, const char *name); +void rtw_deinit_delayed_work(struct delayed_work *dwork); +int rtw_queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, u32 delay, void* context); +BOOLEAN rtw_cancel_delayed_work(struct delayed_work *dwork); +#endif + +void rtw_thread_enter(char *name); +void rtw_thread_exit(void); +#ifdef PLATFORM_LINUX +#define rtw_warn_on(condition) WARN_ON(condition) +#else +#define rtw_warn_on(condition) do {} while (0) +#endif + +/*********************************** Timer related *****************************************/ +_timerHandle rtw_timerCreate( const signed char *pcTimerName, + osdepTickType xTimerPeriodInTicks, + u32 uxAutoReload, + void * pvTimerID, + TIMER_FUN pxCallbackFunction ); +u32 rtw_timerDelete( _timerHandle xTimer, + osdepTickType xBlockTime ); +u32 rtw_timerIsTimerActive( _timerHandle xTimer ); +u32 rtw_timerStop( _timerHandle xTimer, + osdepTickType xBlockTime ); +u32 rtw_timerChangePeriod( _timerHandle xTimer, + osdepTickType xNewPeriod, + osdepTickType xBlockTime ); + +/*********************************** OSDEP API end *****************************************/ +#define LIST_CONTAINOR(ptr, type, member) \ + ((type *)((char *)(ptr)-(SIZE_T)((char *)&((type *)ptr)->member - (char *)ptr))) + +#define time_after(a,b) ((long)(b) - (long)(a) < 0) +#define time_before(a,b) time_after(b,a) +#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0) +#define time_before_eq(a,b) time_after_eq(b,a) + +#define _RND(sz, r) ((((sz)+((r)-1))/(r))*(r)) +#define RND4(x) (((x >> 2) + (((x & 3) == 0) ? 0: 1)) << 2) + +__inline static u32 _RND4(u32 sz) +{ + u32 val; + + val = ((sz >> 2) + ((sz & 3) ? 1: 0)) << 2; + + return val; +} + +__inline static u32 _RND8(u32 sz) +{ + u32 val; + + val = ((sz >> 3) + ((sz & 7) ? 1: 0)) << 3; + + return val; +} + +__inline static u32 _RND128(u32 sz) +{ + u32 val; + + val = ((sz >> 7) + ((sz & 127) ? 1: 0)) << 7; + + return val; +} + +__inline static u32 _RND256(u32 sz) +{ + u32 val; + + val = ((sz >> 8) + ((sz & 255) ? 1: 0)) << 8; + + return val; +} + +__inline static u32 _RND512(u32 sz) +{ + u32 val; + + val = ((sz >> 9) + ((sz & 511) ? 1: 0)) << 9; + + return val; +} + +__inline static u32 bitshift(u32 bitmask) +{ + u32 i; + + for (i = 0; i <= 31; i++) + if (((bitmask>>i) & 0x1) == 1) break; + + return i; +} + +/* Macros for handling unaligned memory accesses */ + +#define RTW_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define RTW_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define RTW_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define RTW_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define RTW_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define RTW_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define RTW_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define RTW_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define RTW_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) + +struct osdep_service_ops { + u8* (*rtw_vmalloc)(u32 sz); + u8* (*rtw_zvmalloc)(u32 sz); + void (*rtw_vmfree)(u8 *pbuf, u32 sz); + u8* (*rtw_malloc)(u32 sz); + u8* (*rtw_zmalloc)(u32 sz); + void (*rtw_mfree)(u8 *pbuf, u32 sz); + void (*rtw_memcpy)(void* dst, void* src, u32 sz); + int (*rtw_memcmp)(void *dst, void *src, u32 sz); + void (*rtw_memset)(void *pbuf, int c, u32 sz); + void (*rtw_init_sema)(_sema *sema, int init_val); + void (*rtw_free_sema)(_sema *sema); + void (*rtw_up_sema)(_sema *sema); + u32 (*rtw_down_timeout_sema)(_sema *sema, u32 timeout); + void (*rtw_mutex_init)(_mutex *pmutex); + void (*rtw_mutex_free)(_mutex *pmutex); + void (*rtw_mutex_get)(_mutex *pmutex); + void (*rtw_mutex_put)(_mutex *pmutex); + void (*rtw_enter_critical)(_lock *plock, _irqL *pirqL); + void (*rtw_exit_critical)(_lock *plock, _irqL *pirqL); + void (*rtw_enter_critical_bh)(_lock *plock, _irqL *pirqL); + void (*rtw_exit_critical_bh)(_lock *plock, _irqL *pirqL); + int (*rtw_enter_critical_mutex)(_mutex *pmutex, _irqL *pirqL); + void (*rtw_exit_critical_mutex)(_mutex *pmutex, _irqL *pirqL); + void (*rtw_spinlock_init)(_lock *plock); + void (*rtw_spinlock_free)(_lock *plock); + void (*rtw_spin_lock)(_lock *plock); + void (*rtw_spin_unlock)(_lock *plock); + void (*rtw_spinlock_irqsave)(_lock *plock, _irqL *irqL); + void (*rtw_spinunlock_irqsave)(_lock *plock, _irqL *irqL); + int (*rtw_init_xqueue)( _xqueue* queue, const char* name, u32 message_size, u32 number_of_messages ); + int (*rtw_push_to_xqueue)( _xqueue* queue, void* message, u32 timeout_ms ); + int (*rtw_pop_from_xqueue)( _xqueue* queue, void* message, u32 timeout_ms ); + int (*rtw_deinit_xqueue)( _xqueue* queue ); + u32 (*rtw_get_current_time)(void); + u32 (*rtw_systime_to_ms)(u32 systime); + u32 (*rtw_systime_to_sec)(u32 systime); + u32 (*rtw_ms_to_systime)(u32 ms); + u32 (*rtw_sec_to_systime)(u32 sec); + void (*rtw_msleep_os)(int ms); + void (*rtw_usleep_os)(int us); + void (*rtw_mdelay_os)(int ms); + void (*rtw_udelay_os)(int us); + void (*rtw_yield_os)(void); + void (*rtw_init_timer)(_timer *ptimer, void *adapter, TIMER_FUN pfunc,void* cntx, const char *name); + void (*rtw_set_timer)(_timer *ptimer,u32 delay_time); + u8 (*rtw_cancel_timer)(_timer *ptimer); + void (*rtw_del_timer)(_timer *ptimer); + void (*ATOMIC_SET)(ATOMIC_T *v, int i); + int (*ATOMIC_READ)(ATOMIC_T *v); + void (*ATOMIC_ADD)(ATOMIC_T *v, int i); + void (*ATOMIC_SUB)(ATOMIC_T *v, int i); + void (*ATOMIC_INC)(ATOMIC_T *v); + void (*ATOMIC_DEC)(ATOMIC_T *v); + int (*ATOMIC_ADD_RETURN)(ATOMIC_T *v, int i); + int (*ATOMIC_SUB_RETURN)(ATOMIC_T *v, int i); + int (*ATOMIC_INC_RETURN)(ATOMIC_T *v); + int (*ATOMIC_DEC_RETURN)(ATOMIC_T *v); + u64 (*rtw_modular64)(u64 x, u64 y); + int (*rtw_get_random_bytes)(void* dst, u32 size); + u32 (*rtw_getFreeHeapSize)(void); + int (*rtw_create_task)(struct task_struct *task, const char *name, u32 stack_size, u32 priority, thread_func_t func, void *thctx); + void (*rtw_delete_task)(struct task_struct *task); + void (*rtw_wakeup_task)(struct task_struct *task); + +#if 0 //TODO + void (*rtw_init_delayed_work)(struct delayed_work *dwork, work_func_t func, const char *name); + void (*rtw_deinit_delayed_work)(struct delayed_work *dwork); + int (*rtw_queue_delayed_work)(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay, void* context); + BOOLEAN (*rtw_cancel_delayed_work)(struct delayed_work *dwork); +#endif + void (*rtw_thread_enter)(char *name); + void (*rtw_thread_exit)(void); + _timerHandle (*rtw_timerCreate)( const signed char *pcTimerName, + osdepTickType xTimerPeriodInTicks, + u32 uxAutoReload, + void * pvTimerID, + TIMER_FUN pxCallbackFunction ); + u32 (*rtw_timerDelete)( _timerHandle xTimer, + osdepTickType xBlockTime ); + u32 (*rtw_timerIsTimerActive)( _timerHandle xTimer ); + u32 (*rtw_timerStop)( _timerHandle xTimer, + osdepTickType xBlockTime ); + u32 (*rtw_timerChangePeriod)( _timerHandle xTimer, + osdepTickType xNewPeriod, + osdepTickType xBlockTime ); +}; +/*********************************** OSDEP API end *****************************************/ + + +#endif //#ifndef __OSDEP_SERVICE_H_ diff --git a/component/common/drivers/wlan/realtek/src/osdep/skbuff.h b/component/common/drivers/wlan/realtek/src/osdep/skbuff.h new file mode 100644 index 0000000..8f11a42 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/skbuff.h @@ -0,0 +1,54 @@ +#ifndef __SKBUFF_H__ +#define __SKBUFF_H__ + +struct sk_buff_head { + struct list_head *next, *prev; + unsigned int qlen; +}; + +#ifdef CONFIG_TRACE_SKB +#define TRACE_SKB_DEPTH 8 +#endif + +struct sk_buff { + /* These two members must be first. */ + struct sk_buff *next; /* Next buffer in list */ + struct sk_buff *prev; /* Previous buffer in list */ + + struct sk_buff_head *list; /* List we are on */ + unsigned char *head; /* Head of buffer */ + unsigned char *data; /* Data head pointer */ + unsigned char *tail; /* Tail pointer */ + unsigned char *end; /* End pointer */ + void *dev; /* Device we arrived on/are leaving by */ + unsigned int len; /* Length of actual data */ +#ifdef CONFIG_TRACE_SKB + unsigned int liston[TRACE_SKB_DEPTH]; /* Trace the Lists we went through */ + const char *funcname[TRACE_SKB_DEPTH]; + unsigned int list_idx; /* Trace the List we are on */ +#endif +}; + +unsigned char *skb_put(struct sk_buff *skb, unsigned int len); +unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); +void skb_reserve(struct sk_buff *skb, unsigned int len); +void skb_assign_buf(struct sk_buff *skb, unsigned char *buf, unsigned int len); +unsigned char *skb_tail_pointer(const struct sk_buff *skb); +void skb_set_tail_pointer(struct sk_buff *skb, const int offset); +unsigned char *skb_end_pointer(const struct sk_buff *skb); + +void init_skb_pool(void); +void init_skb_data_pool(void); + +#ifndef CONFIG_DONT_CARE_TP +struct sk_buff *dev_alloc_skb(unsigned int length, unsigned int reserve_len); +#else +struct sk_buff *dev_alloc_tx_skb(unsigned int length, unsigned int reserve_len); +struct sk_buff *dev_alloc_rx_skb(unsigned int length, unsigned int reserve_len); +#define dev_alloc_skb dev_alloc_tx_skb +#endif +void kfree_skb(struct sk_buff *skb); + + +#endif //__SKBUFF_H__ + diff --git a/component/common/drivers/wlan/realtek/src/osdep/wireless.h b/component/common/drivers/wlan/realtek/src/osdep/wireless.h new file mode 100644 index 0000000..9fcf208 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/wireless.h @@ -0,0 +1,1206 @@ +/* + * This file define a set of standard wireless extensions + * + * Version : 22 16.3.07 + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. + */ + +#ifndef _LINUX_WIRELESS_H +#define _LINUX_WIRELESS_H + +/************************** DOCUMENTATION **************************/ +/* + * Initial APIs (1996 -> onward) : + * ----------------------------- + * Basically, the wireless extensions are for now a set of standard ioctl + * call + /proc/net/wireless + * + * The entry /proc/net/wireless give statistics and information on the + * driver. + * This is better than having each driver having its entry because + * its centralised and we may remove the driver module safely. + * + * Ioctl are used to configure the driver and issue commands. This is + * better than command line options of insmod because we may want to + * change dynamically (while the driver is running) some parameters. + * + * The ioctl mechanimsm are copied from standard devices ioctl. + * We have the list of command plus a structure descibing the + * data exchanged... + * Note that to add these ioctl, I was obliged to modify : + * # net/core/dev.c (two place + add include) + * # net/ipv4/af_inet.c (one place + add include) + * + * /proc/net/wireless is a copy of /proc/net/dev. + * We have a structure for data passed from the driver to /proc/net/wireless + * Too add this, I've modified : + * # net/core/dev.c (two other places) + * # include/linux/netdevice.h (one place) + * # include/linux/proc_fs.h (one place) + * + * New driver API (2002 -> onward) : + * ------------------------------- + * This file is only concerned with the user space API and common definitions. + * The new driver API is defined and documented in : + * # include/net/iw_handler.h + * + * Note as well that /proc/net/wireless implementation has now moved in : + * # net/core/wireless.c + * + * Wireless Events (2002 -> onward) : + * -------------------------------- + * Events are defined at the end of this file, and implemented in : + * # net/core/wireless.c + * + * Other comments : + * -------------- + * Do not add here things that are redundant with other mechanisms + * (drivers init, ifconfig, /proc/net/dev, ...) and with are not + * wireless specific. + * + * These wireless extensions are not magic : each driver has to provide + * support for them... + * + * IMPORTANT NOTE : As everything in the kernel, this is very much a + * work in progress. Contact me if you have ideas of improvements... + */ + +/***************************** INCLUDES *****************************/ + +/* This header is used in user-space, therefore need to be sanitised + * for that purpose. Those includes are usually not compatible with glibc. + * To know which includes to use in user-space, check iwlib.h. */ +#ifdef __KERNEL__ +#include /* for "caddr_t" et al */ +#include /* for "struct sockaddr" et al */ +#include /* for IFNAMSIZ and co... */ +#endif /* __KERNEL__ */ + +//#include +#define IFNAMSIZ 16 +#define ARPHRD_ETHER 1 /* ethernet hardware format */ + +/***************************** VERSION *****************************/ +/* + * This constant is used to know the availability of the wireless + * extensions and to know which version of wireless extensions it is + * (there is some stuff that will be added in the future...) + * I just plan to increment with each new version. + */ +#define WIRELESS_EXT 22 + +/* + * Changes : + * + * V2 to V3 + * -------- + * Alan Cox start some incompatibles changes. I've integrated a bit more. + * - Encryption renamed to Encode to avoid US regulation problems + * - Frequency changed from float to struct to avoid problems on old 386 + * + * V3 to V4 + * -------- + * - Add sensitivity + * + * V4 to V5 + * -------- + * - Missing encoding definitions in range + * - Access points stuff + * + * V5 to V6 + * -------- + * - 802.11 support (ESSID ioctls) + * + * V6 to V7 + * -------- + * - define IW_ESSID_MAX_SIZE and IW_MAX_AP + * + * V7 to V8 + * -------- + * - Changed my e-mail address + * - More 802.11 support (nickname, rate, rts, frag) + * - List index in frequencies + * + * V8 to V9 + * -------- + * - Support for 'mode of operation' (ad-hoc, managed...) + * - Support for unicast and multicast power saving + * - Change encoding to support larger tokens (>64 bits) + * - Updated iw_params (disable, flags) and use it for NWID + * - Extracted iw_point from iwreq for clarity + * + * V9 to V10 + * --------- + * - Add PM capability to range structure + * - Add PM modifier : MAX/MIN/RELATIVE + * - Add encoding option : IW_ENCODE_NOKEY + * - Add TxPower ioctls (work like TxRate) + * + * V10 to V11 + * ---------- + * - Add WE version in range (help backward/forward compatibility) + * - Add retry ioctls (work like PM) + * + * V11 to V12 + * ---------- + * - Add SIOCSIWSTATS to get /proc/net/wireless programatically + * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space + * - Add new statistics (frag, retry, beacon) + * - Add average quality (for user space calibration) + * + * V12 to V13 + * ---------- + * - Document creation of new driver API. + * - Extract union iwreq_data from struct iwreq (for new driver API). + * - Rename SIOCSIWNAME as SIOCSIWCOMMIT + * + * V13 to V14 + * ---------- + * - Wireless Events support : define struct iw_event + * - Define additional specific event numbers + * - Add "addr" and "param" fields in union iwreq_data + * - AP scanning stuff (SIOCSIWSCAN and friends) + * + * V14 to V15 + * ---------- + * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg + * - Make struct iw_freq signed (both m & e), add explicit padding + * - Add IWEVCUSTOM for driver specific event/scanning token + * - Add IW_MAX_GET_SPY for driver returning a lot of addresses + * - Add IW_TXPOW_RANGE for range of Tx Powers + * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points + * - Add IW_MODE_MONITOR for passive monitor + * + * V15 to V16 + * ---------- + * - Increase the number of bitrates in iw_range to 32 (for 802.11g) + * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) + * - Reshuffle struct iw_range for increases, add filler + * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses + * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support + * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" + * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) + * + * V17 to V18 (From Jouni Malinen ) + * ---------- + * - Add support for WPA/WPA2 + * - Add extended encoding configuration (SIOCSIWENCODEEXT and + * SIOCGIWENCODEEXT) + * - Add SIOCSIWGENIE/SIOCGIWGENIE + * - Add SIOCSIWMLME + * - Add SIOCSIWPMKSA + * - Add struct iw_range bit field for supported encoding capabilities + * - Add optional scan request parameters for SIOCSIWSCAN + * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA + * related parameters (extensible up to 4096 parameter values) + * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, + * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND + * + * V18 to V19 + * ---------- + * - Remove (struct iw_point *)->pointer from events and streams + * - Remove header includes to help user space + * - Increase IW_ENCODING_TOKEN_MAX from 32 to 64 + * - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros + * - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM + * - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros + * + * V19 to V20 + * ---------- + * - RtNetlink requests support (SET/GET) + * + * V20 to V21 + * ---------- + * - Remove (struct net_device *)->get_wireless_stats() + * - Change length in ESSID and NICK to strlen() instead of strlen()+1 + * - Add IW_RETRY_SHORT/IW_RETRY_LONG retry modifiers + * - Power/Retry relative values no longer * 100000 + * - Add explicit flag to tell stats are in 802.11k RCPI : IW_QUAL_RCPI + * + * V21 to V22 + * ---------- + * - Prevent leaking of kernel space in stream on 64 bits. + */ + +/**************************** CONSTANTS ****************************/ +typedef unsigned char __u8; +typedef char __s8; +typedef unsigned short __u16; +typedef short __s16; +typedef unsigned int __u32; +typedef int __s32; +typedef unsigned long long __u64; +typedef long long __i64; + +#define E2BIG 7 /* Argument list too long */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ + +/* Device private ioctl calls */ + +/* + * These 16 ioctls are available to devices via the do_ioctl() device + * vector. Each device should include this file and redefine these names + * as their own. Because these are device dependent it is a good idea + * _NOT_ to issue them to random objects and hope. + * + * THESE IOCTLS ARE _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X -DaveM + */ + +#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */ + +/* + * These 16 ioctl calls are protocol private + */ + +#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */ + +/* -------------------------- IOCTL LIST -------------------------- */ + +/* Wireless Identification */ +#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. + * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... + * Don't put the name of your driver there, it's useless. */ + +/* Basic operations */ +#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ +#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ +#define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ +#define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ +#define SIOCSIWMODE 0x8B06 /* set operation mode */ +#define SIOCGIWMODE 0x8B07 /* get operation mode */ +#define SIOCSIWSENS 0x8B08 /* set sensitivity (dBm) */ +#define SIOCGIWSENS 0x8B09 /* get sensitivity (dBm) */ + +/* Informative stuff */ +#define SIOCSIWRANGE 0x8B0A /* Unused */ +#define SIOCGIWRANGE 0x8B0B /* Get range of parameters */ +#define SIOCSIWPRIV 0x8B0C /* Unused */ +#define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ +#define SIOCSIWSTATS 0x8B0E /* Unused */ +#define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ +/* SIOCGIWSTATS is strictly used between user space and the kernel, and + * is never passed to the driver (i.e. the driver will never see it). */ + +/* Spy support (statistics per MAC address - used for Mobile IP support) */ +#define SIOCSIWSPY 0x8B10 /* set spy addresses */ +#define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ +#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ +#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ + +/* Access Point manipulation */ +#define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ +#define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ +#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ +#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ +#define SIOCGIWSCAN 0x8B19 /* get scanning results */ + +/* 802.11 specific support */ +#define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ +#define SIOCGIWESSID 0x8B1B /* get ESSID */ +#define SIOCSIWNICKN 0x8B1C /* set node name/nickname */ +#define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ +/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit + * within the 'iwreq' structure, so we need to use the 'data' member to + * point to a string in user space, like it is done for RANGE... */ + +/* Other parameters useful in 802.11 and some other devices */ +#define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ +#define SIOCGIWRATE 0x8B21 /* get default bit rate (bps) */ +#define SIOCSIWRTS 0x8B22 /* set RTS/CTS threshold (bytes) */ +#define SIOCGIWRTS 0x8B23 /* get RTS/CTS threshold (bytes) */ +#define SIOCSIWFRAG 0x8B24 /* set fragmentation thr (bytes) */ +#define SIOCGIWFRAG 0x8B25 /* get fragmentation thr (bytes) */ +#define SIOCSIWTXPOW 0x8B26 /* set transmit power (dBm) */ +#define SIOCGIWTXPOW 0x8B27 /* get transmit power (dBm) */ +#define SIOCSIWRETRY 0x8B28 /* set retry limits and lifetime */ +#define SIOCGIWRETRY 0x8B29 /* get retry limits and lifetime */ + +/* Encoding stuff (scrambling, hardware security, WEP...) */ +#define SIOCSIWENCODE 0x8B2A /* set encoding token & mode */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ +/* Power saving stuff (power management, unicast and multicast) */ +#define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ +#define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ +/* Modulation bitmask */ +#define SIOCSIWMODUL 0x8B2E /* set Modulations settings */ +#define SIOCGIWMODUL 0x8B2F /* get Modulations settings */ + +/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). + * This ioctl uses struct iw_point and data buffer that includes IE id and len + * fields. More than one IE may be included in the request. Setting the generic + * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers + * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers + * are required to report the used IE as a wireless event, e.g., when + * associating with an AP. */ +#define SIOCSIWGENIE 0x8B30 /* set generic IE */ +#define SIOCGIWGENIE 0x8B31 /* get generic IE */ + +/* WPA : IEEE 802.11 MLME requests */ +#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses + * struct iw_mlme */ +/* WPA : Authentication mode parameters */ +#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ +#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ + +/* WPA : Extended version of encoding configuration */ +#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ +#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ + +/* WPA2 : PMKSA cache management */ +#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ + +/* Send Mgnt Frame or Action Frame */ +#define SIOCSIWMGNTSEND 0x8B37 /* Send Mgnt Frame or Action Frame */ + +/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ + +/* These 32 ioctl are wireless device private, for 16 commands. + * Each driver is free to use them for whatever purpose it chooses, + * however the driver *must* export the description of those ioctls + * with SIOCGIWPRIV and *must* use arguments as defined below. + * If you don't follow those rules, DaveM is going to hate you (reason : + * it make mixed 32/64bit operation impossible). + */ +#define SIOCIWFIRSTPRIV 0x8BE0 +#define SIOCIWLASTPRIV 0x8BFF + +#define SIOCGIWPRIVPASSPHRASE 0x8BFC +#define SIOCSIWPRIVCOUNTRY 0x8BFD +#define SIOCSIWPRIVAPESSID 0x8BFE +#define SIOCSIWPRIVPASSPHRASE 0x8BFF +/* Previously, we were using SIOCDEVPRIVATE, but we now have our + * separate range because of collisions with other tools such as + * 'mii-tool'. + * We now have 32 commands, so a bit more space ;-). + * Also, all 'even' commands are only usable by root and don't return the + * content of ifr/iwr to user (but you are not obliged to use the set/get + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you + * must be compliant with it. + */ + +/* ------------------------- IOCTL STUFF ------------------------- */ + +/* The first and the last (range) */ +#define SIOCIWFIRST 0x8B00 +#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) + +/* Odd : get (world access), even : set (root access) */ +#define IW_IS_SET(cmd) (!((cmd) & 0x1)) +#define IW_IS_GET(cmd) ((cmd) & 0x1) + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + +/* Indicate Mgnt Frame and Action Frame to uplayer*/ +#define IWEVMGNTRECV 0x8C10 /* Indicate Mgnt Frame to uplayer */ + +/* ------------------------- PRIVATE INFO ------------------------- */ +/* + * The following is used with SIOCGIWPRIV. It allow a driver to define + * the interface (name, type of data) for its private ioctl. + * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV + */ + +#define IW_PRIV_TYPE_MASK 0x7000 /* Type of arguments */ +#define IW_PRIV_TYPE_NONE 0x0000 +#define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ +#define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ +#define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ +#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ +#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ + +#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ + +#define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ + +/* + * Note : if the number of args is fixed and the size < 16 octets, + * instead of passing a pointer we will put args in the iwreq struct... + */ + +/* ----------------------- OTHER CONSTANTS ----------------------- */ + +/* Maximum frequencies in the range struct */ +#define IW_MAX_FREQUENCIES 32 +/* Note : if you have something like 80 frequencies, + * don't increase this constant and don't fill the frequency list. + * The user will be able to set by channel anyway... */ + +/* Maximum bit rates in the range struct */ +#define IW_MAX_BITRATES 32 + +/* Maximum tx powers in the range struct */ +#define IW_MAX_TXPOWER 8 +/* Note : if you more than 8 TXPowers, just set the max and min or + * a few of them in the struct iw_range. */ + +/* Maximum of address that you may set with SPY */ +#define IW_MAX_SPY 8 + +/* Maximum of address that you may get in the + list of access points in range */ +#define IW_MAX_AP 64 + +/* Maximum size of the ESSID and NICKN strings */ +#define IW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define IW_MODE_AUTO 0 /* Let the driver decides */ +#define IW_MODE_ADHOC 1 /* Single cell network */ +#define IW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ + +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ +#define IW_QUAL_ALL_INVALID 0x70 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + +/* Maximum number of size of encoding token available + * they are listed in the range structure */ +#define IW_MAX_ENCODING_SIZES 8 + +/* Maximum size of the encoding token in bytes */ +#define IW_ENCODING_TOKEN_MAX 64 /* 512 bits (for now) */ + +/* Flags for encoding (along with the token) */ +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + +/* Power management flags available (along with the value, if any) */ +#define IW_POWER_ON 0x0000 /* No details... */ +#define IW_POWER_TYPE 0xF000 /* Type of parameter */ +#define IW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define IW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ +#define IW_POWER_SAVING 0x4000 /* Value is relative (how aggressive)*/ +#define IW_POWER_MODE 0x0F00 /* Power Management mode */ +#define IW_POWER_UNICAST_R 0x0100 /* Receive only unicast messages */ +#define IW_POWER_MULTICAST_R 0x0200 /* Receive only multicast messages */ +#define IW_POWER_ALL_R 0x0300 /* Receive all messages though PM */ +#define IW_POWER_FORCE_S 0x0400 /* Force PM procedure for sending unicast */ +#define IW_POWER_REPEATER 0x0800 /* Repeat broadcast messages in PM period */ +#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */ +#define IW_POWER_MIN 0x0001 /* Value is a minimum */ +#define IW_POWER_MAX 0x0002 /* Value is a maximum */ +#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ + +/* Transmit Power flags available */ +#define IW_TXPOW_TYPE 0x00FF /* Type of value */ +#define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ +#define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ +#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ + +/* Retry limits and lifetime flags available */ +#define IW_RETRY_ON 0x0000 /* No details... */ +#define IW_RETRY_TYPE 0xF000 /* Type of parameter */ +#define IW_RETRY_LIMIT 0x1000 /* Maximum number of retries*/ +#define IW_RETRY_LIFETIME 0x2000 /* Maximum duration of retries in us */ +#define IW_RETRY_MODIFIER 0x00FF /* Modify a parameter */ +#define IW_RETRY_MIN 0x0001 /* Value is a minimum */ +#define IW_RETRY_MAX 0x0002 /* Value is a maximum */ +#define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ +#define IW_RETRY_SHORT 0x0010 /* Value is for short packets */ +#define IW_RETRY_LONG 0x0020 /* Value is for long packets */ + +/* Scanning request flags */ +#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ +#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ +#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ +#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ +#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ +#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ +#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ +#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ +#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ +/* struct iw_scan_req scan_type */ +#define IW_SCAN_TYPE_ACTIVE 0 +#define IW_SCAN_TYPE_PASSIVE 1 +/* Maximum size of returned data */ +#define IW_SCAN_MAX_DATA 4096 /* In bytes */ + +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ + +/* Generic information element */ +#define IW_GENERIC_IE_MAX 1024 + +/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ +#define IW_MLME_DEAUTH 0 +#define IW_MLME_DISASSOC 1 +#define IW_MLME_AUTH 2 +#define IW_MLME_ASSOC 3 + +/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ +#define IW_AUTH_INDEX 0x0FFF +#define IW_AUTH_FLAGS 0xF000 +/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) + * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the + * parameter that is being set/get to; value will be read/written to + * struct iw_param value field) */ +#define IW_AUTH_WPA_VERSION 0 +#define IW_AUTH_CIPHER_PAIRWISE 1 +#define IW_AUTH_CIPHER_GROUP 2 +#define IW_AUTH_KEY_MGMT 3 +#define IW_AUTH_TKIP_COUNTERMEASURES 4 +#define IW_AUTH_DROP_UNENCRYPTED 5 +#define IW_AUTH_80211_AUTH_ALG 6 +#define IW_AUTH_WPA_ENABLED 7 +#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define IW_AUTH_ROAMING_CONTROL 9 +#define IW_AUTH_PRIVACY_INVOKED 10 + +/* IW_AUTH_WPA_VERSION values (bit field) */ +#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 +#define IW_AUTH_WPA_VERSION_WPA 0x00000002 +#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 + +/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ +#define IW_AUTH_CIPHER_NONE 0x00000001 +#define IW_AUTH_CIPHER_WEP40 0x00000002 +#define IW_AUTH_CIPHER_TKIP 0x00000004 +#define IW_AUTH_CIPHER_CCMP 0x00000008 +#define IW_AUTH_CIPHER_WEP104 0x00000010 + +/* IW_AUTH_KEY_MGMT values (bit field) */ +#define IW_AUTH_KEY_MGMT_802_1X 1 +#define IW_AUTH_KEY_MGMT_PSK 2 + +/* IW_AUTH_80211_AUTH_ALG values (bit field) */ +#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define IW_AUTH_ALG_SHARED_KEY 0x00000002 +#define IW_AUTH_ALG_LEAP 0x00000004 + +/* IW_AUTH_ROAMING_CONTROL values */ +#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ +#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming + * control */ + +/* SIOCSIWENCODEEXT definitions */ +#define IW_ENCODE_SEQ_MAX_SIZE 8 +/* struct iw_encode_ext ->alg */ +#define IW_ENCODE_ALG_NONE 0 +#define IW_ENCODE_ALG_WEP 1 +#define IW_ENCODE_ALG_TKIP 2 +#define IW_ENCODE_ALG_CCMP 3 +/* struct iw_encode_ext ->ext_flags */ +#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 +#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 +#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 +#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 + +/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ +#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ +#define IW_MICFAILURE_GROUP 0x00000004 +#define IW_MICFAILURE_PAIRWISE 0x00000008 +#define IW_MICFAILURE_STAKEY 0x00000010 +#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) + */ + +/* Bit field values for enc_capa in struct iw_range */ +#define IW_ENC_CAPA_WPA 0x00000001 +#define IW_ENC_CAPA_WPA2 0x00000002 +#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 +#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 + +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + +/* Modulations bitmasks */ +#define IW_MODUL_ALL 0x00000000 /* Everything supported */ +#define IW_MODUL_FH 0x00000001 /* Frequency Hopping */ +#define IW_MODUL_DS 0x00000002 /* Original Direct Sequence */ +#define IW_MODUL_CCK 0x00000004 /* 802.11b : 5.5 + 11 Mb/s */ +#define IW_MODUL_11B (IW_MODUL_DS | IW_MODUL_CCK) +#define IW_MODUL_PBCC 0x00000008 /* TI : 5.5 + 11 + 22 Mb/s */ +#define IW_MODUL_OFDM_A 0x00000010 /* 802.11a : 54 Mb/s */ +#define IW_MODUL_11A (IW_MODUL_OFDM_A) +#define IW_MODUL_11AB (IW_MODUL_11B | IW_MODUL_11A) +#define IW_MODUL_OFDM_G 0x00000020 /* 802.11g : 54 Mb/s */ +#define IW_MODUL_11G (IW_MODUL_11B | IW_MODUL_OFDM_G) +#define IW_MODUL_11AG (IW_MODUL_11G | IW_MODUL_11A) +#define IW_MODUL_TURBO 0x00000040 /* ATH : bonding, 108 Mb/s */ +/* In here we should define MIMO stuff. Later... */ +#define IW_MODUL_CUSTOM 0x40000000 /* Driver specific */ + +/* Bitrate flags available */ +#define IW_BITRATE_TYPE 0x00FF /* Type of value */ +#define IW_BITRATE_UNICAST 0x0001 /* Maximum/Fixed unicast bitrate */ +#define IW_BITRATE_BROADCAST 0x0002 /* Fixed broadcast bitrate */ + +/****************************** TYPES ******************************/ + +/* --------------------------- SUBTYPES --------------------------- */ + +struct sockaddr_t { + __u8 sa_len; + __u8 sa_family; + char sa_data[14]; +}; + +/* + * Generic format for most parameters that fit in an int + */ +struct iw_param +{ + __s32 value; /* The value of the parameter itself */ + __u8 fixed; /* Hardware should not use auto select */ + __u8 disabled; /* Disable the feature */ + __u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct iw_point +{ + void *pointer; /* Pointer to the data (in user space) */ + __u16 length; /* number of fields or size in bytes */ + __u16 flags; /* Optional params */ +}; + +/* + * A frequency + * For numbers lower than 10^9, we encode the number in 'm' and + * set 'e' to 0 + * For number greater than 10^9, we divide it by the lowest power + * of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')... + * The power of 10 is in 'e', the result of the division is in 'm'. + */ +struct iw_freq +{ + __s32 m; /* Mantissa */ + __s16 e; /* Exponent */ + __u8 i; /* List index (when in range struct) */ + __u8 flags; /* Flags (fixed/auto) */ +}; + +/* + * Quality of the link + */ +struct iw_quality +{ + __u8 qual; /* link quality (%retries, SNR, + %missed beacons or better...) */ + __u8 level; /* signal level (dBm) */ + __u8 noise; /* noise level (dBm) */ + __u8 updated; /* Flags to know if updated */ +}; + +/* + * Packet discarded in the wireless adapter due to + * "wireless" specific problems... + * Note : the list of counter and statistics in net_device_stats + * is already pretty exhaustive, and you should use that first. + * This is only additional stats... + */ +struct iw_discarded +{ + __u32 nwid; /* Rx : Wrong nwid/essid */ + __u32 code; /* Rx : Unable to code/decode (WEP) */ + __u32 fragment; /* Rx : Can't perform MAC reassembly */ + __u32 retries; /* Tx : Max MAC retries num reached */ + __u32 misc; /* Others cases */ +}; + +/* + * Packet/Time period missed in the wireless adapter due to + * "wireless" specific problems... + */ +struct iw_missed +{ + __u32 beacon; /* Missed beacons/superframe */ +}; + +/* + * Quality range (for spy threshold) + */ +struct iw_thrspy +{ + struct sockaddr_t addr; /* Source address (hw/mac) */ + struct iw_quality qual; /* Quality of the link */ + struct iw_quality low; /* Low threshold */ + struct iw_quality high; /* High threshold */ +}; + +/* + * Optional data for scan request + * + * Note: these optional parameters are controlling parameters for the + * scanning behavior, these do not apply to getting scan results + * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and + * provide a merged results with all BSSes even if the previous scan + * request limited scanning to a subset, e.g., by specifying an SSID. + * Especially, scan results are required to include an entry for the + * current BSS if the driver is in Managed mode and associated with an AP. + */ +struct iw_scan_req +{ + __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ + __u8 essid_len; + __u8 num_channels; /* num entries in channel_list; + * 0 = scan all allowed channels */ + __u8 flags; /* reserved as padding; use zero, this may + * be used in the future for adding flags + * to request different scan behavior */ + struct sockaddr_t bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or + * individual address of a specific BSS */ + + /* + * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using + * the current ESSID. This allows scan requests for specific ESSID + * without having to change the current ESSID and potentially breaking + * the current association. + */ + __u8 essid[IW_ESSID_MAX_SIZE]; + + /* + * Optional parameters for changing the default scanning behavior. + * These are based on the MLME-SCAN.request from IEEE Std 802.11. + * TU is 1.024 ms. If these are set to 0, driver is expected to use + * reasonable default values. min_channel_time defines the time that + * will be used to wait for the first reply on each channel. If no + * replies are received, next channel will be scanned after this. If + * replies are received, total time waited on the channel is defined by + * max_channel_time. + */ + __u32 min_channel_time; /* in TU */ + __u32 max_channel_time; /* in TU */ + + struct iw_freq channel_list[IW_MAX_FREQUENCIES]; +}; + +/* ------------------------- WPA SUPPORT ------------------------- */ + +/* + * Extended data structure for get/set encoding (this is used with + * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* + * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and + * only the data contents changes (key data -> this structure, including + * key data). + * + * If the new key is the first group key, it will be set as the default + * TX key. Otherwise, default TX key index is only changed if + * IW_ENCODE_EXT_SET_TX_KEY flag is set. + * + * Key will be changed with SIOCSIWENCODEEXT in all cases except for + * special "change TX key index" operation which is indicated by setting + * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. + * + * tx_seq/rx_seq are only used when respective + * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal + * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start + * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally + * used only by an Authenticator (AP or an IBSS station) to get the + * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and + * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for + * debugging/testing. + */ +struct iw_encode_ext +{ + __u32 ext_flags; /* IW_ENCODE_EXT_* */ + __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ + struct sockaddr_t addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast + * (group) keys or unicast address for + * individual keys */ + __u16 alg; /* IW_ENCODE_ALG_* */ + __u16 key_len; +#ifdef __CC_ARM //Fix Keil compile error, must modify sizeof iw_encode_ext - Alex Fang + __u8 key[1]; +#else + __u8 key[0]; +#endif +}; + +/* SIOCSIWMLME data */ +struct iw_mlme +{ + __u16 cmd; /* IW_MLME_* */ + __u16 reason_code; + struct sockaddr_t addr; +}; + +/* SIOCSIWPMKSA data */ +#define IW_PMKSA_ADD 1 +#define IW_PMKSA_REMOVE 2 +#define IW_PMKSA_FLUSH 3 + +#define IW_PMKID_LEN 16 + +struct iw_pmksa +{ + __u32 cmd; /* IW_PMKSA_* */ + struct sockaddr_t bssid; + __u8 pmkid[IW_PMKID_LEN]; +}; + +/* IWEVMICHAELMICFAILURE data */ +struct iw_michaelmicfailure +{ + __u32 flags; + struct sockaddr_t src_addr; + __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ +}; + +/* IWEVPMKIDCAND data */ +#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ +struct iw_pmkid_cand +{ + __u32 flags; /* IW_PMKID_CAND_* */ + __u32 index; /* the smaller the index, the higher the + * priority */ + struct sockaddr_t bssid; +}; + +/* ------------------------ WIRELESS STATS ------------------------ */ +/* + * Wireless statistics (used for /proc/net/wireless) + */ +struct iw_statistics +{ + __u16 status; /* Status + * - device dependent for now */ + + struct iw_quality qual; /* Quality of the link + * (instant/mean/max) */ + struct iw_discarded discard; /* Packet discarded counts */ + struct iw_missed miss; /* Packet missed counts */ +}; + +/* ------------------------ IOCTL REQUEST ------------------------ */ +/* + * This structure defines the payload of an ioctl, and is used + * below. + * + * Note that this structure should fit on the memory footprint + * of iwreq (which is the same as ifreq), which mean a max size of + * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... + * You should check this when increasing the structures defined + * above in this file... + */ +union iwreq_data +{ + /* Config - generic */ + char name[IFNAMSIZ]; + /* Name : used to verify the presence of wireless extensions. + * Name of the protocol/provider... */ + + struct iw_point essid; /* Extended network name */ + struct iw_param nwid; /* network id (or domain - the cell) */ + struct iw_freq freq; /* frequency or channel : + * 0-1000 = channel + * > 1000 = frequency in Hz */ + + struct iw_param sens; /* signal level threshold */ + struct iw_param bitrate; /* default bit rate */ + struct iw_param txpower; /* default transmit power */ + struct iw_param rts; /* RTS threshold threshold */ + struct iw_param frag; /* Fragmentation threshold */ + __u32 mode; /* Operation mode */ + struct iw_param retry; /* Retry limits & lifetime */ + + struct iw_point encoding; /* Encoding stuff : tokens */ + struct iw_param power; /* PM duration/timeout */ + struct iw_quality qual; /* Quality part of statistics */ + + struct sockaddr_t ap_addr; /* Access point address */ + struct sockaddr_t addr; /* Destination address (hw/mac) */ + + struct iw_param param; /* Other small parameters */ + struct iw_point data; /* Other large parameters */ + struct iw_point passphrase; /* Extended network name */ +}; + +/* + * The structure to exchange data for ioctl. + * This structure is the same as 'struct ifreq', but (re)defined for + * convenience... + * Do I need to remind you about structure size (32 octets) ? + */ +struct iwreq +{ +#if 0 + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; +#endif + char ifr_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + + /* Data part (defined just above) */ + union iwreq_data u; +}; + +/* -------------------------- IOCTL DATA -------------------------- */ +/* + * For those ioctl which want to exchange mode data that what could + * fit in the above structure... + */ + +/* + * Range of parameters + */ + +struct iw_range +{ + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; + + /* signal level threshold range */ + __s32 sensitivity; + + /* Quality of link & SNR stuff */ + /* Quality range (link, level, noise) + * If the quality is absolute, it will be in the range [0 ; max_qual], + * if the quality is dBm, it will be in the range [max_qual ; 0]. + * Don't forget that we use 8 bit arithmetics... */ + struct iw_quality max_qual; /* Quality of the link */ + /* This should contain the average/typical values of the quality + * indicator. This should be the threshold between a "good" and + * a "bad" link (example : monitor going from green to orange). + * Currently, user space apps like quality monitors don't have any + * way to calibrate the measurement. With this, they can split + * the range between 0 and max_qual in different quality level + * (using a geometric subdivision centered on the average). + * I expect that people doing the user space apps will feedback + * us on which value we need to put in each driver... */ + struct iw_quality avg_qual; /* Quality of the link */ + + /* Rates */ + __u8 num_bitrates; /* Number of entries in the list */ + __s32 bitrate[IW_MAX_BITRATES]; /* list, in bps */ + + /* RTS threshold */ + __s32 min_rts; /* Minimal RTS threshold */ + __s32 max_rts; /* Maximal RTS threshold */ + + /* Frag threshold */ + __s32 min_frag; /* Minimal frag threshold */ + __s32 max_frag; /* Maximal frag threshold */ + + /* Power Management duration & timeout */ + __s32 min_pmp; /* Minimal PM period */ + __s32 max_pmp; /* Maximal PM period */ + __s32 min_pmt; /* Minimal PM timeout */ + __s32 max_pmt; /* Maximal PM timeout */ + __u16 pmp_flags; /* How to decode max/min PM period */ + __u16 pmt_flags; /* How to decode max/min PM timeout */ + __u16 pm_capa; /* What PM options are supported */ + + /* Encoder stuff */ + __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ + __u8 num_encoding_sizes; /* Number of entry in the list */ + __u8 max_encoding_tokens; /* Max number of tokens */ + /* For drivers that need a "login/passwd" form */ + __u8 encoding_login_index; /* token index for login token */ + + /* Transmit power */ + __u16 txpower_capa; /* What options are supported */ + __u8 num_txpower; /* Number of entries in the list */ + __s32 txpower[IW_MAX_TXPOWER]; /* list, in bps */ + + /* Wireless Extension version info */ + __u8 we_version_compiled; /* Must be WIRELESS_EXT */ + __u8 we_version_source; /* Last update of source */ + + /* Retry limits and lifetime */ + __u16 retry_capa; /* What retry options are supported */ + __u16 retry_flags; /* How to decode max/min retry limit */ + __u16 r_time_flags; /* How to decode max/min retry life */ + __s32 min_retry; /* Minimal number of retries */ + __s32 max_retry; /* Maximal number of retries */ + __s32 min_r_time; /* Minimal retry lifetime */ + __s32 max_r_time; /* Maximal retry lifetime */ + + /* Frequency */ + __u16 num_channels; /* Number of channels [0; num - 1] */ + __u8 num_frequency; /* Number of entry in the list */ + struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ + /* Note : this frequency list doesn't need to fit channel numbers, + * because each entry contain its channel index */ + + __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ + + /* More power management stuff */ + __s32 min_pms; /* Minimal PM saving */ + __s32 max_pms; /* Maximal PM saving */ + __u16 pms_flags; /* How to decode max/min PM saving */ + + /* All available modulations for driver (hw may support less) */ + __s32 modul_capa; /* IW_MODUL_* bit field */ + + /* More bitrate stuff */ + __u32 bitrate_capa; /* Types of bitrates supported */ +}; + +/* + * Private ioctl interface information + */ + +struct iw_priv_args +{ + __u32 cmd; /* Number of the ioctl to issue */ + __u16 set_args; /* Type and number of args */ + __u16 get_args; /* Type and number of args */ + char name[IFNAMSIZ]; /* Name of the extension */ +}; + +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* + * Wireless events are carried through the rtnetlink socket to user + * space. They are encapsulated in the IFLA_WIRELESS field of + * a RTM_NEWLINK message. + */ + +/* + * A Wireless Event. Contains basically the same data as the ioctl... + */ +struct iw_event +{ + __u16 len; /* Real lenght of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + union iwreq_data u; /* IOCTL fixed payload */ +}; + +/* Size of the Event prefix (including padding and alignement junk) */ +#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) +/* Size of the various events */ +#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) +#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) +#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr_t)) +#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) + +/* iw_point events are special. First, the payload (extra data) come at + * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, + * we omit the pointer, so start at an offset. */ +#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ + (char *) NULL) +#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ + IW_EV_POINT_OFF) + +/* Size of the Event prefix when packed in stream */ +#define IW_EV_LCP_PK_LEN (4) +/* Size of the various events when packed in stream */ +#define IW_EV_CHAR_PK_LEN (IW_EV_LCP_PK_LEN + IFNAMSIZ) +#define IW_EV_UINT_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(__u32)) +#define IW_EV_FREQ_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_freq)) +#define IW_EV_PARAM_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_param)) +#define IW_EV_ADDR_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct sockaddr_t)) +#define IW_EV_QUAL_PK_LEN (IW_EV_LCP_PK_LEN + sizeof(struct iw_quality)) +#define IW_EV_POINT_PK_LEN (IW_EV_LCP_LEN + 4) + +#define IW_EXT_STR_FOURWAY_DONE "WPA/WPA2 handshake done" +#define IW_EXT_STR_RECONNECTION_FAIL "RECONNECTION FAILURE" +#define IW_EVT_STR_STA_ASSOC "STA Assoc" +#define IW_EVT_STR_STA_DISASSOC "STA Disassoc" +#define IW_EVT_STR_SEND_ACTION_DONE "Send Action Done" +#define IW_EVT_STR_NO_NETWORK "No Assoc Network After Scan Done" +#endif /* _LINUX_WIRELESS_H */ diff --git a/component/common/drivers/wlan/realtek/src/osdep/wlan_intf.h b/component/common/drivers/wlan/realtek/src/osdep/wlan_intf.h new file mode 100644 index 0000000..ebabd19 --- /dev/null +++ b/component/common/drivers/wlan/realtek/src/osdep/wlan_intf.h @@ -0,0 +1,66 @@ +#ifndef __WLAN_INTF_H__ +#define __WLAN_INTF_H__ + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include "wifi_constants.h" + +#ifndef WLAN0_IDX + #define WLAN0_IDX 0 +#endif +#ifndef WLAN1_IDX + #define WLAN1_IDX 1 +#endif +#ifndef WLAN_UNDEF + #define WLAN_UNDEF -1 +#endif + +/***********************************************************/ +/* +struct sk_buff { + // These two members must be first. + struct sk_buff *next; // Next buffer in list + struct sk_buff *prev; // Previous buffer in list + + struct sk_buff_head *list; // List we are on + unsigned char *head; // Head of buffer + unsigned char *data; // Data head pointer + unsigned char *tail; // Tail pointer + unsigned char *end; //End pointer + struct net_device *dev; //Device we arrived on/are leaving by + unsigned int len; // Length of actual data +}; +*/ +/************************************************************/ + +//----- ------------------------------------------------------------------ +// Wlan Interface opened for upper layer +//----- ------------------------------------------------------------------ +int rltk_wlan_init(int idx_wlan, rtw_mode_t mode); //return 0: success. -1:fail +void rltk_wlan_deinit(void); +void rltk_wlan_start(int idx_wlan); +void rltk_wlan_statistic(unsigned char idx); +unsigned char rltk_wlan_running(unsigned char idx); // interface is up. 0: interface is down +int rltk_wlan_control(unsigned long cmd, void *data); +int rltk_wlan_handshake_done(void); +int rltk_wlan_rf_on(void); +int rltk_wlan_rf_off(void); +int rltk_wlan_check_bus(void); +int rltk_wlan_wireless_mode(unsigned char mode); +int rltk_wlan_set_wps_phase(unsigned char is_trigger_wps); +int rtw_ps_enable(int enable); +void rltk_wlan_pre_sleep_processing(void); +void rltk_wlan_post_sleep_processing(void); + + +#ifdef __cplusplus +} +#endif + + + +#endif //#ifndef __WLAN_INTF_H__ diff --git a/component/common/example/cJSON/cJSON_example.c b/component/common/example/cJSON/cJSON_example.c new file mode 100644 index 0000000..f336a36 --- /dev/null +++ b/component/common/example/cJSON/cJSON_example.c @@ -0,0 +1,84 @@ +#include "cmsis_os.h" +#include + +#define malloc pvPortMalloc +#define free vPortFree + +/* The data structure for this example + +{ + "Motion_Sensor" : "i", + "Light" : { + "Red" : "0", + "Green" : "0", + "Blue" : "0", + } + +} + +*/ +static void gen_json_data(int i, int r, int g, int b) +{ + + + cJSON_Hooks memoryHook; + + memoryHook.malloc_fn = malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); + + + cJSON *IOTJSObject = NULL, *colorJSObject = NULL; + char *iot_json = NULL; + + if((IOTJSObject = cJSON_CreateObject()) != NULL) { + + cJSON_AddItemToObject(IOTJSObject, "Motion_Sensor", cJSON_CreateNumber(i)); + cJSON_AddItemToObject(IOTJSObject, "Light", colorJSObject = cJSON_CreateObject()); + + cJSON_AddItemToObject(colorJSObject, "Red", cJSON_CreateNumber(r)); + cJSON_AddItemToObject(colorJSObject, "Green", cJSON_CreateNumber(g)); + cJSON_AddItemToObject(colorJSObject, "Blue", cJSON_CreateNumber(b)); + + iot_json = cJSON_Print(IOTJSObject); + cJSON_Delete(IOTJSObject); + + } + +} + +static void handle_json_data(char *iot_json) +{ + cJSON_Hooks memoryHook; + + memoryHook.malloc_fn = malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); + + + cJSON *IOTJSObject, *sensorJSObject, *lightJSObject, *redJSObject, *greenJSObject, *blueJSObject; + int sensor_data, red, green, blue; + + if((IOTJSObject = cJSON_Parse(iot_json)) != NULL) { + sensorJSObject = cJSON_GetObjectItem(IOTJSObject, "Motion_Sensor"); + if(sensorJSObject) + sensor_data = sensorJSObject->valueint; + + lightJSObject = cJSON_GetObjectItem(IOTJSObject, "Light"); + + if(lightJSObject){ + redJSObject = cJSON_GetObjectItem(lightJSObject, "Red"); + greenJSObject = cJSON_GetObjectItem(lightJSObject, "Green"); + blueJSObject = cJSON_GetObjectItem(lightJSObject, "Blue"); + + if(redJSObject) + red = redJSObject->valueint; + if(greenJSObject) + green = greenJSObject->valueint; + if(blueJSObject) + blue = blueJSObject->valueint; + } + + cJSON_Delete(IOTJSObject); + } +} diff --git a/component/common/example/example_entry.c b/component/common/example/example_entry.c new file mode 100644 index 0000000..37d9f0f --- /dev/null +++ b/component/common/example/example_entry.c @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2015 Realtek Corporation. All rights reserved. + * + * + ******************************************************************************/ +#include + +#if CONFIG_EXAMPLE_MDNS +#include +#endif + +#if CONFIG_EXAMPLE_MCAST +#include +#endif + +#if CONFIG_EXAMPLE_GOOGLE_NEST +#include +#define FromDevice 1 +#define ToDevice 2 +#define TYPE "ToDevice" +#endif + +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT +#include +#endif + +/* + Preprocessor of example +*/ +void pre_example_entry(void) +{ +#if CONFIG_EXAMPLE_WLAN_FAST_CONNECT + example_wlan_fast_connect(); +#endif + +#if CONFIG_EXAMPLE_UART_ADAPTER + example_uart_adapter_init(); +#endif +} + +/* + All of the examples are disabled by default for code size consideration + The configuration is enabled in platform_opts.h +*/ +void example_entry(void) +{ +#if (CONFIG_EXAMPLE_MDNS && !CONFIG_EXAMPLE_UART_ADAPTER) + example_mdns(); +#endif + +#if CONFIG_EXAMPLE_MCAST + example_mcast(); +#endif + +#if CONFIG_EXAMPLE_GOOGLE_NEST + example_google(TYPE); +#endif +} diff --git a/component/common/example/example_entry.h b/component/common/example/example_entry.h new file mode 100644 index 0000000..2cdd514 --- /dev/null +++ b/component/common/example/example_entry.h @@ -0,0 +1,8 @@ +#ifndef __EXAMPLE_ENTRY_H__ +#define __EXAMPLE_ENTRY_H__ + + +void example_entry(void); +void pre_example_entry(void); + +#endif //#ifndef __EXAMPLE_ENTRY_H__ diff --git a/component/common/example/googlenest/example.html b/component/common/example/googlenest/example.html new file mode 100644 index 0000000..858f0d4 --- /dev/null +++ b/component/common/example/googlenest/example.html @@ -0,0 +1,38 @@ + + + + + + + +

Realtek Google Nest API example

+
+ + +

Data To Device

+ +

Input the value of RGB to change the state of light:

+

Red +Green +Blue + +

+ + + + + diff --git a/component/common/example/googlenest/example_google.c b/component/common/example/googlenest/example_google.c new file mode 100644 index 0000000..bf9803c --- /dev/null +++ b/component/common/example/googlenest/example_google.c @@ -0,0 +1,185 @@ +#include "cmsis_os.h" +#include "diag.h" +#include "wifi_conf.h" +#include "wifi_ind.h" +#include "google_nest.h" + + +#include +#include "cJSON.h" + +osThreadId google_thread_id; +#define malloc pvPortMalloc +#define free vPortFree + +void google_data_retrieve(char *response_buf); + +void google_data_retrieve(char *response_buf) { + //printf("\r\n\r\n\r\nResponse_buf:\r\n%s\r\n", response_buf); + char *event = NULL; + char *delims = "\n"; + char *data = NULL, *backup = NULL; + char *info = NULL; + cJSON_Hooks memoryHook; + + event = strtok_r(response_buf, delims, &backup); + data = strtok_r( NULL, delims, &backup ); + + if (!strncmp(data, "data: ", strlen("data: "))){ + info = data + strlen("data: "); + if(info != NULL){ + memoryHook.malloc_fn = malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); + + cJSON *infoJSObject, *pathJSObject, *dataJSObject; + cJSON *redJSObject, *greenJSObject, *blueJSObject; + char *red, *green, *blue; + + + if((infoJSObject = cJSON_Parse(info)) != NULL) { + pathJSObject = cJSON_GetObjectItem(infoJSObject, "path"); + dataJSObject = cJSON_GetObjectItem(infoJSObject, "data"); + if(dataJSObject != NULL) { + + redJSObject = cJSON_GetObjectItem(dataJSObject, "Red"); + greenJSObject = cJSON_GetObjectItem(dataJSObject, "Green"); + blueJSObject = cJSON_GetObjectItem(dataJSObject, "Blue"); + + if(redJSObject) + red = redJSObject->valuestring; + + if(greenJSObject) + green = greenJSObject->valuestring; + + if(blueJSObject) + blue = blueJSObject->valuestring; + + printf("\n\rThe latest RGB information: RGB(%s, %s, %s)\n\r", red, green, blue); + + cJSON_Delete(dataJSObject); + } + cJSON_Delete(infoJSObject); + } + else + printf("\r\nCannot parse the message to JSON!\r\n"); + + } + else + printf("\r\n This is the keep alive message or cannot get the information!\r\n"); + } + else + printf("\r\nData structure may wrong!\r\n"); +} + + +void gn_todevice_start(void) { + + googlenest_context googlenest; + char *googlenest_host = HOST_ADDR; + char *googlenest_uri = "light.json"; + + printf("\r\nStart connecting to Google Nest Server...\r\n"); + memset(&googlenest, 0, sizeof(googlenest_context)); + +reconnect: + if(gn_connect(&googlenest, googlenest_host, GN_PORT) == 0) { + printf("\r\n Connection is OK!\r\n"); + + google_retrieve_data_hook_callback(google_data_retrieve); + if(gn_stream(&googlenest, googlenest_uri) != 0){ + printf("\r\n Connection is fail! \r\n Start Reconnecting...\r\n"); + goto reconnect; + } + + gn_close(&googlenest); + + } + else{ + printf("\r\n Connection is fail! \r\n\r\n\r\n\r\nStart Reconnecting...\r\n"); + goto reconnect; + } + +} + +void gn_fromdevice_start(void) { + googlenest_context googlenest; + char *googlenest_uri = "MotionSensor.json"; + cJSON_Hooks memoryHook; + int j = 0; + + memoryHook.malloc_fn = malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); + printf("\r\nStart connecting to Google Nest Server!\r\n"); + + while(1) { + memset(&googlenest, 0, sizeof(googlenest_context)); + if(gn_connect(&googlenest, HOST_ADDR, GN_PORT) == 0) { + cJSON *MSJSObject; + char *data; + + if((MSJSObject = cJSON_CreateObject()) != NULL) { + cJSON_AddItemToObject(MSJSObject, "MotionSenser", cJSON_CreateNumber(j++)); + data = cJSON_Print(MSJSObject); + cJSON_Delete(MSJSObject); + } + + if(gn_put(&googlenest, googlenest_uri, data) == 0) + printf("\n\rUpdate the Motion Sensor's data to %d\n\r", (j-1)); + free(data); + gn_close(&googlenest); + } + else{ + printf("\n\rConnection failed!\n\r"); + break; + } + + vTaskDelay(5 * configTICK_RATE_HZ); + } +} + +void gn_fromdevice_task(void *arg) { + int i; + + printf("\n\r\n\r\n\r\n\r<<<<<>>>>>>\n\r\n\r\n\r\n\r"); + for (i = 0; i<120; i++) + vTaskDelay(1000 / portTICK_PERIOD_MS); + + gn_fromdevice_start(); +} + +void gn_todevice_task(void *arg) { + int i; + + printf("\n\r\n\r\n\r\n\r<<<<<>>>>>>\n\r\n\r\n\r\n\r"); + for (i = 0; i<120; i++) + vTaskDelay(1000 / portTICK_PERIOD_MS); + + gn_todevice_start(); +} + +void example_google(char *type) { + + + if(strcmp(type, "FromDevice") == 0){ + osThreadDef_t google_thread; + google_thread.instances = 1; + google_thread.name = "google thread"; + google_thread.stacksize = 4096; + google_thread.pthread = (os_pthread)gn_fromdevice_task; + google_thread.tpriority = tskIDLE_PRIORITY+6; + google_thread_id = osThreadCreate(&google_thread, NULL); + } + else if(strcmp(type, "ToDevice") == 0){ + osThreadDef_t google_thread; + google_thread.instances = 1; + google_thread.name = "google thread"; + google_thread.stacksize = 4096; + google_thread.pthread = (os_pthread)gn_todevice_task; + google_thread.tpriority = tskIDLE_PRIORITY+6; + google_thread_id = osThreadCreate(&google_thread, NULL); + } + +} + diff --git a/component/common/example/googlenest/example_google.h b/component/common/example/googlenest/example_google.h new file mode 100644 index 0000000..8de92d0 --- /dev/null +++ b/component/common/example/googlenest/example_google.h @@ -0,0 +1,10 @@ +#ifndef GOOGLE_THREAD_H +#define GOOGLE_THREAD_H + +#define HOST_ADDR "your_firebase_address.firebaseio.com" +#define GN_PORT 443 + +void example_google(char *type); + + +#endif diff --git a/component/common/example/mcast/example_mcast.c b/component/common/example/mcast/example_mcast.c new file mode 100644 index 0000000..b18dae8 --- /dev/null +++ b/component/common/example/mcast/example_mcast.c @@ -0,0 +1,98 @@ +#include "FreeRTOS.h" +#include "task.h" +#include + +#include +#include +#include +extern struct netif xnetif[]; + +static void example_mcast_thread(void *param) +{ +#if LWIP_IGMP + int err = 0; + int socket = -1; + char *group_ip = "224.0.0.251"; + uint16_t port = 5353; + + // Set NETIF_FLAG_IGMP flag for netif which should process IGMP messages + xnetif[0].flags |= NETIF_FLAG_IGMP; + + if((socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + printf("ERROR: socket - AF_INET, SOCK_DGRAM\n"); + err = -1; + } + + // Add multicast group membership on this interface + if(err == 0) { + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = inet_addr(group_ip); + imr.imr_interface.s_addr = INADDR_ANY; + err = setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + if(err < 0) printf("ERROR: setsockopt - IP_ADD_MEMBERSHIP\n"); + } + + // Specify outgoing interface too + if(err == 0) { + struct in_addr intfAddr; + intfAddr.s_addr = INADDR_ANY; + err = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &intfAddr, sizeof(struct in_addr)); + if(err < 0) printf("ERROR: setsockopt - IP_MULTICAST_IF\n"); + } + + // And start listening for packets + if(err == 0) { + struct sockaddr_in bindAddr; + bindAddr.sin_family = AF_INET; + bindAddr.sin_port = htons(port); + bindAddr.sin_addr.s_addr = INADDR_ANY; + err = bind(socket, (struct sockaddr *) &bindAddr, sizeof(bindAddr)); + if(err < 0) printf("ERROR: bind\n"); + } + + if(err == 0) { + unsigned char packet[1024]; + + while(1) { + // Receive multicast + int packetLen; + struct sockaddr from; + struct sockaddr_in *from_sin = (struct sockaddr_in*) &from; + socklen_t fromLen = sizeof(from); + + if((packetLen = recvfrom(socket, &packet, sizeof(packet), 0, &from, &fromLen)) >= 0) { + uint8_t *ip = (uint8_t *) &from_sin->sin_addr.s_addr; + uint16_t from_port = ntohs(from_sin->sin_port); + printf("recvfrom - %d bytes from %d.%d.%d.%d:%d\n", packetLen, ip[0], ip[1], ip[2], ip[3], from_port); + } + + // Send multicast + if(packetLen > 0) { + int sendLen; + struct sockaddr to; + struct sockaddr_in *to_sin = (struct sockaddr_in*) &to; + to_sin->sin_family = AF_INET; + to_sin->sin_port = htons(port); + to_sin->sin_addr.s_addr = inet_addr(group_ip); + + if((sendLen = sendto(socket, packet, packetLen, 0, &to, sizeof(struct sockaddr))) < 0) + printf("ERROR: sendto %s\n", group_ip); + else + printf("sendto - %d bytes to %s:%d\n", sendLen, group_ip, port); + } + } + } + else if(socket != -1) { + close(socket); + } +#else + printf("\nSHOULD ENABLE LWIP_IGMP\n"); +#endif + vTaskDelete(NULL); +} + +void example_mcast(void) +{ + if(xTaskCreate(example_mcast_thread, ((const char*)"example_mcast_thread"), 2048, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS) + printf("\n\r%s xTaskCreate(init_thread) failed", __FUNCTION__); +} diff --git a/component/common/example/mcast/example_mcast.h b/component/common/example/mcast/example_mcast.h new file mode 100644 index 0000000..7be6823 --- /dev/null +++ b/component/common/example/mcast/example_mcast.h @@ -0,0 +1,6 @@ +#ifndef EXAMPLE_MCAST_H +#define EXAMPLE_MCAST_H + +void example_mcast(void); + +#endif /* EXAMPLE_MCAST_H */ diff --git a/component/common/example/mcast/readme.txt b/component/common/example/mcast/readme.txt new file mode 100644 index 0000000..1a6409d --- /dev/null +++ b/component/common/example/mcast/readme.txt @@ -0,0 +1,17 @@ +LWIP MULTICAST EXAMPLE + +Description: +Join multicast group of 224.0.0.251 and listen on port 5353. +Send packet with the content of received packet to multicast group of 224.0.0.251. + +Configuration: +[lwipopts.h] + #define LWIP_UDP 1 + #define LWIP_IGMP 1 +[platform_opts.h] + #define CONFIG_EXAMPLE_MCAST 1 + +Execution: +Can make automatical Wi-Fi connection when booting by using wlan fast connect example. +A multicast example thread will be started automatically when booting. + diff --git a/component/common/example/mdns/example_mdns.c b/component/common/example/mdns/example_mdns.c new file mode 100644 index 0000000..3dd66a4 --- /dev/null +++ b/component/common/example/mdns/example_mdns.c @@ -0,0 +1,55 @@ +#include "FreeRTOS.h" +#include "task.h" +#include + +#include + +#include +#include +extern struct netif xnetif[]; + +static void example_mdns_thread(void *param) +{ + DNSServiceRef dnsServiceRef = NULL; + TXTRecordRef txtRecord; + unsigned char txt_buf[100]; // use fixed buffer for text record to prevent malloc/free + + // Delay to wait for IP by DHCP + vTaskDelay(10000); + + printf("\nmDNS Init\n"); + if(mDNSResponderInit() == 0) { + printf("mDNS Register service\n"); + TXTRecordCreate(&txtRecord, sizeof(txt_buf), txt_buf); + TXTRecordSetValue(&txtRecord, "text1", strlen("text1_content"), "text1_content"); + TXTRecordSetValue(&txtRecord, "text2", strlen("text2_content"), "text2_content"); + dnsServiceRef = mDNSRegisterService("ameba", "_service1._tcp", "local", 5000, &txtRecord); + TXTRecordDeallocate(&txtRecord); + printf("wait for 30s ... \n"); + vTaskDelay(30*1000); + + printf("mDNS Update service\n"); + TXTRecordCreate(&txtRecord, sizeof(txt_buf), txt_buf); + TXTRecordSetValue(&txtRecord, "text1", strlen("text1_content_new"), "text1_content_new"); + mDNSUpdateService(dnsServiceRef, &txtRecord); + TXTRecordDeallocate(&txtRecord); + printf("wait for 30s ... \n"); + vTaskDelay(30*1000); + + if(dnsServiceRef) + mDNSDeregisterService(dnsServiceRef); + + // deregister service before mdns deinit is not necessary + // mDNS deinit will also deregister all services + printf("mDNS Deinit\n"); + mDNSResponderDeinit(); + } + + vTaskDelete(NULL); +} + +void example_mdns(void) +{ + if(xTaskCreate(example_mdns_thread, ((const char*)"example_mdns_thread"), 1024, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS) + printf("\n\r%s xTaskCreate(init_thread) failed", __FUNCTION__); +} diff --git a/component/common/example/mdns/example_mdns.h b/component/common/example/mdns/example_mdns.h new file mode 100644 index 0000000..39c4914 --- /dev/null +++ b/component/common/example/mdns/example_mdns.h @@ -0,0 +1,6 @@ +#ifndef EXAMPLE_MDNS_H +#define EXAMPLE_MDNS_H + +void example_mdns(void); + +#endif /* EXAMPLE_MDNS_H */ diff --git a/component/common/example/wlan_fast_connect/example_wlan_fast_connect.c b/component/common/example/wlan_fast_connect/example_wlan_fast_connect.c new file mode 100644 index 0000000..07e6caa --- /dev/null +++ b/component/common/example/wlan_fast_connect/example_wlan_fast_connect.c @@ -0,0 +1,125 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2015 Realtek Corporation. All rights reserved. + * + * + ******************************************************************************/ + + /** @file + + This example demonstrate how to implement wifi fast reconnection +**/ +#include +#include + + +#include "task.h" +#include +#include +#include "flash_api.h" + +write_reconnect_ptr p_write_reconnect_ptr; + +extern void fATW0(void *arg); +extern void fATW1(void *arg); +extern void fATW2(void *arg); +extern void fATWC(void *arg); + +/* +* Usage: +* wifi connection indication trigger this function to save current +* wifi profile in flash +* +* Condition: +* CONFIG_EXAMPLE_WLAN_FAST_CONNECT flag is set +*/ + +int wlan_wrtie_reconnect_data_to_flash(uint8_t *data, uint32_t len) +{ + flash_t flash; + uint8_t read_data [FAST_RECONNECT_DATA_LEN]; + if(!data) + return -1; + + flash_stream_read(&flash, FAST_RECONNECT_DATA, FAST_RECONNECT_DATA_LEN, (uint8_t *) read_data); + + //wirte it to flash if different content: SSID, Passphrase, Channel + if(memcmp((void *)data, (void *)read_data, NDIS_802_11_LENGTH_SSID) || + memcmp((void *)(data + NDIS_802_11_LENGTH_SSID + 4), (void *)(read_data + NDIS_802_11_LENGTH_SSID + 4), 32) || + memcmp((void *)(data + NDIS_802_11_LENGTH_SSID + 4 + 32 + A_SHA_DIGEST_LEN * 2), (void *)(read_data + NDIS_802_11_LENGTH_SSID + 4 + 32 + A_SHA_DIGEST_LEN * 2), 4)){ + printf("\r\n %s():not the same ssid/passphrase/channel, need to flash it", __func__); + + flash_erase_sector(&flash, FAST_RECONNECT_DATA); + flash_stream_write(&flash, FAST_RECONNECT_DATA, len, (uint8_t *) data); + } + + return 0; +} + +/* +* Usage: +* After wifi init done, waln driver call this function to check whether +* auto-connect is required. +* +* This function read previous saved wlan profile in flash and execute connection. +* +* Condition: +* CONFIG_EXAMPLE_WLAN_FAST_CONNECT flag is set +*/ +int wlan_init_done_callback() +{ + flash_t flash; + uint8_t *data; + uint32_t channel; + uint8_t pscan_config; + char key_id; + + data = (uint8_t *)rtw_zmalloc(FAST_RECONNECT_DATA_LEN); + flash_stream_read(&flash, FAST_RECONNECT_DATA, FAST_RECONNECT_DATA_LEN, (uint8_t *)data); + if(*((uint32_t *) data) != ~0x0){ + memcpy(psk_essid, (uint8_t *)data, NDIS_802_11_LENGTH_SSID + 4); + memcpy(psk_passphrase, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4, (IW_PASSPHRASE_MAX_SIZE + 1)); + memcpy(wpa_global_PSK, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1), A_SHA_DIGEST_LEN * 2); + memcpy(&channel, (uint8_t *)data + NDIS_802_11_LENGTH_SSID + 4 + (IW_PASSPHRASE_MAX_SIZE + 1) + A_SHA_DIGEST_LEN * 2, 4); + sprintf(&key_id,"%d",(channel >> 28)); + channel &= 0xff; + pscan_config = PSCAN_ENABLE | PSCAN_FAST_SURVEY; + //set partial scan for entering to listen beacon quickly + wifi_set_pscan_chan((uint8_t *)&channel, &pscan_config, 1); + +#ifdef CONFIG_AUTO_RECONNECT + wifi_set_autoreconnect(1); +#endif + //set wifi connect + //open mode + if(!strlen((char*)psk_passphrase)){ + fATW0((char*)psk_essid); + } + //wep mode + else if( strlen((char*)psk_passphrase) == 5 || strlen((char*)psk_passphrase) == 13){ + fATW0((char*)psk_essid); + fATW1((char*)psk_passphrase); + fATW2(&key_id); + } + //WPA/WPA2 + else{ + fATW0((char*)psk_essid); + fATW1((char*)psk_passphrase); + } + fATWC(NULL); + } + if(data) + rtw_mfree(data); + return 0; +} + + +void example_wlan_fast_connect() +{ + // Call back from wlan driver after wlan init done + p_wlan_init_done_callback = wlan_init_done_callback; + + // Call back from application layer after wifi_connection success + p_write_reconnect_ptr = wlan_wrtie_reconnect_data_to_flash; + +} diff --git a/component/common/example/wlan_fast_connect/example_wlan_fast_connect.h b/component/common/example/wlan_fast_connect/example_wlan_fast_connect.h new file mode 100644 index 0000000..d8851dd --- /dev/null +++ b/component/common/example/wlan_fast_connect/example_wlan_fast_connect.h @@ -0,0 +1,35 @@ +#ifndef __EXAMPLE_FAST_RECONNECTION_H__ +#define __EXAMPLE_FAST_RECONNECTION_H__ + + +/****************************************************************************** + * + * Copyright(c) 2007 - 2015 Realtek Corporation. All rights reserved. + * + * + ******************************************************************************/ +#include "FreeRTOS.h" +#include + +#define IW_PASSPHRASE_MAX_SIZE 64 +#define FAST_RECONNECT_DATA (0xC0000 - 0x1000) +#define NDIS_802_11_LENGTH_SSID 32 +#define A_SHA_DIGEST_LEN 20 +#define FAST_RECONNECT_DATA_LEN (NDIS_802_11_LENGTH_SSID+4 +(IW_PASSPHRASE_MAX_SIZE + 1)+ A_SHA_DIGEST_LEN * 2 + 4) + +typedef int (*wlan_init_done_ptr)(void); +typedef int (*write_reconnect_ptr)(uint8_t *data, uint32_t len); + +//Variable +extern unsigned char psk_essid[NET_IF_NUM][NDIS_802_11_LENGTH_SSID+4]; +extern unsigned char psk_passphrase[NET_IF_NUM][IW_PASSPHRASE_MAX_SIZE + 1]; +extern unsigned char wpa_global_PSK[NET_IF_NUM][A_SHA_DIGEST_LEN * 2]; +extern unsigned char psk_passphrase64[IW_PASSPHRASE_MAX_SIZE + 1]; + +//Function +extern wlan_init_done_ptr p_wlan_init_done_callback; +extern write_reconnect_ptr p_write_reconnect_ptr; + +void example_wlan_fast_connect(void); + +#endif //#ifndef __EXAMPLE_FAST_RECONNECTION_H__ diff --git a/component/common/mbed/api/error.h b/component/common/mbed/api/error.h new file mode 100644 index 0000000..3a40358 --- /dev/null +++ b/component/common/mbed/api/error.h @@ -0,0 +1,66 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ERROR_H +#define MBED_ERROR_H + +/** To generate a fatal compile-time error, you can use the pre-processor #error directive. + * + * @code + * #error "That shouldn't have happened!" + * @endcode + * + * If the compiler evaluates this line, it will report the error and stop the compile. + * + * For example, you could use this to check some user-defined compile-time variables: + * + * @code + * #define NUM_PORTS 7 + * #if (NUM_PORTS > 4) + * #error "NUM_PORTS must be less than 4" + * #endif + * @endcode + * + * Reporting Run-Time Errors: + * To generate a fatal run-time error, you can use the mbed error() function. + * + * @code + * error("That shouldn't have happened!"); + * @endcode + * + * If the mbed running the program executes this function, it will print the + * message via the USB serial port, and then die with the blue lights of death! + * + * The message can use printf-style formatting, so you can report variables in the + * message too. For example, you could use this to check a run-time condition: + * + * @code + * if(x >= 5) { + * error("expected x to be less than 5, but got %d", x); + * } + * #endcode + */ + +#ifdef __cplusplus +extern "C" { +#endif + +void error(const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/api/mbed_assert.h b/component/common/mbed/api/mbed_assert.h new file mode 100644 index 0000000..1bcfb09 --- /dev/null +++ b/component/common/mbed/api/mbed_assert.h @@ -0,0 +1,50 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ASSERT_H +#define MBED_ASSERT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal mbed assert function which is invoked when MBED_ASSERT macro failes. + * This function is active only if NDEBUG is not defined prior to including this + * assert header file. + * In case of MBED_ASSERT failing condition, the assertation message is printed + * to stderr and mbed_die() is called. + * @param expr Expresion to be checked. + * @param file File where assertation failed. + * @param line Failing assertation line number. + */ +void mbed_assert_internal(const char *expr, const char *file, int line); + +#ifdef __cplusplus +} +#endif + +#ifdef NDEBUG +#define MBED_ASSERT(expr) ((void)0) + +#else +#define MBED_ASSERT(expr) \ +do { \ + if (!(expr)) { \ + mbed_assert_internal(#expr, __FILE__, __LINE__); \ + } \ +} while (0) +#endif + +#endif diff --git a/component/common/mbed/api/wait_api.h b/component/common/mbed/api/wait_api.h new file mode 100644 index 0000000..03c2714 --- /dev/null +++ b/component/common/mbed/api/wait_api.h @@ -0,0 +1,66 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_WAIT_API_H +#define MBED_WAIT_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Generic wait functions. + * + * These provide simple NOP type wait capabilities. + * + * Example: + * @code + * #include "mbed.h" + * + * DigitalOut heartbeat(LED1); + * + * int main() { + * while (1) { + * heartbeat = 1; + * wait(0.5); + * heartbeat = 0; + * wait(0.5); + * } + * } + */ + +/** Waits for a number of seconds, with microsecond resolution (within + * the accuracy of single precision floating point). + * + * @param s number of seconds to wait + */ +void wait(float s); + +/** Waits a number of milliseconds. + * + * @param ms the whole number of milliseconds to wait + */ +void wait_ms(int ms); + +/** Waits a number of microseconds. + * + * @param us the whole number of microseconds to wait + */ +void wait_us(int us); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/common/.gitkeep b/component/common/mbed/common/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/component/common/mbed/common/Makefile b/component/common/mbed/common/Makefile new file mode 100644 index 0000000..dd65552 --- /dev/null +++ b/component/common/mbed/common/Makefile @@ -0,0 +1,52 @@ + +include $(MAKE_INCLUDE_GEN) + +.PHONY: all clean + +MODULE_IFLAGS = -I../ + +#*****************************************************************************# +# Object FILE LIST # +#*****************************************************************************# +OBJS = +OBJS_ROM = +OBJS_RAM = +ifeq ($(CONFIG_TIMER_MODULE),y) + OBJS += us_ticker_api.o wait_api.o +endif + +ifeq ($(CONFIG_LIB_BUILD_RAM),y) + OBJS = $(OBJS_RAM) +else ifeq ($(CONFIG_RELEASE_BUILD_RAM_ALL),y) + OBJS += $(OBJS_RAM) +else ifeq ($(CONFIG_RELEASE_BUILD_LIBRARIES),y) + OBJS = $(CSRC_ROM) +else ifeq ($(CONFIG_NORMAL_BUILD),y) + OBJS += $(CSRC_ROM) + OBJS += $(CSRC_RAM) +endif + + +#*****************************************************************************# +# RULES TO GENERATE TARGETS # +#*****************************************************************************# + +# Define the Rules to build the core targets +all: CORE_TARGETS COPY_RAM_OBJS + +#*****************************************************************************# +# GENERATE OBJECT FILE +#*****************************************************************************# +CORE_TARGETS: $(OBJS) + + +#*****************************************************************************# +# RULES TO CLEAN TARGETS # +#*****************************************************************************# +clean: + $(REMOVE) *.o + $(REMOVE) *.i + $(REMOVE) *.s + $(REMOVE) *.d + +-include $(DEPS) \ No newline at end of file diff --git a/component/common/mbed/common/us_ticker_api.c b/component/common/mbed/common/us_ticker_api.c new file mode 100644 index 0000000..8218b9c --- /dev/null +++ b/component/common/mbed/common/us_ticker_api.c @@ -0,0 +1,118 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "us_ticker_api.h" +#include "cmsis.h" + +static ticker_event_handler event_handler; +static ticker_event_t *head = NULL; + +void us_ticker_set_handler(ticker_event_handler handler) { + us_ticker_init(); + + event_handler = handler; +} + +void us_ticker_irq_handler(void) { + us_ticker_clear_interrupt(); + + /* Go through all the pending TimerEvents */ + while (1) { + if (head == NULL) { + // There are no more TimerEvents left, so disable matches. + us_ticker_disable_interrupt(); + return; + } + + if ((int)(head->timestamp - us_ticker_read()) <= 0) { + // This event was in the past: + // point to the following one and execute its handler + ticker_event_t *p = head; + head = head->next; + if (event_handler != NULL) { + event_handler(p->id); // NOTE: the handler can set new events + } + /* Note: We continue back to examining the head because calling the + * event handler may have altered the chain of pending events. */ + } else { + // This event and the following ones in the list are in the future: + // set it as next interrupt and return + us_ticker_set_interrupt(head->timestamp); + return; + } + } +} + +void us_ticker_insert_event(ticker_event_t *obj, timestamp_t timestamp, uint32_t id) { + /* disable interrupts for the duration of the function */ + __disable_irq(); + + // initialise our data + obj->timestamp = timestamp; + obj->id = id; + + /* Go through the list until we either reach the end, or find + an element this should come before (which is possibly the + head). */ + ticker_event_t *prev = NULL, *p = head; + while (p != NULL) { + /* check if we come before p */ + if ((int)(timestamp - p->timestamp) <= 0) { + break; + } + /* go to the next element */ + prev = p; + p = p->next; + } + /* if prev is NULL we're at the head */ + if (prev == NULL) { + head = obj; + us_ticker_set_interrupt(timestamp); + } else { + prev->next = obj; + } + /* if we're at the end p will be NULL, which is correct */ + obj->next = p; + + __enable_irq(); +} + +void us_ticker_remove_event(ticker_event_t *obj) { + __disable_irq(); + + // remove this object from the list + if (head == obj) { + // first in the list, so just drop me + head = obj->next; + if (head == NULL) { + us_ticker_disable_interrupt(); + } else { + us_ticker_set_interrupt(head->timestamp); + } + } else { + // find the object before me, then drop me + ticker_event_t* p = head; + while (p != NULL) { + if (p->next == obj) { + p->next = obj->next; + break; + } + p = p->next; + } + } + + __enable_irq(); +} diff --git a/component/common/mbed/common/wait_api.c b/component/common/mbed/common/wait_api.c new file mode 100644 index 0000000..f7e8884 --- /dev/null +++ b/component/common/mbed/common/wait_api.c @@ -0,0 +1,30 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "wait_api.h" +#include "us_ticker_api.h" + +void wait(float s) { + wait_us((int)(s * 1000000.0f)); +} + +void wait_ms(int ms) { + wait_us(ms * 1000); +} + +void wait_us(int us) { + uint32_t start = us_ticker_read(); + while ((us_ticker_read() - start) < (uint32_t)us); +} diff --git a/component/common/mbed/hal/analogin_api.h b/component/common/mbed/hal/analogin_api.h new file mode 100644 index 0000000..98d02c1 --- /dev/null +++ b/component/common/mbed/hal/analogin_api.h @@ -0,0 +1,39 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ANALOGIN_API_H +#define MBED_ANALOGIN_API_H + +#include "device.h" + +#if DEVICE_ANALOGIN + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct analogin_s analogin_t; + +void analogin_init (analogin_t *obj, PinName pin); +float analogin_read (analogin_t *obj); +uint16_t analogin_read_u16(analogin_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/analogout_api.h b/component/common/mbed/hal/analogout_api.h new file mode 100644 index 0000000..97a2013 --- /dev/null +++ b/component/common/mbed/hal/analogout_api.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ANALOGOUT_API_H +#define MBED_ANALOGOUT_API_H + +#include "device.h" + +#if DEVICE_ANALOGOUT + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dac_s dac_t; + +void analogout_init (dac_t *obj, PinName pin); +void analogout_free (dac_t *obj); +void analogout_write (dac_t *obj, float value); +void analogout_write_u16(dac_t *obj, uint16_t value); +float analogout_read (dac_t *obj); +uint16_t analogout_read_u16 (dac_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/can_api.h b/component/common/mbed/hal/can_api.h new file mode 100644 index 0000000..48bc104 --- /dev/null +++ b/component/common/mbed/hal/can_api.h @@ -0,0 +1,80 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_CAN_API_H +#define MBED_CAN_API_H + +#include "device.h" + +#if DEVICE_CAN + +#include "PinNames.h" +#include "PeripheralNames.h" +#include "can_helper.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + IRQ_RX, + IRQ_TX, + IRQ_ERROR, + IRQ_OVERRUN, + IRQ_WAKEUP, + IRQ_PASSIVE, + IRQ_ARB, + IRQ_BUS, + IRQ_READY +} CanIrqType; + + +typedef enum { + MODE_RESET, + MODE_NORMAL, + MODE_SILENT, + MODE_TEST_GLOBAL, + MODE_TEST_LOCAL, + MODE_TEST_SILENT +} CanMode; + +typedef void (*can_irq_handler)(uint32_t id, CanIrqType type); + +typedef struct can_s can_t; + +void can_init (can_t *obj, PinName rd, PinName td); +void can_free (can_t *obj); +int can_frequency(can_t *obj, int hz); + +void can_irq_init (can_t *obj, can_irq_handler handler, uint32_t id); +void can_irq_free (can_t *obj); +void can_irq_set (can_t *obj, CanIrqType irq, uint32_t enable); + +int can_write (can_t *obj, CAN_Message, int cc); +int can_read (can_t *obj, CAN_Message *msg, int handle); +int can_mode (can_t *obj, CanMode mode); +int can_filter(can_t *obj, uint32_t id, uint32_t mask, CANFormat format, int32_t handle); +void can_reset (can_t *obj); +unsigned char can_rderror (can_t *obj); +unsigned char can_tderror (can_t *obj); +void can_monitor (can_t *obj, int silent); + +#ifdef __cplusplus +}; +#endif + +#endif // MBED_CAN_API_H + +#endif diff --git a/component/common/mbed/hal/ethernet_api.h b/component/common/mbed/hal/ethernet_api.h new file mode 100644 index 0000000..4cae77e --- /dev/null +++ b/component/common/mbed/hal/ethernet_api.h @@ -0,0 +1,63 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_ETHERNET_API_H +#define MBED_ETHERNET_API_H + +#include "device.h" + +#if DEVICE_ETHERNET + +#ifdef __cplusplus +extern "C" { +#endif + +// Connection constants + +int ethernet_init(void); +void ethernet_free(void); + +// write size bytes from data to ethernet buffer +// return num bytes written +// or -1 if size is too big +int ethernet_write(const char *data, int size); + +// send ethernet write buffer, returning the packet size sent +int ethernet_send(void); + +// recieve from ethernet buffer, returning packet size, or 0 if no packet +int ethernet_receive(void); + +// read size bytes in to data, return actual num bytes read (0..size) +// if data == NULL, throw the bytes away +int ethernet_read(char *data, int size); + +// get the ethernet address +void ethernet_address(char *mac); + +// see if the link is up +int ethernet_link(void); + +// force link settings +void ethernet_set_link(int speed, int duplex); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + diff --git a/component/common/mbed/hal/gpio_api.h b/component/common/mbed/hal/gpio_api.h new file mode 100644 index 0000000..e4cf7fd --- /dev/null +++ b/component/common/mbed/hal/gpio_api.h @@ -0,0 +1,51 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_GPIO_API_H +#define MBED_GPIO_API_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Set the given pin as GPIO + * @param pin The pin to be set as GPIO + * @return The GPIO port mask for this pin + **/ +uint32_t gpio_set(PinName pin); + +/* GPIO object */ +void gpio_init(gpio_t *obj, PinName pin); + +void gpio_mode (gpio_t *obj, PinMode mode); +void gpio_dir (gpio_t *obj, PinDirection direction); + +void gpio_write(gpio_t *obj, int value); +int gpio_read (gpio_t *obj); + +// the following set of functions are generic and are implemented in the common gpio.c file +void gpio_init_in(gpio_t* gpio, PinName pin); +void gpio_init_in_ex(gpio_t* gpio, PinName pin, PinMode mode); +void gpio_init_out(gpio_t* gpio, PinName pin); +void gpio_init_out_ex(gpio_t* gpio, PinName pin, int value); +void gpio_init_inout(gpio_t* gpio, PinName pin, PinDirection direction, PinMode mode, int value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/hal/gpio_irq_api.h b/component/common/mbed/hal/gpio_irq_api.h new file mode 100644 index 0000000..ccdb30c --- /dev/null +++ b/component/common/mbed/hal/gpio_irq_api.h @@ -0,0 +1,47 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_GPIO_IRQ_API_H +#define MBED_GPIO_IRQ_API_H + +#include "device.h" + +#if DEVICE_INTERRUPTIN + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + IRQ_NONE, + IRQ_RISE, + IRQ_FALL +} gpio_irq_event; + +typedef void (*gpio_irq_handler)(uint32_t id, gpio_irq_event event); + +int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id); +void gpio_irq_free(gpio_irq_t *obj); +void gpio_irq_set (gpio_irq_t *obj, gpio_irq_event event, uint32_t enable); +void gpio_irq_enable(gpio_irq_t *obj); +void gpio_irq_disable(gpio_irq_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/i2c_api.h b/component/common/mbed/hal/i2c_api.h new file mode 100644 index 0000000..adc9443 --- /dev/null +++ b/component/common/mbed/hal/i2c_api.h @@ -0,0 +1,58 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_I2C_API_H +#define MBED_I2C_API_H + +#include "device.h" + +#if DEVICE_I2C + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct i2c_s i2c_t; + +enum { + I2C_ERROR_NO_SLAVE = -1, + I2C_ERROR_BUS_BUSY = -2 +}; + +void i2c_init (i2c_t *obj, PinName sda, PinName scl); +void i2c_frequency (i2c_t *obj, int hz); +int i2c_start (i2c_t *obj); +int i2c_stop (i2c_t *obj); +int i2c_read (i2c_t *obj, int address, char *data, int length, int stop); +int i2c_write (i2c_t *obj, int address, const char *data, int length, int stop); +void i2c_reset (i2c_t *obj); +int i2c_byte_read (i2c_t *obj, int last); +int i2c_byte_write (i2c_t *obj, int data); + +#if DEVICE_I2CSLAVE +void i2c_slave_mode (i2c_t *obj, int enable_slave); +int i2c_slave_receive(i2c_t *obj); +int i2c_slave_read (i2c_t *obj, char *data, int length); +int i2c_slave_write (i2c_t *obj, const char *data, int length); +void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/pinmap.h b/component/common/mbed/hal/pinmap.h new file mode 100644 index 0000000..653d5fe --- /dev/null +++ b/component/common/mbed/hal/pinmap.h @@ -0,0 +1,43 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_PINMAP_H +#define MBED_PINMAP_H + +#include "PinNames.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PinName pin; + int peripheral; + int function; +} PinMap; + +void pin_function(PinName pin, int function); +void pin_mode (PinName pin, PinMode mode); + +uint32_t pinmap_peripheral(PinName pin, const PinMap* map); +uint32_t pinmap_merge (uint32_t a, uint32_t b); +void pinmap_pinout (PinName pin, const PinMap *map); +uint32_t pinmap_find_peripheral(PinName pin, const PinMap* map); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/hal/port_api.h b/component/common/mbed/hal/port_api.h new file mode 100644 index 0000000..f687cfe --- /dev/null +++ b/component/common/mbed/hal/port_api.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_PORTMAP_H +#define MBED_PORTMAP_H + +#include "device.h" + +#if DEVICE_PORTIN || DEVICE_PORTOUT + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct port_s port_t; + +PinName port_pin(PortName port, int pin_n); + +void port_init (port_t *obj, PortName port, int mask, PinDirection dir); +void port_mode (port_t *obj, PinMode mode); +void port_dir (port_t *obj, PinDirection dir); +void port_write(port_t *obj, int value); +int port_read (port_t *obj); + +#ifdef __cplusplus +} +#endif +#endif + +#endif diff --git a/component/common/mbed/hal/pwmout_api.h b/component/common/mbed/hal/pwmout_api.h new file mode 100644 index 0000000..6557fcd --- /dev/null +++ b/component/common/mbed/hal/pwmout_api.h @@ -0,0 +1,49 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_PWMOUT_API_H +#define MBED_PWMOUT_API_H + +#include "device.h" + +#if DEVICE_PWMOUT + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct pwmout_s pwmout_t; + +void pwmout_init (pwmout_t* obj, PinName pin); +void pwmout_free (pwmout_t* obj); + +void pwmout_write (pwmout_t* obj, float percent); +float pwmout_read (pwmout_t* obj); + +void pwmout_period (pwmout_t* obj, float seconds); +void pwmout_period_ms (pwmout_t* obj, int ms); +void pwmout_period_us (pwmout_t* obj, int us); + +void pwmout_pulsewidth (pwmout_t* obj, float seconds); +void pwmout_pulsewidth_ms(pwmout_t* obj, int ms); +void pwmout_pulsewidth_us(pwmout_t* obj, int us); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/rtc_api.h b/component/common/mbed/hal/rtc_api.h new file mode 100644 index 0000000..663f888 --- /dev/null +++ b/component/common/mbed/hal/rtc_api.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_RTC_API_H +#define MBED_RTC_API_H + +#include "device.h" + +#if DEVICE_RTC + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void rtc_init(void); +void rtc_free(void); +int rtc_isenabled(void); + +time_t rtc_read(void); +void rtc_write(time_t t); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/serial_api.h b/component/common/mbed/hal/serial_api.h new file mode 100644 index 0000000..2b0f0c4 --- /dev/null +++ b/component/common/mbed/hal/serial_api.h @@ -0,0 +1,78 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SERIAL_API_H +#define MBED_SERIAL_API_H + +#include "device.h" + +#if DEVICE_SERIAL + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ParityNone = 0, + ParityOdd = 1, + ParityEven = 2, + ParityForced1 = 3, + ParityForced0 = 4 +} SerialParity; + +typedef enum { + RxIrq, + TxIrq +} SerialIrq; + +typedef enum { + FlowControlNone, + FlowControlRTS, + FlowControlCTS, + FlowControlRTSCTS +} FlowControl; + +typedef void (*uart_irq_handler)(uint32_t id, SerialIrq event); + +typedef struct serial_s serial_t; + +void serial_init (serial_t *obj, PinName tx, PinName rx); +void serial_free (serial_t *obj); +void serial_baud (serial_t *obj, int baudrate); +void serial_format (serial_t *obj, int data_bits, SerialParity parity, int stop_bits); + +void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id); +void serial_irq_set (serial_t *obj, SerialIrq irq, uint32_t enable); + +int serial_getc (serial_t *obj); +void serial_putc (serial_t *obj, int c); +int serial_readable (serial_t *obj); +int serial_writable (serial_t *obj); +void serial_clear (serial_t *obj); + +void serial_break_set (serial_t *obj); +void serial_break_clear(serial_t *obj); + +void serial_pinout_tx(PinName tx); + +void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/sleep_api.h b/component/common/mbed/hal/sleep_api.h new file mode 100644 index 0000000..c8cf3b6 --- /dev/null +++ b/component/common/mbed/hal/sleep_api.h @@ -0,0 +1,64 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SLEEP_API_H +#define MBED_SLEEP_API_H + +#include "device.h" + +#if DEVICE_SLEEP + +#ifdef __cplusplus +extern "C" { +#endif + +/** Send the microcontroller to sleep + * + * The processor is setup ready for sleep, and sent to sleep using __WFI(). In this mode, the + * system clock to the core is stopped until a reset or an interrupt occurs. This eliminates + * dynamic power used by the processor, memory systems and buses. The processor, peripheral and + * memory state are maintained, and the peripherals continue to work and can generate interrupts. + * + * The processor can be woken up by any internal peripheral interrupt or external pin interrupt. + * + * @note + * The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored. + * Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be + * able to access the LocalFileSystem + */ +void sleep(void); + +/** Send the microcontroller to deep sleep + * + * This processor is setup ready for deep sleep, and sent to sleep using __WFI(). This mode + * has the same sleep features as sleep plus it powers down peripherals and clocks. All state + * is still maintained. + * + * The processor can only be woken up by an external interrupt on a pin or a watchdog timer. + * + * @note + * The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored. + * Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be + * able to access the LocalFileSystem + */ +void deepsleep(void); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/spi_api.h b/component/common/mbed/hal/spi_api.h new file mode 100644 index 0000000..f605ba3 --- /dev/null +++ b/component/common/mbed/hal/spi_api.h @@ -0,0 +1,73 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SPI_API_H +#define MBED_SPI_API_H + +#include "device.h" + +#if DEVICE_SPI + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPI_DMA_RX_EN (1<<0) +#define SPI_DMA_TX_EN (1<<1) + +//#define SPI_SCLK_IDLE_LOW 0 // the SCLK is Low when SPI is inactive +//#define SPI_SCLK_IDLE_HIGH 2 // the SCLK is High when SPI is inactive +enum { + SPI_SCLK_IDLE_LOW=0, // the SCLK is Low when SPI is inactive + SPI_SCLK_IDLE_HIGH=2 // the SCLK is High when SPI is inactive +}; + +//#define SPI_CS_TOGGLE_EVERY_FRAME 0 // the CS toggle every frame +//#define SPI_CS_TOGGLE_START_STOP 1 // the CS toggle at start and stop +enum { + SPI_CS_TOGGLE_EVERY_FRAME=0, // the CS toggle every frame + SPI_CS_TOGGLE_START_STOP=1 // the CS toggle at start and stop +}; + +typedef enum { + CS_0 = 0, + CS_1 = 1, + CS_2 = 2, + CS_3 = 3, + CS_4 = 4, + CS_5 = 5, + CS_6 = 6, + CS_7 = 7 +}ChipSelect; + +typedef struct spi_s spi_t; + +void spi_init (spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel); +void spi_free (spi_t *obj); +void spi_format (spi_t *obj, int bits, int mode, int slave); +void spi_frequency (spi_t *obj, int hz); +int spi_master_write (spi_t *obj, int value); +int spi_slave_receive(spi_t *obj); +int spi_slave_read (spi_t *obj); +void spi_slave_write (spi_t *obj, int value); +int spi_busy (spi_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal/us_ticker_api.h b/component/common/mbed/hal/us_ticker_api.h new file mode 100644 index 0000000..ea62d7c --- /dev/null +++ b/component/common/mbed/hal/us_ticker_api.h @@ -0,0 +1,51 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_US_TICKER_API_H +#define MBED_US_TICKER_API_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint64_t timestamp_t; + +uint32_t us_ticker_read(void); + +typedef void (*ticker_event_handler)(uint32_t id); +void us_ticker_set_handler(ticker_event_handler handler); + +typedef struct ticker_event_s { + timestamp_t timestamp; + uint32_t id; + struct ticker_event_s *next; +} ticker_event_t; + +void us_ticker_init(void); +void us_ticker_set_interrupt(timestamp_t timestamp); +void us_ticker_disable_interrupt(void); +void us_ticker_clear_interrupt(void); +void us_ticker_irq_handler(void); + +void us_ticker_insert_event(ticker_event_t *obj, timestamp_t timestamp, uint32_t id); +void us_ticker_remove_event(ticker_event_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/hal_ext/dma_api.h b/component/common/mbed/hal_ext/dma_api.h new file mode 100644 index 0000000..7c627cb --- /dev/null +++ b/component/common/mbed/hal_ext/dma_api.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_GDMA_API_H +#define MBED_GDMA_API_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct gdma_s { + HAL_GDMA_OBJ gdma_obj; + uint8_t gdma_allocated; +}; + +typedef struct gdma_s gdma_t; + +typedef void (*dma_irq_handler)(uint32_t id); + +void dma_memcpy_init(gdma_t *dma_obj, dma_irq_handler handler, uint32_t id); +void dma_memcpy_deinit(gdma_t *dma_obj); +void dma_memcpy(gdma_t *dma_obj, void *dst, void* src, uint32_t len); + +#ifdef __cplusplus +} +#endif + +#endif // end of "#define MBED_GDMA_API_H" diff --git a/component/common/mbed/hal_ext/flash_api.h b/component/common/mbed/hal_ext/flash_api.h new file mode 100644 index 0000000..375e17d --- /dev/null +++ b/component/common/mbed/hal_ext/flash_api.h @@ -0,0 +1,45 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + + #ifndef MBED_EXT_FLASH_API_EXT_H +#define MBED_EXT_FLASH_API_EXT_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct flash_s flash_t; + +/** + * global data structure + */ +extern flash_t flash; + +enum { + FLASH_COMPLETE = 0, + FLASH_ERROR_2 = 1, +}; + +//void flash_init (flash_t *obj); +void flash_erase_sector (flash_t *obj, uint32_t address); +int flash_read_word (flash_t *obj, uint32_t address, uint32_t * data); +int flash_write_word (flash_t *obj, uint32_t address, uint32_t data); +int flash_stream_read (flash_t *obj, uint32_t address, uint32_t len, uint8_t * data); +int flash_stream_write (flash_t *obj, uint32_t address, uint32_t len, uint8_t * data); +void flash_write_protect (flash_t *obj, uint32_t protect); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/component/common/mbed/hal_ext/i2s_api.h b/component/common/mbed/hal_ext/i2s_api.h new file mode 100644 index 0000000..8533af3 --- /dev/null +++ b/component/common/mbed/hal_ext/i2s_api.h @@ -0,0 +1,76 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2015, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + +#ifndef MBED_EXT_I2S_API_EXT_H +#define MBED_EXT_I2S_API_EXT_H + +#include "device.h" +#include "rtl8195a.h" +#include "hal_i2s.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SR_8KHZ = I2S_SR_8KHZ, + SR_16KHZ = I2S_SR_16KHZ, + SR_24KHZ = I2S_SR_24KHZ, + SR_32KHZ = I2S_SR_32KHZ, + SR_48KHZ = I2S_SR_48KHZ, + SR_96KHZ = I2S_SR_96KHZ, + SR_7p35KHZ = I2S_SR_7p35KHZ, + SR_11p02KHZ = I2S_SR_11p02KHZ, + SR_22p05KHZ = I2S_SR_22p05KHZ, + SR_29p4KHZ = I2S_SR_29p4KHZ, + SR_44p1KHZ = I2S_SR_44p1KHZ, + SR_88p2KHZ = I2S_SR_88p2KHZ +}; + +enum { + CH_STEREO = I2S_CH_STEREO, + CH_MONO = I2S_CH_MONO +}; + +enum { + WL_16b = I2S_WL_16, + WL_24b = I2S_WL_24 +}; + +enum { + I2S_DIR_RX = I2S_ONLY_RX, // Rx Only + I2S_DIR_TX = I2S_ONLY_TX, // Tx Only + I2S_DIR_TXRX = I2S_TXRX // Tx & Rx (BiDirection) +}; + +typedef void (*i2s_irq_handler)(uint32_t id, char *pbuf); + +typedef struct i2s_s i2s_t; + +void i2s_init(i2s_t *obj, PinName sck, PinName ws, PinName sd); +void i2s_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, + uint32_t page_num, uint32_t page_size); +void i2s_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id); +void i2s_rx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id); +void i2s_set_direction(i2s_t *obj, int trx_type); +void i2s_set_param(i2s_t *obj, int channel_num, int rate, int word_len); +void i2s_deinit(i2s_t *obj); +int* i2s_get_tx_page(i2s_t *obj); +void i2s_send_page(i2s_t *obj, uint32_t *pbuf); +void i2s_recv_page(i2s_t *obj); +void i2s_enable(i2s_t *obj); +void i2s_disable(i2s_t *obj); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/component/common/mbed/hal_ext/nfc_api.h b/component/common/mbed/hal_ext/nfc_api.h new file mode 100644 index 0000000..d282568 --- /dev/null +++ b/component/common/mbed/hal_ext/nfc_api.h @@ -0,0 +1,70 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_NFC_API_H +#define MBED_NFC_API_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFCTAGLENGTH 36 // maximum 36*4=144 bytes +#define NFC_MAX_CACHE_PAGE_NUM 36 // maximum 36*4=144 bytes + +typedef enum _NFC_STATUS_ { + NFC_OK = 0, + NFC_ERROR = -1 +}NFC_STATUS, *PNFC_STATUS; + +typedef enum _NFC_PWR_STATUS_ { + NFC_PWR_DISABLE = 0, + NFC_PWR_RUNNING = 1, + NFC_PWR_SLEEP0 = 2, + NFC_PWR_SLEEP1 = 3, + NFC_PWR_DOWN = 4, + NFC_PWR_ERROR = -1 +}NFC_PWR_STATUS, *PNFC_PWR_STATUS; + +typedef enum _NFC_EVENT_ { + NFC_EV_READER_PRESENT = (1<<0), + NFC_EV_READ = (1<<1), + NFC_EV_WRITE = (1<<2), + NFC_EV_ERR = (1<<3), + NFC_EV_CACHE_READ = (1<<4) +}NFC_EVENT, *PNFC_EVENT; + +typedef struct nfctag_s nfctag_t; + +typedef void (*nfc_read_cb)(void *arg, void *buf, unsigned int page); +typedef void(*nfc_write_cb)(void *arg, unsigned int page, uint32_t pgdat); +typedef void(*nfc_event_cb)(void *arg, unsigned int event); +typedef void(*nfc_cache_read_cb)(void *arg, void *buf, unsigned int page); + +int nfc_init(nfctag_t *obj, uint32_t *pg_init_val); +void nfc_read(nfctag_t *obj, nfc_read_cb handler, void *arg); +void nfc_write(nfctag_t *obj, nfc_write_cb handler, void *arg); +void nfc_event(nfctag_t *obj, nfc_event_cb handler, void *arg, unsigned int event_mask); +int nfc_power(nfctag_t *obj, int pwr_mode, int wake_event); +int nfc_cache_write(nfctag_t *obj, uint32_t *tbuf, unsigned int spage, unsigned int pg_num); +int nfc_cache_raed(nfctag_t *obj, nfc_cache_read_cb handler, void *arg, unsigned int start_pg); +int nfc_status(nfctag_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/hal_ext/serial_ex_api.h b/component/common/mbed/hal_ext/serial_ex_api.h new file mode 100644 index 0000000..cd58938 --- /dev/null +++ b/component/common/mbed/hal_ext/serial_ex_api.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SERIAL_EX_API_H +#define MBED_SERIAL_EX_API_H + +#include "device.h" + +#if DEVICE_SERIAL + +#ifdef __cplusplus +extern "C" { +#endif + +void serial_send_comp_handler(serial_t *obj, void *handler, uint32_t id); +void serial_recv_comp_handler(serial_t *obj, void *handler, uint32_t id); +int32_t serial_recv_stream (serial_t *obj, char *prxbuf, uint32_t len); +int32_t serial_send_stream (serial_t *obj, char *ptxbuf, uint32_t len); +int32_t serial_recv_stream_dma (serial_t *obj, char *prxbuf, uint32_t len); +int32_t serial_send_stream_dma (serial_t *obj, char *ptxbuf, uint32_t len); +int32_t serial_send_stream_abort (serial_t *obj); +int32_t serial_recv_stream_abort (serial_t *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif // #ifndef MBED_SERIAL_EX_API_H diff --git a/component/common/mbed/hal_ext/sleep_ex_api.h b/component/common/mbed/hal_ext/sleep_ex_api.h new file mode 100644 index 0000000..19efbb0 --- /dev/null +++ b/component/common/mbed/hal_ext/sleep_ex_api.h @@ -0,0 +1,97 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SLEEP_EX_API_H +#define MBED_SLEEP_EX_API_H + +#include "device.h" + +#if DEVICE_SLEEP + +#ifdef __cplusplus +extern "C" { +#endif + +/* Sleep Eake Up event, the User application also + need to config the peripheral to trigger wake up event */ +#define SLEEP_WAKEUP_BY_STIMER (SLP_STIMER) // wake up by system timer +#define SLEEP_WAKEUP_BY_GTIMER (SLP_GTIMER) // wake up by General purpose timer timeout +#define SLEEP_WAKEUP_BY_GPIO_INT (SLP_GPIO) // wake up by GPIO Port A[7:0] Interrupt +#define SLEEP_WAKEUP_BY_WLAN (SLP_WL) // wake up by WLan event +#define SLEEP_WAKEUP_BY_NFC (SLP_NFC) // wake up by NFC event +#define SLEEP_WAKEUP_BY_SDIO (SLP_SDIO) // wake up by SDIO event +#define SLEEP_WAKEUP_BY_USB (SLP_USB) // wake up by USB event + +// Deep Standby Wakeup event +#define STANDBY_WAKEUP_BY_STIMER (BIT0) // wake up by system timer +#define STANDBY_WAKEUP_BY_NFC (BIT1) // wake up by NFC event +//#define SLEEP_WAKEUP_BY_DS_TIMER (BIT2) // The timer to wakeup from Deep Sleep timer +// Do not modify these definition, or need to modify the code also. +#define STANDBY_WAKEUP_BY_PA5 (BIT4) // GPIO Port A[5] +#define STANDBY_WAKEUP_BY_PC7 (BIT5) // GPIO Port C[7] +#define STANDBY_WAKEUP_BY_PD5 (BIT6) // GPIO Port D[5] +#define STANDBY_WAKEUP_BY_PE3 (BIT7) // GPIO Port E[3] + +// Deep Sleep Wakeup event +#define DSLEEP_WAKEUP_BY_TIMER (DS_TIMER33) +#define DSLEEP_WAKEUP_BY_GPIO (DS_GPIO) // GPIO Port B[1] + +typedef struct _SLEEP_WKUP_EVENT_ { + u8 wakeup_event; // Wake up event: Timer, NFC, GPIO + u8 gpio_option; // GPIO Wakeup setting: [3:0]: Pin 3~0 enable, [7:4]: pin3~0 active high/low + u32 timer_duration; // the sleep duration and then wakeup +} SLEEP_WAKEUP_EVENT, *PSLEEP_WAKEUP_EVENT; +/** Send the microcontroller to sleep + * + * The processor is setup ready for sleep, and sent to sleep using __WFI(). In this mode, the + * system clock to the core is stopped until a reset or an interrupt occurs. This eliminates + * dynamic power used by the processor, memory systems and buses. The processor, peripheral and + * memory state are maintained, and the peripherals continue to work and can generate interrupts. + * + * The processor can be woken up by any internal peripheral interrupt or external pin interrupt. + * + * @note + * The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored. + * Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be + * able to access the LocalFileSystem + */ +void sleep_ex(uint32_t wakeup_event, uint32_t sleep_duration); + +void standby_wakeup_event_add(uint32_t wakeup_event, uint32_t sleep_duration_ms, uint32_t gpio_active); +void standby_wakeup_event_del(uint32_t wakeup_event); +void deepstandby_ex(void); + +/** Send the microcontroller to deep sleep + * + * This processor is setup ready for deep sleep, and sent to sleep using __WFI(). This mode + * has the same sleep features as sleep plus it powers down peripherals and clocks. All state + * is still maintained. + * + * The processor can only be woken up by an external interrupt on a pin or a timer. + * + * @note + * The mbed interface semihosting is disconnected as part of going to sleep, and can not be restored. + * Flash re-programming and the USB serial port will remain active, but the mbed program will no longer be + * able to access the LocalFileSystem + */ +void deepsleep_ex(uint32_t wakeup_event, uint32_t sleep_duration); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal_ext/spi_ex_api.h b/component/common/mbed/hal_ext/spi_ex_api.h new file mode 100644 index 0000000..89149b8 --- /dev/null +++ b/component/common/mbed/hal_ext/spi_ex_api.h @@ -0,0 +1,58 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SPI_EXT_API_H +#define MBED_SPI_EXT_API_H + +#include "device.h" + +#if DEVICE_SPI + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPI_STATE_READY 0x00 +#define SPI_STATE_RX_BUSY (1<<1) +#define SPI_STATE_TX_BUSY (1<<2) + +typedef enum { + SpiRxIrq, + SpiTxIrq +} SpiIrq; + +typedef void (*spi_irq_handler)(uint32_t id, SpiIrq event); + +void spi_irq_hook(spi_t *obj, spi_irq_handler handler, uint32_t id); +int32_t spi_slave_read_stream(spi_t *obj, char *rx_buffer, uint32_t length); +int32_t spi_slave_write_stream(spi_t *obj, char *tx_buffer, uint32_t length); +int32_t spi_master_read_stream(spi_t *obj, char *rx_buffer, uint32_t length); +int32_t spi_master_write_stream(spi_t *obj, char *tx_buffer, uint32_t length); +int32_t spi_master_write_read_stream(spi_t *obj, char *tx_buffer, + char *rx_buffer, uint32_t length); +#ifdef CONFIG_GDMA_EN +int32_t spi_slave_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length); +int32_t spi_slave_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length); +int32_t spi_master_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length); +int32_t spi_master_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/component/common/mbed/hal_ext/sys_api.h b/component/common/mbed/hal_ext/sys_api.h new file mode 100644 index 0000000..73d8d54 --- /dev/null +++ b/component/common/mbed/hal_ext/sys_api.h @@ -0,0 +1,50 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_WATCHDOG_API_H +#define MBED_WATCHDOG_API_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Turn off the JTAG function + * + * @return None + * + */ +void sys_jtag_off(void); +void sys_clear_ota_signature(void); +void sys_recover_ota_signature(void); +void sys_log_uart_on(void); +void sys_log_uart_off(void); +void sys_adc_calibration(u8 write, u16 *offset, u16 *gain); + +/** + * @brief system software reset + * + * @return None + * + */ +void sys_reset(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/hal_ext/wdt_api.h b/component/common/mbed/hal_ext/wdt_api.h new file mode 100644 index 0000000..7531c34 --- /dev/null +++ b/component/common/mbed/hal_ext/wdt_api.h @@ -0,0 +1,61 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_WATCHDOG_API_H +#define MBED_WATCHDOG_API_H + +#include "device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*wdt_irq_handler)(uint32_t id); + +/** Initial the watch dog time setting + * + * This function will initial and enable the watchdog timer with a given timeout value. + * When the watchdog timer timeout event is triggered, the system will be reset. User also can + * register a callback function to handle the watchdog timer timeout event. + */ +void watchdog_init(uint32_t timeout_ms); + +/** Start the watchdog counting + * + * This function will active the watchdog timer down counting. When the watchdog timer count down + * to 0, a callback function will be called or the system will be reset. + */ +void watchdog_start(void); + +/** Stop the watchdog counting + * + * This function will stop the watchdog timer down counting. If a user application aware a + * procedure may takes too long and cause the watchdog timer timeout, the application use this + * function to stop the watchdog timer to prevent the watchdog timer timeout. + */ +void watchdog_stop(void); + +/** Refresh the watchdog counting + * + * This function will reload the watchdog timer counting value. Usually a application do the watchdog + * timer reflash in the main loop to prevent the watchdog timer timeout. + */ +void watchdog_refresh(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/targets/cmsis/rtl8195a b/component/common/mbed/targets/cmsis/rtl8195a new file mode 100644 index 0000000..c9c1d60 --- /dev/null +++ b/component/common/mbed/targets/cmsis/rtl8195a @@ -0,0 +1,14 @@ +/* mbed Microcontroller Library - CMSIS + * Copyright (C) 2009-2011 ARM Limited. All rights reserved. + * + * A generic CMSIS include header, pulling in RTL8195A specifics + */ + +#ifndef MBED_CMSIS_H +#define MBED_CMSIS_H + +#include +#include + +#endif + diff --git a/component/common/mbed/targets/hal/rtl8195a/PeripheralNames.h b/component/common/mbed/targets/hal/rtl8195a/PeripheralNames.h new file mode 100644 index 0000000..1496e8f --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/PeripheralNames.h @@ -0,0 +1,94 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_PERIPHERALNAMES_H +#define MBED_PERIPHERALNAMES_H + +#include "cmsis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if 0 +typedef enum { + UART_1 = (int)USART1_BASE, + UART_2 = (int)USART2_BASE, + UART_3 = (int)USART3_BASE, + UART_4 = (int)UART4_BASE, + UART_5 = (int)UART5_BASE, + UART_6 = (int)USART6_BASE +} UARTName; + +typedef enum { + ADC0_0 = 0, + ADC0_1, + ADC0_2, + ADC0_3, + ADC0_4, + ADC0_5, + ADC0_6, + ADC0_7, + ADC0_8, + ADC0_9, + ADC0_10, + ADC0_11, + ADC0_12, + ADC0_13, + ADC0_14, + ADC0_15 +} ADCName; + +typedef enum { + DAC_0 = 0, + DAC_1 +} DACName; + +typedef enum { + SPI_1 = (int)SPI1_BASE, + SPI_2 = (int)SPI2_BASE, + SPI_3 = (int)SPI3_BASE, +} SPIName; + +typedef enum { + I2C_1 = (int)I2C1_BASE, + I2C_2 = (int)I2C2_BASE, + I2C_3 = (int)I2C3_BASE +} I2CName; + +typedef enum { + PWM_1 = 1, + PWM_2, + PWM_3, + PWM_4, + PWM_5, + PWM_6 +} PWMName; + +typedef enum { + CAN_1 = (int)CAN1_BASE, + CAN_2 = (int)CAN2_BASE +} CANName; +#endif + +#define STDIO_UART_TX PA_6 +#define STDIO_UART_RX PA_7 +#define STDIO_UART UART0 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/PinNames.h b/component/common/mbed/targets/hal/rtl8195a/PinNames.h new file mode 100644 index 0000000..3b3795f --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/PinNames.h @@ -0,0 +1,212 @@ + +#ifndef _PINNAMES_H_ +#define _PINNAMES_H_ + +#include "cmsis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + PORT_A = 0, + PORT_B = 1, + PORT_C = 2, + PORT_D = 3, + PORT_E = 4, + PORT_F = 5, + PORT_G = 6, + PORT_H = 7, + PORT_I = 8, + PORT_J = 9, + PORT_K = 10, + + PORT_V = 11, + PORT_MAX +} GPIO_PORT; + +#define RTL_PIN_PERI(FUN, IDX, SEL) ((int)(((FUN) << 8) | ((IDX)<<4) | (SEL))) +#define RTL_PIN_FUNC(FUN, SEL) ((int)(((FUN) << 7) | (SEL))) +#define RTL_GET_PERI_SEL(peri) ((int)((peri)&0x0F)) +#define RTL_GET_PERI_IDX(peri) ((int)(((peri) >> 4)&0x0F)) + +typedef enum { + PIN_INPUT=0, + PIN_OUTPUT +} PinDirection; + +typedef enum { + PA_0 = (PORT_A<<4|0), + PA_1 = (PORT_A<<4|1), + PA_2 = (PORT_A<<4|2), + PA_3 = (PORT_A<<4|3), + PA_4 = (PORT_A<<4|4), + PA_5 = (PORT_A<<4|5), + PA_6 = (PORT_A<<4|6), + PA_7 = (PORT_A<<4|7), + + PB_0 = (PORT_B<<4|0), + PB_1 = (PORT_B<<4|1), + PB_2 = (PORT_B<<4|2), + PB_3 = (PORT_B<<4|3), + PB_4 = (PORT_B<<4|4), + PB_5 = (PORT_B<<4|5), + PB_6 = (PORT_B<<4|6), + PB_7 = (PORT_B<<4|7), + + PC_0 = (PORT_C<<4|0), + PC_1 = (PORT_C<<4|1), + PC_2 = (PORT_C<<4|2), + PC_3 = (PORT_C<<4|3), + PC_4 = (PORT_C<<4|4), + PC_5 = (PORT_C<<4|5), + PC_6 = (PORT_C<<4|6), + PC_7 = (PORT_C<<4|7), + PC_8 = (PORT_C<<4|8), + PC_9 = (PORT_C<<4|9), + + PD_0 = (PORT_D<<4|0), + PD_1 = (PORT_D<<4|1), + PD_2 = (PORT_D<<4|2), + PD_3 = (PORT_D<<4|3), + PD_4 = (PORT_D<<4|4), + PD_5 = (PORT_D<<4|5), + PD_6 = (PORT_D<<4|6), + PD_7 = (PORT_D<<4|7), + PD_8 = (PORT_D<<4|8), + PD_9 = (PORT_D<<4|9), + + PE_0 = (PORT_E<<4|0), + PE_1 = (PORT_E<<4|1), + PE_2 = (PORT_E<<4|2), + PE_3 = (PORT_E<<4|3), + PE_4 = (PORT_E<<4|4), + PE_5 = (PORT_E<<4|5), + PE_6 = (PORT_E<<4|6), + PE_7 = (PORT_E<<4|7), + PE_8 = (PORT_E<<4|8), + PE_9 = (PORT_E<<4|9), + PE_A = (PORT_E<<4|10), + + PF_0 = (PORT_F<<4|0), + PF_1 = (PORT_F<<4|1), + PF_2 = (PORT_F<<4|2), + PF_3 = (PORT_F<<4|3), + PF_4 = (PORT_F<<4|4), + PF_5 = (PORT_F<<4|5), +// PF_6 = (PORT_F<<4|6), +// PF_7 = (PORT_F<<4|7), + + PG_0 = (PORT_G<<4|0), + PG_1 = (PORT_G<<4|1), + PG_2 = (PORT_G<<4|2), + PG_3 = (PORT_G<<4|3), + PG_4 = (PORT_G<<4|4), + PG_5 = (PORT_G<<4|5), + PG_6 = (PORT_G<<4|6), + PG_7 = (PORT_G<<4|7), + + PH_0 = (PORT_H<<4|0), + PH_1 = (PORT_H<<4|1), + PH_2 = (PORT_H<<4|2), + PH_3 = (PORT_H<<4|3), + PH_4 = (PORT_H<<4|4), + PH_5 = (PORT_H<<4|5), + PH_6 = (PORT_H<<4|6), + PH_7 = (PORT_H<<4|7), + + PI_0 = (PORT_I<<4|0), + PI_1 = (PORT_I<<4|1), + PI_2 = (PORT_I<<4|2), + PI_3 = (PORT_I<<4|3), + PI_4 = (PORT_I<<4|4), + PI_5 = (PORT_I<<4|5), + PI_6 = (PORT_I<<4|6), + PI_7 = (PORT_I<<4|7), + + PJ_0 = (PORT_J<<4|0), + PJ_1 = (PORT_J<<4|1), + PJ_2 = (PORT_J<<4|2), + PJ_3 = (PORT_J<<4|3), + PJ_4 = (PORT_J<<4|4), + PJ_5 = (PORT_J<<4|5), + PJ_6 = (PORT_J<<4|6), +// PJ_7 = (PORT_J<<4|7), + + PK_0 = (PORT_K<<4|0), + PK_1 = (PORT_K<<4|1), + PK_2 = (PORT_K<<4|2), + PK_3 = (PORT_K<<4|3), + PK_4 = (PORT_K<<4|4), + PK_5 = (PORT_K<<4|5), + PK_6 = (PORT_K<<4|6), +// PK_7 = (PORT_K<<4|7), + + AD_1 = (PORT_V<<4|1), + AD_2 = (PORT_V<<4|2), + AD_3 = (PORT_V<<4|3), + + // Arduino connector namings +/* + A0 = PA_0, + A1 = PA_1, + A2 = PA_4, + A3 = PB_0, + A4 = PC_1, + A5 = PC_0, + D0 = PA_3, + D1 = PA_2, + D2 = PA_10, + D3 = PB_3, + D4 = PB_5, + D5 = PB_4, + D6 = PB_10, + D7 = PA_8, + D8 = PA_9, + D9 = PC_7, + D10 = PB_6, + D11 = PA_7, + D12 = PA_6, + D13 = PA_5, + D14 = PB_9, + D15 = PB_8, +*/ + + // Generic signals namings + LED1 = PB_4, + LED2 = PB_5, + LED3 = PB_6, + LED4 = PB_7, + USER_BUTTON = PA_3, + SERIAL_TX = PA_7, + SERIAL_RX = PA_6, + USBTX = PA_7, + USBRX = PA_6, + I2C_SCL = PC_5, + I2C_SDA = PC_4, + SPI_MOSI = PC_2, + SPI_MISO = PC_3, + SPI_SCK = PC_1, + SPI_CS = PC_0, + PWM_OUT = PD_4, + + // Not connected + NC = (uint32_t)0xFFFFFFFF +} PinName; + +typedef enum { + PullNone = 0, + PullUp = 1, + PullDown = 2, + OpenDrain = 3, + PullDefault = PullNone +} PinMode; + +#define PORT_NUM(pin) (((uint32_t)(pin) >> 4) & 0xF) +#define PIN_NUM(pin) ((uint32_t)(pin) & 0xF) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/PortNames.h b/component/common/mbed/targets/hal/rtl8195a/PortNames.h new file mode 100644 index 0000000..29a5324 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/PortNames.h @@ -0,0 +1,38 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_PORTNAMES_H +#define MBED_PORTNAMES_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + PortA = 0, + PortB = 1, + PortC = 2, + PortD = 3, + PortE = 4, + PortF = 5, + PortG = 6, + PortH = 7, + PortI = 8 +} PortName; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/analogin_api.c b/component/common/mbed/targets/hal/rtl8195a/analogin_api.c new file mode 100644 index 0000000..c09b8d0 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/analogin_api.c @@ -0,0 +1,160 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + +#include "objects.h" +#include "PinNames.h" +#include "hal_adc.h" +#include "analogin_api.h" + + + +#if CONFIG_ADC_EN +//#include "cmsis.h" +#include "pinmap.h" + + +extern u32 ConfigDebugErr; +extern u32 ConfigDebuginfo; + + +void analogin_init (analogin_t *obj, PinName pin){ + + uint32_t adc_idx; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_USERCB_ADPT pSalADCUserCBAdpt = NULL; + PSAL_ADC_HND pSalADCHND = NULL; + + ConfigDebugErr &= (~(_DBG_ADC_|_DBG_GDMA_)); + ConfigDebugInfo&= (~(_DBG_ADC_|_DBG_GDMA_)); + + adc_idx = pin & 0x0F; + + /* Get I2C device handler */ + pSalADCMngtAdpt = &(obj->SalADCMngtAdpt); + pSalADCUserCBAdpt = (PSAL_ADC_USERCB_ADPT)&(obj->SalADCUserCBAdpt); + + /*To assign the rest pointers*/ + pSalADCMngtAdpt->pSalHndPriv = &(obj->SalADCHndPriv); + pSalADCMngtAdpt->pSalHndPriv->ppSalADCHnd = (void**)&(pSalADCMngtAdpt->pSalHndPriv); + + /* To assign the default (ROM) HAL OP initialization function */ + pSalADCMngtAdpt->pHalOpInit = &HalADCOpInit; + + /* To assign the default (ROM) HAL GDMA OP initialization function */ + pSalADCMngtAdpt->pHalGdmaOpInit = &HalGdmaOpInit; + + /* To assign the default (ROM) SAL interrupt function */ + pSalADCMngtAdpt->pSalIrqFunc = &ADCISRHandle; + + /* To assign the default (ROM) SAL DMA TX interrupt function */ + pSalADCMngtAdpt->pSalDMAIrqFunc = &ADCGDMAISRHandle; + + pSalADCMngtAdpt->pHalInitDat = &(obj->HalADCInitData); + pSalADCMngtAdpt->pHalOp = &(obj->HalADCOp); + pSalADCMngtAdpt->pIrqHnd = &(obj->ADCIrqHandleDat); + pSalADCMngtAdpt->pHalGdmaAdp = &(obj->HalADCGdmaAdpt); + pSalADCMngtAdpt->pHalGdmaOp = &(obj->HalADCGdmaOp); + pSalADCMngtAdpt->pIrqGdmaHnd = &(obj->ADCGdmaIrqHandleDat); + pSalADCMngtAdpt->pUserCB = &(obj->SalADCUserCB); + + /* Assign the private SAL handle to public SAL handle */ + pSalADCHND = &(pSalADCMngtAdpt->pSalHndPriv->SalADCHndPriv); + + /* Assign the internal HAL initial data pointer to the SAL handle */ + pSalADCHND->pInitDat = pSalADCMngtAdpt->pHalInitDat; + + /* Assign the internal user callback pointer to the SAL handle */ + pSalADCHND->pUserCB = pSalADCMngtAdpt->pUserCB; + + /*To assign user callback pointers*/ + pSalADCMngtAdpt->pUserCB->pTXCB = pSalADCUserCBAdpt; + pSalADCMngtAdpt->pUserCB->pTXCCB = (pSalADCUserCBAdpt+1); + pSalADCMngtAdpt->pUserCB->pRXCB = (pSalADCUserCBAdpt+2); + pSalADCMngtAdpt->pUserCB->pRXCCB = (pSalADCUserCBAdpt+3); + pSalADCMngtAdpt->pUserCB->pRDREQCB = (pSalADCUserCBAdpt+4); + pSalADCMngtAdpt->pUserCB->pERRCB = (pSalADCUserCBAdpt+5); + pSalADCMngtAdpt->pUserCB->pDMATXCB = (pSalADCUserCBAdpt+6); + pSalADCMngtAdpt->pUserCB->pDMATXCCB = (pSalADCUserCBAdpt+7); + pSalADCMngtAdpt->pUserCB->pDMARXCB = (pSalADCUserCBAdpt+8); + pSalADCMngtAdpt->pUserCB->pDMARXCCB = (pSalADCUserCBAdpt+9); + + /* Set ADC Device Number */ + pSalADCHND->DevNum = adc_idx; + + /* Load ADC default value */ + RtkADCLoadDefault(pSalADCHND); + + /* Assign ADC Pin Mux */ + pSalADCHND->PinMux = 0; + pSalADCHND->OpType = ADC_RDREG_TYPE; + + /* Init ADC now */ + pSalADCHND->pInitDat->ADCBurstSz = 8; + pSalADCHND->pInitDat->ADCOneShotTD = 8; + RtkADCInit(pSalADCHND); +} + +float analogin_read(analogin_t *obj){ + float value; + uint32_t AnaloginTmp[2] = {0,0}; + uint32_t AanloginCh = 0; + uint32_t AnaloginDatMsk = 0xFFFF; + uint8_t AnaloginIdx = 0; + uint32_t AnalogDat = 0; + + uint32_t AnalogDatFull = 0; + + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_HND pSalADCHND = NULL; + + pSalADCMngtAdpt = &(obj->SalADCMngtAdpt); + pSalADCHND = &(pSalADCMngtAdpt->pSalHndPriv->SalADCHndPriv); + AnaloginIdx = pSalADCHND->DevNum; + RtkADCReceiveBuf(pSalADCHND,&AnaloginTmp[0]); + + AnaloginDatMsk = (u32)(AnaloginDatMsk<<((u32)(16*(AnaloginIdx&0x01)))); + AnalogDat = AnaloginTmp[(AnaloginIdx/2)]; + AnalogDat = (AnalogDat & AnaloginDatMsk); + AnalogDat = (AnalogDat>>((u32)(16*(AnaloginIdx&0x01)))); + + AnalogDatFull = 0xCE80; + + value = (float)(AnalogDat) / (float)(AnalogDatFull); + + return (float)value; +} + +uint16_t analogin_read_u16(analogin_t *obj){ + uint32_t AnaloginTmp[2] = {0,0}; + uint32_t AanloginCh = 0; + uint32_t AnaloginDatMsk = 0xFFFF; + uint8_t AnaloginIdx = 0; + uint32_t AnalogDat = 0; + + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_HND pSalADCHND = NULL; + + pSalADCMngtAdpt = &(obj->SalADCMngtAdpt); + pSalADCHND = &(pSalADCMngtAdpt->pSalHndPriv->SalADCHndPriv); + AnaloginIdx = pSalADCHND->DevNum; + RtkADCReceiveBuf(pSalADCHND,&AnaloginTmp[0]); + + + //DBG_8195A("[0]:%08x, %08x\n", AnaloginTmp[0], AnaloginTmp[1] ); + AnaloginDatMsk = (u32)(AnaloginDatMsk<<((u32)(16*(AnaloginIdx&0x01)))); + AnalogDat = AnaloginTmp[(AnaloginIdx/2)]; + AnalogDat = (AnalogDat & AnaloginDatMsk); + AnalogDat = (AnalogDat>>((u32)(16*(AnaloginIdx&0x01)))); + + return (uint16_t)AnalogDat; + +} + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/device.h b/component/common/mbed/targets/hal/rtl8195a/device.h new file mode 100644 index 0000000..2ab06fa --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/device.h @@ -0,0 +1,48 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_DEVICE_H +#define MBED_DEVICE_H + +#define DEVICE_PORTIN 1 +#define DEVICE_PORTOUT 1 +#define DEVICE_PORTINOUT 1 + +#define DEVICE_INTERRUPTIN 1 + +#define DEVICE_ANALOGIN 1 +#define DEVICE_ANALOGOUT 0 + +#define DEVICE_SERIAL 1 + +#define DEVICE_I2C 1 +#define DEVICE_I2CSLAVE 1 + +#define DEVICE_SPI 1 +#define DEVICE_SPISLAVE 1 + +#define DEVICE_CAN 0 + +#define DEVICE_RTC 1 + +#define DEVICE_ETHERNET 0 + +#define DEVICE_PWMOUT 1 + +#define DEVICE_SLEEP 1 + +#include "objects.h" + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/dma_api.c b/component/common/mbed/targets/hal/rtl8195a/dma_api.c new file mode 100644 index 0000000..316a487 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/dma_api.c @@ -0,0 +1,77 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek + * All rights reserved. + * + * 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 HOLDER 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 "dma_api.h" +#include "cmsis.h" + +extern BOOL HalGdmaMemCpyInit(PHAL_GDMA_OBJ pHalGdmaObj); +extern VOID HalGdmaMemCpyDeInit(PHAL_GDMA_OBJ pHalGdmaObj); +extern VOID* HalGdmaMemCpy(PHAL_GDMA_OBJ pHalGdmaObj, void* pDest, void* pSrc, u32 len); + + +/** + * @brief Initial the GDMA + * + * @param dma_obj: the GDMA object + * handler: the callback function for a DMA transfer complete. + * id: the argument of the callback function. + * @return None + * + */ +void dma_memcpy_init(gdma_t *dma_obj, dma_irq_handler handler, uint32_t id) +{ + dma_obj->gdma_obj.GdmaIrqHandle.IrqFun = (IRQ_FUN)handler; + dma_obj->gdma_obj.GdmaIrqHandle.Data = (u32)id; + dma_obj->gdma_allocated = HalGdmaMemCpyInit(&(dma_obj->gdma_obj)); +} + +/** + * @brief De-Initial the GDMA + * + * @param dma_obj: the GDMA object + * @return None + * + */ +void dma_memcpy_deinit(gdma_t *dma_obj) +{ + if (dma_obj->gdma_allocated) { + HalGdmaMemCpyDeInit(&(dma_obj->gdma_obj)); + } +} + +/** + * @brief To do a memory copy by DMA + * + * @param None + * @return None + * + */ +void dma_memcpy(gdma_t *dma_obj, void *dst, void* src, uint32_t len) +{ +#if 0 + if (!dma_obj->gdma_allocated) { + dma_irq_handler handler; + + _memcpy(dst, src, len); + handler = dma_obj->GdmaIrqHandle.IrqFun; + handler(dma_obj->GdmaIrqHandle.Data); + } +#endif + HalGdmaMemCpy(&(dma_obj->gdma_obj), dst, src, len); +} + + diff --git a/component/common/mbed/targets/hal/rtl8195a/flash_api.c b/component/common/mbed/targets/hal/rtl8195a/flash_api.c new file mode 100644 index 0000000..0d628b9 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/flash_api.c @@ -0,0 +1,321 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + + +#include "objects.h" +#include "PinNames.h" + + +#include "pinmap.h" + +#include "rtl8195a.h" +#include "hal_spi_flash.h" +#include "hal_platform.h" +#include "rtl8195a_spi_flash.h" +#include "hal_api.h" +#include "flash_api.h" + +extern u32 ConfigDebugInfo; + +/** + * global data structure + */ +flash_t flash; + +static void flash_init(flash_t *obj); + +/** + * @brief lock flash access + * @param none + * @retval none + */ +void flash_lock() +{ + + HAL_WRITE32(0xE000ED00, 0x9C, 0x9807E012); + HAL_WRITE32(0xE000ED00, 0xA0, 0x06000017); +} + +/** + * @brief unlock flash access + * @param none + * @retval none + */ +void flash_unlock() +{ + HAL_WRITE32(0xE000ED00, 0x9C, 0x9807E012); + HAL_WRITE32(0xE000ED00, 0xA0, 0x03000017); +} + +/** + * @brief Control the flash chip write protect enable/disable + * @param protect: 1/0: protect/unprotect + * @retval none + */ +void flash_write_protect(flash_t *obj, uint32_t protect) +{ + flash_init(obj); + SpicWriteProtectFlashRtl8195A(protect); + SpicDisableRtl8195A(); +} + +/** + * @brief Init Flash + * @param obj: address of the flash object + * @retval none + */ +static void flash_init(flash_t *obj) +{ + //SPIC_INIT_PARA spic_init_para; + + // Init SPI Flash Controller +// DBG_8195A("Initial Spi Flash Controller\n"); + SPI_FLASH_PIN_FCTRL(ON); + + if (!SpicFlashInitRtl8195A(SpicOneBitMode)){ + + DBG_8195A("SPI Init Fail!!!!!!\n"); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSTBY_INFO3, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSTBY_INFO3)|0xf); + } + else { +// DBG_8195A("SPI Init SUCCESS\n"); + } +} + +/** + * @brief Erase flash sector + * @param address: Specifies the starting address to be erased. + * @retval none + */ +void flash_erase_sector(flash_t *obj, uint32_t address) +{ + flash_init(obj); + SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address); + SpicDisableRtl8195A(); +} + +/** + * @brief Read a word from specified address + * @param obj: Specifies the parameter of flash object. + * @param address: Specifies the address to be read. + * @param data: Specified the address to save the readback data. + * @retval status: Success:1 or Failure: Others. + */ +int flash_read_word(flash_t *obj, uint32_t address, uint32_t * data) +{ + + // Check address + + // Read Word + flash_init(obj); + + // Wait flash busy done (wip=0) + SpicWaitWipDoneRefinedRtl8195A(obj->SpicInitPara); + + * data = HAL_READ32(SPI_FLASH_BASE, address); + SpicDisableRtl8195A(); + + return 1; +} + +/** + * @brief Write a word to specified address + * @param obj: Specifies the parameter of flash object. + * @param address: Specifies the address to be programmed. + * @param data: Specified the data to be programmed. + * @retval status: Success:1 or Failure: Others. + */ +int flash_write_word(flash_t *obj, uint32_t address, uint32_t data) +{ + // Disable write protection +// flash_unlock(); + flash_init(obj); + + //Write word + HAL_WRITE32(SPI_FLASH_BASE, address, data); + + // Wait spic busy done + SpicWaitBusyDoneRtl8195A(); + + // Wait flash busy done (wip=0) + SpicWaitWipDoneRefinedRtl8195A(obj->SpicInitPara); + + SpicDisableRtl8195A(); + // Enable write protection +// flash_lock(); + return 1; +} + + +/** + * @brief Read a stream of data from specified address + * @param obj: Specifies the parameter of flash object. + * @param address: Specifies the address to be read. + * @param len: Specifies the length of the data to read. + * @param data: Specified the address to save the readback data. + * @retval status: Success:1 or Failure: Others. + */ +int flash_stream_read(flash_t *obj, uint32_t address, uint32_t len, uint8_t * data) +{ + u32 offset_to_align; + u32 i; + u32 read_word; + uint8_t *ptr; + uint8_t *pbuf; + + flash_init(obj); + + // Wait flash busy done (wip=0) + SpicWaitWipDoneRefinedRtl8195A(obj->SpicInitPara); + + offset_to_align = address & 0x03; + pbuf = data; + if (offset_to_align != 0) { + // the start address is not 4-bytes aligned + read_word = HAL_READ32(SPI_FLASH_BASE, (address - offset_to_align)); + ptr = (uint8_t*)&read_word + offset_to_align; + offset_to_align = 4 - offset_to_align; + for (i=0;i> 2) + 1) << 2; // address = next 4-bytes aligned + + ptr = (uint8_t*)&read_word; + if ((u32)pbuf & 0x03) { + while (len >= 4) { + read_word = HAL_READ32(SPI_FLASH_BASE, address); + for (i=0;i<4;i++) { + *pbuf = *(ptr+i); + pbuf++; + } + address += 4; + len -= 4; + } + } + else { + while (len >= 4) { + *((u32 *)pbuf) = HAL_READ32(SPI_FLASH_BASE, address); + pbuf += 4; + address += 4; + len -= 4; + } + } + + if (len > 0) { + read_word = HAL_READ32(SPI_FLASH_BASE, address); + for (i=0;iSpicInitPara); + } + + address = (((address-1) >> 2) + 1) << 2; // address = next 4-bytes aligned + + if ((u32)pbuf & 0x03) { + while (len >= 4) { + write_word = (u32)(*pbuf) | ((u32)(*(pbuf+1)) << 8) | ((u32)(*(pbuf+2)) << 16) | ((u32)(*(pbuf+3)) << 24); + //Write word + HAL_WRITE32(SPI_FLASH_BASE, address, write_word); + // Wait spic busy done + SpicWaitBusyDoneRtl8195A(); + // Wait flash busy done (wip=0) + SpicWaitWipDoneRefinedRtl8195A(obj->SpicInitPara); + pbuf += 4; + address += 4; + len -= 4; + } + } + else { + while (len >= 4) { + //Write word + HAL_WRITE32(SPI_FLASH_BASE, address, (u32)*((u32 *)pbuf)); + // Wait spic busy done + SpicWaitBusyDoneRtl8195A(); + // Wait flash busy done (wip=0) + SpicWaitWipDoneRefinedRtl8195A(obj->SpicInitPara); + pbuf += 4; + address += 4; + len -= 4; + } + } + + if (len > 0) { + write_word = HAL_READ32(SPI_FLASH_BASE, address); + ptr = (uint8_t*)&write_word; + for (i=0;iSpicInitPara); + } + + SpicDisableRtl8195A(); + return 1; +} + diff --git a/component/common/mbed/targets/hal/rtl8195a/gpio_api.c b/component/common/mbed/targets/hal/rtl8195a/gpio_api.c new file mode 100644 index 0000000..6dbf718 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/gpio_api.c @@ -0,0 +1,114 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include "pinmap.h" + +#if CONFIG_GPIO_EN + +#include "gpio_api.h" + +void gpio_set_hal_pin_mode(gpio_t *obj) +{ + if (obj->direction == PIN_OUTPUT) { + switch (obj->mode) { + case PullNone: + case PullDown: + case PullUp: + obj->hal_pin.pin_mode = DOUT_PUSH_PULL; + break; + + case OpenDrain: + obj->hal_pin.pin_mode = DOUT_OPEN_DRAIN; + break; + + default: + obj->hal_pin.pin_mode = DOUT_PUSH_PULL; + break; + } + } + else { + switch (obj->mode) { + case PullNone: + case OpenDrain: + obj->hal_pin.pin_mode = DIN_PULL_NONE; + break; + + case PullDown: + obj->hal_pin.pin_mode = DIN_PULL_LOW; + break; + + case PullUp: + obj->hal_pin.pin_mode = DIN_PULL_HIGH; + break; + + default: + obj->hal_pin.pin_mode = DIN_PULL_NONE; + break; + } + } +} + +uint32_t gpio_set(PinName pin) +{ + u32 ip_pin; + + //MBED_ASSERT(pin != (PinName)NC); + DBG_ASSERT(pin != (PinName)NC); + pin_function(pin, 0); + ip_pin = HAL_GPIO_GetPinName((u32)pin); +// DBG_GPIO_INFO("%s chip_pin[0x%x]->ip_pin[0x%x]\n", __FUNCTION__, pin, ip_pin); + + return ip_pin; +} + +void gpio_init(gpio_t *obj, PinName pin) +{ + if (pin == (PinName)NC) + return; + + obj->pin = pin; + obj->mode = PullNone; + obj->direction = PIN_INPUT; + obj->hal_pin.pin_name = gpio_set(pin); // get the IP pin name + obj->hal_pin.pin_mode = DIN_PULL_NONE; + HAL_GPIO_Init(&obj->hal_pin); +} + +void gpio_mode(gpio_t *obj, PinMode mode) +{ + obj->mode = mode; + gpio_set_hal_pin_mode(obj); + //DBG_GPIO_INFO("%s GPIO[0x%x], mode=%d\n", __FUNCTION__, obj->hal_pin.pin_name, obj->hal_pin.pin_mode); + HAL_GPIO_Init(&obj->hal_pin); +} + +void gpio_dir(gpio_t *obj, PinDirection direction) { +// MBED_ASSERT(obj->pin != (PinName)NC); + DBG_ASSERT(obj->pin != (PinName)NC); + obj->direction = direction; + gpio_set_hal_pin_mode(obj); + //DBG_GPIO_INFO("%s GPIO[0x%x], mode=%d\n", __FUNCTION__, obj->hal_pin.pin_name, obj->hal_pin.pin_mode); + HAL_GPIO_Init(&obj->hal_pin); +} + +void gpio_write(gpio_t *obj, int value) +{ +// MBED_ASSERT(obj->pin != (PinName)NC); + DBG_ASSERT(obj->pin != (PinName)NC); + HAL_GPIO_WritePin(&obj->hal_pin, value); +} + +int gpio_read(gpio_t *obj) { +// MBED_ASSERT(obj->pin != (PinName)NC); + DBG_ASSERT(obj->pin != (PinName)NC); + return HAL_GPIO_ReadPin(&obj->hal_pin); +} + +#endif \ No newline at end of file diff --git a/component/common/mbed/targets/hal/rtl8195a/gpio_irq_api.c b/component/common/mbed/targets/hal/rtl8195a/gpio_irq_api.c new file mode 100644 index 0000000..efce684 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/gpio_irq_api.c @@ -0,0 +1,73 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include "pinmap.h" + +//static uint32_t channel_ids[32] = {0}; + +//static gpio_irq_handler irq_handler; + +#if CONFIG_GPIO_EN +#include "gpio_irq_api.h" + +int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id) +{ + + if (pin == NC) return -1; + + obj->pin = pin; + obj->hal_pin.pin_name = HAL_GPIO_GetPinName((u32)pin);; // get the IP pin name + obj->hal_pin.pin_mode = INT_FALLING; // default use Falling trigger + HAL_GPIO_Irq_Init(&obj->hal_pin); + HAL_GPIO_UserRegIrq(&obj->hal_pin, (VOID*) handler, (VOID*) id); + + return 0; +} + +void gpio_irq_free(gpio_irq_t *obj) +{ + HAL_GPIO_UserUnRegIrq(&obj->hal_pin); + HAL_GPIO_DeInit(&obj->hal_pin); +} + +void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable) +{ + switch(event) { + case IRQ_RISE: + obj->hal_pin.pin_mode = INT_RISING; + break; + + case IRQ_FALL: + obj->hal_pin.pin_mode = INT_FALLING; + break; + + case IRQ_NONE: + // ? + break; + + default: + break; + } + HAL_GPIO_Irq_Init(&obj->hal_pin); + + HAL_GPIO_IntCtrl(&obj->hal_pin, enable); +} + +void gpio_irq_enable(gpio_irq_t *obj) +{ + HAL_GPIO_UnMaskIrq(&obj->hal_pin); +} + +void gpio_irq_disable(gpio_irq_t *obj) +{ + HAL_GPIO_MaskIrq(&obj->hal_pin); +} + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/gpio_object.h b/component/common/mbed/targets/hal/rtl8195a/gpio_object.h new file mode 100644 index 0000000..63820cf --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/gpio_object.h @@ -0,0 +1,39 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_GPIO_OBJECT_H +#define MBED_GPIO_OBJECT_H + +#include "mbed_assert.h" + +#include "basic_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + PinName pin; + uint32_t mask; + + uint32_t reg_out_offset; + uint32_t reg_dir_offset; +} gpio_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/i2c_api.c b/component/common/mbed/targets/hal/rtl8195a/i2c_api.c new file mode 100644 index 0000000..7a73af1 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/i2c_api.c @@ -0,0 +1,593 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + +//#include "mbed_assert.h" +#include "objects.h" +#include "PinNames.h" +//#include +#include "hal_i2c.h" +#include "i2c_api.h" + + + +#if CONFIG_I2C_EN + +//#include "cmsis.h" +#include "pinmap.h" + + +static const PinMap PinMap_I2C_SDA[] = { + {PD_4, RTL_PIN_PERI(I2C0, 0, S0), RTL_PIN_FUNC(I2C0, S0)}, + {PH_1, RTL_PIN_PERI(I2C0, 0, S1), RTL_PIN_FUNC(I2C0, S1)}, + {PC_8, RTL_PIN_PERI(I2C0, 0, S2), RTL_PIN_FUNC(I2C0, S2)}, + {PE_7, RTL_PIN_PERI(I2C0, 0, S3), RTL_PIN_FUNC(I2C0, S3)}, + + {PC_4, RTL_PIN_PERI(I2C1, 1, S0), RTL_PIN_FUNC(I2C1, S0)}, + {PH_3, RTL_PIN_PERI(I2C1, 1, S1), RTL_PIN_FUNC(I2C1, S1)}, + {PD_7, RTL_PIN_PERI(I2C1, 1, S2), RTL_PIN_FUNC(I2C1, S2)}, + + {PB_7, RTL_PIN_PERI(I2C2, 2, S0), RTL_PIN_FUNC(I2C2, S0)}, + {PE_1, RTL_PIN_PERI(I2C2, 2, S1), RTL_PIN_FUNC(I2C2, S1)}, + {PC_7, RTL_PIN_PERI(I2C2, 2, S2), RTL_PIN_FUNC(I2C2, S2)}, + + {PB_3, RTL_PIN_PERI(I2C3, 3, S0), RTL_PIN_FUNC(I2C3, S0)}, + {PE_3, RTL_PIN_PERI(I2C3, 3, S1), RTL_PIN_FUNC(I2C3, S1)}, + {PE_5, RTL_PIN_PERI(I2C3, 3, S2), RTL_PIN_FUNC(I2C3, S2)}, + {PD_9, RTL_PIN_PERI(I2C3, 3, S3), RTL_PIN_FUNC(I2C3, S3)}, + + {NC, NC, 0} +}; + +static const PinMap PinMap_I2C_SCL[] = { + {PD_5, RTL_PIN_PERI(I2C0, 0, S0), RTL_PIN_FUNC(I2C0, S0)}, + {PH_0, RTL_PIN_PERI(I2C0, 0, S1), RTL_PIN_FUNC(I2C0, S1)}, + {PC_9, RTL_PIN_PERI(I2C0, 0, S2), RTL_PIN_FUNC(I2C0, S2)}, + {PE_6, RTL_PIN_PERI(I2C0, 0, S3), RTL_PIN_FUNC(I2C0, S3)}, + + {PC_5, RTL_PIN_PERI(I2C1, 1, S0), RTL_PIN_FUNC(I2C1, S0)}, + {PH_2, RTL_PIN_PERI(I2C1, 1, S1), RTL_PIN_FUNC(I2C1, S1)}, + {PD_6, RTL_PIN_PERI(I2C1, 1, S2), RTL_PIN_FUNC(I2C1, S2)}, + + {PB_6, RTL_PIN_PERI(I2C2, 2, S0), RTL_PIN_FUNC(I2C2, S0)}, + {PE_0, RTL_PIN_PERI(I2C2, 2, S1), RTL_PIN_FUNC(I2C2, S1)}, + {PC_6, RTL_PIN_PERI(I2C2, 2, S2), RTL_PIN_FUNC(I2C2, S2)}, + + {PB_2, RTL_PIN_PERI(I2C3, 3, S0), RTL_PIN_FUNC(I2C3, S0)}, + {PE_2, RTL_PIN_PERI(I2C3, 3, S1), RTL_PIN_FUNC(I2C3, S1)}, + {PE_4, RTL_PIN_PERI(I2C3, 3, S2), RTL_PIN_FUNC(I2C3, S2)}, + {PD_8, RTL_PIN_PERI(I2C3, 3, S3), RTL_PIN_FUNC(I2C3, S3)}, + + {NC, NC, 0} +}; + +static uint16_t i2c_target_addr[4]; +static SAL_I2C_TRANSFER_BUF i2ctxtranbuf[4]; +static SAL_I2C_TRANSFER_BUF i2crxtranbuf[4]; +extern u32 ConfigDebugErr; +extern u32 ConfigDebuginfo; +void i2c_init(i2c_t *obj, PinName sda, PinName scl) { + + uint32_t i2c_sel; + uint32_t i2c_idx; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_USERCB_ADPT pSalI2CUserCBAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + + // Determine the I2C to use + uint32_t i2c_sda = (uint32_t)pinmap_peripheral(sda, PinMap_I2C_SDA); + uint32_t i2c_scl = (uint32_t)pinmap_peripheral(scl, PinMap_I2C_SCL); + ConfigDebugErr &= (~(_DBG_I2C_|_DBG_GDMA_)); + ConfigDebugInfo&= (~(_DBG_I2C_|_DBG_GDMA_)); + i2c_sel = (uint32_t)pinmap_merge(i2c_sda, i2c_scl); + i2c_idx = RTL_GET_PERI_IDX(i2c_sel); + if (unlikely(i2c_idx == NC)) { + DBG_8195A("%s: Cannot find matched UART\n", __FUNCTION__); + return; + } + + DBG_8195A("i2c_sel:%x\n",i2c_sel); + DBG_8195A("i2c_idx:%x\n",i2c_idx); + + /* Get I2C device handler */ + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)&(obj->SalI2CUserCBAdpt); + + + + /*To assign the rest pointers*/ + pSalI2CMngtAdpt->MstRDCmdCnt = 0; + pSalI2CMngtAdpt->InnerTimeOut = 2000; // inner time-out count, 2000 ms + pSalI2CMngtAdpt->pSalHndPriv = &(obj->SalI2CHndPriv); + pSalI2CMngtAdpt->pSalHndPriv->ppSalI2CHnd = (void**)&(pSalI2CMngtAdpt->pSalHndPriv); + + /* To assign the default (ROM) HAL OP initialization function */ + pSalI2CMngtAdpt->pHalOpInit = &HalI2COpInit; + + /* To assign the default (ROM) HAL GDMA OP initialization function */ + pSalI2CMngtAdpt->pHalGdmaOpInit = &HalGdmaOpInit; + + /* To assign the default (ROM) SAL interrupt function */ + pSalI2CMngtAdpt->pSalIrqFunc = &I2CISRHandle; + + /* To assign the default (ROM) SAL DMA TX interrupt function */ + pSalI2CMngtAdpt->pSalDMATxIrqFunc = &I2CTXGDMAISRHandle; + + /* To assign the default (ROM) SAL DMA RX interrupt function */ + pSalI2CMngtAdpt->pSalDMARxIrqFunc = &I2CRXGDMAISRHandle; + + pSalI2CMngtAdpt->pHalInitDat = &(obj->HalI2CInitData); + pSalI2CMngtAdpt->pHalOp = &(obj->HalI2COp); + pSalI2CMngtAdpt->pIrqHnd = &(obj->I2CIrqHandleDat); + pSalI2CMngtAdpt->pHalTxGdmaAdp = &(obj->HalI2CTxGdmaAdpt); + pSalI2CMngtAdpt->pHalRxGdmaAdp = &(obj->HalI2CRxGdmaAdpt); + pSalI2CMngtAdpt->pHalGdmaOp = &(obj->HalI2CGdmaOp); + pSalI2CMngtAdpt->pIrqTxGdmaHnd = &(obj->I2CTxGdmaIrqHandleDat); + pSalI2CMngtAdpt->pIrqRxGdmaHnd = &(obj->I2CRxGdmaIrqHandleDat); + pSalI2CMngtAdpt->pUserCB = &(obj->SalI2CUserCB); + pSalI2CMngtAdpt->pDMAConf = &(obj->SalI2CDmaUserDef); + + /* Assign the private SAL handle to public SAL handle */ + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + /* Assign the internal HAL initial data pointer to the SAL handle */ + pSalI2CHND->pInitDat = pSalI2CMngtAdpt->pHalInitDat; + + /* Assign the internal user callback pointer to the SAL handle */ + pSalI2CHND->pUserCB = pSalI2CMngtAdpt->pUserCB; + + /* Assign the internal user define DMA configuration to the SAL handle */ + pSalI2CHND->pDMAConf = pSalI2CMngtAdpt->pDMAConf; + + /*To assign user callback pointers*/ + pSalI2CMngtAdpt->pUserCB->pTXCB = pSalI2CUserCBAdpt; + pSalI2CMngtAdpt->pUserCB->pTXCCB = (pSalI2CUserCBAdpt+1); + pSalI2CMngtAdpt->pUserCB->pRXCB = (pSalI2CUserCBAdpt+2); + pSalI2CMngtAdpt->pUserCB->pRXCCB = (pSalI2CUserCBAdpt+3); + pSalI2CMngtAdpt->pUserCB->pRDREQCB = (pSalI2CUserCBAdpt+4); + pSalI2CMngtAdpt->pUserCB->pERRCB = (pSalI2CUserCBAdpt+5); + pSalI2CMngtAdpt->pUserCB->pDMATXCB = (pSalI2CUserCBAdpt+6); + pSalI2CMngtAdpt->pUserCB->pDMATXCCB = (pSalI2CUserCBAdpt+7); + pSalI2CMngtAdpt->pUserCB->pDMARXCB = (pSalI2CUserCBAdpt+8); + pSalI2CMngtAdpt->pUserCB->pDMARXCCB = (pSalI2CUserCBAdpt+9); + pSalI2CMngtAdpt->pUserCB->pGENCALLCB= (pSalI2CUserCBAdpt+10); + + /* Set I2C Device Number */ + pSalI2CHND->DevNum = i2c_idx; + + /* Load I2C default value */ + RtkI2CLoadDefault(pSalI2CHND); + + /* Assign I2C Pin Mux */ + pSalI2CHND->PinMux = RTL_GET_PERI_SEL(i2c_sel); + pSalI2CHND->OpType = I2C_INTR_TYPE; + pSalI2CHND->I2CMaster = I2C_MASTER_MODE; + pSalI2CHND->I2CSpdMod = I2C_SS_MODE; + pSalI2CHND->I2CClk = 100; + pSalI2CHND->I2CAckAddr = 0; + pSalI2CHND->TimeOut = 300; + pSalI2CHND->I2CExd |= (I2C_EXD_MTR_ADDR_RTY); + + pSalI2CMngtAdpt->InnerTimeOut = pSalI2CHND->TimeOut; + + + /* Deinit I2C first */ + //i2c_reset(obj); + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); +} + +void i2c_frequency(i2c_t *obj, int hz) { + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + uint16_t i2c_default_clk = (uint16_t) pSalI2CHND->I2CClk; + uint16_t i2c_user_clk = (uint16_t) (hz/1000); + + + + if (i2c_default_clk != i2c_user_clk) { + /* Deinit I2C first */ + i2c_reset(obj); + if (i2c_user_clk <= 100) { + pSalI2CHND->I2CSpdMod = I2C_SS_MODE; + } + else if ((i2c_user_clk > 100) && (i2c_user_clk <= 400)) { + pSalI2CHND->I2CSpdMod = I2C_FS_MODE; + } + else if (i2c_user_clk > 400) { + pSalI2CHND->I2CSpdMod = I2C_HS_MODE; + } + else { + pSalI2CHND->I2CSpdMod = I2C_SS_MODE; + } + + /* Load the user defined I2C clock */ + pSalI2CHND->I2CClk = i2c_user_clk; + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); + } +} + +inline int i2c_start(i2c_t *obj) { + return 0; +} + +inline int i2c_stop(i2c_t *obj) { + return 0; +} + +extern u32 +HalDelayUs( + IN u32 us +); + +int i2c_read(i2c_t *obj, int address, char *data, int length, int stop) { + + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + u32 I2CInTOTcnt = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + if (i2c_target_addr[pSalI2CHND->DevNum] != address) { + /* Deinit I2C first */ + i2c_reset(obj); + + /* Load the user defined I2C target slave address */ + i2c_target_addr[pSalI2CHND->DevNum] = address; + pSalI2CHND->I2CAckAddr = address; + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); + } + + /* Check if the it's the last byte or not */ + pSalI2CHND->I2CExd &= (~I2C_EXD_MTR_HOLD_BUS); + if (!stop) { + pSalI2CHND->I2CExd |= I2C_EXD_MTR_HOLD_BUS; + } + + pSalI2CHND->pRXBuf = &i2crxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pRXBuf->DataLen = length; + pSalI2CHND->pRXBuf->TargetAddr= pSalI2CHND->I2CAckAddr; + pSalI2CHND->pRXBuf->RegAddr = 0; + pSalI2CHND->pRXBuf->pDataBuf = (u8 *)data; + + if (RtkI2CReceive(pSalI2CHND) != HAL_OK) { + length = length - pSalI2CHND->pRXBuf->DataLen; + return ((int)length); + } + else { + //DBG_8195A(">\n"); + /* Calculate user time out parameters */ + I2CInTOTcnt = 300; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + while((pSalI2CHND->DevSts != I2C_STS_IDLE) && + (pSalI2CHND->DevSts != I2C_STS_ERROR) && + (pSalI2CHND->DevSts != I2C_STS_TIMEOUT)) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + + /* DeInit I2C, Init I2C */ + //RtkI2CDeInit(pSalI2CHND); + //HalDelayUs(1000); + //RtkI2CInit(pSalI2CHND); + + return ((int)(length)); + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + /* DeInit I2C, Init I2C */ + //RtkI2CDeInit(pSalI2CHND); + + //RtkI2CInit(pSalI2CHND); + + return ((int)(length)); + } + } + } + //DBG_8195A("<\n"); + if (pSalI2CHND->DevSts != I2C_STS_TIMEOUT) + return ((int)(length - pSalI2CHND->pRXBuf->DataLen)); + else + return ((int)(length)); + } +} + +int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop) { + + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + u32 I2CInTOTcnt = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + if (i2c_target_addr[pSalI2CHND->DevNum] != address) { + /* Deinit I2C first */ + i2c_reset(obj); + + /* Load the user defined I2C target slave address */ + i2c_target_addr[pSalI2CHND->DevNum] = address; + pSalI2CHND->I2CAckAddr = address; + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); + } + + /* Check if the it's the last byte or not */ + pSalI2CHND->I2CExd &= (~I2C_EXD_MTR_HOLD_BUS); + if (!stop) { + pSalI2CHND->I2CExd |= I2C_EXD_MTR_HOLD_BUS; + } + + pSalI2CHND->pTXBuf = &i2ctxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pTXBuf->DataLen = length; + pSalI2CHND->pTXBuf->TargetAddr= pSalI2CHND->I2CAckAddr; + pSalI2CHND->pTXBuf->RegAddr = 0; + pSalI2CHND->pTXBuf->pDataBuf = (u8 *)data; + + if (RtkI2CSend(pSalI2CHND) != HAL_OK) { + length = length - pSalI2CHND->pTXBuf->DataLen; + return ((int)length); + } + else { + //DBG_8195A("(\n"); + /* Calculate user time out parameters */ + I2CInTOTcnt = 300; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + while((pSalI2CHND->DevSts != I2C_STS_IDLE) && + (pSalI2CHND->DevSts != I2C_STS_ERROR) && + (pSalI2CHND->DevSts != I2C_STS_TIMEOUT)) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + /* DeInit I2C, Init I2C */ + //RtkI2CDeInit(pSalI2CHND); + + //RtkI2CInit(pSalI2CHND); + return ((int)(length)); + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + /* DeInit I2C, Init I2C */ + //RtkI2CDeInit(pSalI2CHND); + + //RtkI2CInit(pSalI2CHND); + return ((int)(length)); + } + } + } + + if (pSalI2CHND->DevSts != I2C_STS_TIMEOUT) + return ((int)(length - pSalI2CHND->pTXBuf->DataLen)); + else + return ((int)(length)); + } +} + +int i2c_byte_read(i2c_t *obj, int last) { + uint8_t i2cdatlocal; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + /* Check if the it's the last byte or not */ + pSalI2CHND->I2CExd &= (~I2C_EXD_MTR_HOLD_BUS); + if (!last) { + pSalI2CHND->I2CExd |= I2C_EXD_MTR_HOLD_BUS; + } + + pSalI2CHND->pRXBuf = &i2crxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pRXBuf->DataLen = 1; + pSalI2CHND->pRXBuf->TargetAddr= pSalI2CHND->I2CAckAddr; + pSalI2CHND->pRXBuf->RegAddr = 0; + pSalI2CHND->pRXBuf->pDataBuf = &i2cdatlocal; + RtkI2CReceive(pSalI2CHND); + + return (int)i2cdatlocal; +} + +int i2c_byte_write(i2c_t *obj, int data) { + + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + pSalI2CHND->I2CExd &= (~I2C_EXD_MTR_HOLD_BUS); + pSalI2CHND->I2CExd |= I2C_EXD_MTR_HOLD_BUS; + + pSalI2CHND->pTXBuf = &i2ctxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pTXBuf->DataLen = 1; + pSalI2CHND->pTXBuf->TargetAddr= pSalI2CHND->I2CAckAddr; + pSalI2CHND->pTXBuf->RegAddr = 0; + pSalI2CHND->pTXBuf->pDataBuf = (unsigned char*)&data; + + if (RtkI2CSend(pSalI2CHND) != HAL_OK) { + return 0; + } + + return 1; +} + +void i2c_reset(i2c_t *obj) { + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + /* Deinit I2C directly */ + RtkI2CDeInit(pSalI2CHND); +} + +#if DEVICE_I2CSLAVE + +void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask) { + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + uint16_t i2c_default_addr = (uint16_t) pSalI2CHND->I2CAckAddr; + uint16_t i2c_user_addr = (uint16_t) address; + + if (i2c_default_addr != i2c_user_addr) { + /* Deinit I2C first */ + i2c_reset(obj); + + /* Load the user defined I2C clock */ + pSalI2CHND->I2CAckAddr = i2c_user_addr; + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); + } +} + +void i2c_slave_mode(i2c_t *obj, int enable_slave) { + + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + /* Deinit I2C first */ + i2c_reset(obj); + + /* Load the user defined I2C clock */ + pSalI2CHND->I2CMaster = I2C_MASTER_MODE; + if (enable_slave) + pSalI2CHND->I2CMaster = I2C_SLAVE_MODE; + + /* Init I2C now */ + RtkI2CInit(pSalI2CHND); +} + +// See I2CSlave.h +#define NoData 0 // the slave has not been addressed +#define ReadAddressed 1 // the master has requested a read from this slave (slave = transmitter) +#define WriteGeneral 2 // the master is writing to all slave +#define WriteAddressed 3 // the master is writing to this slave (slave = receiver) + +int i2c_slave_receive(i2c_t *obj) { + + int i2cslvrevsts = NoData; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + i2cslvrevsts = RtkSalI2CSts(pSalI2CHND); + return i2cslvrevsts; +} + +int i2c_slave_read(i2c_t *obj, char *data, int length) { + + u32 I2CInTOTcnt = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + + //uint8_t i2cdatlocal; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + pSalI2CHND->pRXBuf = &i2crxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pRXBuf->DataLen = length; + pSalI2CHND->pRXBuf->pDataBuf = (u8 *)data; + + if (RtkI2CReceive(pSalI2CHND) != HAL_OK) { + return 0; //error + } + else { + /* Calculate user time out parameters */ + I2CInTOTcnt = I2C_TIMEOOUT_ENDLESS; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + while((pSalI2CHND->DevSts != I2C_STS_IDLE) && + (pSalI2CHND->DevSts != I2C_STS_ERROR) && + (pSalI2CHND->DevSts != I2C_STS_TIMEOUT)) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + return ((int)(length)); + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + return ((int)(length)); + } + } + } + + if (pSalI2CHND->DevSts != I2C_STS_TIMEOUT) + return ((int)(length - pSalI2CHND->pTXBuf->DataLen)); + else + return ((int)(length)); + } +} + +int i2c_slave_write(i2c_t *obj, const char *data, int length) { + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + pSalI2CMngtAdpt = &(obj->SalI2CMngtAdpt); + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + pSalI2CHND->pTXBuf = &i2ctxtranbuf[pSalI2CHND->DevNum]; + pSalI2CHND->pTXBuf->DataLen = length; + //obj->i2c->pTXBuf->TargetAddr= obj->i2c->I2CAckAddr; + //obj->i2c->pTXBuf->RegAddr = 0; + pSalI2CHND->pTXBuf->pDataBuf = (u8 *)data; + + if (RtkI2CSend(pSalI2CHND) != HAL_OK) { + return 0; //error + } + + return 1; +} + + +#endif // CONFIG_I2C_SLAVE_EN + +#endif // CONFIG_I2C_EN + diff --git a/component/common/mbed/targets/hal/rtl8195a/i2s_api.c b/component/common/mbed/targets/hal/rtl8195a/i2s_api.c new file mode 100644 index 0000000..a4b7ee5 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/i2s_api.c @@ -0,0 +1,244 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2015, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include "i2s_api.h" +#include "pinmap.h" + +#if CONFIG_I2S_EN +static const PinMap PinMap_I2S_TX[] = { + {PE_2, RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)}, + {PH_2, RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)}, + {PD_2, RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)}, + {PC_7, RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)}, + {PC_2, RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)}, + {PD_6, RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)}, + {PE_6, RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)}, + {NC, NC, 0} +}; + +static const PinMap PinMap_I2S_RX[] = { + {PH_5, RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)}, + {PC_5, RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)}, + {PC_4, RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)}, + {PD_3, RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)}, + {PE_8, RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S1)}, + {NC, NC, 0} +}; + +static const PinMap PinMap_I2S_CLK[] = { + {PE_1, RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)}, + {PH_1, RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)}, + {PD_1, RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)}, + {PC_8, RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)}, + {PC_1, RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)}, + {PD_5, RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)}, + {PE_5, RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)}, + {NC, NC, 0} +}; + +static const PinMap PinMap_I2S_WS[] = { + {PE_0, RTL_PIN_PERI(I2S0, 0, S0), RTL_PIN_FUNC(I2S0, S0)}, + {PH_0, RTL_PIN_PERI(I2S0, 0, S1), RTL_PIN_FUNC(I2S0, S1)}, + {PD_0, RTL_PIN_PERI(I2S0, 0, S2), RTL_PIN_FUNC(I2S0, S2)}, + {PC_9, RTL_PIN_PERI(I2S0, 0, S3), RTL_PIN_FUNC(I2S0, S3)}, + {PC_0, RTL_PIN_PERI(I2S1, 1, S0), RTL_PIN_FUNC(I2S1, S0)}, + {PD_4, RTL_PIN_PERI(I2S1, 1, S1), RTL_PIN_FUNC(I2S1, S1)}, + {PE_4, RTL_PIN_PERI(I2S1, 1, S2), RTL_PIN_FUNC(I2S1, S2)}, + {NC, NC, 0} +}; + +static const HAL_I2S_DEF_SETTING I2SDefaultSetting = { + .I2SMaster = I2S_MASTER_MODE, // I2S Function Mode + .DevSts = I2S_STS_UNINITIAL, //I2S device status + .I2SChNum = I2S_CH_STEREO, //I2S Channel number mono or stereo + .I2SPageNum = I2S_4PAGE, //I2S Page number 2~4 + .I2STRxAct = I2S_TXRX, //I2S tx rx act, tx only or rx only or tx+rx + .I2SWordLen = I2S_WL_16, //I2S Word length 16bit or 24bit + .I2SPageSize = (768/4)-1, //I2S Page size 1~4096 word + .I2SRate = I2S_SR_48KHZ, //I2S sample rate 8k ~ 96khz + + .I2STxIntrMSK = I2S_TX_INT_PAGE0_OK|I2S_TX_INT_PAGE1_OK| \ + I2S_TX_INT_PAGE2_OK|I2S_TX_INT_PAGE3_OK, /*I2S Tx Interrupt Mask*/ + .I2SRxIntrMSK = I2S_RX_INT_PAGE0_OK|I2S_RX_INT_PAGE1_OK| \ + I2S_RX_INT_PAGE2_OK|I2S_RX_INT_PAGE3_OK /*I2S Rx Interrupt Mask*/ +}; + +void i2s_init(i2s_t *obj, PinName sck, PinName ws, PinName sd) +{ + uint32_t i2s_sck, i2s_ws, i2s_tx, i2s_rx; + uint32_t i2s_sck_ws, i2s_sel;; + uint8_t i2s_idx; + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + u8 trx_act; + + // Determine the UART to use (UART0, UART1, or UART3) + i2s_sck = pinmap_peripheral(sck, PinMap_I2S_CLK); + i2s_ws = pinmap_peripheral(ws, PinMap_I2S_WS); + i2s_tx = pinmap_peripheral(sd, PinMap_I2S_TX); + i2s_rx = pinmap_peripheral(sd, PinMap_I2S_RX); + + i2s_sck_ws = pinmap_merge(i2s_sck, i2s_ws); + if (unlikely(i2s_sck_ws == NC)) { + DBG_I2S_ERR("%s: Cannot find matched I2S for given pin\n", __FUNCTION__); + return; + } + + trx_act = I2S_TXRX; + i2s_sel = pinmap_merge(i2s_sck_ws, i2s_tx); + if (i2s_sel == NC) { + i2s_sel = pinmap_merge(i2s_sck_ws, i2s_rx); + trx_act = I2S_ONLY_RX; + if (unlikely(i2s_sel == NC)) { + DBG_I2S_ERR("%s: Cannot find matched I2S for given pin\n", __FUNCTION__); + return; + } + } + + i2s_idx = RTL_GET_PERI_IDX(i2s_sel); + + pI2SAdapter->DevNum = i2s_idx; + pI2SAdapter->PinMux = RTL_GET_PERI_SEL(i2s_sel);; + DBG_I2S_INFO("%s: Use I2S%d Sel%d\r\n", __FUNCTION__, pI2SAdapter->DevNum, pI2SAdapter->PinMux); + + pI2SAdapter->pInitDat = &obj->InitDat; + RtkI2SLoadDefault(pI2SAdapter, (VOID*)&I2SDefaultSetting); + pI2SAdapter->pInitDat->I2STRxAct = trx_act; + + // Load user defined parameters + pI2SAdapter->pInitDat->I2SChNum = obj->channel_num; + pI2SAdapter->pInitDat->I2SRate = obj->sampling_rate; + pI2SAdapter->pInitDat->I2SWordLen = obj->word_length; + pI2SAdapter->pInitDat->I2STRxAct = obj->direction; + + RtkI2SInit(pI2SAdapter); +} + +void i2s_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, + uint32_t page_num, uint32_t page_size) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + u32 i; + + if ((page_num < 2) || (page_num > 4) || (page_size < 8)) { + DBG_I2S_INFO("%s: PageNum(%d) valid value is 2~4; PageSize(%d must > 8)\r\n", \ + __FUNCTION__, page_num, page_size); + return; + } + + pI2SAdapter->pInitDat->I2SPageNum = page_num - 1; + pI2SAdapter->pInitDat->I2SPageSize = page_size/4 - 1; // unit is 4-bytes + pI2SAdapter->pInitDat->I2STxData = (u8*)tx_buf; + pI2SAdapter->pInitDat->I2SRxData = (u8*)rx_buf; + HalI2SSetDMABuf(pI2SAdapter->pInitDat); + + for (i=0;iTxPageList[i] = (uint32_t*)(tx_buf + ((page_size) * i)); + pI2SAdapter->RxPageList[i] = (uint32_t*)(rx_buf + ((page_size) * i)); + } +} + +void i2s_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + pI2SAdapter->UserCB.TxCCB = handler; + pI2SAdapter->UserCB.TxCBId = id; +} + +void i2s_rx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + pI2SAdapter->UserCB.RxCCB = handler; + pI2SAdapter->UserCB.RxCBId = id; +} + +void i2s_set_direction(i2s_t *obj, int trx_type) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + obj->direction = trx_type; + pI2SAdapter->pInitDat->I2STRxAct = trx_type; + HalI2SSetDirection(pI2SAdapter->pInitDat); +} + +void i2s_set_param(i2s_t *obj, int channel_num, int rate, int word_len) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + obj->channel_num = channel_num; + obj->sampling_rate = rate; + obj->word_length = word_len; + pI2SAdapter->pInitDat->I2SChNum = channel_num; + pI2SAdapter->pInitDat->I2SRate = rate; + pI2SAdapter->pInitDat->I2SWordLen = word_len; + HalI2SSetChNum(pI2SAdapter->pInitDat); + HalI2SSetRate(pI2SAdapter->pInitDat); + HalI2SSetWordLen(pI2SAdapter->pInitDat); +} + +void i2s_deinit(i2s_t *obj) +{ + RtkI2SDeInit((VOID*)&obj->I2SAdapter); +} + +int* i2s_get_tx_page(i2s_t *obj) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + u8 page_idx; + + page_idx = HalI2SGetTxPage((VOID*)pI2SAdapter->pInitDat); + if (page_idx <= pI2SAdapter->pInitDat->I2SPageNum) { + return ((int*)pI2SAdapter->TxPageList[page_idx]); + } else { + return NULL; + } +} + +void i2s_send_page(i2s_t *obj, uint32_t *pbuf) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + u32 page_num, i; + + page_num = pI2SAdapter->pInitDat->I2SPageNum + 1; + for (i=0;iTxPageList[i] == pbuf) { + HalI2SPageSend(pI2SAdapter->pInitDat, i); + break; // break the for loop + } + } + + if (i == page_num) { + DBG_I2S_ERR("i2s_send_page: the pbuf(0x%x) is not a DMA buffer\r\n", pbuf); + } +} + +void i2s_recv_page(i2s_t *obj) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + HalI2SPageRecv(pI2SAdapter->pInitDat); +} + +void i2s_enable(i2s_t *obj) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + RtkI2SEnable(pI2SAdapter); +} + +void i2s_disable(i2s_t *obj) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) &obj->I2SAdapter; + + RtkI2SDisable(pI2SAdapter); +} + +#endif // end of "#if CONFIG_I2S_EN" diff --git a/component/common/mbed/targets/hal/rtl8195a/nfc_api.c b/component/common/mbed/targets/hal/rtl8195a/nfc_api.c new file mode 100644 index 0000000..87aa22d --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/nfc_api.c @@ -0,0 +1,243 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include "pinmap.h" + +#if CONFIG_NFC_NORMAL + +#include "nfc_api.h" + +/** + * @brief The NFC tag write callback function wrapper + * + * @return None + * + */ +void nfc_tagwrite_callback(PNFC_ADAPTER pNFCAdp, uint32_t page, uint32_t wr_data) +{ + nfctag_t *obj; + nfc_write_cb handler; + + obj = pNFCAdp->nfc_obj; + + handler = (nfc_write_cb)obj->nfc_wr_cb; + if (NULL != handler) { + handler(obj->wr_cb_arg, page, wr_data); + } +} + +/** + * @brief The NFC tag read callback function wrapper + * + * @return None + * + */ +void nfc_event_callback(PNFC_ADAPTER pNFCAdp, uint32_t event) +{ + nfctag_t *obj; + nfc_event_cb handler; + + obj = pNFCAdp->nfc_obj; + + handler = (nfc_event_cb)obj->nfc_ev_cb; + if (NULL != handler) { + if (obj->event_mask & event) { + handler(obj->ev_cb_arg, event); + } + } +} + +/** + * @brief The NFC tag read callback function wrapper + * + * @return None + * + */ +void nfc_tagread_callback(PNFC_ADAPTER pNFCAdp, uint32_t page) +{ + // notify upper layer when read tag page 0 only + if (0 == page) { + nfc_event_callback(pNFCAdp, NFC_EV_READ); + } +} + + +/** + * @brief The NFC cache read done callback function wrapper + * + * @return None + * + */ +void nfc_cache_read_callback(PNFC_ADAPTER pNFCAdp, uint32_t start_pg, uint32_t *pbuf) +{ + nfctag_t *obj; + nfc_write_cb handler; + + obj = pNFCAdp->nfc_obj; + + handler = (nfc_write_cb)obj->nfc_cache_rd_cb; + if (NULL != handler) { + handler(obj->cache_read_cb_arg, start_pg, (uint32_t)pbuf); + } +} + +/** + * @brief To initial NFC tag hardware and resource + * + * @return The result + * + */ +int nfc_init(nfctag_t *obj, uint32_t *pg_init_val) +{ + _memset((void *)obj, 0, sizeof(nfctag_t)); + HalNFCDmemInit(pg_init_val, NFCTAGLENGTH); + HalNFCInit(&(obj->NFCAdapter)); + HalNFCFwDownload(); + obj->NFCAdapter.nfc_obj = obj; + obj->pwr_status = NFC_PWR_RUNNING; + + return NFC_OK; +} + +/** + * @brief To free NFC tag hardware and resource + * + * @return The result + * + */ +int nfc_free(nfctag_t *obj) +{ + HalNFCDeinit(&(obj->NFCAdapter)); + return NFC_OK; +} + +/** + * @brief To register the callback function for NFC read occurred + * + * @return None + * + */ +void nfc_read(nfctag_t *obj, nfc_read_cb handler, void *arg) +{ + obj->nfc_rd_cb = (void *)handler; + obj->rd_cb_arg = arg; +} + +/** + * @brief To register the callback function for NFC write occurred + * + * @return None + * + */ +void nfc_write(nfctag_t *obj, nfc_write_cb handler, void *arg) +{ + obj->nfc_wr_cb = (void *)handler; + obj->wr_cb_arg = arg; +} + +/** + * @brief To register the callback function for NFC events occurred + * and the event mask + * + * @return None + * + */ +void nfc_event(nfctag_t *obj, nfc_event_cb handler, void *arg, unsigned int event_mask) +{ + obj->nfc_ev_cb = (void *)handler; + obj->ev_cb_arg = arg; + obj->event_mask = event_mask; +} + +/** + * @brief To set a new power mode to the NFC device + * + * @return The result + * + */ +int nfc_power(nfctag_t *obj, int pwr_mode, int wake_event) +{ + // TODO: + + return NFC_OK; +} + + +/** + * @brief to update the NFC read cache. The data in the NFC read cache + * buffer will be transmitted out when NFC read occurred + * + * @return The result + * + */ +int nfc_cache_write(nfctag_t *obj, uint32_t *tbuf, unsigned int spage, unsigned int pg_num) +{ + u8 remain_pg; + u8 pg_offset=0; + u8 i; + + if ((spage+pg_num) > NFC_MAX_CACHE_PAGE_NUM) { + return NFC_ERROR; + } + + remain_pg = pg_num; + while (remain_pg > 0) { + if (remain_pg >= 4) { + A2NWriteCatch (&obj->NFCAdapter, (spage+pg_offset), 4, (u32*)(&tbuf[pg_offset])); + remain_pg -= 4; + pg_offset += 4; + } + else { + for(i=0;iNFCAdapter, (spage+pg_offset), 1, (u32*)(&tbuf[pg_offset])); + pg_offset++; + } + remain_pg = 0; + } + } + + return NFC_OK; +} + +/** + * @brief To get current NFC status + * + * @return The result + * + */ +int nfc_cache_raed(nfctag_t *obj, nfc_cache_read_cb handler, + void *arg, unsigned int start_pg) +{ + if (start_pg > NFC_MAX_CACHE_PAGE_NUM) { + return NFC_ERROR; + } + + obj->nfc_cache_rd_cb = (void *)handler; + obj->cache_read_cb_arg = arg; + + A2NReadCatch(&(obj->NFCAdapter), (u8)start_pg); + + return NFC_OK; +} + +/** + * @brief to read back the NFC read cache. + * + * @return The result + * + */ +int nfc_status(nfctag_t *obj) +{ + // TODO: + + return (obj->pwr_status); +} + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/objects.h b/component/common/mbed/targets/hal/rtl8195a/objects.h new file mode 100644 index 0000000..0cf6346 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/objects.h @@ -0,0 +1,174 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_OBJECTS_H +#define MBED_OBJECTS_H + +#include "cmsis.h" +#include "PortNames.h" +#include "PeripheralNames.h" +#include "PinNames.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_GPIO_EN +struct gpio_irq_s { + PinName pin; + uint32_t event; + HAL_GPIO_PIN hal_pin; +}; + +typedef struct gpio_irq_s gpio_irq_t; + +struct gpio_s { + PinName pin; + PinMode mode; + PinDirection direction; + HAL_GPIO_PIN hal_pin; +}; + +typedef struct gpio_s gpio_t; + +struct port_s { + PortName port; + uint32_t mask; + PinDirection direction; + uint8_t *pin_def; +}; +#endif // end of "#ifdef CONFIG_GPIO_EN" + +#ifdef CONFIG_UART_EN +struct serial_s { + HAL_RUART_OP hal_uart_op; + HAL_RUART_ADAPTER hal_uart_adp; +#ifdef CONFIG_GDMA_EN + UART_DMA_CONFIG uart_gdma_cfg; + HAL_GDMA_ADAPTER uart_gdma_adp_tx; + HAL_GDMA_ADAPTER uart_gdma_adp_rx; +#endif +}; +#endif // end of "#ifdef CONFIG_UART_EN" + +#ifdef CONFIG_SPI_COM_EN + +#endif + +#ifdef CONFIG_PWM_EN +struct pwmout_s { + uint8_t pwm_idx; + uint8_t pin_sel; + uint32_t period; + uint32_t pulse; + +}; +#endif + +#ifdef CONFIG_I2C_EN +struct i2c_s { + SAL_I2C_MNGT_ADPT SalI2CMngtAdpt; + SAL_I2C_HND_PRIV SalI2CHndPriv; + HAL_I2C_INIT_DAT HalI2CInitData; + HAL_I2C_OP HalI2COp; + IRQ_HANDLE I2CIrqHandleDat; + HAL_GDMA_ADAPTER HalI2CTxGdmaAdpt; + HAL_GDMA_ADAPTER HalI2CRxGdmaAdpt; + HAL_GDMA_OP HalI2CGdmaOp; + IRQ_HANDLE I2CTxGdmaIrqHandleDat; + IRQ_HANDLE I2CRxGdmaIrqHandleDat; + SAL_I2C_USER_CB SalI2CUserCB; + SAL_I2C_USERCB_ADPT SalI2CUserCBAdpt[SAL_USER_CB_NUM]; + SAL_I2C_DMA_USER_DEF SalI2CDmaUserDef; +}; +#endif + + +struct flash_s +{ + SPIC_INIT_PARA SpicInitPara; +}; + + + +#ifdef CONFIG_ADC_EN +struct analogin_s { + SAL_ADC_MNGT_ADPT SalADCMngtAdpt; + SAL_ADC_HND_PRIV SalADCHndPriv; + HAL_ADC_INIT_DAT HalADCInitData; + HAL_ADC_OP HalADCOp; + IRQ_HANDLE ADCIrqHandleDat; + HAL_GDMA_ADAPTER HalADCGdmaAdpt; + HAL_GDMA_OP HalADCGdmaOp; + IRQ_HANDLE ADCGdmaIrqHandleDat; + SAL_ADC_USER_CB SalADCUserCB; + SAL_ADC_USERCB_ADPT SalADCUserCBAdpt[SAL_USER_CB_NUM]; +}; +#endif + +#if 0 +struct i2c_s { + I2C_Type *i2c; +}; + +struct spi_s { + SPI_Type *spi; +}; + +#endif + +#ifdef CONFIG_NFC_EN +struct nfctag_s { + NFC_ADAPTER NFCAdapter; + void *nfc_rd_cb; // read callback function + void *rd_cb_arg; + void *nfc_wr_cb; // write callback function + void *wr_cb_arg; + void *nfc_ev_cb; // event callback function + void *ev_cb_arg; + void *nfc_cache_rd_cb; // cache read callback function + void *cache_read_cb_arg; + unsigned int event_mask; + int pwr_status; +}; +#endif + +#ifdef CONFIG_TIMER_EN +struct gtimer_s { + TIMER_ADAPTER hal_gtimer_adp; + void *handler; + u32 hid; + u8 timer_id; + u8 is_periodcal; +}; +#endif + +#ifdef CONFIG_I2S_EN +struct i2s_s { + HAL_I2S_ADAPTER I2SAdapter; + HAL_I2S_INIT_DAT InitDat; + u8 sampling_rate; + u8 channel_num; + u8 word_length; + u8 direction; +}; + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/pinmap.c b/component/common/mbed/targets/hal/rtl8195a/pinmap.c new file mode 100644 index 0000000..c0dfdf0 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/pinmap.c @@ -0,0 +1,34 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +//#include "mbed_assert.h" +#include "objects.h" +#include "pinmap.h" +//#include "error.h" + +/** + * Configure pin enable and function + */ +void pin_function(PinName pin, int function) +{ + // MBED_ASSERT(pin != (PinName)NC); + //1 Our HAL API cannot support to configure the pin function by this way + /* the pin function (pin mux) is depends on each IP On/Off and priority, so we cannot + set the pin function directly */ +} + +/** + * Configure pin pull-up/pull-down + */ +void pin_mode(PinName pin, PinMode mode) +{ +// MBED_ASSERT(pin != (PinName)NC); + HAL_GPIO_PullCtrl((u32)pin, (u32)mode); + +} diff --git a/component/common/mbed/targets/hal/rtl8195a/pinmap_common.c b/component/common/mbed/targets/hal/rtl8195a/pinmap_common.c new file mode 100644 index 0000000..1a1001a --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/pinmap_common.c @@ -0,0 +1,73 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "basic_types.h" +#include "diag.h" +#include "pinmap.h" +//#include "error.h" + +__weak void pinmap_pinout(PinName pin, const PinMap *map) { +#if 0 + if (pin == NC) + return; + + while (map->pin != NC) { + if (map->pin == pin) { + pin_function(pin, map->function); + + pin_mode(pin, PullNone); + return; + } + map++; + } + DBG_GPIO_ERR("%s: could not pinout\n", __FUNCTION__); +#endif +} + +__weak uint32_t pinmap_merge(uint32_t a, uint32_t b) { + // both are the same (inc both NC) + if (a == b) + return a; + + // one (or both) is not connected + if (a == (uint32_t)NC) + return b; + if (b == (uint32_t)NC) + return a; + + // mis-match error case + DBG_GPIO_ERR("%s: pinmap mis-match\n", __FUNCTION__); + return (uint32_t)NC; +} + +__weak uint32_t pinmap_find_peripheral(PinName pin, const PinMap* map) { + while (map->pin != NC) { + if (map->pin == pin) + return map->peripheral; + map++; + } + return (uint32_t)NC; +} + +__weak uint32_t pinmap_peripheral(PinName pin, const PinMap* map) { + uint32_t peripheral = (uint32_t)NC; + + if (pin == (PinName)NC) + return (uint32_t)NC; + peripheral = pinmap_find_peripheral(pin, map); + if ((uint32_t)NC == peripheral) // no mapping available + DBG_GPIO_ERR("%s: pinmap not found for peripheral\n", __FUNCTION__); + return peripheral; +} diff --git a/component/common/mbed/targets/hal/rtl8195a/port_api.c b/component/common/mbed/targets/hal/rtl8195a/port_api.c new file mode 100644 index 0000000..f5ad6da --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/port_api.c @@ -0,0 +1,150 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include "port_api.h" +#include "pinmap.h" +#include "gpio_api.h" +#include "PinNames.h" +//#include "mbed_error.h" + +#if CONFIG_GPIO_EN + +#if DEVICE_PORTIN || DEVICE_PORTOUT + +#define GPIO_PORT_NUM 2 +#define GPIO_PORT_WIDTH 8 +#define GPIO_PORT_WIDTH_MAX 16 + +const u8 Default_Port_PinDef[GPIO_PORT_NUM][GPIO_PORT_WIDTH+1] = { + // Port 0 + {PA_6, PA_7, PA_5, PD_4, + PD_5, PA_4, PA_3, PA_2, + 0xFF}, + + // Port 1 + {PB_4, PB_5, PC_0, PC_2, + PC_3, PC_1, PB_3, PB_2, + 0xFF} +}; + +extern VOID HAL_GPIO_Init(HAL_GPIO_PIN *GPIO_Pin); +extern u32 HAL_GPIO_GetPinName(u32 chip_pin); + +// high nibble = port number (0=A, 1=B, 2=C, 3=D, 4=E, 5=F, ...) +// low nibble = pin number +PinName port_pin(PortName port, int pin_n) { + return (PinName)(pin_n + (port << 4)); +} + +void port_init(port_t *obj, PortName port, int mask, PinDirection dir) +{ + u32 i; + u32 port_width=0; + + if (port >= GPIO_PORT_NUM) { + DBG_GPIO_ERR("port_init: Invalid port num(%d), max port num is %d\r\n", \ + port, (GPIO_PORT_NUM-1)); + } + + // Fill PORT object structure for future use + obj->port = port; + obj->mask = mask; + obj->direction = dir; + +// if (obj->pin_def == NULL) { + obj->pin_def = (uint8_t*)&Default_Port_PinDef[port][0]; +// } + + i=0; + while (obj->pin_def[i] != 0xff) { + i++; + if (i == GPIO_PORT_WIDTH_MAX) { + break; + } + } + + obj->mask &= ((1<direction = dir; + for (i = 0; i < GPIO_PORT_WIDTH_MAX; i++) { // Process all pins + if (obj->mask & (1 << i)) { // If the pin is used + + GPIO_Pin.pin_name = HAL_GPIO_GetPinName(obj->pin_def[i]); // get the IP pin name + + if (dir == PIN_OUTPUT) { + GPIO_Pin.pin_mode = DOUT_PUSH_PULL; + } else { // PIN_INPUT + GPIO_Pin.pin_mode = DIN_PULL_NONE; + } + HAL_GPIO_Init(&GPIO_Pin); + } + } +} + +void port_mode(port_t *obj, PinMode mode) +{ + uint32_t i; + + for (i = 0; i < GPIO_PORT_WIDTH_MAX; i++) { // Process all pins + if (obj->mask & (1 << i)) { // If the pin is used + pin_mode(obj->pin_def[i], mode); + } + } +} + +void port_write(port_t *obj, int value) +{ + uint32_t i; + HAL_GPIO_PIN GPIO_Pin; + + for (i = 0; i < GPIO_PORT_WIDTH_MAX; i++) { // Process all pins + if (obj->mask & (1 << i)) { // If the pin is used + GPIO_Pin.pin_name = HAL_GPIO_GetPinName(obj->pin_def[i]); // get the IP pin name + GPIO_Pin.pin_mode = DOUT_PUSH_PULL; + HAL_GPIO_WritePin(&GPIO_Pin, ((value>>i) & 0x01)); + } + } +} + +int port_read(port_t *obj) +{ + int value=0; + u32 i; + HAL_GPIO_PIN_MODE pin_mode; + HAL_GPIO_PIN GPIO_Pin; + + if (obj->direction == PIN_OUTPUT) { + pin_mode = DOUT_PUSH_PULL; + } else { // PIN_INPUT + pin_mode = DIN_PULL_NONE; + } + + for (i = 0; i < GPIO_PORT_WIDTH_MAX; i++) { // Process all pins + if (obj->mask & (1 << i)) { // If the pin is used + GPIO_Pin.pin_name = HAL_GPIO_GetPinName(obj->pin_def[i]); // get the IP pin name + GPIO_Pin.pin_mode = pin_mode; + if (HAL_GPIO_ReadPin(&GPIO_Pin)) { + value |= (1< + +#if DEVICE_PWMOUT + +#ifdef CONFIG_PWM_EN +#include "pwmout_api.h" + +static const PinMap PinMap_PWM[] = { + {PB_4, RTL_PIN_PERI(PWM0, 0, S0), RTL_PIN_FUNC(PWM0, S0)}, + {PB_5, RTL_PIN_PERI(PWM1, 1, S0), RTL_PIN_FUNC(PWM1, S0)}, + {PB_6, RTL_PIN_PERI(PWM2, 2, S0), RTL_PIN_FUNC(PWM2, S0)}, + {PB_7, RTL_PIN_PERI(PWM3, 3, S0), RTL_PIN_FUNC(PWM3, S0)}, + + {PC_0, RTL_PIN_PERI(PWM0, 0, S1), RTL_PIN_FUNC(PWM0, S1)}, + {PC_1, RTL_PIN_PERI(PWM1, 1, S1), RTL_PIN_FUNC(PWM1, S1)}, + {PC_2, RTL_PIN_PERI(PWM2, 2, S1), RTL_PIN_FUNC(PWM2, S1)}, + {PC_3, RTL_PIN_PERI(PWM3, 3, S1), RTL_PIN_FUNC(PWM3, S1)}, + + {PD_3, RTL_PIN_PERI(PWM0, 0, S2), RTL_PIN_FUNC(PWM0, S2)}, + {PD_4, RTL_PIN_PERI(PWM1, 1, S2), RTL_PIN_FUNC(PWM1, S2)}, + {PD_5, RTL_PIN_PERI(PWM2, 2, S2), RTL_PIN_FUNC(PWM2, S2)}, + {PD_6, RTL_PIN_PERI(PWM3, 3, S2), RTL_PIN_FUNC(PWM3, S2)}, + + {PE_0, RTL_PIN_PERI(PWM0, 0, S3), RTL_PIN_FUNC(PWM0, S3)}, + {PE_1, RTL_PIN_PERI(PWM1, 1, S3), RTL_PIN_FUNC(PWM1, S3)}, + {PE_2, RTL_PIN_PERI(PWM2, 2, S3), RTL_PIN_FUNC(PWM2, S3)}, + {PE_3, RTL_PIN_PERI(PWM3, 3, S3), RTL_PIN_FUNC(PWM3, S3)}, + + {NC, NC, 0} +}; + +void pwmout_init(pwmout_t* obj, PinName pin) +{ + uint32_t peripheral; + u32 pwm_idx; + u32 pin_sel; + + DBG_PWM_INFO("%s: Init PWM for pin(0x%x)\n", __FUNCTION__, pin); + + // Get the peripheral name from the pin and assign it to the object + peripheral = pinmap_peripheral(pin, PinMap_PWM); + + if (unlikely(peripheral == NC)) { + DBG_PWM_ERR("%s: Cannot find matched pwm for this pin(0x%x)\n", __FUNCTION__, pin); + return; + } + + pwm_idx = RTL_GET_PERI_IDX(peripheral); + pin_sel = RTL_GET_PERI_SEL(peripheral); + + obj->pwm_idx = pwm_idx; + obj->pin_sel = pin_sel; + obj->period = 0; + obj->pulse = 0; + HAL_Pwm_Init(pwm_idx, pin_sel); + pwmout_period_us(obj, 20000); // 20 ms per default + HAL_Pwm_Enable(pwm_idx); +} + +void pwmout_free(pwmout_t* obj) +{ + HAL_Pwm_Disable(obj->pwm_idx); +} + +void pwmout_write(pwmout_t* obj, float value) +{ + if (value < (float)0.0) { + value = 0.0; + } + else if (value > (float)1.0) { + value = 1.0; + } + + obj->pulse = (uint32_t)((float)obj->period * value); + HAL_Pwm_SetDuty(obj->pwm_idx, obj->period, obj->pulse); +} + +float pwmout_read(pwmout_t* obj) +{ + float value = 0; + if (obj->period > 0) { + value = (float)(obj->pulse) / (float)(obj->period); + } + return ((value > (float)1.0) ? (float)(1.0) : (value)); +} + +void pwmout_period(pwmout_t* obj, float seconds) +{ + pwmout_period_us(obj, (int)(seconds * 1000000.0f)); +} + +void pwmout_period_ms(pwmout_t* obj, int ms) +{ + pwmout_period_us(obj, (int)(ms * 1000)); +} + +void pwmout_period_us(pwmout_t* obj, int us) +{ + float dc = pwmout_read(obj); + + obj->period = us; + // Set duty cycle again + pwmout_write(obj, dc); +} + +void pwmout_pulsewidth(pwmout_t* obj, float seconds) +{ + pwmout_pulsewidth_us(obj, (int)(seconds * 1000000.0f)); +} + +void pwmout_pulsewidth_ms(pwmout_t* obj, int ms) +{ + pwmout_pulsewidth_us(obj, ms * 1000); +} + +void pwmout_pulsewidth_us(pwmout_t* obj, int us) +{ + float value = (float)us / (float)obj->period; + pwmout_write(obj, value); +} + +#endif // #ifdef CONFIG_PWM_EN +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/rtc_api.c b/component/common/mbed/targets/hal/rtl8195a/rtc_api.c new file mode 100644 index 0000000..8beee24 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/rtc_api.c @@ -0,0 +1,115 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2015, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + *******************************************************************************/ +#include "rtc_api.h" + +#if DEVICE_RTC +#include +#include "timer_api.h" // software-RTC: use a g-timer for the tick of the RTC + +#define SW_RTC_TIMER_ID TIMER5 + +static gtimer_t sw_rtc; +static struct tm rtc_timeinfo; +static int sw_rtc_en=0; + +const static u8 dim[14] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28 }; + +static inline bool is_leap_year(unsigned int year) +{ + return (!(year % 4) && (year % 100)) || !(year % 400); +} + + +static u8 days_in_month (u8 month, u8 year) +{ + u8 ret = dim [ month - 1 ]; + if (ret == 0) + ret = is_leap_year (year) ? 29 : 28; + return ret; +} + +void sw_rtc_tick_handler(uint32_t id) +{ + if(++rtc_timeinfo.tm_sec > 59) { // Increment seconds, check for overflow + rtc_timeinfo.tm_sec = 0; // Reset seconds + if(++rtc_timeinfo.tm_min > 59) { // Increment minutes, check for overflow + rtc_timeinfo.tm_min = 0; // Reset minutes + if(++rtc_timeinfo.tm_hour > 23) { // Increment hours, check for overflow + rtc_timeinfo.tm_hour = 0; // Reset hours + ++rtc_timeinfo.tm_yday; // Increment day of year + if(++rtc_timeinfo.tm_wday > 6) // Increment day of week, check for overflow + rtc_timeinfo.tm_wday = 0; // Reset day of week + // Increment day of month, check for overflow + if(++rtc_timeinfo.tm_mday > + days_in_month(rtc_timeinfo.tm_mon, rtc_timeinfo.tm_year + 1900)) { + rtc_timeinfo.tm_mday = 1; // Reset day of month + if(++rtc_timeinfo.tm_mon > 11) { // Increment month, check for overflow + rtc_timeinfo.tm_mon = 0; // Reset month + rtc_timeinfo.tm_yday = 0; // Reset day of year + ++rtc_timeinfo.tm_year; // Increment year + } // - year + } // - month + } // - day + } // - hour + } +} + +void rtc_init(void) +{ + // Initial a periodical timer + gtimer_init(&sw_rtc, SW_RTC_TIMER_ID); + // Tick every 1 sec + gtimer_start_periodical(&sw_rtc, 1000000, (void*)sw_rtc_tick_handler, (uint32_t)&sw_rtc); + sw_rtc_en = 1; +} + +void rtc_free(void) +{ + sw_rtc_en = 0; + gtimer_stop(&sw_rtc); + gtimer_deinit(&sw_rtc); +} + +int rtc_isenabled(void) +{ + return(sw_rtc_en); +} + +time_t rtc_read(void) +{ + time_t t; + + // Convert to timestamp + t = mktime(&rtc_timeinfo); + + return t; +} + +void rtc_write(time_t t) +{ + // Convert the time in to a tm + struct tm *timeinfo = localtime(&t); + + gtimer_stop(&sw_rtc); + + // Set the RTC + rtc_timeinfo.tm_sec = timeinfo->tm_sec; + rtc_timeinfo.tm_min = timeinfo->tm_min; + rtc_timeinfo.tm_hour = timeinfo->tm_hour; + rtc_timeinfo.tm_mday = timeinfo->tm_mday; + rtc_timeinfo.tm_wday = timeinfo->tm_wday; + rtc_timeinfo.tm_yday = timeinfo->tm_yday; + rtc_timeinfo.tm_mon = timeinfo->tm_mon; + rtc_timeinfo.tm_year = timeinfo->tm_year; + + gtimer_start(&sw_rtc); +} + +#endif // endof "#if DEVICE_RTC" diff --git a/component/common/mbed/targets/hal/rtl8195a/serial_api.c b/component/common/mbed/targets/hal/rtl8195a/serial_api.c new file mode 100644 index 0000000..a5c5e4d --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/serial_api.c @@ -0,0 +1,536 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + +#include "objects.h" +//#include "mbed_assert.h" +#include "serial_api.h" +#include "serial_ex_api.h" + +#if CONFIG_UART_EN + +//#include "cmsis.h" +#include "pinmap.h" +#include + +static const PinMap PinMap_UART_TX[] = { + {PC_3, RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)}, + {PE_0, RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)}, + {PA_7, RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)}, + {PD_3, RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)}, + {PE_4, RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)}, + {PB_5, RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)}, + {PA_4, RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)}, + {PC_9, RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)}, + {PD_7, RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)}, + {NC, NC, 0} +}; + +static const PinMap PinMap_UART_RX[] = { + {PC_0, RTL_PIN_PERI(UART0, 0, S0), RTL_PIN_FUNC(UART0, S0)}, + {PE_3, RTL_PIN_PERI(UART0, 0, S1), RTL_PIN_FUNC(UART0, S1)}, + {PA_6, RTL_PIN_PERI(UART0, 0, S2), RTL_PIN_FUNC(UART0, S2)}, + {PD_0, RTL_PIN_PERI(UART1, 1, S0), RTL_PIN_FUNC(UART1, S0)}, + {PE_7, RTL_PIN_PERI(UART1, 1, S1), RTL_PIN_FUNC(UART1, S1)}, + {PB_4, RTL_PIN_PERI(UART1, 1, S2), RTL_PIN_FUNC(UART1, S2)}, + {PA_0, RTL_PIN_PERI(UART2, 2, S0), RTL_PIN_FUNC(UART2, S0)}, + {PC_6, RTL_PIN_PERI(UART2, 2, S1), RTL_PIN_FUNC(UART2, S1)}, + {PD_4, RTL_PIN_PERI(UART2, 2, S2), RTL_PIN_FUNC(UART2, S2)}, + {NC, NC, 0} +}; + +#define UART_NUM (3) +#define SERIAL_TX_IRQ_EN 0x01 +#define SERIAL_RX_IRQ_EN 0x02 +#define SERIAL_TX_DMA_EN 0x01 +#define SERIAL_RX_DMA_EN 0x02 + +static uint32_t serial_irq_ids[UART_NUM] = {0, 0, 0}; + +static uart_irq_handler irq_handler[UART_NUM]; +static uint32_t serial_irq_en[UART_NUM]={0, 0, 0}; + +#ifdef CONFIG_GDMA_EN +static uint32_t serial_dma_en[UART_NUM] = {0, 0, 0}; +static HAL_GDMA_OP UartGdmaOp; +#endif + +#ifdef CONFIG_MBED_ENABLED +int stdio_uart_inited = 0; +serial_t stdio_uart; +#endif + +static void SerialTxDoneCallBack(VOID *pAdapter); +static void SerialRxDoneCallBack(VOID *pAdapter); + +void serial_init(serial_t *obj, PinName tx, PinName rx) +{ + uint32_t uart_tx, uart_rx; + uint32_t uart_sel; + uint8_t uart_idx; + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter; +#ifdef CONFIG_GDMA_EN + PUART_DMA_CONFIG pHalRuartDmaCfg; + PHAL_GDMA_OP pHalGdmaOp=&UartGdmaOp; +#endif + + // Determine the UART to use (UART0, UART1, or UART3) + uart_tx = pinmap_peripheral(tx, PinMap_UART_TX); + uart_rx = pinmap_peripheral(rx, PinMap_UART_RX); + + uart_sel = pinmap_merge(uart_tx, uart_rx); + uart_idx = RTL_GET_PERI_IDX(uart_sel); + if (unlikely(uart_idx == (uint8_t)NC)) { + DBG_UART_ERR("%s: Cannot find matched UART\n", __FUNCTION__); + return; + } + + pHalRuartOp = &(obj->hal_uart_op); + pHalRuartAdapter = &(obj->hal_uart_adp); + + if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter)) { + DBG_UART_ERR("%s: Allocate Adapter Failed\n", __FUNCTION__); + return; + } + + HalRuartOpInit((VOID*)pHalRuartOp); + +#ifdef CONFIG_GDMA_EN + HalGdmaOpInit((VOID*)pHalGdmaOp); + pHalRuartDmaCfg = &obj->uart_gdma_cfg; + pHalRuartDmaCfg->pHalGdmaOp = pHalGdmaOp; + pHalRuartDmaCfg->pTxHalGdmaAdapter = &obj->uart_gdma_adp_tx; + pHalRuartDmaCfg->pRxHalGdmaAdapter = &obj->uart_gdma_adp_rx; +#endif + + pHalRuartOp->HalRuartAdapterLoadDef(pHalRuartAdapter, uart_idx); + pHalRuartAdapter->PinmuxSelect = RTL_GET_PERI_SEL(uart_sel); + pHalRuartAdapter->BaudRate = 9600; + + // Configure the UART pins + // TODO: +// pinmap_pinout(tx, PinMap_UART_TX); +// pinmap_pinout(rx, PinMap_UART_RX); +// pin_mode(tx, PullUp); +// pin_mode(rx, PullUp); + + pHalRuartOp->HalRuartInit(pHalRuartAdapter); + pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter); + pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter); + +#ifdef CONFIG_MBED_ENABLED + // For stdio management + if (uart_idx == STDIO_UART) { + stdio_uart_inited = 1; + memcpy(&stdio_uart, obj, sizeof(serial_t)); + } +#endif +} + +void serial_free(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + PHAL_RUART_OP pHalRuartOp; +#ifdef CONFIG_GDMA_EN + u8 uart_idx; + PUART_DMA_CONFIG pHalRuartDmaCfg; + +#endif + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartOp = &(obj->hal_uart_op); + + pHalRuartOp->HalRuartDeInit(pHalRuartAdapter); + +#ifdef CONFIG_GDMA_EN + uart_idx = pHalRuartAdapter->UartIndex; + pHalRuartDmaCfg = &obj->uart_gdma_cfg; + if (serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN) { + HalRuartRxGdmaDeInit(pHalRuartDmaCfg); + serial_dma_en[uart_idx] &= ~SERIAL_RX_DMA_EN; + } + + if (serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN) { + HalRuartTxGdmaDeInit(pHalRuartDmaCfg); + serial_dma_en[uart_idx] &= ~SERIAL_TX_DMA_EN; + } +#endif + // TODO: recovery Pin Mux + +} + +void serial_baud(serial_t *obj, int baudrate) { + PHAL_RUART_ADAPTER pHalRuartAdapter; + PHAL_RUART_OP pHalRuartOp; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartOp = &(obj->hal_uart_op); + + pHalRuartAdapter->BaudRate = baudrate; + pHalRuartOp->HalRuartInit(pHalRuartAdapter); +} + +void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + PHAL_RUART_OP pHalRuartOp; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartOp = &(obj->hal_uart_op); + + if (data_bits == 8) { + pHalRuartAdapter->WordLen = RUART_WLS_8BITS; + } else { + pHalRuartAdapter->WordLen = RUART_WLS_7BITS; + } + + + switch (parity) { + case ParityOdd: + case ParityForced0: + pHalRuartAdapter->Parity = RUART_PARITY_ENABLE; + pHalRuartAdapter->ParityType = RUART_ODD_PARITY; + break; + case ParityEven: + case ParityForced1: + pHalRuartAdapter->Parity = RUART_PARITY_ENABLE; + pHalRuartAdapter->ParityType = RUART_EVEN_PARITY; + break; + default: // ParityNone + pHalRuartAdapter->Parity = RUART_PARITY_DISABLE; + break; + } + + if (stop_bits == 1) { + pHalRuartAdapter->StopBit = RUART_1_STOP_BIT; + } else { + pHalRuartAdapter->StopBit = RUART_NO_STOP_BIT; + } + + pHalRuartOp->HalRuartInit(pHalRuartAdapter); +} + +/****************************************************************************** + * INTERRUPTS HANDLING + ******************************************************************************/ + +static void SerialTxDoneCallBack(VOID *pAdapter) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter = pAdapter; + u8 uart_idx = pHalRuartAdapter->UartIndex; + + // Mask UART TX FIFI empty + pHalRuartAdapter->Interrupts &= ~RUART_IER_ETBEI; + HalRuartSetIMRRtl8195a (pHalRuartAdapter); + + if (irq_handler[uart_idx] != NULL) { + irq_handler[uart_idx](serial_irq_ids[uart_idx], TxIrq); + } +} + +static void SerialRxDoneCallBack(VOID *pAdapter) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter = pAdapter; + u8 uart_idx = pHalRuartAdapter->UartIndex; + + if (irq_handler[uart_idx] != NULL) { + irq_handler[uart_idx](serial_irq_ids[uart_idx], RxIrq); + } +} + +void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; +// PHAL_RUART_OP pHalRuartOp; + u8 uart_idx; + + pHalRuartAdapter = &(obj->hal_uart_adp); +// pHalRuartOp = &(obj->hal_uart_op); + + uart_idx = pHalRuartAdapter->UartIndex; + + irq_handler[uart_idx] = handler; + serial_irq_ids[uart_idx] = id; + + pHalRuartAdapter->TxTDCallback = SerialTxDoneCallBack; + pHalRuartAdapter->TxTDCbPara = (void*)pHalRuartAdapter; + pHalRuartAdapter->RxDRCallback = SerialRxDoneCallBack; + pHalRuartAdapter->RxDRCbPara = (void*)pHalRuartAdapter; + +// pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter); +// pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter); +} + + +void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + PHAL_RUART_OP pHalRuartOp; + u8 uart_idx; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartOp = &(obj->hal_uart_op); + uart_idx = pHalRuartAdapter->UartIndex; + + if (enable) { + if (irq == RxIrq) { + pHalRuartAdapter->Interrupts |= RUART_IER_ERBI | RUART_IER_ELSI; + serial_irq_en[uart_idx] |= SERIAL_RX_IRQ_EN; + HalRuartSetIMRRtl8195a (pHalRuartAdapter); + } + else { + serial_irq_en[uart_idx] |= SERIAL_TX_IRQ_EN; + } + pHalRuartOp->HalRuartRegIrq(pHalRuartAdapter); + pHalRuartOp->HalRuartIntEnable(pHalRuartAdapter); + } + else { // disable + if (irq == RxIrq) { + pHalRuartAdapter->Interrupts &= ~(RUART_IER_ERBI | RUART_IER_ELSI); + serial_irq_en[uart_idx] &= ~SERIAL_RX_IRQ_EN; + } + else { + pHalRuartAdapter->Interrupts &= RUART_IER_ETBEI; + serial_irq_en[uart_idx] &= ~SERIAL_TX_IRQ_EN; + } + HalRuartSetIMRRtl8195a (pHalRuartAdapter); + if (pHalRuartAdapter->Interrupts == 0) { + InterruptUnRegister(&pHalRuartAdapter->IrqHandle); + InterruptDis(&pHalRuartAdapter->IrqHandle); + } + } +} + +/****************************************************************************** + * READ/WRITE + ******************************************************************************/ + +int serial_getc(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + + while (!serial_readable(obj)); + return (int)((HAL_RUART_READ32(uart_idx, RUART_REV_BUF_REG_OFF)) & 0xFF); +} + +void serial_putc(serial_t *obj, int c) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + + while (!serial_writable(obj)); + HAL_RUART_WRITE32(uart_idx, RUART_TRAN_HOLD_REG_OFF, (c & 0xFF)); + + if (serial_irq_en[uart_idx] & SERIAL_TX_IRQ_EN) { + // UnMask TX FIFO empty IRQ + pHalRuartAdapter->Interrupts |= RUART_IER_ETBEI; + HalRuartSetIMRRtl8195a (pHalRuartAdapter); + } +} + +int serial_readable(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + + if ((HAL_RUART_READ32(uart_idx, RUART_LINE_STATUS_REG_OFF)) & RUART_LINE_STATUS_REG_DR) { + return 1; + } + else { + return 0; + } +} + +int serial_writable(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + + if (HAL_RUART_READ32(uart_idx, RUART_LINE_STATUS_REG_OFF) & + (RUART_LINE_STATUS_REG_THRE)) { + return 1; + } + else { + return 0; + } +} + +void serial_clear(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + PHAL_RUART_OP pHalRuartOp; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartOp = &(obj->hal_uart_op); + + pHalRuartOp->HalRuartResetRxFifo(pHalRuartAdapter); +} + +void serial_pinout_tx(PinName tx) +{ + pinmap_pinout(tx, PinMap_UART_TX); +} + +void serial_break_set(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + u32 RegValue; + + RegValue = HAL_RUART_READ32(uart_idx, RUART_LINE_CTL_REG_OFF); + RegValue |= BIT_UART_LCR_BREAK_CTRL; + HAL_RUART_WRITE32(uart_idx, RUART_LINE_CTL_REG_OFF, RegValue); +} + +void serial_break_clear(serial_t *obj) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + u32 RegValue; + + RegValue = HAL_RUART_READ32(uart_idx, RUART_LINE_CTL_REG_OFF); + RegValue &= ~(BIT_UART_LCR_BREAK_CTRL); + HAL_RUART_WRITE32(uart_idx, RUART_LINE_CTL_REG_OFF, RegValue); +} + +void serial_send_comp_handler(serial_t *obj, void *handler, uint32_t id) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartAdapter->TxCompCallback = (void(*)(void*))handler; + pHalRuartAdapter->TxCompCbPara = (void*)id; +} + +void serial_recv_comp_handler(serial_t *obj, void *handler, uint32_t id) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter; + + pHalRuartAdapter = &(obj->hal_uart_adp); + pHalRuartAdapter->RxCompCallback = (void(*)(void*))handler; + pHalRuartAdapter->RxCompCbPara = (void*)id; +} + +int32_t serial_recv_stream (serial_t *obj, char *prxbuf, uint32_t len) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + int ret; + + pHalRuartOp = &(obj->hal_uart_op); + ret = pHalRuartOp->HalRuartIntRecv(pHalRuartAdapter, (u8*)prxbuf, len); + return (ret); +} + +int32_t serial_send_stream (serial_t *obj, char *ptxbuf, uint32_t len) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + int ret; + + pHalRuartOp = &(obj->hal_uart_op); + ret = pHalRuartOp->HalRuartIntSend(pHalRuartAdapter, (u8*)ptxbuf, len); + return (ret); +} + +#ifdef CONFIG_GDMA_EN + +int32_t serial_recv_stream_dma (serial_t *obj, char *prxbuf, uint32_t len) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + int32_t ret; + + pHalRuartOp = &(obj->hal_uart_op); + if ((serial_dma_en[uart_idx] & SERIAL_RX_DMA_EN)==0) { + PUART_DMA_CONFIG pHalRuartDmaCfg; + + pHalRuartDmaCfg = &obj->uart_gdma_cfg; +#if 0 + pHalRuartOp->HalRuartRxGdmaLoadDef (pHalRuartAdapter, pHalRuartDmaCfg); + pHalRuartOp->HalRuartDmaInit (pHalRuartAdapter); + InterruptRegister(&pHalRuartDmaCfg->RxGdmaIrqHandle); + InterruptEn(&pHalRuartDmaCfg->RxGdmaIrqHandle); + serial_dma_en[uart_idx] |= SERIAL_RX_DMA_EN; +#else + if (HAL_OK == HalRuartRxGdmaInit(pHalRuartOp, pHalRuartAdapter, pHalRuartDmaCfg)) { + serial_dma_en[uart_idx] |= SERIAL_RX_DMA_EN; + } + else { + return HAL_BUSY; + } +#endif + } + ret = pHalRuartOp->HalRuartDmaRecv(pHalRuartAdapter, (u8*)prxbuf, len); + return (ret); +} + +int32_t serial_send_stream_dma (serial_t *obj, char *ptxbuf, uint32_t len) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + u8 uart_idx = pHalRuartAdapter->UartIndex; + int32_t ret; + + pHalRuartOp = &(obj->hal_uart_op); + + if ((serial_dma_en[uart_idx] & SERIAL_TX_DMA_EN)==0) { + PUART_DMA_CONFIG pHalRuartDmaCfg; + + pHalRuartDmaCfg = &obj->uart_gdma_cfg; +#if 0 + pHalRuartOp->HalRuartTxGdmaLoadDef (pHalRuartAdapter, pHalRuartDmaCfg); + pHalRuartOp->HalRuartDmaInit (pHalRuartAdapter); + InterruptRegister(&pHalRuartDmaCfg->TxGdmaIrqHandle); + InterruptEn(&pHalRuartDmaCfg->TxGdmaIrqHandle); + serial_dma_en[uart_idx] |= SERIAL_TX_DMA_EN; +#else + if (HAL_OK == HalRuartTxGdmaInit(pHalRuartOp, pHalRuartAdapter, pHalRuartDmaCfg)) { + serial_dma_en[uart_idx] |= SERIAL_TX_DMA_EN; + } + else { + return HAL_BUSY; + } +#endif + } + ret = pHalRuartOp->HalRuartDmaSend(pHalRuartAdapter, (u8*)ptxbuf, len); + return (ret); +} +#endif // end of "#ifdef CONFIG_GDMA_EN" + +int32_t serial_send_stream_abort (serial_t *obj) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + int ret; + + pHalRuartOp = &(obj->hal_uart_op); + + ret = pHalRuartOp->HalRuartStopSend((VOID*)pHalRuartAdapter); + HalRuartResetTxFifo((VOID*)pHalRuartAdapter); + return (ret); +} + +int32_t serial_recv_stream_abort (serial_t *obj) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter=(PHAL_RUART_ADAPTER)&(obj->hal_uart_adp); + int ret; + + pHalRuartOp = &(obj->hal_uart_op); + + ret = pHalRuartOp->HalRuartStopRecv((VOID*)pHalRuartAdapter); + ret = pHalRuartOp->HalRuartResetRxFifo((VOID*)pHalRuartAdapter); + return (ret); +} + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/sleep.c b/component/common/mbed/targets/hal/rtl8195a/sleep.c new file mode 100644 index 0000000..5a47673 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/sleep.c @@ -0,0 +1,245 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, STMicroelectronics + * 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 STMicroelectronics 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 HOLDER 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 "sleep_ex_api.h" +#include "cmsis.h" + +extern VOID SleepCG(u8 Option, u32 SDuration); +extern VOID DeepStandby(u8 Option, u32 SDuration, u8 GpioOption); +extern VOID DeepSleep(u8 Option, u32 SDuration); + +SLEEP_WAKEUP_EVENT DStandbyWakeupEvent={0}; + +/** + * @brief To make the system entering the Clock Gated power saving. + * This function just make the system to enter the clock gated + * power saving mode and pending on wake up event waitting. + * The user application need to configure the peripheral to + * generate system wake up event, like GPIO interrupt + * , G-Timer timeout, etc. befor entering power saving mode. + * + * @param wakeup_event: A bit map of wake up event. Available event: + * SLEEP_WAKEUP_BY_STIMER + * SLEEP_WAKEUP_BY_GTIMER + * SLEEP_WAKEUP_BY_GPIO_INT + * SLEEP_WAKEUP_BY_WLAN + * SLEEP_WAKEUP_BY_NFC + * SLEEP_WAKEUP_BY_SDIO + * SLEEP_WAKEUP_BY_USB + * sleep_duration: the system sleep duration in ms, only valid + * for SLEEP_WAKEUP_BY_STIMER wake up event. + * + * @retval None + */ +void sleep_ex(uint32_t wakeup_event, uint32_t sleep_duration) +{ + u8 wake_ev=0; + + wake_ev = wakeup_event & 0xff; + + if (sleep_duration == 0) { + wake_ev &= ~SLP_STIMER; + } + + if (wake_ev == 0) { + // error: No wakeup event, skip the entering sleep mode + return; + } + SleepCG(wake_ev, sleep_duration); +} + + +/** + * @brief To add a wake up event to wake up the system from the + * deep standby power saving mode. + * + * @param wakeup_event: A bit map of wake up event. Available event: + * STANDBY_WAKEUP_BY_STIMER + * STANDBY_WAKEUP_BY_NFC + * STANDBY_WAKEUP_BY_PA5 (GPIO) + * STANDBY_WAKEUP_BY_PC7 (GPIO) + * STANDBY_WAKEUP_BY_PD5 (GPIO) + * STANDBY_WAKEUP_BY_PE3 (GPIO) + * sleep_duration_ms: the system sleep duration in ms, only valid + * for STANDBY_WAKEUP_BY_STIMER wake up event. + * gpio_active: for a GPIO pin to wake up the system by + * goes high(1) or low(0) + * + * @retval None + */ +void standby_wakeup_event_add(uint32_t wakeup_event, uint32_t sleep_duration_ms, uint32_t gpio_active) +{ + u32 i; + u8 gpio_event; + u8 gpio_en; + u8 gpio_act; + + if (wakeup_event & STANDBY_WAKEUP_BY_STIMER) { + DStandbyWakeupEvent.wakeup_event |= DSTBY_STIMER; + DStandbyWakeupEvent.timer_duration = sleep_duration_ms; + } + +#if 0 + if (wakeup_event & STANDBY_WAKEUP_BY_DS_TIMER) { + DStandbyWakeupEvent.wakeup_event |= DSTBY_TIMER33; + // TODO: Sleep Duration ? + } +#endif + + if (wakeup_event & STANDBY_WAKEUP_BY_NFC) { + DStandbyWakeupEvent.wakeup_event |= DSTBY_NFC; + } + + gpio_event = STANDBY_WAKEUP_BY_PA5; + gpio_en = BIT0; + gpio_act = BIT4; + // Loop 4 to check 4 GPIO wake up event + for (i=0;i<4;i++) { + if (wakeup_event & gpio_event) { + DStandbyWakeupEvent.wakeup_event |= DSTBY_GPIO; + DStandbyWakeupEvent.gpio_option |= gpio_en; + if (gpio_active) { + // Active High + DStandbyWakeupEvent.gpio_option |= gpio_act; + } + else { + // Active Low + DStandbyWakeupEvent.gpio_option &= ~gpio_act; + } + } + gpio_event = gpio_event << 1; + gpio_en = gpio_en << 1; + gpio_act = gpio_act << 1; + } +} + +/** + * @brief To delete a wake up event for wakeing up the system from the + * deep standby power saving mode. + * + * @param wakeup_event: A bit map of wake up event. Available event: + * STANDBY_WAKEUP_BY_STIMER + * STANDBY_WAKEUP_BY_NFC + * STANDBY_WAKEUP_BY_PA5 (GPIO) + * STANDBY_WAKEUP_BY_PC7 (GPIO) + * STANDBY_WAKEUP_BY_PD5 (GPIO) + * STANDBY_WAKEUP_BY_PE3 (GPIO) + * @retval None + */ +void standby_wakeup_event_del(uint32_t wakeup_event) +{ + if (wakeup_event & STANDBY_WAKEUP_BY_STIMER) { + DStandbyWakeupEvent.wakeup_event &= ~DSTBY_STIMER; + } + +#if 0 + if (wakeup_event & STANDBY_WAKEUP_BY_DS_TIMER) { + DStandbyWakeupEvent.wakeup_event &= ~DSTBY_TIMER33; + } +#endif + + if (wakeup_event & STANDBY_WAKEUP_BY_NFC) { + DStandbyWakeupEvent.wakeup_event &= ~DSTBY_NFC; + } + + if (wakeup_event & STANDBY_WAKEUP_BY_PA5) { + DStandbyWakeupEvent.gpio_option &= ~BIT0; + } + + if (wakeup_event & STANDBY_WAKEUP_BY_PC7) { + DStandbyWakeupEvent.gpio_option &= ~BIT1; + } + + if (wakeup_event & STANDBY_WAKEUP_BY_PD5) { + DStandbyWakeupEvent.gpio_option &= ~BIT2; + } + + if (wakeup_event & STANDBY_WAKEUP_BY_PE3) { + DStandbyWakeupEvent.gpio_option &= ~BIT3; + } + + if ((DStandbyWakeupEvent.gpio_option & 0x0f) == 0) { + // All GPIO wake up pin are disabled + DStandbyWakeupEvent.wakeup_event &= ~DSTBY_GPIO; + } +} + +/** + * @brief To make the system entering the Deep Standby power saving. + * The CPU, memory and part fo peripheral power is off when + * entering deep standby power saving mode. The program needs + * to be reload from the flash at system resume. + * + * @retval None + */ +void deepstandby_ex(void) +{ + if ((DStandbyWakeupEvent.wakeup_event & (DSTBY_STIMER|DSTBY_NFC|DSTBY_GPIO)) == 0) { + // error: no wakeup event was added, so skip the entering standby power saving + return; + } + + DeepStandby(DStandbyWakeupEvent.wakeup_event, + DStandbyWakeupEvent.timer_duration, DStandbyWakeupEvent.gpio_option); +} + +/** + * @brief To make the system entering the Deep Sleep power saving mode. + * The CPU, memory and peripheral power is off when entering + * deep sleep power saving mode. The program needs to be reload + * and all peripheral needs be re-configure when system resume. + * + * @param wakeup_event: A bit map of wake up event. Available event: + * DSLEEP_WAKEUP_BY_TIMER + * DSLEEP_WAKEUP_BY_GPIO + * sleep_duration: the system sleep duration in ms, only valid + * for DSLEEP_WAKEUP_BY_TIMER wake up event. + * + * @retval None + */ +void deepsleep_ex(uint32_t wakeup_event, uint32_t sleep_duration) +{ + u8 wake_ev=0; + + if ((wakeup_event & DSLEEP_WAKEUP_BY_TIMER) && (sleep_duration > 0)) { + // wake up by timeout or GPIO pin goes high + wake_ev |= DS_TIMER33; + } + + if (wakeup_event & DSLEEP_WAKEUP_BY_GPIO) { + // wake up by GPIO pin goes high + wake_ev |= DS_GPIO; + } + + if (wake_ev == 0) { + // error: No wake up event, skip entering deep sleep mode + return; + } + DeepSleep (wake_ev, sleep_duration); +} diff --git a/component/common/mbed/targets/hal/rtl8195a/spi_api.c b/component/common/mbed/targets/hal/rtl8195a/spi_api.c new file mode 100644 index 0000000..2573232 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/spi_api.c @@ -0,0 +1,681 @@ + +#include "objects.h" +#include "spi_api.h" +#include "spi_ex_api.h" +#include "PinNames.h" +#include "pinmap.h" +#include "hal_ssi.h" + +extern u32 SystemGetCpuClk(VOID); +extern VOID HAL_GPIO_PullCtrl(u32 pin, u32 mode); + +void spi_tx_done_callback(VOID *obj); +void spi_rx_done_callback(VOID *obj); + +#ifdef CONFIG_GDMA_EN +HAL_GDMA_OP SpiGdmaOp; +#endif + +uint8_t SPI0_IS_AS_SLAVE = 0; + +//TODO: Load default Setting: It should be loaded from external setting file. +extern const DW_SSI_DEFAULT_SETTING SpiDefaultSetting; + +static const PinMap PinMap_SSI_MOSI[] = { + {PE_2, RTL_PIN_PERI(SPI0, 0, S0), RTL_PIN_FUNC(SPI0, S0)}, + {PC_2, RTL_PIN_PERI(SPI0, 0, S1), RTL_PIN_FUNC(SPI0, S1)}, + {PA_1, RTL_PIN_PERI(SPI1, 1, S0), RTL_PIN_FUNC(SPI1, S0)}, + {PB_6, RTL_PIN_PERI(SPI1, 1, S1), RTL_PIN_FUNC(SPI1, S1)}, + {PD_6, RTL_PIN_PERI(SPI1, 1, S2), RTL_PIN_FUNC(SPI1, S2)}, + {PG_2, RTL_PIN_PERI(SPI2, 2, S0), RTL_PIN_FUNC(SPI2, S0)}, + {PE_6, RTL_PIN_PERI(SPI2, 2, S1), RTL_PIN_FUNC(SPI2, S1)}, + {PD_2, RTL_PIN_PERI(SPI2, 2, S2), RTL_PIN_FUNC(SPI2, S2)}, + {NC, NC, 0} +}; + +static const PinMap PinMap_SSI_MISO[] = { + {PE_3, RTL_PIN_PERI(SPI0, 0, S0), RTL_PIN_FUNC(SPI0, S0)}, + {PC_3, RTL_PIN_PERI(SPI0, 0, S1), RTL_PIN_FUNC(SPI0, S1)}, + {PA_0, RTL_PIN_PERI(SPI1, 1, S0), RTL_PIN_FUNC(SPI1, S0)}, + {PB_7, RTL_PIN_PERI(SPI1, 1, S1), RTL_PIN_FUNC(SPI1, S1)}, + {PD_7, RTL_PIN_PERI(SPI1, 1, S2), RTL_PIN_FUNC(SPI1, S2)}, + {PG_3, RTL_PIN_PERI(SPI2, 2, S0), RTL_PIN_FUNC(SPI2, S0)}, + {PE_7, RTL_PIN_PERI(SPI2, 2, S1), RTL_PIN_FUNC(SPI2, S1)}, + {PD_3, RTL_PIN_PERI(SPI2, 2, S2), RTL_PIN_FUNC(SPI2, S2)}, + {NC, NC, 0} +}; + + +void spi_init (spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) +{ + SSI_DBG_ENTRANCE("spi_init()\n"); + + uint32_t ssi_mosi, ssi_miso, ssi_peri; + uint8_t ssi_idx, ssi_pinmux; + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + obj->state = 0; + uint32_t SystemClock = SystemGetCpuClk(); + uint32_t MaxSsiFreq = (SystemClock >> 2) >> 1; + + /* SsiClockDivider doesn't support odd number */ + + DBG_SSI_INFO("SystemClock: %d\n", SystemClock); + DBG_SSI_INFO("MaxSsiFreq : %d\n", MaxSsiFreq); + + ssi_mosi = pinmap_peripheral(mosi, PinMap_SSI_MOSI); + ssi_miso = pinmap_peripheral(miso, PinMap_SSI_MISO); + //DBG_SSI_INFO("ssi_mosi: %d, ssi_miso: %d\n", ssi_mosi, ssi_miso); + + ssi_peri = pinmap_merge(ssi_mosi, ssi_miso); + if (unlikely(ssi_peri == NC)) { + DBG_SSI_ERR("spi_init(): Cannot find matched SSI index.\n"); + return; + } + obj->sclk = (u8)sclk; + ssi_idx = RTL_GET_PERI_IDX(ssi_peri); + ssi_pinmux = RTL_GET_PERI_SEL(ssi_peri); + DBG_SSI_INFO("ssi_peri: %d, ssi_idx: %d, ssi_pinmux: %d\n", ssi_peri, ssi_idx, ssi_pinmux); + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + pHalSsiAdaptor->Index = ssi_idx; + pHalSsiAdaptor->PinmuxSelect = ssi_pinmux; + +#if 0 + // XXX: Only for test + if ((ssi_idx == 0) && (SPI0_IS_AS_SLAVE == 1)) { + //DBG_SSI_INFO("SSI%d will be as slave. (spi0_is_slave: %d)\n", index, spi0_is_slave); + pHalSsiAdaptor->Role = SSI_SLAVE; + } + else +#endif + { + //DBG_SSI_INFO("SSI%d will be as master. (spi0_is_slave: %d)\n", index, spi0_is_slave); + pHalSsiAdaptor->Role = SSI_MASTER; + } + + HalSsiOpInit((VOID*)pHalSsiOp); + + pHalSsiOp->HalSsiSetDeviceRole(pHalSsiAdaptor, pHalSsiAdaptor->Role); + + /* Pinmux workaround */ + if ((ssi_idx == 0) && (ssi_pinmux == SSI0_MUX_TO_GPIOC)) { + EEPROM_PIN_CTRL(OFF); + } + + if ((ssi_idx == 0) && (ssi_pinmux == SSI0_MUX_TO_GPIOE)) { + DBG_SSI_WARN(ANSI_COLOR_MAGENTA"SPI0 Pin may conflict with JTAG\r\n"ANSI_COLOR_RESET); + } + + + pHalSsiOp->HalSsiPinmuxEnable(pHalSsiAdaptor); + + + //TODO: Implement default setting structure. + pHalSsiOp->HalSsiLoadSetting(pHalSsiAdaptor, (void*)&SpiDefaultSetting); + pHalSsiAdaptor->DefaultRxThresholdLevel = SpiDefaultSetting.RxThresholdLevel; + + pHalSsiOp->HalSsiInit(pHalSsiAdaptor); + + pHalSsiAdaptor->TxCompCallback = spi_tx_done_callback; + pHalSsiAdaptor->TxCompCbPara = (void*)obj; + pHalSsiAdaptor->RxCompCallback = spi_rx_done_callback; + pHalSsiAdaptor->RxCompCbPara = (void*)obj; + +#ifdef CONFIG_GDMA_EN + HalGdmaOpInit((VOID*)&SpiGdmaOp); + pHalSsiAdaptor->DmaConfig.pHalGdmaOp = &SpiGdmaOp; + pHalSsiAdaptor->DmaConfig.pRxHalGdmaAdapter = &obj->spi_gdma_adp_rx; + pHalSsiAdaptor->DmaConfig.pTxHalGdmaAdapter = &obj->spi_gdma_adp_tx; + obj->dma_en = 0; +#endif +} + +void spi_free (spi_t *obj) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + pHalSsiOp->HalSsiInterruptDisable(pHalSsiAdaptor); + pHalSsiOp->HalSsiDisable(pHalSsiAdaptor); + pHalSsiOp->HalSsiPinmuxDisable(pHalSsiAdaptor); + + SPI0_MULTI_CS_CTRL(OFF); + +#ifdef CONFIG_GDMA_EN + if (obj->dma_en & SPI_DMA_RX_EN) { + HalSsiRxGdmaDeInit(pHalSsiAdaptor); + } + + if (obj->dma_en & SPI_DMA_TX_EN) { + HalSsiTxGdmaDeInit(pHalSsiAdaptor); + } + obj->dma_en = 0; +#endif +} + +void spi_format (spi_t *obj, int bits, int mode, int slave) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + pHalSsiAdaptor->DataFrameSize = (bits - 1); + + /* + * mode | POL PHA + * -----+-------- + * 0 | 0 0 + * 1 | 0 1 + * 2 | 1 0 + * 3 | 1 1 + * + * SCPOL_INACTIVE_IS_LOW = 0, + * SCPOL_INACTIVE_IS_HIGH = 1 + * + * SCPH_TOGGLES_IN_MIDDLE = 0, + * SCPH_TOGGLES_AT_START = 1 + */ + switch (mode) + { + case 0: + pHalSsiAdaptor->SclkPolarity = SCPOL_INACTIVE_IS_LOW; + pHalSsiAdaptor->SclkPhase = SCPH_TOGGLES_IN_MIDDLE; + break; + case 1: + pHalSsiAdaptor->SclkPolarity = SCPOL_INACTIVE_IS_LOW; + pHalSsiAdaptor->SclkPhase = SCPH_TOGGLES_AT_START; + break; + case 2: + pHalSsiAdaptor->SclkPolarity = SCPOL_INACTIVE_IS_HIGH; + pHalSsiAdaptor->SclkPhase = SCPH_TOGGLES_IN_MIDDLE; + break; + case 3: + pHalSsiAdaptor->SclkPolarity = SCPOL_INACTIVE_IS_HIGH; + pHalSsiAdaptor->SclkPhase = SCPH_TOGGLES_AT_START; + break; + default: // same as 3 + pHalSsiAdaptor->SclkPolarity = SCPOL_INACTIVE_IS_HIGH; + pHalSsiAdaptor->SclkPhase = SCPH_TOGGLES_AT_START; + break; + } + + if (slave == 1) { + if (pHalSsiAdaptor->Index == 0) { + pHalSsiAdaptor->Role = SSI_SLAVE; + pHalSsiAdaptor->SlaveOutputEnable = SLV_TXD_ENABLE; // <-- Slave only + SPI0_IS_AS_SLAVE = 1; + DBG_SSI_INFO("SPI0 is as slave\n"); + } + else { + DBG_SSI_ERR("The SPI%d cannot work as Slave mode, only SPI0 does.\r\n", pHalSsiAdaptor->Index); + pHalSsiAdaptor->Role = SSI_MASTER; + } + } + else { + pHalSsiAdaptor->Role = SSI_MASTER; + } + pHalSsiOp->HalSsiSetDeviceRole(pHalSsiAdaptor, pHalSsiAdaptor->Role); + +#ifdef CONFIG_GPIO_EN + if (pHalSsiAdaptor->Role == SSI_SLAVE) { + if (pHalSsiAdaptor->SclkPolarity == SCPOL_INACTIVE_IS_LOW) { + HAL_GPIO_PullCtrl((u32)obj->sclk, hal_PullDown); + } + else { + HAL_GPIO_PullCtrl((u32)obj->sclk, hal_PullUp); + } + } +#endif + pHalSsiOp->HalSsiInit(pHalSsiAdaptor); +} + +void spi_frequency (spi_t *obj, int hz) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + + pHalSsiAdaptor = &obj->spi_adp; + HalSsiSetSclk(pHalSsiAdaptor, (u32)hz); +} + +void spi_slave_select(spi_t *obj, ChipSelect slaveindex) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + u8 Index; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + Index = pHalSsiAdaptor->Index; + + if((pHalSsiAdaptor->Role == SSI_MASTER) && (Index == 0)){ + pHalSsiOp->HalSsiSetSlaveEnableRegister((VOID*)pHalSsiAdaptor,slaveindex); + if(slaveindex != CS_0){ + SPI0_MULTI_CS_CTRL(ON); + } + } + else{ + DBG_SSI_ERR("Only SPI 0 master mode supports slave selection.\n"); + } +} + +static inline void ssi_write (spi_t *obj, int value) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + while (!pHalSsiOp->HalSsiWriteable(pHalSsiAdaptor)); + pHalSsiOp->HalSsiWrite((VOID*)pHalSsiAdaptor, value); +} + +static inline int ssi_read(spi_t *obj) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + while (!pHalSsiOp->HalSsiReadable(pHalSsiAdaptor)); + return (int)pHalSsiOp->HalSsiRead(pHalSsiAdaptor); +} + +int spi_master_write (spi_t *obj, int value) +{ + ssi_write(obj, value); + return ssi_read(obj); +} + +int spi_slave_receive (spi_t *obj) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int Readable; + int Busy; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + Readable = pHalSsiOp->HalSsiReadable(pHalSsiAdaptor); + Busy = (int)pHalSsiOp->HalSsiBusy(pHalSsiAdaptor); + return ((Readable && !Busy) ? 1 : 0); +} + +int spi_slave_read (spi_t *obj) +{ + return ssi_read(obj); +} + +void spi_slave_write (spi_t *obj, int value) +{ + ssi_write(obj, value); +} + +int spi_busy (spi_t *obj) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + return (int)pHalSsiOp->HalSsiBusy(pHalSsiAdaptor); +} + +void spi_flush_rx_fifo (spi_t *obj) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + u32 rx_fifo_level; + u32 i; + u16 rx_dummy; + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + while(pHalSsiOp->HalSsiReadable(pHalSsiAdaptor)){ + rx_fifo_level = pHalSsiOp->HalSsiGetRxFifoLevel(pHalSsiAdaptor); + for(i=0;iHalSsiRead(pHalSsiAdaptor); + } + } +} + +// Slave mode read a sequence of data by interrupt mode +int32_t spi_slave_read_stream(spi_t *obj, char *rx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_RX_BUSY) { + DBG_SSI_WARN("spi_slave_read_stream: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + //DBG_SSI_INFO("rx_buffer addr: %X, length: %d\n", rx_buffer, length); + obj->state |= SPI_STATE_RX_BUSY; + if ((ret=pHalSsiOp->HalSsiReadInterrupt(pHalSsiAdaptor, rx_buffer, length)) != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + + return ret; +} + +// Slave mode write a sequence of data by interrupt mode +int32_t spi_slave_write_stream(spi_t *obj, char *tx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_TX_BUSY) { + DBG_SSI_WARN("spi_slave_write_stream: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + obj->state |= SPI_STATE_TX_BUSY; + if ((ret=pHalSsiOp->HalSsiWriteInterrupt(pHalSsiAdaptor, tx_buffer, length)) != HAL_OK) { + obj->state &= ~SPI_STATE_TX_BUSY; + } + return ret; +} + +// Master mode read a sequence of data by interrupt mode +// The length unit is byte, for both 16-bits and 8-bits mode +int32_t spi_master_read_stream(spi_t *obj, char *rx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_RX_BUSY) { + DBG_SSI_WARN("spi_master_read_stream: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + // wait bus idle + while(pHalSsiOp->HalSsiBusy(pHalSsiAdaptor)); + + obj->state |= SPI_STATE_RX_BUSY; + if ((ret=pHalSsiOp->HalSsiReadInterrupt(pHalSsiAdaptor, rx_buffer, length)) == HAL_OK) { + /* as Master mode, it need to push data to TX FIFO to generate clock out + then the slave can transmit data out */ + // send some dummy data out + if ((ret=pHalSsiOp->HalSsiWriteInterrupt(pHalSsiAdaptor, NULL, length)) != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + } + else { + obj->state &= ~SPI_STATE_RX_BUSY; + } + + return ret; +} + +// Master mode write a sequence of data by interrupt mode +// The length unit is byte, for both 16-bits and 8-bits mode +int32_t spi_master_write_stream(spi_t *obj, char *tx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_TX_BUSY) { + DBG_SSI_WARN("spi_master_write_stream: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + obj->state |= SPI_STATE_TX_BUSY; + /* as Master mode, sending data will receive data at sametime, so we need to + drop those received dummy data */ + if ((ret=pHalSsiOp->HalSsiWriteInterrupt(pHalSsiAdaptor, tx_buffer, length)) != HAL_OK) { + obj->state &= ~SPI_STATE_TX_BUSY; + } + return ret; +} + +// Master mode write a sequence of data by interrupt mode +// The length unit is byte, for both 16-bits and 8-bits mode +int32_t spi_master_write_read_stream(spi_t *obj, char *tx_buffer, + char *rx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & (SPI_STATE_RX_BUSY|SPI_STATE_TX_BUSY)) { + DBG_SSI_WARN("spi_master_write_and_read_stream: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + // wait bus idle + while(pHalSsiOp->HalSsiBusy(pHalSsiAdaptor)); + + obj->state |= SPI_STATE_RX_BUSY; + /* as Master mode, sending data will receive data at sametime */ + if ((ret=pHalSsiOp->HalSsiReadInterrupt(pHalSsiAdaptor, rx_buffer, length)) == HAL_OK) { + obj->state |= SPI_STATE_TX_BUSY; + if ((ret=pHalSsiOp->HalSsiWriteInterrupt(pHalSsiAdaptor, tx_buffer, length)) != HAL_OK) { + obj->state &= ~(SPI_STATE_RX_BUSY|SPI_STATE_TX_BUSY); + // Disable RX IRQ + pHalSsiAdaptor->InterruptMask &= ~(BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM); + pHalSsiOp->HalSsiSetInterruptMask((VOID*)pHalSsiAdaptor); + } + } + else { + obj->state &= ~(SPI_STATE_RX_BUSY); + } + + return ret; +} + +void spi_tx_done_callback(VOID *obj) +{ + spi_t *spi_obj = (spi_t *)obj; + spi_irq_handler handler; + + if (spi_obj->state & SPI_STATE_TX_BUSY) { + spi_obj->state &= ~SPI_STATE_TX_BUSY; + if (spi_obj->irq_handler) { + handler = (spi_irq_handler)spi_obj->irq_handler; + handler(spi_obj->irq_id, SpiTxIrq); + } + } +} + +void spi_rx_done_callback(VOID *obj) +{ + spi_t *spi_obj = (spi_t *)obj; + spi_irq_handler handler; + + spi_obj->state &= ~SPI_STATE_RX_BUSY; + if (spi_obj->irq_handler) { + handler = (spi_irq_handler)spi_obj->irq_handler; + handler(spi_obj->irq_id, SpiRxIrq); + } +} + +void spi_irq_hook(spi_t *obj, spi_irq_handler handler, uint32_t id) +{ + obj->irq_handler = (u32)handler; + obj->irq_id = (u32)id; +} + +#ifdef CONFIG_GDMA_EN +int32_t spi_slave_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_RX_BUSY) { + DBG_SSI_WARN("spi_slave_read_stream_dma: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + if ((obj->dma_en & SPI_DMA_RX_EN)==0) { + if (HAL_OK == HalSsiRxGdmaInit(pHalSsiOp, pHalSsiAdaptor)) { + obj->dma_en |= SPI_DMA_RX_EN; + } + else { + return HAL_BUSY; + } + } + + obj->state |= SPI_STATE_RX_BUSY; + ret = HalSsiDmaRecv(pHalSsiAdaptor, rx_buffer, length); + if (ret != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + return (ret); +} + +int32_t spi_slave_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_TX_BUSY) { + DBG_SSI_WARN("spi_slave_write_stream_dma: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + if ((obj->dma_en & SPI_DMA_TX_EN)==0) { + if (HAL_OK == HalSsiTxGdmaInit(pHalSsiOp, pHalSsiAdaptor)) { + obj->dma_en |= SPI_DMA_TX_EN; + } + else { + return HAL_BUSY; + } + } + obj->state |= SPI_STATE_TX_BUSY; + ret = HalSsiDmaSend(pHalSsiAdaptor, tx_buffer, length); + if (ret != HAL_OK) { + obj->state &= ~SPI_STATE_TX_BUSY; + } + return (ret); +} + +int32_t spi_master_read_stream_dma(spi_t *obj, char *rx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_RX_BUSY) { + DBG_SSI_WARN("spi_master_read_stream_dma: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + if ((obj->dma_en & SPI_DMA_RX_EN)==0) { + if (HAL_OK == HalSsiRxGdmaInit(pHalSsiOp, pHalSsiAdaptor)) { + obj->dma_en |= SPI_DMA_RX_EN; + } + else { + return HAL_BUSY; + } + } + + obj->state |= SPI_STATE_RX_BUSY; + ret = HalSsiDmaRecv(pHalSsiAdaptor, rx_buffer, length); + if (ret != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + + // for master mode, we need to send data to generate clock out + if (obj->dma_en & SPI_DMA_TX_EN) { + // TX DMA is on already, so use DMA to TX data + // Make the GDMA to use the rx_buffer too + ret = HalSsiDmaSend(pHalSsiAdaptor, rx_buffer, length); + if (ret != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + } + else { + // TX DMA isn't enabled, so we just use Interrupt mode to TX dummy data + if ((ret=pHalSsiOp->HalSsiWriteInterrupt(pHalSsiAdaptor, NULL, length)) != HAL_OK) { + obj->state &= ~SPI_STATE_RX_BUSY; + } + } + + return ret; +} + +int32_t spi_master_write_stream_dma(spi_t *obj, char *tx_buffer, uint32_t length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdaptor; + PHAL_SSI_OP pHalSsiOp; + int32_t ret; + + if (obj->state & SPI_STATE_TX_BUSY) { + DBG_SSI_WARN("spi_master_write_stream_dma: state(0x%x) is not ready\r\n", + obj->state); + return HAL_BUSY; + } + + pHalSsiAdaptor = &obj->spi_adp; + pHalSsiOp = &obj->spi_op; + + if ((obj->dma_en & SPI_DMA_TX_EN)==0) { + if (HAL_OK == HalSsiTxGdmaInit(pHalSsiOp, pHalSsiAdaptor)) { + obj->dma_en |= SPI_DMA_TX_EN; + } + else { + return HAL_BUSY; + } + } + + obj->state |= SPI_STATE_TX_BUSY; + ret = HalSsiDmaSend(pHalSsiAdaptor, tx_buffer, length); + if (ret != HAL_OK) { + obj->state &= ~SPI_STATE_TX_BUSY; + } + + return ret; +} + +#endif // end of "#ifdef CONFIG_GDMA_EN" diff --git a/component/common/mbed/targets/hal/rtl8195a/sys_api.c b/component/common/mbed/targets/hal/rtl8195a/sys_api.c new file mode 100644 index 0000000..d01fa9a --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/sys_api.c @@ -0,0 +1,180 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek + * All rights reserved. + * + * 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 HOLDER 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 "cmsis.h" +#include "sys_api.h" +#include "flash_api.h" + +#define OTA_Signature "81958711" +#define OTA_Clear "00000000" +#define OTA_Signature_len 8 +#define OTA_Signature_offset 8 +#define OTA_valid_offset 0x100000 +#define printf DiagPrintf + +extern VOID HalJtagPinOff(VOID); + +extern void HalInitLogUart(void); +extern void HalDeinitLogUart(void); + +/** + * @brief Turn off the JTAG function + * + * @return None + * + */ +void sys_jtag_off(void) +{ + HalJtagPinOff(); +} + +void sys_clear_ota_signature(void) +{ + flash_t flash; + u32 ota_offset = 0xFFFFFFFF, part1_offset, part2_offset; + u8 signature[OTA_Signature_len+1]; + + flash_stream_read(&flash, 0x18, 4, (u8*)&part1_offset); + part1_offset = (part1_offset&0xFFFF) * 1024; + flash_stream_read(&flash, part1_offset+OTA_Signature_offset, OTA_Signature_len, signature); + if(!memcmp((char const*)signature, OTA_Signature, OTA_Signature_len)){ + ota_offset = part1_offset; + } + + flash_stream_read(&flash, FLASH_SYSTEM_DATA_ADDR, 4, (u8*)&part2_offset); + flash_stream_read(&flash, part2_offset+OTA_Signature_offset, OTA_Signature_len, signature); + if(!memcmp((char const*)signature, OTA_Signature, OTA_Signature_len)){ + ota_offset = part2_offset; + } + + printf("\n\rOTA offset = 0x%08X", ota_offset); + + if(ota_offset < OTA_valid_offset){ + flash_stream_read(&flash, ota_offset+OTA_Signature_offset, OTA_Signature_len, signature); + signature[OTA_Signature_len] = '\0'; + printf("\n\rSignature = %s", signature); + if(!memcmp((char const*)signature, OTA_Signature, OTA_Signature_len)){ + memcpy((char*)signature, OTA_Clear, OTA_Signature_len); + flash_stream_write(&flash, ota_offset+OTA_Signature_offset, OTA_Signature_len, signature); + flash_stream_read(&flash, ota_offset+OTA_Signature_offset, OTA_Signature_len, signature); + signature[OTA_Signature_len] = '\0'; + printf("\n\rSignature = %s", signature); + printf("\n\rClear OTA signature success."); + } + } +} + +void sys_recover_ota_signature(void) +{ + flash_t flash; + u32 ota_offset = 0xFFFFFFFF, part1_offset, part2_offset; + u8 signature[OTA_Signature_len+1]; + u8* pbuf; + + flash_stream_read(&flash, 0x18, 4, (u8*)&part1_offset); + part1_offset = (part1_offset&0xFFFF) * 1024; + flash_stream_read(&flash, part1_offset+OTA_Signature_offset, OTA_Signature_len, signature); + if(!memcmp((char const*)signature, OTA_Clear, OTA_Signature_len)){ + ota_offset = part1_offset; + } + + flash_stream_read(&flash, FLASH_SYSTEM_DATA_ADDR, 4, (u8*)&part2_offset); + flash_stream_read(&flash, part2_offset+OTA_Signature_offset, OTA_Signature_len, signature); + if(!memcmp((char const*)signature, OTA_Clear, OTA_Signature_len)){ + ota_offset = part2_offset; + } + + printf("\n\rOTA offset = 0x%08X", ota_offset); + + if(ota_offset < OTA_valid_offset){ + flash_stream_read(&flash, ota_offset+OTA_Signature_offset, OTA_Signature_len, signature); + signature[OTA_Signature_len] = '\0'; + printf("\n\rSignature = %s", signature); + if(!memcmp((char const*)signature, OTA_Clear, OTA_Signature_len)){ + // backup + pbuf = RtlMalloc(FLASH_SECTOR_SIZE); + if(!pbuf) return; + flash_stream_read(&flash, ota_offset, FLASH_SECTOR_SIZE, pbuf); + memcpy((char*)pbuf+OTA_Signature_offset, OTA_Signature, OTA_Signature_len); + flash_erase_sector(&flash, FLASH_RESERVED_DATA_BASE); + flash_stream_write(&flash, FLASH_RESERVED_DATA_BASE, FLASH_SECTOR_SIZE, pbuf); + // Write + flash_stream_read(&flash, FLASH_RESERVED_DATA_BASE, FLASH_SECTOR_SIZE, pbuf); + flash_erase_sector(&flash, ota_offset); + flash_stream_write(&flash, ota_offset, FLASH_SECTOR_SIZE, pbuf); + flash_stream_read(&flash, ota_offset+OTA_Signature_offset, OTA_Signature_len, signature); + signature[OTA_Signature_len] = '\0'; + printf("\n\rSignature = %s", signature); + RtlMfree(pbuf, FLASH_SECTOR_SIZE); + printf("\n\rRecover OTA signature success."); + } + } +} + +void sys_log_uart_on(void) +{ + HalInitLogUart(); +} + +void sys_log_uart_off(void) +{ + HalDeinitLogUart(); +} + +void sys_adc_calibration(u8 write, u16 *offset, u16 *gain) +{ + flash_t flash; + u8* pbuf; + + if(write){ + // backup + pbuf = RtlMalloc(FLASH_SECTOR_SIZE); + if(!pbuf) return; + flash_stream_read(&flash, FLASH_SYSTEM_DATA_ADDR, FLASH_SECTOR_SIZE, pbuf); + memcpy((char*)pbuf+FLASH_ADC_PARA_OFFSET, offset, 2); + memcpy((char*)pbuf+FLASH_ADC_PARA_OFFSET+2, gain, 2); + flash_erase_sector(&flash, FLASH_RESERVED_DATA_BASE); + flash_stream_write(&flash, FLASH_RESERVED_DATA_BASE, FLASH_SECTOR_SIZE, pbuf); + // Write + flash_stream_read(&flash, FLASH_RESERVED_DATA_BASE, FLASH_SECTOR_SIZE, pbuf); + flash_erase_sector(&flash, FLASH_SYSTEM_DATA_ADDR); + flash_stream_write(&flash, FLASH_SYSTEM_DATA_ADDR, FLASH_SECTOR_SIZE, pbuf); + RtlMfree(pbuf, FLASH_SECTOR_SIZE); + printf("\n\rStore ADC calibration success."); + } + flash_stream_read(&flash, FLASH_ADC_PARA_BASE, 2, (u8*)offset); + flash_stream_read(&flash, FLASH_ADC_PARA_BASE+2, 2, (u8*)gain); + printf("\n\rADC offset = 0x%04X, gain = 0x%04X.\n\r", *offset, *gain); +} + +/** + * @brief system software reset + * + * @return None + * + */ +void sys_reset(void) +{ + // Set processor clock to default before system reset + HAL_WRITE32(SYSTEM_CTRL_BASE, 0x14, 0x00000021); + HalDelayUs(100*1000); + + // Cortex-M3 SCB->AIRCR + HAL_WRITE32(0xE000ED00, 0x0C, (0x5FA << 16) | // VECTKEY + (HAL_READ32(0xE000ED00, 0x0C) & (7 << 8)) | // PRIGROUP + (1 << 2)); // SYSRESETREQ +} diff --git a/component/common/mbed/targets/hal/rtl8195a/timer_api.c b/component/common/mbed/targets/hal/rtl8195a/timer_api.c new file mode 100644 index 0000000..0bfb9ad --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/timer_api.c @@ -0,0 +1,135 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ + +#include "objects.h" +//#include +#include "timer_api.h" +//#include "PeripheralNames.h" + +#if CONFIG_TIMER_EN + +extern HAL_TIMER_OP HalTimerOp; + +extern HAL_Status HalTimerInitRtl8195a_Patch( + IN VOID *Data +); + +static void gtimer_timeout_handler (uint32_t tid) +{ + gtimer_t *obj = (gtimer_t *)tid; + PTIMER_ADAPTER pTimerAdapter = &(obj->hal_gtimer_adp); + gtimer_irq_handler handler; + + if (obj->handler != NULL) { + handler = (gtimer_irq_handler)obj->handler; + handler(obj->hid); + } + + if (!obj->is_periodcal) { + gtimer_stop(obj); + } +} + +void gtimer_init (gtimer_t *obj, uint32_t tid) +{ + PTIMER_ADAPTER pTimerAdapter = &(obj->hal_gtimer_adp); + + if (tid > GTIMER_MAX) { + DBG_TIMER_ERR("%s: Invalid TimerId=%d\r\n", __FUNCTION__, tid); + return; + } + + pTimerAdapter->IrqDis = 0; // Enable Irq @ initial + pTimerAdapter->IrqHandle.IrqFun = (IRQ_FUN) gtimer_timeout_handler; + pTimerAdapter->IrqHandle.IrqNum = TIMER2_7_IRQ; + pTimerAdapter->IrqHandle.Priority = 0; + pTimerAdapter->IrqHandle.Data = (u32)obj; + pTimerAdapter->TimerId = (u8)tid; + pTimerAdapter->TimerIrqPriority = 0; + pTimerAdapter->TimerLoadValueUs = 0xFFFFFFFF; // Just a whatever value + pTimerAdapter->TimerMode = USER_DEFINED; + + HalTimerInit ((VOID*) pTimerAdapter); +// gtimer_stop(obj); // HAL Initial will let the timer started, just stop it after initial +} + +void gtimer_deinit (gtimer_t *obj) +{ + PTIMER_ADAPTER pTimerAdapter = &(obj->hal_gtimer_adp); + + HalTimerDeInit((void*)pTimerAdapter); +} + +uint32_t gtimer_read_tick (gtimer_t *obj) +{ + PTIMER_ADAPTER pTimerAdapter = &obj->hal_gtimer_adp; + + return (HalTimerOp.HalTimerReadCount(pTimerAdapter->TimerId)); +} + +uint64_t gtimer_read_us (gtimer_t *obj) +{ + uint64_t time_us; + + time_us = gtimer_read_tick(obj)*1000000/32768; + + return (time_us); +} + +void gtimer_reload (gtimer_t *obj, uint32_t duration_us) +{ + PTIMER_ADAPTER pTimerAdapter = &obj->hal_gtimer_adp; + + HalTimerReLoad(pTimerAdapter->TimerId, duration_us); +} + + +void gtimer_start (gtimer_t *obj) +{ + PTIMER_ADAPTER pTimerAdapter = &obj->hal_gtimer_adp; + u8 TimerId = pTimerAdapter->TimerId; + + HalTimerEnable(TimerId); +#if 0 + HalTimerOp.HalTimerEn(pTimerAdapter->TimerId); + + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF), + HAL_TIMER_READ32(TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF) | (BIT0)); +#endif +} + +void gtimer_start_one_shout (gtimer_t *obj, uint32_t duration_us, void* handler, uint32_t hid) +{ + obj->is_periodcal = _FALSE; + obj->handler = handler; + obj->hid = hid; + gtimer_reload(obj, duration_us); + gtimer_start(obj); +} + +void gtimer_start_periodical (gtimer_t *obj, uint32_t duration_us, void* handler, uint32_t hid) +{ + obj->is_periodcal = _TRUE; + obj->handler = handler; + obj->hid = hid; + gtimer_reload(obj, duration_us); + gtimer_start(obj); +} + +void gtimer_stop (gtimer_t *obj) +{ + PTIMER_ADAPTER pTimerAdapter = &obj->hal_gtimer_adp; + +// obj->handler = NULL; +// HalTimerOp.HalTimerDis(pTimerAdapter->TimerId); + HalTimerDisable(pTimerAdapter->TimerId); +} + +#endif // end of "#if CONFIG_TIMER_EN" diff --git a/component/common/mbed/targets/hal/rtl8195a/timer_api.h b/component/common/mbed/targets/hal/rtl8195a/timer_api.h new file mode 100644 index 0000000..81193b4 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/timer_api.h @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2014, Realtek Semiconductor Corp. +* All rights reserved. +* +* This module is a confidential and proprietary property of RealTek and +* possession or use of this module requires written permission of RealTek. +*******************************************************************************/ +#ifndef MBED_EXT_TIMER_API_EXT_H +#define MBED_EXT_TIMER_API_EXT_H + +#include "device.h" +//#include "rtl8195a.h" + +typedef void (*gtimer_irq_handler)(uint32_t id); + +typedef struct gtimer_s gtimer_t; +enum { + TIMER0 = 2, // GTimer 2, share with us_tick(wait_ms()) functions + TIMER1 = 3, // GTimer 3, share with PWM_0 + TIMER2 = 4, // GTimer 4, share with PWM_1 + TIMER3 = 5, // GTimer 5, share with PWM_2 + TIMER4 = 6, // GTimer 6, share with PWM_3 + TIMER5 = 7, // share with the software-RTC + + GTIMER_MAX = 7 +}; + +void gtimer_init (gtimer_t *obj, uint32_t tid); +void gtimer_deinit (gtimer_t *obj); +uint32_t gtimer_read_tick (gtimer_t *obj); +uint64_t gtimer_read_us (gtimer_t *obj); +void gtimer_reload (gtimer_t *obj, uint32_t duration_us); +void gtimer_start (gtimer_t *obj); +void gtimer_start_one_shout (gtimer_t *obj, uint32_t duration_us, void* handler, uint32_t hid); +void gtimer_start_periodical (gtimer_t *obj, uint32_t duration_us, void* handler, uint32_t hid); +void gtimer_stop (gtimer_t *obj); + +#endif diff --git a/component/common/mbed/targets/hal/rtl8195a/us_ticker.c b/component/common/mbed/targets/hal/rtl8195a/us_ticker.c new file mode 100644 index 0000000..84d0f95 --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/us_ticker.c @@ -0,0 +1,103 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek Semiconductor Corp. + * All rights reserved. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + ******************************************************************************* + */ +#include "objects.h" +#include +#include "us_ticker_api.h" +#include "PeripheralNames.h" + +#define SYS_TIM_ID 1 // the G-Timer ID for System +#define APP_TIM_ID 6 // the G-Timer ID for Application + +static int us_ticker_inited = 0; +static TIMER_ADAPTER TimerAdapter; + +extern HAL_TIMER_OP HalTimerOp; + +VOID _us_ticker_irq_handler(IN VOID *Data) +{ + us_ticker_irq_handler(); +} + +void us_ticker_init(void) +{ + + if (us_ticker_inited) return; + us_ticker_inited = 1; + + // Initial a G-Timer + TimerAdapter.IrqDis = 1; // Disable Irq + TimerAdapter.IrqHandle.IrqFun = (IRQ_FUN) _us_ticker_irq_handler; + TimerAdapter.IrqHandle.IrqNum = TIMER2_7_IRQ; + TimerAdapter.IrqHandle.Priority = 0x20; + TimerAdapter.IrqHandle.Data = (u32)NULL; + TimerAdapter.TimerId = APP_TIM_ID; + TimerAdapter.TimerIrqPriority = 0; + TimerAdapter.TimerLoadValueUs = 1; + TimerAdapter.TimerMode = FREE_RUN_MODE; // Countdown Free Run + + HalTimerOp.HalTimerInit((VOID*) &TimerAdapter); + + DBG_TIMER_INFO("%s: Timer_Id=%d\n", __FUNCTION__, APP_TIM_ID); +} + +uint32_t us_ticker_read() +{ + uint32_t tick_cnt; + uint32_t tick_cnt_div_4; + uint32_t tick_cnt_mod_4; + uint64_t us_tick; + + //1 Our G-timer resolution is ~31 us (1/32K), and is a countdown timer +// if (!us_ticker_inited) { +// us_ticker_init(); +// } + tick_cnt = HalTimerOp.HalTimerReadCount(SYS_TIM_ID); + tick_cnt = 0xffffffff - tick_cnt; // it's a down counter + tick_cnt_div_4 = tick_cnt >> 2; + tick_cnt_mod_4 = tick_cnt - (tick_cnt_div_4 << 2); + us_tick = (TIMER_TICK_US_X4*tick_cnt_div_4) + (TIMER_TICK_US*tick_cnt_mod_4); + + // TODO: handle overflow + return ((uint32_t)us_tick); +} + +void us_ticker_set_interrupt(timestamp_t timestamp) +{ + uint32_t cur_time_us; + uint32_t time_def; + + cur_time_us = us_ticker_read(); + if ((uint32_t)timestamp >= cur_time_us) { + time_def = (uint32_t)timestamp - cur_time_us; + } + else { + time_def = 0xffffffff - cur_time_us + (uint32_t)timestamp; + } + + if (time_def < TIMER_TICK_US) { + time_def = TIMER_TICK_US; // at least 1 tick + } + + TimerAdapter.IrqDis = 0; // Enable Irq + TimerAdapter.TimerLoadValueUs = time_def; + TimerAdapter.TimerMode = USER_DEFINED; // Countdown Free Run + + HalTimerOp.HalTimerInit((VOID*) &TimerAdapter); +} + +void us_ticker_disable_interrupt(void) +{ + HalTimerOp.HalTimerDis((u32)TimerAdapter.TimerId); +} + +void us_ticker_clear_interrupt(void) +{ + HalTimerOp.HalTimerIrqClear((u32)TimerAdapter.TimerId); +} diff --git a/component/common/mbed/targets/hal/rtl8195a/wdt_api.c b/component/common/mbed/targets/hal/rtl8195a/wdt_api.c new file mode 100644 index 0000000..bb24caf --- /dev/null +++ b/component/common/mbed/targets/hal/rtl8195a/wdt_api.c @@ -0,0 +1,94 @@ +/* mbed Microcontroller Library + ******************************************************************************* + * Copyright (c) 2014, Realtek + * All rights reserved. + * + * 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 HOLDER 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 "wdt_api.h" +#include "cmsis.h" + +extern VOID WDGInitial(u32 Period); +extern VOID WDGStart(VOID); +extern VOID WDGStop(VOID); +extern VOID WDGRefresh(VOID); +extern VOID WDGIrqInitial(VOID); +extern VOID WDGIrqCallBackReg(VOID *CallBack, u32 Id); + +/** + * @brief Initial the watch dog time setting + * + * @param timeout_ms: the watch-dog timer timeout value, in ms. + * default action of timeout is to reset the whole system. + * @return None + * + */ +void watchdog_init(uint32_t timeout_ms) +{ + WDGInitial(timeout_ms); +} + +/** + * @brief Start the watchdog counting + * + * @param None + * @return None + * + */ +void watchdog_start(void) +{ + WDGStart(); +} + +/** + * @brief Stop the watchdog counting + * + * @param None + * @return None + * + */ +void watchdog_stop(void) +{ + WDGStop(); +} + +/** + * @brief Refresh the watchdog counting to prevent WDT timeout + * + * @param None + * @return None + * + */ +void watchdog_refresh(void) +{ + WDGRefresh(); +} + +/** + * @brief Switch the watchdog timer to interrupt mode and + * register a watchdog timer timeout interrupt handler. + * The interrupt handler will be called when the watch-dog + * timer is timeout. + * + * @param handler: the callback function for WDT timeout interrupt. + * id: the parameter for the callback function + * @return None + * + */ +void watchdog_irq_init(wdt_irq_handler handler, uint32_t id) +{ + WDGIrqCallBackReg((VOID*)handler, (u32)id); + WDGIrqInitial(); +} + + diff --git a/component/common/network/dhcp/dhcps.c b/component/common/network/dhcp/dhcps.c new file mode 100644 index 0000000..a684fe6 --- /dev/null +++ b/component/common/network/dhcp/dhcps.c @@ -0,0 +1,618 @@ + +#include "dhcps.h" +#include "tcpip.h" + +//static struct dhcp_server_state dhcp_server_state_machine; +static uint8_t dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; +/* recorded the client MAC addr(default sudo mac) */ +//static uint8_t dhcps_record_first_client_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; +/* recorded transaction ID (default sudo id)*/ +static uint8_t dhcp_recorded_xid[4] = {0xff, 0xff, 0xff, 0xff}; + +/* UDP Protocol Control Block(PCB) */ +static struct udp_pcb *dhcps_pcb; + +static struct ip_addr dhcps_send_broadcast_address; +static struct ip_addr dhcps_local_address; +static struct ip_addr dhcps_local_mask; +static struct ip_addr dhcps_local_gateway; +static struct ip_addr dhcps_network_id; +static struct ip_addr dhcps_subnet_broadcast; +static struct ip_addr dhcps_allocated_client_address; +static int dhcps_addr_pool_set = 0; +static struct ip_addr dhcps_addr_pool_start; +static struct ip_addr dhcps_addr_pool_end; +#if 0 +static struct ip_addr dhcps_owned_first_ip; +static struct ip_addr dhcps_owned_last_ip; +static uint8_t dhcps_num_of_available_ips; +#endif +static struct dhcp_msg *dhcp_message_repository; +static int dhcp_message_total_options_lenth; + +/* allocated IP range */ +static struct table ip_table; +static struct ip_addr client_request_ip; + +static uint8_t dhcp_client_ethernet_address[16]; +static uint8_t bound_client_ethernet_address[16]; + +static xSemaphoreHandle dhcps_ip_table_semaphore; + +static struct netif * dhcps_netif = NULL; +/** + * @brief latch the specific ip in the ip table. + * @param d the specific index + * @retval None. + */ +#if (!IS_USE_FIXED_IP) +static void mark_ip_in_table(uint8_t d) +{ +#if (debug_dhcps) + printf("\r\n mark ip %d\r\n",d); +#endif + xSemaphoreTake(dhcps_ip_table_semaphore, portMAX_DELAY); + if (0 < d && d <= 32) { + ip_table.ip_range[0] = MARK_RANGE1_IP_BIT(ip_table, d); +#if (debug_dhcps) + printf("\r\n ip_table.ip_range[0] = 0x%x\r\n",ip_table.ip_range[0]); +#endif + } else if (32 < d && d <= 64) { + ip_table.ip_range[1] = MARK_RANGE2_IP_BIT(ip_table, (d - 32)); +#if (debug_dhcps) + printf("\r\n ip_table.ip_range[1] = 0x%x\r\n",ip_table.ip_range[1]); +#endif + } else if (64 < d && d <= 96) { + ip_table.ip_range[2] = MARK_RANGE3_IP_BIT(ip_table, (d - 64)); +#if (debug_dhcps) + printf("\r\n ip_table.ip_range[2] = 0x%x\r\n",ip_table.ip_range[2]); +#endif + } else if (96 < d && d <= 128) { + ip_table.ip_range[3] = MARK_RANGE4_IP_BIT(ip_table, (d - 96)); +#if (debug_dhcps) + printf("\r\n ip_table.ip_range[3] = 0x%x\r\n",ip_table.ip_range[3]); +#endif + } else { + printf("\r\n Request ip over the range(1-128) \r\n"); + } + xSemaphoreGive(dhcps_ip_table_semaphore); + +} +#endif + +/** + * @brief get one usable ip from the ip table of dhcp server. + * @param: None + * @retval the usable index which represent the ip4_addr(ip) of allocated ip addr. + */ +#if (!IS_USE_FIXED_IP) +static uint8_t search_next_ip(void) +{ + uint8_t range_count, offset_count; + uint8_t start, end; + if(dhcps_addr_pool_set){ + start = (uint8_t)ip4_addr4(&dhcps_addr_pool_start); + end = (uint8_t)ip4_addr4(&dhcps_addr_pool_end); + }else{ + start = 0; + end = 255; + } + xSemaphoreTake(dhcps_ip_table_semaphore, portMAX_DELAY); + for (range_count = 0; range_count < 4; range_count++) { + for (offset_count = 0;offset_count < 32; offset_count++) { + if ((((ip_table.ip_range[range_count] >> offset_count) & 0x01) == 0) + &&(((range_count * 32) + (offset_count + 1)) >= start) + &&(((range_count * 32) + (offset_count + 1)) <= end)) { + xSemaphoreGive(dhcps_ip_table_semaphore); + return ((range_count * 32) + (offset_count + 1)); + } + } + } + xSemaphoreGive(dhcps_ip_table_semaphore); + return 0; +} +#endif + +/** + * @brief fill in the option field with message type of a dhcp message. + * @param msg_option_base_addr: the addr be filled start. + * message_type: the type code you want to fill in + * @retval the start addr of the next dhcp option. + */ +static uint8_t *add_msg_type(uint8_t *msg_option_base_addr, uint8_t message_type) +{ + uint8_t *option_start; + msg_option_base_addr[0] = DHCP_OPTION_CODE_MSG_TYPE; + msg_option_base_addr[1] = DHCP_OPTION_LENGTH_ONE; + msg_option_base_addr[2] = message_type; + option_start = msg_option_base_addr + 3; + if (DHCP_MESSAGE_TYPE_NAK == message_type) + *option_start++ = DHCP_OPTION_CODE_END; + return option_start; +} + + +static uint8_t *fill_one_option_content(uint8_t *option_base_addr, + uint8_t option_code, uint8_t option_length, void *copy_info) +{ + uint8_t *option_data_base_address; + uint8_t *next_option_start_address = NULL; + option_base_addr[0] = option_code; + option_base_addr[1] = option_length; + option_data_base_address = option_base_addr + 2; + switch (option_length) { + case DHCP_OPTION_LENGTH_FOUR: + memcpy(option_data_base_address, copy_info, DHCP_OPTION_LENGTH_FOUR); + next_option_start_address = option_data_base_address + 4; + break; + case DHCP_OPTION_LENGTH_TWO: + memcpy(option_data_base_address, copy_info, DHCP_OPTION_LENGTH_TWO); + next_option_start_address = option_data_base_address + 2; + break; + case DHCP_OPTION_LENGTH_ONE: + memcpy(option_data_base_address, copy_info, DHCP_OPTION_LENGTH_ONE); + next_option_start_address = option_data_base_address + 1; + break; + } + + return next_option_start_address; +} + +/** + * @brief fill in the needed content of the dhcp offer message. + * @param optptr the addr which the tail of dhcp magic field. + * @retval the addr represent to add the end of option. + */ +static void add_offer_options(uint8_t *option_start_address) +{ + uint8_t *temp_option_addr; + /* add DHCP options 1. + The subnet mask option specifies the client's subnet mask */ + temp_option_addr = fill_one_option_content(option_start_address, + DHCP_OPTION_CODE_SUBNET_MASK, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcps_local_mask); + + /* add DHCP options 3 (i.e router(gateway)). The time server option + specifies a list of RFC 868 [6] time servers available to the client. */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_ROUTER, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcps_local_address); + + /* add DHCP options 6 (i.e DNS). + The option specifies a list of DNS servers available to the client. */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_DNS_SERVER, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcps_local_address); + /* add DHCP options 51. + This option is used to request a lease time for the IP address. */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_LEASE_TIME, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcp_option_lease_time_one_day); + /* add DHCP options 54. + The identifier is the IP address of the selected server. */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_SERVER_ID, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcps_local_address); + /* add DHCP options 28. + This option specifies the broadcast address in use on client's subnet.*/ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_BROADCAST_ADDRESS, DHCP_OPTION_LENGTH_FOUR, + (void *)&dhcps_subnet_broadcast); + /* add DHCP options 26. + This option specifies the Maximum transmission unit to use */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_INTERFACE_MTU, DHCP_OPTION_LENGTH_TWO, + (void *) &dhcp_option_interface_mtu_576); + /* add DHCP options 31. + This option specifies whether or not the client should solicit routers */ + temp_option_addr = fill_one_option_content(temp_option_addr, + DHCP_OPTION_CODE_PERFORM_ROUTER_DISCOVERY, DHCP_OPTION_LENGTH_ONE, + NULL); + *temp_option_addr++ = DHCP_OPTION_CODE_END; + +} + + +/** + * @brief fill in common content of a dhcp message. + * @param m the pointer which point to the dhcp message store in. + * @retval None. + */ +static void dhcps_initialize_message(struct dhcp_msg *dhcp_message_repository, struct ip_addr yiaddr) +{ + + dhcp_message_repository->op = DHCP_MESSAGE_OP_REPLY; + dhcp_message_repository->htype = DHCP_MESSAGE_HTYPE; + dhcp_message_repository->hlen = DHCP_MESSAGE_HLEN; + dhcp_message_repository->hops = 0; + memcpy((char *)dhcp_recorded_xid, (char *) dhcp_message_repository->xid, + sizeof(dhcp_message_repository->xid)); + dhcp_message_repository->secs = 0; + dhcp_message_repository->flags = htons(BOOTP_BROADCAST); + + memcpy((char *)dhcp_message_repository->yiaddr, + (char *)&yiaddr, + sizeof(dhcp_message_repository->yiaddr)); + + memset((char *)dhcp_message_repository->ciaddr, 0, + sizeof(dhcp_message_repository->ciaddr)); + memset((char *)dhcp_message_repository->siaddr, 0, + sizeof(dhcp_message_repository->siaddr)); + memset((char *)dhcp_message_repository->giaddr, 0, + sizeof(dhcp_message_repository->giaddr)); + memcpy((char *)dhcp_message_repository->chaddr, &dhcp_client_ethernet_address, + sizeof(dhcp_message_repository->chaddr)); + memset((char *)dhcp_message_repository->sname, 0, + sizeof(dhcp_message_repository->sname)); + memset((char *)dhcp_message_repository->file, 0, + sizeof(dhcp_message_repository->file)); + memset((char *)dhcp_message_repository->options, 0, + dhcp_message_total_options_lenth); + memcpy((char *)dhcp_message_repository->options, (char *)dhcp_magic_cookie, + sizeof(dhcp_magic_cookie)); +} + +/** + * @brief init and fill in the needed content of dhcp offer message. + * @param packet_buffer packet buffer for UDP. + * @retval None. + */ +static void dhcps_send_offer(struct pbuf *packet_buffer) +{ + uint8_t temp_ip = 0; + dhcp_message_repository = (struct dhcp_msg *)packet_buffer->payload; +#if (!IS_USE_FIXED_IP) + if ((ip4_addr4(&dhcps_allocated_client_address) != 0) && + (memcmp((void *)&dhcps_allocated_client_address, (void *)&client_request_ip, 4) == 0) && + (memcmp((void *)&bound_client_ethernet_address, (void *)&dhcp_client_ethernet_address, 16) == 0)) { + temp_ip = (uint8_t) ip4_addr4(&client_request_ip); + } else if ((ip4_addr1(&client_request_ip) == ip4_addr1(&dhcps_network_id)) && + (ip4_addr2(&client_request_ip) == ip4_addr2(&dhcps_network_id)) && + (ip4_addr3(&client_request_ip) == ip4_addr3(&dhcps_network_id))) { + uint8_t request_ip4 = (uint8_t) ip4_addr4(&client_request_ip); + if ((request_ip4 != 0) && (((request_ip4 - 1) / 32) >= 0) && (((request_ip4 - 1) / 32) <= 3) && + (((ip_table.ip_range[(request_ip4 - 1) / 32] >> ((request_ip4 - 1) % 32)) & 0x01) == 0)) { + temp_ip = request_ip4; + } + } + + /* create new client ip */ + if(temp_ip == 0) temp_ip = search_next_ip(); +#if (debug_dhcps) + printf("\r\n temp_ip = %d",temp_ip); +#endif + if (temp_ip == 0) { +#if 0 + memset(&ip_table, 0, sizeof(struct table)); + mark_ip_in_table((uint8_t)ip4_addr4(&dhcps_local_address)); + printf("\r\n reset ip table!!\r\n"); +#endif + printf("\r\n No useable ip!!!!\r\n"); + } + + IP4_ADDR(&dhcps_allocated_client_address, (ip4_addr1(&dhcps_network_id)), + ip4_addr2(&dhcps_network_id), ip4_addr3(&dhcps_network_id), temp_ip); + memcpy(bound_client_ethernet_address, dhcp_client_ethernet_address, sizeof(bound_client_ethernet_address)); +#endif + dhcps_initialize_message(dhcp_message_repository, dhcps_allocated_client_address); + add_offer_options(add_msg_type(&dhcp_message_repository->options[4], + DHCP_MESSAGE_TYPE_OFFER)); + udp_sendto_if(dhcps_pcb, packet_buffer, + &dhcps_send_broadcast_address, DHCP_CLIENT_PORT, dhcps_netif); +} + +/** + * @brief init and fill in the needed content of dhcp nak message. + * @param packet buffer packet buffer for UDP. + * @retval None. + */ +static void dhcps_send_nak(struct pbuf *packet_buffer) +{ + struct ip_addr zero_address; + IP4_ADDR(&zero_address, 0, 0, 0, 0); + + dhcp_message_repository = (struct dhcp_msg *)packet_buffer->payload; + dhcps_initialize_message(dhcp_message_repository, zero_address); + add_msg_type(&dhcp_message_repository->options[4], DHCP_MESSAGE_TYPE_NAK); + udp_sendto_if(dhcps_pcb, packet_buffer, + &dhcps_send_broadcast_address, DHCP_CLIENT_PORT, dhcps_netif); +} + +/** + * @brief init and fill in the needed content of dhcp ack message. + * @param packet buffer packet buffer for UDP. + * @retval None. + */ +static void dhcps_send_ack(struct pbuf *packet_buffer) +{ + dhcp_message_repository = (struct dhcp_msg *)packet_buffer->payload; + dhcps_initialize_message(dhcp_message_repository, dhcps_allocated_client_address); + add_offer_options(add_msg_type(&dhcp_message_repository->options[4], + DHCP_MESSAGE_TYPE_ACK)); + udp_sendto_if(dhcps_pcb, packet_buffer, + &dhcps_send_broadcast_address, DHCP_CLIENT_PORT, dhcps_netif); +} + +/** + * @brief according by the input message type to reflect the correspond state. + * @param option_message_type the input server state + * @retval the server state which already transfer to. + */ +uint8_t dhcps_handle_state_machine_change(uint8_t option_message_type) +{ + switch (option_message_type) { + case DHCP_MESSAGE_TYPE_DECLINE: + dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; + break; + case DHCP_MESSAGE_TYPE_DISCOVER: + if (dhcp_server_state_machine == DHCP_SERVER_STATE_IDLE) { + dhcp_server_state_machine = DHCP_SERVER_STATE_OFFER; + } + break; + case DHCP_MESSAGE_TYPE_REQUEST: + +#if (!IS_USE_FIXED_IP) + if (dhcp_server_state_machine == DHCP_SERVER_STATE_OFFER) { + if (ip4_addr4(&dhcps_allocated_client_address) != 0) { + if (memcmp((void *)&dhcps_allocated_client_address, (void *)&client_request_ip, 4) == 0) { + dhcp_server_state_machine = DHCP_SERVER_STATE_ACK; + } else { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } + } else { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } + } else if (dhcp_server_state_machine == DHCP_SERVER_STATE_IDLE) { + if ((ip4_addr4(&dhcps_allocated_client_address) != 0) && + (memcmp((void *)&dhcps_allocated_client_address, (void *)&client_request_ip, 4) == 0) && + (memcmp((void *)&bound_client_ethernet_address, (void *)&dhcp_client_ethernet_address, 16) == 0)) { + dhcp_server_state_machine = DHCP_SERVER_STATE_ACK; + } else if ((ip4_addr1(&client_request_ip) == ip4_addr1(&dhcps_network_id)) && + (ip4_addr2(&client_request_ip) == ip4_addr2(&dhcps_network_id)) && + (ip4_addr3(&client_request_ip) == ip4_addr3(&dhcps_network_id))) { + uint8_t request_ip4 = (uint8_t) ip4_addr4(&client_request_ip); + if ((request_ip4 != 0) && (((request_ip4 - 1) / 32) >= 0) && (((request_ip4 - 1) / 32) <= 3) && + (((ip_table.ip_range[(request_ip4 - 1) / 32] >> ((request_ip4 - 1) % 32)) & 0x01) == 0)) { + IP4_ADDR(&dhcps_allocated_client_address, (ip4_addr1(&dhcps_network_id)), + ip4_addr2(&dhcps_network_id), ip4_addr3(&dhcps_network_id), request_ip4); + memcpy(bound_client_ethernet_address, dhcp_client_ethernet_address, sizeof(bound_client_ethernet_address)); + dhcp_server_state_machine = DHCP_SERVER_STATE_ACK; + } else { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } + } else { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } + } else { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } +#else + if (!(dhcp_server_state_machine == DHCP_SERVER_STATE_ACK || + dhcp_server_state_machine == DHCP_SERVER_STATE_NAK)) { + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; + } +#endif + break; + case DHCP_MESSAGE_TYPE_RELEASE: + dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; + break; + } + + return dhcp_server_state_machine; +} +/** + * @brief parse the dhcp message option part. + * @param optptr: the addr of the first option field. + * len: the total length of all option fields. + * @retval dhcp server state. + */ +static uint8_t dhcps_handle_msg_options(uint8_t *option_start, int16_t total_option_length) +{ + + int16_t option_message_type = 0; + uint8_t *option_end = option_start + total_option_length; + //dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; + + /* begin process the dhcp option info */ + while (option_start < option_end) { + switch ((uint8_t)*option_start) { + case DHCP_OPTION_CODE_MSG_TYPE: + option_message_type = *(option_start + 2); // 2 => code(1)+lenth(1) + break; + case DHCP_OPTION_CODE_REQUEST_IP_ADDRESS : +#if IS_USE_FIXED_IP + if (memcmp((char *)&dhcps_allocated_client_address, + (char *)option_start + 2, 4) == 0) + dhcp_server_state_machine = DHCP_SERVER_STATE_ACK; + else + dhcp_server_state_machine = DHCP_SERVER_STATE_NAK; +#else + memcpy((char *)&client_request_ip, (char *)option_start + 2, 4); +#endif + break; + } + // calculate the options offset to get next option's base addr + option_start += option_start[1] + 2; // optptr[1]: length value + (code(1)+ Len(1)) + } + return dhcps_handle_state_machine_change(option_message_type); +} + +/** + * @brief get message from buffer then check whether it is dhcp related or not. + * if yes , parse it more to undersatnd the client's request. + * @param same as recv callback function definition + * @retval if message is dhcp related then return dhcp server state, + * otherwise return 0 + */ +static uint8_t dhcps_check_msg_and_handle_options(struct pbuf *packet_buffer) +{ + int dhcp_message_option_offset; + dhcp_message_repository = (struct dhcp_msg *)packet_buffer->payload; + memcpy(dhcp_client_ethernet_address, dhcp_message_repository->chaddr, sizeof(dhcp_client_ethernet_address)); + dhcp_message_option_offset = ((int)dhcp_message_repository->options + - (int)packet_buffer->payload); + dhcp_message_total_options_lenth = (packet_buffer->len + - dhcp_message_option_offset); + /* check the magic number,if correct parse the content of options */ + if (memcmp((char *)dhcp_message_repository->options, + (char *)dhcp_magic_cookie, sizeof(dhcp_magic_cookie)) == 0) { + return dhcps_handle_msg_options(&dhcp_message_repository->options[4], + (dhcp_message_total_options_lenth - 4)); + } + + return 0; +} + + +/** + * @brief handle imcoming dhcp message and response message to client + * @param same as recv callback function definition + * @retval None + */ +static void dhcps_receive_udp_packet_handler(void *arg, struct udp_pcb *udp_pcb, +struct pbuf *udp_packet_buffer, struct ip_addr *sender_addr, uint16_t sender_port) +{ + int16_t total_length_of_packet_buffer; + struct pbuf *merged_packet_buffer = NULL; + + dhcp_message_repository = (struct dhcp_msg *)udp_packet_buffer->payload; + if (udp_packet_buffer == NULL) { + printf("\n\r Error!!!! System doesn't allocate any buffer \n\r"); + return; + } + if (sender_port == DHCP_CLIENT_PORT) { + total_length_of_packet_buffer = udp_packet_buffer->tot_len; + if (udp_packet_buffer->next != NULL) { + merged_packet_buffer = pbuf_coalesce(udp_packet_buffer, + PBUF_TRANSPORT); + if (merged_packet_buffer->tot_len != + total_length_of_packet_buffer) { + pbuf_free(udp_packet_buffer); + return; + } + } + switch (dhcps_check_msg_and_handle_options(udp_packet_buffer)) { + case DHCP_SERVER_STATE_OFFER: + dhcps_send_offer(udp_packet_buffer); + break; + case DHCP_SERVER_STATE_ACK: + dhcps_send_ack(udp_packet_buffer); +#if (!IS_USE_FIXED_IP) + mark_ip_in_table((uint8_t)ip4_addr4(&dhcps_allocated_client_address)); +#endif + dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; + break; + case DHCP_SERVER_STATE_NAK: + dhcps_send_nak(udp_packet_buffer); + dhcp_server_state_machine = DHCP_SERVER_STATE_IDLE; + break; + case DHCP_OPTION_CODE_END: + break; + } + } + + /* free the UDP connection, so we can accept new clients */ + udp_disconnect(udp_pcb); + + /* Free the packet buffer */ + if (merged_packet_buffer != NULL) + pbuf_free(merged_packet_buffer); + else + pbuf_free(udp_packet_buffer); +} + +void dhcps_set_addr_pool(int addr_pool_set, struct ip_addr * addr_pool_start, struct ip_addr *addr_pool_end) +{ + if(addr_pool_set){ + dhcps_addr_pool_set = 1; + + memcpy(&dhcps_addr_pool_start, addr_pool_start, + sizeof(struct ip_addr)); + + memcpy(&dhcps_addr_pool_end, addr_pool_end, + sizeof(struct ip_addr)); + }else{ + dhcps_addr_pool_set = 0; + } +} +/** + * @brief Initialize dhcp server. + * @param None. + * @retval None. + * Note, for now,we assume the server latch ip 192.168.1.1 and support dynamic + * or fixed IP allocation. + */ +void dhcps_init(struct netif * pnetif) +{ +// printf("dhcps_init,wlan:%c\n\r",pnetif->name[1]); + + dhcps_netif = pnetif; + + if (dhcps_pcb != NULL) { + udp_remove(dhcps_pcb); + dhcps_pcb = NULL; + } + + dhcps_pcb = udp_new(); + IP4_ADDR(&dhcps_send_broadcast_address, 255, 255, 255, 255); + /* get net info from net interface */ + + memcpy(&dhcps_local_address, &pnetif->ip_addr, + sizeof(struct ip_addr)); + memcpy(&dhcps_local_mask, &pnetif->netmask, + sizeof(struct ip_addr)); + + memcpy(&dhcps_local_gateway, &pnetif->gw, + sizeof(struct ip_addr)); + + /* calculate the usable network ip range */ + dhcps_network_id.addr = ((pnetif->ip_addr.addr) & + (pnetif->netmask.addr)); + + dhcps_subnet_broadcast.addr = ((dhcps_network_id.addr | + ~(pnetif->netmask.addr))); +#if 0 + dhcps_owned_first_ip.addr = htonl((ntohl(dhcps_network_id.addr) + 1)); + dhcps_owned_last_ip.addr = htonl(ntohl(dhcps_subnet_broadcast.addr) - 1); + dhcps_num_of_available_ips = ((ntohl(dhcps_owned_last_ip.addr) + - ntohl(dhcps_owned_first_ip.addr)) + 1); +#endif + +#if IS_USE_FIXED_IP + IP4_ADDR(&dhcps_allocated_client_address, ip4_addr1(&dhcps_local_address) + , ip4_addr2(&dhcps_local_address), ip4_addr3(&dhcps_local_address), + (ip4_addr4(&dhcps_local_address)) + 1 ); +#else + if (dhcps_ip_table_semaphore != NULL) { + vSemaphoreDelete(dhcps_ip_table_semaphore); + dhcps_ip_table_semaphore = NULL; + } + dhcps_ip_table_semaphore = xSemaphoreCreateMutex(); + + //dhcps_ip_table = (struct ip_table *)(pvPortMalloc(sizeof(struct ip_table))); + memset(&ip_table, 0, sizeof(struct table)); + memset(&dhcps_allocated_client_address, 0, sizeof(struct ip_addr)); + memset(bound_client_ethernet_address, 0, sizeof(bound_client_ethernet_address)); + mark_ip_in_table((uint8_t)ip4_addr4(&dhcps_local_address)); + mark_ip_in_table((uint8_t)ip4_addr4(&dhcps_local_gateway)); +#if 0 + for (i = 1; i < ip4_addr4(&dhcps_local_address); i++) { + mark_ip_in_table(i); + } +#endif +#endif + udp_bind(dhcps_pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + udp_recv(dhcps_pcb, dhcps_receive_udp_packet_handler, NULL); +} + +void dhcps_deinit(void) +{ + if (dhcps_pcb != NULL) { + udp_remove(dhcps_pcb); + dhcps_pcb = NULL; + } + if (dhcps_ip_table_semaphore != NULL) { + vSemaphoreDelete(dhcps_ip_table_semaphore); + dhcps_ip_table_semaphore = NULL; + } +} diff --git a/component/common/network/dhcp/dhcps.h b/component/common/network/dhcp/dhcps.h new file mode 100644 index 0000000..e3253f9 --- /dev/null +++ b/component/common/network/dhcp/dhcps.h @@ -0,0 +1,123 @@ + +#ifndef __DHCPS_H__ +#define __DHCPS_H__ + +#include "lwip/arch.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/stats.h" +#include "lwip/sys.h" + +#include + + +#define IS_USE_FIXED_IP 0 +#define debug_dhcps 0 + +/* dhcp server states */ +#define DHCP_SERVER_STATE_OFFER (1) +#define DHCP_SERVER_STATE_DECLINE (2) +#define DHCP_SERVER_STATE_ACK (3) +#define DHCP_SERVER_STATE_NAK (4) +#define DHCP_SERVER_STATE_IDLE (5) + + +#define BOOTP_BROADCAST (0x8000) + +#define DHCP_MESSAGE_OP_REQUEST (1) +#define DHCP_MESSAGE_OP_REPLY (2) + +#define DHCP_MESSAGE_HTYPE (1) +#define DHCP_MESSAGE_HLEN (6) + +#define DHCP_SERVER_PORT (67) +#define DHCP_CLIENT_PORT (68) + +#define DHCP_MESSAGE_TYPE_DISCOVER (1) +#define DHCP_MESSAGE_TYPE_OFFER (2) +#define DHCP_MESSAGE_TYPE_REQUEST (3) +#define DHCP_MESSAGE_TYPE_DECLINE (4) +#define DHCP_MESSAGE_TYPE_ACK (5) +#define DHCP_MESSAGE_TYPE_NAK (6) +#define DHCP_MESSAGE_TYPE_RELEASE (7) + +#define DHCP_OPTION_LENGTH_ONE (1) +#define DHCP_OPTION_LENGTH_TWO (2) +#define DHCP_OPTION_LENGTH_THREE (3) +#define DHCP_OPTION_LENGTH_FOUR (4) + +#define DHCP_OPTION_CODE_SUBNET_MASK (1) +#define DHCP_OPTION_CODE_ROUTER (3) +#define DHCP_OPTION_CODE_DNS_SERVER (6) +#define DHCP_OPTION_CODE_INTERFACE_MTU (26) +#define DHCP_OPTION_CODE_BROADCAST_ADDRESS (28) +#define DHCP_OPTION_CODE_PERFORM_ROUTER_DISCOVERY (31) +#define DHCP_OPTION_CODE_REQUEST_IP_ADDRESS (50) +#define DHCP_OPTION_CODE_LEASE_TIME (51) +#define DHCP_OPTION_CODE_MSG_TYPE (53) +#define DHCP_OPTION_CODE_SERVER_ID (54) +#define DHCP_OPTION_CODE_REQ_LIST (55) +#define DHCP_OPTION_CODE_END (255) + +#define IP_FREE_TO_USE (1) +#define IP_ALREADY_IN_USE (0) + +#define HW_ADDRESS_LENGTH (6) + +/* Reference by RFC 2131 */ +struct dhcp_msg { + uint8_t op; /* Message op code/message type. 1 = BOOTREQUEST, 2 = BOOTREPLY */ + uint8_t htype; /* Hardware address type */ + uint8_t hlen; /* Hardware address length */ + uint8_t hops; /* Client sets to zero, optionally used by relay agents + when booting via a relay agent */ + uint8_t xid[4]; /* Transaction ID, a random number chosen by the client, + used by the client and server to associate messages and + responses between a client and a server */ + uint16_t secs; /* Filled in by client, seconds elapsed since client began address + acquisition or renewal process.*/ + uint16_t flags; /* bit 0: Broadcast flag, bit 1~15:MBZ must 0*/ + uint8_t ciaddr[4]; /* Client IP address; only filled in if client is in BOUND, + RENEW or REBINDING state and can respond to ARP requests. */ + uint8_t yiaddr[4]; /* 'your' (client) IP address */ + uint8_t siaddr[4]; /* IP address of next server to use in bootstrap; + returned in DHCPOFFER, DHCPACK by server. */ + uint8_t giaddr[4]; /* Relay agent IP address, used in booting via a relay agent.*/ + uint8_t chaddr[16]; /* Client hardware address */ + uint8_t sname[64]; /* Optional server host name, null terminated string.*/ + uint8_t file[128]; /* Boot file name, null terminated string; "generic" name or + null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER.*/ + uint8_t options[312]; /* Optional parameters field. reference the RFC 2132 */ +}; + +/* use this to check whether the message is dhcp related or not */ +static const uint8_t dhcp_magic_cookie[4] = {99, 130, 83, 99}; +static const uint8_t dhcp_option_lease_time_one_day[] = {0x00, 0x01, 0x51, 0x80}; +static const uint8_t dhcp_option_interface_mtu_576[] = {0x02, 0x40}; + +struct table { + uint32_t ip_range[4]; +}; + +struct address_pool{ + uint32_t start; + uint32_t end; +}; + +/* 01~32 */ +#define MARK_RANGE1_IP_BIT(table, ip) ((table.ip_range[0]) | (1 << ((ip) - 1))) +/* 33~64 */ +#define MARK_RANGE2_IP_BIT(table, ip) ((table.ip_range[1]) | (1 << ((ip) - 1))) +/* 65~96 */ +#define MARK_RANGE3_IP_BIT(table, ip) ((table.ip_range[2]) | (1 << ((ip) - 1))) +/* 97~128 */ +#define MARK_RANGE4_IP_BIT(table, ip) ((table.ip_range[3]) | (1 << ((ip) - 1))) + +/* expose API */ +void dhcps_set_addr_pool(int addr_pool_set, struct ip_addr * addr_pool_start, struct ip_addr *addr_pool_end); +void dhcps_init(struct netif * pnetif); +void dhcps_deinit(void); + +extern struct netif *netif_default; + +#endif diff --git a/component/common/network/lwip/lwip_v1.3.2/CHANGELOG b/component/common/network/lwip/lwip_v1.3.2/CHANGELOG new file mode 100644 index 0000000..860d1c4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/CHANGELOG @@ -0,0 +1,2419 @@ +FUTURE + + * TODO: The lwIP source code makes some invalid assumptions on processor + word-length, storage sizes and alignment. See the mailing lists for + problems with exoteric (/DSP) architectures showing these problems. + We still have to fix some of these issues neatly. + + * TODO: the PPP code is broken in a few ways. There are namespace + collisions on BSD systems and many assumptions on word-length + (sizeof(int)). In ppp.c an assumption is made on the availability of + a thread subsystem. Either PPP needs to be moved to contrib/ports/??? + or rearranged to be more generic. + +HISTORY + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ New features: + + + ++ Bugfixes: + + +(STABLE-1.3.2) + + ++ New features: + + 2009-10-27 Simon Goldschmidt/Stephan Lesage + * netifapi.c/.h: Added netifapi_netif_set_addr() + + 2009-10-07 Simon Goldschmidt/Fabian Koch + * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to + support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO) + + 2009-08-26 Simon Goldschmidt/Simon Kallweit + * slipif.c/.h: bug #26397: SLIP polling support + + 2009-08-25 Simon Goldschmidt + * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN), + New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK. + + 2009-08-25 Simon Goldschmidt + * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*) + + 2009-08-24 Jakob Stoklund Olesen + * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond + to netif_set_link_up(). + + 2009-08-23 Simon Goldschmidt + * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state + to a human-readable string. + + ++ Bugfixes: + + 2009-12-24: Kieran Mansley + * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing + (BUG#28241) + + 2009-12-06: Simon Goldschmidt + * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can + be statically allocated (like in ucip) + + 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev) + * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT + + 2009-12-03: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit + could have non-zero length + + 2009-12-02: Simon Goldschmidt + * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting + tcp_input_pcb until after calling the pcb's callbacks + + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + + 2009-11-29: Simon Goldschmidt + * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by + queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty + + 2009-11-26: Simon Goldschmidt + * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending + segment + + 2009-11-26: Simon Goldschmidt + * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle + algorithm at PCB level + + 2009-11-22: Simon Goldschmidt + * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent + + 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach) + * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when + reusing time-wait pcb + + 2009-11-20: Simon Goldschmidt (patch by Albert Bartel) + * sockets.c: Fixed bug #28062: Data received directly after accepting + does not wake up select + + 2009-11-11: Simon Goldschmidt + * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo) + + 2009-10-30: Simon Goldschmidt + * opt.h: Increased default value for TCP_MSS to 536, updated default + value for TCP_WND to 4*TCP_MSS to keep delayed ACK working. + + 2009-10-28: Kieran Mansley + * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code + to follow algorithm from TCP/IP Illustrated + + 2009-10-27: Kieran Mansley + * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK + + 2009-10-25: Simon Goldschmidt + * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if + pcb->recv is NULL to keep rcv_wnd correct) + + 2009-10-25: Simon Goldschmidt + * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state + + 2009-10-23: Simon Goldschmidt (David Empson) + * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes + + 2009-10-21: Simon Goldschmidt + * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and + trailing 1 byte len (SYN/FIN) + + 2009-10-21: Simon Goldschmidt + * tcp_out.c: Fixed bug #27315: zero window probe and FIN + + 2009-10-19: Simon Goldschmidt + * dhcp.c/.h: Minor code simplification (don't store received pbuf, change + conditional code to assert where applicable), check pbuf length before + testing for valid reply + + 2009-10-19: Simon Goldschmidt + * dhcp.c: Removed most calls to udp_connect since they aren't necessary + when using udp_sendto_if() - always stay connected to IP_ADDR_ANY. + + 2009-10-16: Simon Goldschmidt + * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop + valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is + enabled + + 2009-10-15: Simon Goldschmidt (Oleg Tyshev) + * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit + + 2009-10-15: Simon Goldschmidt + * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv() + timeout + + 2009-10-15: Simon Goldschmidt + * autoip.c: Fixed bug #27704: autoip starts with wrong address + LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead + of network byte order + + 2009-10-11 Simon Goldschmidt (Jörg Kesten) + * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments + which are not consecutive when retransmitting unacked segments + + 2009-10-09 Simon Goldschmidt + * opt.h: Fixed default values of some stats to only be enabled if used + Fixes bug #27338: sys_stats is defined when NO_SYS = 1 + + 2009-08-30 Simon Goldschmidt + * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK + function" by checking for loopback before calling ip_frag + + 2009-08-25 Simon Goldschmidt + * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0 + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27078: Possible memory leak in pppInit() + + 2009-08-23 Simon Goldschmidt + * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result + is error. + + 2009-08-23 Simon Goldschmidt + * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF + Fixed wrong parenthesis, added check in init.c + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms + + 2009-08-23 Simon Goldschmidt + * many ppp files: bug #27267: Added include to string.h where needed + + 2009-08-23 Simon Goldschmidt + * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian) + + +(STABLE-1.3.1) + + ++ New features: + + 2009-05-10 Simon Goldschmidt + * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option + LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only + one pbuf to help MACs that don't support scatter-gather DMA. + + 2009-05-09 Simon Goldschmidt + * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming + ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + + 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen + * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive + extended info about the currently received packet. + + 2009-04-27 Simon Goldschmidt + * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1 + + 2009-04-25 Simon Goldschmidt + * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next + bigger malloc pool if one is empty (only usable with MEM_USE_POOLS). + + 2009-04-21 Simon Goldschmidt + * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static + hosts table. New configuration options DNS_LOCAL_HOSTLIST and + DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined + as an external function for lookup. + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for + TCP timestamp options, off by default. Rework tcp_enqueue() to + take option flags rather than specified option data + + 2009-02-18 Simon Goldschmidt + * cc.h: Added printf formatter for size_t: SZT_F + + 2009-02-16 Simon Goldschmidt (patch by Rishi Khan) + * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast + pings + + 2009-02-12 Simon Goldschmidt + * init.h: Added LWIP_VERSION to get the current version of the stack + + 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler) + * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead + of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc + is otherwise used) + + 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach) + * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial() + is only used by UDPLITE at present, so conditionalise it. + + 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli) + * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP + "seed" address. This should reduce AUTOIP conflicts if + LWIP_AUTOIP_CREATE_SEED_ADDR is overridden. + + 2008-10-02 Jonathan Larmour and Rishi Khan + * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking + socket. + + 2008-06-30 Simon Goldschmidt + * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from + interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows + mem_free to run between mem_malloc iterations. Added illegal counter for + mem stats. + + 2008-06-27 Simon Goldschmidt + * stats.h/.c, some other files: patch #6483: stats module improvement: + Added defines to display each module's statistic individually, added stats + defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. + + 2008-06-17 Simon Goldschmidt + * err.h: patch #6459: Made err_t overridable to use a more efficient type + (define LWIP_ERR_T in cc.h) + + 2008-06-17 Simon Goldschmidt + * slipif.c: patch #6480: Added a configuration option for slipif for symmetry + to loopif + + 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) + * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly + modified version of patch # 6370: Moved loopif code to netif.c so that + loopback traffic is supported on all netifs (all local IPs). + Added option to limit loopback packets for each netifs. + + + ++ Bugfixes: + 2009-08-12 Kieran Mansley + * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when + out of window or out of order properly + + 2009-08-12 Kieran Mansley + * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1 + + 2009-07-28 Simon Goldschmidt + * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s + + 2009-07-27 Kieran Mansley + * api.h api_msg.h netdb.h sockets.h: add missing #include directives + + 2009-07-09 Kieran Mansley + * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for + recv_avail and don't increment counters until message successfully + sent to mbox + + 2009-06-25 Kieran Mansley + * api_msg.c api.h: BUG26722: initialise netconn write variables + in netconn_alloc + + 2009-06-25 Kieran Mansley + * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set + + 2009-06-25 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct + simultaneous close behaviour, and make snd_nxt have the same meaning + as in the RFCs. + + 2009-05-12 Simon Goldschmidt + * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on + arp_table / uses etharp_query" by adding etharp_gratuitous() + + 2009-05-12 Simon Goldschmidt + * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options + to the IP header (used by igmp_ip_output_if) + + 2009-05-06 Simon Goldschmidt + * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if + defined) for SWAP_BYTES_IN_WORD to speed up checksumming. + + 2009-05-05 Simon Goldschmidt + * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select() + to crash + + 2009-05-04 Simon Goldschmidt + * init.c: snmp was not initialized in lwip_init() + + 2009-05-04 Frédéric Bernon + * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled. + + 2009-05-03 Simon Goldschmidt + * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full + (and unsent->next == NULL) + + 2009-05-02 Simon Goldschmidt + * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after + 1.3.0 in CVS only) - fixes compilation of ppp_oe.c + + 2009-05-02 Simon Goldschmidt + * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields + + 2009-05-01 Simon Goldschmidt + * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets + + 2009-05-01 Simon Goldschmidt + * ppp.c: bug #24228: Memory corruption with PPP and DHCP + + 2009-04-29 Frédéric Bernon + * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the + SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception + of broadcast packets even when this option wasn't set. Port maintainers + which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h. + If you want this option also filter broadcast on recv operations, you also + have to set IP_SOF_BROADCAST_RECV=1 in opt.h. + + 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen + * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and + DHCP/AUTOIP cooperation + + 2009-04-25 Simon Goldschmidt, Oleg Tyshev + * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd + Fixed by sorting the unsent and unacked queues (segments are inserted at the + right place in tcp_output and tcp_rexmit). + + 2009-04-25 Simon Goldschmidt + * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation + when debugging": memp_sizes contained the wrong sizes (including sanity + regions); memp pools for MEM_USE_POOLS were too small + + 2009-04-24 Simon Goldschmidt, Frédéric Bernon + * inet.c: patch #6765: Fix a small problem with the last changes (incorrect + behavior, with with ip address string not ended by a '\0', a space or a + end of line) + + 2009-04-19 Simon Goldschmidt + * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails, + pcb->err is called, not pcb->connected (with an error code). + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with + no-copy-tcpwrite": deallocate option data, only concat segments with same flags + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated + in the header pbuf, not the data pbuf) + + 2009-04-18 Simon Goldschmidt + * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore() + + 2009-04-15 Simon Goldschmidt + * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp + + 2009-04-15 Simon Goldschmidt + * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in + + 2009-04-15 Simon Goldschmidt + * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function + ip_hinted_output() (for smaller code mainly) + + 2009-04-15 Simon Goldschmidt + * inet.c: patch #6765: Supporting new line characters in inet_aton() + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option; + Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu + is big enough in dhcp_start + + 2009-04-15 Simon Goldschmidt + * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak + + 2009-04-15 Simon Goldschmidt + * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY + + 2009-04-15 Simon Goldschmidt + * sockets.c: bug #26121: set_errno can be overridden + + 2009-04-09 Kieran Mansley (patch from Luca Ceresoli ) + * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when + LWIP_TCP==0 + + 2009-04-09 Kieran Mansley (patch from Roy Lee ) + * tcp.h: Patch#6802 Add do-while-clauses to those function like + macros in tcp.h + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window + updates are calculated and sent (BUG20515) + + * tcp_in.c: cope with SYN packets received during established states, + and retransmission of initial SYN. + + * tcp_out.c: set push bit correctly when tcp segments are merged + + 2009-03-27 Kieran Mansley + * tcp_out.c set window correctly on probes (correcting change made + yesterday) + + 2009-03-26 Kieran Mansley + * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping + connections where no reset required (bug #25622) + + * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes + (bug #20779) + + 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach) + * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be + too small depending on MEM_ALIGNMENT + + 2009-02-16 Simon Goldschmidt + * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard; + converted size argument of netconn_write to 'size_t' + + 2009-02-16 Simon Goldschmidt + * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host + by moving accept callback function pointer to TCP_PCB_COMMON + + 2009-02-12 Simon Goldschmidt + * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size" + option) + + 2009-02-11 Simon Goldschmidt + * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start) + + 2009-02-11 Simon Goldschmidt + * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize: + RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv()) + + 2009-02-10 Simon Goldschmidt + * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD: + Accepts_pending is decrease on a corresponding listen pcb when a connection + in state SYN_RCVD is close. + + 2009-01-28 Jonathan Larmour + * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run + out of pool pbufs. + + 2008-12-19 Simon Goldschmidt + * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 + + 2008-12-10 Tamas Somogyi, Frédéric Bernon + * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and + port uses deleted netbuf. + + 2008-10-18 Simon Goldschmidt + * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length + in tcp_parseopt + + 2008-10-15 Simon Goldschmidt + * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers + by packing the struct ip_reass_helper. + + 2008-10-03 David Woodhouse, Jonathan Larmour + * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address. + + 2008-10-02 Jonathan Larmour + * dns.c: Hard-code structure sizes, to avoid issues on some compilers where + padding is included. + + 2008-09-30 Jonathan Larmour + * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an + assertion check that addrlen isn't NULL. + + 2008-09-30 Jonathan Larmour + * tcp.c: Fix bug #24227, wrong error message in tcp_bind. + + 2008-08-26 Simon Goldschmidt + * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and + inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h + + 2008-08-14 Simon Goldschmidt + * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when + tcp_close returns != ERR_OK) + + 2008-07-08 Frédéric Bernon + * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters + in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). + + 2008-06-24 Jonathan Larmour + * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused + if tcp_seg_copy fails. + + 2008-06-17 Simon Goldschmidt + * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) + and created defines for swapping bytes and folding u32 to u16. + + 2008-05-30 Kieran Mansley + * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd + rather than rcv_ann_wnd when deciding if packets are in-window. + Contributed by + + 2008-05-30 Kieran Mansley + * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow + passing as function pointers when MEM_LIBC_MALLOC is defined. + + 2008-05-09 Jonathan Larmour + * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to + stop it being treated as a fatal error. + + 2008-04-15 Simon Goldschmidt + * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP + (flag now cleared) + + 2008-03-27 Simon Goldschmidt + * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free + from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 + in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs + or heap memory from interrupt context + + 2008-03-26 Simon Goldschmidt + * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote + host sent a zero mss as TCP option. + + +(STABLE-1.3.0) + + ++ New features: + + 2008-03-10 Jonathan Larmour + * inet_chksum.c: Allow choice of one of the sample algorithms to be + made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM. + + 2008-01-22 Frédéric Bernon + * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in + TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names. + + 2008-01-14 Frédéric Bernon + * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable + to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the + tcp_recv callback (see rawapi.txt). + + 2008-01-14 Frédéric Bernon, Marc Chaland + * ip.c: Integrate patch #6369" ip_input : checking before realloc". + + 2008-01-12 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::sem per netconn::op_completed like suggested for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-12 Frédéric Bernon + * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE, + DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues + sizes), like suggested for the task #7490 "Add return value to sys_mbox_post". + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490 + "Add return value to sys_mbox_post". tcpip_callback is always defined as + "blocking" ("block" parameter = 1). + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-05 Frédéric Bernon + * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: + Introduce changes for task #7490 "Add return value to sys_mbox_post" with some + modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which + indicate the number of pointers query by the mailbox. There is three defines + in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the + netconn::acceptmbox. Port maintainers, you can decide to just add this new + parameter in your implementation, but to ignore it to keep the previous behavior. + The new sys_mbox_trypost function return a value to know if the mailbox is + full or if the message is posted. Take a look to sys_arch.txt for more details. + This new function is used in tcpip_input (so, can be called in an interrupt + context since the function is not blocking), and in recv_udp and recv_raw. + + 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c, + tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the + "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add + documentation in the rawapi.txt file. + + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer + + 2007-12-31 Frédéric Bernon, Luca Ceresoli + * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets + in autoip". The change in etharp_raw could be removed, since all calls to + etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be + wrong in the future. + + 2007-12-30 Frédéric Bernon, Tom Evans + * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address + Filtering" reported by Tom Evans. + + 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c, + sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API + applications have to call 'tcp_accepted(pcb)' in their accept callback to + keep accepting new connections. + + 2007-12-13 Frédéric Bernon + * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result" + by err_t type. Add a new err_t code "ERR_INPROGRESS". + + 2007-12-12 Frédéric Bernon + * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles + are the one which have ram usage. + + 2007-12-05 Frédéric Bernon + * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static + set of variables (=0) or a local one (=1). In this last case, your port should + provide a function "struct hostent* sys_thread_hostent( struct hostent* h)" + which have to do a copy of "h" and return a pointer ont the "per-thread" copy. + + 2007-12-03 Simon Goldschmidt + * ip.c: ip_input: check if a packet is for inp first before checking all other + netifs on netif_list (speeds up packet receiving in most cases) + + 2007-11-30 Simon Goldschmidt + * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access + UDP: move a (connected) pcb selected for input to the front of the list of + pcbs so that it is found faster next time. Same for RAW pcbs that have eaten + a packet. + + 2007-11-28 Simon Goldschmidt + * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS + + 2007-11-25 Simon Goldschmidt + * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy + algorithm. + + 2007-11-24 Simon Goldschmidt + * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c + to the new file netdb.c; included lwip_getaddrinfo. + + 2007-11-21 Simon Goldschmidt + * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss + based on the MTU of the netif used to send. Enabled by default. Disable by + setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492. + + 2007-11-19 Frédéric Bernon + * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name + received match the name query), implement DNS_USES_STATIC_BUF (the place where + copy dns payload to parse the response), return an error if there is no place + for a new query, and fix some minor problems. + + 2007-11-16 Simon Goldschmidt + * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c + removed files: core/inet.c, core/inet6.c + Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into + inet and chksum part; changed includes in all lwIP files as appropriate + + 2007-11-16 Simon Goldschmidt + * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential + dns resolver function for netconn api (netconn_gethostbyname) and socket api + (gethostbyname/gethostbyname_r). + + 2007-11-15 Jim Pettinato, Frédéric Bernon + * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name + requests with RAW api interface. Initialization is done in lwip_init() with + build time options. DNS timer is added in tcpip_thread context. DHCP can set + DNS server ip addresses when options are received. You need to set LWIP_DNS=1 + in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get + some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo" + list with points to improve. + + 2007-11-06 Simon Goldschmidt + * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly + enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status + for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined. + + 2007-11-06 Simon Goldschmidt + * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include + core header files in api.h (ip/tcp/udp/raw.h) to hide the internal + implementation from netconn api applications. + + 2007-11-03 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP & + RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled + by default). Netconn API users can use the netconn_recv_bufsize macro to access + it. This is a first release which have to be improve for TCP. Note it used the + netconn::recv_avail which need to be more "thread-safe" (note there is already + the problem for FIONREAD with lwip_ioctl/ioctlsocket). + + 2007-11-01 Frédéric Bernon, Marc Chaland + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c: + Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api + layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api + layer. This option enable to delayed TCP PUSH flag on multiple "write" calls. + Note that previous "copy" parameter for "write" APIs is now called "apiflags". + + 2007-10-24 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than + TCP_EVENT_xxx macros to get a code more readable. It could also help to remove + some code (like we have talk in "patch #5919 : Create compile switch to remove + select code"), but it could be done later. + + 2007-10-08 Simon Goldschmidt + * many files: Changed initialization: many init functions are not needed any + more since we now rely on the compiler initializing global and static + variables to zero! + + 2007-10-06 Simon Goldschmidt + * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY + to enqueue the received pbufs so that multiple packets can be reassembled + simultaneously and no static reassembly buffer is needed. + + 2007-10-05 Simon Goldschmidt + * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so + all netifs (or ports) can use it. + + 2007-10-05 Frédéric Bernon + * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the + common function to reduce a little bit the footprint (for all functions using + only the "netif" parameter). + + 2007-10-03 Frédéric Bernon + * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down, + netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce + a little bit the footprint (for all functions using only the "netif" parameter). + + 2007-09-15 Frédéric Bernon + * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF + option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for + netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for + IP_MULTICAST_TTL and IP_MULTICAST_IF. + + 2007-09-10 Frédéric Bernon + * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles + even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime() + each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can + decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but + call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime() + or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. + This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside + snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only + when it's queried (any direct call to "sysuptime" is changed by a call to + snmp_get_sysuptime). + + 2007-09-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP, + and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags + if you want IGMP on an interface. igmp_stop() is now called inside netif_remove(). + igmp_report_groups() is now called inside netif_set_link_up() (need to have + LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait + the next query message to receive the matching multicast streams). + + 2007-09-08 Frédéric Bernon + * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains + IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change). + Use this new field to access to common pcb fields (ttl, tos, so_options, etc...). + Enable to access to these fields with LWIP_TCP=0. + + 2007-09-05 Frédéric Bernon + * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h, + ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option + LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default). + Be careful, disabling ICMP make your product non-compliant to RFC1122, but + help to reduce footprint, and to reduce "visibility" on the Internet. + + 2007-09-05 Frédéric Bernon, Bill Florac + * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list + for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new + parameters have to be provided: a task name, and a task stack size. For this + one, since it's platform dependant, you could define the best one for you in + your lwipopts.h. For port maintainers, you can just add these new parameters + in your sys_arch.c file, and but it's not mandatory, use them in your OS + specific functions. + + 2007-09-05 Frédéric Bernon + * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings + inside init.c for task #7142 "Sanity check user-configurable values". + + 2007-09-04 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by + memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the + value). It will avoid potential fragmentation problems, use a counter to know + how many times a group is used on an netif, and free it when all applications + leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity + check if LWIP_IGMP!=0). + + 2007-09-03 Frédéric Bernon + * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement". + Initialize igmp_mac_filter to NULL in netif_add (this field should be set in + the netif's "init" function). Use the "imr_interface" field (for socket layer) + and/or the "interface" field (for netconn layer), for join/leave operations. + The igmp_join/leavegroup first parameter change from a netif to an ipaddr. + This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany). + + 2007-08-30 Frédéric Bernon + * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions + from api/api_lib". Now netbuf API is independant of netconn, and can be used + with other API (application based on raw API, or future "socket2" API). Ports + maintainers just have to add src/api/netbuf.c in their makefile/projects. + + 2007-08-30 Frédéric Bernon, Jonathan Larmour + * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check + user-configurable values". + + 2007-08-29 Frédéric Bernon + * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start. + igmp_start is call inside netif_add. Now, igmp initialization is in the same + spirit than the others modules. Modify some IGMP debug traces. + + 2007-08-29 Frédéric Bernon + * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function" + Add lwip_init function to regroup all modules initializations, and to provide + a place to add code for task #7142 "Sanity check user-configurable values". + Ports maintainers should remove direct initializations calls from their code, + and add init.c in their makefiles. Note that lwip_init() function is called + inside tcpip_init, but can also be used by raw api users since all calls are + disabled when matching options are disabled. Also note that their is new options + in opt.h, you should configure in your lwipopts.h (they are enabled per default). + + 2007-08-26 Marc Boucher + * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL + since they can under certain circumstances be called with an invalid conn + pointer after the connection has been closed (and conn has been freed). + + 2007-08-25 Frédéric Bernon (Artem Migaev's Patch) + * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up". + Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set. + + 2007-08-22 Frédéric Bernon + * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK + to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release. + + 2007-08-22 Frédéric Bernon + * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT & + ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the + name is tcpip_input (we keep the name of 1.2.0 function). + + 2007-08-17 Jared Grubb + * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool + settings into new memp_std.h and optional user file lwippools.h. This adds + more dynamic mempools, and allows the user to create an arbitrary number of + mempools for mem_malloc. + + 2007-08-16 Marc Boucher + * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function; + otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely + close the connection. + + 2007-08-16 Marc Boucher + * sockets.c: lwip_accept(): check netconn_peer() error return. + + 2007-08-16 Marc Boucher + * mem.c, mem.h: Added mem_calloc(). + + 2007-08-16 Marc Boucher + * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT) + for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG + and starving other message types. + Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API + + 2007-08-16 Marc Boucher + * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf + type and flgs (later renamed to flags). + Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*. + Improved lwip_recvfrom(). TCP push now propagated. + + 2007-08-16 Marc Boucher + * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global + provided by etharp. + + 2007-08-16 Marc Boucher + * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h, + etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c: + Added PPPoE support and various PPP improvements. + + 2007-07-25 Simon Goldschmidt + * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial, + making netbuf_copy_partial use this function. + + 2007-07-25 Simon Goldschmidt + * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with + 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and + other stacks. + + 2007-07-13 Jared Grubb (integrated by Frédéric Bernon) + * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add + a link callback in the netif struct, and functions to handle it. Be carefull + for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c) + if you want to be sure to be compatible with future changes... + + 2007-06-30 Frédéric Bernon + * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions. + + 2007-06-21 Simon Goldschmidt + * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both + LWIP_AUTOIP =0 and =1 to remove redundant code. + + 2007-06-21 Simon Goldschmidt + * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option + MEM_USE_POOLS to use 4 pools with different sized elements instead of a + heap. This both prevents memory fragmentation and gives a higher speed + at the cost of more memory consumption. Turned off by default. + + 2007-06-21 Simon Goldschmidt + * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of + netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into + int to be able to send a bigger buffer than 64K with one time (mainly + used from lwip_send). + + 2007-06-21 Simon Goldschmidt + * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write + into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too. + + 2007-06-21 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in + netconn_write from api_lib.c to api_msg.c to also prevent multiple context- + changes on low memory or empty send-buffer. + + 2007-06-18 Simon Goldschmidt + * etharp.c, etharp.h: Changed etharp to use a defined hardware address length + of 6 to avoid loading netif->hwaddr_len every time (since this file is only + used for ethernet and struct eth_addr already had a defined length of 6). + + 2007-06-17 Simon Goldschmidt + * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets + to disable UDP checksum generation on transmit. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid + pointers or parameters, and let the possibility to redefined it in cc.h. Use + this macro to check "conn" parameter in api_msg.c functions. + + 2007-06-11 Simon Goldschmidt + * sockets.c, sockets.h: Added UDP lite support for sockets + + 2007-06-10 Simon Goldschmidt + * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled + by default) to switch off UDP-Lite support if not needed (reduces udp.c code + size) + + 2007-06-09 Dominik Spies (integrated by Frédéric Bernon) + * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h: + AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and + LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt + (see TODO mark in the source code). + + 2007-06-09 Simon Goldschmidt + * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for + etharp_output() to match netif->output so etharp_output() can be used + directly as netif->output to save one function call. + + 2007-06-08 Simon Goldschmidt + * netif.h, ethernetif.c, slipif.c, loopif.c: Added define + NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables, + added initialization of those to ethernetif, slipif and loopif. + + 2007-05-18 Simon Goldschmidt + * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF + (defaulting to off for now) that can be set to 0 to send fragmented + packets by passing PBUF_REFs down the stack. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP + connections, such present in patch #5959. + + 2007-05-23 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx + code in only one part... + + 2007-05-18 Simon Goldschmidt + * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp + elements to overflow. This is achieved by adding some bytes before and after + each pool element (increasing their size, of course), filling them with a + prominent value and checking them on freeing the element. + Set it to 2 to also check every element in every pool each time memp_malloc() + or memp_free() is called (slower but more helpful). + + 2007-05-10 Simon Goldschmidt + * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for + PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce + code size. + + 2007-05-11 Frédéric Bernon + * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c: + Include a function pointer instead of a table index in the message to reduce + footprint. Disable some part of lwip_send and lwip_sendto if some options are + not set (LWIP_TCP, LWIP_UDP, LWIP_RAW). + + 2007-05-10 Simon Goldschmidt + * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus + \ extern "C" {' in all header files. Now you can write your application using + the lwIP stack in C++ and simply #include the core files. Note I have left + out the netif/ppp/*h header files for now, since I don't know which files are + included by applications and which are for internal use only. + + 2007-05-09 Simon Goldschmidt + * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library + memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for + situations where some compilers might inline the copy and save a function + call. Also replaced all calls to memcpy() with calls to (S)MEMCPY(). + + 2007-05-08 Simon Goldschmidt + * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc()) + to be overriden in case the C-library malloc implementation is not protected + against concurrent access. + + 2007-05-04 Simon Goldschmidt (Atte Kojo) + * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending + multiple packets to the same host. + + 2007-05-04 Frédéric Bernon, Jonathan Larmour + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible + to corrupt remote addr/port connection state". Reduce problems "not enought memory" with + netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between + sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function. + Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct, + these fields are now renamed "addr" & "port". + + 2007-04-11 Jonathan Larmour + * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new + sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return + with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro + by the port in sys_arch.h if desired. + + 2007-04-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API + allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp + clients, using new functions from netifapi.h. Disable as default (no port change to do). + + 2007-04-05 Frédéric Bernon + * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant. + + 2007-04-04 Simon Goldschmidt + * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x) + use this for and architecture-independent form to tell the compiler you intentionally + are not using this variable. Can be overriden in cc.h. + + 2007-03-28 Frédéric Bernon + * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to + define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded + string, point on one of your's ethernetif field, or alloc a string you will free yourself). + It will be used by DHCP to register a client hostname, but can also be use when you call + snmp_set_sysname. + + 2007-03-28 Frédéric Bernon + * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to + initialize a network interface's flag with. It tell this interface is an ethernet + device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility + Support for IPv4" section 4.6) when interface is "up" with netif_set_up(). + + 2007-03-26 Frédéric Bernon, Jonathan Larmour + * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build + time if you only use PPP or SLIP. The default is enable. Note we don't have to call + etharp_init in your port's initilization sequence if you use tcpip.c, because this call + is done in tcpip_init function. + + 2007-03-22 Frédéric Bernon + * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the + new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in + your lwipopts.h. More, unused counters are not defined in the stats structs, and not + display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined + but never used. Fix msg_in.c with the correct #if test for a stat display. + + 2007-03-21 Kieran Mansley + * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). + Provides callback on netif up/down state change. + + 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c, + ip.c, netif.h, tcpip.c, opt.h: + New configuration option LWIP_IGMP to enable IGMP processing. Based on only one + filter per all network interfaces. Declare a new function in netif to enable to + control the MAC filter (to reduce lwIP traffic processing). + + 2007-03-11 Frédéric Bernon + * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can + be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this + unless you know what you're doing (default are RFC1122 compliant). Note + that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds. + + 2007-03-08 Frédéric Bernon + * tcp.h: Keepalive values can be configured at compile time, but don't change + this unless you know what you're doing (default are RFC1122 compliant). + + 2007-03-08 Frédéric Bernon + * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h: + Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO + on UDP sockets/netconn. + + 2007-03-08 Simon Goldschmidt + * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time. + + 2007-03-06 Frédéric Bernon + * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: + Implement SO_RCVTIMEO on UDP sockets/netconn. + + 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt) + * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated + on the stack and remove the API msg type from memp + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * sockets.h, sockets.c: Move socket initialization to new + lwip_socket_init() function. + NOTE: this changes the API with ports. Ports will have to be + updated to call lwip_socket_init() now. + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * api_lib.c: Use memcpy in netbuf_copy_partial. + + + ++ Bug fixes: + + 2008-03-17 Frédéric Bernon, Ed Kerekes + * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have + some problems to fill the IP header on some targets, use now the + ip.h macros to do it). + + 2008-03-13 Frédéric Bernon + * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using + (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a + TCP connection caused a crash. Note that using (lwip_)recvfrom + like this is a bit slow and that using (lwip)getpeername is the + good lwip way to do it (so, using recv is faster on tcp sockets). + + 2008-03-12 Frédéric Bernon, Jonathan Larmour + * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's + recv_raw() does not consume data", and the ping sample (with + LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom + returned the IP payload, without the IP header). + + 2008-03-04 Jonathan Larmour + * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors + and/or warnings on some systems where mem_size_t and size_t differ. + * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc. + + 2008-03-04 Kieran Mansley (contributions by others) + * Numerous small compiler error/warning fixes from contributions to + mailing list after 1.3.0 release candidate made. + + 2008-01-25 Cui hengbin (integrated by Frédéric Bernon) + * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures. + + 2008-01-15 Kieran Mansley + * tcp_out.c: BUG20511. Modify persist timer to start when we are + prevented from sending by a small send window, not just a zero + send window. + + 2008-01-09 Jonathan Larmour + * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid + conflict with Linux system headers. + + 2008-01-06 Jonathan Larmour + * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP + address entirely on receiving a DHCPNAK, and restarting discovery. + + 2007-12-21 Simon Goldschmidt + * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail + is not protected" by using new macros for interlocked access to modify/test + netconn->recv_avail. + + 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev) + * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state) + + 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling + of silly window avoidance and prevent lwIP from shrinking the window) + + 2007-12-04 Simon Goldschmidt + * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last + data packet was lost): add assert that all segment lists are empty in + tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED + state from LAST_ACK in tcp_process + + 2007-12-02 Simon Goldschmidt + * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET + If including for system-struct timeval, LWIP_TIMEVAL_PRIVATE now + has to be set to 0 in lwipopts.h + + 2007-12-02 Simon Goldschmidt + * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always + allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen + netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox. + This is a fix for thread-safety and allocates all items needed for a netconn + when the netconn is created. + + 2007-11-30 Simon Goldschmidt + * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple + netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed + to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same + port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address) + + 2007-11-27 Simon Goldschmidt + * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by + letting ip_route only use netifs that are up. + + 2007-11-27 Simon Goldschmidt + * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF + and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and + sockets block most operations once they have seen a fatal error. + + 2007-11-27 Simon Goldschmidt + * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the + netif to send as an argument (to be able to send on netifs that are down). + + 2007-11-26 Simon Goldschmidt + * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs + arrive out-of-order + + 2007-11-21 Simon Goldschmidt + * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early + Fixed the nagle algorithm; nagle now also works for all raw API applications + and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY' + + 2007-11-12 Frédéric Bernon + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most + of the netconn_peer and netconn_addr processing is done inside tcpip_thread + context in do_getaddr. + + 2007-11-10 Simon Goldschmidt + * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can + happen any time). Now the packet simply isn't enqueued when out of memory. + + 2007-11-01 Simon Goldschmidt + * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or + TCP_MSS if that is smaller) as long as no MSS option is received from the + remote host. + + 2007-11-01 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN) + is now based on TCP_MSS instead of pcb->mss (on passive open now effectively + sending our configured TCP_MSS instead of the one received). + + 2007-11-01 Simon Goldschmidt + * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was + calculated based on the configured TCP_MSS, not on the MSS option received + with SYN+ACK. + + 2007-10-09 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too + short and also was generated wrong if checksum coverage != tot_len; + receive: checksum was calculated wrong if checksum coverage != tot_len + + 2007-10-08 Simon Goldschmidt + * mem.c: lfree was not updated in mem_realloc! + + 2007-10-07 Frédéric Bernon + * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential + crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: + this change cause an API breakage for netconn_addr, since a parameter + type change. Any compiler should cause an error without any changes in + yours netconn_peer calls (so, it can't be a "silent change"). It also + reduce a little bit the footprint for socket layer (lwip_getpeername & + lwip_getsockname use now a common lwip_getaddrname function since + netconn_peer & netconn_addr have the same parameters). + + 2007-09-20 Simon Goldschmidt + * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state) + by checking tcp_tw_pcbs also + + 2007-09-19 Simon Goldschmidt + * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies) + + 2007-09-15 Mike Kleshov + * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used) + + 2007-09-06 Frédéric Bernon + * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove + it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which + already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h" + if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h. + + 2007-08-30 Frédéric Bernon + * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, + and fix some coding style. + + 2007-08-28 Frédéric Bernon + * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any + kind of packets. These packets are considered like Ethernet packets (payload + pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets + are considered like IP packets (payload pointing to iphdr). + + 2007-08-27 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error + problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state + and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT). + + 2007-08-24 Kieran Mansley + * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy + compiler (Paradigm C++) + + 2007-08-09 Frédéric Bernon, Bill Florac + * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement. + Introduce IGMP_STATS to centralize statistics management. + + 2007-08-09 Frédéric Bernon, Bill Florac + * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast + packet on a udp pcb binded on an netif's IP address, and not on "any". + + 2007-08-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement. + This is mainly on using lookup/lookfor, and some coding styles... + + 2007-07-26 Frédéric Bernon (and "thedoctor") + * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages. + + 2007-07-25 Simon Goldschmidt + * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if + tcp_output fails in tcp_close, the code in do_close_internal gets simpler + (tcp_output is called again later from tcp timers). + + 2007-07-25 Simon Goldschmidt + * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old + copy_from_pbuf, which illegally modified the given pbuf. + + 2007-07-25 Simon Goldschmidt + * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs: + changed snd_queuelen++ to snd_queuelen += pbuf_clen(p). + + 2007-07-24 Simon Goldschmidt + * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the + correct state (must be CLOSED). + + 2007-07-13 Thomas Taranowski (commited by Jared Grubb) + * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed + allocation. It now returns NULL. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in + all error cases. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed, + because current code doesn't follow rawapi.txt documentation. + + 2007-07-13 Kieran Mansley + * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in + out of sequence processing of received packets + + 2007-07-03 Simon Goldschmidt + * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an + assumption is made that this pbuf is in one piece (i.e. not chained). These + assumptions clash with the possibility of converting to fully pool-based + pbuf implementations, where PBUF_RAM pbufs might be chained. + + 2007-07-03 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems + when closing tcp netconns: removed conn->sem, less context switches when + closing, both netconn_close and netconn_delete should safely close tcp + connections. + + 2007-07-02 Simon Goldschmidt + * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c, + tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off) + to cache ARP table indices with each pcb instead of single-entry cache for + the complete stack. + + 2007-07-02 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent + warnings when assigning to smaller types. + + 2007-06-28 Simon Goldschmidt + * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing. + + 2007-06-28 Simon Goldschmidt + * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if + a segment contained chained pbufs) + + 2007-06-28 Frédéric Bernon + * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute + a "pseudo-random" value based on netif's MAC and some autoip fields. It's always + possible to define this macro in your own lwipopts.h to always use C library's + rand(). Note that autoip_create_rand_addr doesn't use this macro. + + 2007-06-28 Frédéric Bernon + * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option + LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications + in api_lib/api_msg (use pointers and not type with table, etc...) + + 2007-06-26 Simon Goldschmidt + * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload + for udp packets with no matching pcb. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match + could get udp input packets if the remote side matched. + + 2007-06-13 Simon Goldschmidt + * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get + changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0. + + 2007-06-13 Simon Goldschmidt + * api_msg.c: pcb_new sets conn->err if protocol is not implemented + -> netconn_new_..() does not allocate a new connection for unsupported + protocols. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * api_lib.c: change return expression in netconn_addr and netconn_peer, because + conn->err was reset to ERR_OK without any reasons (and error was lost)... + + 2007-06-13 Frédéric Bernon, Matthias Weisser + * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename + MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid + some macro names collision with some OS macros. + + 2007-06-11 Simon Goldschmidt + * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0, + create checksum over the complete packet. On RX, if it's < 8 (and not 0), + discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both + UDP & UDP Lite. + + 2007-06-11 Srinivas Gollakota & Oleg Tyshev + * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags" + where TCP flags wasn't initialized in tcp_keepalive. + + 2007-06-03 Simon Goldschmidt + * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function + registered, p->payload was modified without modifying p->len if sending + icmp_dest_unreach() (had no negative effect but was definitively wrong). + + 2007-06-03 Simon Goldschmidt + * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp + re-used the input pbuf even if that didn't have enough space to include the + link headers. Now the space is tested and a new pbuf is allocated for the + echo response packet if the echo request pbuf isn't big enough. + + 2007-06-01 Simon Goldschmidt + * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread. + + 2007-05-23 Frédéric Bernon + * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only + allocated by do_listen if success) and netconn_accept errors handling. In + most of api_lib functions, we replace some errors checkings like "if (conn==NULL)" + by ASSERT, except for netconn_delete. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return + an error code if it's impossible to fetch a pbuf on a TCP connection (and not + directly close the recvmbox). + + 2007-05-22 Simon Goldschmidt + * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of + bound but unconnected (and non-listening) tcp_pcbs. + + 2007-05-22 Frédéric Bernon + * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only + used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of + sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features + like "sys_timeout" in their application threads. + + 2007-05-22 Frédéric Bernon + * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see + which parameters are used by which do_xxx function, and to avoid "misusing" + parameters (patch #5938). + + 2007-05-22 Simon Goldschmidt + * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938: + changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto + is only 8 bits wide. This affects the api, as there, the protocol was + u16_t, too. + + 2007-05-18 Simon Goldschmidt + * memp.c: addition to patch #5913: smaller pointer was returned but + memp_memory was the same size -> did not save memory. + + 2007-05-16 Simon Goldschmidt + * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns + != ERR_OK. + + 2007-05-16 Simon Goldschmidt + * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same + as the one of the netif used for sending to prevent sending from old + addresses after a netif address gets changed (partly fixes bug #3168). + + 2007-05-16 Frédéric Bernon + * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work + with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in + tcpip_init) because we have to be sure that network interfaces are already + added (mac filter is updated only in igmp_init for the moment). + + 2007-05-16 Simon Goldschmidt + * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls + into sys_arch_sem_wait calls to prevent timers from running while waiting + for the heap. This fixes bug #19167. + + 2007-05-13 Simon Goldschmidt + * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines + for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from + tcp.h to sockets.h. + + 2007-05-07 Simon Goldschmidt + * mem.c: Another attempt to fix bug #17922. + + 2007-05-04 Simon Goldschmidt + * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy() + implementation so that it can be reused (don't allocate the target + pbuf inside pbuf_copy()). + + 2007-05-04 Simon Goldschmidt + * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem + to save a little RAM (next pointer of memp is not used while not in pool). + + 2007-05-03 "maq" + * sockets.c: Fix ioctl FIONREAD when some data remains from last recv. + (patch #3574). + + 2007-04-23 Simon Goldschmidt + * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results + in NULL reference for incoming TCP packets". Loopif has to be configured + (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input() + (multithreading environments, e.g. netif->input() = tcpip_input()) or + putting packets on a list that is fed to the stack by calling loopif_poll() + (single-thread / NO_SYS / polling environment where e.g. + netif->input() = ip_input). + + 2007-04-17 Jonathan Larmour + * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold + the difference between two u16_t's. + * sockets.h: FD_SETSIZE needs to match number of sockets, which is + MEMP_NUM_NETCONN in sockets.c right now. + + 2007-04-12 Jonathan Larmour + * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580). + + 2007-04-12 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission + timer is reset to fix bug#19434, with help from Oleg Tyshev. + + 2007-04-11 Simon Goldschmidt + * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than + previously thought need to be copied (everything but PBUF_ROM!). Cleaned up + pbuf.c: removed functions no needed any more (by etharp). + + 2007-04-11 Kieran Mansley + * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix + "Constant is long" warnings with 16bit compilers. Contributed by + avatar@mmlab.cse.yzu.edu.tw + + 2007-04-05 Frédéric Bernon, Jonathan Larmour + * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on + the mailbox is active". Now, the post is only done during a connect, and do_send, + do_write and do_join_leave_group don't do anything if a previous error was signaled. + + 2007-04-03 Frédéric Bernon + * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output + packets. See patch #5834. + + 2007-03-30 Frédéric Bernon + * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add + missing pcb allocations checking (in do_bind, and for each raw_new). Fix style. + + 2007-03-30 Frédéric Bernon + * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with + others environment defines (these were too "generic"). + + 2007-03-28 Frédéric Bernon + * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call + result and can cause a crash. lwip_send now check netbuf_ref result. + + 2007-03-28 Simon Goldschmidt + * sockets.c Remove "#include " from sockets.c to avoid multiple + definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is + defined. This is the way it should have been already (looking at + doc/sys_arch.txt) + + 2007-03-28 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS + + IP and TCP headers *and* physical link headers + + 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov) + * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause + to send some garbage. It is not a definitive solution, but the patch does solve + the problem for most cases. + + 2007-03-22 Frédéric Bernon + * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used). + + 2007-03-22 Frédéric Bernon + * api_lib.c: somes resources couldn't be freed if there was errors during + netconn_new_with_proto_and_callback. + + 2007-03-22 Frédéric Bernon + * ethernetif.c: update netif->input calls to check return value. In older ports, + it's a good idea to upgrade them, even if before, there could be another problem + (access to an uninitialized mailbox). + + 2007-03-21 Simon Goldschmidt + * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed + by casting to unsigned). + + 2007-03-21 Frédéric Bernon + * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from + api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a + dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call. + Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a + faster and more reliable communication between api_lib and tcpip. + + 2007-03-21 Frédéric Bernon + * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0. + + 2007-03-21 Frédéric Bernon + * api_msg.c, igmp.c, igmp.h: Fix C++ style comments + + 2007-03-21 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS + + IP and TCP headers + + 2007-03-21 Kieran Mansley + * Fix all uses of pbuf_header to check the return value. In some + cases just assert if it fails as I'm not sure how to fix them, but + this is no worse than before when they would carry on regardless + of the failure. + + 2007-03-21 Kieran Mansley + * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and + comment out missing header include in icmp.c + + 2007-03-20 Frédéric Bernon + * memp.h, stats.c: Fix stats_display function where memp_names table wasn't + synchronized with memp.h. + + 2007-03-20 Frédéric Bernon + * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input, + tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with + network interfaces. Also fix a compiler warning. + + 2007-03-20 Kieran Mansley + * udp.c: Only try and use pbuf_header() to make space for headers if + not a ROM or REF pbuf. + + 2007-03-19 Frédéric Bernon + * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg() + and api_msg_post(). + + 2007-03-19 Frédéric Bernon + * Remove unimplemented "memp_realloc" function from memp.h. + + 2007-03-11 Simon Goldschmidt + * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused + memory corruption. + + 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov) + * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251 + (missing `const' qualifier in socket functions), to get more compatible to + standard POSIX sockets. + + 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov) + * sockets.c: Add asserts inside bind, connect and sendto to check input + parameters. Remove excessive set_errno() calls after get_socket(), because + errno is set inside of get_socket(). Move last sock_set_errno() inside + lwip_close. + + 2007-03-09 Simon Goldschmidt + * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory + was allocated too small. + + 2007-03-06 Simon Goldschmidt + * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect + the stack from concurrent access. + + 2007-03-06 Frédéric Bernon, Dmitry Potapov + * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy + call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input(). + + 2007-03-06 Simon Goldschmidt + * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files + if IP_FRAG == 0 and IP_REASSEMBLY == 0 + + 2007-03-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration + option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput. + Allow to do ARP processing for incoming packets inside tcpip_thread + (protecting ARP layer against concurrent access). You can also disable + old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0. + Older ports have to use tcpip_ethinput. + + 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov) + * err.h, err.c: fixed compiler warning "initialization dircards qualifiers + from pointer target type" + + 2007-03-05 Frédéric Bernon + * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES, + ETHARP_TRUST_IP_MAC, review SO_REUSE) + + 2007-03-04 Frédéric Bernon + * api_msg.c: Remove some compiler warnings : parameter "pcb" was never + referenced. + + 2007-03-04 Frédéric Bernon + * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from + Dmitry Potapov). + The api_msg struct stay on the stack (not moved to netconn struct). + + 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov) + * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if + SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available) + Also fixed cast warning in pbuf_alloc() + + 2007-03-04 Simon Goldschmidt + * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt + existing pbuf chain when enqueuing multiple pbufs to a pending ARP request + + 2007-03-03 Frédéric Bernon + * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;" + It is static, and never used in udp.c except udp_init(). + + 2007-03-02 Simon Goldschmidt + * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from + tcpip_thread() to tcpip_init(). This way, raw API connections can be + initialized before tcpip_thread is running (e.g. before OS is started) + + 2007-03-02 Frédéric Bernon + * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call + interval. + + 2007-02-28 Kieran Mansley + * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved + outside the region of the pbuf by pbuf_header() + + 2007-02-28 Kieran Mansley + * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero + when supplied timeout is also non-zero + +(STABLE-1.2.0) + + 2006-12-05 Leon Woestenberg + * CHANGELOG: Mention STABLE-1.2.0 release. + + ++ New features: + + 2006-12-01 Christiaan Simons + * mem.h, opt.h: Added MEM_LIBC_MALLOC option. + Note this is a workaround. Currently I have no other options left. + + 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour) + * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define + to include/lwip/opt.h. + * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL. + Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h. + * opt.h: Add above new options. + + 2006-08-18 Christiaan Simons + * tcp_{in,out}.c: added SNMP counters. + * ipv4/ip.c: added SNMP counters. + * ipv4/ip_frag.c: added SNMP counters. + + 2006-08-08 Christiaan Simons + * etharp.{c,h}: added etharp_find_addr() to read + (stable) ethernet/IP address pair from ARP table + + 2006-07-14 Christiaan Simons + * mib_structs.c: added + * include/lwip/snmp_structs.h: added + * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct + + 2006-07-06 Christiaan Simons + * snmp/asn1_{enc,dec}.c added + * snmp/mib2.c added + * snmp/msg_{in,out}.c added + * include/lwip/snmp_asn1.h added + * include/lwip/snmp_msg.h added + * doc/snmp_agent.txt added + + 2006-03-29 Christiaan Simons + * inet.c, inet.h: Added platform byteswap support. + Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and + optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros. + + ++ Bug fixes: + + 2006-11-30 Christiaan Simons + * dhcp.c: Fixed false triggers of request_timeout. + + 2006-11-28 Christiaan Simons + * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags. + + 2006-10-11 Christiaan Simons + * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h: + Partially accepted patch #5449 for ANSI C compatibility / build fixes. + * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol + identifier from 170 to 136 (bug #17574). + + 2006-10-10 Christiaan Simons + * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice. + + 2006-08-17 Christiaan Simons + * udp.c: Fixed bug #17200, added check for broadcast + destinations for PCBs bound to a unicast address. + + 2006-08-07 Christiaan Simons + * api_msg.c: Flushing TCP output in do_close() (bug #15926). + + 2006-06-27 Christiaan Simons + * api_msg.c: Applied patch for cold case (bug #11135). + In accept_function() ensure newconn->callback is always initialized. + + 2006-06-15 Christiaan Simons + * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748), + facilitate printing of mem_size_t and u16_t statistics. + + 2006-06-14 Christiaan Simons + * api_msg.c: Applied patch #5146 to handle allocation failures + in accept() by Kevin Lawson. + + 2006-05-26 Christiaan Simons + * api_lib.c: Removed conn->sem creation and destruction + from netconn_write() and added sys_sem_new to netconn_new_*. + +(STABLE-1_1_1) + + 2006-03-03 Christiaan Simons + * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap + access and added pbuf_alloc() return value checks. + + 2006-01-01 Leon Woestenberg + * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is + now handled by the checksum routine properly. + + 2006-02-27 Leon Woestenberg + * pbuf.c: Fix alignment; pbuf_init() would not work unless + pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.) + + 2005-12-20 Leon Woestenberg + * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch + submitted by Mitrani Hiroshi. + + 2005-12-15 Christiaan Simons + * inet.c: Disabled the added summing routine to preserve code space. + + 2005-12-14 Leon Woestenberg + * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson. + Added Curt McDowell's optimized checksumming routine for future + inclusion. Need to create test case for unaliged, aligned, odd, + even length combination of cases on various endianess machines. + + 2005-12-09 Christiaan Simons + * inet.c: Rewrote standard checksum routine in proper portable C. + + 2005-11-25 Christiaan Simons + * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only. + * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t, + u32_t, s32_t typedefs. This solves most debug word-length assumes. + + 2005-07-17 Leon Woestenberg + * inet.c: Fixed unaligned 16-bit access in the standard checksum + routine by Peter Jolasson. + * slipif.c: Fixed implementation assumption of single-pbuf datagrams. + + 2005-02-04 Leon Woestenberg + * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch. + * tcp_{out|in}.c: Applied patch fixing unaligned access. + + 2005-01-04 Leon Woestenberg + * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement. + + 2005-01-03 Leon Woestenberg + * udp.c: UDP pcb->recv() was called even when it was NULL. + +(STABLE-1_1_0) + + 2004-12-28 Leon Woestenberg + * etharp.*: Disabled multiple packets on the ARP queue. + This clashes with TCP queueing. + + 2004-11-28 Leon Woestenberg + * etharp.*: Fixed race condition from ARP request to ARP timeout. + Halved the ARP period, doubled the period counts. + ETHARP_MAX_PENDING now should be at least 2. This prevents + the counter from reaching 0 right away (which would allow + too little time for ARP responses to be received). + + 2004-11-25 Leon Woestenberg + * dhcp.c: Decline messages were not multicast but unicast. + * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD. + Do not try hard to insert arbitrary packet's source address, + etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. + etharp_query() now always DOES call ETHARP_TRY_HARD so that users + querying an address will see it appear in the cache (DHCP could + suffer from this when a server invalidly gave an in-use address.) + * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are + comparing network addresses (identifiers), not the network masks + themselves. + * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given + IP address actually belongs to the network of the given interface. + + 2004-11-24 Kieran Mansley + * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state. + +(STABLE-1_1_0-RC1) + + 2004-10-16 Kieran Mansley + * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately, + even if one is already pending, if the rcv_wnd is above a threshold + (currently TCP_WND/2). This avoids waiting for a timer to expire to send a + delayed ACK in order to open the window if the stack is only receiving data. + + 2004-09-12 Kieran Mansley + * tcp*.*: Retransmit time-out handling improvement by Sam Jansen. + + 2004-08-20 Tony Mountifield + * etharp.c: Make sure the first pbuf queued on an ARP entry + is properly ref counted. + + 2004-07-27 Tony Mountifield + * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler + warnings about comparison. + * pbuf.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. Closed an unclosed comment. + * tcp.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. + * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons(). + * inet.c: Added a couple of casts to quiet the compiler. + No need to test isascii(c) before isdigit(c) or isxdigit(c). + + 2004-07-22 Tony Mountifield + * inet.c: Made data types consistent in inet_ntoa(). + Added casts for return values of checksum routines, to pacify compiler. + * ip_frag.c, tcp_out.c, sockets.c, pbuf.c + Small corrections to some debugging statements, to pacify compiler. + + 2004-07-21 Tony Mountifield + * etharp.c: Removed spurious semicolon and added missing end-of-comment. + * ethernetif.c Updated low_level_output() to match prototype for + netif->linkoutput and changed low_level_input() similarly for consistency. + * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype + of raw_recv() in raw.h and so avoid compiler error. + * sockets.c: Added trivial (int) cast to keep compiler happier. + * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros. + +(STABLE-1_0_0) + + ++ Changes: + + 2004-07-05 Leon Woestenberg + * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure + your cc.h file defines this either 1 or 0. If non-defined, + defaults to 1. + * .c: Added and includes where used. + * etharp.c: Made some array indices unsigned. + + 2004-06-27 Leon Woestenberg + * netif.*: Added netif_set_up()/down(). + * dhcp.c: Changes to restart program flow. + + 2004-05-07 Leon Woestenberg + * etharp.c: In find_entry(), instead of a list traversal per candidate, do a + single-pass lookup for different candidates. Should exploit locality. + + 2004-04-29 Leon Woestenberg + * tcp*.c: Cleaned up source comment documentation for Doxygen processing. + * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC. + * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by + the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option. + + ++ Bug fixes: + + 2004-04-27 Leon Woestenberg + * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution + suggested by Timmy Brolin. Fix for 32-bit processors that cannot access + non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix + is to prefix the 14-bit Ethernet headers with two padding bytes. + + 2004-04-23 Leon Woestenberg + * ip_addr.c: Fix in the ip_addr_isbroadcast() check. + * etharp.c: Fixed the case where the packet that initiates the ARP request + is not queued, and gets lost. Fixed the case where the packets destination + address is already known; we now always queue the packet and perform an ARP + request. + +(STABLE-0_7_0) + + ++ Bug fixes: + + * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition. + * Fixed TCP bug in dequeueing of FIN from out of order segment queue. + * Fixed two possible NULL references in rare cases. + +(STABLE-0_6_6) + + ++ Bug fixes: + + * Fixed DHCP which did not include the IP address in DECLINE messages. + + ++ Changes: + + * etharp.c has been hauled over a bit. + +(STABLE-0_6_5) + + ++ Bug fixes: + + * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic. + * Packets sent from ARP queue had invalid source hardware address. + + ++ Changes: + + * Pass-by ARP requests do now update the cache. + + ++ New features: + + * No longer dependent on ctype.h. + * New socket options. + * Raw IP pcb support. + +(STABLE-0_6_4) + + ++ Bug fixes: + + * Some debug formatters and casts fixed. + * Numereous fixes in PPP. + + ++ Changes: + + * DEBUGF now is LWIP_DEBUGF + * pbuf_dechain() has been re-enabled. + * Mentioned the changed use of CVS branches in README. + +(STABLE-0_6_3) + + ++ Bug fixes: + + * Fixed pool pbuf memory leak in pbuf_alloc(). + Occured if not enough PBUF_POOL pbufs for a packet pbuf chain. + Reported by Savin Zlobec. + + * PBUF_POOL chains had their tot_len field not set for non-first + pbufs. Fixed in pbuf_alloc(). + + ++ New features: + + * Added PPP stack contributed by Marc Boucher + + ++ Changes: + + * Now drops short packets for ICMP/UDP/TCP protocols. More robust. + + * ARP queueuing now queues the latest packet instead of the first. + This is the RFC recommended behaviour, but can be overridden in + lwipopts.h. + +(0.6.2) + + ++ Bugfixes: + + * TCP has been fixed to deal with the new use of the pbuf->ref + counter. + + * DHCP dhcp_inform() crash bug fixed. + + ++ Changes: + + * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed + pbuf_refresh(). This has sped up pbuf pool operations considerably. + Implemented by David Haas. + +(0.6.1) + + ++ New features: + + * The packet buffer implementation has been enhanced to support + zero-copy and copy-on-demand for packet buffers which have their + payloads in application-managed memory. + Implemented by David Haas. + + Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy + if an outgoing packet can be directly sent on the link, or perform + a copy-on-demand when necessary. + + The application can safely assume the packet is sent, and the RAM + is available to the application directly after calling udp_send() + or similar function. + + ++ Bugfixes: + + * ARP_QUEUEING should now correctly work for all cases, including + PBUF_REF. + Implemented by Leon Woestenberg. + + ++ Changes: + + * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer + to a '0.0.0.0' IP address. + + * The packet buffer implementation is changed. The pbuf->ref counter + meaning has changed, and several pbuf functions have been + adapted accordingly. + + * netif drivers have to be changed to set the hardware address length field + that must be initialized correctly by the driver (hint: 6 for Ethernet MAC). + See the contrib/ports/c16x cs8900 driver as a driver example. + + * netif's have a dhcp field that must be initialized to NULL by the driver. + See the contrib/ports/c16x cs8900 driver as a driver example. + +(0.5.x) This file has been unmaintained up to 0.6.1. All changes are + logged in CVS but have not been explained here. + +(0.5.3) Changes since version 0.5.2 + + ++ Bugfixes: + + * memp_malloc(MEMP_API_MSG) could fail with multiple application + threads because it wasn't protected by semaphores. + + ++ Other changes: + + * struct ip_addr now packed. + + * The name of the time variable in arp.c has been changed to ctime + to avoid conflicts with the time() function. + +(0.5.2) Changes since version 0.5.1 + + ++ New features: + + * A new TCP function, tcp_tmr(), now handles both TCP timers. + + ++ Bugfixes: + + * A bug in tcp_parseopt() could cause the stack to hang because of a + malformed TCP option. + + * The address of new connections in the accept() function in the BSD + socket library was not handled correctly. + + * pbuf_dechain() did not update the ->tot_len field of the tail. + + * Aborted TCP connections were not handled correctly in all + situations. + + ++ Other changes: + + * All protocol header structs are now packed. + + * The ->len field in the tcp_seg structure now counts the actual + amount of data, and does not add one for SYN and FIN segments. + +(0.5.1) Changes since version 0.5.0 + + ++ New features: + + * Possible to run as a user process under Linux. + + * Preliminary support for cross platform packed structs. + + * ARP timer now implemented. + + ++ Bugfixes: + + * TCP output queue length was badly initialized when opening + connections. + + * TCP delayed ACKs were not sent correctly. + + * Explicit initialization of BSS segment variables. + + * read() in BSD socket library could drop data. + + * Problems with memory alignment. + + * Situations when all TCP buffers were used could lead to + starvation. + + * TCP MSS option wasn't parsed correctly. + + * Problems with UDP checksum calculation. + + * IP multicast address tests had endianess problems. + + * ARP requests had wrong destination hardware address. + + ++ Other changes: + + * struct eth_addr changed from u16_t[3] array to u8_t[6]. + + * A ->linkoutput() member was added to struct netif. + + * TCP and UDP ->dest_* struct members where changed to ->remote_*. + + * ntoh* macros are now null definitions for big endian CPUs. + +(0.5.0) Changes since version 0.4.2 + + ++ New features: + + * Redesigned operating system emulation layer to make porting easier. + + * Better control over TCP output buffers. + + * Documenation added. + + ++ Bugfixes: + + * Locking issues in buffer management. + + * Bugfixes in the sequential API. + + * IP forwarding could cause memory leakage. This has been fixed. + + ++ Other changes: + + * Directory structure somewhat changed; the core/ tree has been + collapsed. + +(0.4.2) Changes since version 0.4.1 + + ++ New features: + + * Experimental ARP implementation added. + + * Skeleton Ethernet driver added. + + * Experimental BSD socket API library added. + + ++ Bugfixes: + + * In very intense situations, memory leakage could occur. This has + been fixed. + + ++ Other changes: + + * Variables named "data" and "code" have been renamed in order to + avoid name conflicts in certain compilers. + + * Variable++ have in appliciable cases been translated to ++variable + since some compilers generate better code in the latter case. + +(0.4.1) Changes since version 0.4 + + ++ New features: + + * TCP: Connection attempts time out earlier than data + transmissions. Nagle algorithm implemented. Push flag set on the + last segment in a burst. + + * UDP: experimental support for UDP-Lite extensions. + + ++ Bugfixes: + + * TCP: out of order segments were in some cases handled incorrectly, + and this has now been fixed. Delayed acknowledgements was broken + in 0.4, has now been fixed. Binding to an address that is in use + now results in an error. Reset connections sometimes hung an + application; this has been fixed. + + * Checksum calculation sometimes failed for chained pbufs with odd + lengths. This has been fixed. + + * API: a lot of bug fixes in the API. The UDP API has been improved + and tested. Error reporting and handling has been + improved. Logical flaws and race conditions for incoming TCP + connections has been found and removed. + + * Memory manager: alignment issues. Reallocating memory sometimes + failed, this has been fixed. + + * Generic library: bcopy was flawed and has been fixed. + + ++ Other changes: + + * API: all datatypes has been changed from generic ones such as + ints, to specified ones such as u16_t. Functions that return + errors now have the correct type (err_t). + + * General: A lot of code cleaned up and debugging code removed. Many + portability issues have been fixed. + + * The license was changed; the advertising clause was removed. + + * C64 port added. + + * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri + Kosunen, Mikael Caleres, and Frits Wilmink for reporting and + fixing bugs! + +(0.4) Changes since version 0.3.1 + + * Memory management has been radically changed; instead of + allocating memory from a shared heap, memory for objects that are + rapidly allocated and deallocated is now kept in pools. Allocation + and deallocation from those memory pools is very fast. The shared + heap is still present but is used less frequently. + + * The memory, memory pool, and packet buffer subsystems now support + 4-, 2-, or 1-byte alignment. + + * "Out of memory" situations are handled in a more robust way. + + * Stack usage has been reduced. + + * Easier configuration of lwIP parameters such as memory usage, + TTLs, statistics gathering, etc. All configuration parameters are + now kept in a single header file "lwipopts.h". + + * The directory structure has been changed slightly so that all + architecture specific files are kept under the src/arch + hierarchy. + + * Error propagation has been improved, both in the protocol modules + and in the API. + + * The code for the RTXC architecture has been implemented, tested + and put to use. + + * Bugs have been found and corrected in the TCP, UDP, IP, API, and + the Internet checksum modules. + + * Bugs related to porting between a 32-bit and a 16-bit architecture + have been found and corrected. + + * The license has been changed slightly to conform more with the + original BSD license, including the advertisement clause. + +(0.3.1) Changes since version 0.3 + + * Fix of a fatal bug in the buffer management. Pbufs with allocated + RAM never returned the RAM when the pbuf was deallocated. + + * TCP congestion control, window updates and retransmissions did not + work correctly. This has now been fixed. + + * Bugfixes in the API. + +(0.3) Changes since version 0.2 + + * New and improved directory structure. All include files are now + kept in a dedicated include/ directory. + + * The API now has proper error handling. A new function, + netconn_err(), now returns an error code for the connection in + case of errors. + + * Improvements in the memory management subsystem. The system now + keeps a pointer to the lowest free memory block. A new function, + mem_malloc2() tries to allocate memory once, and if it fails tries + to free some memory and retry the allocation. + + * Much testing has been done with limited memory + configurations. lwIP now does a better job when overloaded. + + * Some bugfixes and improvements to the buffer (pbuf) subsystem. + + * Many bugfixes in the TCP code: + + - Fixed a bug in tcp_close(). + + - The TCP receive window was incorrectly closed when out of + sequence segments was received. This has been fixed. + + - Connections are now timed-out of the FIN-WAIT-2 state. + + - The initial congestion window could in some cases be too + large. This has been fixed. + + - The retransmission queue could in some cases be screwed up. This + has been fixed. + + - TCP RST flag now handled correctly. + + - Out of sequence data was in some cases never delivered to the + application. This has been fixed. + + - Retransmitted segments now contain the correct acknowledgment + number and advertised window. + + - TCP retransmission timeout backoffs are not correctly computed + (ala BSD). After a number of retransmissions, TCP now gives up + the connection. + + * TCP connections now are kept on three lists, one for active + connections, one for listening connections, and one for + connections that are in TIME-WAIT. This greatly speeds up the fast + timeout processing for sending delayed ACKs. + + * TCP now provides proper feedback to the application when a + connection has been successfully set up. + + * More comments have been added to the code. The code has also been + somewhat cleaned up. + +(0.2) Initial public release. diff --git a/component/common/network/lwip/lwip_v1.3.2/COPYING b/component/common/network/lwip/lwip_v1.3.2/COPYING new file mode 100644 index 0000000..e23898b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 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 + * + */ + + diff --git a/component/common/network/lwip/lwip_v1.3.2/FILES b/component/common/network/lwip/lwip_v1.3.2/FILES new file mode 100644 index 0000000..6625319 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/FILES @@ -0,0 +1,4 @@ +src/ - The source code for the lwIP TCP/IP stack. +doc/ - The documentation for lwIP. + +See also the FILES file in each subdirectory. diff --git a/component/common/network/lwip/lwip_v1.3.2/README b/component/common/network/lwip/lwip_v1.3.2/README new file mode 100644 index 0000000..a62cc4f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/README @@ -0,0 +1,89 @@ +INTRODUCTION + +lwIP is a small independent implementation of the TCP/IP protocol +suite that has been developed by Adam Dunkels at the Computer and +Networks Architectures (CNA) lab at the Swedish Institute of Computer +Science (SICS). + +The focus of the lwIP TCP/IP implementation is to reduce the RAM usage +while still having a full scale TCP. This making lwIP suitable for use +in embedded systems with tens of kilobytes of free RAM and room for +around 40 kilobytes of code ROM. + +FEATURES + + * IP (Internet Protocol) including packet forwarding over multiple network + interfaces + * ICMP (Internet Control Message Protocol) for network maintenance and debugging + * IGMP (Internet Group Management Protocol) for multicast traffic management + * UDP (User Datagram Protocol) including experimental UDP-lite extensions + * TCP (Transmission Control Protocol) with congestion control, RTT estimation + and fast recovery/fast retransmit + * Specialized raw/native API for enhanced performance + * Optional Berkeley-like socket API + * DNS (Domain names resolver) + * SNMP (Simple Network Management Protocol) + * DHCP (Dynamic Host Configuration Protocol) + * AUTOIP (for IPv4, conform with RFC 3927) + * PPP (Point-to-Point Protocol) + * ARP (Address Resolution Protocol) for Ethernet + +LICENSE + +lwIP is freely available under a BSD license. + +DEVELOPMENT + +lwIP has grown into an excellent TCP/IP stack for embedded devices, +and developers using the stack often submit bug fixes, improvements, +and additions to the stack to further increase its usefulness. + +Development of lwIP is hosted on Savannah, a central point for +software development, maintenance and distribution. Everyone can +help improve lwIP by use of Savannah's interface, CVS and the +mailing list. A core team of developers will commit changes to the +CVS source tree. + +The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and +contributions (such as platform ports) are in the 'contrib' module. + +See doc/savannah.txt for details on CVS server access for users and +developers. + +Last night's CVS tar ball can be downloaded from: + http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING] + +The current CVS trees are web-browsable: + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/ + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/ + +Submit patches and bugs via the lwIP project page: + http://savannah.nongnu.org/projects/lwip/ + + +DOCUMENTATION + +The original out-dated homepage of lwIP and Adam Dunkels' papers on +lwIP are at the official lwIP home page: + http://www.sics.se/~adam/lwip/ + +Self documentation of the source code is regularly extracted from the +current CVS sources and is available from this web page: + http://www.nongnu.org/lwip/ + +There is now a constantly growin wiki about lwIP at + http://lwip.wikia.com/wiki/LwIP_Wiki + +Also, there are mailing lists you can subscribe at + http://savannah.nongnu.org/mail/?group=lwip +plus searchable archives: + http://lists.nongnu.org/archive/html/lwip-users/ + http://lists.nongnu.org/archive/html/lwip-devel/ + +Reading Adam's papers, the files in docs/, browsing the source code +documentation and browsing the mailing list archives is a good way to +become familiar with the design of lwIP. + +Adam Dunkels +Leon Woestenberg + diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/FILES b/component/common/network/lwip/lwip_v1.3.2/doc/FILES new file mode 100644 index 0000000..05d356f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/FILES @@ -0,0 +1,6 @@ +savannah.txt - How to obtain the current development source code. +contrib.txt - How to contribute to lwIP as a developer. +rawapi.txt - The documentation for the core API of lwIP. + Also provides an overview about the other APIs and multithreading. +snmp_agent.txt - The documentation for the lwIP SNMP agent. +sys_arch.txt - The documentation for a system abstraction layer of lwIP. diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/contrib.txt b/component/common/network/lwip/lwip_v1.3.2/doc/contrib.txt new file mode 100644 index 0000000..39596fc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/contrib.txt @@ -0,0 +1,63 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two) + can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded + as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead + for reporting a compiler warning fix. +6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/rawapi.txt b/component/common/network/lwip/lwip_v1.3.2/doc/rawapi.txt new file mode 100644 index 0000000..8eec6e7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/rawapi.txt @@ -0,0 +1,478 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len, + u8_t copy) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The copy argument is either + 0 or 1 and indicates whether the new memory should be allocated for + the data to be copied into. If the argument is 0, no new memory + should be allocated and the data should only be referenced by + pointer. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, u8_t interval, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb)) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct tcp_pcb *pcb) + + Aborts the connection by sending a RST (reset) segment to the remote + host. The pcb is deallocated. This function never fails. + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + struct ip_addr *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, struct ip_addr *ipaddr, + struct ip_addr *netmask, struct ip_addr *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/savannah.txt b/component/common/network/lwip/lwip_v1.3.2/doc/savannah.txt new file mode 100644 index 0000000..409905b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/snmp_agent.txt b/component/common/network/lwip/lwip_v1.3.2/doc/snmp_agent.txt new file mode 100644 index 0000000..9b58616 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads then the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for an dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/component/common/network/lwip/lwip_v1.3.2/doc/sys_arch.txt b/component/common/network/lwip/lwip_v1.3.2/doc/sys_arch.txt new file mode 100644 index 0000000..66310a9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/doc/sys_arch.txt @@ -0,0 +1,228 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- sys_sem_t sys_sem_new(u8_t count) + + Creates and returns a new semaphore. The "count" argument specifies + the initial state of the semaphore. + +- void sys_sem_free(sys_sem_t sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- sys_mbox_t sys_mbox_new(int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + +- void sys_mbox_free(sys_mbox_t mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- struct sys_timeouts *sys_arch_timeouts(void) + + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is repressented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single thread sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cc.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cc.h new file mode 100644 index 0000000..be882d9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cc.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned int u32_t; +typedef signed long s32_t; +typedef u32_t mem_ptr_t; +typedef int sys_prot_t; + +#define U16_F "d" +#define S16_F "d" +#define X16_F "x" +#define U32_F "d" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + +/* define compiler specific symbols */ +#if defined (__ICCARM__) +#if !defined (__IARSTDLIB__) +#define _STRING +#ifndef memcmp +#define memcmp(dst, src, sz) _memcmp(dst, src, sz) +#endif +#ifndef memset +#define memset(dst, val, sz) _memset(dst, val, sz) +#endif +#ifndef memcpy +#define memcpy(dst, src, sz) _memcpy(dst, src, sz) +#endif +#endif // __IARSTDLIB__ + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cpu.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/epstruct.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/init.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/lib.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/perf.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/arch/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.c new file mode 100644 index 0000000..a0f38bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.c @@ -0,0 +1,274 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +#include +#include + +#define netifMTU (1500) +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) + + +#ifdef CONFIG_CONCURRENT_MODE +#define IF2NAME0 'r' +#define IF2NAME1 '2' +#endif + +static void arp_timer(void *arg); + + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ + +static void low_level_init(struct netif *netif) +{ + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set netif maximum transfer unit */ + netif->mtu = 1500; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Wlan interface is initialized later */ +} + + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + + + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + if (sg_len) + rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len); + + return ERR_OK; +} + + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +//static struct pbuf * low_level_input(struct netif *netif){} + + +/** + * This function is the ethernetif_input task, it is processed when a packet + * is ready to be read from the interface. It uses the function low_level_input() + * that should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +//void ethernetif_input( void * pvParameters ) + + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); + +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + + return ERR_OK; +} + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.c new file mode 100644 index 0000000..1793f87 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +sys_mbox_t sys_mbox_new(int size) +{ + xQueueHandle mbox; + + ( void ) size; + + mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + + return mbox; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t mbox) +{ + if( uxQueueMessagesWaiting( mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t mbox, void *data) +{ + while ( xQueueSendToBack(mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} + +/*-----------------------------------------------------------------------------------*/ +// Creates and returns a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +sys_sem_t sys_sem_new(u8_t count) +{ + xSemaphoreHandle xSemaphore; + + vSemaphoreCreateBinary( xSemaphore ); + + if( xSemaphore == NULL ) + { + +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + + return SYS_SEM_NULL; // TODO need assert + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(xSemaphore,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return xSemaphore; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake( sem, portMAX_DELAY ) != pdTRUE ){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t sem) +{ + xSemaphoreGive( sem ); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete( sem ); +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts *sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} + +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( const portCHAR * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/realtek/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cc.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cc.h new file mode 100644 index 0000000..3ca260f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cc.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +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 u32_t mem_ptr_t; +typedef int sys_prot_t; + + +#define U16_F "hu" +#define S16_F "d" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + + + + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cpu.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/epstruct.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/init.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/lib.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/perf.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/arch/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.c new file mode 100644 index 0000000..9c6f99c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.c @@ -0,0 +1,649 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/icmp.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +#include "main.h" // for the definition of CONFIG_WLAN + + +#if !CONFIG_WLAN +#include "stm32f2x7_eth.h" +#else +#include +#endif +#include + +#ifdef CONFIG_DONT_CARE_TP +#define netifMTU (576) +#else +#define netifMTU (1500) +#endif +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) +#define FAKE_PING_REPLY 0 + +#if !CONFIG_WLAN +/* Define those to better describe your network interface. */ +#define IFNAME0 's' +#define IFNAME1 't' + +static struct netif *s_pxNetIf = NULL; +xSemaphoreHandle s_xSemaphore = NULL; + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + +static void ethernetif_input( void * pvParameters ); +#endif + +static void arp_timer(void *arg); + + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ + +static void low_level_init(struct netif *netif) +{ + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + +#if !CONFIG_WLAN + /* set netif MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; +#endif + + /* set netif maximum transfer unit */ + netif->mtu = netifMTU; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + +#if !CONFIG_WLAN + s_pxNetIf =netif; + + /* create binary semaphore used for informing ethernetif of frame reception */ + if (s_xSemaphore == NULL) + { + s_xSemaphore= xSemaphoreCreateCounting(20,0); + } + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* Enable Ethernet Rx interrrupt */ + { + uint32_t i; + for(i=0; itot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_arp = (struct etharp_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_arp = (struct etharp_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = fake_src_mac; + q_arp->opcode = htons(ARP_REPLY); + q_arp->shwaddr = fake_src_mac; + q_arp->sipaddr = p_arp->dipaddr; + q_arp->dhwaddr = p_eth->src; + q_arp->dipaddr = p_arp->sipaddr; + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_arp_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake arp reply \n\r"); +} + +void fake_echo_reply(struct netif *netif, struct pbuf *p) +{ + struct pbuf *q, *tq, *tp; + int q_len, p_len; + struct eth_hdr *p_eth, *q_eth; + struct ip_hdr *p_ip, *q_ip; + struct icmp_echo_hdr *p_echo, *q_echo; + + // Allocate buffer to store received packet + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_ip = (struct ip_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_ip = (struct ip_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + p_echo = (struct icmp_echo_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + q_echo = (struct icmp_echo_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = p_eth->dest; + q_ip->src.addr = p_ip->dest.addr; + q_ip->dest.addr = p_ip->src.addr; + q_ip->_chksum = 0; + q_ip->_chksum = inet_chksum(q_ip, sizeof(struct ip_hdr)); + q_echo->type = ICMP_ER; + q_echo->code = 0; + q_echo->chksum = 0; + q_echo->chksum = inet_chksum(q_echo, q->tot_len - sizeof(struct eth_hdr) - sizeof(struct ip_hdr)); + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_echo_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake echo reply \n\r"); +} +#endif // #if FAKE_PING_REPLY + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ +#if !CONFIG_WLAN + static xSemaphoreHandle xTxSemaphore = NULL; + struct pbuf *q; + uint32_t l = 0; + u8 *buffer ; + + if (xTxSemaphore == NULL) + { + vSemaphoreCreateBinary (xTxSemaphore); + } + + if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME)) + { + buffer = (u8 *)(DMATxDescToSet->Buffer1Addr); + for(q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)&buffer[l], q->payload, q->len); + l = l + q->len; + } + ETH_Prepare_Transmit_Descriptors(l); + xSemaphoreGive(xTxSemaphore); + } +#else + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + +#if FAKE_PING_REPLY + { + char *header = p->payload; + if(header[12] == 0x08 && header[13] == 0x06) + { // arp packet + if(header[21] == 0x01) + { // arp request packet + printf("\n\rfake_ping: arp request packet."); + if(0) + { + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_arp_reply(netif, p); + return ERR_OK; + } + }else if(header[12] == 0x08 && header[13] == 0x00) + { // ip packet + if(header[15] == 0x00 && header[23] == 0x01) + { // icmp packet + printf("\n\rfake_ping: icmp packet."); + if(0){ + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_echo_reply(netif, p); + return ERR_OK; + } + } + } +#endif // #if FAKE_PING_REPLY + if (sg_len) + rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len); +#endif // #if !CONFIG_WLAN + + return ERR_OK; +} + + +#if !CONFIG_WLAN +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + u16_t len; + uint32_t l=0,i =0; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxNextDesc; + + p = NULL; + + /* Get received frame */ + frame = ETH_Get_Received_Frame_interrupt(); + + /* check that frame has no error */ + if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) + { + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + /* Copy received frame from ethernet driver buffer to stack buffer */ + if (p != NULL) + { + for (q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len); + l = l + q->len; + } + } + } + + /* Release descriptors to DMA */ + /* Check if received frame with multiple DMA buffer segments */ + if (DMA_RX_FRAME_infos->Seg_Count > 1) + { + DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; + } + else + { + DMARxNextDesc = frame.descriptor; + } + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxNextDesc->Status = ETH_DMARxDesc_OWN; + DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + + +/** + * This function is the ethernetif_input task, it is processed when a packet + * is ready to be read from the interface. It uses the function low_level_input() + * that should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +void ethernetif_input( void * pvParameters ) +{ + struct pbuf *p; + + for( ;; ) + { + if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) + { + p = low_level_input( s_pxNetIf ); + if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) + { + pbuf_free(p); + p=NULL; + } + } + } +} +#endif + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ +#if CONFIG_WLAN + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); +#endif +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if !CONFIG_WLAN + /* ethernet */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#else + /* wlan interface*/ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + +#endif // WLAN + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + + return ERR_OK; +} + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.c new file mode 100644 index 0000000..c2792a8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +sys_mbox_t sys_mbox_new(int size) +{ + xQueueHandle mbox; + + ( void ) size; + + mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + + return mbox; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t mbox) +{ + if( uxQueueMessagesWaiting( mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t mbox, void *data) +{ + while ( xQueueSendToBack(mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} + +/*-----------------------------------------------------------------------------------*/ +// Creates and returns a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +sys_sem_t sys_sem_new(u8_t count) +{ + xSemaphoreHandle xSemaphore; + + vSemaphoreCreateBinary( xSemaphore ); + + if( xSemaphore == NULL ) + { + +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + + return SYS_SEM_NULL; // TODO need assert + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(xSemaphore,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return xSemaphore; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake( sem, portMAX_DELAY ) != pdTRUE ){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t sem) +{ + xSemaphoreGive( sem ); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete( sem ); +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts *sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} + +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( char const * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.c b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.c new file mode 100644 index 0000000..5100fb2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.c @@ -0,0 +1,304 @@ +/** + * @file + * Ethernet Interface for standalone applications (without RTOS) - works only for + * ethernet polling mode (polling for ethernet frame reception) + * + */ + +/* + * 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/mem.h" +#include "netif/etharp.h" +#include "ethernetif.h" +#include "stm32f2x7_eth.h" +#include "main.h" +#include + +/* Network interface name */ +#define IFNAME0 's' +#define IFNAME1 't' + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Driver Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Driver Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + + + + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void low_level_init(struct netif *netif) +{ +#ifdef CHECKSUM_BY_HARDWARE + int i; +#endif + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + +#ifdef CHECKSUM_BY_HARDWARE + /* Enable the TCP, UDP and ICMP checksum insertion for the Tx frames */ + for(i=0; iBuffer1Addr); + + /* copy frame from pbufs to driver buffers */ + for(q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)&buffer[framelength], q->payload, q->len); + framelength = framelength + q->len; + } + + /* Note: padding and CRC for transmitted frame + are automatically inserted by DMA */ + + /* Prepare transmit descriptors to give to DMA*/ + ETH_Prepare_Transmit_Descriptors(framelength); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + u16_t len; + int l =0; + FrameTypeDef frame; + u8 *buffer; + uint32_t i=0; + __IO ETH_DMADESCTypeDef *DMARxNextDesc; + + + p = NULL; + + /* get received frame */ + frame = ETH_Get_Received_Frame(); + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + /* copy received frame to pbuf chain */ + if (p != NULL) + { + for (q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len); + l = l + q->len; + } + } + + /* Release descriptors to DMA */ + /* Check if frame with multiple DMA buffer segments */ + if (DMA_RX_FRAME_infos->Seg_Count > 1) + { + DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; + } + else + { + DMARxNextDesc = frame.descriptor; + } + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxNextDesc->Status = ETH_DMARxDesc_OWN; + DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +err_t ethernetif_input(struct netif *netif) +{ + err_t err; + struct pbuf *p; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + + /* no packet could be read, silently ignore this */ + if (p == NULL) return ERR_MEM; + + /* entry point to the LwIP stack */ + err = netif->input(p, netif); + + if (err != ERR_OK) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + return err; +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + + + diff --git a/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.h b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.h new file mode 100644 index 0000000..9ff1408 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/port/stm32f2x7/standalone/ethernetif.h @@ -0,0 +1,11 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +err_t ethernetif_init(struct netif *netif); +err_t ethernetif_input(struct netif *netif); + +#endif diff --git a/component/common/network/lwip/lwip_v1.3.2/src/api/api_lib.c b/component/common/network/lwip/lwip_v1.3.2/src/api/api_lib.c new file mode 100644 index 0000000..8947902 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/api_lib.c @@ -0,0 +1,568 @@ +/* + * @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; + TCPIP_APIMSG(&msg); + + if (conn->err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + LWIP_ASSERT("conn has no op_completed", conn->op_completed != SYS_SEM_NULL); + LWIP_ASSERT("conn has no recvmbox", conn->recvmbox != SYS_MBOX_NULL); + LWIP_ASSERT("conn->acceptmbox shouldn't exist", conn->acceptmbox == SYS_MBOX_NULL); + 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); + + conn->pcb.tcp = NULL; + netconn_free(conn); + + 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, struct ip_addr *addr, u16_t *port, u8_t local) +{ + struct api_msg msg; + + 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; + TCPIP_APIMSG(&msg); + + return conn->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, struct ip_addr *addr, u16_t port) +{ + struct api_msg msg; + + 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; + TCPIP_APIMSG(&msg); + return conn->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, struct ip_addr *addr, u16_t port) +{ + struct api_msg msg; + + 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 */ + tcpip_apimsg(&msg); + return conn->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; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_disconnect; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + return conn->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) +{ + struct api_msg msg; + + /* 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 */ + TCPIP_APIMSG(&msg); + return conn->err; +} + +/** + * Accept a new connection on a TCP listening netconn. + * + * @param conn the TCP listen netconn + * @return the newly accepted netconn or NULL on timeout + */ +struct netconn * +netconn_accept(struct netconn *conn) +{ + struct netconn *newconn; + + LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return NULL;); + LWIP_ERROR("netconn_accept: invalid acceptmbox", (conn->acceptmbox != SYS_MBOX_NULL), return NULL;); + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { + newconn = NULL; + } else +#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 TCP_LISTEN_BACKLOG + if (newconn != NULL) { + /* Let the stack know that we have accepted the connection. */ + struct api_msg msg; + msg.function = do_recv; + msg.msg.conn = conn; + TCPIP_APIMSG(&msg); + } +#endif /* TCP_LISTEN_BACKLOG */ + } + + return newconn; +} + +/** + * Receive data (in form of a netbuf containing a packet buffer) from a netconn + * + * @param conn the netconn from which to receive data + * @return a new netbuf containing received data or NULL on memory error or timeout + */ +struct netbuf * +netconn_recv(struct netconn *conn) +{ + struct api_msg msg; + struct netbuf *buf = NULL; + struct pbuf *p; + u16_t len; + + LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return NULL;); + + if (conn->recvmbox == SYS_MBOX_NULL) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal (ERR_CONN)?? */ + /* TCP listen conns don't have a recvmbox! */ + conn->err = ERR_CONN; + return NULL; + } + + if (ERR_IS_FATAL(conn->err)) { + return NULL; + } + + if (conn->type == NETCONN_TCP) { +#if LWIP_TCP + if (conn->state == NETCONN_LISTEN) { + /* @todo: should calling netconn_recv on a TCP listen conn be fatal?? */ + conn->err = ERR_CONN; + return NULL; + } + + buf = memp_malloc(MEMP_NETBUF); + + if (buf == NULL) { + conn->err = ERR_MEM; + return NULL; + } + +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + memp_free(MEMP_NETBUF, buf); + conn->err = ERR_TIMEOUT; + return NULL; + } +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&p, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_DEC(conn->recv_avail, len); + } else { + len = 0; + } + + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, len); + + /* If we are closed, we indicate that we no longer wish to use the socket */ + if (p == NULL) { + memp_free(MEMP_NETBUF, buf); + /* Avoid to lose any previous error code */ + if (conn->err == ERR_OK) { + conn->err = ERR_CLSD; + } + return NULL; + } + + buf->p = p; + buf->ptr = p; + buf->port = 0; + buf->addr = NULL; + + /* Let the stack know that we have taken the data. */ + msg.function = do_recv; + msg.msg.conn = conn; + if (buf != NULL) { + msg.msg.msg.r.len = buf->p->tot_len; + } else { + msg.msg.msg.r.len = 1; + } + TCPIP_APIMSG(&msg); +#endif /* LWIP_TCP */ + } else { +#if (LWIP_UDP || LWIP_RAW) +#if LWIP_SO_RCVTIMEO + if (sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, conn->recv_timeout)==SYS_ARCH_TIMEOUT) { + buf = NULL; + } +#else + sys_arch_mbox_fetch(conn->recvmbox, (void *)&buf, 0); +#endif /* LWIP_SO_RCVTIMEO*/ + if (buf!=NULL) { + SYS_ARCH_DEC(conn->recv_avail, buf->p->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVMINUS, buf->p->tot_len); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err)); + + return buf; +} + +/** + * 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, struct ip_addr *addr, u16_t port) +{ + if (buf != NULL) { + 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; + + 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; + TCPIP_APIMSG(&msg); + return conn->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 (0x01) data will be copied into memory belonging to the stack + * - NETCONN_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent + * @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; + + 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;); + + 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. */ + TCPIP_APIMSG(&msg); + return conn->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) +{ + struct api_msg msg; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + msg.function = do_close; + msg.msg.conn = conn; + tcpip_apimsg(&msg); + return conn->err; +} + +#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 interface 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, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave) +{ + struct api_msg msg; + + 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.interface = interface; + msg.msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&msg); + return conn->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 struct ip_addr 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, struct ip_addr *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;); + + sem = sys_sem_new(0); + if (sem == SYS_SEM_NULL) { + return ERR_MEM; + } + + 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*/ + +err_t netconn_abort(struct netconn *conn) +{ + if (conn->acceptmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->acceptmbox, NULL); + } + return ERR_OK; +} + +#endif /* LWIP_NETCONN */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/api/api_msg.c b/component/common/network/lwip/lwip_v1.3.2/src/api/api_msg.c new file mode 100644 index 0000000..8920139 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/api_msg.c @@ -0,0 +1,1243 @@ +/* + * @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 + +/* 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, + struct ip_addr *addr) +{ + struct pbuf *q; + struct netbuf *buf; + struct netconn *conn; +#if LWIP_SO_RCVBUF + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + LWIP_UNUSED_ARG(addr); + conn = arg; + +#if LWIP_SO_RCVBUF + SYS_ARCH_GET(conn->recv_avail, recv_avail); + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL) && + ((recv_avail + (int)(p->tot_len)) <= conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn != NULL) && (conn->recvmbox != SYS_MBOX_NULL)) { +#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) { + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(q); + return 0; + } + + buf->p = q; + buf->ptr = q; + buf->addr = &(((struct ip_hdr*)(q->payload))->src); + buf->port = pcb->protocol; + + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return 0; + } else { + SYS_ARCH_INC(conn->recv_avail, q->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, q->tot_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, + struct ip_addr *addr, u16_t port) +{ + struct netbuf *buf; + struct netconn *conn; +#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 = 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) || (conn->recvmbox == SYS_MBOX_NULL) || + ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) { +#else /* LWIP_SO_RCVBUF */ + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { +#endif /* LWIP_SO_RCVBUF */ + pbuf_free(p); + return; + } + + buf = memp_malloc(MEMP_NETBUF); + if (buf == NULL) { + pbuf_free(p); + return; + } else { + buf->p = p; + buf->ptr = p; + 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)); + buf->toaddr = (struct ip_addr*)&iphdr->dest; + buf->toport = udphdr->dest; + } +#endif /* LWIP_NETBUF_RECVINFO */ + } + + if (sys_mbox_trypost(conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); + return; + } else { + SYS_ARCH_INC(conn->recv_avail, p->tot_len); + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, p->tot_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 = arg; + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + if ((conn == NULL) || (conn->recvmbox == SYS_MBOX_NULL)) { + return ERR_VAL; + } + + conn->err = err; + if (p != NULL) { + len = p->tot_len; + SYS_ARCH_INC(conn->recv_avail, len); + } else { + len = 0; + } + + if (sys_mbox_trypost(conn->recvmbox, p) != ERR_OK) { + return ERR_MEM; + } else { + /* 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 = 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); + } + + 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 = arg; + + LWIP_UNUSED_ARG(pcb); + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + if (conn->state == NETCONN_WRITE) { + LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL); + do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + do_close_internal(conn); + } + + if (conn) { + if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT)) { + 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; + + conn = arg; + LWIP_ASSERT("conn != NULL", (conn != NULL)); + + conn->pcb.tcp = NULL; + + conn->err = err; + if (conn->recvmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->recvmbox, NULL); + } + if (conn->op_completed != SYS_SEM_NULL && conn->state == NETCONN_CONNECT) { + conn->state = NETCONN_NONE; + sys_sem_signal(conn->op_completed); + } + if (conn->acceptmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->acceptmbox, NULL); + } + if ((conn->state == NETCONN_WRITE) || (conn->state == NETCONN_CLOSE)) { + /* calling do_writemore/do_close_internal is not necessary + since the pcb has already been deleted! */ + conn->state = NETCONN_NONE; + /* wake up the waiting task */ + sys_sem_signal(conn->op_completed); + } +} + +/** + * 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; + +#if API_MSG_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(newpcb->state); +#endif /* TCP_DEBUG */ +#endif /* API_MSG_DEBUG */ + conn = (struct netconn *)arg; + + LWIP_ERROR("accept_function: invalid conn->acceptmbox", + conn->acceptmbox != SYS_MBOX_NULL, 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); + newconn->err = err; + + if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { + /* When returning != ERR_OK, the connection is aborted in tcp_process(), + so do nothing here! */ + newconn->pcb.tcp = NULL; + 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 err_t +pcb_new(struct api_msg_msg *msg) +{ + msg->conn->err = ERR_OK; + + 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->conn->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->conn->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->conn->err = ERR_MEM; + break; + } + setup_tcp(msg->conn); + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->conn->err = ERR_VAL; + break; + } + + return msg->conn->err; +} + +/** + * 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) +{ + 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 = 0; + + conn = memp_malloc(MEMP_NETCONN); + if (conn == NULL) { + return NULL; + } + + conn->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 ((conn->op_completed = sys_sem_new(0)) == SYS_SEM_NULL) { + memp_free(MEMP_NETCONN, conn); + return NULL; + } + if ((conn->recvmbox = sys_mbox_new(size)) == SYS_MBOX_NULL) { + sys_sem_free(conn->op_completed); + memp_free(MEMP_NETCONN, conn); + return NULL; + } + + conn->acceptmbox = SYS_MBOX_NULL; + conn->state = NETCONN_NONE; + /* initialize socket to -1 since 0 is a valid socket */ + conn->socket = -1; + conn->callback = callback; + conn->recv_avail = 0; +#if LWIP_TCP + conn->write_msg = NULL; + conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 0; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#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; +#endif /* LWIP_SO_RCVBUF */ + 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) +{ + void *mem; + LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL); + + /* Drain the recvmbox. */ + if (conn->recvmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->recvmbox, &mem) != SYS_MBOX_EMPTY) { + if (conn->type == NETCONN_TCP) { + if(mem != NULL) { + pbuf_free((struct pbuf *)mem); + } + } else { + netbuf_delete((struct netbuf *)mem); + } + } + sys_mbox_free(conn->recvmbox); + conn->recvmbox = SYS_MBOX_NULL; + } + + /* Drain the acceptmbox. */ + if (conn->acceptmbox != SYS_MBOX_NULL) { + while (sys_mbox_tryfetch(conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) { + netconn_delete((struct netconn *)mem); + } + sys_mbox_free(conn->acceptmbox); + conn->acceptmbox = SYS_MBOX_NULL; + } + + sys_sem_free(conn->op_completed); + conn->op_completed = SYS_SEM_NULL; + + memp_free(MEMP_NETCONN, conn); +} + +#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; + + 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)); + + /* Set back some callback pointers */ + tcp_arg(conn->pcb.tcp, NULL); + if (conn->pcb.tcp->state == LISTEN) { + tcp_accept(conn->pcb.tcp, NULL); + } else { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + /* some callbacks have to be reset if tcp_close is not successful */ + tcp_sent(conn->pcb.tcp, NULL); + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } + /* Try to close the connection */ + err = tcp_close(conn->pcb.tcp); + if (err == ERR_OK) { + /* Closing succeeded */ + conn->state = NETCONN_NONE; + /* Set back some callback pointers as conn is going away */ + conn->pcb.tcp = NULL; + conn->err = ERR_OK; + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + 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); + } + /* 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) +{ + 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: + msg->conn->state = NETCONN_CLOSE; + 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; + } + } + /* tcp netconns don't come here! */ + + /* Trigger select() in socket layer. This send should something else so the + errorfd is set, not the read and write fd! */ + API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0); + API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0); + + if (msg->conn->op_completed != SYS_SEM_NULL) { + 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->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->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->conn->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port); + break; +#endif /* LWIP_TCP */ + default: + break; + } + } else { + /* msg->conn->pcb is NULL */ + msg->conn->err = ERR_VAL; + } + } + 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; + + LWIP_UNUSED_ARG(pcb); + + conn = arg; + + if (conn == NULL) { + return ERR_VAL; + } + + conn->err = err; + if ((conn->type == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + conn->state = NETCONN_NONE; + 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) { + sys_sem_signal(msg->conn->op_completed); + return; + } + + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->conn->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr); + sys_sem_signal(msg->conn->op_completed); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->conn->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port); + sys_sem_signal(msg->conn->op_completed); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + msg->conn->state = NETCONN_CONNECT; + setup_tcp(msg->conn); + msg->conn->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port, + do_connected); + /* sys_sem_signal() is called from do_connected (or err_tcp()), + * when the connection is established! */ + break; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->conn->err = ERR_VAL; + sys_sem_signal(msg->conn->op_completed); }while(0)); + break; + } +} + +/** + * 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); + } +#endif /* LWIP_UDP */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * 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 LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + if (msg->conn->pcb.tcp != NULL) { + if (msg->conn->type == NETCONN_TCP) { + if (msg->conn->pcb.tcp->state == CLOSED) { +#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) { + msg->conn->err = ERR_MEM; + } else { + /* delete the recvmbox and allocate the acceptmbox */ + if (msg->conn->recvmbox != SYS_MBOX_NULL) { + /** @todo: should we drain the recvmbox here? */ + sys_mbox_free(msg->conn->recvmbox); + msg->conn->recvmbox = SYS_MBOX_NULL; + } + if (msg->conn->acceptmbox == SYS_MBOX_NULL) { + if ((msg->conn->acceptmbox = sys_mbox_new(DEFAULT_ACCEPTMBOX_SIZE)) == SYS_MBOX_NULL) { + msg->conn->err = ERR_MEM; + } + } + if (msg->conn->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 { + msg->conn->err = ERR_CONN; + } + } + } + } +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * 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->err)) { + if (msg->conn->pcb.tcp != NULL) { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + if (msg->msg.b->addr == NULL) { + msg->conn->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p); + } else { + msg->conn->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, msg->msg.b->addr); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: + if (msg->msg.b->addr == NULL) { + msg->conn->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p); + } else { + msg->conn->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, msg->msg.b->addr, msg->msg.b->port); + } + break; +#endif /* LWIP_UDP */ + default: + break; + } + } + } + TCPIP_APIMSG_ACK(msg); +} + +/** + * 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) +{ +#if LWIP_TCP + if (!ERR_IS_FATAL(msg->conn->err)) { + 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 */ + { + tcp_recved(msg->conn->pcb.tcp, msg->msg.r.len); + } + } + } + } +#endif /* LWIP_TCP */ + TCPIP_APIMSG_ACK(msg); +} + +#if LWIP_TCP +/** + * 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; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + + LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE)); + + dataptr = (u8_t*)conn->write_msg->msg.w.dataptr + conn->write_offset; + diff = conn->write_msg->msg.w.len - conn->write_offset; + if (diff > 0xffffUL) { /* max_u16_t */ + len = 0xffff; +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } 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->write_delayed = 1; +#endif + } + + err = tcp_write(conn->pcb.tcp, dataptr, len, conn->write_msg->msg.w.apiflags); + LWIP_ASSERT("do_writemore: invalid length!", ((conn->write_offset + len) <= conn->write_msg->msg.w.len)); + if (err == ERR_OK) { + conn->write_offset += len; + if (conn->write_offset == conn->write_msg->msg.w.len) { + /* everything was written */ + write_finished = 1; + conn->write_msg = NULL; + conn->write_offset = 0; + /* API_EVENT might call tcp_tmr, so reset conn->state now */ + conn->state = NETCONN_NONE; + } + err = tcp_output_nagle(conn->pcb.tcp); + conn->err = err; + if ((err == ERR_OK) && (tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT)) { + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + } + } 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_enqueue returned ERR_MEM, try tcp_output anyway */ + err = tcp_output(conn->pcb.tcp); + +#if LWIP_TCPIP_CORE_LOCKING + conn->write_delayed = 1; +#endif + } else { + /* On errors != ERR_MEM, we don't try writing any more but return + the error to the application thread. */ + conn->err = err; + write_finished = 1; + } + + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + conn->state = NETCONN_NONE; +#if LWIP_TCPIP_CORE_LOCKING + if (conn->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->err)) { + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { +#if LWIP_TCP + msg->conn->state = NETCONN_WRITE; + /* set all the variables used by do_writemore */ + LWIP_ASSERT("already writing", msg->conn->write_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->write_msg = msg; + msg->conn->write_offset = 0; +#if LWIP_TCPIP_CORE_LOCKING + msg->conn->write_delayed = 0; + 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 + do_writemore(msg->conn); +#endif + /* for both cases: if do_writemore was called, don't ACK the APIMSG! */ + return; +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + } else { + msg->conn->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); + + 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->conn->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->conn->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 */ + } + } else { + msg->conn->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 + if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { + msg->conn->state = NETCONN_CLOSE; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + } else +#endif /* LWIP_TCP */ + { + msg->conn->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->err)) { + 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->conn->err = igmp_joingroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } else { + msg->conn->err = igmp_leavegroup(msg->msg.jl.interface, msg->msg.jl.multiaddr); + } +#endif /* LWIP_UDP */ +#if (LWIP_TCP || LWIP_RAW) + } else { + msg->conn->err = ERR_VAL; +#endif /* (LWIP_TCP || LWIP_RAW) */ + } + } + } + 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, struct ip_addr *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); + + 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/component/common/network/lwip/lwip_v1.3.2/src/api/err.c b/component/common/network/lwip/lwip_v1.3.2/src/api/err.c new file mode 100644 index 0000000..c8266ab --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/err.c @@ -0,0 +1,74 @@ +/* + * @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 */ + "Connection aborted.", /* ERR_ABRT -5 */ + "Connection reset.", /* ERR_RST -6 */ + "Connection closed.", /* ERR_CLSD -7 */ + "Not connected.", /* ERR_CONN -8 */ + "Illegal value.", /* ERR_VAL -9 */ + "Illegal argument.", /* ERR_ARG -10 */ + "Address in use.", /* ERR_USE -11 */ + "Low-level netif error.", /* ERR_IF -12 */ + "Already connected.", /* ERR_ISCONN -13 */ + "Operation in progress." /* ERR_INPROGRESS -14 */ +}; + +/** + * 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/component/common/network/lwip/lwip_v1.3.2/src/api/netbuf.c b/component/common/network/lwip/lwip_v1.3.2/src/api/netbuf.c new file mode 100644 index 0000000..5cb24f1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/netbuf.c @@ -0,0 +1,240 @@ +/** + * @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! + * + * @return a pointer to a new netbuf + * NULL on lack of memory + */ +struct +netbuf *netbuf_new(void) +{ + struct netbuf *buf; + + buf = memp_malloc(MEMP_NETBUF); + if (buf != NULL) { + buf->p = NULL; + buf->ptr = NULL; + buf->addr = NULL; + buf->port = 0; +#if LWIP_NETBUF_RECVINFO + buf->toaddr = NULL; + buf->toport = 0; +#endif /* LWIP_NETBUF_RECVINFO */ + return buf; + } else { + return NULL; + } +} + +/** + * Deallocate a netbuf allocated by netbuf_new(). + * + * @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. + * + * @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 + * + * @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/component/common/network/lwip/lwip_v1.3.2/src/api/netdb.c b/component/common/network/lwip/lwip_v1.3.2/src/api/netdb.c new file mode 100644 index 0000000..dc91837 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/netdb.c @@ -0,0 +1,346 @@ +/** + * @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/ip_addr.h" +#include "lwip/api.h" + +#include +#include + +/** helper struct for gethostbyname_r to access the char* buffer */ +struct gethostbyname_r_helper { + struct ip_addr *addrs; + struct ip_addr 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; + struct ip_addr addr; + + /* buffer variables for lwip_gethostbyname() */ + HOSTENT_STORAGE struct hostent s_hostent; + HOSTENT_STORAGE char *s_aliases; + HOSTENT_STORAGE struct ip_addr s_hostent_addr; + HOSTENT_STORAGE struct ip_addr *s_phostent_addr; + + /* 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 = &s_hostent_addr; + s_hostent.h_name = (char*)name; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_INET; + s_hostent.h_length = sizeof(struct ip_addr); + 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(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(struct ip_addr); + 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; + mem_free(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; + struct ip_addr 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 */ + addr.addr = htonl(INADDR_LOOPBACK); + } + + 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; + } + ai = mem_malloc(total_size); + if (ai == NULL) { + goto memerr; + } + memset(ai, 0, total_size); + sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo)); + /* set up sockaddr */ + sa->sin_addr.s_addr = addr.addr; + sa->sin_family = AF_INET; + sa->sin_len = sizeof(struct sockaddr_in); + sa->sin_port = htons(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) { + mem_free(ai); + } + return EAI_MEMORY; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/api/netifapi.c b/component/common/network/lwip/lwip_v1.3.2/src/api/netifapi.c new file mode 100644 index 0000000..6ee58dc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/netifapi.c @@ -0,0 +1,162 @@ +/** + * @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, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + 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, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *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, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ) +{ + 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/component/common/network/lwip/lwip_v1.3.2/src/api/sockets.c b/component/common/network/lwip/lwip_v1.3.2/src/api/sockets.c new file mode 100644 index 0000000..bf09d46 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/sockets.c @@ -0,0 +1,1971 @@ +/** + * @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 + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** Contains all internal pointers and states used for a socket */ +struct lwip_socket { + /** sockets currently are built on netconns, each socket has one netconn */ + struct netconn *conn; + /** data that was left from the previous read */ + struct netbuf *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 received, set by event_callback(), + tested by select */ + u16_t sendevent; + /** socket flags (currently, only used for O_NONBLOCK) */ + u16_t flags; + /** last error that occurred on this socket */ + int err; +}; + +/** Description for a task waiting in select */ +struct lwip_select_cb { + /** Pointer to the next waiting task */ + struct lwip_select_cb *next; + /** 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_socket *sock; + /** socket index for which to change options */ + int s; + /** 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_socket sockets[NUM_SOCKETS]; +/** The global list of tasks waiting for select */ +static struct lwip_select_cb *select_cb_list; + +/** Semaphore protecting the sockets array */ +static sys_sem_t socksem; +/** Semaphore protecting select_cb_list */ +static sys_sem_t selectsem; + +/** 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. */ + ETIMEDOUT, /* ERR_TIMEOUT -3 Timeout */ + EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */ + ECONNABORTED, /* ERR_ABRT -5 Connection aborted. */ + ECONNRESET, /* ERR_RST -6 Connection reset. */ + ESHUTDOWN, /* ERR_CLSD -7 Connection closed. */ + ENOTCONN, /* ERR_CONN -8 Not connected. */ + EINVAL, /* ERR_VAL -9 Illegal value. */ + EIO, /* ERR_ARG -10 Illegal argument. */ + EADDRINUSE, /* ERR_USE -11 Address in use. */ + -1, /* ERR_IF -12 Low-level netif error */ + -1, /* ERR_ISCONN -13 Already connected. */ + EINPROGRESS /* ERR_INPROGRESS -14 Operation in progress */ +}; + +#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 +#define set_errno(err) +#endif + +#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) +{ + socksem = sys_sem_new(1); + selectsem = sys_sem_new(1); +} + +/** + * Map a externally used socket index to the internal socket representation. + * + * @param s externally used socket index + * @return struct lwip_socket for the socket or NULL if not found + */ +static struct lwip_socket * +get_socket(int s) +{ + struct lwip_socket *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; +} + +/** + * Allocate a new socket for a given netconn. + * + * @param newconn the netconn for which to allocate a socket + * @return the index of the new socket; -1 on error + */ +static int +alloc_socket(struct netconn *newconn) +{ + int i; + + /* Protect socket array */ + sys_sem_wait(socksem); + + /* allocate a new socket identifier */ + for (i = 0; i < NUM_SOCKETS; ++i) { + if (!sockets[i].conn) { + sockets[i].conn = newconn; + sockets[i].lastdata = NULL; + sockets[i].lastoffset = 0; + sockets[i].rcvevent = 0; + sockets[i].sendevent = 1; /* TCP send buf is empty */ + sockets[i].flags = 0; + sockets[i].err = 0; + sys_sem_signal(socksem); + return i; + } + } + sys_sem_signal(socksem); + return -1; +} + +/* 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_socket *sock, *nsock; + struct netconn *newconn; + struct ip_addr naddr; + u16_t port; + int newsock; + struct sockaddr_in sin; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); + sock = get_socket(s); + if (!sock) + return -1; + + if ((sock->flags & O_NONBLOCK) && (sock->rcvevent <= 0)) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s)); + sock_set_errno(sock, EWOULDBLOCK); + return -1; + } + + newconn = netconn_accept(sock->conn); + if (!newconn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); + sock_set_errno(sock, err_to_errno(sock->conn->err)); + return -1; + } + + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, &naddr, &port); + if (err != ERR_OK) { + 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); + sin.sin_addr.s_addr = naddr.addr; + + if (*addrlen > sizeof(sin)) + *addrlen = sizeof(sin); + + MEMCPY(addr, &sin, *addrlen); + } + + newsock = alloc_socket(newconn); + if (newsock == -1) { + netconn_delete(newconn); + sock_set_errno(sock, ENFILE); + return -1; + } + LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS)); + newconn->callback = event_callback; + nsock = &sockets[newsock]; + LWIP_ASSERT("invalid socket pointer", nsock != NULL); + + sys_sem_wait(socksem); + /* 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! + */ + nsock->rcvevent += -1 - newconn->socket; + newconn->socket = newsock; + sys_sem_signal(socksem); + + 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_socket *sock; + struct ip_addr local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) + return -1; + + LWIP_ERROR("lwip_bind: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((const struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + local_addr.addr = ((const struct sockaddr_in *)name)->sin_addr.s_addr; + local_port = ((const struct sockaddr_in *)name)->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_socket *sock; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s)); + + sock = get_socket(s); + if (!sock) { + return -1; + } + + netconn_delete(sock->conn); + + sys_sem_wait(socksem); + if (sock->lastdata) { + netbuf_delete(sock->lastdata); + } + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->conn = NULL; + sock_set_errno(sock, 0); + sys_sem_signal(socksem); + return 0; +} + +int +lwip_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + struct lwip_socket *sock; + err_t err; + + sock = get_socket(s); + if (!sock) + return -1; + + LWIP_ERROR("lwip_connect: invalid address", ((namelen == sizeof(struct sockaddr_in)) && + ((((const struct sockaddr_in *)name)->sin_family) == AF_INET)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + if (((const struct sockaddr_in *)name)->sin_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + struct ip_addr remote_addr; + u16_t remote_port; + + remote_addr.addr = ((const struct sockaddr_in *)name)->sin_addr.s_addr; + remote_port = ((const struct sockaddr_in *)name)->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: need TCP_LISTEN_BACKLOG=1) + * @return 0 on success, non-zero on failure + */ +int +lwip_listen(int s, int backlog) +{ + struct lwip_socket *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 */ + if (backlog < 0) { + backlog = 0; + } + if (backlog > 0xff) { + backlog = 0xff; + } + + err = netconn_listen_with_backlog(sock->conn, 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_socket *sock; + struct netbuf *buf; + u16_t buflen, copylen, off = 0; + struct ip_addr *addr; + u16_t port; + u8_t done = 0; + + 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", (void*)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) || (sock->flags & O_NONBLOCK)) && + (sock->rcvevent <= 0)) { + if (off > 0) { + /* 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. */ + sock->lastdata = buf = netconn_recv(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv netbuf=%p\n", (void*)buf)); + + if (!buf) { + if (off > 0) { + /* 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!\n", s)); + sock_set_errno(sock, (((sock->conn->pcb.ip != NULL) && (sock->conn->err == ERR_OK)) + ? ETIMEDOUT : err_to_errno(sock->conn->err))); + return 0; + } + } + + buflen = netbuf_len(buf); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%"U16_F" 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 */ + netbuf_copy_partial(buf, (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) || + (buf->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) { + if (from && fromlen) { + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + } + + memset(&sin, 0, sizeof(sin)); + sin.sin_len = sizeof(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = 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=%"U16_F"\n", port, off)); + } else { + #if SOCKETS_DEBUG + struct sockaddr_in sin; + + if (netconn_type(sock->conn) == NETCONN_TCP) { + addr = (struct ip_addr*)&(sin.sin_addr.s_addr); + netconn_getaddr(sock->conn, addr, &port, 0); + } else { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(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=%"U16_F"\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", (void*)buf)); + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", (void*)buf)); + netbuf_delete(buf); + } + } + } while (!done); + + 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_socket *sock; + err_t err; + + 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 + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* (LWIP_UDP || LWIP_RAW) */ + } + + err = netconn_write(sock->conn, data, size, NETCONN_COPY | ((flags & MSG_MORE)?NETCONN_MORE:0)); + + 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_socket *sock; + struct ip_addr remote_addr; + err_t err; + u16_t short_size; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; + u16_t remote_port; +#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 + sock_set_errno(sock, err_to_errno(ERR_ARG)); + return -1; +#endif /* LWIP_TCP */ + } + + 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)) && + ((((const struct sockaddr_in *)to)->sin_family) == AF_INET))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + +#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; + + p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF); + if (p == NULL) { + err = ERR_MEM; + } else { + p->payload = (void*)data; + p->len = p->tot_len = short_size; + + remote_addr.addr = ((const struct sockaddr_in *)to)->sin_addr.s_addr; + + LOCK_TCPIP_CORE(); + if (sock->conn->type==NETCONN_RAW) { + err = sock->conn->err = raw_sendto(sock->conn->pcb.raw, p, &remote_addr); + } else { + err = sock->conn->err = udp_sendto(sock->conn->pcb.udp, p, &remote_addr, ntohs(((const struct sockaddr_in *)to)->sin_port)); + } + UNLOCK_TCPIP_CORE(); + + pbuf_free(p); + } + } +#else + /* initialize a buffer */ + buf.p = buf.ptr = NULL; + if (to) { + remote_addr.addr = ((const struct sockaddr_in *)to)->sin_addr.s_addr; + remote_port = ntohs(((const struct sockaddr_in *)to)->sin_port); + buf.addr = &remote_addr; + buf.port = remote_port; + } else { + remote_addr.addr = 0; + remote_port = 0; + buf.addr = NULL; + buf.port = 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, &remote_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 { + 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)); + 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); + + 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; + * out: set of sockets that had read events + * @param writeset in: set of sockets to check for write events; + * out: set of sockets that had write events + * @param exceptset not yet implemented + * @return number of sockets that had events (read+write) + */ +static int +lwip_selscan(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset) +{ + int i, nready = 0; + fd_set lreadset, lwriteset, lexceptset; + struct lwip_socket *p_sock; + + 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++) { + if (FD_ISSET(i, readset)) { + /* See if netconn of this socket is ready for read */ + p_sock = get_socket(i); + if (p_sock && (p_sock->lastdata || (p_sock->rcvevent > 0))) { + FD_SET(i, &lreadset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i)); + nready++; + } + } + if (FD_ISSET(i, writeset)) { + /* See if netconn of this socket is ready for write */ + p_sock = get_socket(i); + if (p_sock && p_sock->sendevent) { + FD_SET(i, &lwriteset); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i)); + nready++; + } + } + } + *readset = lreadset; + *writeset = lwriteset; + FD_ZERO(exceptset); + + 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) +{ + int i; + int nready; + fd_set lreadset, lwriteset, lexceptset; + u32_t msectimeout; + struct lwip_select_cb select_cb; + struct lwip_select_cb *p_selcb; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%ld tvusec=%ld)\n", + maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset, + timeout ? (long)timeout->tv_sec : (long)-1, + timeout ? (long)timeout->tv_usec : (long)-1)); + + select_cb.next = 0; + select_cb.readset = readset; + select_cb.writeset = writeset; + select_cb.exceptset = exceptset; + select_cb.sem_signalled = 0; + + /* Protect ourselves searching through the list */ + sys_sem_wait(selectsem); + + if (readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + if (writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + if (exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + /* Go through each socket in each list to count number of sockets which + currently match */ + nready = lwip_selscan(maxfdp1, &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) { + sys_sem_signal(selectsem); + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n")); + set_errno(0); + + return 0; + } + + /* 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.sem = sys_sem_new(0); + /* Note that we are still protected */ + /* Put this select_cb on top of list */ + select_cb.next = select_cb_list; + select_cb_list = &select_cb; + + /* Now we can safely unprotect */ + sys_sem_signal(selectsem); + + /* Now 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) + msectimeout = 1; + } + + i = sys_sem_wait_timeout(select_cb.sem, msectimeout); + + /* Take us off the list */ + sys_sem_wait(selectsem); + if (select_cb_list == &select_cb) + select_cb_list = select_cb.next; + else + for (p_selcb = select_cb_list; p_selcb; p_selcb = p_selcb->next) { + if (p_selcb->next == &select_cb) { + p_selcb->next = select_cb.next; + break; + } + } + + sys_sem_signal(selectsem); + + sys_sem_free(select_cb.sem); + if (i == 0) { + /* Timeout */ + if (readset) + FD_ZERO(readset); + if (writeset) + FD_ZERO(writeset); + if (exceptset) + FD_ZERO(exceptset); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n")); + set_errno(0); + + return 0; + } + + if (readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + if (writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + if (exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + /* See what's set */ + nready = lwip_selscan(maxfdp1, &lreadset, &lwriteset, &lexceptset); + } else + sys_sem_signal(selectsem); + + if (readset) + *readset = lreadset; + if (writeset) + *writeset = lwriteset; + if (exceptset) + *exceptset = lexceptset; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready)); + set_errno(0); + + 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_socket *sock; + struct lwip_select_cb *scb; + + 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_sem_wait(socksem); + if (conn->socket < 0) { + if (evt == NETCONN_EVT_RCVPLUS) { + conn->socket--; + } + sys_sem_signal(socksem); + return; + } + s = conn->socket; + sys_sem_signal(socksem); + } + + sock = get_socket(s); + if (!sock) { + return; + } + } else { + return; + } + + sys_sem_wait(selectsem); + /* 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; + default: + LWIP_ASSERT("unknown event", 0); + break; + } + sys_sem_signal(selectsem); + + /* Now decide if anyone is waiting for this socket */ + /* NOTE: This code is written this way to protect the select link list + but to avoid a deadlock situation by releasing socksem before + signalling for the select. This means we need to go through the 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. */ + while (1) { + sys_sem_wait(selectsem); + for (scb = select_cb_list; scb; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* Test this select call for our socket */ + if (scb->readset && FD_ISSET(s, scb->readset)) + if (sock->rcvevent > 0) + break; + if (scb->writeset && FD_ISSET(s, scb->writeset)) + if (sock->sendevent) + break; + } + } + if (scb) { + scb->sem_signalled = 1; + sys_sem_signal(scb->sem); + sys_sem_signal(selectsem); + } else { + sys_sem_signal(selectsem); + break; + } + } +} + +/** + * Unimplemented: Close one end of a full-duplex connection. + * Currently, the full connection is closed. + */ +int +lwip_shutdown(int s, int how) +{ + LWIP_UNUSED_ARG(how); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); + return lwip_close(s); /* XXX temporary hack until proper implementation */ +} + +static int +lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local) +{ + struct lwip_socket *sock; + struct sockaddr_in sin; + struct ip_addr 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); + sin.sin_addr.s_addr = naddr.addr; + + 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_socket *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; +#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; + 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_socket *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: + if (sock->err == 0) { + sock_set_errno(sock, err_to_errno(sock->conn->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 = sock->conn->recv_timeout; + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + *(int *)optval = sock->conn->recv_bufsize; + 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*/ + } /* 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: + ((struct in_addr*) optval)->s_addr = sock->conn->pcb.udp->multicast_ip.addr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n", + s, *(u32_t *)optval)); + break; +#endif /* LWIP_IGMP */ + } /* 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 */ + + } /* 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; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* 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_socket *sock = get_socket(s); + int 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_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; + 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_socket *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: + sock->conn->recv_timeout = ( *(int*)optval ); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + sock->conn->recv_bufsize = ( *(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 */ + } /* 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: + sock->conn->pcb.udp->multicast_ip.addr = ((struct in_addr*) optval)->s_addr; + 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; + if(optname == IP_ADD_MEMBERSHIP){ + data->err = igmp_joingroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } else { + data->err = igmp_leavegroup((struct ip_addr*)&(imr->imr_interface.s_addr), (struct ip_addr*)&(imr->imr_multiaddr.s_addr)); + } + if(data->err != ERR_OK) { + data->err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + } /* 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 */ + + } /* 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)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_tx = 8; + } else { + sock->conn->pcb.udp->chksum_len_tx = *(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)) { + /* don't allow illegal values! */ + sock->conn->pcb.udp->chksum_len_rx = 8; + } else { + sock->conn->pcb.udp->chksum_len_rx = *(int*)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n", + s, (*(int*)optval)) ); + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + } /* switch (level) */ + sys_sem_signal(sock->conn->op_completed); +} + +int +lwip_ioctl(int s, long cmd, void *argp) +{ + struct lwip_socket *sock = get_socket(s); + u16_t buflen = 0; + s16_t recv_avail; + + if (!sock) + return -1; + + switch (cmd) { + 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) { + buflen = netbuf_len(sock->lastdata); + 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; + + case FIONBIO: + if (argp && *(u32_t*)argp) + sock->flags |= O_NONBLOCK; + else + sock->flags &= ~O_NONBLOCK; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags & O_NONBLOCK))); + 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) */ +} + +#endif /* LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/api/tcpip.c b/component/common/network/lwip/lwip_v1.3.2/src/api/tcpip.c new file mode 100644 index 0000000..002df90 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/api/tcpip.c @@ -0,0 +1,596 @@ +/** + * @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/pbuf.h" +#include "lwip/ip_frag.h" +#include "lwip/tcp.h" +#include "lwip/autoip.h" +#include "lwip/dhcp.h" +#include "lwip/igmp.h" +#include "lwip/dns.h" +#include "lwip/tcpip.h" +#include "lwip/init.h" +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* global variables */ +static void (* tcpip_init_done)(void *arg); +static void *tcpip_init_done_arg; +static sys_mbox_t mbox = SYS_MBOX_NULL; + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +sys_sem_t lock_tcpip_core; +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +#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 +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; + } +} + +#if !NO_SYS +/** + * 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); + } +} +#endif /* !NO_SYS */ +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_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 +arp_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_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 + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_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(TCPIP_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(TCPIP_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(TCPIP_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(TCPIP_DEBUG, ("tcpip: dns_tmr()\n")); + dns_tmr(); + sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL); +} +#endif /* LWIP_DNS */ + +/** + * 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 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 (tcpip_init_done != NULL) { + tcpip_init_done(tcpip_init_done_arg); + } + + LOCK_TCPIP_CORE(); + while (1) { /* MAIN Loop */ + sys_mbox_fetch(mbox, (void *)&msg); + switch (msg->type) { +#if LWIP_NETCONN + case TCPIP_MSG_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 */ + + case TCPIP_MSG_INPKT: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg)); +#if LWIP_ARP + if (msg->msg.inp.netif->flags & NETIF_FLAG_ETHARP) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ARP */ + { ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + memp_free(MEMP_TCPIP_MSG_INPKT, msg); + break; + +#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.f(msg->msg.cb.ctx); + memp_free(MEMP_TCPIP_MSG_API, msg); + break; + + 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; + + default: + 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 netif doesn't got NETIF_FLAG_ETHARP flag) + * @param inp the network interface on which the packet was received + */ +err_t +tcpip_input(struct pbuf *p, struct netif *inp) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + 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; +} + +/** + * 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(void (*f)(void *ctx), void *ctx, u8_t block) +{ + struct tcpip_msg *msg; + + if (mbox != SYS_MBOX_NULL) { + msg = memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return ERR_MEM; + } + + msg->type = TCPIP_MSG_CALLBACK; + msg->msg.cb.f = f; + 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; +} + +/** + * call sys_timeout in tcpip_thread + * + * @param msec time in miliseconds 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 (mbox != SYS_MBOX_NULL) { + 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 miliseconds 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 (mbox != SYS_MBOX_NULL) { + 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; +} + +#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; + + if (mbox != SYS_MBOX_NULL) { + 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 ERR_OK; + } + 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) +{ + LOCK_TCPIP_CORE(); + apimsg->function(&(apimsg->msg)); + UNLOCK_TCPIP_CORE(); + return ERR_OK; + +} +#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 (mbox != SYS_MBOX_NULL) { + netifapimsg->msg.sem = sys_sem_new(0); + if (netifapimsg->msg.sem == SYS_SEM_NULL) { + netifapimsg->msg.err = ERR_MEM; + return netifapimsg->msg.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(void (* initfunc)(void *), void *arg) +{ + lwip_init(); + + tcpip_init_done = initfunc; + tcpip_init_done_arg = arg; + mbox = sys_mbox_new(TCPIP_MBOX_SIZE); +#if LWIP_TCPIP_CORE_LOCKING + lock_tcpip_core = sys_sem_new(1); +#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 = 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/component/common/network/lwip/lwip_v1.3.2/src/core/dhcp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/dhcp.c new file mode 100644 index 0000000..7087107 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/dhcp.c @@ -0,0 +1,1778 @@ +/* + * @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: + * - Proper parsing of DHCP messages exploiting file/sname field overloading. + * - Add JavaDoc style documentation (API, internals). + * - 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/inet.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 + +/* DHCP client state machine functions */ +static void dhcp_handle_ack(struct netif *netif); +static void dhcp_handle_nak(struct netif *netif); +static void dhcp_handle_offer(struct netif *netif); + +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 void dhcp_check(struct netif *netif); +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, struct ip_addr *addr, u16_t port); +static err_t dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p); +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type); +static u8_t dhcp_get_option_byte(u8_t *ptr); +#if 0 +static u16_t dhcp_get_option_short(u8_t *ptr); +#endif +static u32_t dhcp_get_option_long(u8_t *ptr); +static void dhcp_free_reply(struct dhcp *dhcp); + +/* 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 request, fill in common headers */ +static err_t dhcp_create_request(struct netif *netif); +/* free a DHCP request */ +static void dhcp_delete_request(struct netif *netif); +/* 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 +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 +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 +dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + /* obtain the server address */ + u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID); + 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)); + if (option_ptr != NULL) { + dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); + /* remember offered address */ + ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + + dhcp_select(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 +dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + + 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_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + 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(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.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 + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + 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_request(netif); + 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 +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 +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 clients' 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 +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 +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")); + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + * @param netif the netif under DHCP control + */ +static void +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")); + dhcp_rebind(netif); + } +} + +/** + * Handle a DHCP ACK packet + * + * @param netif the netif under DHCP control + */ +static void +dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + u8_t *option_ptr; + /* clear options we might not get from the ACK */ + dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = 0; + dhcp->offered_bc_addr.addr = 0; + + /* lease time given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_LEASE_TIME); + if (option_ptr != NULL) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_long(option_ptr + 2); + } + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T1); + if (option_ptr != NULL) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T2); + if (option_ptr != NULL) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_set(&dhcp->offered_ip_addr, &dhcp->msg_in->yiaddr); + +/** + * Patch #1308 + * TODO: we must check if the file field is not overloaded by DHCP options! + */ +#if 0 + /* boot server address */ + ip_addr_set(&dhcp->offered_si_addr, &dhcp->msg_in->siaddr); + /* boot file name */ + if (dhcp->msg_in->file[0]) { + dhcp->boot_file_name = mem_malloc(strlen(dhcp->msg_in->file) + 1); + strcpy(dhcp->boot_file_name, dhcp->msg_in->file); + } +#endif + + /* subnet mask */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SUBNET_MASK); + /* subnet mask given? */ + if (option_ptr != NULL) { + dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* gateway router */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_ROUTER); + if (option_ptr != NULL) { + dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* broadcast address */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_BROADCAST); + if (option_ptr != NULL) { + dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* DNS servers */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER); + if (option_ptr != NULL) { + u8_t n; + dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]) / (u32_t)sizeof(struct ip_addr); + /* limit to at most DHCP_MAX_DNS DNS servers */ + if (dhcp->dns_count > DHCP_MAX_DNS) + dhcp->dns_count = DHCP_MAX_DNS; + for (n = 0; n < dhcp->dns_count; n++) { + dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2 + n * 4])); +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&(dhcp->offered_dns_addr[n].addr))); +#endif /* LWIP_DNS */ + } +#if LWIP_DNS + dns_setserver( n, (struct ip_addr *)(&ip_addr_any)); +#endif /* LWIP_DNS */ + } +} + +/** + * 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 +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 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 = 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 && + dhcp->options_in == NULL && dhcp->options_in_len == 0); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* 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")); + mem_free((void *)dhcp); + netif->dhcp = dhcp = NULL; + return ERR_MEM; + } +#if IP_SOF_BROADCAST + dhcp->pcb->so_options|=SOF_BROADCAST; +#endif /* IP_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 +dhcp_inform(struct netif *netif) +{ + struct dhcp *dhcp, *old_dhcp; + err_t result = ERR_OK; + dhcp = mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not allocate dhcp\n")); + return; + } + memset(dhcp, 0, sizeof(struct dhcp)); + + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): allocated dhcp\n")); + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform(): could not obtain pcb")); + goto free_dhcp_and_return; + } + old_dhcp = netif->dhcp; + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_INFORM); + + 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); + +#if IP_SOF_BROADCAST + dhcp->pcb->so_options|=SOF_BROADCAST; +#endif /* IP_SOF_BROADCAST */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif); + dhcp_delete_request(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_inform: could not allocate DHCP request\n")); + } + + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + netif->dhcp = old_dhcp; +free_dhcp_and_return: + mem_free((void *)dhcp); +} + +/** Handle a possible change in the network configuration. + * + * This enters the REBOOTING state to verify that the currently bound + * address is still valid. + */ +void +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; + 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 dhcp_arp_reply(struct netif *netif, struct ip_addr *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", addr->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 +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_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DECLINE); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.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_request(netif); + 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 + + +/** + * Start the DHCP process, discover a DHCP server. + * + * @param netif the netif under DHCP control + */ +static err_t +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(&dhcp->offered_ip_addr, IP_ADDR_ANY); + dhcp_set_state(dhcp, DHCP_SELECTING); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n")); + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DISCOVER); + + 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_request(netif); + 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 +dhcp_bind(struct netif *netif) +{ + u32_t timeout; + struct dhcp *dhcp; + struct ip_addr 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)); + } + /* copy offered network mask */ + ip_addr_set(&sn_mask, &dhcp->offered_sn_mask); + + /* subnet mask not given? */ + /* TODO: this is not a valid check. what if the network mask is 0? */ + if (sn_mask.addr == 0) { + /* choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&sn_mask); + if (first_octet <= 127) { + sn_mask.addr = htonl(0xff000000); + } else if (first_octet >= 192) { + sn_mask.addr = htonl(0xffffff00); + } else { + sn_mask.addr = htonl(0xffff0000); + } + } + + ip_addr_set(&gw_addr, &dhcp->offered_gw_addr); + /* gateway address not given? */ + if (gw_addr.addr == 0) { + /* copy network address */ + gw_addr.addr = (dhcp->offered_ip_addr.addr & sn_mask.addr); + /* use first host address on network as gateway */ + gw_addr.addr |= 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", dhcp->offered_ip_addr.addr)); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr)); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.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 +dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + 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_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + 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 + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + 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_request(netif); + + 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 +dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; +#if LWIP_NETIF_HOSTNAME + const char *p; +#endif /* LWIP_NETIF_HOSTNAME */ + 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_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + 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 + p = (const char*)netif->hostname; + if (p != NULL) { + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, strlen(p)); + 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_request(netif); + 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 +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_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + 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(dhcp->offered_ip_addr.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_request(netif); + 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 +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")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + 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_request(netif); + 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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release_unicast(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_RELEASING); + /* clean old DHCP offer *//* + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0;*/ + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); + + 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_request(netif); + 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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;); + /* 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->options_in == NULL && dhcp->options_in_len == 0); + mem_free((void *)dhcp); + netif->dhcp = NULL; + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + * + * TODO: we might also want to reset the timeout here? + */ +static void +dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) { + dhcp->state = new_state; + dhcp->tries = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void +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 +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 +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 +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 +dhcp_unfold_reply(struct dhcp *dhcp, struct pbuf *p) +{ + u16_t ret; + LWIP_ERROR("dhcp != NULL", (dhcp != NULL), return ERR_ARG;); + /* free any left-overs from previous unfolds */ + dhcp_free_reply(dhcp); + /* options present? */ + if (p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) { + dhcp->options_in_len = p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + dhcp->options_in = mem_malloc(dhcp->options_in_len); + if (dhcp->options_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); + dhcp->options_in_len = 0; + return ERR_MEM; + } + } + dhcp->msg_in = mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + if (dhcp->msg_in == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); + if (dhcp->options_in != NULL) { + mem_free(dhcp->options_in); + dhcp->options_in = NULL; + dhcp->options_in_len = 0; + } + return ERR_MEM; + } + + /** copy the DHCP message without options */ + ret = pbuf_copy_partial(p, dhcp->msg_in, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN, 0); + LWIP_ASSERT("ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN", ret == sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", + sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)); + + if (dhcp->options_in != NULL) { + /** copy the DHCP options */ + ret = pbuf_copy_partial(p, dhcp->options_in, dhcp->options_in_len, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + LWIP_ASSERT("ret == dhcp->options_in_len", ret == dhcp->options_in_len); + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", + dhcp->options_in_len)); + } + LWIP_UNUSED_ARG(ret); + return ERR_OK; +} + +/** + * Free the incoming DHCP message including contiguous copy of + * its DHCP options. + */ +static void dhcp_free_reply(struct dhcp *dhcp) +{ + if (dhcp->msg_in != NULL) { + mem_free((void *)dhcp->msg_in); + dhcp->msg_in = NULL; + } + if (dhcp->options_in) { + mem_free(dhcp->options_in); + dhcp->options_in = NULL; + dhcp->options_in_len = 0; + } + LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n")); +} + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *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 *options_ptr; + 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, + (u16_t)(ntohl(addr->addr) >> 24 & 0xff), (u16_t)(ntohl(addr->addr) >> 16 & 0xff), + (u16_t)(ntohl(addr->addr) >> 8 & 0xff), (u16_t)(ntohl(addr->addr) & 0xff), 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 && + dhcp->options_in == NULL && dhcp->options_in_len == 0); + + if (p->len < DHCP_MIN_REPLY_LEN) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message 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_unfold_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 */ + options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE); + if (options_ptr == NULL) { + 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 = dhcp_get_option_byte(options_ptr + 2); + /* 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); + dhcp->request_timeout = 0; +#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->request_timeout = 0; + 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->request_timeout = 0; + 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_free_reply(dhcp); + pbuf_free(p); +} + +/** + * Create a DHCP request, fill in common headers + * + * @param netif the netif under DHCP control + */ +static err_t +dhcp_create_request(struct netif *netif) +{ + struct dhcp *dhcp; + 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_request: netif != NULL", (netif != NULL), return ERR_ARG;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_create_request: dhcp != NULL", (dhcp != NULL), return ERR_VAL;); + LWIP_ASSERT("dhcp_create_request: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_request: 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_request(): could not allocate pbuf\n")); + return ERR_MEM; + } + LWIP_ASSERT("dhcp_create_request: check that first pbuf can hold struct dhcp_msg", + (dhcp->p_out->len >= sizeof(struct dhcp_msg))); + + /* 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; + /* TODO: make link layer independent */ + dhcp->msg_out->hlen = DHCP_HLEN_ETH; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + dhcp->msg_out->flags = 0; + dhcp->msg_out->ciaddr.addr = 0; + if (dhcp->state==DHCP_BOUND || dhcp->state==DHCP_RENEWING || dhcp->state==DHCP_REBINDING || dhcp->state == DHCP_RELEASING) { + dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr; + } + dhcp->msg_out->yiaddr.addr = 0; + dhcp->msg_out->siaddr.addr = 0; + dhcp->msg_out->giaddr.addr = 0; + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->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 = htonl(0x63825363UL); + 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 */ + } + return ERR_OK; +} + +/** + * Free previously allocated memory used to send a DHCP request. + * + * @param netif the netif under DHCP control + */ +static void +dhcp_delete_request(struct netif *netif) +{ + struct dhcp *dhcp; + LWIP_ERROR("dhcp_delete_request: netif != NULL", (netif != NULL), return;); + dhcp = netif->dhcp; + LWIP_ERROR("dhcp_delete_request: dhcp != NULL", (dhcp != NULL), return;); + LWIP_ASSERT("dhcp_delete_request: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_delete_request: 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 +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)) { + /* 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; + } +} + +/** + * Find the offset of a DHCP option inside the DHCP message. + * + * @param dhcp DHCP client + * @param option_type + * + * @return a byte offset into the UDP message where the option was found, or + * zero if the given option was not found. + */ +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type) +{ + u8_t overload = DHCP_OVERLOAD_NONE; + + /* options available? */ + if ((dhcp->options_in != NULL) && (dhcp->options_in_len > 0)) { + /* start with options field */ + u8_t *options = (u8_t *)dhcp->options_in; + u16_t offset = 0; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while ((offset < dhcp->options_in_len) && (options[offset] != DHCP_OPTION_END)) { + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + /* are the sname and/or file field overloaded with options? */ + if (options[offset] == DHCP_OPTION_OVERLOAD) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded message detected\n")); + /* skip option type and length */ + offset += 2; + overload = options[offset++]; + } + /* requested option found */ + else if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", options[offset])); + /* skip option type */ + offset++; + /* skip option length, and then length bytes */ + offset += 1 + options[offset]; + } + } + /* is this an overloaded message? */ + if (overload != DHCP_OVERLOAD_NONE) { + u16_t field_len; + if (overload == DHCP_OVERLOAD_FILE) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n")); + options = (u8_t *)&dhcp->msg_in->file; + field_len = DHCP_FILE_LEN; + } else if (overload == DHCP_OVERLOAD_SNAME) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_SNAME_LEN; + /* TODO: check if else if () is necessary */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN; + } + offset = 0; + + /* at least 1 byte to read and no end marker */ + while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) { + if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); + /* skip option type */ + offset++; + offset += 1 + options[offset]; + } + } + } + } + return NULL; +} + +/** + * Return the byte of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u8_t +dhcp_get_option_byte(u8_t *ptr) +{ + LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr))); + return *ptr; +} + +#if 0 /* currently unused */ +/** + * Return the 16-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u16_t +dhcp_get_option_short(u8_t *ptr) +{ + u16_t value; + value = *ptr++ << 8; + value |= *ptr; + LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value)); + return value; +} +#endif + +/** + * Return the 32-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u32_t dhcp_get_option_long(u8_t *ptr) +{ + u32_t value; + value = (u32_t)(*ptr++) << 24; + value |= (u32_t)(*ptr++) << 16; + value |= (u32_t)(*ptr++) << 8; + value |= (u32_t)(*ptr++); + LWIP_DEBUGF(DHCP_DEBUG, ("option long value=%"U32_F"\n", value)); + return value; +} + +#endif /* LWIP_DHCP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/dns.c b/component/common/network/lwip/lwip_v1.3.2/src/core/dns.c new file mode 100644 index 0000000..b18a360 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/dns.c @@ -0,0 +1,980 @@ +/* + * @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/dns.h" + +#include + +/** DNS server IP address */ +#ifndef DNS_SERVER_ADDRESS +#define DNS_SERVER_ADDRESS inet_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 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS query message structure */ +struct dns_query { + /* DNS query record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define SIZEOF_DNS_QUERY 4 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +/** DNS answer message structure */ +struct dns_answer { + /* DNS answer record starts with either a domain name or a pointer + to a name already present somewhere in the packet. */ + PACK_STRUCT_FIELD(u16_t type); + PACK_STRUCT_FIELD(u16_t class); + PACK_STRUCT_FIELD(u32_t ttl); + PACK_STRUCT_FIELD(u16_t len); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#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]; + struct ip_addr ipaddr; + /* pointer to callback on DNS query done */ + dns_found_callback found; + void *arg; +}; + +#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 */ + u32_t addr; + struct local_hostlist_entry *next; +}; + +#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, struct ip_addr *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 struct ip_addr dns_servers[DNS_MAX_SERVERS]; + +#if (DNS_USES_STATIC_BUF == 1) +static u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 1) */ + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + struct ip_addr dnsserver; + + /* initialize default DNS server address */ + dnsserver.addr = DNS_SERVER_ADDRESS; + + 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 +dns_setserver(u8_t numdns, struct ip_addr *dnsserver) +{ + if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) && + (dnsserver != NULL) && (dnsserver->addr !=0 )) { + 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. + */ +struct ip_addr +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 +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; + for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) { + entry = mem_malloc(sizeof(struct local_hostlist_entry)); + LWIP_ASSERT("mem-error in dns_init_local", entry != NULL); + if (entry != NULL) { + struct local_hostlist_entry *init_entry = &local_hostlist_init[i]; + entry->name = init_entry->name; + 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 + * INADDR_NONE if not found. + */ +static u32_t +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 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 local_hostlist_static[i].addr; + } + } +#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + return INADDR_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 +dns_local_removehost(const char *hostname, const struct ip_addr *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) || (entry->addr == 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; + mem_free(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 +dns_local_addhost(const char *hostname, const struct ip_addr *addr) +{ + struct local_hostlist_entry *entry; + entry = mem_malloc(sizeof(struct local_hostlist_entry)); + if (entry == NULL) { + return ERR_MEM; + } + entry->name = hostname; + entry->addr = 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 struct ip_addr to + * better check for failure: != INADDR_NONE) or INADDR_NONE if the hostname + * was not found in the cached dns_table. + */ +static u32_t +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)) != INADDR_NONE) { + return addr; + } +#endif /* DNS_LOCAL_HOSTLIST */ +#ifdef DNS_LOOKUP_LOCAL_EXTERN + if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != INADDR_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 dns_table[i].ipaddr.addr; + } + } + + return INADDR_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 +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 * +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 +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", dns_servers[numdns].addr != 0); + + /* 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 = 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 = htons(DNS_RRTYPE_A); + qry.class = htons(DNS_RRCLASS_IN); + MEMCPY( query, &qry, SIZEOF_DNS_QUERY); + + /* resize pbuf to the exact dns query */ + pbuf_realloc(p, (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 +dns_check_entry(u8_t i) +{ + 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 */ + dns_send(pEntry->numdns, pEntry->name, i); + break; + } + + case DNS_STATE_ASKING: { + if (--pEntry->tmr == 0) { + if (++pEntry->retries == DNS_MAX_RETRIES) { + if ((pEntry->numdns+1numdns+1].addr!=0)) { + /* 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 */ + dns_send(pEntry->numdns, pEntry->name, i); + } + 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 +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 +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + u8_t i; + char *pHostname; + struct dns_hdr *hdr; + struct dns_answer ans; + struct dns_table_entry *pEntry; + u8_t nquestions, nanswers; +#if (DNS_USES_STATIC_BUF == 0) + u8_t dns_payload[DNS_MSG_SIZE]; +#endif /* (DNS_USES_STATIC_BUF == 0) */ +#if (DNS_USES_STATIC_BUF == 2) + u8_t* dns_payload; +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + 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 memerr1; + } + + /* 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 memerr1; + } + +#if (DNS_USES_STATIC_BUF == 2) + dns_payload = mem_malloc(p->tot_len); + if (dns_payload == NULL) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n")); + /* free pbuf and return */ + goto memerr1; + } +#endif /* (DNS_USES_STATIC_BUF == 2) */ + + /* 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. */ + MEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER); + if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) { + /* 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 */ + MEMCPY( &(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(struct ip_addr)); + 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 memerr2; + } 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 memerr2; + +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; + +memerr2: +#if (DNS_USES_STATIC_BUF == 2) + /* free dns buffer */ + mem_free(dns_payload); +#endif /* (DNS_USES_STATIC_BUF == 2) */ + +memerr1: + /* 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 +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; + + /* 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; + strcpy(pEntry->name, name); + + /* 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. + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a struct ip_addr 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 +dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found, + void *callback_arg) +{ + /* 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_VAL; + } + +#if LWIP_HAVE_LOOPIF + if (strcmp(hostname,"localhost")==0) { + addr->addr = htonl(INADDR_LOOPBACK); + return ERR_OK; + } +#endif /* LWIP_HAVE_LOOPIF */ + + /* host name already in octet notation? set ip addr and return ERR_OK + * already have this address cached? */ + if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) || + ((addr->addr = dns_lookup(hostname)) != INADDR_NONE)) { + return ERR_OK; + } + + /* queue query with specified callback */ + return dns_enqueue(hostname, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/init.c b/component/common/network/lwip/lwip_v1.3.2/src/core/init.c new file mode 100644 index 0000000..2a76d4c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/init.c @@ -0,0 +1,274 @@ +/** + * @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.h" +#include "lwip/snmp_msg.h" +#include "lwip/autoip.h" +#include "lwip/igmp.h" +#include "lwip/dns.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_DNS) + #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" +#endif +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, you have to reduce it 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_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 (PPP_SUPPORT && (NO_SYS==1)) + #error "If you want to use PPP, you have to define NO_SYS=0 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 ((NO_SYS==0) && (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 + + +/* 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 +#if SO_REUSE +/* I removed the lot since this was an ugly hack. It broke the raw-API. + It also came with many ugly goto's, Christiaan Simons. */ + #error "SO_REUSE currently unavailable, this was a hack" +#endif + +#ifdef LWIP_DEBUG +static void +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 or equal to TCP_SND_BUF.\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 */ +} +#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(); + sys_init(); + mem_init(); + 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 + 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 */ +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/autoip.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/autoip.c new file mode 100644 index 0000000..ff89e44 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/autoip.c @@ -0,0 +1,497 @@ +/* + * @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, struct ip_addr *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")); +} + +/** + * 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_start(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_start(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, struct ip_addr *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)); + ipaddr->addr = htonl(addr); + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + ("autoip_create_addr(): tried_llipaddr=%"U16_F", 0x%08"X32_F"\n", + (u16_t)(netif->autoip->tried_llipaddr), (u32_t)(ipaddr->addr))); +} + +/** + * 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; + struct ip_addr sn_mask, gw_addr; + + LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, + ("autoip_bind(netif=%p) %c%c%"U16_F" 0x%08"X32_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num, autoip->llipaddr.addr)); + + 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 + */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + + 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 = 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; + memset(&autoip->llipaddr, 0, sizeof(struct ip_addr)); + autoip->lastconflict = 0; + } + + autoip_create_addr(netif, &(autoip->llipaddr)); + autoip->tried_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; + + /* 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; + } 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; + } + } + 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 + */ + struct ip_addr sipaddr, dipaddr; + struct eth_addr netifaddr; + netifaddr.addr[0] = netif->hwaddr[0]; + netifaddr.addr[1] = netif->hwaddr[1]; + netifaddr.addr[2] = netif->hwaddr[2]; + netifaddr.addr[3] = netif->hwaddr[3]; + netifaddr.addr[4] = netif->hwaddr[4]; + netifaddr.addr[5] = netif->hwaddr[5]; + + /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without + * structure packing (not using structure copy which breaks strict-aliasing rules). + */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(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_start(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/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/icmp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/icmp.c new file mode 100644 index 0000000..7b0a02e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/icmp.c @@ -0,0 +1,340 @@ +/** + * @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.h" +#include "lwip/inet_chksum.h" +#include "lwip/ip.h" +#include "lwip/def.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; + struct ip_addr tmpaddr; + s16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + + iphdr = 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_ECHO: +#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING + { + int accepted = 1; +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip_addr_ismulticast(&iphdr->dest)) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(&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 = 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 = p->payload; + tmpaddr.addr = iphdr->src.addr; + iphdr->src.addr = iphdr->dest.addr; + iphdr->dest.addr = tmpaddr.addr; + ICMPH_TYPE_SET(iecho, ICMP_ER); + + +/* This part of code has been modified by ST's MCD Application Team */ +/* To use the Checksum Offload Engine for the putgoing ICMP packets, + the ICMP checksum field should be set to 0, this is required only for Tx ICMP*/ +#ifdef CHECKSUM_BY_HARDWARE + iecho->chksum = 0; +#else + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += htons(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP_ECHO << 8); + } +#endif + + /* 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; + ret = ip_output_if(p, &(iphdr->src), 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 +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; + + /* ICMP header + IP header + 8 bytes of data */ + 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 = p->payload; + 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 = q->payload; + icmphdr->type = type; + icmphdr->code = code; + icmphdr->id = 0; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + 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; + 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_output(q, NULL, &(iphdr->src), ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/igmp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/igmp.c new file mode 100644 index 0000000..b89a06b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/igmp.c @@ -0,0 +1,757 @@ +/** + * @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.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" + +/*----------------------------------------------------------------------------- + * Globales + *----------------------------------------------------------------------------*/ + +static struct igmp_group* igmp_group_list; +static struct ip_addr allsystems; +static struct ip_addr 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->interface)); + 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->interface == 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->interface == 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, struct ip_addr *addr) +{ + struct igmp_group *group = igmp_group_list; + + while (group != NULL) { + if ((group->interface == 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, struct ip_addr *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 = memp_malloc(MEMP_IGMP_GROUP); + if (group != NULL) { + group->interface = 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 + */ +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, struct ip_addr *dest) +{ + struct ip_hdr * iphdr; + struct igmp_msg* igmp; + struct igmp_group* group; + struct igmp_group* groupref; + + /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ + iphdr = 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 incoming IP address! */ + + /* If group can be found or create... */ + if (!group) { + pbuf_free(p); + 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)) && (igmp->igmp_group_address.addr == 0)) { + /* 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.v1_rxed); + 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; + } + + IGMP_STATS_INC(igmp.group_query_rxed); + groupref = igmp_group_list; + while (groupref) { + /* Do not send messages on the all systems group address! */ + if ((groupref->interface == 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 (group->group_address.addr != 0) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); + ip_addr_debug_print(IGMP_DEBUG, &group->group_address); + if (ip_addr_cmp (dest, &allsystems)) { + 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-lookfor the group since we used dest last time */ + group = igmp_lookfor_group(inp, &igmp->igmp_group_address); + } 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.unicast_query); + igmp_delaying_member( group, igmp->igmp_maxresp); + } + } + } + break; + } + case IGMP_V2_MEMB_REPORT: { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); + + IGMP_STATS_INC(igmp.report_rxed); + 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->interface)); + 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(struct ip_addr *ifaddr, struct ip_addr *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.join_sent); + 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(struct ip_addr *ifaddr, struct ip_addr *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.leave_sent); + 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 -= 1; + 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 + */ +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->interface)); + + 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()) + */ +void +igmp_start_timer(struct igmp_group *group, u8_t max_time) +{ + /** + * @todo Important !! this should be random 0 -> max_time. Find out how to do this + */ + group->timer = max_time; +} + +/** + * Stop a timer for an igmp_group + * + * @param group the igmp_group for which to stop the timer + */ +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 + */ +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) && (maxresp > group->timer))) { + igmp_start_timer(group, (maxresp)/2); + 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 + */ +err_t +igmp_ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, struct netif *netif) +{ + /* This is the "router alert" option */ + u16_t ra[2]; + ra[0] = htons (ROUTER_ALERT); + ra[1] = 0x0000; /* Router shall examine packet */ + return ip_output_if_opt(p, src, dest, ttl, 0, proto, 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 + */ +void +igmp_send(struct igmp_group *group, u8_t type) +{ + struct pbuf* p = NULL; + struct igmp_msg* igmp = NULL; + struct ip_addr src = {0}; + struct ip_addr* dest = NULL; + + /* IP header + "router alert" option + IGMP header */ + p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM); + + if (p) { + igmp = p->payload; + LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg", + (p->len >= sizeof(struct igmp_msg))); + ip_addr_set(&src, &((group->interface)->ip_addr)); + + if (type == IGMP_V2_MEMB_REPORT) { + dest = &(group->group_address); + IGMP_STATS_INC(igmp.report_sent); + ip_addr_set(&(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_set(&(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, IGMP_TTL, IP_PROTO_IGMP, group->interface); + } + + pbuf_free(p); + } else { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n")); + } +} + +#endif /* LWIP_IGMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet.c new file mode 100644 index 0000000..5e0a6aa --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet.c @@ -0,0 +1,278 @@ +/** + * @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" + +/* 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 +inet_addr(const char *cp) +{ + struct in_addr val; + + if (inet_aton(cp, &val)) { + return (val.s_addr); + } + return (INADDR_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 +inet_aton(const char *cp, struct in_addr *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; + } + if (addr) + addr->s_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 * +inet_ntoa(struct in_addr addr) +{ + static char str[16]; + u32_t s_addr = addr.s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + + rp = str; + 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--) + *rp++ = inv[i]; + *rp++ = '.'; + ap++; + } + *--rp = 0; + return str; +} + +/** + * 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 +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 +ntohs(u16_t n) +{ + return 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 +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 +ntohl(u32_t n) +{ + return htonl(n); +} + +#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet_chksum.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet_chksum.c new file mode 100644 index 0000000..739854a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/inet_chksum.c @@ -0,0 +1,438 @@ +/** + * @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/inet.h" + +#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 1 +# endif +#endif +/* If none set: */ +#ifndef LWIP_CHKSUM_ALGORITHM +# define LWIP_CHKSUM_ALGORITHM 0 +#endif + +/** Like the name says... */ +#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 +/* 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 + +/** Split an u32_t in two u16_ts and add them up */ +#define FOLD_U32T(u) ((u >> 16) + (u & 0x0000ffffUL)) + +#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 +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 +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((u32_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 *)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 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 +lwip_standard_chksum(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((u32_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((u32_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 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, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + 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); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->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 + */ +/* Currently only used by UDPLITE, although this could change in the future. */ +#if LWIP_UDPLITE +u16_t +inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len) +{ + u32_t acc; + 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); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->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); +} +#endif /* LWIP_UDPLITE */ + +/* 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); +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip.c new file mode 100644 index 0000000..df6946c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip.c @@ -0,0 +1,768 @@ +/** + * @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.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.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/stats.h" +#include "arch/perf.h" + +#include + +/** + * 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; + +/** + * 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(struct ip_addr *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 ((netif_default == NULL) || (!netif_is_up(netif_default))) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutnoroutes(); + return NULL; + } + /* no matching netif found, use default netif */ + 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 + * @return the netif on which the packet was sent (NULL if it wasn't sent) + */ +static struct netif * +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + /* Find network interface where to forward this IP packet to. */ + netif = ip_route((struct ip_addr *)&(iphdr->dest)); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n", + iphdr->dest.addr)); + snmp_inc_ipoutnoroutes(); + return (struct netif *)NULL; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP + if(netif->flags & NETIF_FLAG_IPSWITCH == 0) { + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + snmp_inc_ipoutnoroutes(); + return (struct netif *)NULL; + } + } +#endif +/* Realtek Modified End */ + + /* 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 (struct netif *)NULL; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%"X32_F"\n", + iphdr->dest.addr)); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if ((netif->flags & NETIF_FLAG_IPSWITCH) && netif->mtu && (p->tot_len > netif->mtu)) { + ip_frag(p,netif,(struct ip_addr *)&(iphdr->dest)); + return netif; + } +#endif +#endif +/* Realtek Modified End */ + /* transmit pbuf on chosen interface */ + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); + return netif; +} +#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 LWIP_DHCP || LWIP_UPNP + int check_ip_src=1; +#endif /* LWIP_DHCP || LWIP_UPNP */ + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = 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); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(&(iphdr->dest))) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(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", + iphdr->dest.addr, netif->ip_addr.addr, + iphdr->dest.addr & netif->netmask.addr, + netif->ip_addr.addr & netif->netmask.addr, + iphdr->dest.addr & ~(netif->netmask.addr))); + + /* 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(&(iphdr->dest), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(&(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 (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); + } + +#if LWIP_DHCP + /* 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 (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest))); + if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* LWIP_DHCP */ + +/* This part of code has been modified by ST's MCD Application Team */ +/* To use the UPnP responder for device discovery */ +#if LWIP_UPNP + /* Pass UPNP messages regardless of destination address. UPNP traffic is addressed + * using multicast addressing so we must not filter on IP. + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: UDP packet to UPNP client port %"U16_F"\n", + ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest))); + if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == 1900) { + LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: UPNP packet accepted.\n")); + netif = inp; + check_ip_src = 0; + } + } + } +#endif /* LWIP_UPNP */ + + /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ +#if LWIP_DHCP || LWIP_UPNP + /* 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 && (iphdr->src.addr != 0)) +#endif /* LWIP_DHCP || LWIP_UPNP */ + { if ((ip_addr_isbroadcast(&(iphdr->src), inp)) || + (ip_addr_ismulticast(&(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 +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP + if(inp->flags & NETIF_FLAG_IPSWITCH) +#else +/* Realtek Modified End */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) +#endif + { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } + /* non-broadcast packet? */ + 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) & 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) & 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 = 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,&(iphdr->dest)); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && + !ip_addr_ismulticast(&(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; + + 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, struct ip_addr *src, struct ip_addr *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, struct ip_addr *src, struct ip_addr *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; + static u16_t ip_id = 0; + + 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) { + /* 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); + } + } +#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 = 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); + + ip_addr_set(&(iphdr->dest), dest); + + IPH_VHLTOS_SET(iphdr, 4, ip_hlen / 4, tos); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_OFFSET_SET(iphdr, 0); + IPH_ID_SET(iphdr, htons(ip_id)); + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen)); +#endif + } else { + /* IP header already included in p */ + iphdr = p->payload; + dest = &(iphdr->dest); + } + + 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); + } +#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 + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + 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, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + 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, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + 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 = 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(&iphdr->src), + ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), + ip4_addr4(&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(&iphdr->dest), + ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), + ip4_addr4(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_addr.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_addr.c new file mode 100644 index 0000000..30a165b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_addr.c @@ -0,0 +1,84 @@ +/** + * @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/inet.h" +#include "lwip/netif.h" + +#define IP_ADDR_ANY_VALUE 0x00000000UL +#define IP_ADDR_BROADCAST_VALUE 0xffffffffUL + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const struct ip_addr ip_addr_any = { IP_ADDR_ANY_VALUE }; +const struct ip_addr ip_addr_broadcast = { IP_ADDR_BROADCAST_VALUE }; + +/** + * 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 ip_addr_isbroadcast(struct ip_addr *addr, struct netif *netif) +{ + u32_t addr2test; + + addr2test = addr->addr; + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((~addr2test == IP_ADDR_ANY_VALUE) || + (addr2test == IP_ADDR_ANY_VALUE)) + 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 (addr2test == netif->ip_addr.addr) + return 0; + /* on the same (sub) network... */ + else if (ip_addr_netcmp(addr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr2test & ~netif->netmask.addr) == + (IP_ADDR_BROADCAST_VALUE & ~netif->netmask.addr))) + /* => network broadcast address */ + return 1; + else + return 0; +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_frag.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_frag.c new file mode 100644 index 0000000..59afe94 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv4/ip_frag.c @@ -0,0 +1,792 @@ +/** + * @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/ip.h" +#include "lwip/inet.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 to be packed since it has to fit inside the IP header. + */ +#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); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * 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) +{ + int pbufs_freed = 0; + 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); + pbufs_freed += pbuf_clen(p); + 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; + pbufs_freed += pbuf_clen(pcur); + 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* +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 = memp_malloc(MEMP_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { + ipr = 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 +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 +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 ((ntohs(IPH_OFFSET(fraghdr)) & 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)]; +#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, struct ip_addr *dest) +{ + struct pbuf *rambuf; +#if IP_FRAG_USES_STATIC_BUF + struct pbuf *header; +#else + struct pbuf *newpbuf; + 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 + 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 = rambuf->payload; + SMEMCPY(iphdr, p->payload, IP_HLEN); +#else /* IP_FRAG_USES_STATIC_BUF */ + original_iphdr = 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 */ + /* 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 = 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) { + newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; + /* Is this pbuf already empty? */ + if (!newpbuflen) { + p = p->next; + continue; + } + newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); + if (newpbuf == NULL) { + pbuf_free(rambuf); + return ERR_MEM; + } + /* Mirror this pbuf, although we might not need all of it. */ + newpbuf->payload = p->payload; + newpbuf->len = newpbuf->tot_len = newpbuflen; + /* 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 /* 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/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/README b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/README new file mode 100644 index 0000000..3620004 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/icmp6.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/icmp6.c new file mode 100644 index 0000000..4fcc895 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/icmp6.c @@ -0,0 +1,179 @@ +/* + * 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.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" + +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + + ICMP_STATS_INC(icmp.recv); + + /* TODO: check length before accessing payload! */ + + type = ((u8_t *)p->payload)[0]; + + switch (type) { + case ICMP6_ECHO: + 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")); + + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + return; + } + iecho = p->payload; + iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN); + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.chkerr); + /* return;*/ + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len)); + ip_addr_set(&tmpaddr, &(iphdr->src)); + ip_addr_set(&(iphdr->src), &(iphdr->dest)); + ip_addr_set(&(iphdr->dest), &tmpaddr); + iecho->type = ICMP6_ER; + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) { + iecho->chksum += htons(ICMP6_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP6_ECHO << 8); + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.xmit); + + /* LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ + ip_output_if (p, &(iphdr->src), IP_HDRINCL, + iphdr->hoplim, IP_PROTO_ICMP, inp); + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + + pbuf_free(p); +} + +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + idur = q->payload; + idur->type = (u8_t)ICMP6_DUR; + idur->icode = (u8_t)t; + + SMEMCPY((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); + ICMP_STATS_INC(icmp.xmit); + + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n")); + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + tehdr = q->payload; + tehdr->type = (u8_t)ICMP6_TE; + tehdr->icode = (u8_t)t; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); + ICMP_STATS_INC(icmp.xmit); + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/inet6.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/inet6.c new file mode 100644 index 0000000..c3de85c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/inet6.c @@ -0,0 +1,163 @@ +/** + * @file + * Functions common to all TCP/IPv6 modules, such as the Internet checksum and 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/def.h" +#include "lwip/inet.h" + +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ + +static u32_t +chksum(void *dataptr, u16_t len) +{ + u16_t *sdataptr = dataptr; + u32_t acc; + + + for(acc = 0; len > 1; len -= 2) { + acc += *sdataptr++; + } + + /* add up any odd byte */ + if (len == 1) { + acc += htons((u16_t)(*(u8_t *)dataptr) << 8); + } + + return acc; + +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ + +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped, i; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + + for(i = 0; i < 8; i++) { + acc += ((u16_t *)src->addr)[i] & 0xffff; + acc += ((u16_t *)dest->addr)[i] & 0xffff; + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + } + acc += (u16_t)htons((u16_t)proto); + acc += ((u16_t *)&proto_len)[0] & 0xffff; + acc += ((u16_t *)&proto_len)[1] & 0xffff; + + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc, sum; + + acc = chksum(dataptr, len); + sum = (acc & 0xffff) + (acc >> 16); + sum += (sum >> 16); + return ~(sum & 0xffff); +} + +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 += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6.c new file mode 100644 index 0000000..7e43420 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6.c @@ -0,0 +1,397 @@ +/* + * 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 + * + */ + + + +/* ip.c + * + * This is the code for the IP layer for IPv6. + * + */ + + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" + +/* ip_init: + * + * Initializes the IP layer. + */ + +void +ip_init(void) +{ +} + +/* ip_route: + * + * 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. + */ + +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + return netif; + } + } + + return netif_default; +} + +/* 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. + */ + +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr) +{ + struct netif *netif; + + PERF_START; + + if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { + + LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + pbuf_free(p); + return; + } + /* Decrement TTL and send ICMP if ttl == 0. */ + if (--iphdr->hoplim == 0) { +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (iphdr->nexthdr != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + return; + } + + /* Incremental update of the IP checksum. */ + /* if (iphdr->chksum >= htons(0xffff - 0x100)) { + iphdr->chksum += htons(0x100) + 1; + } else { + iphdr->chksum += htons(0x100); + }*/ + + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + + PERF_STOP("ip_forward"); + + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); +} + +/* ip_input: + * + * 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. + */ + +void +ip_input(struct pbuf *p, struct netif *inp) { + struct ip_hdr *iphdr; + struct netif *netif; + + + PERF_START; + +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + + IP_STATS_INC(ip.recv); + + /* identify the IP header */ + iphdr = p->payload; + + + if (iphdr->v != 6) { + LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n")); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + return; + } + + /* is this packet for us? */ + for(netif = netif_list; netif != NULL; netif = netif->next) { +#if IP_DEBUG + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("\n")); +#endif /* IP_DEBUG */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) { + break; + } + } + + + if (netif == NULL) { + /* packet not for us, route or discard */ +#if IP_FORWARD + ip_forward(p, iphdr); +#endif + pbuf_free(p); + return; + } + + pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len)); + + /* send to upper layers */ +#if IP_DEBUG + /* LWIP_DEBUGF("ip_input: \n"); + ip_debug_print(p); + LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ +#endif /* IP_DEBUG */ + + if(pbuf_header(p, -IP_HLEN)) { + LWIP_ASSERT("Can't move over header in packet", 0); + return; + } + + switch (iphdr->nexthdr) { + case IP_PROTO_UDP: + udp_input(p, inp); + break; + case IP_PROTO_TCP: + tcp_input(p, inp); + break; +#if LWIP_ICMP + case IP_PROTO_ICMP: + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable */ + icmp_dest_unreach(p, ICMP_DUR_PROTO); +#endif /* LWIP_ICMP */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n", + iphdr->nexthdr)); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + } + PERF_STOP("ip_input"); +} + + +/* ip_output_if: + * + * 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. + */ + +err_t +ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, + u8_t proto, struct netif *netif) +{ + struct ip_hdr *iphdr; + + PERF_START; + + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n")); + IP_STATS_INC(ip.err); + + return ERR_BUF; + } + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + + iphdr = p->payload; + + + if (dest != IP_HDRINCL) { + LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n")); + iphdr->hoplim = ttl; + iphdr->nexthdr = proto; + iphdr->len = htons(p->tot_len - IP_HLEN); + ip_addr_set(&(iphdr->dest), dest); + + iphdr->v = 6; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + } else { + dest = &(iphdr->dest); + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len)); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + PERF_STOP("ip_output_if"); + return netif->output(netif, p, dest); +} + +/* ip_output: + * + * Simple interface to ip_output_if. It finds the outgoing network interface and + * calls upon ip_output_if to do the actual work. + */ + +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto) +{ + struct netif *netif; + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if (p, src, dest, ttl, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +err_t +ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + 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 +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" | %"X16_F"%"X16_F" | %"X16_F"%"X16_F" | (v, traffic class, flow label)\n", + iphdr->v, + iphdr->tclass1, iphdr->tclass2, + iphdr->flow1, iphdr->flow2)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" | %2"U16_F" | %2"U16_F" | (len, nexthdr, hoplim)\n", + ntohs(iphdr->len), + iphdr->nexthdr, + iphdr->hoplim)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6_addr.c b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..2da6cea --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/ipv6/ip6_addr.c @@ -0,0 +1,72 @@ +/* + * 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/inet.h" + +u8_t +ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask) +{ + return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) && + (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) && + (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) && + (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3])); + +} + +u8_t +ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2) +{ + return(addr1->addr[0] == addr2->addr[0] && + addr1->addr[1] == addr2->addr[1] && + addr1->addr[2] == addr2->addr[2] && + addr1->addr[3] == addr2->addr[3]); +} + +void +ip_addr_set(struct ip_addr *dest, struct ip_addr *src) +{ + SMEMCPY(dest, src, sizeof(struct ip_addr)); + /* dest->addr[0] = src->addr[0]; + dest->addr[1] = src->addr[1]; + dest->addr[2] = src->addr[2]; + dest->addr[3] = src->addr[3];*/ +} + +u8_t +ip_addr_isany(struct ip_addr *addr) +{ + if (addr == NULL) return 1; + return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0); +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/mem.c b/component/common/network/lwip/lwip_v1.3.2/src/core/mem.c new file mode 100644 index 0000000..008c2c9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/mem.c @@ -0,0 +1,633 @@ +/** + * @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 + +#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++) { +#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[next]) of the next struct */ + mem_size_t prev; + /** 1: this area is used; 0: this area is unused */ + u8_t used; +}; + +/** 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) + +/** the heap. we need one struct mem at the end and some room for alignment */ +static u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +/** 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_sem_t mem_sem; + +#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_arch_sem_wait(mem_sem, 0) +#define LWIP_MEM_FREE_UNPROTECT() sys_sem_signal(mem_sem) +/* 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_realloc() + * + * This assumes access to the heap is protected by the calling function + * already. + */ +static void +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 *)&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 *)&ram[nmem->next])->prev = (u8_t *)mem - ram; + } + + /* plug hole backward */ + pmem = (struct mem *)&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 *)&ram[mem->next])->prev = (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 = LWIP_MEM_ALIGN(ram_heap); + /* initialize the start of the heap */ + mem = (struct mem *)ram; + mem->next = MEM_SIZE_ALIGNED; + mem->prev = 0; + mem->used = 0; + /* initialize the end of the heap */ + ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED]; + ram_end->used = 1; + ram_end->next = MEM_SIZE_ALIGNED; + ram_end->prev = MEM_SIZE_ALIGNED; + + mem_sem = sys_sem_new(1); + + /* initialize the lowest-free pointer to the start of the heap */ + lfree = (struct mem *)ram; + + MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED); +} + +/** + * 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 *)((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 - ((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(); +} + +/** + * In contrast to its name, mem_realloc can only shrink memory, not expand it. + * Since the only use (for now) is in pbuf_realloc (which also can only shrink), + * this shouldn't be a problem! + * + * @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_realloc(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_realloc: 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_realloc: 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 *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + /* ... and its offset pointer */ + ptr = (u8_t *)mem - ram; + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; + LWIP_ASSERT("mem_realloc 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(); + + MEM_STATS_DEC_USED(used, (size - newsize)); + + mem2 = (struct mem *)&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 *)&ram[ptr2]; + } + mem2 = (struct mem *)&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 *)&ram[mem2->next])->prev = ptr2; + } + /* 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 *)&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 *)&ram[mem2->next])->prev = ptr2; + } + /* 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_arch_sem_wait(mem_sem, 0); + 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 = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; + ptr = ((struct mem *)&ram[ptr])->next) { + mem = (struct mem *)&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 *)&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 *)&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 - ((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 *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used))); + } + LWIP_MEM_ALLOC_UNPROTECT(); + sys_sem_signal(mem_sem); + 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_sem_signal(mem_sem); + 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/component/common/network/lwip/lwip_v1.3.2/src/core/memp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/memp.c new file mode 100644 index 0000000..8a34c4b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/memp.c @@ -0,0 +1,386 @@ +/** + * @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.h" +#include "lwip/igmp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "netif/etharp.h" +#include "lwip/ip_frag.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] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#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) (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] = { +#define LWIP_MEMPOOL(name,num,size,desc) (desc), +#include "lwip/memp_std.h" +}; +#endif /* LWIP_DEBUG */ + +/** This is the actual memory used by the pools. */ +static u8_t memp_memory[MEM_ALIGNMENT - 1 +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) +#include "lwip/memp_std.h" +]; + +#if MEMP_SANITY_CHECK +/** + * Check that memp-lists don't form a circle + */ +static int +memp_sanity(void) +{ + s16_t i, c; + struct memp *m, *n; + + for (i = 0; i < MEMP_MAX; i++) { + for (m = memp_tab[i]; m != NULL; m = m->next) { + c = 1; + for (n = memp_tab[i]; n != NULL; n = n->next) { + if (n == m && --c < 0) { + return 0; + } + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ +#if MEMP_OVERFLOW_CHECK +/** + * 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_size the element size of the pool p comes from + */ +static void +memp_overflow_check_element(struct memp *p, u16_t memp_size) +{ + 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) { + LWIP_ASSERT("detected memp underflow!", 0); + } + } +#endif +#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0 + m = (u8_t*)p + MEMP_SIZE + memp_size; + for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) { + if (m[k] != 0xcd) { + LWIP_ASSERT("detected memp overflow!", 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 +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + + p = 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(p, memp_sizes[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 +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + + p = 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]); + } + + memp = LWIP_MEM_ALIGN(memp_memory); + /* for every pool: */ + for (i = 0; i < MEMP_MAX; ++i) { + memp_tab[i] = NULL; + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = memp_tab[i]; + memp_tab[i] = memp; + memp = (struct memp *)((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*)((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 *)((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(memp, memp_sizes[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 */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/netif.c b/component/common/network/lwip/lwip_v1.3.2/src/core/netif.c new file mode 100644 index 0000000..6c697e7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/netif.c @@ -0,0 +1,687 @@ +/** + * @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.h" +#include "lwip/snmp.h" +#include "lwip/igmp.h" +#include "netif/etharp.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) { if (n->status_callback) (n->status_callback)(n); } +#else +#define NETIF_STATUS_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +#define NETIF_LINK_CALLBACK(n) { if (n->link_callback) (n->link_callback)(n); } +#else +#define NETIF_LINK_CALLBACK(n) { /* NOP */ } +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +struct netif *netif_list; +struct netif *netif_default; + +/** + * 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, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + static u8_t netifnum = 0; + + /* reset new interface configuration state */ + netif->ip_addr.addr = 0; + netif->netmask.addr = 0; + netif->gw.addr = 0; + 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, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *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 */ + + snmp_delete_ipaddridx_tree(netif); + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + snmp_dec_iflist(); + } + 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; + snmp_dec_iflist(); + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + /* 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, struct ip_addr *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 ((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))) { + /* 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(&netif->ip_addr), + ip4_addr2(&netif->ip_addr), + ip4_addr3(&netif->ip_addr), + ip4_addr4(&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, struct ip_addr *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(&netif->gw), + ip4_addr2(&netif->gw), + ip4_addr3(&netif->gw), + ip4_addr4(&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, struct ip_addr *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(&netif->netmask), + ip4_addr2(&netif->netmask), + ip4_addr3(&netif->netmask), + ip4_addr4(&netif->netmask))); +} + +#if LWIP_NETIF_HOSTNAME +void netif_set_hostname(struct netif *netif, char* hostname){ + netif->hostname = hostname; +} +#endif + +/** + * 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_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); + +#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 + + NETIF_LINK_CALLBACK(netif); + NETIF_STATUS_CALLBACK(netif); + } +} + +/** + * Ask if an interface is up + */ +u8_t netif_is_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_UP)?1:0; +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Set callback to be called when interface is brought up/down + */ +void netif_set_status_callback(struct netif *netif, void (* status_callback)(struct netif *netif )) +{ + if ( netif ) + netif->status_callback = status_callback; +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Called by a driver when its link goes up + */ +void netif_set_link_up(struct netif *netif ) +{ + 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 ) +{ + netif->flags &= ~NETIF_FLAG_LINK_UP; + NETIF_LINK_CALLBACK(netif); +} + +/** + * Ask if a link is up + */ +u8_t netif_is_link_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_LINK_UP) ? 1 : 0; +} + +/** + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif )) +{ + 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, + struct ip_addr *ipaddr) +{ + struct pbuf *r; + err_t err; + struct pbuf *last; +#if LWIP_LOOPBACK_MAX_PBUFS + u8_t clen = 0; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + 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) { + 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); + r = NULL; + 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); + r = NULL; + 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); + +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* For multithreading environment, schedule a call to netif_poll */ + tcpip_callback((void (*)(void *))(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; + 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) { + /* 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/component/common/network/lwip/lwip_v1.3.2/src/core/pbuf.c b/component/common/network/lwip/lwip_v1.3.2/src/core/pbuf.c new file mode 100644 index 0000000..59de5bc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/pbuf.c @@ -0,0 +1,929 @@ +/** + * @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.h" +#endif + +#include + +#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 !TCP_QUEUE_OOSEQ || NO_SYS +#define PBUF_POOL_IS_EMPTY() +#else /* !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 +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 +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 /* !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; + break; + case 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 = 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 = 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; + /* 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 = 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; +} + + +/** + * 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 = mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); + LWIP_ASSERT("mem_realloc give 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) { + 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 */ + 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); + + 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; + /* 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) { + 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: The source pbuf 'p' is not freed by this function because that can + * be illegal in some places! + * + * @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; +} diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/raw.c b/component/common/network/lwip/lwip_v1.3.2/src/core/raw.c new file mode 100644 index 0000000..57acd07 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/raw.c @@ -0,0 +1,353 @@ +/** + * @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/inet.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" +#include "lwip/stats.h" +#include "lwip/snmp.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 +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 = 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) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((pcb->so_options & SOF_BROADCAST) || !ip_addr_isbroadcast(&(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, &(iphdr->src)) != 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 +raw_bind(struct raw_pcb *pcb, struct ip_addr *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 +raw_connect(struct raw_pcb *pcb, struct ip_addr *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 +raw_recv(struct raw_pcb *pcb, + u8_t (* recv)(void *arg, struct raw_pcb *upcb, struct pbuf *p, + struct ip_addr *addr), + 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 +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr) +{ + err_t err; + struct netif *netif; + struct ip_addr *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; + } + /* 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 0x%"X32_F"\n", ipaddr->addr)); + /* 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 +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 +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 * +raw_new(u8_t proto) { + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n")); + + 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/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_dec.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_dec.c new file mode 100644 index 0000000..650fb40 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_enc.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_enc.c new file mode 100644 index 0000000..77af6b4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = length; + } + else + { + /* most significant length octet */ + *msg_ptr = length >> 8; + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = value >> (octets_needed << 3); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (ident[0] * 40) + ident[1]; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = sub_id >> shift; + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib2.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib2.c new file mode 100644 index 0000000..bc5830d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib2.c @@ -0,0 +1,4128 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/tcp.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar, + (struct mib_node* const)&snmp_scalar, (struct mib_node* const)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node* const)&udp_root, (struct mib_node* const)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node* const)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udp_scalar, (struct mib_node* const)&udp_scalar, + (struct mib_node* const)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root, (struct mib_node* const)&tcpconntree_root, + (struct mib_node* const)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node* const)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcpconntable, (struct mib_node* const)&tcp_scalar, + (struct mib_node* const)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 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, 26 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar, + (struct mib_node* const)&icmp_scalar, (struct mib_node* const)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root, + (struct mib_node* const)&ipntomtree_root, (struct mib_node* const)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node* const)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root, (struct mib_node* const)&iprtetree_root, + (struct mib_node* const)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node* const)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root, + (struct mib_node* const)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node* const)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ip_scalar, + (struct mib_node* const)&ip_scalar, (struct mib_node* const)&ipaddrtable, + (struct mib_node* const)&iprtetable, (struct mib_node* const)&ipntomtable, + (struct mib_node* const)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root, + (struct mib_node* const)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node* const)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node* const)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root, + (struct mib_node* const)&iflist_root, (struct mib_node* const)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node* const)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node* const)&interfaces_scalar, (struct mib_node* const)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar, (struct mib_node* const)&sys_tem_scalar, + (struct mib_node* const)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node* const)&sys_tem, + (struct mib_node* const)&interfaces, + (struct mib_node* const)&at, + (struct mib_node* const)&mib2_ip, + (struct mib_node* const)&icmp, +#if LWIP_TCP + (struct mib_node* const)&tcp, +#endif + (struct mib_node* const)&udp, + (struct mib_node* const)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node* const)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#if SNMP_PRIVATE_MIB +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node* const)&mgmt, (struct mib_node* const)&private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node* const)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +void ocstrncpy(u8_t *dst, u8_t *src, u8_t n) +{ + while (n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + while(n > 0) + { + n--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + struct ip_addr hip; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + hip.addr = ntohl(ip->addr); + snmp_iptooid(&hip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + struct ip_addr ip; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + ip.addr = ntohl(ni->ip_addr.addr); + snmp_iptooid(&ip, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + insert = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) insert = 1; + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t delete = 0; + struct ip_addr dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + dst.addr = 0; + delete = 1; + } + else + { + /* route to the network address */ + dst.addr = ntohl(ni->ip_addr.addr & ni->netmask.addr); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (dst.addr != 0) delete = 1; + } + if (delete) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + struct ip_addr ip; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + ip.addr = ntohl(pcb->local_ip.addr); + snmp_iptooid(&ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + pcb = udp_pcbs; + while ((pcb != NULL)) + { + if ((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == udpidx[4])) + { + bindings++; + } + pcb = pcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy(value,sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t))); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime(value); + } + break; + case 4: /* sysContact */ + ocstrncpy(value,syscontact_ptr,len); + break; + case 5: /* sysName */ + ocstrncpy(value,sysname_ptr,len); + break; + case 6: /* sysLocation */ + ocstrncpy(value,syslocation_ptr,len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + id = od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr,value,len); + *syscontact_len_ptr = len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr,value,len); + *sysname_len_ptr = len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr,value,len); + *syslocation_len_ptr = len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy(value,(u8_t*)netif->name,len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy(value,netif->hwaddr,len); + break; + case 7: /* ifAdminStatus */ +#if LWIP_NETIF_LINK_CALLBACK + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; +#endif + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t))); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value (struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + struct ip_addr ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + struct ip_addr *dst = value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + struct ip_addr *dst = value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = ip_addr_broadcast.addr & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + struct ip_addr dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + dest.addr = htonl(dest.addr); + + if (dest.addr == 0) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + id = ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte has 0.0.0.0 dest */ + dst->addr = 0; + } + else + { + /* netifs have netaddress dest */ + dst->addr = netif->ip_addr.addr & netif->netmask.addr; + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = value; + + if (dest.addr == 0) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + struct ip_addr *dst = value; + + if (dest.addr == 0) + { + /* default rte use 0.0.0.0 mask */ + dst->addr = 0; + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t))); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + struct ip_addr* ipaddr_ret; +#endif /* LWIP_ARP */ + struct ip_addr ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + ip.addr = htonl(ip.addr); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + struct ip_addr *dst = value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + s32_t *sint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct ip_addr lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lip.addr = htonl(lip.addr); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rip.addr = htonl(rip.addr); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + struct ip_addr ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ip.addr = htonl(ip.addr); + port = od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !((pcb->local_ip.addr == ip.addr) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + struct ip_addr *dst = value; + *dst = pcb->local_ip; + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = value; + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + id = od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = value; + *snmpenableauthentraps_ptr = *sint_ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib_structs.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib_structs.c new file mode 100644 index 0000000..39a2949 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/mib_structs.c @@ -0,0 +1,1183 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/mem.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + u16_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, struct ip_addr *ip) +{ + u32_t ipa; + + ipa = ident[0]; + ipa <<= 8; + ipa |= ident[1]; + ipa <<= 8; + ipa |= ident[2]; + ipa <<= 8; + ipa |= ident[3]; + ip->addr = ipa; +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(struct ip_addr *ip, s32_t *ident) +{ + u32_t ipa; + + ipa = ip->addr; + ident[0] = (ipa >> 24) & 0xff; + ident[1] = (ipa >> 16) & 0xff; + ident[2] = (ipa >> 8) & 0xff; + ident[3] = ipa & 0xff; +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)mem_malloc(sizeof(struct mib_list_node)); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + mem_free(ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)mem_malloc(sizeof(struct mib_list_rootnode)); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + mem_free(lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (rerurn) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + j = i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_in.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_in.c new file mode 100644 index 0000000..d0c3c75 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_in.c @@ -0,0 +1,1454 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip_addr.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + msg_ps->error_index = 1 + msg_ps->vb_idx; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + vb->value_len = msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + vb->value_len = object_def.v_len; + if (vb->value_len > 0) + { + vb->value = mem_malloc(vb->value_len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + mem_free(vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + vb = snmp_varbind_alloc(&oid, object_def.asn_type, object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access == MIB_OBJECT_READ_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + struct udp_hdr *udphdr; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + /* peek in the UDP header (goto IP payload) */ + if(pbuf_header(p, UDP_HLEN)){ + LWIP_ASSERT("Can't move to UDP header", 0); + pbuf_free(p); + return; + } + udphdr = p->payload; + + /* check if datagram is really directed at us (including broadcast requests) */ + if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT)) + { + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idxstate != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx != SNMP_CONCURRENT_REQUESTS) + { + err_t err_ret; + u16_t payload_len; + u16_t payload_ofs; + u16_t varbind_ofs = 0; + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + /* read UDP payload length from UDP header */ + payload_len = ntohs(udphdr->len) - UDP_HLEN; + + /* adjust to UDP payload */ + payload_ofs = UDP_HLEN; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) || + (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) && + ((msg_ps->error_status == SNMP_ES_NOERROR) && + (msg_ps->error_index == 0)) ) + { + /* Only accept requests and requests without error (be robust) */ + err_ret = err_ret; + } + else + { + /* Reject response and trap headers or error requests as input! */ + err_ret = ERR_ARG; + } + if (err_ret == ERR_OK) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0)) + { + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); + } + else + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + } + } + else + { + /* header check failed + drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + } + } + else + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + } + } + else + { + /* datagram not for us */ + pbuf_free(p); + } +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb = snmp_varbind_alloc(&oid, type, len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind)); + LWIP_ASSERT("vb != NULL",vb != NULL); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i); + LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL); + if (vb->ident == NULL) + { + mem_free(vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + /* allocate raw bytes for our object value */ + vb->value = mem_malloc(len); + LWIP_ASSERT("vb->value != NULL",vb->value != NULL); + if (vb->value == NULL) + { + if (vb->ident != NULL) + { + mem_free(vb->ident); + } + mem_free(vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + mem_free(vb->value); + } + if (vb->ident != NULL ) + { + mem_free(vb->ident); + } + mem_free(vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_out.c b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_out.c new file mode 100644 index 0000000..b705aac --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/snmp/msg_out.c @@ -0,0 +1,683 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + struct ip_addr dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, struct ip_addr *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].dip.addr = htonl(dst->addr); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + if (m_stat->error_status == SNMP_ES_TOOBIG) + { + snmp_varbind_list_enc(&emptyvb, p, ofs); + } + else + { + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + } + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + struct ip_addr dst_ip; + struct pbuf *p; + u16_t i,tot_len; + + for (i=0, td = &trap_dst[0]; ienable != 0) && (td->dip.addr != 0)) + { + /* network order trap destination */ + trap_msg.dip.addr = td->dip.addr; + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + dst_ip.addr = ntohl(dst_if->ip_addr.addr); + trap_msg.sip_raw[0] = dst_ip.addr >> 24; + trap_msg.sip_raw[1] = dst_ip.addr >> 16; + trap_msg.sip_raw[2] = dst_ip.addr >> 8; + trap_msg.sip_raw[3] = dst_ip.addr; + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** connect to the TRAP destination */ + udp_connect(trap_msg.pcb, &trap_msg.dip, SNMP_TRAP_PORT); + udp_send(trap_msg.pcb, p); + /** disassociate remote address and port with this pcb */ + udp_disconnect(trap_msg.pcb); + + pbuf_free(p); + } + else + { + return ERR_MEM; + } + } + } + return ERR_OK; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/stats.c b/component/common/network/lwip/lwip_v1.3.2/src/core/stats.c new file mode 100644 index 0000000..2ef179d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/stats.c @@ -0,0 +1,149 @@ +/** + * @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; + +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, char *name) +{ + LWIP_PLATFORM_DIAG("\n\r%s\n\r\t", name); + LWIP_PLATFORM_DIAG("xmit: %"STAT_COUNTER_F"\n\r\t", proto->xmit); + LWIP_PLATFORM_DIAG("recv: %"STAT_COUNTER_F"\n\r\t", proto->recv); + LWIP_PLATFORM_DIAG("fw: %"STAT_COUNTER_F"\n\r\t", proto->fw); + LWIP_PLATFORM_DIAG("drop: %"STAT_COUNTER_F"\n\r\t", proto->drop); + LWIP_PLATFORM_DIAG("chkerr: %"STAT_COUNTER_F"\n\r\t", proto->chkerr); + LWIP_PLATFORM_DIAG("lenerr: %"STAT_COUNTER_F"\n\r\t", proto->lenerr); + LWIP_PLATFORM_DIAG("memerr: %"STAT_COUNTER_F"\n\r\t", proto->memerr); + LWIP_PLATFORM_DIAG("rterr: %"STAT_COUNTER_F"\n\r\t", proto->rterr); + LWIP_PLATFORM_DIAG("proterr: %"STAT_COUNTER_F"\n\r\t", proto->proterr); + LWIP_PLATFORM_DIAG("opterr: %"STAT_COUNTER_F"\n\r\t", proto->opterr); + LWIP_PLATFORM_DIAG("err: %"STAT_COUNTER_F"\n\r\t", proto->err); + LWIP_PLATFORM_DIAG("cachehit: %"STAT_COUNTER_F"\n\r", proto->cachehit); +} + +#if IGMP_STATS +void +stats_display_igmp(struct stats_igmp *igmp) +{ + LWIP_PLATFORM_DIAG("\n\rIGMP\n\r\t")); + LWIP_PLATFORM_DIAG("lenerr: %"STAT_COUNTER_F"\n\r\t", igmp->lenerr); + LWIP_PLATFORM_DIAG("chkerr: %"STAT_COUNTER_F"\n\r\t", igmp->chkerr); + LWIP_PLATFORM_DIAG("v1_rxed: %"STAT_COUNTER_F"\n\r\t", igmp->v1_rxed); + LWIP_PLATFORM_DIAG("join_sent: %"STAT_COUNTER_F"\n\r\t", igmp->join_sent); + LWIP_PLATFORM_DIAG("leave_sent: %"STAT_COUNTER_F"\n\r\t", igmp->leave_sent); + LWIP_PLATFORM_DIAG("unicast_query: %"STAT_COUNTER_F"\n\r\t", igmp->unicast_query); + LWIP_PLATFORM_DIAG("report_sent: %"STAT_COUNTER_F"\n\r\t", igmp->report_sent); + LWIP_PLATFORM_DIAG("report_rxed: %"STAT_COUNTER_F"\n\r\t", igmp->report_rxed); + LWIP_PLATFORM_DIAG("group_query_rxed: %"STAT_COUNTER_F"\n\r", igmp->group_query_rxed); +} +#endif /* IGMP_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, char *name) +{ + LWIP_PLATFORM_DIAG("\n\rMEM %s\n\r\t", name); + LWIP_PLATFORM_DIAG("avail: %"U32_F"\n\r\t", (u32_t)mem->avail); + LWIP_PLATFORM_DIAG("used: %"U32_F"\n\r\t", (u32_t)mem->used); + LWIP_PLATFORM_DIAG("max: %"U32_F"\n\r\t", (u32_t)mem->max); + LWIP_PLATFORM_DIAG("err: %"U32_F"\n\r", (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("\n\rSYS\n\r\t"); + LWIP_PLATFORM_DIAG("sem.used: %"U32_F"\n\r\t", (u32_t)sys->sem.used); + LWIP_PLATFORM_DIAG("sem.max: %"U32_F"\n\r\t", (u32_t)sys->sem.max); + LWIP_PLATFORM_DIAG("sem.err: %"U32_F"\n\r\t", (u32_t)sys->sem.err); + LWIP_PLATFORM_DIAG("mbox.used: %"U32_F"\n\r\t", (u32_t)sys->mbox.used); + LWIP_PLATFORM_DIAG("mbox.max: %"U32_F"\n\r\t", (u32_t)sys->mbox.max); + LWIP_PLATFORM_DIAG("mbox.err: %"U32_F"\n\r\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/component/common/network/lwip/lwip_v1.3.2/src/core/sys.c b/component/common/network/lwip/lwip_v1.3.2/src/core/sys.c new file mode 100644 index 0000000..cb5e86a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/sys.c @@ -0,0 +1,346 @@ +/** + * @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" + +#if (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/tcpip.h" + +/** + * Struct used for sys_sem_wait_timeout() to tell wether the time + * has run out or the semaphore has really become available. + */ +struct sswt_cb +{ + s16_t timeflag; + sys_sem_t *psem; +}; + +/** + * Wait (forever) for a message to arrive in an mbox. + * While waiting, timeouts (for this thread) are processed. + * + * @param mbox the mbox to fetch the message from + * @param msg the place to store the message + */ +void +sys_mbox_fetch(sys_mbox_t mbox, void **msg) +{ + u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; + void *arg; + + again: + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, 0); + LOCK_TCPIP_CORE(); + } else { + if (timeouts->next->time > 0) { + UNLOCK_TCPIP_CORE(); + time_needed = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + LOCK_TCPIP_CORE(); + } 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 = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", *(void**)&h, arg)); + h(arg); + } + + /* 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 < timeouts->next->time) { + timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } + } + } +} + +/** + * Wait (forever) for a semaphore to become available. + * While waiting, timeouts (for this thread) are processed. + * + * @param sem semaphore to wait for + */ +void +sys_sem_wait(sys_sem_t sem) +{ + u32_t time_needed; + struct sys_timeouts *timeouts; + struct sys_timeo *tmptimeout; + sys_timeout_handler h; + void *arg; + + again: + + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + sys_arch_sem_wait(sem, 0); + } else { + if (timeouts->next->time > 0) { + time_needed = sys_arch_sem_wait(sem, timeouts->next->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 = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", *(void**)&h, (void *)arg)); + h(arg); + } + + /* 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 < timeouts->next->time) { + timeouts->next->time -= time_needed; + } else { + timeouts->next->time = 0; + } + } + } +} + +/** + * Create a one-shot timer (aka timeout). Timeouts are processed in the + * following cases: + * - while waiting for a message using sys_mbox_fetch() + * - while waiting for a semaphore using sys_sem_wait() or sys_sem_wait_timeout() + * - while sleeping using the inbuilt sys_msleep() + * + * @param msecs time in milliseconds after that the timer should expire + * @param h callback function to call when msecs have elapsed + * @param arg argument to pass to the callback function + */ +void +sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *timeout, *t; + + timeout = memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + LWIP_ASSERT("sys_timeout: timeout != NULL", timeout != NULL); + return; + } + timeout->next = NULL; + timeout->h = h; + timeout->arg = arg; + timeout->time = msecs; + + timeouts = sys_arch_timeouts(); + + LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n", + (void *)timeout, msecs, *(void**)&h, (void *)arg)); + + if (timeouts == NULL) { + LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL); + return; + } + + if (timeouts->next == NULL) { + timeouts->next = timeout; + return; + } + + if (timeouts->next->time > msecs) { + timeouts->next->time -= msecs; + timeout->next = timeouts->next; + timeouts->next = timeout; + } else { + for(t = timeouts->next; 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 'h' in the list of timeouts. + * + * @param h callback function that would be called by the timeout + * @param arg callback argument that would be passed to h +*/ +void +sys_untimeout(sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *prev_t, *t; + + timeouts = sys_arch_timeouts(); + + if (timeouts == NULL) { + LWIP_ASSERT("sys_untimeout: timeouts != NULL", timeouts != NULL); + return; + } + if (timeouts->next == NULL) { + return; + } + + for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) { + if ((t->h == h) && (t->arg == arg)) { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) { + timeouts->next = 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; +} + +/** + * Timeout handler function for sys_sem_wait_timeout() + * + * @param arg struct sswt_cb* used to signal a semaphore and end waiting. + */ +static void +sswt_handler(void *arg) +{ + struct sswt_cb *sswt_cb = (struct sswt_cb *) arg; + + /* Timeout. Set flag to TRUE and signal semaphore */ + sswt_cb->timeflag = 1; + sys_sem_signal(*(sswt_cb->psem)); +} + +/** + * Wait for a semaphore with timeout (specified in ms) + * + * @param sem semaphore to wait + * @param timeout timeout in ms (0: wait forever) + * @return 0 on timeout, 1 otherwise + */ +int +sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout) +{ + struct sswt_cb sswt_cb; + + sswt_cb.psem = &sem; + sswt_cb.timeflag = 0; + + /* If timeout is zero, then just wait forever */ + if (timeout > 0) { + /* Create a timer and pass it the address of our flag */ + sys_timeout(timeout, sswt_handler, &sswt_cb); + } + sys_sem_wait(sem); + /* Was it a timeout? */ + if (sswt_cb.timeflag) { + /* timeout */ + return 0; + } else { + /* Not a timeout. Remove timeout entry */ + sys_untimeout(sswt_handler, &sswt_cb); + return 1; + } +} + +/** + * Sleep for some ms. Timeouts are processed while sleeping. + * + * @param ms number of milliseconds to sleep + */ +void +sys_msleep(u32_t ms) +{ + sys_sem_t delaysem = sys_sem_new(0); + + sys_sem_wait_timeout(delaysem, ms); + + sys_sem_free(delaysem); +} + + +#endif /* NO_SYS */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/core/tcp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp.c new file mode 100644 index 0000000..81c4432 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp.c @@ -0,0 +1,1461 @@ +/** + * @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/debug.h" +#include "lwip/stats.h" + +#include + +const char *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; + +struct tcp_pcb *tcp_tmp_pcb; + +static u8_t tcp_timer; +static u16_t tcp_new_port(void); + +/** + * 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 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. + * + * @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 + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ + err_t err; + +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in ")); + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ + + 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. */ + err = ERR_OK; + TCP_RMV(&tcp_bound_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); + memp_free(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_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = FIN_WAIT_1; + } + break; + case ESTABLISHED: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + snmp_inc_tcpestabresets(); + pcb->state = LAST_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 when tcp_close returns. */ + tcp_output(pcb); + } + return err; +} + +/** + * 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; + struct ip_addr remote_ip, local_ip; +#if LWIP_CALLBACK_API + void (* errf)(void *arg, err_t err); +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + + /* 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_set(&local_ip, &(pcb->local_ip)); + ip_addr_set(&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 */ + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + if (reset) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } + } +} + +/** + * 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, struct ip_addr *ipaddr, u16_t port) +{ + struct tcp_pcb *cpcb; + + LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + if (port == 0) { + port = tcp_new_port(); + } + /* Check if the address already is in use. */ + /* Check the listen pcbs. */ + for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* Check the connected pcbs. */ + for(cpcb = tcp_active_pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* Check the bound, not yet connected pcbs. */ + for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah), + * we have to check the pcbs in TIME-WAIT state, also: */ + for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + 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. + * + * @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; + } + lpcb = 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->so_options = pcb->so_options; + lpcb->so_options |= SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_set(&lpcb->local_ip, &pcb->local_ip); + 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.listen_pcbs, lpcb); + 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 */ + pcb->rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt; + } + 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/2), 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); + + 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) +{ + 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; + } + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; 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. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @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, struct ip_addr *ipaddr, u16_t port, + err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) +{ + err_t ret; + u32_t iss; + + 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; + } else { + return ERR_VAL; + } + pcb->remote_port = port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + } + 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; + pcb->state = SYN_SENT; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#endif /* LWIP_CALLBACK_API */ + TCP_RMV(&tcp_bound_pcbs, pcb); + TCP_REG(&tcp_active_pcbs, pcb); + + snmp_inc_tcpactiveopens(); + + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS +#if LWIP_TCP_TIMESTAMPS + | TF_SEG_OPTS_TS +#endif + ); + if (ret == ERR_OK) { + 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, *pcb2, *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]; + } + + /* 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) { + pcb->ssthresh = pcb->mss * 2; + } + 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(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&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) { + 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; + } + + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + if (pcb_reset) { + tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, + pcb->local_port, pcb->remote_port); + } + + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + + /* We check if we should poll the connection. */ + ++pcb->polltmr; + if (pcb->polltmr >= pcb->pollinterval) { + pcb->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + TCP_EVENT_POLL(pcb, err); + if (err == ERR_OK) { + tcp_output(pcb); + } + } + + prev = pcb; + pcb = pcb->next; + } + } + + + /* 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) { + 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->next; + memp_free(MEMP_TCP_PCB, pcb); + 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; + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = 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; + } + } + + /* send delayed ACKs */ + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + } +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + * @param seg tcp_seg list of TCP segments to free + * @return the number of pbufs that were deallocated + */ +u8_t +tcp_segs_free(struct tcp_seg *seg) +{ + u8_t count = 0; + struct tcp_seg *next; + while (seg != NULL) { + next = seg->next; + count += tcp_seg_free(seg); + seg = next; + } + return count; +} + +/** + * Frees a TCP segment (tcp_seg structure). + * + * @param seg single tcp_seg to free + * @return the number of pbufs that were deallocated + */ +u8_t +tcp_seg_free(struct tcp_seg *seg) +{ + u8_t count = 0; + + if (seg != NULL) { + if (seg->p != NULL) { + count = pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } + return count; +} + +/** + * 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 = 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 + +#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 +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 +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. + * + * @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 = memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* 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 = 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 = 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)); + pcb->prio = TCP_PRIO_NORMAL; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_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 = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / 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. + * + * @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. + * + * @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, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) +{ + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @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, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) +{ + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param errf callback function to call for this pcb when a fatal error + * has occured on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, + void (* errf)(void *arg, err_t err)) +{ + pcb->errf = errf; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @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, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) +{ + 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. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval) +{ +#if LWIP_CALLBACK_API + pcb->poll = 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 /* LW */ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + + /* Stop the retransmission timer as it will expect data on unacked + queue if it fires */ + pcb->rtime = -1; + + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; + } +} + +/** + * 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, struct ip_addr *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_enqueue(), 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/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_in.c b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_in.c new file mode 100644 index 0000000..9c79898 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_in.c @@ -0,0 +1,1506 @@ +/** + * @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.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.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; +static struct tcp_hdr *tcphdr; +static struct ip_hdr *iphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * 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 + */ +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; + u8_t hdrlen; + err_t err; + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + iphdr = p->payload; + 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); + return; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(&(iphdr->dest), inp) || + ip_addr_ismulticast(&(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, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + 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, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), + 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); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + 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); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* 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), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(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; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + 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) { + 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), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(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); + 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) { + if ((ip_addr_isany(&(lpcb->local_ip)) || + ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && + lpcb->local_port == tcphdr->dest) { + /* 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); + pbuf_free(p); + return; + } + prev = (struct tcp_pcb *)lpcb; + } + } + +#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.dataptr = p->payload; + 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); + if (err == ERR_OK) { + pcb->refused_data = NULL; + } else { + /* drop incoming packets, because pcb is "full" */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); + TCP_STATS_INC(tcp.drop); + 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); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + 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); + } + + if (recv_data != NULL) { + if(flags & TCP_PSH) { + recv_data->flags |= PBUF_FLAG_PUSH; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + + /* 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) { + TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); + } + + 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 */ + } + } + tcp_input_pcb = NULL; + + + /* give up our reference to inseg.p */ + if (inseg.p != NULL) + { + pbuf_free(inseg.p); + 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_rst(ackno, seqno + tcplen, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + 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 + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + 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, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } else if (flags & TCP_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 */ + 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); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); + npcb->local_port = pcb->local_port; + ip_addr_set(&(npcb->remote_ip), &(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_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER); + /* 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(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, TF_SEG_OPTS_MSS +#if LWIP_TCP_TIMESTAMPS + /* and maybe include the TIMESTAMP option */ + | (npcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0) +#endif + ); + 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 + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + 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; + } + + /* Update the PCB (in)activity timer. */ + 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); + 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, &(iphdr->dest), &(iphdr->src), + 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. */ + 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; + } + } + /* incorrect ACK number */ + else { + /* send RST */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + 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 +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_FLAGS_SET(cseg->tcphdr, TCPH_FLAGS(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 + +/** + * 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 + 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) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + 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) + * + * 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)) { + 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) { + /* 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)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* 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. */ + 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->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); + tcp_seg_free(next); + + 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. */ + 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->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); + 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)) { + /* 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); + + 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 */ + 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)){ + /* 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.*/ + + 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); + } + } + /* KJM following line changed to use p->payload rather than inseg->p->payload + to fix bug #9076 */ + inseg.dataptr = p->payload; + inseg.len -= (u16_t)(pcb->rcv_nxt - seqno); + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else { + if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + 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)){ + 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 %"U32_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 + 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 + * rcv_nxt + * . |--ooseq--| + * .==seg============|FIN + */ + while (pcb->ooseq != NULL) { + struct tcp_seg *old_ooseq = pcb->ooseq; + pcb->ooseq = pcb->ooseq->next; + tcp_seg_free(old_ooseq); + } + } + else { + struct tcp_seg* next = pcb->ooseq; + struct tcp_seg *old_seg; + /* rcv_nxt + * . |--ooseq--| + * .==seg============| + */ + while (next && + TCP_SEQ_GEQ(seqno + tcplen, + next->tcphdr->seqno + next->len)) { + /* inseg doesn't have FIN (already processed) */ + if (TCPH_FLAGS(next->tcphdr) & TCP_FIN && + (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) { + TCPH_FLAGS_SET(inseg.tcphdr, + TCPH_FLAGS(inseg.tcphdr) | TCP_FIN); + tcplen = TCP_TCPLEN(&inseg); + } + old_seg = next; + next = next->next; + tcp_seg_free(old_seg); + } + /* rcv_nxt + * . |--ooseq--| + * .==seg============| + */ + if (next && + TCP_SEQ_GT(seqno + tcplen, + next->tcphdr->seqno)) { + /* FIN in inseg already handled by dropping whole ooseq queue */ + inseg.len = (u16_t)(pcb->ooseq->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) == pcb->ooseq->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 + is 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) { + 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); + 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); + } + } + 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/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_out.c b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_out.c new file mode 100644 index 0000000..ebff525 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/tcp_out.c @@ -0,0 +1,1054 @@ +/** + * @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.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.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +#include + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +static struct tcp_hdr * +tcp_output_set_header(struct tcp_pcb *pcb, struct pbuf *p, int optlen, + u32_t seqno_be /* already in network byte order */) +{ + struct tcp_hdr *tcphdr = 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_FLAGS_SET(tcphdr, TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_ann_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, (5 + optlen / 4)); + tcphdr->chksum = 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 tcphdr; +} + +/** + * Called by tcp_close() to send a segment including flags but not data. + * + * @param pcb the tcp_pcb over which to send a segment + * @param flags the flags to set in the segment header + * @return ERR_OK if sent, another err_t otherwise + */ +err_t +tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) +{ + /* no data, no length, flags, copy=1, no optdata */ + return tcp_enqueue(pcb, NULL, 0, flags, TCP_WRITE_FLAG_COPY, 0); +} + +/** + * 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 of the TCP connection to enqueue data for. + * @param data pointer to the data to send + * @param len length (in bytes) of the data to send + * @param apiflags combination of following flags : + * - 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 + * + * @see tcp_write() + */ +err_t +tcp_write(struct tcp_pcb *pcb, const void *data, u16_t len, u8_t apiflags) +{ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n", (void *)pcb, + data, len, (u16_t)apiflags)); + /* connection is in valid state for data transmission? */ + if (pcb->state == ESTABLISHED || + pcb->state == CLOSE_WAIT || + pcb->state == SYN_SENT || + pcb->state == SYN_RCVD) { + if (len > 0) { +#if LWIP_TCP_TIMESTAMPS + return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, + pcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0); +#else + return tcp_enqueue(pcb, (void *)data, len, 0, apiflags, 0); +#endif + } + return ERR_OK; + } else { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } +} + +/** + * Enqueue data and/or TCP options for transmission + * + * Called by tcp_connect(), tcp_listen_input(), tcp_send_ctrl() and 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 flags tcp header flags to set in the outgoing segment + * @param apiflags combination of following flags : + * - 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, + * @param optflags options to include in segment later on (see definition of struct tcp_seg) + */ +err_t +tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, + u8_t flags, u8_t apiflags, u8_t optflags) +{ + struct pbuf *p; + struct tcp_seg *seg, *useg, *queue; + u32_t seqno; + u16_t left, seglen; + void *ptr; + u16_t queuelen; + u8_t optlen; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, + ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", apiflags=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)flags, (u16_t)apiflags)); + LWIP_ERROR("tcp_enqueue: packet needs payload, options, or SYN/FIN (programmer violates API)", + ((len != 0) || (optflags != 0) || ((flags & (TCP_SYN | TCP_FIN)) != 0)), + return ERR_ARG;); + LWIP_ERROR("tcp_enqueue: len != 0 || arg == NULL (programmer violates API)", + ((len != 0) || (arg == NULL)), return ERR_ARG;); + + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + left = len; + ptr = arg; + + optlen = LWIP_TCP_OPT_LENGTH(optflags); + + /* seqno will be the sequence number of the first segment enqueued + * by the call to this function. */ + seqno = pcb->snd_lbb; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: 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 */ + queuelen = pcb->snd_queuelen; + /* check for configured max queuelen and possible overflow */ + if ((queuelen >= TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_WARNING, + ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + pcb->flags |= TF_NAGLEMEMERR; + return ERR_MEM; + } + if (queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + + /* First, break up the data into segments and tuck them together in + * the local "queue" variable. */ + useg = queue = seg = NULL; + seglen = 0; + while (queue == NULL || left > 0) { + /* The segment length (including options) should be at most the MSS */ + seglen = left > (pcb->mss - optlen) ? (pcb->mss - optlen) : left; + + /* Allocate memory for tcp_seg, and fill in fields. */ + seg = memp_malloc(MEMP_TCP_SEG); + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for tcp_seg\n")); + goto memerr; + } + seg->next = NULL; + seg->p = NULL; + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } + /* subsequent segments of to-be-queued data */ + else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("useg != NULL", useg != NULL); + useg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + useg = seg; + + /* If copy is set, memory should be allocated + * and data copied into pbuf, otherwise data comes from + * ROM or other static memory, and need not be copied. */ + if (apiflags & TCP_WRITE_FLAG_COPY) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen + optlen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + LWIP_ASSERT("check that first pbuf can hold the complete seglen", + (seg->p->len >= seglen + optlen)); + queuelen += pbuf_clen(seg->p); + if (arg != NULL) { + MEMCPY((char *)seg->p->payload + optlen, ptr, seglen); + } + seg->dataptr = seg->p->payload; + } + /* do not copy data */ + else { + /* First, allocate a pbuf for the headers. */ + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for header pbuf\n")); + goto memerr; + } + queuelen += pbuf_clen(seg->p); + + /* Second, 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. + */ + if (left > 0) { + if ((p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) { + /* If allocation fails, we have to deallocate the header pbuf as well. */ + pbuf_free(seg->p); + seg->p = NULL; + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } + ++queuelen; + /* reference the non-volatile payload data */ + p->payload = ptr; + seg->dataptr = ptr; + + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(seg->p/*header*/, p/*data*/); + p = NULL; + } + } + + /* 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 | LWIP_DBG_LEVEL_SERIOUS, + ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + goto memerr; + } + + seg->len = seglen; + + /* build TCP header */ + if (pbuf_header(seg->p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + goto memerr; + } + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + seg->tcphdr->urgp = 0; + TCPH_FLAGS_SET(seg->tcphdr, flags); + /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ + + seg->flags = optflags; + + /* Set the length of the header */ + TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + left -= seglen; + seqno += seglen; + ptr = (void *)((u8_t *)ptr + seglen); + } + + /* Now that the data to be enqueued has been broken up into TCP + segments in the queue variable, we add them to the end of the + pcb->unsent queue. */ + if (pcb->unsent == NULL) { + useg = NULL; + } + else { + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + } + /* { useg is last segment on the unsent queue, NULL if list is empty } */ + + /* If there is room in the last pbuf on the unsent queue, + chain the first pbuf on the queue together with that. */ + if (useg != NULL && + TCP_TCPLEN(useg) != 0 && + !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && + (!(flags & (TCP_SYN | TCP_FIN)) || (flags == TCP_FIN)) && + /* fit within max seg size */ + (useg->len + queue->len <= pcb->mss) && + /* only concatenate segments with the same options */ + (useg->flags == queue->flags) && + /* segments are consecutive */ + (ntohl(useg->tcphdr->seqno) + useg->len == ntohl(queue->tcphdr->seqno)) ) { + /* Remove TCP header from first segment of our to-be-queued list */ + if(pbuf_header(queue->p, -(TCP_HLEN + optlen))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + TCP_STATS_INC(tcp.err); + goto memerr; + } + if (queue->p->len == 0) { + /* free the first (header-only) pbuf if it is now empty (contained only headers) */ + struct pbuf *old_q = queue->p; + queue->p = queue->p->next; + old_q->next = NULL; + queuelen--; + pbuf_free(old_q); + } + if (flags & TCP_FIN) { + /* the new segment contains only FIN, no data -> put the FIN into the last segment */ + LWIP_ASSERT("FIN enqueued together with data", queue->p == NULL && queue->len == 0); + TCPH_SET_FLAG(useg->tcphdr, TCP_FIN); + } else { + LWIP_ASSERT("zero-length pbuf", (queue->p != NULL) && (queue->p->len > 0)); + pbuf_cat(useg->p, queue->p); + useg->len += queue->len; + useg->next = queue->next; + } + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); + if (seg == queue) { + seg = useg; + seglen = useg->len; + } + memp_free(MEMP_TCP_SEG, queue); + } + else { + /* empty list */ + if (useg == NULL) { + /* initialize list with this segment */ + pcb->unsent = queue; + } + /* enqueue segment */ + else { + useg->next = queue; + } + } + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + ++len; + } + if (flags & TCP_FIN) { + pcb->flags |= TF_FIN; + } + pcb->snd_lbb += len; + + pcb->snd_buf -= len; + + /* update number of segments on the queues */ + pcb->snd_queuelen = queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued, but only + if the segment has data (indicated by seglen > 0). */ + if (seg != NULL && seglen > 0 && 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 (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + + +#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 +tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts) +{ + /* Pad with two NOP options to make everything nicely aligned */ + opts[0] = 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 = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + 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); + + tcphdr = tcp_output_set_header(pcb, p, optlen, htonl(pcb->snd_nxt)); + + /* 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 +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); + } + + /* 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_enqueue 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_enqueue/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); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + 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 { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } + + 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 */ + opts = (u32_t *)(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 + + /* 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_set(&(pcb->local_ip), &(netif->ip_addr)); + } + + /* Set retransmission timer running if it is not currently enabled */ + if(pcb->rtime == -1) + pcb->rtime = 0; + + 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 + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, + &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif + 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, + struct ip_addr *local_ip, struct ip_addr *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 = p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK); + tcphdr->wnd = htons(TCP_WND); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 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(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&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 = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, + ("tcp_keepalive: 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 = tcp_output_set_header(pcb, p, 0, htonl(pcb->snd_nxt - 1)); + +#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(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&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); + len = is_fin ? TCP_HLEN : TCP_HLEN + 1; + + p = pbuf_alloc(PBUF_IP, len, PBUF_RAM); + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n")); + return; + } + LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr", + (p->len >= sizeof(struct tcp_hdr))); + + tcphdr = tcp_output_set_header(pcb, p, 0, seg->tcphdr->seqno); + + 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 */ + *((char *)p->payload + sizeof(struct tcp_hdr)) = *(char *)seg->dataptr; + } + +#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/component/common/network/lwip/lwip_v1.3.2/src/core/udp.c b/component/common/network/lwip/lwip_v1.3.2/src/core/udp.c new file mode 100644 index 0000000..31bbf8c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/core/udp.c @@ -0,0 +1,848 @@ +/** + * @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.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 +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 = 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(&(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(&iphdr->dest), ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), ip4_addr4(&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), &(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(&pcb->local_ip), ip4_addr2(&pcb->local_ip), + ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&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), &(iphdr->dest)) || +#if LWIP_IGMP + ip_addr_ismulticast(&(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), &(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; + } +/* This part of code has been modified by ST's MCD Application Team */ +/* To use the UPnP responder for device discovery */ +#if LWIP_UPNP + if((local_match != 0) && (dest == 1900)) { + break; + } +#endif /* LWIP_UPNP */ + 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, &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, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(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, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + 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(); + /* callback */ + if (pcb->recv != NULL) { + /* now the recv function is responsible for freeing p */ + pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, 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(&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 +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); +} + +/** + * 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 +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port) +{ + 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 0x%"X32_F"\n", dst_ip->addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +} + +/** + * 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 +udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif) +{ + struct udp_hdr *udphdr; + struct ip_addr *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; + } + /* chain header q in front of given pbuf p */ + 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 = q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(dst_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* 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, chklen); + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) + udphdr->chksum = 0xffff; +#endif /* CHECKSUM_CHECK_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) { + udphdr->chksum = 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 (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; + } +#endif /* CHECKSUM_CHECK_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 +udp_bind(struct udp_pcb *pcb, struct ip_addr *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; + } + + /* this code does not allow upper layer to share a UDP port for + listening to broadcast or multicast traffic (See SO_REUSE_ADDR and + SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR + combine with implementation of UDP PCB flags. Leon Woestenberg. */ +#ifdef LWIP_UDP_TODO + /* port matches that of PCB in list? */ + else + 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; + } +#endif + } + + 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", + (u16_t)((ntohl(pcb->local_ip.addr) >> 24) & 0xff), + (u16_t)((ntohl(pcb->local_ip.addr) >> 16) & 0xff), + (u16_t)((ntohl(pcb->local_ip.addr) >> 8) & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), 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 +udp_connect(struct udp_pcb *pcb, struct ip_addr *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", + (u16_t)((ntohl(pcb->remote_ip.addr) >> 24) & 0xff), + (u16_t)((ntohl(pcb->remote_ip.addr) >> 16) & 0xff), + (u16_t)((ntohl(pcb->remote_ip.addr) >> 8) & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), pcb->remote_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 +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ip_addr_set(&pcb->remote_ip, IP_ADDR_ANY); + 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 +udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port), + 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 +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 * +udp_new(void) +{ + struct udp_pcb *pcb; + 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 +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/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/autoip.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/autoip.h new file mode 100644 index 0000000..f711a61 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/autoip.h @@ -0,0 +1,116 @@ +/** + * @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 +{ + struct ip_addr 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); + +/** 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/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/icmp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/icmp.h new file mode 100644 index 0000000..9e9faf6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/icmp.h @@ -0,0 +1,111 @@ +/** + * 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. + */ +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 + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#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); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/igmp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/igmp.h new file mode 100644 index 0000000..86108ae --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/igmp.h @@ -0,0 +1,162 @@ +/** + * 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 constants + */ +#define IP_PROTO_IGMP 2 +#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 */ + +/* 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 */ +#define IGMP_DEL_MAC_FILTER 0 +#define IGMP_ADD_MAC_FILTER 1 + +/* 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(struct ip_addr igmp_group_address); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * now a 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 { + struct igmp_group *next; + struct netif *interface; + struct ip_addr group_address; + u8_t last_reporter_flag; /* signifies we were the last person to report */ + u8_t group_state; + u16_t timer; + u8_t use; /* counter of simultaneous uses */ +}; + + +/* Prototypes */ +void igmp_init(void); + +err_t igmp_start( struct netif *netif); + +err_t igmp_stop( struct netif *netif); + +void igmp_report_groups( struct netif *netif); + +struct igmp_group *igmp_lookfor_group( struct netif *ifp, struct ip_addr *addr); + +struct igmp_group *igmp_lookup_group( struct netif *ifp, struct ip_addr *addr); + +err_t igmp_remove_group( struct igmp_group *group); + +void igmp_input( struct pbuf *p, struct netif *inp, struct ip_addr *dest); + +err_t igmp_joingroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +err_t igmp_leavegroup( struct ip_addr *ifaddr, struct ip_addr *groupaddr); + +void igmp_tmr(void); + +void igmp_timeout( struct igmp_group *group); + +void igmp_start_timer( struct igmp_group *group, u8_t max_time); + +void igmp_stop_timer( struct igmp_group *group); + +void igmp_delaying_member( struct igmp_group *group, u8_t maxresp); + +err_t igmp_ip_output_if( struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t proto, struct netif *netif); + +void igmp_send( struct igmp_group *group, u8_t type); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet.h new file mode 100644 index 0000000..6f259c7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet.h @@ -0,0 +1,103 @@ +/** + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For compatibility with BSD code */ +struct in_addr { + u32_t s_addr; +}; + +#define INADDR_NONE ((u32_t)0xffffffffUL) /* 255.255.255.255 */ +#define INADDR_LOOPBACK ((u32_t)0x7f000001UL) /* 127.0.0.1 */ +#define INADDR_ANY ((u32_t)0x00000000UL) /* 0.0.0.0 */ +#define INADDR_BROADCAST ((u32_t)0xffffffffUL) /* 255.255.255.255 */ + +u32_t inet_addr(const char *cp); +int inet_aton(const char *cp, struct in_addr *addr); +char *inet_ntoa(struct in_addr addr); /* returns ptr to static buffer; not reentrant! */ + +#ifdef htons +#undef htons +#endif /* htons */ +#ifdef htonl +#undef htonl +#endif /* htonl */ +#ifdef ntohs +#undef ntohs +#endif /* ntohs */ +#ifdef ntohl +#undef ntohl +#endif /* ntohl */ + +#ifndef LWIP_PLATFORM_BYTESWAP +#define LWIP_PLATFORM_BYTESWAP 0 +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define htons(x) (x) +#define ntohs(x) (x) +#define htonl(x) (x) +#define ntohl(x) (x) +#else /* BYTE_ORDER != BIG_ENDIAN */ +#ifdef LWIP_PREFIX_BYTEORDER_FUNCS +/* workaround for naming collisions on some platforms */ +#define htons lwip_htons +#define ntohs lwip_ntohs +#define htonl lwip_htonl +#define ntohl lwip_ntohl +#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */ +#if LWIP_PLATFORM_BYTESWAP +#define htons(x) LWIP_PLATFORM_HTONS(x) +#define ntohs(x) LWIP_PLATFORM_HTONS(x) +#define htonl(x) LWIP_PLATFORM_HTONL(x) +#define ntohl(x) LWIP_PLATFORM_HTONL(x) +#else /* LWIP_PLATFORM_BYTESWAP */ +u16_t htons(u16_t x); +u16_t ntohs(u16_t x); +u32_t htonl(u32_t x); +u32_t ntohl(u32_t x); +#endif /* LWIP_PLATFORM_BYTESWAP */ + +#endif /* BYTE_ORDER == BIG_ENDIAN */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet_chksum.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet_chksum.h new file mode 100644 index 0000000..ba64ce8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/inet_chksum.h @@ -0,0 +1,60 @@ +/** + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *dataptr, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len); +#if LWIP_UDPLITE +u16_t inet_chksum_pseudo_partial(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len, u16_t chksum_len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip.h new file mode 100644 index 0000000..fbf5a22 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip.h @@ -0,0 +1,198 @@ +/** + * 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_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 */ \ + struct ip_addr local_ip; \ + struct ip_addr remote_ip; \ + /* Socket options */ \ + u16_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 (u16_t)0x0001U /* turn on debugging info recording */ +#define SOF_ACCEPTCONN (u16_t)0x0002U /* socket has had listen() */ +#define SOF_REUSEADDR (u16_t)0x0004U /* allow local address reuse */ +#define SOF_KEEPALIVE (u16_t)0x0008U /* keep connections alive */ +#define SOF_DONTROUTE (u16_t)0x0010U /* just use interface addresses */ +#define SOF_BROADCAST (u16_t)0x0020U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +#define SOF_USELOOPBACK (u16_t)0x0040U /* bypass hardware when possible */ +#define SOF_LINGER (u16_t)0x0080U /* linger on close if data present */ +#define SOF_OOBINLINE (u16_t)0x0100U /* leave received OOB data in line */ +#define SOF_REUSEPORT (u16_t)0x0200U /* allow local address & port reuse */ + + +#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 / protocol*/ + PACK_STRUCT_FIELD(u16_t _ttl_proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FIELD(struct ip_addr src); + PACK_STRUCT_FIELD(struct ip_addr 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) (ntohs((hdr)->_ttl_proto) >> 8) +#define IPH_PROTO(hdr) (ntohs((hdr)->_ttl_proto) & 0xff) +#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_proto = (htons(IPH_PROTO(hdr) | ((u16_t)(ttl) << 8))) +#define IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (IPH_TTL(hdr) << 8))) +#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; + +#define ip_init() /* Compatibility define, not init needed. */ +struct netif *ip_route(struct ip_addr *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto); +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, + struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if IP_OPTIONS_SEND +err_t ip_output_if_opt(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options, + u16_t optlen); +#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) +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_addr.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_addr.h new file mode 100644 index 0000000..2fcccc2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_addr.h @@ -0,0 +1,173 @@ +/** + * 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/inet.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_addr { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * 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 + +struct netif; + +extern const struct ip_addr ip_addr_any; +extern const struct ip_addr ip_addr_broadcast; + +/** IP_ADDR_ can be used as a fixed IP address + * for the wildcard and the broadcast address + */ +#define IP_ADDR_ANY ((struct ip_addr *)&ip_addr_any) +#define IP_ADDR_BROADCAST ((struct ip_addr *)&ip_addr_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) ((((u32_t)(a)) & 0x80000000UL) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET) +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET) +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET) + +#define IN_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_MULTICAST(a) IN_CLASSD(a) + +#define IN_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) +#define IN_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL) + +#define IN_LOOPBACKNET 127 /* official! */ + +#define IP4_ADDR(ipaddr, a,b,c,d) \ + (ipaddr)->addr = htonl(((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff)) + +#define ip_addr_set(dest, src) (dest)->addr = \ + ((src) == NULL? 0:\ + (src)->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 == 0) + +u8_t ip_addr_isbroadcast(struct ip_addr *, struct netif *); + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & ntohl(0xf0000000UL)) == ntohl(0xe0000000UL)) + +#define ip_addr_islinklocal(addr1) (((addr1)->addr & ntohl(0xffff0000UL)) == ntohl(0xa9fe0000UL)) + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff : 0, \ + ipaddr != NULL ? \ + (u16_t)ntohl((ipaddr)->addr) & 0xff : 0)) + +/* 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(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff) +#define ip4_addr2(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff) +#define ip4_addr3(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff) +#define ip4_addr4(ipaddr) ((u16_t)(ntohl((ipaddr)->addr)) & 0xff) + +/** + * Same as inet_ntoa() but takes a struct ip_addr* + */ +#define ip_ntoa(addr) ((addr != NULL) ? inet_ntoa(*((struct in_addr*)(addr))) : "NULL") + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_frag.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_frag.h new file mode 100644 index 0000000..ee18728 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv4/lwip/ip_frag.h @@ -0,0 +1,76 @@ +/** + * 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); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#endif /* IP_REASSEMBLY */ + +#if IP_FRAG +err_t ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/icmp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/icmp.h new file mode 100644 index 0000000..87e9ffd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/icmp.h @@ -0,0 +1,100 @@ +/* + * 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" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP6_DUR 1 +#define ICMP6_TE 3 +#define ICMP6_ECHO 128 /* echo */ +#define ICMP6_ER 129 /* echo 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 */ +}; + +void icmp_input(struct pbuf *p, struct netif *inp); + +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +struct icmp_echo_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u16_t id; + u16_t seqno; +}; + +struct icmp_dur_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +struct icmp_te_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ICMP */ + +#endif /* __LWIP_ICMP_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/inet.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/inet.h new file mode 100644 index 0000000..de1a0b6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/inet.h @@ -0,0 +1,68 @@ +/* + * 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/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *data, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len); + +u32_t inet_addr(const char *cp); +s8_t inet_aton(const char *cp, struct in_addr *addr); + +#ifndef _MACHINE_ENDIAN_H_ +#ifndef _NETINET_IN_H +#ifndef _LINUX_BYTEORDER_GENERIC_H +u16_t htons(u16_t n); +u16_t ntohs(u16_t n); +u32_t htonl(u32_t n); +u32_t ntohl(u32_t n); +#endif /* _LINUX_BYTEORDER_GENERIC_H */ +#endif /* _NETINET_IN_H */ +#endif /* _MACHINE_ENDIAN_H_ */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip.h new file mode 100644 index 0000000..a01cfc6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip.h @@ -0,0 +1,130 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_HLEN 40 + +#define IP_PROTO_ICMP 58 +#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 struct ip_addr local_ip; \ + struct ip_addr remote_ip; \ + /* Socket options */ \ + u16_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl; \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + + +/* The IPv6 header. */ +struct ip_hdr { +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t tclass1:4, v:4; + u8_t flow1:4, tclass2:4; +#else + u8_t v:4, tclass1:4; + u8_t tclass2:8, flow1:4; +#endif + u16_t flow2; + u16_t len; /* payload length */ + u8_t nexthdr; /* next header */ + u8_t hoplim; /* hop limit (TTL) */ + struct ip_addr src, dest; /* source and destination IP addresses */ +}; + +#define IPH_PROTO(hdr) (iphdr->nexthdr) + +void ip_init(void); + +#include "lwip/netif.h" + +struct netif *ip_route(struct ip_addr *dest); + +void ip_input(struct pbuf *p, struct netif *inp); + +/* source and destination addresses in network byte order, please */ +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto); + +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, + struct netif *netif); + +#define ip_current_netif() NULL +#define ip_current_header() NULL + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip_addr.h b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip_addr.h new file mode 100644 index 0000000..b2d8ae5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/ipv6/lwip/ip_addr.h @@ -0,0 +1,97 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_ADDR_ANY 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN + struct ip_addr { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * 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 + +#define IP6_ADDR(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = htonl((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ + (ipaddr)->addr[1] = htonl(((c & 0xffff) << 16) | (d & 0xffff)); \ + (ipaddr)->addr[2] = htonl(((e & 0xffff) << 16) | (f & 0xffff)); \ + (ipaddr)->addr[3] = htonl(((g & 0xffff) << 16) | (h & 0xffff)); } while(0) + +u8_t ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask); +u8_t ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2); +void ip_addr_set(struct ip_addr *dest, struct ip_addr *src); +u8_t ip_addr_isany(struct ip_addr *addr); + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F"\n", \ + (ntohl(ipaddr->addr[0]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[0]) & 0xffff, \ + (ntohl(ipaddr->addr[1]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[1]) & 0xffff, \ + (ntohl(ipaddr->addr[2]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[2]) & 0xffff, \ + (ntohl(ipaddr->addr[3]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[3]) & 0xffff)); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api.h new file mode 100644 index 0000000..eec83bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api.h @@ -0,0 +1,229 @@ +/** + * 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_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 + +// Evan add for port netconn api +#define port_netconn_recv(conn , buf, ret) do{buf = netconn_recv(conn);}while(0); +#define port_netconn_accept(conn , newconn, ret) do{newconn = netconn_accept(conn);}while(0); +// Evan add end + +/* 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 */ +#define NETCONN_NOFLAG 0x00 +#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */ +#define NETCONN_COPY 0x01 +#define NETCONN_MORE 0x02 + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) (t&0xF0) +#define NETCONNTYPE_DATAGRAM(t) (t&0xE0) + +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 +}; + +enum netconn_state { + NETCONN_NONE, + NETCONN_WRITE, + NETCONN_LISTEN, + NETCONN_CONNECT, + NETCONN_CLOSE +}; + +enum netconn_evt { + NETCONN_EVT_RCVPLUS, + NETCONN_EVT_RCVMINUS, + NETCONN_EVT_SENDPLUS, + NETCONN_EVT_SENDMINUS +}; + +#if LWIP_IGMP +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; + +/** 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 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; + /** mbox where new connections are stored until processed + by the application thread */ + sys_mbox_t acceptmbox; + /** only used for socket layer */ + int 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 */ + int recv_bufsize; +#endif /* LWIP_SO_RCVBUF */ + s16_t recv_avail; +#if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. */ + struct api_msg_msg *write_msg; + /** 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; +#if LWIP_TCPIP_CORE_LOCKING + /** 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. */ + u8_t write_delayed; +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#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); \ + } + +/* 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, + struct ip_addr *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, + struct ip_addr *addr, + u16_t port); +err_t netconn_connect (struct netconn *conn, + struct ip_addr *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) +struct netconn * netconn_accept (struct netconn *conn); +struct netbuf * netconn_recv (struct netconn *conn); +err_t netconn_sendto (struct netconn *conn, + struct netbuf *buf, struct ip_addr *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_abort(struct netconn *conn); + +#if LWIP_IGMP +err_t netconn_join_leave_group (struct netconn *conn, + struct ip_addr *multiaddr, + struct ip_addr *interface, + enum netconn_igmp join_or_leave); +#endif /* LWIP_IGMP */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, struct ip_addr *addr); +#endif /* LWIP_DNS */ + +#define netconn_err(conn) ((conn)->err) +#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN */ + +#endif /* __LWIP_API_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api_msg.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api_msg.h new file mode 100644 index 0000000..a1e1924 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/api_msg.h @@ -0,0 +1,162 @@ +/** + * 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 + +/* 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; + /** 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 { + struct ip_addr *ipaddr; + u16_t port; + } bc; + /** used for do_getaddr */ + struct { + struct ip_addr *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 { + u16_t len; + } r; +#if LWIP_IGMP + /** used for do_join_leave_group */ + struct { + struct ip_addr *multiaddr; + struct ip_addr *interface; + 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 */ + struct ip_addr *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); +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/arch.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/arch.h new file mode 100644 index 0000000..19d5734 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/arch.h @@ -0,0 +1,233 @@ +/** + * 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 */ + +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/debug.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/debug.h new file mode 100644 index 0000000..f9eff83 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/debug.h @@ -0,0 +1,102 @@ +/** + * 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" +#include + +/** 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_PLATFORM_DIAG printf +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/def.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/def.h new file mode 100644 index 0000000..0e84852 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/def.h @@ -0,0 +1,47 @@ +/** + * 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__ + +/* this might define NULL already */ +#include "lwip/arch.h" + +#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 + + +#endif /* __LWIP_DEF_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dhcp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dhcp.h new file mode 100644 index 0000000..f93fcd0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dhcp.h @@ -0,0 +1,247 @@ +/** @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 + +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; + /** incoming msg options */ + void *options_in; + /** ingoing msg options length */ + u16_t options_in_len; + /** current DHCP state machine state */ + u8_t state; + /** retries of current request */ + u8_t tries; + + 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 */ + struct ip_addr server_ip_addr; /* dhcp server address that offered this lease */ + struct ip_addr offered_ip_addr; + struct ip_addr offered_sn_mask; + struct ip_addr offered_gw_addr; + struct ip_addr offered_bc_addr; +#define DHCP_MAX_DNS 2 + u32_t dns_count; /* actual number of DNS servers obtained */ + struct ip_addr offered_dns_addr[DHCP_MAX_DNS]; /* DNS server addresses */ + + 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) */ +#if LWIP_DHCP_AUTOIP_COOP + u8_t autoip_coop_state; +#endif +/** Patch #1308 + * TODO: See dhcp.c "TODO"s + */ +#if 0 + struct ip_addr offered_si_addr; + u8_t *boot_file_name; +#endif +}; + +/* 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(struct ip_addr ciaddr); + PACK_STRUCT_FIELD(struct ip_addr yiaddr); + PACK_STRUCT_FIELD(struct ip_addr siaddr); + PACK_STRUCT_FIELD(struct ip_addr giaddr); +#define DHCP_CHADDR_LEN 16U + PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]); +#define DHCP_SNAME_LEN 64U + PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]); +#define DHCP_FILE_LEN 128U + 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 + +/** 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); +err_t dhcp_release_unicast(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, struct ip_addr *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_MSG_OFS (UDP_DATA_OFS) + #define DHCP_OP_OFS (DHCP_MSG_OFS + 0) + #define DHCP_HTYPE_OFS (DHCP_MSG_OFS + 1) + #define DHCP_HLEN_OFS (DHCP_MSG_OFS + 2) + #define DHCP_HOPS_OFS (DHCP_MSG_OFS + 3) + #define DHCP_XID_OFS (DHCP_MSG_OFS + 4) + #define DHCP_SECS_OFS (DHCP_MSG_OFS + 8) + #define DHCP_FLAGS_OFS (DHCP_MSG_OFS + 10) + #define DHCP_CIADDR_OFS (DHCP_MSG_OFS + 12) + #define DHCP_YIADDR_OFS (DHCP_MSG_OFS + 16) + #define DHCP_SIADDR_OFS (DHCP_MSG_OFS + 20) + #define DHCP_GIADDR_OFS (DHCP_MSG_OFS + 24) + #define DHCP_CHADDR_OFS (DHCP_MSG_OFS + 28) + #define DHCP_SNAME_OFS (DHCP_MSG_OFS + 44) + #define DHCP_FILE_OFS (DHCP_MSG_OFS + 108) +#define DHCP_MSG_LEN 236 + +#define DHCP_COOKIE_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN) +#define DHCP_OPTIONS_OFS (DHCP_MSG_OFS + DHCP_MSG_LEN + 4) + +#define DHCP_CLIENT_PORT 68 +#define DHCP_SERVER_PORT 67 + +/** DHCP client states */ +#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 +#define DHCP_RELEASING 11 +#define DHCP_BACKING_OFF 12 +#define DHCP_OFF 13 + +/** 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 + +#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 + +#define DHCP_HTYPE_ETH 1 + +#define DHCP_HLEN_ETH 6 + +#define DHCP_BROADCAST_FLAG 15 +#define DHCP_BROADCAST_MASK (1 << DHCP_FLAG_BROADCAST) + +/** 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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dns.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dns.h new file mode 100644 index 0000000..be91d49 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/dns.h @@ -0,0 +1,97 @@ +/** + * 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 */ + +/** 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 */ + +/** 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 a struct ip_addr 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, struct ip_addr *ipaddr, void *callback_arg); + + +void dns_init(void); + +void dns_tmr(void); + +void dns_setserver(u8_t numdns, struct ip_addr *dnsserver); + +struct ip_addr dns_getserver(u8_t numdns); + +err_t dns_gethostbyname(const char *hostname, struct ip_addr *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 struct ip_addr *addr); +err_t dns_local_addhost(const char *hostname, const struct ip_addr *addr); +#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */ + +#endif /* LWIP_DNS */ + +#endif /* __LWIP_DNS_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/err.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/err.h new file mode 100644 index 0000000..2cd5819 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/err.h @@ -0,0 +1,87 @@ +/** + * 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_IS_FATAL(e) ((e) < ERR_RTE) + +#define ERR_ABRT -5 /* Connection aborted. */ +#define ERR_RST -6 /* Connection reset. */ +#define ERR_CLSD -7 /* Connection closed. */ +#define ERR_CONN -8 /* Not connected. */ + +#define ERR_VAL -9 /* Illegal value. */ + +#define ERR_ARG -10 /* Illegal argument. */ + +#define ERR_USE -11 /* Address in use. */ + +#define ERR_IF -12 /* Low-level netif error */ +#define ERR_ISCONN -13 /* Already connected. */ + +#define ERR_INPROGRESS -14 /* Operation in progress */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/init.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/init.h new file mode 100644 index 0000000..86be6da --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/** + * 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 3U +/** x.x.X: Revision of the stack */ +#define LWIP_VERSION_REVISION 2U +/** 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 255U + +/** 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); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/mem.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/mem.h new file mode 100644 index 0000000..7976462 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/mem.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_MEM_H__ +#define __LWIP_MEM_H__ + +#include "lwip/opt.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 free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#endif +#ifndef mem_realloc +static void *mem_realloc(void *mem, mem_size_t size) +{ + LWIP_UNUSED_ARG(size); + return 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; +#else +typedef u16_t mem_size_t; +#endif /* MEM_SIZE > 64000 */ + +#if MEM_USE_POOLS +/** mem_init is not used when using pools instead of a heap */ +#define mem_init() +/** mem_realloc 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_realloc(mem, size) (mem) +#else /* MEM_USE_POOLS */ +/* lwIP alternative malloc */ +void mem_init(void); +void *mem_realloc(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#endif /* MEM_LIBC_MALLOC */ + +#ifndef LWIP_MEM_ALIGN_SIZE +#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1)) +#endif + +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp.h new file mode 100644 index 0000000..33046db --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/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) 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); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp_std.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp_std.h new file mode 100644 index 0000000..0d25696 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/memp_std.h @@ -0,0 +1,102 @@ +/** + * 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) +#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) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#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") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ + +#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") +LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT") +#endif /* NO_SYS==0 */ + +#if ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#endif /* LWIP_IGMP */ + +#if NO_SYS==0 +LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT") +#endif /* NO_SYS==0 */ + + +/* + * 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") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * 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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netbuf.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netbuf.h new file mode 100644 index 0000000..0dfb367 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netbuf.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_NETBUF_H__ +#define __LWIP_NETBUF_H__ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netbuf { + struct pbuf *p, *ptr; + struct ip_addr *addr; + u16_t port; +#if LWIP_NETBUF_RECVINFO + struct ip_addr *toaddr; + u16_t toport; +#endif /* LWIP_NETBUF_RECVINFO */ +}; + +/* Network buffer functions: */ +struct netbuf * netbuf_new (void); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +u16_t netbuf_len (struct netbuf *buf); +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#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_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) ((buf)->toaddr) +#define netbuf_destport(buf) ((buf)->toport) +#endif /* LWIP_NETBUF_RECVINFO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETBUF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netdb.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netdb.h new file mode 100644 index 0000000..00cc172 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netdb.h @@ -0,0 +1,111 @@ +/** + * 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" + +#if LWIP_DNS && LWIP_SOCKET + +#include /* for size_t */ + +#include "lwip/sockets.h" + +/* 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 */ + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netif.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netif.h new file mode 100644 index 0000000..59869ac --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netif.h @@ -0,0 +1,269 @@ +/** + * 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/inet.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 + +/** TODO: define the use (where, when, whom) of netif flags */ + +/** whether the network interface is 'up'. this is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + */ +#define NETIF_FLAG_UP 0x01U +/** if set, the netif has broadcast capability */ +#define NETIF_FLAG_BROADCAST 0x02U +/** if set, the netif is one end of a point-to-point connection */ +#define NETIF_FLAG_POINTTOPOINT 0x04U +/** if set, the interface is configured using DHCP */ +#define NETIF_FLAG_DHCP 0x08U +/** if set, the interface has an active link + * (set by the network interface driver) */ +#define NETIF_FLAG_LINK_UP 0x10U +/** if set, the netif is an device using ARP */ +#define NETIF_FLAG_ETHARP 0x20U +/** if set, the netif has IGMP capability */ +#define NETIF_FLAG_IGMP 0x40U +/** if set, the netif has IP layer switch capability */ +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP +#define NETIF_FLAG_IPSWITCH 0x80U +#endif +/* Realtek Modified End */ + +/** 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 */ + struct ip_addr ip_addr; + struct ip_addr netmask; + struct ip_addr gw; + + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + err_t (* input)(struct pbuf *p, struct netif *inp); + /** 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. */ + err_t (* output)(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr); + /** 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. */ + err_t (* linkoutput)(struct netif *netif, struct pbuf *p); +#if LWIP_NETIF_STATUS_CALLBACK + /** This function is called when the netif state is set to up or down + */ + void (* status_callback)(struct netif *netif); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + /** This function is called when the netif link is set to up or down + */ + void (* link_callback)(struct netif *netif); +#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) */ + 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.*/ + err_t (*igmp_mac_filter)( struct netif *netif, struct ip_addr *group, u8_t action); +#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. */ + 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; + +#define netif_init() /* Compatibility define, not init needed. */ + +struct netif *netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)); + +void +netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw); +void netif_remove(struct netif * netif); + +/* 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); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr); +void netif_set_netmask(struct netif *netif, struct ip_addr *netmask); +void netif_set_gw(struct netif *netif, struct ip_addr *gw); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +u8_t netif_is_up(struct netif *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, void (* status_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +u8_t netif_is_link_up(struct netif *netif); +/* + * Set callback to be called when link is brought up/down + */ +void netif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif)); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#ifdef __cplusplus +} +#endif + +#if ENABLE_LOOPBACK +err_t netif_loop_output(struct netif *netif, struct pbuf *p, struct ip_addr *dest_ip); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#endif /* __LWIP_NETIF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netifapi.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netifapi.h new file mode 100644 index 0000000..4145dd7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/netifapi.h @@ -0,0 +1,105 @@ +/* + * 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 + +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 { + struct ip_addr *ipaddr; + struct ip_addr *netmask; + struct ip_addr *gw; + void *state; + err_t (* init) (struct netif *netif); + err_t (* input)(struct pbuf *p, struct netif *netif); + } add; + struct { + void (* voidfunc)(struct netif *netif); + err_t (* errtfunc)(struct netif *netif); + } 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, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif) ); + +err_t netifapi_netif_set_addr ( struct netif *netif, + struct ip_addr *ipaddr, + struct ip_addr *netmask, + struct ip_addr *gw ); + +err_t netifapi_netif_common ( struct netif *netif, + void (* voidfunc)(struct netif *netif), + err_t (* errtfunc)(struct netif *netif) ); + +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/opt.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/opt.h new file mode 100644 index 0000000..69162cf --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/opt.h @@ -0,0 +1,1841 @@ +/* + * @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 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) 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_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 simultaneously IP packets queued for + * reassembly (whole packets, not fragments!) + */ +#ifndef MEMP_NUM_REASSDATA +#define MEMP_NUM_REASSDATA 5 +#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 + +/** + * 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: Outgoing packets are queued during hardware address + * resolution. + */ +#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. + */ +#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 + +/* + -------------------------------- + ---------- 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 1 +#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. + */ +#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 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 1 +#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 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: + */ +#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 + +/* + ---------------------------------- + ---------- 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 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 use a local buffer if DNS_USES_STATIC_BUF=0, a static one if + DNS_USES_STATIC_BUF=1, or a dynamic one if DNS_USES_STATIC_BUF=2. + The buffer will be of size DNS_MSG_SIZE */ +#ifndef DNS_USES_STATIC_BUF +#define DNS_USES_STATIC_BUF 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)) +#endif + +/** + * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than or equal + * to TCP_SND_BUF. It is the amount of space which must be available in the + * TCP snd_buf for select to return writable. + */ +#ifndef TCP_SNDLOWAT +#define TCP_SNDLOWAT ((TCP_SND_BUF)/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 + +/** + * 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 +#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 1 +#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 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 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 "TCP_IP" +#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 pppMain thread. + */ +#ifndef PPP_THREAD_NAME +#define PPP_THREAD_NAME "pppMain" +#endif + +/** + * PPP_THREAD_STACKSIZE: The stack size used by the pppMain thread. + * 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 pppMain thread. + * 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_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#ifndef LWIP_NETCONN +#define LWIP_NETCONN 1 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#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 1 +#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 and SO_REUSEPORT options. DO NOT USE! + */ +#ifndef SO_REUSE +#define SO_REUSE 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 +#define ETHARP_STATS 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 + +/* + --------------------------------------- + ---------- 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 + +/** + * 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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/pbuf.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/pbuf.h new file mode 100644 index 0000000..4156c36 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/pbuf.h @@ -0,0 +1,120 @@ +/** + * 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 + +#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 */ +} pbuf_type; + + +/** indicates this packet's data should be immediately passed to the application */ +#define PBUF_FLAG_PUSH 0x01U + +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; + +}; + +/* 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 size, pbuf_type type); +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +void pbuf_ref_chain(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/raw.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/raw.h new file mode 100644 index 0000000..659ad9d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/raw.h @@ -0,0 +1,97 @@ +/** + * 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/inet.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct raw_pcb { +/* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /* receive callback function + * @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. + */ + u8_t (* recv)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + struct ip_addr *addr); + /* 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); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, struct ip_addr *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, struct ip_addr *ipaddr); + +void raw_recv (struct raw_pcb *pcb, + u8_t (* recv)(void *arg, struct raw_pcb *pcb, + struct pbuf *p, + struct ip_addr *addr), + void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr); +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); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sio.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sio.h new file mode 100644 index 0000000..28ae2f2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/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); +#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); +#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); +#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); +#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); +#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); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp.h new file mode 100644 index 0000000..20bbce5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp.h @@ -0,0 +1,364 @@ +/** + * 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" +#include "lwip/netif.h" +#include "lwip/udp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @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, struct ip_addr *ip); +void snmp_delete_arpidx_tree(struct netif *ni, struct ip_addr *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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_asn1.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_asn1.h new file mode 100644 index 0000000..8a60288 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/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 (!0x80 | !0x40) +#define SNMP_ASN1_APPLIC (!0x80 | 0x40) +#define SNMP_ASN1_CONTXT ( 0x80 | !0x40) + +#define SNMP_ASN1_CONSTR (0x20) +#define SNMP_ASN1_PRIMIT (!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, u8_t octets_needed, u32_t value); +err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_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, u8_t raw_len, u8_t *raw); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SNMP */ + +#endif /* __LWIP_SNMP_ASN1_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_msg.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_msg.h new file mode 100644 index 0000000..32ba8fb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_msg.h @@ -0,0 +1,311 @@ +/** + * @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" + +#if LWIP_SNMP + +#if SNMP_PRIVATE_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 */ + struct ip_addr 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 */ + struct ip_addr 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, struct ip_addr *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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_structs.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_structs.h new file mode 100644 index 0000000..aa3f02e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/snmp_structs.h @@ -0,0 +1,262 @@ +/** + * @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 +#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 object access */ +#define MIB_OBJECT_READ_ONLY 0 +#define MIB_OBJECT_READ_WRITE 1 +#define MIB_OBJECT_WRITE_ONLY 2 +#define MIB_OBJECT_NOT_ACCESSIBLE 3 + +/** 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 */ + const u8_t node_type; + /* array or max list length */ + const 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 (* const get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od); + void (* const 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); + + const u8_t node_type; + const u16_t maxlength; + + /* aditional 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; + + /* aditional 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; + + /* aditional struct members */ + /** points to an extenal (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, struct ip_addr *ip); +void snmp_iptooid(struct ip_addr *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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sockets.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sockets.h new file mode 100644 index 0000000..44a18e6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sockets.h @@ -0,0 +1,357 @@ +/** + * 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! + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* Unimplemented: 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 + +/* Socket flags: */ +#ifndef O_NONBLOCK +#define O_NONBLOCK 04000U +#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); + +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/stats.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/stats.h new file mode 100644 index 0000000..99878dc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/stats.h @@ -0,0 +1,283 @@ +/** + * 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 lenerr; /* Invalid length error. */ + STAT_COUNTER chkerr; /* Checksum error. */ + STAT_COUNTER v1_rxed; /* */ + STAT_COUNTER join_sent; /* */ + STAT_COUNTER leave_sent; /* */ + STAT_COUNTER unicast_query; /* */ + STAT_COUNTER report_sent; /* */ + STAT_COUNTER report_rxed; /* */ + STAT_COUNTER group_query_rxed; /* */ +}; + +struct stats_mem { + 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 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; + +#define stats_init() /* Compatibility define, not init needed. */ + +#define STATS_INC(x) ++lwip_stats.x +#define STATS_DEC(x) --lwip_stats.x +#else +#define stats_init() +#define STATS_INC(x) +#define STATS_DEC(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) do { lwip_stats.mem.used += y; \ + if (lwip_stats.mem.max < lwip_stats.mem.used) { \ + lwip_stats.mem.max = lwip_stats.mem.used; \ + } \ + } while(0) +#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) do { ++lwip_stats.memp[i].used; \ + if (lwip_stats.memp[i].max < lwip_stats.memp[i].used) { \ + lwip_stats.memp[i].max = lwip_stats.memp[i].used; \ + } \ + } while(0) +#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_DISPLAY() stats_display_sys(&lwip_stats.sys) +#else +#define SYS_STATS_INC(x) +#define SYS_STATS_DEC(x) +#define SYS_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, char *name); +void stats_display_igmp(struct stats_igmp *igmp); +void stats_display_mem(struct stats_mem *mem, char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sys.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sys.h new file mode 100644 index 0000000..b9a85ee --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/sys.h @@ -0,0 +1,243 @@ +/** + * 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" + +#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_mbox_t; +struct sys_timeo {u8_t dummy;}; + +#define sys_init() +#define sys_timeout(m,h,a) +#define sys_untimeout(m,a) +#define sys_sem_new(c) c +#define sys_sem_signal(s) +#define sys_sem_wait(s) +#define sys_sem_wait_timeout(s,t) +#define sys_arch_sem_wait(s,t) +#define sys_sem_free(s) +#define sys_mbox_new(s) 0 +#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) + +#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" + +typedef void (* sys_timeout_handler)(void *arg); + +struct sys_timeo { + struct sys_timeo *next; + u32_t time; + sys_timeout_handler h; + void *arg; +}; + +struct sys_timeouts { + struct sys_timeo *next; +}; + +/* sys_init() must be called before anthing else. */ +void sys_init(void); + +/* + * sys_timeout(): + * + * Schedule a timeout a specified amount of milliseconds in the + * future. When the timeout occurs, the specified timeout handler will + * be called. The handler will be passed the "arg" argument when + * called. + * + */ +void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +void sys_untimeout(sys_timeout_handler h, void *arg); +struct sys_timeouts *sys_arch_timeouts(void); + +/* Semaphore functions. */ +sys_sem_t sys_sem_new(u8_t count); +void sys_sem_signal(sys_sem_t sem); +u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout); +void sys_sem_free(sys_sem_t sem); +void sys_sem_wait(sys_sem_t sem); +int sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout); + +/* Time functions. */ +#ifndef sys_msleep +void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */ +#endif +#ifndef sys_jiffies +u32_t sys_jiffies(void); /* since power up. */ +#endif + +/* Mailbox functions. */ +sys_mbox_t sys_mbox_new(int size); +void sys_mbox_post(sys_mbox_t mbox, void *msg); +err_t sys_mbox_trypost(sys_mbox_t mbox, void *msg); +u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout); +#ifndef sys_arch_mbox_tryfetch /* Allow port to override with a macro */ +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) +void sys_mbox_free(sys_mbox_t mbox); +void sys_mbox_fetch(sys_mbox_t mbox, void **msg); + +/* Thread functions. */ +sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio); + +#endif /* NO_SYS */ + +/** Returns the current time in milliseconds. */ +u32_t sys_now(void); + +/* 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); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#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/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcp.h new file mode 100644 index 0000000..43004de --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcp.h @@ -0,0 +1,707 @@ +/** + * 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; + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +#define tcp_init() /* Compatibility define, not init needed. */ +void tcp_tmr (void); /* Must be called every + TCP_TMR_INTERVAL + ms. (Typically 250 ms). */ +/* Application program's interface: */ +struct tcp_pcb * tcp_new (void); +struct tcp_pcb * tcp_alloc (u8_t prio); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)); +void tcp_recv (struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)); +void tcp_sent (struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)); +void tcp_poll (struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval); +void tcp_err (struct tcp_pcb *pcb, + void (* err)(void *arg, err_t err)); + +#define tcp_mss(pcb) ((pcb)->mss) +#define tcp_sndbuf(pcb) ((pcb)->snd_buf) +#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) (((struct tcp_pcb_listen *)(pcb))->accepts_pending--) +#else /* TCP_LISTEN_BACKLOG */ +#define tcp_accepted(pcb) +#endif /* TCP_LISTEN_BACKLOG */ + +void tcp_recved (struct tcp_pcb *pcb, u16_t len); +err_t tcp_bind (struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +err_t tcp_connect (struct tcp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abandon (struct tcp_pcb *pcb, int reset); +#define tcp_abort(pcb) tcp_abandon((pcb), 1) +err_t tcp_close (struct tcp_pcb *pcb); + +/* Flags for "apiflags" parameter in tcp_write and tcp_enqueue */ +#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); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +/* It is also possible to call these two functions at the right + intervals (instead of calling tcp_tmr()). */ +void tcp_slowtmr (void); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +err_t tcp_output (struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); + +/** + * 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 250 /* 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 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* 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 & htons((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(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)) + +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 +}; + +/** 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_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 err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err) +#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; \ + /* ports are in host byte order */ \ + u16_t local_port; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK + + +/* 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_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). */ + + + /* 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. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb which has send buffer space available + * @param space the amount of bytes available + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* sent)(void *arg, struct tcp_pcb *pcb, u16_t space); + + /* Function to be called when (in-sequence) data has arrived. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb for which data has arrived + * @param p the packet buffer which arrived + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* recv)(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); + + /* Function to be called when a connection has been set up. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb that now is connected + * @param err an error argument (TODO: that is current always ERR_OK?) + * @return value is currently ignored + */ + err_t (* connected)(void *arg, struct tcp_pcb *pcb, err_t err); + + /* Function which is called periodically. + * The period can be adjusted in multiples of the TCP slow timer interval + * by changing tcp_pcb.polltmr. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param pcb the tcp_pcb to poll for + * @return ERR_OK: try to send some data by calling tcp_output + */ + err_t (* poll)(void *arg, struct tcp_pcb *pcb); + + /* Function to be called whenever a fatal error occurs. + * There is no pcb parameter since most of the times, the pcb is + * already deallocated (or there is no pcb) when this function is called. + * @param arg user-supplied argument (tcp_pcb.callback_arg) + * @param err an indication why the error callback is called: + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ + void (* errf)(void *arg, err_t err); +#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); + +#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_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_OK; \ + } 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_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 */ + +/* This structure represents a TCP segment on the unsent and unacked 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 */ + 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. */ + 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) = htonl(((u32_t)2 << 24) | \ + ((u32_t)4 << 16) | \ + (((u32_t)TCP_MSS / 256) << 8) | \ + (TCP_MSS & 255)) + +/* Internal functions and global variables: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +u8_t tcp_segs_free(struct tcp_seg *seg); +u8_t tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#define tcp_ack(pcb) \ + do { \ + if((pcb)->flags & TF_ACK_DELAY) { \ + (pcb)->flags &= ~TF_ACK_DELAY; \ + (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb); \ + } \ + else { \ + (pcb)->flags |= TF_ACK_DELAY; \ + } \ + } while (0) + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + tcp_output(pcb); \ + } while (0) + +err_t tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags); +err_t tcp_enqueue(struct tcp_pcb *pcb, void *dataptr, u16_t len, + u8_t flags, u8_t apiflags, u8_t optflags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst(u32_t seqno, u32_t ackno, + struct ip_addr *local_ip, struct ip_addr *remote_ip, + u16_t local_port, u16_t remote_port); + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss(u16_t sendmss, struct ip_addr *addr); +#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); +#endif /* LWIP_CALLBACK_API */ + +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; + +const char* tcp_debug_state_str(enum tcp_state s); +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#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 */ + +#if NO_SYS +#define tcp_timer_needed() +#else +void tcp_timer_needed(void); +#endif + +/* 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 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. */ +#if 0 +#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", 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 */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcpip.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcpip.h new file mode 100644 index 0000000..8bfbaf7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/tcpip.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. + * + * 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/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_TCPIP_CORE_LOCKING +/** The global semaphore to lock the stack. */ +extern sys_sem_t lock_tcpip_core; +#define LOCK_TCPIP_CORE() sys_sem_wait(lock_tcpip_core) +#define UNLOCK_TCPIP_CORE() sys_sem_signal(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 +#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 */ + +void tcpip_init(void (* tcpip_init_done)(void *), 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(void (*f)(void *ctx), 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); + +err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg); +err_t tcpip_untimeout(sys_timeout_handler h, void *arg); + +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 */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT +}; + +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 { + void (*f)(void *ctx); + void *ctx; + } cb; + struct { + u32_t msecs; + sys_timeout_handler h; + void *arg; + } tmo; + } msg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !NO_SYS */ + +#endif /* __LWIP_TCPIP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/udp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/udp.h new file mode 100644 index 0000000..e441dac --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/lwip/udp.h @@ -0,0 +1,153 @@ +/** + * 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 + +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 */ + struct ip_addr 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 + * 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 + */ + void (* recv)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *addr, u16_t port); + /* 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); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, struct ip_addr *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + struct ip_addr *addr, + u16_t port), + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#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); + +#define udp_init() /* Compatibility define, not init needed. */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/netif/etharp.h b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/etharp.h new file mode 100644 index 0000000..847f605 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/etharp.h @@ -0,0 +1,192 @@ +/** + * 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 /* 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 ETH_PAD_SIZE +#define ETH_PAD_SIZE 0 +#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 +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 +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 */ +struct etharp_hdr { + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t proto); + PACK_STRUCT_FIELD(u16_t _hwlen_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_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 1000 + +#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 */ + +/** ARP message types (opcodes) */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#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); +s8_t etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret); +void etharp_ip_input(struct netif *netif, struct pbuf *p); +void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, + struct pbuf *p); +err_t etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr); +err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr); +/** 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) + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#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 struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode); +#endif /* LWIP_AUTOIP */ + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP */ + +#endif /* __NETIF_ARP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/netif/loopif.h b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/loopif.h new file mode 100644 index 0000000..304af4b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/loopif.h @@ -0,0 +1,53 @@ +/* + * 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 __NETIF_LOOPIF_H__ +#define __NETIF_LOOPIF_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +#define loopif_poll netif_poll +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ + +err_t loopif_init(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_LOOPIF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/netif/ppp_oe.h b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/ppp_oe.h new file mode 100644 index 0000000..ee9408a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/ppp_oe.h @@ -0,0 +1,161 @@ +/** +* 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 + +#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) + +struct pppoe_softc; + + +void pppoe_init(void); + +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); + +extern int pppoe_hdrlen; + +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_OE_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/include/netif/slipif.h b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/slipif.h new file mode 100644 index 0000000..ccd03c8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/include/netif/slipif.h @@ -0,0 +1,51 @@ +/* + * 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 __NETIF_SLIPIF_H__ +#define __NETIF_SLIPIF_H__ + +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/FILES b/component/common/network/lwip/lwip_v1.3.2/src/netif/FILES new file mode 100644 index 0000000..099dbf3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/FILES @@ -0,0 +1,29 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The PPP stack has been ported from ucip (http://ucip.sourceforge.net). + It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although + compared to that, it has some modifications for embedded systems and + the source code has been reordered a bit. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/etharp.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/etharp.c new file mode 100644 index 0000000..28b2111 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/etharp.c @@ -0,0 +1,1235 @@ +/* + * @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 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/inet.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 + +/** 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 + +#define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) +#define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) + +#define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) +#define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) + +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; +#endif + struct ip_addr ipaddr; + struct eth_addr ethaddr; + enum etharp_state state; + u8_t ctime; + struct netif *netif; +}; + +const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +const struct eth_addr ethzero = {{0,0,0,0,0,0}}; +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; +#if !LWIP_NETIF_HWADDRHINT +static u8_t etharp_cached_entry; +#endif + +/** + * 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_TRY_HARD 1 +#define ETHARP_FIND_ONLY 2 + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \ + *((netif)->addr_hint) = (hint); +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif); +#else /* LWIP_NETIF_HWADDRHINT */ +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags); +#endif /* LWIP_NETIF_HWADDRHINT */ + +static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags); + + +/* Some checks, instead of etharp_init(): */ +#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f)) + #error "If you want to use ARP, ARP_TABLE_SIZE must fit in an s8_t, so, 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); + } +} +#endif + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL microseconds (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) { + arp_table[i].ctime++; + if (((arp_table[i].state == ETHARP_STATE_STABLE) && + (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 */ + /* remove from SNMP ARP index tree */ + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); +#if ARP_QUEUEING + /* 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; + } +#endif + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; + } +#if ARP_QUEUEING + /* still pending entry? (not expired) */ + if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + } +#endif + } +} + +/** + * 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_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 + * - ETHARP_TRY_HARD: Try hard to create a entry by allowing recycling of + * active (stable or pending) entries. + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t +#if LWIP_NETIF_HWADDRHINT +find_entry(struct ip_addr *ipaddr, u8_t flags, struct netif *netif) +#else /* LWIP_NETIF_HWADDRHINT */ +find_entry(struct ip_addr *ipaddr, u8_t flags) +#endif /* LWIP_NETIF_HWADDRHINT */ +{ + 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; +#if ARP_QUEUEING + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; +#endif + + /* First, test if the last call to this function asked for the + * same address. If so, we're really fast! */ + if (ipaddr) { + /* ipaddr to search for was given */ +#if LWIP_NETIF_HWADDRHINT + if ((netif != NULL) && (netif->addr_hint != NULL)) { + /* per-pcb cached entry was given */ + u8_t per_pcb_cache = *(netif->addr_hint); + if ((per_pcb_cache < ARP_TABLE_SIZE) && arp_table[per_pcb_cache].state == ETHARP_STATE_STABLE) { + /* the per-pcb-cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[per_pcb_cache].ipaddr)) { + /* per-pcb cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return per_pcb_cache; + } + } + } +#else /* #if LWIP_NETIF_HWADDRHINT */ + if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) { + /* the cached entry is stable */ + if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) { + /* cached entry was the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_cached_entry; + } + } +#endif /* #if LWIP_NETIF_HWADDRHINT */ + } + + /** + * 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) { + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (arp_table[i].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; + } + /* pending entry? */ + else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* 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 pending entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return i; +#if ARP_QUEUEING + /* pending with queued packets? */ + } else if (arp_table[i].q != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } +#endif + /* pending without queued packets? */ + } else { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + } + /* stable entry? */ + else if (arp_table[i].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 stable entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + return i; + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + } else 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 */ + + /* no empty entry found and not allowed to recycle? */ + if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) + /* or don't create new entry, only search? */ + || ((flags & ETHARP_FIND_ONLY) != 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_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)); + } + /* 2) found recyclable stable entry? */ + else 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)); +#if ARP_QUEUEING + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL); +#endif + /* 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)); +#if ARP_QUEUEING + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + 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))); + free_etharp_q(arp_table[i].q); + arp_table[i].q = NULL; +#endif + /* no empty or recyclable entries found */ + } else { + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + + if (arp_table[i].state != ETHARP_STATE_EMPTY) + { + snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr); + } + /* recycle entry (no-op for an already empty entry) */ + arp_table[i].state = ETHARP_STATE_EMPTY; + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_set(&arp_table[i].ipaddr, ipaddr); + } + arp_table[i].ctime = 0; +#if LWIP_NETIF_HWADDRHINT + NETIF_SET_HINT(netif, i); +#else /* #if LWIP_NETIF_HWADDRHINT */ + etharp_cached_entry = i; +#endif /* #if LWIP_NETIF_HWADDRHINT */ + 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 +etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) +{ + struct eth_hdr *ethhdr = p->payload; + u8_t k; + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + k = ETHARP_HWADDR_LEN; + while(k > 0) { + k--; + ethhdr->dest.addr[k] = dst->addr[k]; + ethhdr->src.addr[k] = src->addr[k]; + } + ethhdr->type = 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 ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags Defines behaviour: + * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified, + * only existing ARP entries will be updated. + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t +update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i; + u8_t k; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry()\n")); + 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(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(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 */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, flags, netif); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, flags); +#endif /* LWIP_NETIF_HWADDRHINT */ + /* bail out if no entry could be found */ + if (i < 0) + return (err_t)i; + + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + /* record network interface */ + arp_table[i].netif = netif; + + /* 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 */ + k = ETHARP_HWADDR_LEN; + while (k > 0) { + k--; + arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; + } + /* reset time stamp */ + arp_table[i].ctime = 0; +#if ARP_QUEUEING + /* this is where we will send out queued packets! */ + 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); + /* send the queued IP packet */ + etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); + /* free the queued IP packet */ + pbuf_free(p); + } +#endif + return ERR_OK; +} + +/** + * 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, struct ip_addr *ipaddr, + struct eth_addr **eth_ret, struct ip_addr **ip_ret) +{ + s8_t i; + + LWIP_UNUSED_ARG(netif); + +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_FIND_ONLY, NULL); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, ETHARP_FIND_ONLY); +#endif /* LWIP_NETIF_HWADDRHINT */ + 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; +} + +/** + * 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() + */ +void +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct eth_hdr *ethhdr; + struct ip_hdr *iphdr; + 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 = 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 */ + + /* 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 ARP table */ + /* @todo We could use ETHARP_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), 0); +} + + +/** + * 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() + */ +void +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 */ + struct ip_addr sipaddr, dipaddr; + u8_t i; + u8_t for_us; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + 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 = 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 != htons(HWTYPE_ETHERNET)) || + (hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) || + (hdr->proto != htons(ETHTYPE_IP)) || + (ethhdr->type != 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, ARPH_HWLEN(hdr), hdr->proto, ARPH_PROTOLEN(hdr), 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). */ + SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr)); + SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr)); + + /* this interface is not configured? */ + if (netif->ip_addr.addr == 0) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? */ + if (for_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. */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD); + /* ARP message not directed to us? */ + } else { + /* update the source IP address in the cache, if present */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0); + } + + /* now act on the message itself */ + switch (htons(hdr->opcode)) { + /* ARP request? */ + case 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); + + hdr->dipaddr = hdr->sipaddr; + SMEMCPY(&hdr->sipaddr, &netif->ip_addr, sizeof(hdr->sipaddr)); + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); + i = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr; +#endif /* LWIP_AUTOIP */ + + while(i > 0) { + i--; + hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; +#if LWIP_AUTOIP + ethhdr->dest.addr[i] = ethdst_hwaddr[i]; +#else /* LWIP_AUTOIP */ + ethhdr->dest.addr[i] = hdr->shwaddr.addr[i]; +#endif /* LWIP_AUTOIP */ + hdr->shwaddr.addr[i] = ethaddr->addr[i]; + ethhdr->src.addr[i] = ethaddr->addr[i]; + } + + /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header + are already correct, we tested that before */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* we are not configured? */ + } else if (netif->ip_addr.addr == 0) { + /* { 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 } */ +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP + if(netif->flags & NETIF_FLAG_IPSWITCH) + netif->linkoutput(netif, p); + else +#endif +/* Realtek Modified End */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case 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 +/* Realtek Modified Start */ +#ifdef CONFIG_DONT_CARE_TP + if(netif->flags & NETIF_FLAG_IPSWITCH) + netif->linkoutput(netif, p); +#endif +/* Realtek Modified End */ + 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, struct ip_addr *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))) { + /* interface has default gateway? */ + if (netif->gw.addr != 0) { + /* 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; + } + } + /* 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, struct ip_addr *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 */ +#if LWIP_NETIF_HWADDRHINT + i = find_entry(ipaddr, ETHARP_TRY_HARD, netif); +#else /* LWIP_NETIF_HWADDRHINT */ + i = find_entry(ipaddr, ETHARP_TRY_HARD); +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* 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. */ + } + } + + /* packet given? */ + if (q != NULL) { + /* stable entry? */ + if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping */ + /* 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) { +#if ARP_QUEUEING /* queue the given q packet */ + 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 ... */ + struct etharp_q_entry *new_entry; + /* allocate a new arp queue entry */ + new_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 } through initialization */ + } + } 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 } through initialization */ + } +#else /* ARP_QUEUEING == 0 */ + /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */ + /* { result == ERR_MEM } through initialization */ + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); +#endif + } + } + 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 +etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, + const struct eth_addr *ethdst_addr, + const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, + const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, + const u16_t opcode) +{ + struct pbuf *p; + err_t result = ERR_OK; + u8_t k; /* ARP entry index */ + 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 = 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)); + k = ETHARP_HWADDR_LEN; +#if LWIP_AUTOIP + /* If we are using Link-Local, ARP packets must be broadcast on the + * link layer. (See RFC3927 Section 2.5) */ + ethdst_hwaddr = ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr; +#endif /* LWIP_AUTOIP */ + /* Write MAC-Addresses (combined loop for both headers) */ + while(k > 0) { + k--; + /* Write the ARP MAC-Addresses */ + hdr->shwaddr.addr[k] = hwsrc_addr->addr[k]; + hdr->dhwaddr.addr[k] = hwdst_addr->addr[k]; + /* Write the Ethernet MAC-Addresses */ +#if LWIP_AUTOIP + ethhdr->dest.addr[k] = ethdst_hwaddr[k]; +#else /* LWIP_AUTOIP */ + ethhdr->dest.addr[k] = ethdst_addr->addr[k]; +#endif /* LWIP_AUTOIP */ + ethhdr->src.addr[k] = ethsrc_addr->addr[k]; + } + hdr->sipaddr = *(struct ip_addr2 *)ipsrc_addr; + hdr->dipaddr = *(struct ip_addr2 *)ipdst_addr; + + hdr->hwtype = htons(HWTYPE_ETHERNET); + hdr->proto = htons(ETHTYPE_IP); + /* set hwlen and protolen together */ + hdr->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr)); + + ethhdr->type = 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, struct ip_addr *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); +} + +/** + * 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; + + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, + ("ethernet_input: dest:%02x:%02x:%02x:%02x:%02x:%02x, src:%02x:%02x:%02x:%02x:%02x:%02x, type:%2hx\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 = htons(ethhdr->type); +#if ETHARP_SUPPORT_VLAN + if (type == ETHTYPE_VLAN) { + struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR); +#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 = htons(vlan->tpid); + } +#endif /* ETHARP_SUPPORT_VLAN */ + + switch (type) { + /* IP packet? */ + case ETHTYPE_IP: +#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, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + pbuf_free(p); + p = NULL; + } else { + /* pass to IP layer */ + ip_input(p, netif); + } + break; + + case ETHTYPE_ARP: + /* pass p to ARP module */ + etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); + break; + +#if PPPOE_SUPPORT + case ETHTYPE_PPPOEDISC: /* PPP Over Ethernet Discovery Stage */ + pppoe_disc_input(netif, p); + break; + + case 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); + pbuf_free(p); + p = NULL; + break; + } + + /* This means the pbuf is freed or consumed, + so the caller doesn't have to free it again */ + return ERR_OK; +} +#endif /* LWIP_ARP */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/loopif.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/loopif.c new file mode 100644 index 0000000..cbcda78 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/loopif.c @@ -0,0 +1,66 @@ +/** + * @file + * Loop Interface + * + **/ + +/* + * 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_HAVE_LOOPIF + +#include "netif/loopif.h" +#include "lwip/snmp.h" + +/** + * 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 + */ +err_t +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 */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.c new file mode 100644 index 0000000..cbd3eb2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.c @@ -0,0 +1,990 @@ +/***************************************************************************** +* auth.c - Network Authentication and Phase Control program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Ported from public pppd code. +*****************************************************************************/ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "lcp.h" +#include "pap.h" +#include "chap.h" +#include "auth.h" +#include "ipcp.h" + +#if CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +extern char *crypt (const char *, const char *); + +/* Prototypes for procedures local to this file. */ + +static void network_phase (int); +static void check_idle (void *); +static void connect_time_expired (void *); +#if 0 +static int login (char *, char *, char **, int *); +#endif +static void logout (void); +static int null_login (int); +static int get_pap_passwd (int, char *, char *); +static int have_pap_secret (void); +static int have_chap_secret (char *, char *, u32_t); +static int ip_addr_check (u32_t, struct wordlist *); +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +static void set_allowed_addrs(int unit, struct wordlist *addrs); +static void free_wordlist (struct wordlist *); +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#if CBCP_SUPPORT +static void callback_phase (int); +#endif /* CBCP_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +#if PAP_SUPPORT || CHAP_SUPPORT +/* The name by which the peer authenticated itself to us. */ +static char peer_authname[MAXNAMELEN]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called login() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +#if PAP_SUPPORT || CHAP_SUPPORT +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(int unit) +{ + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG((LOG_INFO, "link_required: %d\n", unit)); +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(int unit) +{ + AUTHDEBUG((LOG_INFO, "link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + logout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG((LOG_NOTICE, "Connection terminated.\n")); + pppLinkTerminated(unit); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(int unit) +{ + int i; + struct protent *protp; + + AUTHDEBUG((LOG_INFO, "link_down: %d\n", unit)); + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; + } + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); + } + } + num_np_open = 0; + num_np_up = 0; + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + pppLinkDown(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(int unit) +{ + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG((LOG_INFO, "link_established: %d\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG((LOG_WARNING, "peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG((LOG_ERR, "No secret found for PAP login\n")); + } + } + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } +} + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(int unit, u16_t protocol) +{ + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG((LOG_INFO, "auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + + +#if PAP_SUPPORT || CHAP_SUPPORT +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +{ + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_PEER; + break; + case PPP_PAP: + pbit = PAP_PEER; + break; + default: + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(int unit, u16_t protocol) +{ + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG((LOG_INFO, "auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + /* + * XXX Warning: the unit number indicates the interface which is + * not necessarily the PPP connection. It works here as long + * as we are only supporting PPP interfaces. + */ + pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(int unit, u16_t protocol) +{ + int pbit; + + AUTHDEBUG((LOG_INFO, "auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; + default: + AUTHDEBUG((LOG_WARNING, "auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG((LOG_INFO, "np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); + } + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG((LOG_INFO, "np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(int unit) +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG((LOG_INFO, "auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; + } + } +} + +#if PAP_SUPPORT +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) +{ +#if 1 + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ +#else + int ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u_short attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ + + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG((LOG_WARNING, "%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + sys_msleep((attempts - 3) * 5); + } + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +#endif +} +#endif /* PAP_SUPPORT */ + + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(int unit, u32_t addr) +{ + return ip_addr_check(addr, addresses[unit]); +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(u32_t addr) +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + + +#if CHAP_SUPPORT +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int get_secret( int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) +{ +#if 1 + int len; + struct wordlist *addrs; + + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; + + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; +#else + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } + + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG((LOG_ERR, "Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} +#endif /* CHAP_SUPPORT */ + + +#if 0 /* UNUSED */ +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options(void) +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } +} +#endif + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * Proceed to the network phase. + */ +static void +network_phase(int unit) +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } + } + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(void *arg) +{ + struct ppp_idle idle; + u_short itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG((LOG_INFO, "Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + AUTHDEBUG((LOG_INFO, "Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +#if 0 +/* + * login - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +login(char *user, char *passwd, char **msg, int *msglen) +{ + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); +} +#endif + +/* + * logout - Logout the user. + */ +static void +logout(void) +{ + logged_in = 0; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; +} + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_pap_passwd(int unit, char *user, char *passwd) +{ + LWIP_UNUSED_ARG(unit); +/* normally we would reject PAP if no password is provided, + but this causes problems with some providers (like CHT in Taiwan) + who incorrectly request PAP and expect a bogus/empty password, so + always provide a default user/passwd of "none"/"none" +*/ + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(void) +{ + /* XXX Fail until we set up our passwords. */ + return 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(char *client, char *server, u32_t remote) +{ + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + /* XXX Fail until we set up our passwords. */ + return 0; +} + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(int unit, struct wordlist *addrs) +{ + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; + +#if 0 + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } + } + } +#endif +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +static int +ip_addr_check(u32_t addr, struct wordlist *addrs) +{ + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; +} + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(struct wordlist *wp) +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.h new file mode 100644 index 0000000..86ff049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/auth.h @@ -0,0 +1,111 @@ +/***************************************************************************** +* auth.h - PPP Authentication and phase control header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD pppd.h. +*****************************************************************************/ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); + +/* peer successfully authenticated itself */ +void auth_peer_success (int, u16_t, char *, int); + +/* we failed to authenticate ourselves */ +void auth_withpeer_fail (int, u16_t); + +/* we successfully authenticated ourselves */ +void auth_withpeer_success (int, u16_t); + +/* check authentication options supplied */ +void auth_check_options (void); + +/* check what secrets we have */ +void auth_reset (int); + +/* Check peer-supplied username/password */ +int check_passwd (int, char *, int, char *, int, char **, int *); + +/* get "secret" for chap */ +int get_secret (int, char *, char *, char *, int *, int); + +/* check if IP address is authorized */ +int auth_ip_addr (int, u32_t); + +/* check if IP address is unreasonable */ +int bad_ip_adrs (u32_t); + +#endif /* AUTH_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.c new file mode 100644 index 0000000..b3ea6b2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.c @@ -0,0 +1,903 @@ +/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/ +/***************************************************************************** +* chap.c - Network Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap.c. +*****************************************************************************/ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "magic.h" +#include "randm.h" +#include "auth.h" +#include "md5.h" +#include "chap.h" +#include "chpms.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Protocol entry points. + */ +static void ChapInit (int); +static void ChapLowerUp (int); +static void ChapLowerDown (int); +static void ChapInput (int, u_char *, int); +static void ChapProtocolReject (int); +#if 0 +static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif + +static void ChapChallengeTimeout (void *); +static void ChapResponseTimeout (void *); +static void ChapReceiveChallenge (chap_state *, u_char *, int, int); +static void ChapRechallenge (void *); +static void ChapReceiveResponse (chap_state *, u_char *, int, int); +static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapSendStatus (chap_state *, int); +static void ChapSendChallenge (chap_state *); +static void ChapSendResponse (chap_state *); +static void ChapGenChallenge (chap_state *); + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, +#if 0 + ChapPrintPkt, + NULL, +#endif + 1, + "CHAP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(int unit, char *our_name, int digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(int unit, char *our_name, int digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(int unit) +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG((LOG_ERR, "Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(int unit) +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(ChapChallengeTimeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(ChapRechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); + } + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(int unit, u_char *inpacket, int packet_len) +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG((LOG_WARNING, "Unknown CHAP code (%d) received.\n", code)); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(chap_state *cstate, u_char *inp, int id, int len) +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'\n", rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name\n", rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating us to %s\n", rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#ifdef CHAPMS + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s\n", rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, secret, &secret_len, 1)) { + /* CHAPDEBUG((LOG_WARNING, TL_CHAP, "No CHAP secret found for authenticating %s\n", rhostname)); */ + CHAPDEBUG((LOG_WARNING, "No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG((LOG_INFO, "unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG((LOG_ERR, "CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n", cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG((LOG_ERR, "CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(chap_state *cstate) +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(chap_state *cstate, int code) +{ + u_char *outp; + int outlen, msglen; + char msg[256]; + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.\n", code, cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(chap_state *cstate) +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + cstate->chal_len = chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic() & 0xff); + } +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(chap_state *cstate) +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#if 0 +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static int +ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) { + printer(arg, " %s", ChapCodenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} +#endif + +#endif /* CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.h new file mode 100644 index 0000000..83dafd7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chap.h @@ -0,0 +1,166 @@ +/***************************************************************************** +* chap.h - Network Challenge Handshake Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-03 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.4 2007/12/19 20:47:22 fbernon Exp $ + */ + +#ifndef CHAP_H +#define CHAP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/****************** +*** PUBLIC DATA *** +******************/ +extern chap_state chap[]; + +extern struct protent chap_protent; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +void ChapAuthWithPeer (int, char *, int); +void ChapAuthPeer (int, char *, int); + +#endif /* CHAP_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.c new file mode 100644 index 0000000..582cfd2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.c @@ -0,0 +1,399 @@ +/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/ +/*** The original PPPD code is written in a way to require either the UNIX DES + encryption functions encrypt(3) and setkey(3) or the DES library libdes. + Since both is not included in lwIP, MSCHAP currently does not work! */ +/***************************************************************************** +* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap_ms.c. +*****************************************************************************/ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#define USE_CRYPT + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "md4.h" +#ifndef USE_CRYPT +#include "des.h" +#endif +#include "chap.h" +#include "chpms.h" + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ + +/* XXX Don't know what to do with these. */ +extern void setkey(const char *); +extern void encrypt(char *, int); + +static void DesEncrypt (u_char *, u_char *, u_char *); +static void MakeKey (u_char *, u_char *); + +#ifdef USE_CRYPT +static void Expand (u_char *, u_char *); +static void Collapse (u_char *, u_char *); +#endif + +static void ChallengeResponse( + u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */ +); +static void ChapMS_NT( + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response +); +static u_char Get7Bits( + u_char *input, + int startBit +); + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +void +ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'\n", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +static void +ChallengeResponse( u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */) +{ + char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey(crypt_key); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt(des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char +Get7Bits( u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void +Expand(u_char *in, u_char *out) +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } +} + +/* The inverse of Expand + */ +static void +Collapse(u_char *in, u_char *out) +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } +} +#endif + +static void +MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */ + u_char *des_key /* OUT 64 bit DES key with parity bits added */) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + MDstruct md4Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u_char)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (htons((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + MDreverse((u_long *)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse(rchallenge, (char *)md4Context.buffer, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u_char)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +#endif /* MSCHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.h new file mode 100644 index 0000000..df070fb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/chpms.h @@ -0,0 +1,64 @@ +/***************************************************************************** +* chpms.h - Network Microsoft Challenge Handshake Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-01-30 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef CHPMS_H +#define CHPMS_H + +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS (chap_state *, char *, int, char *, int); + +#endif /* CHPMS_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.c new file mode 100644 index 0000000..ee549f2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.c @@ -0,0 +1,908 @@ +/***************************************************************************** +* fsm.c - Network Control Protocol Finite State Machine program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD fsm.c. +*****************************************************************************/ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" + +#include + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +#if PPP_DEBUG + +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; + +#endif /* PPP_DEBUG */ + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +static void fsm_timeout (void *); +static void fsm_rconfreq (fsm *, u_char, u_char *, int); +static void fsm_rconfack (fsm *, int, u_char *, int); +static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); +static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rtermack (fsm *); +static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_sconfreq (fsm *, int); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +int peer_mru[NUM_PPP]; + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(fsm *f) +{ + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; + + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG((LOG_INFO, "%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG((LOG_INFO, "%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the LS_CLOSED state. + */ +void +fsm_close(fsm *f, char *reason) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG((LOG_INFO, "%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(fsm *f, u_char *inpacket, int l) +{ + u_char *inp = inpacket; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG((LOG_INFO, "fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(fsm *f) +{ + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(void *arg) +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG((LOG_WARNING, "%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG((LOG_WARNING, "%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; + + default: + FSMDEBUG((LOG_INFO, "%s: UNHANDLED timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +{ + int code, reject_if_disagree; + + FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u_char)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(fsm *f, int id, u_char *inp, int len) +{ + FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +{ + int (*proc) (fsm *, u_char *, int); + int ret; + + FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(fsm *f, int id, u_char *p, int len) +{ + LWIP_UNUSED_ARG(p); + + FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG((LOG_INFO, "%s terminated by peer (%x)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG((LOG_INFO, "%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(fsm *f) +{ + FSMDEBUG((LOG_INFO, "fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(fsm *f, u_char *inp, int len) +{ + u_char code, id; + + FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG((LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(fsm *f, int retransmit) +{ + u_char *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.h new file mode 100644 index 0000000..14034ec --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/fsm.h @@ -0,0 +1,169 @@ +/***************************************************************************** +* fsm.h - Network Control Protocol Finite State Machine header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD code. +*****************************************************************************/ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.4 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef FSM_H +#define FSM_H + +/***************************************************************************** +************************* PUBLIC DEFINITIONS ********************************* +*****************************************************************************/ +/* + * LCP Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + +/* + * Link states. + */ +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/***************************************************************************** +************************* PUBLIC DATA TYPES ********************************** +*****************************************************************************/ +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/***************************************************************************** +*********************** PUBLIC DATA STRUCTURES ******************************* +*****************************************************************************/ +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ + + +/***************************************************************************** +************************** PUBLIC FUNCTIONS ********************************** +*****************************************************************************/ + +/* + * Prototypes + */ +void fsm_init (fsm*); +void fsm_lowerup (fsm*); +void fsm_lowerdown (fsm*); +void fsm_open (fsm*); +void fsm_close (fsm*, char*); +void fsm_input (fsm*, u_char*, int); +void fsm_protreject (fsm*); +void fsm_sdata (fsm*, u_char, u_char, u_char*, int); + +#endif /* FSM_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.c new file mode 100644 index 0000000..0ff4ce3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.c @@ -0,0 +1,1427 @@ +/***************************************************************************** +* ipcp.c - Network PPP IP Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "auth.h" +#include "fsm.h" +#include "vj.h" +#include "ipcp.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ +#if 0 +static void ipcp_script (fsm *, char *); /* Run an up/down script */ +#endif +static void ipcp_finished (fsm *); /* Don't need lower layer */ + +/* + * Protocol entry points from main code. + */ +static void ipcp_init (int); +static void ipcp_open (int); +static void ipcp_close (int, char *); +static void ipcp_lowerup (int); +static void ipcp_lowerdown (int); +static void ipcp_input (int, u_char *, int); +static void ipcp_protrej (int); + +static void ipcp_clear_addrs (int); + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if 0 + ipcp_printpkt, + NULL, +#endif + 1, + "IPCP", +#if 0 + ip_check_options, + NULL, + ip_active_pkt +#endif +}; + + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +/* local vars */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#define inet_ntoa(addr) ip_ntoa(((struct ip_addr*)&(addr))) + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(int unit) +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(int unit) +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(int unit, char *reason) +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(int unit) +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(int unit, u_char *p, int len) +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(fsm *f) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(fsm *f) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!\n")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG((LOG_INFO, "remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG((LOG_INFO, "secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; +#ifdef OLD_CI_ADDRS + ipcp_options *go = &ipcp_gotoptions[f->unit]; +#endif + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ +#ifdef OLD_CI_ADDRS + u32_t ciaddr2; /* Parsed address values */ +#endif + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +#ifdef OLD_CI_ADDRS /* Need to save space... */ + case CI_ADDRS: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG((LOG_INFO, "his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG((LOG_INFO, "our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; +#endif + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking DNS%d Request %d\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG((LOG_INFO, "ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG((LOG_INFO, + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG((LOG_INFO, "ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG((LOG_INFO, "ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options(u_long localAddr) +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } +} +#endif + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(fsm *f) +{ + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG((LOG_INFO, "ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG((LOG_ERR, "Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG((LOG_ERR, "Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG((LOG_WARNING, "sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG((LOG_WARNING, "sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG((LOG_NOTICE, "local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG((LOG_NOTICE, "remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG((LOG_NOTICE, "primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG((LOG_NOTICE, "secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(fsm *f) +{ + IPCPDEBUG((LOG_INFO, "ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, etc. + */ +static void +ipcp_clear_addrs(int unit) +{ + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(fsm *f) +{ + np_finished(f->unit, PPP_IP); +} + +#if 0 +static int +ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(u_char *pkt, int len) +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; +} +#endif + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.h new file mode 100644 index 0000000..dfcf4fb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ipcp.h @@ -0,0 +1,124 @@ +/***************************************************************************** +* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.3 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef IPCP_H +#define IPCP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +typedef struct ipcp_options { + u_int neg_addr : 1; /* Negotiate IP Address? */ + u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ + u_int req_addr : 1; /* Ask peer to send IP address? */ + u_int default_route : 1; /* Assign default route through interface? */ + u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u_int neg_vj : 1; /* Van Jacobson Compression? */ + u_int old_vj : 1; /* use old (short) form of VJ option? */ + u_int accept_local : 1; /* accept peer's value for ouraddr */ + u_int accept_remote : 1; /* accept peer's value for hisaddr */ + u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex; /* VJ slots - 1. */ + u_char cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +extern struct protent ipcp_protent; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +#endif /* IPCP_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.c new file mode 100644 index 0000000..85a0add --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.c @@ -0,0 +1,2035 @@ +/***************************************************************************** +* lcp.c - Network Link Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "chap.h" +#include "magic.h" +#include "auth.h" +#include "lcp.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u_char, u_char*, int); + +static void lcp_rprotrej (fsm*, u_char*, int); + +/* + * routines to send LCP echos to peer + */ +static void lcp_echo_lowerup (int); +static void lcp_echo_lowerdown (int); +static void LcpEchoTimeout (void*); +static void lcp_received_echo_reply (fsm*, int, u_char*, int); +static void LcpSendEchoRequest (fsm*); +static void LcpLinkFailure (fsm*); +static void LcpEchoCheck (fsm*); + +/* + * Protocol entry points. + * Some of these are called directly. + */ +static void lcp_input (int, u_char *, int); +static void lcp_protrej (int); + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +/* global vars */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ + + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if 0 + lcp_printpkt, + NULL, +#endif + 1, + "LCP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG((LOG_INFO, "lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(int unit, char *reason) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(&lcp_fsm[unit], reason); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(int unit) +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0] + | ((u_long)xmit_accm[unit][1] << 8) + | ((u_long)xmit_accm[unit][2] << 16) + | ((u_long)xmit_accm[unit][3] << 24); + LCPDEBUG((LOG_INFO, "lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(int unit) +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(int unit, u_char *p, int len) +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); +} + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(int unit, u_char *p, int len) +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(fsm *f, u_char *inp, int len) +{ + int i; + struct protent *protp; + u_short prot; + + if (len < sizeof (u_short)) { + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG((LOG_INFO, "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG((LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x\n", prot)); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +static void +lcp_protrej(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG((LOG_WARNING, "lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(fsm *f) +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int lcp_cilen(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG((LOG_INFO, "lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG((LOG_ERR, "Bug in lcp_addci: wrong length\n")); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG((LOG_INFO, "lcp_acki: Ack\n")); + return (1); +bad: + LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!\n")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG((LOG_NOTICE, "Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO, "lcp_rejci: void opt %d rejected\n", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: short opt %d rejected\n", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: chap opt %d rejected\n", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: long opt %d rejected\n", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: LQR opt %d rejected\n", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG((LOG_INFO,"lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(fsm *f, + u_char *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ +#if TRACELCP > 0 + char traceBuf[80]; + int traceNdx = 0; +#endif + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG((LOG_INFO, "lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#ifdef CHAPMS + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG((LOG_WARNING, "lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); +#endif + + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); +#endif + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + default: +#if TRACELCP + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } +#endif + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + +#if TRACELCP > 0 + if (traceNdx > 0) { + LCPDEBUG((LOG_INFO, "lcp_reqci: %s\n", traceBuf)); + } +#endif + LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(fsm *f) +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(fsm *f) +{ + link_required(f->unit); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(fsm *f) +{ + link_terminated(f->unit); +} + + +#if 0 +/* + * print_string - print a readable representation of a string using + * printer. + */ +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } + } + printer(arg, "\""); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); +} +#endif + +/* + * Time to shut down the link because there is nothing out there. + */ +static void +LcpLinkFailure (fsm *f) +{ + if (f->state == LS_OPENED) { + LCPDEBUG((LOG_INFO, "No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG((LOG_NOTICE, "Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ +static void +LcpEchoCheck (fsm *f) +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ +static void +LcpEchoTimeout (void *arg) +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ +static void +lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +{ + u32_t magic; + + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG((LOG_WARNING, "lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG((LOG_WARNING, "appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ +static void +LcpSendEchoRequest (fsm *f) +{ + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending++ >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + LcpEchoCheck (f); + } +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.h new file mode 100644 index 0000000..1a5e5a4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/lcp.h @@ -0,0 +1,167 @@ +/***************************************************************************** +* lcp.h - Network Link Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.3 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef LCP_H +#define LCP_H + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ + +/* + * LCP-specific packet types. + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + u_int passive : 1; /* Don't die if we don't get a response */ + u_int silent : 1; /* Wait for the other end to start first */ + u_int restart : 1; /* Restart vs. exit after close */ + u_int neg_mru : 1; /* Negotiate the MRU? */ + u_int neg_asyncmap : 1; /* Negotiate the async map? */ + u_int neg_upap : 1; /* Ask for UPAP authentication? */ + u_int neg_chap : 1; /* Ask for CHAP authentication? */ + u_int neg_magicnumber : 1; /* Ask for magic number? */ + u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_int neg_cbcp : 1; /* Negotiate use of CBCP */ +#ifdef PPP_MULTILINK + u_int neg_mrru : 1; /* Negotiate multilink MRRU */ + u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ +#endif + u_short mru; /* Value of MRU */ +#ifdef PPP_MULTILINK + u_short mrru; /* Value of MRRU, and multilink enable */ +#endif + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#ifdef PPP_MULTILINK + struct epdisc endpoint; /* endpoint discriminator */ +#endif +} lcp_options; + +/* + * Values for phase from BSD pppd.h based on RFC 1661. + */ +typedef enum { + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE +} LinkPhase; + + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern ext_accm xmit_accm[]; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 + +#endif /* LCP_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.c new file mode 100644 index 0000000..d3922bb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.c @@ -0,0 +1,82 @@ +/***************************************************************************** +* magic.c - Network Random Number Generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD magic.c. +*****************************************************************************/ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "ppp.h" +#include "randm.h" +#include "magic.h" + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * magicInit - Initialize the magic number generator. + * + * Since we use another random number generator that has its own + * initialization, we do nothing here. + */ +void magicInit() +{ + return; +} + +/* + * magic - Returns the next magic number. + */ +u32_t magic() +{ + return avRandom(); +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.h new file mode 100644 index 0000000..bc51749 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/magic.h @@ -0,0 +1,67 @@ +/***************************************************************************** +* magic.h - Network Random Number Generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.2 2007/12/02 22:35:55 fbernon Exp $ + */ + +#ifndef MAGIC_H +#define MAGIC_H + +/***************************************************************************** +************************** PUBLIC FUNCTIONS ********************************** +*****************************************************************************/ + +/* Initialize the magic number generator */ +void magicInit(void); + +/* Returns the next magic number */ +u32_t magic(void); + +#endif /* MAGIC_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.c new file mode 100644 index 0000000..7a60d78 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.c @@ -0,0 +1,320 @@ +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "ppp.h" +#include "pppdebug.h" + +#include "md5.h" + +#include + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (u32_t *buf, u32_t *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* 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) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##UL +#else +#ifdef WIN32 +#define UL(x) x##UL +#else +#define UL(x) x +#endif +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (u32_t)0; + + /* Load magic initialization constants. */ + mdContext->buf[0] = (u32_t)0x67452301UL; + mdContext->buf[1] = (u32_t)0xefcdab89UL; + mdContext->buf[2] = (u32_t)0x98badcfeUL; + mdContext->buf[3] = (u32_t)0x10325476UL; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + +#if 0 + ppp_trace(LOG_INFO, "MD5Update: %u:%.*H\n", inLen, MIN(inLen, 20) * 2, inBuf); + ppp_trace(LOG_INFO, "MD5Update: %u:%s\n", inLen, inBuf); +#endif + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) { + mdContext->i[1]++; + } + mdContext->i[0] += ((u32_t)inLen << 3); + mdContext->i[1] += ((u32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +MD5Final (unsigned char hash[], MD5_CTX *mdContext) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + SMEMCPY(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void +Transform (u32_t *buf, u32_t *in) +{ + u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.h new file mode 100644 index 0000000..e129533 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/md5.h @@ -0,0 +1,55 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init ( MD5_CTX *mdContext); +void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final ( unsigned char hash[], MD5_CTX *mdContext); + +#endif /* MD5_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.c new file mode 100644 index 0000000..e8c45df --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.c @@ -0,0 +1,622 @@ +/***************************************************************************** +* pap.c - Network Password Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-12 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "auth.h" +#include "pap.h" + +#include + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +/* + * Protocol entry points. + */ +static void upap_init (int); +static void upap_lowerup (int); +static void upap_lowerdown (int); +static void upap_input (int, u_char *, int); +static void upap_protrej (int); + +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u_char *, int, int); +static void upap_rauthack (upap_state *, u_char *, int, int); +static void upap_rauthnak (upap_state *, u_char *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u_char, u_char, char *, int); + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if 0 + upap_printpkt, + NULL, +#endif + 1, + "PAP", +#if 0 + NULL, + NULL, + NULL +#endif +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Set the default login name and password for the pap sessions + */ +void +upap_setloginpasswd(int unit, const char *luser, const char *lpassword) +{ + upap_state *u = &upap[unit]; + + /* Save the username and password we're given */ + u->us_user = luser; + u->us_userlen = strlen(luser); + u->us_passwd = lpassword; + u->us_passwdlen = strlen(lpassword); +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(int unit, char *user, char *password) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + upap_setloginpasswd(unit, user, password); + + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(int unit) +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG((LOG_INFO, "upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG((LOG_ERR, "No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request */ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerup: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG((LOG_INFO, "upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(int unit) +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG((LOG_ERR, "PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG((LOG_ERR, "PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(int unit, u_char *inpacket, int l) +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(upap_state *u, u_char *inp, int id, int len) +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + int retcode; + char *msg; + int msglen; + + UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + GETCHAR(msglen, inp); + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nakk. + */ +static void +upap_rauthnak(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + } else { + GETCHAR(msglen, inp); + if(msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG((LOG_ERR, "PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(upap_state *u) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); +} + +#if 0 +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static int upap_printpkt( + u_char *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg +) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} +#endif /* 0 */ + +#endif /* PAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.h new file mode 100644 index 0000000..0a09fc8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pap.h @@ -0,0 +1,131 @@ +/***************************************************************************** +* pap.h - PPP Password Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef PAP_H +#define PAP_H + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +extern upap_state upap[]; + +void upap_setloginpasswd(int unit, const char *luser, const char *lpassword); +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); + +extern struct protent pap_protent; + +#endif /* PAP_SUPPORT */ + +#endif /* PAP_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.c new file mode 100644 index 0000000..13fa5ed --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.c @@ -0,0 +1,1989 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip.h" /* for ip_input() */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "randm.h" +#include "fsm.h" +#if PAP_SUPPORT +#include "pap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap.h" +#endif /* CHAP_SUPPORT */ +#include "ipcp.h" +#include "lcp.h" +#include "magic.h" +#include "auth.h" +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} PPPDevStates; + +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +/* + * PPP interface control block. + */ +typedef struct PPPControl_s { + char openFlag; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ + int kill_link; /* Shut the link down. */ + int sig_hup; /* Carrier lost. */ + struct pbuf *inHead, *inTail; /* The input packet. */ + PPPDevStates inState; /* The input process state. */ + char inEscaped; /* Escape next character. */ + u16_t inProtocol; /* The input protocol code. */ + u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + int mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u_long lastXMit; /* Time of last transmission. */ + ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ + ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vjEnabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vjComp; /* Van Jacobson compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + struct netif netif; + + struct ppp_addrs addrs; + + void (*linkStatusCB)(void *ctx, int errCode, void *arg); + void *linkStatusCtx; + +} PPPControl; + + +/* + * Ioctl definitions. + */ + +struct npioctl { + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum NPmode mode; +}; + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +#if PPPOS_SUPPORT +static void pppMain(void *pd); +static void pppDrop(PPPControl *pc); +static void pppInProc(int pd, u_char *s, int l); +#endif /* PPPOS_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +u_long subnetMask; + +static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +struct protent *ppp_protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL +}; + + +/* + * Buffers for outgoing packets. This must be accessed only from the appropriate + * PPP task so that it doesn't need to be protected to avoid collisions. + */ +u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ + +#if PPPOS_SUPPORT +/* + * FCS lookup table as calculated by genfcstab. + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char pppACCMMask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; + + +void +pppMainWakeup(int pd) +{ + PPPDEBUG((LOG_DEBUG, "pppMainWakeup: unit %d\n", pd)); + sio_read_abort(pppControl[pd].fd); +} +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPDEBUG((LOG_DEBUG, "pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +void +pppLinkDown(int pd) +{ + PPPDEBUG((LOG_DEBUG, "pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if(pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +/* these callbacks are necessary because lcp_* functions + must be called in the same context as pppInput(), + namely the tcpip_thread(), essentially because + they manipulate timeouts which are thread-private +*/ + +static void +pppStartCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStartCB: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ +} + +static void +pppStopCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppStopCB: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +static void +pppHupCB(void *arg) +{ + int pd = (int)arg; + + PPPDEBUG((LOG_DEBUG, "pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* Initialize the PPP subsystem. */ + +struct ppp_settings ppp_settings; + +void +pppInit(void) +{ + struct protent *protp; + int i, j; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + + magicInit(); + + subnetMask = htonl(0xffffff00); + + for (i = 0; i < NUM_PPP; i++) { + pppControl[i].openFlag = 0; + + /* + * Initialize to the standard option set. + */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); + } + } + +#if PPPOE_SUPPORT + pppoe_init(); +#endif /* PPPOE_SUPPORT */ +} + +void +pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) +{ + switch(authType) { + case PPPAUTHTYPE_NONE: + default: +#ifdef LWIP_PPP_STRICT_PAP_REJECT + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; + + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if(user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if(passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } +} + +#if PPPOS_SUPPORT +/* Open a new PPP connection using the given I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. If this port + * connects to a modem, the modem connection must be + * established before calling this. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. */ +int +pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pppControl[pd].openFlag = !0; + } + + /* Launch a deamon thread. */ + if (pd >= 0) { + pppControl[pd].openFlag = 1; + + lcp_init(pd); + pc = &pppControl[pd]; + pc->fd = fd; +#if PPPOE_SUPPORT + pc->ethif= NULL; +#endif /* PPPOE_SUPPORT */ + pc->kill_link = 0; + pc->sig_hup = 0; + pc->if_up = 0; + pc->errCode = 0; + pc->inState = PDIDLE; + pc->inHead = NULL; + pc->inTail = NULL; + pc->inEscaped = 0; + pc->lastXMit = 0; + +#if VJ_SUPPORT + pc->vjEnabled = 0; + vj_compress_init(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + memset(pc->inACCM, 0, sizeof(ext_accm)); + pc->inACCM[15] = 0x60; + memset(pc->outACCM, 0, sizeof(ext_accm)); + pc->outACCM[15] = 0x60; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + sys_thread_new(PPP_THREAD_NAME, pppMain, (void*)pd, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); + if(!linkStatusCB) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + pppClose(pd); + if (pc->errCode) { + pd = pc->errCode; + } else { + pd = PPPERR_CONNECT; + } + } + } + } + } + + return pd; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void +pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pppControl[pd].openFlag = !0; + } + + /* PPP session descriptor found, start PPPoE */ + if (pd >= 0) { + + pppControl[pd].openFlag = 1; + + lcp_init(pd); + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + pc = &pppControl[pd]; + pc->if_up = 0; + pc->errCode = 0; + pc->lastXMit = 0; +#if PPPOS_SUPPORT + pc->kill_link = 0; + pc->sig_hup = 0; + pc->inState = PDIDLE; + pc->inHead = NULL; + pc->inTail = NULL; + pc->inEscaped = 0; +#if VJ_SUPPORT + pc->vjEnabled = 0; +#endif /* VJ_SUPPORT */ +#endif /* PPPOS_SUPPORT */ + pc->ethif= ethif; + + memset(pc->inACCM, 0, sizeof(ext_accm)); + memset(pc->outACCM, 0, sizeof(ext_accm)); + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + + if(!linkStatusCB) { + while(pd >= 0 && !pc->if_up) { + sys_msleep(500); + if (lcp_phase[pd] == PHASE_DEAD) { + pppClose(pd); + if (pc->errCode) { + pd = pc->errCode; + } else { + pd = PPPERR_CONNECT; + } + } + } + } + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +/* Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. */ +int +pppClose(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppClose: unit %d kill_link -> pppStopCB\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + tcpip_callback(pppStopCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pc->kill_link = !0; + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } + + if(!pc->linkStatusCB) { + while(st >= 0 && lcp_phase[pd] != PHASE_DEAD) { + sys_msleep(500); + break; + } + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +pppSigHUP(int pd) +{ + PPPControl *pc = &pppControl[pd]; + +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG((LOG_DEBUG, "pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + tcpip_callback(pppHupCB, (void*)pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + pc->sig_hup = 1; + pppMainWakeup(pd); +#endif /* PPPOS_SUPPORT */ + } +} + +#if PPPOS_SUPPORT +static void +nPut(PPPControl *pc, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG((LOG_WARNING, + "PPP nPut: incomplete sio_write(%d,, %u) = %d\n", pc->fd, b->len, c)); + LINK_STATS_INC(link.err); + pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ + break; + } + } + + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * pppAppend - append given character to end of given pbuf. If outACCM + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (outACCM && ESCAPE_P(*outACCM, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return ERR_MEM; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + +/* Send a packet on the given connection. */ +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, struct ip_addr *ipaddr) +{ + int pd = (int)netif->state; + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_short protocol = PPP_IP; + u_int fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u_char c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, PPP_IP, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG((LOG_ERR, "pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vjEnabled) { + switch (vj_compress_tcp(&pc->vjComp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG((LOG_WARNING, "pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + pbuf_free(headMB); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->lastXMit = sys_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG((LOG_INFO, "pppifOutput[%d]: proto=0x%04X\n", pd, protocol)); + + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return ERR_OK; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +pppIOCtl(int pd, int cmd, void *arg) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->errCode = *(int *)arg; + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->errCode); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; + } + } + + return st; +} + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_int +pppMTU(int pd) +{ + PPPControl *pc = &pppControl[pd]; + u_int st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + pb = pbuf_alloc(PBUF_LINK, pppoe_hdrlen + n, PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -pppoe_hdrlen); + + pc->lastXMit = sys_jiffies(); + + MEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + return PPPERR_DEVICE; + } + + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int +pppWrite(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_char c; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->lastXMit = sys_jiffies(); + + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG((LOG_WARNING, + "pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + return PPPERR_ALLOC; + } + + PPPDEBUG((LOG_INFO, "pppWrite[%d]: len=%d\n", pd, headMB->len)); + /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config( int unit, int mtu, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG((LOG_INFO, "ppp_send_config[%d]: outACCM=%X %X %X %X\n", + unit, + pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(int unit, ext_accm *accm) +{ + SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm)); + PPPDEBUG((LOG_INFO, "ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", + unit, + pppControl[unit].outACCM[0], + pppControl[unit].outACCM[1], + pppControl[unit].outACCM[2], + pppControl[unit].outACCM[3])); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32 / 8; i++) { + pc->inACCM[i] = (u_char)(asyncmap >> (i * 8)); + } + PPPDEBUG((LOG_INFO, "ppp_recv_config[%d]: inACCM=%X %X %X %X\n", + unit, + pc->inACCM[0], pc->inACCM[1], pc->inACCM[2], pc->inACCM[3])); +} + +#if 0 +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr) +{ + return 0; /* XXX Currently no compression. */ +} + +/* + * ccp_flags_set - inform kernel about the current state of CCP. + */ +void +ccp_flags_set(int unit, int isopen, int isup) +{ + /* XXX */ +} + +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(int unit) +{ + /* XXX */ + return 0; +} +#endif + +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); + + return 0; +} + + +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t +GetMask(u32_t addr) +{ + u32_t mask, nmask; + + htonl(addr); + if (IN_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IN_CLASSA_NET; + } else if (IN_CLASSB(addr)) { + nmask = IN_CLASSB_NET; + } else { + nmask = IN_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = subnetMask | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(int pd, int vjcomp, int cidcomp, int maxcid) +{ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPControl *pc = &pppControl[pd]; + + pc->vjEnabled = vjcomp; + pc->vjComp.compressSlot = cidcomp; + pc->vjComp.maxSlotIndex = maxcid; + PPPDEBUG((LOG_INFO, "sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + LWIP_UNUSED_ARG(pd); + LWIP_UNUSED_ARG(vjcomp); + LWIP_UNUSED_ARG(cidcomp); + LWIP_UNUSED_ARG(maxcid); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +/* + * pppifNetifInit - netif init callback + */ +static err_t +pppifNetifInit(struct netif *netif) +{ + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = pppMTU((int)netif->state); + return ERR_OK; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, &pc->addrs.his_ipaddr, (void *)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); + pc->if_up = 1; + pc->errCode = PPPERR_NONE; + + PPPDEBUG((LOG_DEBUG, "sifup: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); + } + } else { + st = 0; + PPPDEBUG((LOG_ERR, "sifup[%d]: netif_add failed\n", pd)); + } + } + + return st; +} + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int +sifnpmode(int u, int proto, enum NPmode mode) +{ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + /* make sure the netif status callback is called */ + netif_set_down(&pc->netif); + netif_remove(&pc->netif); + PPPDEBUG((LOG_DEBUG, "sifdown: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); + } + } + return st; +} + +/** + * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS + */ +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; +} + +/** + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? + */ +int +cifaddr( int pd, u32_t o, u32_t h) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } + + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG((LOG_WARNING, "sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } + + return st; +} + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT +/* The main PPP process function. This implements the state machine according + * to section 4 of RFC 1661: The Point-To-Point Protocol. */ +static void +pppMain(void *arg) +{ + int pd = (int)arg; + struct pbuf *p; + PPPControl* pc; + int c; + + pc = &pppControl[pd]; + + p = pbuf_alloc(PBUF_RAW, PPP_MRU+PPP_HDRLEN, PBUF_RAM); + if (!p) { + LWIP_ASSERT("p != NULL", p); + pc->errCode = PPPERR_ALLOC; + goto out; + } + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + tcpip_callback(pppStartCB, arg); + while (lcp_phase[pd] != PHASE_DEAD) { + if (pc->kill_link) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d kill_link -> pppStopCB\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + tcpip_callback(pppStopCB, arg); + pc->kill_link = 0; + } else if (pc->sig_hup) { + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d sig_hup -> pppHupCB\n", pd)); + pc->sig_hup = 0; + tcpip_callback(pppHupCB, arg); + } else { + c = sio_read(pc->fd, p->payload, p->len); + if(c > 0) { + pppInProc(pd, p->payload, c); + } else { + /* nothing received, give other tasks a chance to run */ + sys_msleep(1); + } + } + } + PPPDEBUG((LOG_INFO, "pppMain: unit %d: PHASE_DEAD\n", pd)); + pppDrop(pc); /* bug fix #17726 */ + pbuf_free(p); + +out: + PPPDEBUG((LOG_DEBUG, "pppMain: unit %d: linkStatusCB=%lx errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } + + pc->openFlag = 0; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + +void +pppOverEthernetInitFailed(void* arg) +{ + PPPControl* pc; + int pd = (int)arg; + + pppHupCB(arg); + pppStopCB(arg); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG((LOG_INFO, "pppMain: unit %d: Connecting\n", pd)); + tcpip_callback(pppStartCB, (void*)pd); + } else { + PPPControl* pc; + pc = &pppControl[pd]; + tcpip_callback(pppOverEthernetInitFailed, (void*)pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf * +pppSingleBuf(struct pbuf *p) +{ + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG((LOG_ERR, + "pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +struct pppInputHeader { + int unit; + u16_t proto; +}; + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +static void +pppInput(void *arg) +{ + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; + + pd = ((struct pppInputHeader *)nb->payload)->unit; + protocol = ((struct pppInputHeader *)nb->payload)->proto; + + if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG((LOG_INFO, "pppInput: discarding proto 0x%04X in phase %d\n", protocol, lcp_phase[pd])); + goto drop; + } + } + + switch(protocol) { + case PPP_VJC_COMP: /* VJ compressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ compressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG((LOG_INFO, "pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG((LOG_WARNING, "pppInput[%d]: Dropping VJ uncompressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG((LOG_INFO, + "pppInput[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG((LOG_INFO, "pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = pppSingleBuf(nb); + (*protp->input)(pd, nb->payload, nb->len); + goto out; + } + } + + /* No handler for this protocol so reject the packet. */ + PPPDEBUG((LOG_INFO, "pppInput[%d]: rejecting unsupported proto 0x%04X len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } +#if BYTE_ORDER == LITTLE_ENDIAN + protocol = htons(protocol); + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + lcp_sprotrej(pd, nb->payload, nb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + +out: + pbuf_free(nb); + return; +} + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +pppDrop(PPPControl *pc) +{ + if (pc->inHead != NULL) { +#if 0 + PPPDEBUG((LOG_INFO, "pppDrop: %d:%.*H\n", pc->inHead->len, min(60, pc->inHead->len * 2), pc->inHead->payload)); +#endif + PPPDEBUG((LOG_INFO, "pppDrop: pbuf len=%d\n", pc->inHead->len)); + if (pc->inTail && (pc->inTail != pc->inHead)) { + pbuf_free(pc->inTail); + } + pbuf_free(pc->inHead); + pc->inHead = NULL; + pc->inTail = NULL; + } +#if VJ_SUPPORT + vj_uncompress_err(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); +} + +/** + * Process a received octet string. + */ +static void +pppInProc(int pd, u_char *s, int l) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *nextNBuf; + u_char curChar; + + PPPDEBUG((LOG_DEBUG, "pppInProc[%d]: got %d bytes\n", pd, l)); + while (l-- > 0) { + curChar = *s++; + + /* Handle special characters. */ + if (ESCAPE_P(pc->inACCM, curChar)) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pc->inEscaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pc->inState <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pc->inState < PDDATA) { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Dropping incomplete packet %d\n", + pd, pc->inState)); + LINK_STATS_INC(link.lenerr); + pppDrop(pc); + /* If the fcs is invalid, drop the packet. */ + } else if (pc->inFCS != PPP_GOODFCS) { + PPPDEBUG((LOG_INFO, + "pppInProc[%d]: Dropping bad fcs 0x%04X proto=0x%04X\n", + pd, pc->inFCS, pc->inProtocol)); + LINK_STATS_INC(link.chkerr); + pppDrop(pc); + /* Otherwise it's a good packet so pass it on. */ + } else { + /* Trim off the checksum. */ + if(pc->inTail->len >= 2) { + pc->inTail->len -= 2; + + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + } else { + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + + pbuf_realloc(pc->inHead, pc->inHead->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + if(tcpip_callback(pppInput, pc->inHead) != ERR_OK) { + PPPDEBUG((LOG_ERR, "pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pd)); + pbuf_free(pc->inHead); + LINK_STATS_INC(link.drop); + } + pc->inHead = NULL; + pc->inTail = NULL; + } + + /* Prepare for a new packet. */ + pc->inFCS = PPP_INITFCS; + pc->inState = PDADDRESS; + pc->inEscaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Dropping ACCM char <%d>\n", pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pc->inEscaped) { + pc->inEscaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pc->inState) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pc->inFCS = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pc->inState = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pc->inState = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG((LOG_WARNING, + "pppInProc[%d]: Invalid control <%d>\n", pd, curChar)); + pc->inState = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pc->inProtocol = curChar; + pc->inState = PDDATA; + } else { + pc->inProtocol = (u_int)curChar << 8; + pc->inState = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pc->inProtocol |= curChar; + pc->inState = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pc->inTail == NULL || pc->inTail->len == PBUF_POOL_BUFSIZE) { + if(pc->inTail) { + pc->inTail->tot_len = pc->inTail->len; + if (pc->inTail != pc->inHead) { + pbuf_cat(pc->inHead, pc->inTail); + } + } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG((LOG_ERR, "pppInProc[%d]: NO FREE MBUFS!\n", pd)); + LINK_STATS_INC(link.memerr); + pppDrop(pc); + pc->inState = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pc->inHead == NULL) { + struct pppInputHeader *pih = nextNBuf->payload; + + pih->unit = pd; + pih->proto = pc->inProtocol; + + nextNBuf->len += sizeof(*pih); + + pc->inHead = nextNBuf; + } + pc->inTail = nextNBuf; + } + /* Load character into buffer. */ + ((u_char*)pc->inTail->payload)[pc->inTail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pc->inFCS = PPP_FCS(pc->inFCS, curChar); + } + } + + avRandomize(); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void +pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + if(tcpip_callback(pppInput, pb) != ERR_OK) { + PPPDEBUG((LOG_ERR, "pppInProcOverEthernet[%d]: tcpip_callback() failed, dropping packet\n", pd)); + goto drop; + } + + return; + +drop: + LINK_STATS_INC(link.drop); + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.h new file mode 100644 index 0000000..ebc733b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp.h @@ -0,0 +1,465 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/api.h" +#include "lwip/sockets.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/tcpip.h" +#include "lwip/netif.h" + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) sys_untimeout((f), (a)), sys_timeout((t)*1000, (f), (a)) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +#ifndef __u_char_defined + +/* Type definitions for BSD code. */ +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; + +#endif + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; ppp_trace(LOG_INFO, "Remote message: %s\n", m); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if 0 + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif + int enabled_flag; /* 0 iff protocol is disabled */ + char *name; /* Text name of protocol */ +#if 0 + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +struct ppp_addrs { + struct ip_addr our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* Initialize the PPP subsystem. */ +void pppInit(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum pppAuthType { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); + +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int pppOverSerialOpen(sio_fd_t fd, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); + +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx); + +/* for source code compatibility */ +#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int pppClose(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void pppSigHUP(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int pppIOCtl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_int pppMTU(int pd); + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +void pppMainWakeup(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, int, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, int, int); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp_oe.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp_oe.c new file mode 100644 index 0000000..5a8a45c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1227 @@ +/***************************************************************************** +* ppp_oe.c - 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. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "lwip/sys.h" + +#include "netif/ppp_oe.h" +#include "netif/etharp.h" + +#include +#include + +/** @todo Replace this part with a simple list like other lwIP lists */ +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List declarations. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#endif /* !_SYS_QUEUE_H_ */ + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (VAL) / 256; \ + *(PTR)++ = (VAL) % 256 + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +struct pppoe_softc { + LIST_ENTRY(pppoe_softc) sc_list; + 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 */ + + char *sc_service_name; /* if != NULL: requested name of service */ + char *sc_concentrator_name; /* if != NULL: requested concentrator id */ + u8_t *sc_ac_cookie; /* 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 */ +}; + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +static LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; + +int pppoe_hdrlen; + +void +pppoe_init(void) +{ + pppoe_hdrlen = sizeof(struct eth_hdr) + PPPOE_HEADERLEN; + LIST_INIT(&pppoe_softc_list); +} + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = mem_malloc(sizeof(struct pppoe_softc)); + if(!sc) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc * sc; + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + tcpip_untimeout(pppoe_timeout, sc); + LIST_REMOVE(sc, sc_list); + + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + mem_free(sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (LIST_EMPTY(&pppoe_softc_list)) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG((LOG_DEBUG, "pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(void *arg) +{ + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + char *error; + u8_t *ac_cookie; + size_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + error = NULL; + if (errortag && len) { + error = mem_malloc(len+1); + if (error) { + strncpy(error, (char*)pb->payload + off + sizeof(pt), len); + error[len-1] = '\0'; + } + } + if (error) { + printf("%s: %s: %s\n", devname, err_msg, error); + mem_free(error); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; + #endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: + #ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + #else + /* ignore, we are no access concentrator */ + goto done; + #endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + } + sc->sc_ac_cookie = mem_malloc(ac_cookie_len); + if (sc->sc_ac_cookie == NULL) { + goto done; + } + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + tcpip_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + tcpip_timeout (100, pppoe_linkstatus_up, sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%04x) session = 0x%04x\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + ph->code, session); + } else { + printf("pppoe: unknown code (0x%04x) session = 0x%04x\n", ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG((LOG_DEBUG, "pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len, l1 = 0, l2 = 0; /* XXX: gcc */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG((LOG_ERR, "ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ + if (sc->sc_service_name != NULL) { + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + tcpip_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + tcpip_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + tcpip_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = htons(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG((LOG_ERR, "pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + tcpip_untimeout(pppoe_timeout, sc); + PPPDEBUG((LOG_DEBUG, "pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + if (sc->sc_ac_cookie) { + mem_free(sc->sc_ac_cookie); + sc->sc_ac_cookie = NULL; + } + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pppdebug.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pppdebug.h new file mode 100644 index 0000000..6253863 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/pppdebug.h @@ -0,0 +1,86 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* 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 (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/************************ +*** PUBLIC DATA TYPES *** +************************/ +/* Trace levels. */ +typedef enum { +LOG_CRITICAL = 0, +LOG_ERR = 1, +LOG_NOTICE = 2, +LOG_WARNING = 3, +LOG_INFO = 5, +LOG_DETAIL = 6, +LOG_DEBUG = 7 +} LogCodes; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * ppp_trace - a form of printf to send tracing information to stderr + */ +void ppp_trace(int level, const char *format,...); + +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG + +#define AUTHDEBUG(a) ppp_trace a +#define IPCPDEBUG(a) ppp_trace a +#define UPAPDEBUG(a) ppp_trace a +#define LCPDEBUG(a) ppp_trace a +#define FSMDEBUG(a) ppp_trace a +#define CHAPDEBUG(a) ppp_trace a +#define PPPDEBUG(a) ppp_trace a + +#else /* PPP_DEBUG */ + +#define AUTHDEBUG(a) +#define IPCPDEBUG(a) +#define UPAPDEBUG(a) +#define LCPDEBUG(a) +#define FSMDEBUG(a) +#define CHAPDEBUG(a) +#define PPPDEBUG(a) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.c new file mode 100644 index 0000000..83c4174 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.c @@ -0,0 +1,249 @@ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "md5.h" +#include "randm.h" + +#include "ppp.h" +#include "pppdebug.h" + +#include + +#if MD5_SUPPORT /* this module depends on MD5 */ +#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ +static long randCount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Since this is to be called on power up, we don't have much + * system randomess to work with. Here all we use is the + * real-time clock. We'll accumulate more randomness as soon + * as things start happening. + */ +void +avRandomInit() +{ + avChurnRand(NULL, 0); +} + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +void +avChurnRand(char *randData, u32_t randLen) +{ + MD5_CTX md5; + + /* ppp_trace(LOG_INFO, "churnRand: %u@%P\n", randLen, randData); */ + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + if (randData) { + MD5Update(&md5, (u_char *)randData, randLen); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sysData; + + /* Load sysData fields here. */ + MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); + } + MD5Final((u_char *)randPool, &md5); +/* ppp_trace(LOG_INFO, "churnRand: -> 0\n"); */ +} + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Note: It's important that there be sufficient randomness in randPool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call churnRand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * randCount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void +avGenRand(char *buf, u32_t bufLen) +{ + MD5_CTX md5; + u_char tmp[16]; + u32_t n; + + while (bufLen > 0) { + n = LWIP_MIN(bufLen, RANDPOOLSZ); + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); + MD5Final(tmp, &md5); + randCount++; + MEMCPY(buf, tmp, n); + buf += n; + bufLen -= n; + } +} + +/* + * Return a new random number. + */ +u32_t +avRandom() +{ + u32_t newRand; + + avGenRand((char *)&newRand, sizeof(newRand)); + + return newRand; +} + +#else /* MD5_SUPPORT */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int avRandomized = 0; /* Set when truely randomized. */ +static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void +avRandomInit() +{ +#if 0 + /* Get a pointer into the last 4 bytes of clockBuf. */ + u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + + /* + * Initialize our seed using the real-time clock, the idle + * counter, the millisecond timer, and the hardware timer + * tick counter. The real-time clock and the hardware + * tick counter are the best sources of randomness but + * since the tick counter is only 16 bit (and truncated + * at that), the idle counter and millisecond timer + * (which may be small values) are added to help + * randomize the lower 16 bits of the seed. + */ + readClk(); + avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr + + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; +#else + avRandomSeed += sys_jiffies(); /* XXX */ +#endif + + /* Initialize the Borland random number generator. */ + srand((unsigned)avRandomSeed); +} + +/* + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void +avRandomize(void) +{ + static u32_t last_jiffies; + + if (!avRandomized) { + avRandomized = !0; + avRandomInit(); + /* The initialization function also updates the seed. */ + } else { + /* avRandomSeed += (avRandomSeed << 16) + TM1; */ + avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t +avRandom() +{ + return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); +} + +#endif /* MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.h new file mode 100644 index 0000000..a0984b0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/randm.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#ifndef RANDM_H +#define RANDM_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * Initialize the random number generator. + */ +void avRandomInit(void); + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + */ +void avChurnRand(char *randData, u32_t randLen); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +#if MD5_SUPPORT +#define avRandomize() avChurnRand(NULL, 0) +#else /* MD5_SUPPORT */ +void avRandomize(void); +#endif /* MD5_SUPPORT */ + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void avGenRand(char *buf, u32_t bufLen); + +/* + * Return a new random number. + */ +u32_t avRandom(void); + + +#endif /* RANDM_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.c new file mode 100644 index 0000000..694b702 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.c @@ -0,0 +1,660 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "pppdebug.h" + +#include "vj.h" + +#include + +#if VJ_SUPPORT + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +#if defined(NO_CHAR_BITFIELDS) +#define getip_hl(base) ((base).ip_hl_v&0xf) +#define getth_off(base) (((base).th_x2_off&0xf0)>>4) +#else +#define getip_hl(base) ((base).ip_hl) +#define getth_off(base) ((base).th_off) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_int i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (n); \ + cp[0] = (n) >> 8; \ + cp += 2; \ + } else { \ + *cp++ = (n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u_short tmp = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip *ip = (struct ip *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = getip_hl(*ip); + register struct tcphdr *oth; + register struct tcphdr *th; + register u_short deltaS, deltaA; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (ip->ip_p != IPPROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((ip->ip_off & htons(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcphdr *)&((long *)ip)[hlen]; + if ((th->th_flags & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr + || ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr + || *(long *)th != ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr + && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr + && *(long *)th == ((long *)&cs->cs_ip)[getip_hl(cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcphdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += getth_off(*th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG((LOG_INFO, "vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || getth_off(*th) != getth_off(*oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (getth_off(*th) > 5 && BCMP(th + 1, oth + 1, (getth_off(*th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->th_flags & TCP_URG) { + deltaS = ntohs(th->th_urp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->th_urp != oth->th_urp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->th_win) - ntohs(oth->th_win))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->th_ack) - ntohl(oth->th_ack)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->th_seq) - ntohl(oth->th_seq)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (ip->ip_len != cs->cs_ip.ip_len && + ntohs(cs->cs_ip.ip_len) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u_short)(ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id)); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (th->th_flags & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->th_sum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = changes | NEW_C; + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = changes; + } + *cp++ = deltaA >> 8; + *cp++ = deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + ip->ip_p = cs->cs_id; + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip *ip; + + ip = (struct ip *)nb->payload; + hlen = getip_hl(*ip) << 2; + if (ip->ip_p >= MAX_SLOTS + || hlen + sizeof(struct tcphdr) > nb->len + || (hlen += getth_off(*((struct tcphdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG((LOG_INFO, "vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + ip->ip_p, hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = ip->ip_p]; + comp->flags &=~ VJF_TOSS; + ip->ip_p = IPPROTO_TCP; + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcphdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = getip_hl(cs->cs_ip) << 2; + th = (struct tcphdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->th_sum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + th->th_flags |= TCP_PSH; + } else { + th->th_flags &=~ TCP_PSH; + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_ack) + i; + th->th_ack = htonl(tmp); + tmp = ntohl(th->th_seq) + i; + th->th_seq = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len) - cs->cs_hlen; + th->th_seq = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + th->th_flags |= TCP_URG; + DECODEU(th->th_urp); + } else { + th->th_flags &=~ TCP_URG; + } + if (changes & NEW_W) { + DECODES(th->th_win); + } + if (changes & NEW_A) { + DECODEL(th->th_ack); + } + if (changes & NEW_S) { + DECODEL(th->th_seq); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip.ip_id); + } else { + cs->cs_ip.ip_id = ntohs(cs->cs_ip.ip_id) + 1; + cs->cs_ip.ip_id = htons(cs->cs_ip.ip_id); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG((LOG_INFO, "vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + cs->cs_ip.ip_len = htons(tmp); +#else + cs->cs_ip.ip_len = htons(n0->tot_len - vjlen + cs->cs_hlen); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + cs->cs_ip.ip_sum = 0; + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + cs->cs_ip.ip_sum = (u_short)(~tmp); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG((LOG_WARNING, "vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* VJ_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.h new file mode 100644 index 0000000..b9617da --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vj.h @@ -0,0 +1,155 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef VJ_H +#define VJ_H + +#include "vjbsdhdr.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vjbsdhdr.h b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vjbsdhdr.h new file mode 100644 index 0000000..f462676 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/ppp/vjbsdhdr.h @@ -0,0 +1,75 @@ +#ifndef VJBSDHDR_H +#define VJBSDHDR_H + +#include "lwip/tcp.h" + +/* + * Structure of an internet header, naked of options. + * + * We declare ip_len and ip_off to be short, rather than u_short + * pragmatically since otherwise unsigned comparisons can result + * against negative integers quite easily, and fail in subtle ways. + */ +PACK_STRUCT_BEGIN +struct ip +{ +#if defined(NO_CHAR_BITFIELDS) + u_char ip_hl_v; /* bug in GCC for mips means the bitfield stuff will sometimes break - so we use a char for both and get round it with macro's instead... */ +#else +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned ip_hl:4, /* header length */ + ip_v :4; /* version */ +#elif BYTE_ORDER == BIG_ENDIAN + unsigned ip_v :4, /* version */ + ip_hl:4; /* header length */ +#else + COMPLAIN - NO BYTE ORDER SELECTED! +#endif +#endif + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; +PACK_STRUCT_END + +typedef u32_t tcp_seq; + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +PACK_STRUCT_BEGIN +struct tcphdr +{ + u_short th_sport; /* source port */ + u_short th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#if defined(NO_CHAR_BITFIELDS) + u_char th_x2_off; +#else +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned th_x2 :4, /* (unused) */ + th_off:4; /* data offset */ +#endif +#if BYTE_ORDER == BIG_ENDIAN + unsigned th_off:4, /* data offset */ + th_x2 :4; /* (unused) */ +#endif +#endif + u_char th_flags; + u_short th_win; /* window */ + u_short th_sum; /* checksum */ + u_short th_urp; /* urgent pointer */ +}; +PACK_STRUCT_END + +#endif /* VJBSDHDR_H */ diff --git a/component/common/network/lwip/lwip_v1.3.2/src/netif/slipif.c b/component/common/network/lwip/lwip_v1.3.2/src/netif/slipif.c new file mode 100644 index 0000000..089d2d3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.3.2/src/netif/slipif.c @@ -0,0 +1,367 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * 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. 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 built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" + +#define SLIP_BLOCK 1 +#define SLIP_DONTBLOCK 0 + +#define SLIP_END 0300 /* 0xC0 */ +#define SLIP_ESC 0333 /* 0xDB */ +#define SLIP_ESC_END 0334 /* 0xDC */ +#define SLIP_ESC_ESC 0335 /* 0xDD */ + +#define SLIP_MAX_SIZE 1500 + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + enum slipif_recv_state state; + u16_t i, recved; +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +err_t +slipif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_UNUSED_ARG(ipaddr); + + priv = netif->state; + + /* Send pbuf out on the serial I/O device. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + sio_send(c, priv->sd); + break; + } + } + } + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Static function for easy use of blockig or non-blocking + * sio_read + * + * @param fd serial device handle + * @param data pointer to data buffer for receiving + * @param len maximum length (in bytes) of data to receive + * @param block if 1, call sio_read; if 0, call sio_tryread + * @return return value of sio_read of sio_tryread + */ +static u32_t +slip_sio_read(sio_fd_t fd, u8_t* data, u32_t len, u8_t block) +{ + if (block) { + return sio_read(fd, data, len); + } else { + return sio_tryread(fd, data, len); + } +} + +/** + * Handle the incoming SLIP stream character by character + * + * Poll the serial layer by calling sio_read() or sio_tryread(). + * + * @param netif the lwip network interface structure for this slipif + * @param block if 1, block until data is received; if 0, return when all data + * from the buffer is received (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf * +slipif_input(struct netif *netif, u8_t block) +{ + struct slipif_priv *priv; + u8_t c; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + while (slip_sio_read(priv->sd, &c, 1, block) > 0) { + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + continue; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + continue; + } + break; + case SLIP_RECV_ESCAPE: + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + priv->state = SLIP_RECV_NORMAL; + /* FALLTHROUGH */ + } + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + break; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + } + + return NULL; +} + +#if !NO_SYS +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + struct pbuf *p; + struct netif *netif = (struct netif *)nf; + + while (1) { + p = slipif_input(netif, SLIP_BLOCK); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + p = NULL; + } + } + } +} +#endif /* !NO_SYS */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default) + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output; + netif->mtu = SLIP_MAX_SIZE; + netif->flags |= NETIF_FLAG_POINTTOPOINT; + + /* Try to open the serial port (netif->num contains the port number). */ + priv->sd = sio_open(netif->num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif + * ifSpeed: no assumption can be made without knowing more about the + * serial line! + */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, 0); + + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + struct pbuf *p; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + while ((p = slipif_input(netif, SLIP_DONTBLOCK)) != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/component/common/network/lwip/lwip_v1.4.1/CHANGELOG b/component/common/network/lwip/lwip_v1.4.1/CHANGELOG new file mode 100644 index 0000000..af68299 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/CHANGELOG @@ -0,0 +1,3349 @@ +HISTORY + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ New features: + + + ++ Bugfixes: + + + + +(STABLE-1.4.1) + + ++ New features: + + 2012-03-25: Simon Goldschmidt (idea by Mason) + * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h + which are a simple wrapper to the correct lwIP include files. + + 2012-01-16: Simon Goldschmidt + * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP + + 2011-12-17: Simon Goldschmidt + * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW) + (fixes bug #35061) + + 2011-09-27: Simon Goldschmidt + * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989) + (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h) + + 2011-09-21: Simon Goldschmidt + * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on + send (TCP only, bug #33820) + + 2011-09-21: Simon Goldschmidt + * init.c: Converted runtime-sanity-checks into compile-time checks that can + be disabled (since runtime checks can often not be seen on embedded targets) + + 2011-09-11: Simon Goldschmidt + * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file + to get a clear separation of which functions an application or port may use + (task #11281) + + 2011-09-11: Simon Goldschmidt + * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize + initial local TCP/UDP ports (so that different port ranges are used after + a reboot; bug #33818; this one added tcp_init/udp_init functions again) + + 2011-09-03: Simon Goldschmidt + * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302) + + 2011-08-24: Simon Goldschmidt + * opt.h, netif.h/.c: added netif remove callback (bug #32397) + + 2011-07-26: Simon Goldschmidt + * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter + function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN) + + 2011-07-21: Simon Goldschmidt (patch by hanhui) + * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour: + Added pbuf flags to mark incoming packets as link-layer broadcast/multicast. + Also added code to allow ip_forward() to forward non-broadcast packets to + the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1). + + 2011-06-26: Simon Goldschmidt (patch by Cameron Gutman) + * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that + pcb->state != LISTEN + + 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage) + * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static + memory message + + + ++ Bugfixes: + + 2012-09-26: Simon Goldschmidt + * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7843 Fix corner case with dhcp timeouts + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet + + 2012-08-22: Simon Goldschmidt + * memp.c: fixed bug #37166: memp_sanity check loops itself + + 2012-05-08: Simon Goldschmidt + * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was + a debug-check issue only) + + 2012-03-27: Simon Goldschmidt + * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c + + 2012-03-27: Simon Goldschmidt (patch by Mason) + * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the + send MSS + + 2012-03-22: Simon Goldschmidt + * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward + + 2012-03-20: Simon Goldschmidt (patch by Mason) + * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list + + 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic) + * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian, + possible bug on little endian system + + 2012-02-23: Simon Goldschmidt + * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt + * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP() + (bug #35541: PPP Memory Leak) + + 2012-02-16: Simon Goldschmidt + * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage) + * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed + + 2012-02-15: Simon Goldschmidt + * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with + MEMP_MEM_MALLOC==1 + + 2012-02-12: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on + MSS > pcb->snd_wnd (by not creating segments bigger than half the window) + + 2012-02-11: Simon Goldschmidt + * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait + queue while closing + + 2012-01-22: Simon Goldschmidt + * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR) + + 2012-01-21: Simon Goldschmidt + * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb + + 2012-01-20: Simon Goldschmidt + * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths + + 2012-01-20: Simon Goldschmidt + * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy + + 2011-11-25: Simon Goldschmidt + * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt + tcp_active_pcbs in some cases + + 2011-11-23: Simon Goldschmidt + * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with + '#ifndef sys_msleep' + + 2011-11-22: Simon Goldschmidt + * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when + netif is brought down + + 2011-10-28: Simon Goldschmidt + * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks + + 2011-10-23: Simon Goldschmidt + * mem.c: fixed bug #34429: possible memory corruption with + LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1 + + 2011-10-18: Simon Goldschmidt + * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard + error value + + 2011-10-18: Simon Goldschmidt + * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small + windows (bug #34176 select after non-blocking send times out) + + 2011-10-18: Simon Goldschmidt + * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't + consider netif->mtu, causes slow network + + 2011-10-18: Simon Goldschmidt + * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code + + 2011-10-18: Simon Goldschmidt + * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS + + 2011-10-17: Simon Goldschmidt + * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api + + 2011-10-13: Simon Goldschmidt + * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no + zero window is received) by starting the persist timer when a zero window is + received, not when we have more data queued for sending than fits into the + window + + 2011-10-13: Simon Goldschmidt + * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex + + 2011-10-13: Simon Goldschmidt + * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is + used and not all protocols are enabled + + 2011-10-12: Simon Goldschmidt + * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4 + + 2011-10-09: Simon Goldschmidt + * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect + byte value when pcb->unacked != NULL + + 2011-10-09: Simon Goldschmidt + * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong + + 2011-09-27: Simon Goldschmidt + * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places... + + 2011-09-27: Simon Goldschmidt + * tcp_in.c: fixed bug #28288: Data after FIN in oos queue + + 2011-09-27: Simon Goldschmidt + * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf + + 2011-09-24: Simon Goldschmidt + * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1 + + 2011-09-23: Simon Goldschmidt + * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for + the last packet including FIN can lose data + + 2011-09-22: Simon Goldschmidt + * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into + account + + 2011-09-21: Simon Goldschmidt + * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks + in init.c + + 2011-09-20: Simon Goldschmidt + * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts) + + 2011-09-11: Simon Goldschmidt + * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs + (bug #34019) + + 2011-09-09: Simon Goldschmidt + * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if + udp port matches + + 2011-09-03: Simon Goldschmidt + * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet + is aggregated and sent to application + + 2011-09-01: Simon Goldschmidt + * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared + to other options + + 2011-09-01: Simon Goldschmidt + * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno + + 2011-08-24: Simon Goldschmidt + * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling + accept() on UDP connections + + 2011-08-24: Simon Goldschmidt + * sockets.h: fixed bug #34057 socklen_t should be a typedef + + 2011-08-24: Simon Goldschmidt + * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo) + + 2011-08-24: Simon Goldschmidt + * dhcp.c: fixed bug #34122 dhcp: hostname can overflow + + 2011-08-24: Simon Goldschmidt + * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr + + 2011-08-22: Simon Goldschmidt + * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This + merely prevents nagle from not transmitting fast after closing.) + + 2011-07-22: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns + always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now + lwip_send() sends as much as possible for non-blocking sockets + + 2011-07-22: Simon Goldschmidt + * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented + for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level. + + 2011-07-21: Simon Goldschmidt + * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by + sending an ARP request when an ARP entry is used in the last minute before + it would time out. + + 2011-07-04: Simon Goldschmidt + * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0. + + 2011-06-26: Simon Goldschmidt + * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by + updating its documentation only. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an + unaligned pointer. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1" + + 2011-05-25: Simon Goldschmidt + * tcp.c: fixed bug #33398 (pointless conversion when checking TCP port range) + + + +(STABLE-1.4.0) + + ++ New features: + + 2011-03-27: Simon Goldschmidt + * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and + calculate it in tcp_zero_window_probe (the only place where it was used). + + 2010-11-21: Simon Goldschmidt + * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif + (fixes bug #31525). + + 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage) + * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for + IP_MULTICAST_LOOP at socket- and raw-API level. + + 2010-06-16: Simon Goldschmidt + * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow + link-layer-addressed UDP traffic to be received while a netif is down (just + like DHCP during configuration) + + 2010-05-22: Simon Goldschmidt + * many many files: bug #27352: removed packing from ip_addr_t, the packed + version is now only used in protocol headers. Added global storage for + current src/dest IP address while in input functions. + + 2010-05-16: Simon Goldschmidt + * def.h: task #10391: Add preprocessor-macros for compile-time htonl + calculation (and use them throughout the stack where applicable) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool + instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own + MEMP pool instead of the heap + + 2010-05-13: Simon Goldschmidt + * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added + new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast + packets to more than one pcb. + + 2010-05-02: Simon Goldschmidt + * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending + UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-04-30: Simon Goldschmidt + * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that + take a precalculated checksum, added pbuf_fill_chksum() to copy data + into a pbuf and at the same time calculating the checksum for that data + + 2010-04-29: Simon Goldschmidt + * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying + 2-byte-aligned IP addresses and MAC addresses + + 2010-04-28: Patch by Bill Auerbach + * ip.c: Inline generating IP checksum to save a function call + + 2010-04-14: Simon Goldschmidt + * tcpip.h/.c, timers.c: Added an overridable define to get informed when the + tcpip_thread processes messages or timeouts to implement a watchdog. + + 2010-03-28: Simon Goldschmidt + * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing + fragment if LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-03-27: Simon Goldschmidt + * etharp.c: Speedup TX by moving code from find_entry to etharp_output/ + etharp_query to prevent unnecessary function calls (inspired by + patch #7135). + + 2010-03-20: Simon Goldschmidt + * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code + since the linker cannot do this automatically to save space. + + 2010-03-20: Simon Goldschmidt + * opt.h, etharp.c/.h: Added support for static ARP table entries + + 2010-03-14: Simon Goldschmidt + * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum + when creating TCP segments, not when (re-)transmitting them. + + 2010-03-07: Simon Goldschmidt + * sockets.c: bug #28775 (select/event_callback: only check select_cb_list + on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code. + This should speed up receiving data on sockets as the select code in + event_callback is only executed when select is waiting. + + 2010-03-06: Simon Goldschmidt + * tcp_out.c: task #7013 (Create option to have all packets delivered to + netif->output in one piece): Always copy to try to create single pbufs + in tcp_write. + + 2010-03-06: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv + by not allocating a netbuf): added function netconn_recv_tcp_pbuf() + for tcp netconns to receive pbufs, not netbufs; use that function + for tcp sockets. + + 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt + * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040: + Work on tcp_enqueue: Don't waste memory when chaining segments, + added option TCP_OVERSIZE to prevent creating many small pbufs when + calling tcp_write with many small blocks of data. Instead, pbufs are + allocated larger than needed and the space is used for later calls to + tcp_write. + + 2010-02-21: Simon Goldschmidt + * stats.c/.h: Added const char* name to mem- and memp-stats for easier + debugging. + + 2010-02-21: Simon Goldschmidt + * tcp.h (and usages), added tcp_impl.h: Splitted API and internal + implementation of tcp to make API usage cleare to application programmers + + 2010-02-14: Simon Goldschmidt/Stephane Lesage + * ip_addr.h: Improved some defines working on ip addresses, added faster + macro to copy addresses that cannot be NULL + + 2010-02-13: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non- + blocking send operation) + + 2010-02-12: Simon Goldschmidt + * sockets.c/.h: Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + 2010-02-12: Simon Goldschmidt + * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated + memory): added autoip_set_struct() and dhcp_set_struct() to let autoip + and dhcp work with user-allocated structs instead of callin mem_malloc + + 2010-02-12: Simon Goldschmidt/Jeff Barber + * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has + SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT + + 2010-02-12: Simon Goldschmidt + * sys layer: task #10139 (Prefer statically allocated memory): converted + mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t; + converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX + to let sys.h use binary semaphores instead of mutexes - as before) + + 2010-02-09: Simon Goldschmidt (Simon Kallweit) + * timers.c/.h: Added function sys_restart_timeouts() from patch #7085 + (Restart system timeout handling) + + 2010-02-09: Simon Goldschmidt + * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into + netif.c) - loopif does not have to be created by the port any more, + just define LWIP_HAVE_LOOPIF to 1. + + 2010-02-08: Simon Goldschmidt + * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa + inet_ntoa_r/ipaddr_ntoa_r + + 2010-02-08: Simon Goldschmidt + * netif.h: Added netif_s/get_igmp_mac_filter() macros + + 2010-02-05: Simon Goldschmidt + * netif.h: Added function-like macros to get/set the hostname on a netif + + 2010-02-04: Simon Goldschmidt + * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to + make changing the actual implementation behind the typedef easier. + + 2010-02-01: Simon Goldschmidt + * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool + for allocating memory when getaddrinfo() is called. + + 2010-01-31: Simon Goldschmidt + * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse + them once instead of parsing for every option. This also removes + the need for mem_malloc from dhcp_recv and makes it possible to + correctly retrieve the BOOTP file. + + 2010-01-30: simon Goldschmidt + * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect + the sockets array. + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, api_msg.c, sockets.c: Added except set support in select + (patch #6860) + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c: + Add non-blocking support for connect (partly from patch #6860), + plus many cleanups in socket & netconn API. + + 2010-01-27: Simon Goldschmidt + * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding + to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 + + 2010-01-26: Simon Goldschmidt + * snmp: Use memp pools for snmp instead of the heap; added 4 new pools. + + 2010-01-14: Simon Goldschmidt + * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback + by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback() + + 2010-01-13: Simon Goldschmidt + * mem.c: The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + (patch #6966 and bug #26133) + + 2010-01-10: Simon Goldschmidt (Bill Auerbach) + * opt.h, memp.c: patch #6822 (Add option to place memory pools in + separate arrays) + + 2010-01-10: Simon Goldschmidt + * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define + LWIP_RAND() for lwip-wide randomization (to be defined in cc.h) + + 2009-12-31: Simon Goldschmidt + * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h + added timers.c/.h: Separated timer implementation from semaphore/mbox + implementation, moved timer implementation to timers.c/.h, timers are + now only called from tcpip_thread or by explicitly checking them. + (TASK#7235) + + 2009-12-27: Simon Goldschmidt + * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option + LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE) + + + ++ Bugfixes: + + 2011-04-20: Simon Goldschmidt + * sys_arch.txt: sys_arch_timeouts() is not needed any more. + + 2011-04-13: Simon Goldschmidt + * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by + using ports in the IANA private/dynamic range (49152 through 65535). + + 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl: + * etharp.h/.c: Fixed broken VLAN support. + + 2011-03-27: Simon Goldschmidt + * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp + pcbs) by checking if the pcb was bound (local_port != 0). + + 2011-03-27: Simon Goldschmidt + * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice) + + 2011-03-27: Simon Goldschmidt + * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and + raw pcbs with LWIP_TCPIP_CORE_LOCKING==1. + + 2011-03-27: Simon Goldschmidt + * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route + is present never times out) by starting retransmission timer before checking + route. + + 2011-03-22: Simon Goldschmidt + * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only + calling sio_read_abort() if the file descriptor is valid. + + 2011-03-14: Simon Goldschmidt + * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect + more than once can render a socket useless) since it mainly involves changing + "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal. + + 2011-03-13: Simon Goldschmidt + * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing + err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN: + use EALRADY instead of -1 + + 2011-03-13: Simon Goldschmidt + * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the + connection has been aborted by err_tcp (since this is not a normal closing + procedure). + + 2011-03-13: Simon Goldschmidt + * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind + with pcb->state != CLOSED + + 2011-02-17: Simon Goldschmidt + * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in + documentation + + 2011-02-17: Simon Goldschmidt + * many files: Added missing U/UL modifiers to fix 16-bit-arch portability. + + 2011-01-24: Simon Goldschmidt + * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems + + 2010-12-02: Simon Goldschmidt + * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal. + + 2010-11-23: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for + LWIP_SO_RCVBUF and ioctl/FIONREAD. + + 2010-11-23: Simon Goldschmidt + * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at + least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet. + + 2010-11-23: Simon Goldschmidt + * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after + refusing 'refused_data' again. + + 2010-11-22: Simon Goldschmidt + * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS + after a successful nonblocking connection. + + 2010-11-22: Simon Goldschmidt + * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr + must be sent link-local + + 2010-11-22: Simon Goldschmidt + * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for + LWIP_TIMERS==0 + + 2010-11-20: Simon Goldschmidt + * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number + + 2010-11-20: Simon Goldschmidt + * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to + resemble other stacks. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else + no-copy TCP writes will never succeed. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does + not match documentation: return ERR_ARG instead of ERR_VAL if not + initialized or wrong argument. + + 2010-10-20: Simon Goldschmidt + * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16 + + 2010-10-05: Simon Goldschmidt + * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when + replugging the network cable after an AutoIP address was assigned. + + 2010-08-10: Simon Goldschmidt + * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs + + 2010-08-03: Simon Goldschmidt + * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625) + + 2010-08-01: Simon Goldschmidt (patch by Greg Renda) + * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big + endian architectures) + + 2010-07-28: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP + disabled. + + 2010-07-27: Simon Goldschmidt + * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no + harm but never did anything + + 2010-07-21: Simon Goldschmidt + * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not + add IP options) + + 2010-07-16: Kieran Mansley + * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator + + 2010-07-10: Simon Goldschmidt + * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options + + 2010-06-30: Simon Goldschmidt + * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in + netconn_delete) + + 2010-06-28: Kieran Mansley + * timers.c remove unportable printing of C function pointers + + 2010-06-24: Simon Goldschmidt + * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag + NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading + + 2010-06-24: Simon Goldschmidt + * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly + implemented shutdown at socket level. + + 2010-06-21: Simon Goldschmidt + * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has + problems with zero-copy DMA MACs) by adding custom pbufs and implementing + custom pbufs that reference other (original) pbufs. Additionally set + IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side. + + 2010-06-15: Simon Goldschmidt + * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses + + 2010-06-14: Simon Goldschmidt + * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses + + 2010-06-12: Simon Goldschmidt + * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop + state + + 2010-05-17: Simon Goldschmidt + * netdb.c: Correctly NULL-terminate h_addr_list + + 2010-05-16: Simon Goldschmidt + * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent + "symbol already defined" i.e. when linking to winsock + + 2010-05-05: Simon Goldschmidt + * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may + overflow) + + 2010-04-21: Simon Goldschmidt + * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening + connection) + + 2010-03-28: Luca Ceresoli + * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers + + 2010-03-27: Luca Ceresoli + * mib2.c: patch #7130: remove meaningless const qualifiers + + 2010-03-26: Simon Goldschmidt + * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too + + 2010-03-26: Simon Goldschmidt + * various files: Fixed compiling with different options disabled (TCP/UDP), + triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled + + 2010-03-25: Simon Goldschmidt + * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly + + 2010-03-25: Simon Goldschmidt + * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side + overrunning our rcv_wnd in ooseq case. + + 2010-03-22: Simon Goldschmidt + * tcp.c: tcp_listen() did not copy the pcb's prio. + + 2010-03-19: Simon Goldschmidt + * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set + + 2010-03-14: Simon Goldschmidt + * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports + where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h + and basing PBUF_LINK_HLEN on it. + + 2010-03-08: Simon Goldschmidt + * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections + when assiging routable address): when checking incoming packets and + aborting existing connection on address change, filter out link-local + addresses. + + 2010-03-06: Simon Goldschmidt + * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING + + 2010-03-06: Simon Goldschmidt + * ipv4/ip.c: Don't try to forward link-local addresses + + 2010-03-06: Simon Goldschmidt + * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal- + addresses to gw + + 2010-03-05: Simon Goldschmidt + * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type + and state. + + 2010-03-05: Simon Goldschmidt + * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split + into multiple calls to tcp_write. + + 2010-02-21: Simon Goldschmidt + * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep + the implementation of DNS_USES_STATIC_BUF==1) + + 2010-02-20: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement + close() vs. shutdown(). Now the application does not get any more + recv callbacks after calling tcp_close(). Added tcp_shutdown(). + + 2010-02-19: Simon Goldschmidt + * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent + confusion with realloc() + + 2010-02-15: Simon Goldschmidt/Stephane Lesage + * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK + (fixes bug #28899) + + 2010-02-14: Simon Goldschmidt + * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with + LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and + admin-status of a netif are up + + 2010-02-14: Simon Goldschmidt + * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet + reception and is not really necessary + + 2010-02-14: Simon Goldschmidt + * etharp.c/.h: Fixed ARP input processing: only add a new entry if a + request was directed as us (RFC 826, Packet Reception), otherwise + only update existing entries; internalized some functions + + 2010-02-14: Simon Goldschmidt + * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be + disabled on netif used for PPPoE) by adding a new netif flag + (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet + device but prevents usage of ARP (so that ethernet_input can be used + for PPPoE). + + 2010-02-12: Simon Goldschmidt + * netif.c: netif_set_link_up/down: only do something if the link state + actually changes + + 2010-02-12: Simon Goldschmidt/Stephane Lesage + * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking + connect) + + 2010-02-12: Simon Goldschmidt + * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h) + + 2010-02-09: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110 + (recv() makes receive window update for data that wasn't received by + application) + + 2010-02-09: Simon Goldschmidt/Stephane Lesage + * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out + or any netconn_recv() error) + + 2010-02-09: Simon Goldschmidt + * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets) + + 2010-02-09: Simon Goldschmidt + * netif.c: For loopback packets, adjust the stats- and snmp-counters + for the loopback netif. + + 2010-02-08: Simon Goldschmidt + * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity + since they are not used anywhere else. + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats + (patch from bug #28798) + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and + another bug when LWIP_RAND() returns zero. + + 2010-02-04: Simon Goldschmidt + * nearly every file: Use macros defined in ip_addr.h (some of them new) + to work with IP addresses (preparation for bug #27352 - Change ip_addr + from struct to typedef (u32_t) - and better code). + + 2010-01-31: Simon Goldschmidt + * netif.c: Don't call the link-callback from netif_set_up/down() since + this invalidly retriggers DHCP. + + 2010-01-29: Simon Goldschmidt + * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the + portability file inet.h and its contents from the stack: moved htonX- + functions to def.h (and the new def.c - they are not ipv4 dependent), + let inet.h depend on ip_addr.h and not the other way round. + This fixes bug #28732. + + 2010-01-28: Kieran Mansley + * tcp.c: Ensure ssthresh >= 2*MSS + + 2010-01-27: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv + callback can lead to accessing unallocated memory. As a consequence, + ERR_ABRT means the application has called tcp_abort()! + + 2010-01-25: Simon Goldschmidt + * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY + not implemented in SNMP): write-only or not-accessible are still + returned by getnext (though not by get) + + 2010-01-24: Simon Goldschmidt + * snmp: Renamed the private mib node from 'private' to 'mib_private' to + not use reserved C/C++ keywords + + 2010-01-23: Simon Goldschmidt + * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less + than 1 ms + + 2010-01-21: Simon Goldschmidt + * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called + if tcp_enqueue fails) both in raw- and netconn-API + + 2010-01-19: Simon Goldschmidt + * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp + + 2010-01-18: Iordan Neshev/Simon Goldschmidt + * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some + bugfix backports from 2.4.x. + + 2010-01-18: Simon Goldschmidt + * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong + + 2010-01-17: Simon Goldschmidt + * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c): + task #10102: "netconn: clean up conn->err threading issues" by adding + error return value to struct api_msg_msg + + 2010-01-17: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept() + to return err_t (bugs #27709 and #28087) + + 2010-01-14: Simon Goldschmidt + * ...: Use typedef for function prototypes throughout the stack. + + 2010-01-13: Simon Goldschmidt + * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive + window = 0) by correctly draining recvmbox/acceptmbox + + 2010-01-11: Simon Goldschmidt + * pap.c: Fixed bug #13315 (PPP PAP authentication can result in + erroneous callbacks) by copying the code from recent pppd + + 2010-01-10: Simon Goldschmidt + * raw.c: Fixed bug #28506 (raw_bind should filter received packets) + + 2010-01-10: Simon Goldschmidt + * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)()) + + 2010-01-08: Simon Goldschmidt + * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535) + + 2010-01-08: Simon Goldschmidt + * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string + passed to dns_local_addhost() might be volatile + + 2010-01-07: Simon Goldschmidt + * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too + + 2010-01-06: Simon Goldschmidt + * netdb.h: Fixed bug #28496: missing include guards in netdb.h + + 2009-12-31: Simon Goldschmidt + * many ppp files: Reorganised PPP source code from ucip structure to pppd + structure to easily compare our code against the pppd code (around v2.3.1) + + 2009-12-27: Simon Goldschmidt + * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted + unit test + + +(STABLE-1.3.2) + + ++ New features: + + 2009-10-27 Simon Goldschmidt/Stephan Lesage + * netifapi.c/.h: Added netifapi_netif_set_addr() + + 2009-10-07 Simon Goldschmidt/Fabian Koch + * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to + support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO) + + 2009-08-26 Simon Goldschmidt/Simon Kallweit + * slipif.c/.h: bug #26397: SLIP polling support + + 2009-08-25 Simon Goldschmidt + * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN), + New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK. + + 2009-08-25 Simon Goldschmidt + * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*) + + 2009-08-24 Jakob Stoklund Olesen + * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond + to netif_set_link_up(). + + 2009-08-23 Simon Goldschmidt + * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state + to a human-readable string. + + ++ Bugfixes: + + 2009-12-24: Kieran Mansley + * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing + (BUG#28241) + + 2009-12-06: Simon Goldschmidt + * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can + be statically allocated (like in ucip) + + 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev) + * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT + + 2009-12-03: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit + could have non-zero length + + 2009-12-02: Simon Goldschmidt + * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting + tcp_input_pcb until after calling the pcb's callbacks + + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + + 2009-11-29: Simon Goldschmidt + * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by + queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty + + 2009-11-26: Simon Goldschmidt + * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending + segment + + 2009-11-26: Simon Goldschmidt + * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle + algorithm at PCB level + + 2009-11-22: Simon Goldschmidt + * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent + + 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach) + * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when + reusing time-wait pcb + + 2009-11-20: Simon Goldschmidt (patch by Albert Bartel) + * sockets.c: Fixed bug #28062: Data received directly after accepting + does not wake up select + + 2009-11-11: Simon Goldschmidt + * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo) + + 2009-10-30: Simon Goldschmidt + * opt.h: Increased default value for TCP_MSS to 536, updated default + value for TCP_WND to 4*TCP_MSS to keep delayed ACK working. + + 2009-10-28: Kieran Mansley + * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code + to follow algorithm from TCP/IP Illustrated + + 2009-10-27: Kieran Mansley + * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK + + 2009-10-25: Simon Goldschmidt + * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if + pcb->recv is NULL to keep rcv_wnd correct) + + 2009-10-25: Simon Goldschmidt + * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state + + 2009-10-23: Simon Goldschmidt (David Empson) + * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes + + 2009-10-21: Simon Goldschmidt + * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and + trailing 1 byte len (SYN/FIN) + + 2009-10-21: Simon Goldschmidt + * tcp_out.c: Fixed bug #27315: zero window probe and FIN + + 2009-10-19: Simon Goldschmidt + * dhcp.c/.h: Minor code simplification (don't store received pbuf, change + conditional code to assert where applicable), check pbuf length before + testing for valid reply + + 2009-10-19: Simon Goldschmidt + * dhcp.c: Removed most calls to udp_connect since they aren't necessary + when using udp_sendto_if() - always stay connected to IP_ADDR_ANY. + + 2009-10-16: Simon Goldschmidt + * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop + valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is + enabled + + 2009-10-15: Simon Goldschmidt (Oleg Tyshev) + * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit + + 2009-10-15: Simon Goldschmidt + * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv() + timeout + + 2009-10-15: Simon Goldschmidt + * autoip.c: Fixed bug #27704: autoip starts with wrong address + LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead + of network byte order + + 2009-10-11 Simon Goldschmidt (Jörg Kesten) + * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments + which are not consecutive when retransmitting unacked segments + + 2009-10-09 Simon Goldschmidt + * opt.h: Fixed default values of some stats to only be enabled if used + Fixes bug #27338: sys_stats is defined when NO_SYS = 1 + + 2009-08-30 Simon Goldschmidt + * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK + function" by checking for loopback before calling ip_frag + + 2009-08-25 Simon Goldschmidt + * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0 + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27078: Possible memory leak in pppInit() + + 2009-08-23 Simon Goldschmidt + * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result + is error. + + 2009-08-23 Simon Goldschmidt + * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF + Fixed wrong parenthesis, added check in init.c + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms + + 2009-08-23 Simon Goldschmidt + * many ppp files: bug #27267: Added include to string.h where needed + + 2009-08-23 Simon Goldschmidt + * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian) + + +(STABLE-1.3.1) + + ++ New features: + + 2009-05-10 Simon Goldschmidt + * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option + LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only + one pbuf to help MACs that don't support scatter-gather DMA. + + 2009-05-09 Simon Goldschmidt + * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming + ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + + 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen + * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive + extended info about the currently received packet. + + 2009-04-27 Simon Goldschmidt + * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1 + + 2009-04-25 Simon Goldschmidt + * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next + bigger malloc pool if one is empty (only usable with MEM_USE_POOLS). + + 2009-04-21 Simon Goldschmidt + * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static + hosts table. New configuration options DNS_LOCAL_HOSTLIST and + DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined + as an external function for lookup. + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for + TCP timestamp options, off by default. Rework tcp_enqueue() to + take option flags rather than specified option data + + 2009-02-18 Simon Goldschmidt + * cc.h: Added printf formatter for size_t: SZT_F + + 2009-02-16 Simon Goldschmidt (patch by Rishi Khan) + * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast + pings + + 2009-02-12 Simon Goldschmidt + * init.h: Added LWIP_VERSION to get the current version of the stack + + 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler) + * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead + of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc + is otherwise used) + + 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach) + * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial() + is only used by UDPLITE at present, so conditionalise it. + + 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli) + * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP + "seed" address. This should reduce AUTOIP conflicts if + LWIP_AUTOIP_CREATE_SEED_ADDR is overridden. + + 2008-10-02 Jonathan Larmour and Rishi Khan + * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking + socket. + + 2008-06-30 Simon Goldschmidt + * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from + interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows + mem_free to run between mem_malloc iterations. Added illegal counter for + mem stats. + + 2008-06-27 Simon Goldschmidt + * stats.h/.c, some other files: patch #6483: stats module improvement: + Added defines to display each module's statistic individually, added stats + defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. + + 2008-06-17 Simon Goldschmidt + * err.h: patch #6459: Made err_t overridable to use a more efficient type + (define LWIP_ERR_T in cc.h) + + 2008-06-17 Simon Goldschmidt + * slipif.c: patch #6480: Added a configuration option for slipif for symmetry + to loopif + + 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) + * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly + modified version of patch # 6370: Moved loopif code to netif.c so that + loopback traffic is supported on all netifs (all local IPs). + Added option to limit loopback packets for each netifs. + + + ++ Bugfixes: + 2009-08-12 Kieran Mansley + * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when + out of window or out of order properly + + 2009-08-12 Kieran Mansley + * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1 + + 2009-07-28 Simon Goldschmidt + * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s + + 2009-07-27 Kieran Mansley + * api.h api_msg.h netdb.h sockets.h: add missing #include directives + + 2009-07-09 Kieran Mansley + * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for + recv_avail and don't increment counters until message successfully + sent to mbox + + 2009-06-25 Kieran Mansley + * api_msg.c api.h: BUG26722: initialise netconn write variables + in netconn_alloc + + 2009-06-25 Kieran Mansley + * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set + + 2009-06-25 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct + simultaneous close behaviour, and make snd_nxt have the same meaning + as in the RFCs. + + 2009-05-12 Simon Goldschmidt + * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on + arp_table / uses etharp_query" by adding etharp_gratuitous() + + 2009-05-12 Simon Goldschmidt + * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options + to the IP header (used by igmp_ip_output_if) + + 2009-05-06 Simon Goldschmidt + * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if + defined) for SWAP_BYTES_IN_WORD to speed up checksumming. + + 2009-05-05 Simon Goldschmidt + * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select() + to crash + + 2009-05-04 Simon Goldschmidt + * init.c: snmp was not initialized in lwip_init() + + 2009-05-04 Frédéric Bernon + * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled. + + 2009-05-03 Simon Goldschmidt + * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full + (and unsent->next == NULL) + + 2009-05-02 Simon Goldschmidt + * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after + 1.3.0 in CVS only) - fixes compilation of ppp_oe.c + + 2009-05-02 Simon Goldschmidt + * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields + + 2009-05-01 Simon Goldschmidt + * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets + + 2009-05-01 Simon Goldschmidt + * ppp.c: bug #24228: Memory corruption with PPP and DHCP + + 2009-04-29 Frédéric Bernon + * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the + SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception + of broadcast packets even when this option wasn't set. Port maintainers + which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h. + If you want this option also filter broadcast on recv operations, you also + have to set IP_SOF_BROADCAST_RECV=1 in opt.h. + + 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen + * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and + DHCP/AUTOIP cooperation + + 2009-04-25 Simon Goldschmidt, Oleg Tyshev + * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd + Fixed by sorting the unsent and unacked queues (segments are inserted at the + right place in tcp_output and tcp_rexmit). + + 2009-04-25 Simon Goldschmidt + * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation + when debugging": memp_sizes contained the wrong sizes (including sanity + regions); memp pools for MEM_USE_POOLS were too small + + 2009-04-24 Simon Goldschmidt, Frédéric Bernon + * inet.c: patch #6765: Fix a small problem with the last changes (incorrect + behavior, with with ip address string not ended by a '\0', a space or a + end of line) + + 2009-04-19 Simon Goldschmidt + * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails, + pcb->err is called, not pcb->connected (with an error code). + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with + no-copy-tcpwrite": deallocate option data, only concat segments with same flags + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated + in the header pbuf, not the data pbuf) + + 2009-04-18 Simon Goldschmidt + * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore() + + 2009-04-15 Simon Goldschmidt + * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp + + 2009-04-15 Simon Goldschmidt + * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in + + 2009-04-15 Simon Goldschmidt + * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function + ip_hinted_output() (for smaller code mainly) + + 2009-04-15 Simon Goldschmidt + * inet.c: patch #6765: Supporting new line characters in inet_aton() + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option; + Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu + is big enough in dhcp_start + + 2009-04-15 Simon Goldschmidt + * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak + + 2009-04-15 Simon Goldschmidt + * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY + + 2009-04-15 Simon Goldschmidt + * sockets.c: bug #26121: set_errno can be overridden + + 2009-04-09 Kieran Mansley (patch from Luca Ceresoli ) + * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when + LWIP_TCP==0 + + 2009-04-09 Kieran Mansley (patch from Roy Lee ) + * tcp.h: Patch#6802 Add do-while-clauses to those function like + macros in tcp.h + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window + updates are calculated and sent (BUG20515) + + * tcp_in.c: cope with SYN packets received during established states, + and retransmission of initial SYN. + + * tcp_out.c: set push bit correctly when tcp segments are merged + + 2009-03-27 Kieran Mansley + * tcp_out.c set window correctly on probes (correcting change made + yesterday) + + 2009-03-26 Kieran Mansley + * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping + connections where no reset required (bug #25622) + + * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes + (bug #20779) + + 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach) + * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be + too small depending on MEM_ALIGNMENT + + 2009-02-16 Simon Goldschmidt + * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard; + converted size argument of netconn_write to 'size_t' + + 2009-02-16 Simon Goldschmidt + * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host + by moving accept callback function pointer to TCP_PCB_COMMON + + 2009-02-12 Simon Goldschmidt + * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size" + option) + + 2009-02-11 Simon Goldschmidt + * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start) + + 2009-02-11 Simon Goldschmidt + * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize: + RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv()) + + 2009-02-10 Simon Goldschmidt + * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD: + Accepts_pending is decrease on a corresponding listen pcb when a connection + in state SYN_RCVD is close. + + 2009-01-28 Jonathan Larmour + * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run + out of pool pbufs. + + 2008-12-19 Simon Goldschmidt + * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 + + 2008-12-10 Tamas Somogyi, Frédéric Bernon + * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and + port uses deleted netbuf. + + 2008-10-18 Simon Goldschmidt + * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length + in tcp_parseopt + + 2008-10-15 Simon Goldschmidt + * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers + by packing the struct ip_reass_helper. + + 2008-10-03 David Woodhouse, Jonathan Larmour + * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address. + + 2008-10-02 Jonathan Larmour + * dns.c: Hard-code structure sizes, to avoid issues on some compilers where + padding is included. + + 2008-09-30 Jonathan Larmour + * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an + assertion check that addrlen isn't NULL. + + 2008-09-30 Jonathan Larmour + * tcp.c: Fix bug #24227, wrong error message in tcp_bind. + + 2008-08-26 Simon Goldschmidt + * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and + inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h + + 2008-08-14 Simon Goldschmidt + * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when + tcp_close returns != ERR_OK) + + 2008-07-08 Frédéric Bernon + * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters + in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). + + 2008-06-24 Jonathan Larmour + * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused + if tcp_seg_copy fails. + + 2008-06-17 Simon Goldschmidt + * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) + and created defines for swapping bytes and folding u32 to u16. + + 2008-05-30 Kieran Mansley + * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd + rather than rcv_ann_wnd when deciding if packets are in-window. + Contributed by + + 2008-05-30 Kieran Mansley + * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow + passing as function pointers when MEM_LIBC_MALLOC is defined. + + 2008-05-09 Jonathan Larmour + * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to + stop it being treated as a fatal error. + + 2008-04-15 Simon Goldschmidt + * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP + (flag now cleared) + + 2008-03-27 Simon Goldschmidt + * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free + from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 + in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs + or heap memory from interrupt context + + 2008-03-26 Simon Goldschmidt + * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote + host sent a zero mss as TCP option. + + +(STABLE-1.3.0) + + ++ New features: + + 2008-03-10 Jonathan Larmour + * inet_chksum.c: Allow choice of one of the sample algorithms to be + made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM. + + 2008-01-22 Frédéric Bernon + * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in + TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names. + + 2008-01-14 Frédéric Bernon + * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable + to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the + tcp_recv callback (see rawapi.txt). + + 2008-01-14 Frédéric Bernon, Marc Chaland + * ip.c: Integrate patch #6369" ip_input : checking before realloc". + + 2008-01-12 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::sem per netconn::op_completed like suggested for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-12 Frédéric Bernon + * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE, + DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues + sizes), like suggested for the task #7490 "Add return value to sys_mbox_post". + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490 + "Add return value to sys_mbox_post". tcpip_callback is always defined as + "blocking" ("block" parameter = 1). + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-05 Frédéric Bernon + * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: + Introduce changes for task #7490 "Add return value to sys_mbox_post" with some + modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which + indicate the number of pointers query by the mailbox. There is three defines + in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the + netconn::acceptmbox. Port maintainers, you can decide to just add this new + parameter in your implementation, but to ignore it to keep the previous behavior. + The new sys_mbox_trypost function return a value to know if the mailbox is + full or if the message is posted. Take a look to sys_arch.txt for more details. + This new function is used in tcpip_input (so, can be called in an interrupt + context since the function is not blocking), and in recv_udp and recv_raw. + + 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c, + tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the + "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add + documentation in the rawapi.txt file. + + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer + + 2007-12-31 Frédéric Bernon, Luca Ceresoli + * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets + in autoip". The change in etharp_raw could be removed, since all calls to + etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be + wrong in the future. + + 2007-12-30 Frédéric Bernon, Tom Evans + * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address + Filtering" reported by Tom Evans. + + 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c, + sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API + applications have to call 'tcp_accepted(pcb)' in their accept callback to + keep accepting new connections. + + 2007-12-13 Frédéric Bernon + * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result" + by err_t type. Add a new err_t code "ERR_INPROGRESS". + + 2007-12-12 Frédéric Bernon + * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles + are the one which have ram usage. + + 2007-12-05 Frédéric Bernon + * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static + set of variables (=0) or a local one (=1). In this last case, your port should + provide a function "struct hostent* sys_thread_hostent( struct hostent* h)" + which have to do a copy of "h" and return a pointer ont the "per-thread" copy. + + 2007-12-03 Simon Goldschmidt + * ip.c: ip_input: check if a packet is for inp first before checking all other + netifs on netif_list (speeds up packet receiving in most cases) + + 2007-11-30 Simon Goldschmidt + * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access + UDP: move a (connected) pcb selected for input to the front of the list of + pcbs so that it is found faster next time. Same for RAW pcbs that have eaten + a packet. + + 2007-11-28 Simon Goldschmidt + * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS + + 2007-11-25 Simon Goldschmidt + * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy + algorithm. + + 2007-11-24 Simon Goldschmidt + * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c + to the new file netdb.c; included lwip_getaddrinfo. + + 2007-11-21 Simon Goldschmidt + * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss + based on the MTU of the netif used to send. Enabled by default. Disable by + setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492. + + 2007-11-19 Frédéric Bernon + * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name + received match the name query), implement DNS_USES_STATIC_BUF (the place where + copy dns payload to parse the response), return an error if there is no place + for a new query, and fix some minor problems. + + 2007-11-16 Simon Goldschmidt + * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c + removed files: core/inet.c, core/inet6.c + Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into + inet and chksum part; changed includes in all lwIP files as appropriate + + 2007-11-16 Simon Goldschmidt + * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential + dns resolver function for netconn api (netconn_gethostbyname) and socket api + (gethostbyname/gethostbyname_r). + + 2007-11-15 Jim Pettinato, Frédéric Bernon + * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name + requests with RAW api interface. Initialization is done in lwip_init() with + build time options. DNS timer is added in tcpip_thread context. DHCP can set + DNS server ip addresses when options are received. You need to set LWIP_DNS=1 + in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get + some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo" + list with points to improve. + + 2007-11-06 Simon Goldschmidt + * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly + enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status + for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined. + + 2007-11-06 Simon Goldschmidt + * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include + core header files in api.h (ip/tcp/udp/raw.h) to hide the internal + implementation from netconn api applications. + + 2007-11-03 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP & + RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled + by default). Netconn API users can use the netconn_recv_bufsize macro to access + it. This is a first release which have to be improve for TCP. Note it used the + netconn::recv_avail which need to be more "thread-safe" (note there is already + the problem for FIONREAD with lwip_ioctl/ioctlsocket). + + 2007-11-01 Frédéric Bernon, Marc Chaland + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c: + Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api + layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api + layer. This option enable to delayed TCP PUSH flag on multiple "write" calls. + Note that previous "copy" parameter for "write" APIs is now called "apiflags". + + 2007-10-24 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than + TCP_EVENT_xxx macros to get a code more readable. It could also help to remove + some code (like we have talk in "patch #5919 : Create compile switch to remove + select code"), but it could be done later. + + 2007-10-08 Simon Goldschmidt + * many files: Changed initialization: many init functions are not needed any + more since we now rely on the compiler initializing global and static + variables to zero! + + 2007-10-06 Simon Goldschmidt + * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY + to enqueue the received pbufs so that multiple packets can be reassembled + simultaneously and no static reassembly buffer is needed. + + 2007-10-05 Simon Goldschmidt + * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so + all netifs (or ports) can use it. + + 2007-10-05 Frédéric Bernon + * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the + common function to reduce a little bit the footprint (for all functions using + only the "netif" parameter). + + 2007-10-03 Frédéric Bernon + * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down, + netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce + a little bit the footprint (for all functions using only the "netif" parameter). + + 2007-09-15 Frédéric Bernon + * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF + option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for + netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for + IP_MULTICAST_TTL and IP_MULTICAST_IF. + + 2007-09-10 Frédéric Bernon + * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles + even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime() + each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can + decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but + call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime() + or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. + This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside + snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only + when it's queried (any direct call to "sysuptime" is changed by a call to + snmp_get_sysuptime). + + 2007-09-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP, + and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags + if you want IGMP on an interface. igmp_stop() is now called inside netif_remove(). + igmp_report_groups() is now called inside netif_set_link_up() (need to have + LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait + the next query message to receive the matching multicast streams). + + 2007-09-08 Frédéric Bernon + * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains + IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change). + Use this new field to access to common pcb fields (ttl, tos, so_options, etc...). + Enable to access to these fields with LWIP_TCP=0. + + 2007-09-05 Frédéric Bernon + * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h, + ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option + LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default). + Be careful, disabling ICMP make your product non-compliant to RFC1122, but + help to reduce footprint, and to reduce "visibility" on the Internet. + + 2007-09-05 Frédéric Bernon, Bill Florac + * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list + for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new + parameters have to be provided: a task name, and a task stack size. For this + one, since it's platform dependant, you could define the best one for you in + your lwipopts.h. For port maintainers, you can just add these new parameters + in your sys_arch.c file, and but it's not mandatory, use them in your OS + specific functions. + + 2007-09-05 Frédéric Bernon + * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings + inside init.c for task #7142 "Sanity check user-configurable values". + + 2007-09-04 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by + memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the + value). It will avoid potential fragmentation problems, use a counter to know + how many times a group is used on an netif, and free it when all applications + leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity + check if LWIP_IGMP!=0). + + 2007-09-03 Frédéric Bernon + * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement". + Initialize igmp_mac_filter to NULL in netif_add (this field should be set in + the netif's "init" function). Use the "imr_interface" field (for socket layer) + and/or the "interface" field (for netconn layer), for join/leave operations. + The igmp_join/leavegroup first parameter change from a netif to an ipaddr. + This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany). + + 2007-08-30 Frédéric Bernon + * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions + from api/api_lib". Now netbuf API is independant of netconn, and can be used + with other API (application based on raw API, or future "socket2" API). Ports + maintainers just have to add src/api/netbuf.c in their makefile/projects. + + 2007-08-30 Frédéric Bernon, Jonathan Larmour + * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check + user-configurable values". + + 2007-08-29 Frédéric Bernon + * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start. + igmp_start is call inside netif_add. Now, igmp initialization is in the same + spirit than the others modules. Modify some IGMP debug traces. + + 2007-08-29 Frédéric Bernon + * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function" + Add lwip_init function to regroup all modules initializations, and to provide + a place to add code for task #7142 "Sanity check user-configurable values". + Ports maintainers should remove direct initializations calls from their code, + and add init.c in their makefiles. Note that lwip_init() function is called + inside tcpip_init, but can also be used by raw api users since all calls are + disabled when matching options are disabled. Also note that their is new options + in opt.h, you should configure in your lwipopts.h (they are enabled per default). + + 2007-08-26 Marc Boucher + * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL + since they can under certain circumstances be called with an invalid conn + pointer after the connection has been closed (and conn has been freed). + + 2007-08-25 Frédéric Bernon (Artem Migaev's Patch) + * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up". + Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set. + + 2007-08-22 Frédéric Bernon + * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK + to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release. + + 2007-08-22 Frédéric Bernon + * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT & + ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the + name is tcpip_input (we keep the name of 1.2.0 function). + + 2007-08-17 Jared Grubb + * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool + settings into new memp_std.h and optional user file lwippools.h. This adds + more dynamic mempools, and allows the user to create an arbitrary number of + mempools for mem_malloc. + + 2007-08-16 Marc Boucher + * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function; + otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely + close the connection. + + 2007-08-16 Marc Boucher + * sockets.c: lwip_accept(): check netconn_peer() error return. + + 2007-08-16 Marc Boucher + * mem.c, mem.h: Added mem_calloc(). + + 2007-08-16 Marc Boucher + * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT) + for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG + and starving other message types. + Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API + + 2007-08-16 Marc Boucher + * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf + type and flgs (later renamed to flags). + Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*. + Improved lwip_recvfrom(). TCP push now propagated. + + 2007-08-16 Marc Boucher + * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global + provided by etharp. + + 2007-08-16 Marc Boucher + * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h, + etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c: + Added PPPoE support and various PPP improvements. + + 2007-07-25 Simon Goldschmidt + * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial, + making netbuf_copy_partial use this function. + + 2007-07-25 Simon Goldschmidt + * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with + 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and + other stacks. + + 2007-07-13 Jared Grubb (integrated by Frédéric Bernon) + * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add + a link callback in the netif struct, and functions to handle it. Be carefull + for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c) + if you want to be sure to be compatible with future changes... + + 2007-06-30 Frédéric Bernon + * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions. + + 2007-06-21 Simon Goldschmidt + * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both + LWIP_AUTOIP =0 and =1 to remove redundant code. + + 2007-06-21 Simon Goldschmidt + * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option + MEM_USE_POOLS to use 4 pools with different sized elements instead of a + heap. This both prevents memory fragmentation and gives a higher speed + at the cost of more memory consumption. Turned off by default. + + 2007-06-21 Simon Goldschmidt + * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of + netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into + int to be able to send a bigger buffer than 64K with one time (mainly + used from lwip_send). + + 2007-06-21 Simon Goldschmidt + * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write + into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too. + + 2007-06-21 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in + netconn_write from api_lib.c to api_msg.c to also prevent multiple context- + changes on low memory or empty send-buffer. + + 2007-06-18 Simon Goldschmidt + * etharp.c, etharp.h: Changed etharp to use a defined hardware address length + of 6 to avoid loading netif->hwaddr_len every time (since this file is only + used for ethernet and struct eth_addr already had a defined length of 6). + + 2007-06-17 Simon Goldschmidt + * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets + to disable UDP checksum generation on transmit. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid + pointers or parameters, and let the possibility to redefined it in cc.h. Use + this macro to check "conn" parameter in api_msg.c functions. + + 2007-06-11 Simon Goldschmidt + * sockets.c, sockets.h: Added UDP lite support for sockets + + 2007-06-10 Simon Goldschmidt + * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled + by default) to switch off UDP-Lite support if not needed (reduces udp.c code + size) + + 2007-06-09 Dominik Spies (integrated by Frédéric Bernon) + * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h: + AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and + LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt + (see TODO mark in the source code). + + 2007-06-09 Simon Goldschmidt + * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for + etharp_output() to match netif->output so etharp_output() can be used + directly as netif->output to save one function call. + + 2007-06-08 Simon Goldschmidt + * netif.h, ethernetif.c, slipif.c, loopif.c: Added define + NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables, + added initialization of those to ethernetif, slipif and loopif. + + 2007-05-18 Simon Goldschmidt + * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF + (defaulting to off for now) that can be set to 0 to send fragmented + packets by passing PBUF_REFs down the stack. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP + connections, such present in patch #5959. + + 2007-05-23 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx + code in only one part... + + 2007-05-18 Simon Goldschmidt + * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp + elements to overflow. This is achieved by adding some bytes before and after + each pool element (increasing their size, of course), filling them with a + prominent value and checking them on freeing the element. + Set it to 2 to also check every element in every pool each time memp_malloc() + or memp_free() is called (slower but more helpful). + + 2007-05-10 Simon Goldschmidt + * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for + PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce + code size. + + 2007-05-11 Frédéric Bernon + * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c: + Include a function pointer instead of a table index in the message to reduce + footprint. Disable some part of lwip_send and lwip_sendto if some options are + not set (LWIP_TCP, LWIP_UDP, LWIP_RAW). + + 2007-05-10 Simon Goldschmidt + * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus + \ extern "C" {' in all header files. Now you can write your application using + the lwIP stack in C++ and simply #include the core files. Note I have left + out the netif/ppp/*h header files for now, since I don't know which files are + included by applications and which are for internal use only. + + 2007-05-09 Simon Goldschmidt + * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library + memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for + situations where some compilers might inline the copy and save a function + call. Also replaced all calls to memcpy() with calls to (S)MEMCPY(). + + 2007-05-08 Simon Goldschmidt + * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc()) + to be overriden in case the C-library malloc implementation is not protected + against concurrent access. + + 2007-05-04 Simon Goldschmidt (Atte Kojo) + * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending + multiple packets to the same host. + + 2007-05-04 Frédéric Bernon, Jonathan Larmour + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible + to corrupt remote addr/port connection state". Reduce problems "not enought memory" with + netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between + sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function. + Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct, + these fields are now renamed "addr" & "port". + + 2007-04-11 Jonathan Larmour + * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new + sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return + with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro + by the port in sys_arch.h if desired. + + 2007-04-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API + allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp + clients, using new functions from netifapi.h. Disable as default (no port change to do). + + 2007-04-05 Frédéric Bernon + * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant. + + 2007-04-04 Simon Goldschmidt + * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x) + use this for and architecture-independent form to tell the compiler you intentionally + are not using this variable. Can be overriden in cc.h. + + 2007-03-28 Frédéric Bernon + * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to + define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded + string, point on one of your's ethernetif field, or alloc a string you will free yourself). + It will be used by DHCP to register a client hostname, but can also be use when you call + snmp_set_sysname. + + 2007-03-28 Frédéric Bernon + * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to + initialize a network interface's flag with. It tell this interface is an ethernet + device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility + Support for IPv4" section 4.6) when interface is "up" with netif_set_up(). + + 2007-03-26 Frédéric Bernon, Jonathan Larmour + * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build + time if you only use PPP or SLIP. The default is enable. Note we don't have to call + etharp_init in your port's initilization sequence if you use tcpip.c, because this call + is done in tcpip_init function. + + 2007-03-22 Frédéric Bernon + * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the + new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in + your lwipopts.h. More, unused counters are not defined in the stats structs, and not + display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined + but never used. Fix msg_in.c with the correct #if test for a stat display. + + 2007-03-21 Kieran Mansley + * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). + Provides callback on netif up/down state change. + + 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c, + ip.c, netif.h, tcpip.c, opt.h: + New configuration option LWIP_IGMP to enable IGMP processing. Based on only one + filter per all network interfaces. Declare a new function in netif to enable to + control the MAC filter (to reduce lwIP traffic processing). + + 2007-03-11 Frédéric Bernon + * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can + be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this + unless you know what you're doing (default are RFC1122 compliant). Note + that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds. + + 2007-03-08 Frédéric Bernon + * tcp.h: Keepalive values can be configured at compile time, but don't change + this unless you know what you're doing (default are RFC1122 compliant). + + 2007-03-08 Frédéric Bernon + * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h: + Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO + on UDP sockets/netconn. + + 2007-03-08 Simon Goldschmidt + * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time. + + 2007-03-06 Frédéric Bernon + * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: + Implement SO_RCVTIMEO on UDP sockets/netconn. + + 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt) + * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated + on the stack and remove the API msg type from memp + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * sockets.h, sockets.c: Move socket initialization to new + lwip_socket_init() function. + NOTE: this changes the API with ports. Ports will have to be + updated to call lwip_socket_init() now. + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * api_lib.c: Use memcpy in netbuf_copy_partial. + + + ++ Bug fixes: + + 2008-03-17 Frédéric Bernon, Ed Kerekes + * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have + some problems to fill the IP header on some targets, use now the + ip.h macros to do it). + + 2008-03-13 Frédéric Bernon + * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using + (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a + TCP connection caused a crash. Note that using (lwip_)recvfrom + like this is a bit slow and that using (lwip)getpeername is the + good lwip way to do it (so, using recv is faster on tcp sockets). + + 2008-03-12 Frédéric Bernon, Jonathan Larmour + * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's + recv_raw() does not consume data", and the ping sample (with + LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom + returned the IP payload, without the IP header). + + 2008-03-04 Jonathan Larmour + * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors + and/or warnings on some systems where mem_size_t and size_t differ. + * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc. + + 2008-03-04 Kieran Mansley (contributions by others) + * Numerous small compiler error/warning fixes from contributions to + mailing list after 1.3.0 release candidate made. + + 2008-01-25 Cui hengbin (integrated by Frédéric Bernon) + * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures. + + 2008-01-15 Kieran Mansley + * tcp_out.c: BUG20511. Modify persist timer to start when we are + prevented from sending by a small send window, not just a zero + send window. + + 2008-01-09 Jonathan Larmour + * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid + conflict with Linux system headers. + + 2008-01-06 Jonathan Larmour + * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP + address entirely on receiving a DHCPNAK, and restarting discovery. + + 2007-12-21 Simon Goldschmidt + * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail + is not protected" by using new macros for interlocked access to modify/test + netconn->recv_avail. + + 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev) + * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state) + + 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling + of silly window avoidance and prevent lwIP from shrinking the window) + + 2007-12-04 Simon Goldschmidt + * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last + data packet was lost): add assert that all segment lists are empty in + tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED + state from LAST_ACK in tcp_process + + 2007-12-02 Simon Goldschmidt + * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET + If including for system-struct timeval, LWIP_TIMEVAL_PRIVATE now + has to be set to 0 in lwipopts.h + + 2007-12-02 Simon Goldschmidt + * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always + allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen + netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox. + This is a fix for thread-safety and allocates all items needed for a netconn + when the netconn is created. + + 2007-11-30 Simon Goldschmidt + * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple + netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed + to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same + port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address) + + 2007-11-27 Simon Goldschmidt + * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by + letting ip_route only use netifs that are up. + + 2007-11-27 Simon Goldschmidt + * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF + and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and + sockets block most operations once they have seen a fatal error. + + 2007-11-27 Simon Goldschmidt + * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the + netif to send as an argument (to be able to send on netifs that are down). + + 2007-11-26 Simon Goldschmidt + * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs + arrive out-of-order + + 2007-11-21 Simon Goldschmidt + * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early + Fixed the nagle algorithm; nagle now also works for all raw API applications + and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY' + + 2007-11-12 Frédéric Bernon + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most + of the netconn_peer and netconn_addr processing is done inside tcpip_thread + context in do_getaddr. + + 2007-11-10 Simon Goldschmidt + * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can + happen any time). Now the packet simply isn't enqueued when out of memory. + + 2007-11-01 Simon Goldschmidt + * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or + TCP_MSS if that is smaller) as long as no MSS option is received from the + remote host. + + 2007-11-01 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN) + is now based on TCP_MSS instead of pcb->mss (on passive open now effectively + sending our configured TCP_MSS instead of the one received). + + 2007-11-01 Simon Goldschmidt + * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was + calculated based on the configured TCP_MSS, not on the MSS option received + with SYN+ACK. + + 2007-10-09 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too + short and also was generated wrong if checksum coverage != tot_len; + receive: checksum was calculated wrong if checksum coverage != tot_len + + 2007-10-08 Simon Goldschmidt + * mem.c: lfree was not updated in mem_realloc! + + 2007-10-07 Frédéric Bernon + * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential + crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: + this change cause an API breakage for netconn_addr, since a parameter + type change. Any compiler should cause an error without any changes in + yours netconn_peer calls (so, it can't be a "silent change"). It also + reduce a little bit the footprint for socket layer (lwip_getpeername & + lwip_getsockname use now a common lwip_getaddrname function since + netconn_peer & netconn_addr have the same parameters). + + 2007-09-20 Simon Goldschmidt + * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state) + by checking tcp_tw_pcbs also + + 2007-09-19 Simon Goldschmidt + * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies) + + 2007-09-15 Mike Kleshov + * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used) + + 2007-09-06 Frédéric Bernon + * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove + it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which + already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h" + if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h. + + 2007-08-30 Frédéric Bernon + * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, + and fix some coding style. + + 2007-08-28 Frédéric Bernon + * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any + kind of packets. These packets are considered like Ethernet packets (payload + pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets + are considered like IP packets (payload pointing to iphdr). + + 2007-08-27 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error + problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state + and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT). + + 2007-08-24 Kieran Mansley + * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy + compiler (Paradigm C++) + + 2007-08-09 Frédéric Bernon, Bill Florac + * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement. + Introduce IGMP_STATS to centralize statistics management. + + 2007-08-09 Frédéric Bernon, Bill Florac + * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast + packet on a udp pcb binded on an netif's IP address, and not on "any". + + 2007-08-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement. + This is mainly on using lookup/lookfor, and some coding styles... + + 2007-07-26 Frédéric Bernon (and "thedoctor") + * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages. + + 2007-07-25 Simon Goldschmidt + * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if + tcp_output fails in tcp_close, the code in do_close_internal gets simpler + (tcp_output is called again later from tcp timers). + + 2007-07-25 Simon Goldschmidt + * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old + copy_from_pbuf, which illegally modified the given pbuf. + + 2007-07-25 Simon Goldschmidt + * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs: + changed snd_queuelen++ to snd_queuelen += pbuf_clen(p). + + 2007-07-24 Simon Goldschmidt + * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the + correct state (must be CLOSED). + + 2007-07-13 Thomas Taranowski (commited by Jared Grubb) + * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed + allocation. It now returns NULL. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in + all error cases. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed, + because current code doesn't follow rawapi.txt documentation. + + 2007-07-13 Kieran Mansley + * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in + out of sequence processing of received packets + + 2007-07-03 Simon Goldschmidt + * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an + assumption is made that this pbuf is in one piece (i.e. not chained). These + assumptions clash with the possibility of converting to fully pool-based + pbuf implementations, where PBUF_RAM pbufs might be chained. + + 2007-07-03 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems + when closing tcp netconns: removed conn->sem, less context switches when + closing, both netconn_close and netconn_delete should safely close tcp + connections. + + 2007-07-02 Simon Goldschmidt + * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c, + tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off) + to cache ARP table indices with each pcb instead of single-entry cache for + the complete stack. + + 2007-07-02 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent + warnings when assigning to smaller types. + + 2007-06-28 Simon Goldschmidt + * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing. + + 2007-06-28 Simon Goldschmidt + * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if + a segment contained chained pbufs) + + 2007-06-28 Frédéric Bernon + * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute + a "pseudo-random" value based on netif's MAC and some autoip fields. It's always + possible to define this macro in your own lwipopts.h to always use C library's + rand(). Note that autoip_create_rand_addr doesn't use this macro. + + 2007-06-28 Frédéric Bernon + * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option + LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications + in api_lib/api_msg (use pointers and not type with table, etc...) + + 2007-06-26 Simon Goldschmidt + * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload + for udp packets with no matching pcb. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match + could get udp input packets if the remote side matched. + + 2007-06-13 Simon Goldschmidt + * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get + changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0. + + 2007-06-13 Simon Goldschmidt + * api_msg.c: pcb_new sets conn->err if protocol is not implemented + -> netconn_new_..() does not allocate a new connection for unsupported + protocols. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * api_lib.c: change return expression in netconn_addr and netconn_peer, because + conn->err was reset to ERR_OK without any reasons (and error was lost)... + + 2007-06-13 Frédéric Bernon, Matthias Weisser + * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename + MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid + some macro names collision with some OS macros. + + 2007-06-11 Simon Goldschmidt + * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0, + create checksum over the complete packet. On RX, if it's < 8 (and not 0), + discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both + UDP & UDP Lite. + + 2007-06-11 Srinivas Gollakota & Oleg Tyshev + * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags" + where TCP flags wasn't initialized in tcp_keepalive. + + 2007-06-03 Simon Goldschmidt + * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function + registered, p->payload was modified without modifying p->len if sending + icmp_dest_unreach() (had no negative effect but was definitively wrong). + + 2007-06-03 Simon Goldschmidt + * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp + re-used the input pbuf even if that didn't have enough space to include the + link headers. Now the space is tested and a new pbuf is allocated for the + echo response packet if the echo request pbuf isn't big enough. + + 2007-06-01 Simon Goldschmidt + * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread. + + 2007-05-23 Frédéric Bernon + * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only + allocated by do_listen if success) and netconn_accept errors handling. In + most of api_lib functions, we replace some errors checkings like "if (conn==NULL)" + by ASSERT, except for netconn_delete. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return + an error code if it's impossible to fetch a pbuf on a TCP connection (and not + directly close the recvmbox). + + 2007-05-22 Simon Goldschmidt + * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of + bound but unconnected (and non-listening) tcp_pcbs. + + 2007-05-22 Frédéric Bernon + * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only + used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of + sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features + like "sys_timeout" in their application threads. + + 2007-05-22 Frédéric Bernon + * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see + which parameters are used by which do_xxx function, and to avoid "misusing" + parameters (patch #5938). + + 2007-05-22 Simon Goldschmidt + * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938: + changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto + is only 8 bits wide. This affects the api, as there, the protocol was + u16_t, too. + + 2007-05-18 Simon Goldschmidt + * memp.c: addition to patch #5913: smaller pointer was returned but + memp_memory was the same size -> did not save memory. + + 2007-05-16 Simon Goldschmidt + * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns + != ERR_OK. + + 2007-05-16 Simon Goldschmidt + * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same + as the one of the netif used for sending to prevent sending from old + addresses after a netif address gets changed (partly fixes bug #3168). + + 2007-05-16 Frédéric Bernon + * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work + with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in + tcpip_init) because we have to be sure that network interfaces are already + added (mac filter is updated only in igmp_init for the moment). + + 2007-05-16 Simon Goldschmidt + * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls + into sys_arch_sem_wait calls to prevent timers from running while waiting + for the heap. This fixes bug #19167. + + 2007-05-13 Simon Goldschmidt + * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines + for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from + tcp.h to sockets.h. + + 2007-05-07 Simon Goldschmidt + * mem.c: Another attempt to fix bug #17922. + + 2007-05-04 Simon Goldschmidt + * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy() + implementation so that it can be reused (don't allocate the target + pbuf inside pbuf_copy()). + + 2007-05-04 Simon Goldschmidt + * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem + to save a little RAM (next pointer of memp is not used while not in pool). + + 2007-05-03 "maq" + * sockets.c: Fix ioctl FIONREAD when some data remains from last recv. + (patch #3574). + + 2007-04-23 Simon Goldschmidt + * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results + in NULL reference for incoming TCP packets". Loopif has to be configured + (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input() + (multithreading environments, e.g. netif->input() = tcpip_input()) or + putting packets on a list that is fed to the stack by calling loopif_poll() + (single-thread / NO_SYS / polling environment where e.g. + netif->input() = ip_input). + + 2007-04-17 Jonathan Larmour + * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold + the difference between two u16_t's. + * sockets.h: FD_SETSIZE needs to match number of sockets, which is + MEMP_NUM_NETCONN in sockets.c right now. + + 2007-04-12 Jonathan Larmour + * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580). + + 2007-04-12 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission + timer is reset to fix bug#19434, with help from Oleg Tyshev. + + 2007-04-11 Simon Goldschmidt + * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than + previously thought need to be copied (everything but PBUF_ROM!). Cleaned up + pbuf.c: removed functions no needed any more (by etharp). + + 2007-04-11 Kieran Mansley + * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix + "Constant is long" warnings with 16bit compilers. Contributed by + avatar@mmlab.cse.yzu.edu.tw + + 2007-04-05 Frédéric Bernon, Jonathan Larmour + * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on + the mailbox is active". Now, the post is only done during a connect, and do_send, + do_write and do_join_leave_group don't do anything if a previous error was signaled. + + 2007-04-03 Frédéric Bernon + * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output + packets. See patch #5834. + + 2007-03-30 Frédéric Bernon + * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add + missing pcb allocations checking (in do_bind, and for each raw_new). Fix style. + + 2007-03-30 Frédéric Bernon + * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with + others environment defines (these were too "generic"). + + 2007-03-28 Frédéric Bernon + * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call + result and can cause a crash. lwip_send now check netbuf_ref result. + + 2007-03-28 Simon Goldschmidt + * sockets.c Remove "#include " from sockets.c to avoid multiple + definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is + defined. This is the way it should have been already (looking at + doc/sys_arch.txt) + + 2007-03-28 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS + + IP and TCP headers *and* physical link headers + + 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov) + * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause + to send some garbage. It is not a definitive solution, but the patch does solve + the problem for most cases. + + 2007-03-22 Frédéric Bernon + * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used). + + 2007-03-22 Frédéric Bernon + * api_lib.c: somes resources couldn't be freed if there was errors during + netconn_new_with_proto_and_callback. + + 2007-03-22 Frédéric Bernon + * ethernetif.c: update netif->input calls to check return value. In older ports, + it's a good idea to upgrade them, even if before, there could be another problem + (access to an uninitialized mailbox). + + 2007-03-21 Simon Goldschmidt + * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed + by casting to unsigned). + + 2007-03-21 Frédéric Bernon + * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from + api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a + dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call. + Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a + faster and more reliable communication between api_lib and tcpip. + + 2007-03-21 Frédéric Bernon + * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0. + + 2007-03-21 Frédéric Bernon + * api_msg.c, igmp.c, igmp.h: Fix C++ style comments + + 2007-03-21 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS + + IP and TCP headers + + 2007-03-21 Kieran Mansley + * Fix all uses of pbuf_header to check the return value. In some + cases just assert if it fails as I'm not sure how to fix them, but + this is no worse than before when they would carry on regardless + of the failure. + + 2007-03-21 Kieran Mansley + * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and + comment out missing header include in icmp.c + + 2007-03-20 Frédéric Bernon + * memp.h, stats.c: Fix stats_display function where memp_names table wasn't + synchronized with memp.h. + + 2007-03-20 Frédéric Bernon + * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input, + tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with + network interfaces. Also fix a compiler warning. + + 2007-03-20 Kieran Mansley + * udp.c: Only try and use pbuf_header() to make space for headers if + not a ROM or REF pbuf. + + 2007-03-19 Frédéric Bernon + * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg() + and api_msg_post(). + + 2007-03-19 Frédéric Bernon + * Remove unimplemented "memp_realloc" function from memp.h. + + 2007-03-11 Simon Goldschmidt + * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused + memory corruption. + + 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov) + * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251 + (missing `const' qualifier in socket functions), to get more compatible to + standard POSIX sockets. + + 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov) + * sockets.c: Add asserts inside bind, connect and sendto to check input + parameters. Remove excessive set_errno() calls after get_socket(), because + errno is set inside of get_socket(). Move last sock_set_errno() inside + lwip_close. + + 2007-03-09 Simon Goldschmidt + * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory + was allocated too small. + + 2007-03-06 Simon Goldschmidt + * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect + the stack from concurrent access. + + 2007-03-06 Frédéric Bernon, Dmitry Potapov + * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy + call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input(). + + 2007-03-06 Simon Goldschmidt + * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files + if IP_FRAG == 0 and IP_REASSEMBLY == 0 + + 2007-03-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration + option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput. + Allow to do ARP processing for incoming packets inside tcpip_thread + (protecting ARP layer against concurrent access). You can also disable + old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0. + Older ports have to use tcpip_ethinput. + + 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov) + * err.h, err.c: fixed compiler warning "initialization dircards qualifiers + from pointer target type" + + 2007-03-05 Frédéric Bernon + * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES, + ETHARP_TRUST_IP_MAC, review SO_REUSE) + + 2007-03-04 Frédéric Bernon + * api_msg.c: Remove some compiler warnings : parameter "pcb" was never + referenced. + + 2007-03-04 Frédéric Bernon + * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from + Dmitry Potapov). + The api_msg struct stay on the stack (not moved to netconn struct). + + 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov) + * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if + SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available) + Also fixed cast warning in pbuf_alloc() + + 2007-03-04 Simon Goldschmidt + * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt + existing pbuf chain when enqueuing multiple pbufs to a pending ARP request + + 2007-03-03 Frédéric Bernon + * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;" + It is static, and never used in udp.c except udp_init(). + + 2007-03-02 Simon Goldschmidt + * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from + tcpip_thread() to tcpip_init(). This way, raw API connections can be + initialized before tcpip_thread is running (e.g. before OS is started) + + 2007-03-02 Frédéric Bernon + * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call + interval. + + 2007-02-28 Kieran Mansley + * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved + outside the region of the pbuf by pbuf_header() + + 2007-02-28 Kieran Mansley + * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero + when supplied timeout is also non-zero + +(STABLE-1.2.0) + + 2006-12-05 Leon Woestenberg + * CHANGELOG: Mention STABLE-1.2.0 release. + + ++ New features: + + 2006-12-01 Christiaan Simons + * mem.h, opt.h: Added MEM_LIBC_MALLOC option. + Note this is a workaround. Currently I have no other options left. + + 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour) + * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define + to include/lwip/opt.h. + * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL. + Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h. + * opt.h: Add above new options. + + 2006-08-18 Christiaan Simons + * tcp_{in,out}.c: added SNMP counters. + * ipv4/ip.c: added SNMP counters. + * ipv4/ip_frag.c: added SNMP counters. + + 2006-08-08 Christiaan Simons + * etharp.{c,h}: added etharp_find_addr() to read + (stable) ethernet/IP address pair from ARP table + + 2006-07-14 Christiaan Simons + * mib_structs.c: added + * include/lwip/snmp_structs.h: added + * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct + + 2006-07-06 Christiaan Simons + * snmp/asn1_{enc,dec}.c added + * snmp/mib2.c added + * snmp/msg_{in,out}.c added + * include/lwip/snmp_asn1.h added + * include/lwip/snmp_msg.h added + * doc/snmp_agent.txt added + + 2006-03-29 Christiaan Simons + * inet.c, inet.h: Added platform byteswap support. + Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and + optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros. + + ++ Bug fixes: + + 2006-11-30 Christiaan Simons + * dhcp.c: Fixed false triggers of request_timeout. + + 2006-11-28 Christiaan Simons + * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags. + + 2006-10-11 Christiaan Simons + * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h: + Partially accepted patch #5449 for ANSI C compatibility / build fixes. + * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol + identifier from 170 to 136 (bug #17574). + + 2006-10-10 Christiaan Simons + * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice. + + 2006-08-17 Christiaan Simons + * udp.c: Fixed bug #17200, added check for broadcast + destinations for PCBs bound to a unicast address. + + 2006-08-07 Christiaan Simons + * api_msg.c: Flushing TCP output in do_close() (bug #15926). + + 2006-06-27 Christiaan Simons + * api_msg.c: Applied patch for cold case (bug #11135). + In accept_function() ensure newconn->callback is always initialized. + + 2006-06-15 Christiaan Simons + * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748), + facilitate printing of mem_size_t and u16_t statistics. + + 2006-06-14 Christiaan Simons + * api_msg.c: Applied patch #5146 to handle allocation failures + in accept() by Kevin Lawson. + + 2006-05-26 Christiaan Simons + * api_lib.c: Removed conn->sem creation and destruction + from netconn_write() and added sys_sem_new to netconn_new_*. + +(STABLE-1_1_1) + + 2006-03-03 Christiaan Simons + * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap + access and added pbuf_alloc() return value checks. + + 2006-01-01 Leon Woestenberg + * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is + now handled by the checksum routine properly. + + 2006-02-27 Leon Woestenberg + * pbuf.c: Fix alignment; pbuf_init() would not work unless + pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.) + + 2005-12-20 Leon Woestenberg + * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch + submitted by Mitrani Hiroshi. + + 2005-12-15 Christiaan Simons + * inet.c: Disabled the added summing routine to preserve code space. + + 2005-12-14 Leon Woestenberg + * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson. + Added Curt McDowell's optimized checksumming routine for future + inclusion. Need to create test case for unaliged, aligned, odd, + even length combination of cases on various endianess machines. + + 2005-12-09 Christiaan Simons + * inet.c: Rewrote standard checksum routine in proper portable C. + + 2005-11-25 Christiaan Simons + * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only. + * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t, + u32_t, s32_t typedefs. This solves most debug word-length assumes. + + 2005-07-17 Leon Woestenberg + * inet.c: Fixed unaligned 16-bit access in the standard checksum + routine by Peter Jolasson. + * slipif.c: Fixed implementation assumption of single-pbuf datagrams. + + 2005-02-04 Leon Woestenberg + * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch. + * tcp_{out|in}.c: Applied patch fixing unaligned access. + + 2005-01-04 Leon Woestenberg + * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement. + + 2005-01-03 Leon Woestenberg + * udp.c: UDP pcb->recv() was called even when it was NULL. + +(STABLE-1_1_0) + + 2004-12-28 Leon Woestenberg + * etharp.*: Disabled multiple packets on the ARP queue. + This clashes with TCP queueing. + + 2004-11-28 Leon Woestenberg + * etharp.*: Fixed race condition from ARP request to ARP timeout. + Halved the ARP period, doubled the period counts. + ETHARP_MAX_PENDING now should be at least 2. This prevents + the counter from reaching 0 right away (which would allow + too little time for ARP responses to be received). + + 2004-11-25 Leon Woestenberg + * dhcp.c: Decline messages were not multicast but unicast. + * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD. + Do not try hard to insert arbitrary packet's source address, + etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. + etharp_query() now always DOES call ETHARP_TRY_HARD so that users + querying an address will see it appear in the cache (DHCP could + suffer from this when a server invalidly gave an in-use address.) + * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are + comparing network addresses (identifiers), not the network masks + themselves. + * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given + IP address actually belongs to the network of the given interface. + + 2004-11-24 Kieran Mansley + * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state. + +(STABLE-1_1_0-RC1) + + 2004-10-16 Kieran Mansley + * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately, + even if one is already pending, if the rcv_wnd is above a threshold + (currently TCP_WND/2). This avoids waiting for a timer to expire to send a + delayed ACK in order to open the window if the stack is only receiving data. + + 2004-09-12 Kieran Mansley + * tcp*.*: Retransmit time-out handling improvement by Sam Jansen. + + 2004-08-20 Tony Mountifield + * etharp.c: Make sure the first pbuf queued on an ARP entry + is properly ref counted. + + 2004-07-27 Tony Mountifield + * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler + warnings about comparison. + * pbuf.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. Closed an unclosed comment. + * tcp.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. + * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons(). + * inet.c: Added a couple of casts to quiet the compiler. + No need to test isascii(c) before isdigit(c) or isxdigit(c). + + 2004-07-22 Tony Mountifield + * inet.c: Made data types consistent in inet_ntoa(). + Added casts for return values of checksum routines, to pacify compiler. + * ip_frag.c, tcp_out.c, sockets.c, pbuf.c + Small corrections to some debugging statements, to pacify compiler. + + 2004-07-21 Tony Mountifield + * etharp.c: Removed spurious semicolon and added missing end-of-comment. + * ethernetif.c Updated low_level_output() to match prototype for + netif->linkoutput and changed low_level_input() similarly for consistency. + * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype + of raw_recv() in raw.h and so avoid compiler error. + * sockets.c: Added trivial (int) cast to keep compiler happier. + * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros. + +(STABLE-1_0_0) + + ++ Changes: + + 2004-07-05 Leon Woestenberg + * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure + your cc.h file defines this either 1 or 0. If non-defined, + defaults to 1. + * .c: Added and includes where used. + * etharp.c: Made some array indices unsigned. + + 2004-06-27 Leon Woestenberg + * netif.*: Added netif_set_up()/down(). + * dhcp.c: Changes to restart program flow. + + 2004-05-07 Leon Woestenberg + * etharp.c: In find_entry(), instead of a list traversal per candidate, do a + single-pass lookup for different candidates. Should exploit locality. + + 2004-04-29 Leon Woestenberg + * tcp*.c: Cleaned up source comment documentation for Doxygen processing. + * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC. + * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by + the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option. + + ++ Bug fixes: + + 2004-04-27 Leon Woestenberg + * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution + suggested by Timmy Brolin. Fix for 32-bit processors that cannot access + non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix + is to prefix the 14-bit Ethernet headers with two padding bytes. + + 2004-04-23 Leon Woestenberg + * ip_addr.c: Fix in the ip_addr_isbroadcast() check. + * etharp.c: Fixed the case where the packet that initiates the ARP request + is not queued, and gets lost. Fixed the case where the packets destination + address is already known; we now always queue the packet and perform an ARP + request. + +(STABLE-0_7_0) + + ++ Bug fixes: + + * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition. + * Fixed TCP bug in dequeueing of FIN from out of order segment queue. + * Fixed two possible NULL references in rare cases. + +(STABLE-0_6_6) + + ++ Bug fixes: + + * Fixed DHCP which did not include the IP address in DECLINE messages. + + ++ Changes: + + * etharp.c has been hauled over a bit. + +(STABLE-0_6_5) + + ++ Bug fixes: + + * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic. + * Packets sent from ARP queue had invalid source hardware address. + + ++ Changes: + + * Pass-by ARP requests do now update the cache. + + ++ New features: + + * No longer dependent on ctype.h. + * New socket options. + * Raw IP pcb support. + +(STABLE-0_6_4) + + ++ Bug fixes: + + * Some debug formatters and casts fixed. + * Numereous fixes in PPP. + + ++ Changes: + + * DEBUGF now is LWIP_DEBUGF + * pbuf_dechain() has been re-enabled. + * Mentioned the changed use of CVS branches in README. + +(STABLE-0_6_3) + + ++ Bug fixes: + + * Fixed pool pbuf memory leak in pbuf_alloc(). + Occured if not enough PBUF_POOL pbufs for a packet pbuf chain. + Reported by Savin Zlobec. + + * PBUF_POOL chains had their tot_len field not set for non-first + pbufs. Fixed in pbuf_alloc(). + + ++ New features: + + * Added PPP stack contributed by Marc Boucher + + ++ Changes: + + * Now drops short packets for ICMP/UDP/TCP protocols. More robust. + + * ARP queueuing now queues the latest packet instead of the first. + This is the RFC recommended behaviour, but can be overridden in + lwipopts.h. + +(0.6.2) + + ++ Bugfixes: + + * TCP has been fixed to deal with the new use of the pbuf->ref + counter. + + * DHCP dhcp_inform() crash bug fixed. + + ++ Changes: + + * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed + pbuf_refresh(). This has sped up pbuf pool operations considerably. + Implemented by David Haas. + +(0.6.1) + + ++ New features: + + * The packet buffer implementation has been enhanced to support + zero-copy and copy-on-demand for packet buffers which have their + payloads in application-managed memory. + Implemented by David Haas. + + Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy + if an outgoing packet can be directly sent on the link, or perform + a copy-on-demand when necessary. + + The application can safely assume the packet is sent, and the RAM + is available to the application directly after calling udp_send() + or similar function. + + ++ Bugfixes: + + * ARP_QUEUEING should now correctly work for all cases, including + PBUF_REF. + Implemented by Leon Woestenberg. + + ++ Changes: + + * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer + to a '0.0.0.0' IP address. + + * The packet buffer implementation is changed. The pbuf->ref counter + meaning has changed, and several pbuf functions have been + adapted accordingly. + + * netif drivers have to be changed to set the hardware address length field + that must be initialized correctly by the driver (hint: 6 for Ethernet MAC). + See the contrib/ports/c16x cs8900 driver as a driver example. + + * netif's have a dhcp field that must be initialized to NULL by the driver. + See the contrib/ports/c16x cs8900 driver as a driver example. + +(0.5.x) This file has been unmaintained up to 0.6.1. All changes are + logged in CVS but have not been explained here. + +(0.5.3) Changes since version 0.5.2 + + ++ Bugfixes: + + * memp_malloc(MEMP_API_MSG) could fail with multiple application + threads because it wasn't protected by semaphores. + + ++ Other changes: + + * struct ip_addr now packed. + + * The name of the time variable in arp.c has been changed to ctime + to avoid conflicts with the time() function. + +(0.5.2) Changes since version 0.5.1 + + ++ New features: + + * A new TCP function, tcp_tmr(), now handles both TCP timers. + + ++ Bugfixes: + + * A bug in tcp_parseopt() could cause the stack to hang because of a + malformed TCP option. + + * The address of new connections in the accept() function in the BSD + socket library was not handled correctly. + + * pbuf_dechain() did not update the ->tot_len field of the tail. + + * Aborted TCP connections were not handled correctly in all + situations. + + ++ Other changes: + + * All protocol header structs are now packed. + + * The ->len field in the tcp_seg structure now counts the actual + amount of data, and does not add one for SYN and FIN segments. + +(0.5.1) Changes since version 0.5.0 + + ++ New features: + + * Possible to run as a user process under Linux. + + * Preliminary support for cross platform packed structs. + + * ARP timer now implemented. + + ++ Bugfixes: + + * TCP output queue length was badly initialized when opening + connections. + + * TCP delayed ACKs were not sent correctly. + + * Explicit initialization of BSS segment variables. + + * read() in BSD socket library could drop data. + + * Problems with memory alignment. + + * Situations when all TCP buffers were used could lead to + starvation. + + * TCP MSS option wasn't parsed correctly. + + * Problems with UDP checksum calculation. + + * IP multicast address tests had endianess problems. + + * ARP requests had wrong destination hardware address. + + ++ Other changes: + + * struct eth_addr changed from u16_t[3] array to u8_t[6]. + + * A ->linkoutput() member was added to struct netif. + + * TCP and UDP ->dest_* struct members where changed to ->remote_*. + + * ntoh* macros are now null definitions for big endian CPUs. + +(0.5.0) Changes since version 0.4.2 + + ++ New features: + + * Redesigned operating system emulation layer to make porting easier. + + * Better control over TCP output buffers. + + * Documenation added. + + ++ Bugfixes: + + * Locking issues in buffer management. + + * Bugfixes in the sequential API. + + * IP forwarding could cause memory leakage. This has been fixed. + + ++ Other changes: + + * Directory structure somewhat changed; the core/ tree has been + collapsed. + +(0.4.2) Changes since version 0.4.1 + + ++ New features: + + * Experimental ARP implementation added. + + * Skeleton Ethernet driver added. + + * Experimental BSD socket API library added. + + ++ Bugfixes: + + * In very intense situations, memory leakage could occur. This has + been fixed. + + ++ Other changes: + + * Variables named "data" and "code" have been renamed in order to + avoid name conflicts in certain compilers. + + * Variable++ have in appliciable cases been translated to ++variable + since some compilers generate better code in the latter case. + +(0.4.1) Changes since version 0.4 + + ++ New features: + + * TCP: Connection attempts time out earlier than data + transmissions. Nagle algorithm implemented. Push flag set on the + last segment in a burst. + + * UDP: experimental support for UDP-Lite extensions. + + ++ Bugfixes: + + * TCP: out of order segments were in some cases handled incorrectly, + and this has now been fixed. Delayed acknowledgements was broken + in 0.4, has now been fixed. Binding to an address that is in use + now results in an error. Reset connections sometimes hung an + application; this has been fixed. + + * Checksum calculation sometimes failed for chained pbufs with odd + lengths. This has been fixed. + + * API: a lot of bug fixes in the API. The UDP API has been improved + and tested. Error reporting and handling has been + improved. Logical flaws and race conditions for incoming TCP + connections has been found and removed. + + * Memory manager: alignment issues. Reallocating memory sometimes + failed, this has been fixed. + + * Generic library: bcopy was flawed and has been fixed. + + ++ Other changes: + + * API: all datatypes has been changed from generic ones such as + ints, to specified ones such as u16_t. Functions that return + errors now have the correct type (err_t). + + * General: A lot of code cleaned up and debugging code removed. Many + portability issues have been fixed. + + * The license was changed; the advertising clause was removed. + + * C64 port added. + + * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri + Kosunen, Mikael Caleres, and Frits Wilmink for reporting and + fixing bugs! + +(0.4) Changes since version 0.3.1 + + * Memory management has been radically changed; instead of + allocating memory from a shared heap, memory for objects that are + rapidly allocated and deallocated is now kept in pools. Allocation + and deallocation from those memory pools is very fast. The shared + heap is still present but is used less frequently. + + * The memory, memory pool, and packet buffer subsystems now support + 4-, 2-, or 1-byte alignment. + + * "Out of memory" situations are handled in a more robust way. + + * Stack usage has been reduced. + + * Easier configuration of lwIP parameters such as memory usage, + TTLs, statistics gathering, etc. All configuration parameters are + now kept in a single header file "lwipopts.h". + + * The directory structure has been changed slightly so that all + architecture specific files are kept under the src/arch + hierarchy. + + * Error propagation has been improved, both in the protocol modules + and in the API. + + * The code for the RTXC architecture has been implemented, tested + and put to use. + + * Bugs have been found and corrected in the TCP, UDP, IP, API, and + the Internet checksum modules. + + * Bugs related to porting between a 32-bit and a 16-bit architecture + have been found and corrected. + + * The license has been changed slightly to conform more with the + original BSD license, including the advertisement clause. + +(0.3.1) Changes since version 0.3 + + * Fix of a fatal bug in the buffer management. Pbufs with allocated + RAM never returned the RAM when the pbuf was deallocated. + + * TCP congestion control, window updates and retransmissions did not + work correctly. This has now been fixed. + + * Bugfixes in the API. + +(0.3) Changes since version 0.2 + + * New and improved directory structure. All include files are now + kept in a dedicated include/ directory. + + * The API now has proper error handling. A new function, + netconn_err(), now returns an error code for the connection in + case of errors. + + * Improvements in the memory management subsystem. The system now + keeps a pointer to the lowest free memory block. A new function, + mem_malloc2() tries to allocate memory once, and if it fails tries + to free some memory and retry the allocation. + + * Much testing has been done with limited memory + configurations. lwIP now does a better job when overloaded. + + * Some bugfixes and improvements to the buffer (pbuf) subsystem. + + * Many bugfixes in the TCP code: + + - Fixed a bug in tcp_close(). + + - The TCP receive window was incorrectly closed when out of + sequence segments was received. This has been fixed. + + - Connections are now timed-out of the FIN-WAIT-2 state. + + - The initial congestion window could in some cases be too + large. This has been fixed. + + - The retransmission queue could in some cases be screwed up. This + has been fixed. + + - TCP RST flag now handled correctly. + + - Out of sequence data was in some cases never delivered to the + application. This has been fixed. + + - Retransmitted segments now contain the correct acknowledgment + number and advertised window. + + - TCP retransmission timeout backoffs are not correctly computed + (ala BSD). After a number of retransmissions, TCP now gives up + the connection. + + * TCP connections now are kept on three lists, one for active + connections, one for listening connections, and one for + connections that are in TIME-WAIT. This greatly speeds up the fast + timeout processing for sending delayed ACKs. + + * TCP now provides proper feedback to the application when a + connection has been successfully set up. + + * More comments have been added to the code. The code has also been + somewhat cleaned up. + +(0.2) Initial public release. diff --git a/component/common/network/lwip/lwip_v1.4.1/COPYING b/component/common/network/lwip/lwip_v1.4.1/COPYING new file mode 100644 index 0000000..e23898b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 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 + * + */ + + diff --git a/component/common/network/lwip/lwip_v1.4.1/FILES b/component/common/network/lwip/lwip_v1.4.1/FILES new file mode 100644 index 0000000..6625319 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/FILES @@ -0,0 +1,4 @@ +src/ - The source code for the lwIP TCP/IP stack. +doc/ - The documentation for lwIP. + +See also the FILES file in each subdirectory. diff --git a/component/common/network/lwip/lwip_v1.4.1/README b/component/common/network/lwip/lwip_v1.4.1/README new file mode 100644 index 0000000..a62cc4f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/README @@ -0,0 +1,89 @@ +INTRODUCTION + +lwIP is a small independent implementation of the TCP/IP protocol +suite that has been developed by Adam Dunkels at the Computer and +Networks Architectures (CNA) lab at the Swedish Institute of Computer +Science (SICS). + +The focus of the lwIP TCP/IP implementation is to reduce the RAM usage +while still having a full scale TCP. This making lwIP suitable for use +in embedded systems with tens of kilobytes of free RAM and room for +around 40 kilobytes of code ROM. + +FEATURES + + * IP (Internet Protocol) including packet forwarding over multiple network + interfaces + * ICMP (Internet Control Message Protocol) for network maintenance and debugging + * IGMP (Internet Group Management Protocol) for multicast traffic management + * UDP (User Datagram Protocol) including experimental UDP-lite extensions + * TCP (Transmission Control Protocol) with congestion control, RTT estimation + and fast recovery/fast retransmit + * Specialized raw/native API for enhanced performance + * Optional Berkeley-like socket API + * DNS (Domain names resolver) + * SNMP (Simple Network Management Protocol) + * DHCP (Dynamic Host Configuration Protocol) + * AUTOIP (for IPv4, conform with RFC 3927) + * PPP (Point-to-Point Protocol) + * ARP (Address Resolution Protocol) for Ethernet + +LICENSE + +lwIP is freely available under a BSD license. + +DEVELOPMENT + +lwIP has grown into an excellent TCP/IP stack for embedded devices, +and developers using the stack often submit bug fixes, improvements, +and additions to the stack to further increase its usefulness. + +Development of lwIP is hosted on Savannah, a central point for +software development, maintenance and distribution. Everyone can +help improve lwIP by use of Savannah's interface, CVS and the +mailing list. A core team of developers will commit changes to the +CVS source tree. + +The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and +contributions (such as platform ports) are in the 'contrib' module. + +See doc/savannah.txt for details on CVS server access for users and +developers. + +Last night's CVS tar ball can be downloaded from: + http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING] + +The current CVS trees are web-browsable: + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/ + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/ + +Submit patches and bugs via the lwIP project page: + http://savannah.nongnu.org/projects/lwip/ + + +DOCUMENTATION + +The original out-dated homepage of lwIP and Adam Dunkels' papers on +lwIP are at the official lwIP home page: + http://www.sics.se/~adam/lwip/ + +Self documentation of the source code is regularly extracted from the +current CVS sources and is available from this web page: + http://www.nongnu.org/lwip/ + +There is now a constantly growin wiki about lwIP at + http://lwip.wikia.com/wiki/LwIP_Wiki + +Also, there are mailing lists you can subscribe at + http://savannah.nongnu.org/mail/?group=lwip +plus searchable archives: + http://lists.nongnu.org/archive/html/lwip-users/ + http://lists.nongnu.org/archive/html/lwip-devel/ + +Reading Adam's papers, the files in docs/, browsing the source code +documentation and browsing the mailing list archives is a good way to +become familiar with the design of lwIP. + +Adam Dunkels +Leon Woestenberg + diff --git a/component/common/network/lwip/lwip_v1.4.1/UPGRADING b/component/common/network/lwip/lwip_v1.4.1/UPGRADING new file mode 100644 index 0000000..6501107 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/UPGRADING @@ -0,0 +1,144 @@ +This file lists major changes between release versions that require +ports or applications to be changed. Use it to update a port or an +application written for an older version of lwIP to correctly work +with newer versions. + + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ Application changes: + + * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for + compatibility to old applications, but will be removed in the future). + + * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc() + + +++ Raw API: + * Changed the semantics of tcp_close() (since it was rather a + shutdown before): Now the application does *NOT* get any calls to the recv + callback (aside from NULL/closed) after calling tcp_close() + + * When calling tcp_abort() from a raw API TCP callback function, + make sure you return ERR_ABRT to prevent accessing unallocated memory. + (ERR_ABRT now means the applicaiton has called tcp_abort!) + + +++ Netconn API: + * Changed netconn_receive() and netconn_accept() to return + err_t, not a pointer to new data/netconn. + + +++ Socket API: + * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they + now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT. + + * Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + +++ all APIs: + * correctly implemented SO(F)_REUSEADDR + + ++ Port changes + + +++ new files: + + * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h: + + * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains + the actual application programmer's API + + * Separated timer implementation from sys.h/.c, moved to timers.h/.c; + Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you + still want to use your own timer implementation for NO_SYS==0 (as before). + + +++ sys layer: + + * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/ + sys_sem_t; + + * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + + * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use + binary semaphores instead of mutexes - as before) + + +++ new options: + + * Don't waste memory when chaining segments, added option TCP_OVERSIZE to + prevent creating many small pbufs when calling tcp_write with many small + blocks of data. Instead, pbufs are allocated larger than needed and the + space is used for later calls to tcp_write. + + * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs + in tcp_write/udp_send. + + * Added an additional option LWIP_ETHERNET to support ethernet without ARP + (necessary for pure PPPoE) + + * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may + be used to place these pools into user-defined memory by using external + declaration. + + * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT + + +++ new pools: + + * Netdb uses a memp pool for allocating memory when getaddrinfo() is called, + so MEMP_NUM_NETDB has to be set accordingly. + + * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so + MEMP_NUM_LOCALHOSTLIST has to be set accordingly. + + * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have + to be set accordingly. + + * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES + has to be set accordingly + + * Integrated loopif into netif.c - loopif does not have to be created by the + port any more, just define LWIP_HAVE_LOOPIF to 1. + + * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined + in cc.h, e.g. used by igmp) + + * Added printf-formatter X8_F to printf u8_t as hex + + * The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + + * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work + with user-allocated structs instead of calling mem_malloc + + * Added const char* name to mem- and memp-stats for easier debugging. + + * Calculate the TCP/UDP checksum while copying to only fetch data once: + Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum + + * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to + more than one pcb. + + * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned + off any more, if this is set to 0, only one packet (the most recent one) is + queued (like demanded by RFC 1122). + + + ++ Major bugfixes/improvements + + * Implemented tcp_shutdown() to only shut down one end of a connection + * Implemented shutdown() at socket- and netconn-level + * Added errorset support to select() + improved select speed overhead + * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x) + * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1 + * Use macros defined in ip_addr.h to work with IP addresses + * Implemented many nonblocking socket/netconn functions + * Fixed ARP input processing: only add a new entry if a request was directed as us + * mem_realloc() to mem_trim() to prevent confusion with realloc() + * Some improvements for AutoIP (don't route/forward link-local addresses, don't break + existing connections when assigning a routable address) + * Correctly handle remote side overrunning our rcv_wnd in ooseq case + * Removed packing from ip_addr_t, the packed version is now only used in protocol headers + * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0 + * Added support for static ARP table entries + +(STABLE-1.3.2) + + * initial version of this file diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/FILES b/component/common/network/lwip/lwip_v1.4.1/doc/FILES new file mode 100644 index 0000000..05d356f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/FILES @@ -0,0 +1,6 @@ +savannah.txt - How to obtain the current development source code. +contrib.txt - How to contribute to lwIP as a developer. +rawapi.txt - The documentation for the core API of lwIP. + Also provides an overview about the other APIs and multithreading. +snmp_agent.txt - The documentation for the lwIP SNMP agent. +sys_arch.txt - The documentation for a system abstraction layer of lwIP. diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/contrib.txt b/component/common/network/lwip/lwip_v1.4.1/doc/contrib.txt new file mode 100644 index 0000000..39596fc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/contrib.txt @@ -0,0 +1,63 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two) + can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded + as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead + for reporting a compiler warning fix. +6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/rawapi.txt b/component/common/network/lwip/lwip_v1.4.1/doc/rawapi.txt new file mode 100644 index 0000000..8c19030 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/rawapi.txt @@ -0,0 +1,511 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed + into the accept callback! + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The apiflags can be one or more of: + - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated + for the data to be copied into. If this flag is not given, no new memory + should be allocated and the data should only be referenced by pointer. This + also means that the memory behind dataptr must not change until the data is + ACKed by the remote host + - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given, + the PSH flag is set in the last segment created by this call to tcp_write. + If this flag is given, the PSH flag is not set. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct 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! + + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + ip_addr_t *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, ip_addr_t *ipaddr, + ip_addr_t *netmask, ip_addr_t *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. + +For more optimization hints take a look at the lwIP wiki. + +--- Zero-copy MACs + +To achieve zero-copy on transmit, the data passed to the raw API must +remain unchanged until sent. Because the send- (or write-)functions return +when the packets have been enqueued for sending, data must be kept stable +after that, too. + +This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions +must *not* be reused by the application unless their ref-count is 1. + +For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too, +but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while +PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change). + +Also, data passed to tcp_write without the copy-flag must not be changed! + +Therefore, be careful which type of PBUF you use and if you copy TCP data +or not! diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/savannah.txt b/component/common/network/lwip/lwip_v1.4.1/doc/savannah.txt new file mode 100644 index 0000000..409905b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/snmp_agent.txt b/component/common/network/lwip/lwip_v1.4.1/doc/snmp_agent.txt new file mode 100644 index 0000000..2653230 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads than the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for a dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/component/common/network/lwip/lwip_v1.4.1/doc/sys_arch.txt b/component/common/network/lwip/lwip_v1.4.1/doc/sys_arch.txt new file mode 100644 index 0000000..847cd77 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/doc/sys_arch.txt @@ -0,0 +1,267 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that +allows both using pointers or actual OS structures to be used. This way, memory +required for such types can be either allocated in place (globally or on the +stack) or on the heap (allocated internally in the "*_new()" functions). + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- err_t sys_sem_new(sys_sem_t *sem, u8_t count) + + Creates a new semaphore. The semaphore is allocated to the memory that 'sem' + points to (which can be both a pointer or the actual OS structure). + The "count" argument specifies the initial state of the semaphore (which is + either 0 or 1). + If the semaphore has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_sem_free(sys_sem_t *sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t *sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- int sys_sem_valid(sys_sem_t *sem) + + Returns 1 if the semaphore is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_sem_set_invalid(sys_sem_t *sem) + + Invalidate a semaphore so that sys_sem_valid() returns 0. + ATTENTION: This does NOT mean that the semaphore shall be deallocated: + sys_sem_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +- err_t sys_mbox_new(sys_mbox_t *mbox, int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + If the mailbox has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_mbox_free(sys_mbox_t *mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t *mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- int sys_mbox_valid(sys_mbox_t *mbox) + + Returns 1 if the mailbox is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_mbox_set_invalid(sys_mbox_t *mbox) + + Invalidate a mailbox so that sys_mbox_valid() returns 0. + ATTENTION: This does NOT mean that the mailbox shall be deallocated: + sys_mbox_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +For some configurations, you also need: + +- u32_t sys_now(void) + + This optional function returns the current time in milliseconds (don't care + for wraparound, this is only used for time diffs). + Not implementing this function means you cannot use some modules (e.g. TCP + timestamps, internal timeouts for NO_SYS==1). + + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cc.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cc.h new file mode 100644 index 0000000..be882d9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cc.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned int u32_t; +typedef signed long s32_t; +typedef u32_t mem_ptr_t; +typedef int sys_prot_t; + +#define U16_F "d" +#define S16_F "d" +#define X16_F "x" +#define U32_F "d" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + +/* define compiler specific symbols */ +#if defined (__ICCARM__) +#if !defined (__IARSTDLIB__) +#define _STRING +#ifndef memcmp +#define memcmp(dst, src, sz) _memcmp(dst, src, sz) +#endif +#ifndef memset +#define memset(dst, val, sz) _memset(dst, val, sz) +#endif +#ifndef memcpy +#define memcpy(dst, src, sz) _memcpy(dst, src, sz) +#endif +#endif // __IARSTDLIB__ + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cpu.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/epstruct.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/init.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/lib.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/perf.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/sys_arch.h new file mode 100644 index 0000000..2e841bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/arch/sys_arch.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xSemaphoreHandle sys_mutex_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.c new file mode 100644 index 0000000..ff87e04 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.c @@ -0,0 +1,284 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +//#include "lwip/ethip6.h" //Evan add for ipv6 +#include +#include + +#define netifMTU (1500) +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) + + +#ifdef CONFIG_CONCURRENT_MODE +#define IF2NAME0 'r' +#define IF2NAME1 '2' +#endif + +static void arp_timer(void *arg); + + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ + +static void low_level_init(struct netif *netif) +{ + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set netif maximum transfer unit */ + netif->mtu = 1500; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Wlan interface is initialized later */ +} + + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + + + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + if (sg_len) { + if (rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len) == 0) + return ERR_OK; + else + return ERR_BUF; // return a non-fatal error + } + + return ERR_OK; +} + + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +//static struct pbuf * low_level_input(struct netif *netif){} + + +/** + * This function is the ethernetif_input task, it is processed when a packet + * is ready to be read from the interface. It uses the function low_level_input() + * that should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +//void ethernetif_input( void * pvParameters ) + + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); + +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->output = etharp_output; +//#if LWIP_IPV6 +// netif->output_ip6 = ethip6_output; +//#endif + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + + return ERR_OK; +} + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.c new file mode 100644 index 0000000..c496791 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "lwip/lwip_timers.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; +extern void * vTaskGetCurrentTCB( void ); +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +err_t sys_mbox_new(sys_mbox_t *mbox, int size) +{ + (void ) size; + + *mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + if (*mbox == NULL) + return ERR_MEM; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t *mbox) +{ + if( uxQueueMessagesWaiting( *mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( *mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t *mbox, void *data) +{ + while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} +/*----------------------------------------------------------------------------------*/ +int sys_mbox_valid(sys_mbox_t *mbox) +{ + if (*mbox == SYS_MBOX_NULL) + return 0; + else + return 1; +} +/*-----------------------------------------------------------------------------------*/ +void sys_mbox_set_invalid(sys_mbox_t *mbox) +{ + *mbox = SYS_MBOX_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Creates a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +err_t sys_sem_new(sys_sem_t *sem, u8_t count) +{ + vSemaphoreCreateBinary(*sem ); + if(*sem == NULL) + { + +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(*sem,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t *sem) +{ + xSemaphoreGive(*sem); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t *sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete(*sem); +} +/*-----------------------------------------------------------------------------------*/ +int sys_sem_valid(sys_sem_t *sem) +{ + if (*sem == SYS_SEM_NULL) + return 0; + else + return 1; +} + +/*-----------------------------------------------------------------------------------*/ +void sys_sem_set_invalid(sys_sem_t *sem) +{ + *sem = SYS_SEM_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts *sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} +/*-----------------------------------------------------------------------------------*/ + /* Mutexes*/ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_COMPAT_MUTEX == 0 +/* Create a new mutex*/ +err_t sys_mutex_new(sys_mutex_t *mutex) { + + *mutex = xSemaphoreCreateMutex(); + if(*mutex == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.mutex.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + +#if SYS_STATS + ++lwip_stats.sys.mutex.used; + if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) { + lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used; + } +#endif /* SYS_STATS */ + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* Deallocate a mutex*/ +void sys_mutex_free(sys_mutex_t *mutex) +{ +#if SYS_STATS + --lwip_stats.sys.mutex.used; +#endif /* SYS_STATS */ + + vQueueDelete(*mutex); +} +/*-----------------------------------------------------------------------------------*/ +/* Lock a mutex*/ +void sys_mutex_lock(sys_mutex_t *mutex) +{ + sys_arch_sem_wait(*mutex, 0); +} + +/*-----------------------------------------------------------------------------------*/ +/* Unlock a mutex*/ +void sys_mutex_unlock(sys_mutex_t *mutex) +{ + xSemaphoreGive(*mutex); +} +#endif /*LWIP_COMPAT_MUTEX*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( signed portCHAR * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +int sys_thread_delete(xTaskHandle pid) +{ + int i, isFind = 0; + struct timeoutlist *tl, *tend = NULL; + + pid = (( pid == NULL)?(xTaskHandle) vTaskGetCurrentTCB() : pid); + + if (s_nextthread) + { + vPortEnterCritical(); + + tend = &(s_timeoutlist[s_nextthread-1]);//the last one + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + {//find the task, exchange with the last one + memcpy(tl, tend, sizeof(struct timeoutlist)); + memset(tend, 0, sizeof(struct timeoutlist)); + s_nextthread --; + isFind = 1; + break; + } + } + + if (isFind) { + vTaskDelete( pid); + } + + vPortExitCritical(); + + if (isFind) + { + return pdPASS; + } + else + { + return pdFAIL; + } + } + else + { + return pdFAIL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cc.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cc.h new file mode 100644 index 0000000..3ca260f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cc.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +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 u32_t mem_ptr_t; +typedef int sys_prot_t; + + +#define U16_F "hu" +#define S16_F "d" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + + + + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cpu.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/epstruct.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/init.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/lib.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/perf.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/sys_arch.h new file mode 100644 index 0000000..2e841bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/arch/sys_arch.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xSemaphoreHandle sys_mutex_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/MFC6A0B.tmp b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/MFC6A0B.tmp new file mode 100644 index 0000000..5097f29 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/MFC6A0B.tmp @@ -0,0 +1,489 @@ +/** +* @file +* Ethernet Interface Skeleton +* +*/ + +/* +* 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 file is a skeleton for developing Ethernet network interface +* drivers for lwIP. Add code to the low_level functions and do a +* search-and-replace for the word "ethernetif" to replace it with +* something that better describes your network interface. +*/ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +//#include "lwip/sys.h" +//#include "lwip/tcpip.h" +//#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +//#include "queue.h" + +#include "main.h" // for the definition of CONFIG_WLAN + +#if !CONFIG_WLAN +#include "stm32f2x7_eth.h" +#else +#include +#endif +#include + +#include + + +#define netifMTU (1500) +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) + +#if !CONFIG_WLAN +/* Define those to better describe your network interface. */ +#define IFNAME0 's' +#define IFNAME1 't' + +static struct netif *s_pxNetIf = NULL; +xSemaphoreHandle s_xSemaphore = NULL; + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + + +static void ethernetif_input( void * pvParameters ); +#endif + +static void arp_timer(void *arg); + + +/** +* In this function, the hardware should be initialized. +* Called from ethernetif_init(). +* +* @param netif the already initialized lwip network interface structure +* for this ethernetif +*/ +static void low_level_init(struct netif *netif) +{ + uint32_t i; + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + +#if !CONFIG_WLAN + /* set netif MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; +#endif + + /* set netif maximum transfer unit */ + netif->mtu = 1500; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + +#if !CONFIG_WLAN + s_pxNetIf =netif; + + /* create binary semaphore used for informing ethernetif of frame reception */ + if (s_xSemaphore == NULL) + { + vSemaphoreCreateBinary(s_xSemaphore); + xSemaphoreTake( s_xSemaphore, 0); + } + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* Enable Ethernet Rx interrrupt */ + { + for(i=0; iBuffer1Addr); + bufferoffset = 0; + + for(q = p; q != NULL; q = q->next) + { + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) + { + /* Copy data to Tx buffer*/ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + goto error; + } + + buffer = (u8 *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy ); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Prepare transmit descriptors to give to DMA*/ + ETH_Prepare_Transmit_Descriptors(framelength); + + /* Give semaphore and exit */ + error: + + xSemaphoreGive(xTxSemaphore); + } +#endif // #if !CONFIG_WLAN + + return ERR_OK; +} + + +#if !CONFIG_WLAN +/** +* Should allocate a pbuf and transfer the bytes of the incoming +* packet from the interface into the pbuf. +* +* @param netif the lwip network interface structure for this ethernetif +* @return a pbuf filled with the received packet (including MAC header) +* NULL on memory error +*/ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p= NULL, *q; + u32_t len; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxDesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i=0; + + /* get received frame */ + frame = ETH_Get_Received_Frame_interrupt(); + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + if (len > 0) + { + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + } + + if (p != NULL) + { + DMARxDesc = frame.descriptor; + bufferoffset = 0; + for(q = p; q != NULL; q = q->next) + { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) + { + /* Copy data to pbuf*/ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + buffer = (unsigned char *)(DMARxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy remaining data in pbuf */ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + + /* Release descriptors to DMA */ + DMARxDesc =frame.descriptor; + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxDesc->Status = ETH_DMARxDesc_OWN; + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + /* added for test*/ + } + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + + +/** +* This function is the ethernetif_input task, it is processed when a packet +* is ready to be read from the interface. It uses the function low_level_input() +* that should handle the actual reception of bytes from the network +* interface. Then the type of the received packet is determined and +* the appropriate input function is called. +* +* @param netif the lwip network interface structure for this ethernetif +*/ +void ethernetif_input( void * pvParameters ) +{ + struct pbuf *p; + + for( ;; ) + { + if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) + { +TRY_GET_NEXT_FRAME: + p = low_level_input( s_pxNetIf ); + if (p != NULL) + { + if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) + { + pbuf_free(p); + } + else + { + goto TRY_GET_NEXT_FRAME; + } + } + + } + } +} +#endif +/** +* Should be called at the beginning of the program to set up the +* network interface. It calls the function low_level_init() to do the +* actual setup of the hardware. +* +* This function should be passed as a parameter to netif_add(). +* +* @param netif the lwip network interface structure for this ethernetif +* @return ERR_OK if the loopif is initialized +* ERR_MEM if private data couldn't be allocated +* any other err_t on error +*/ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if !CONFIG_WLAN + /* ethernet */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#else + /* wlan interface*/ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ +#endif // WLAN + + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + + return ERR_OK; +} + + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +/* +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} +*/ +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.c new file mode 100644 index 0000000..0f67357 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.c @@ -0,0 +1,653 @@ +/** +* @file +* Ethernet Interface Skeleton +* +*/ + +/* +* 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 file is a skeleton for developing Ethernet network interface +* drivers for lwIP. Add code to the low_level functions and do a +* search-and-replace for the word "ethernetif" to replace it with +* something that better describes your network interface. +*/ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +#include "main.h" // for the definition of CONFIG_WLAN + + +#if !CONFIG_WLAN +#include "stm32f2x7_eth.h" +#else +#include +#endif +#include + +#include + +#ifdef CONFIG_DONT_CARE_TP +#define netifMTU (576) +#else +#define netifMTU (1500) +#endif +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) +#define FAKE_PING_REPLY 0 + +#if !CONFIG_WLAN +/* Define those to better describe your network interface. */ +#define IFNAME0 's' +#define IFNAME1 't' + +static struct netif *s_pxNetIf = NULL; +xSemaphoreHandle s_xSemaphore = NULL; + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + +static void ethernetif_input( void * pvParameters ); +#endif + +static void arp_timer(void *arg); + + +/** +* In this function, the hardware should be initialized. +* Called from ethernetif_init(). +* +* @param netif the already initialized lwip network interface structure +* for this ethernetif +*/ +static void low_level_init(struct netif *netif) +{ + uint32_t i; + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + +#if !CONFIG_WLAN + /* set netif MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; +#endif + + /* set netif maximum transfer unit */ + netif->mtu = netifMTU; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + +#if !CONFIG_WLAN + s_pxNetIf =netif; + + /* create binary semaphore used for informing ethernetif of frame reception */ + if (s_xSemaphore == NULL) + { + s_xSemaphore= xSemaphoreCreateCounting(20,0); + } + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* Enable Ethernet Rx interrrupt */ + { + for(i=0; itot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_arp = (struct etharp_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_arp = (struct etharp_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = fake_src_mac; + q_arp->opcode = htons(ARP_REPLY); + q_arp->shwaddr = fake_src_mac; + q_arp->sipaddr = p_arp->dipaddr; + q_arp->dhwaddr = p_eth->src; + q_arp->dipaddr = p_arp->sipaddr; + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_arp_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake arp reply \n\r"); +} + +void fake_echo_reply(struct netif *netif, struct pbuf *p) +{ + struct pbuf *q, *tq, *tp; + int q_len, p_len; + struct eth_hdr *p_eth, *q_eth; + struct ip_hdr *p_ip, *q_ip; + struct icmp_echo_hdr *p_echo, *q_echo; + + // Allocate buffer to store received packet + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_ip = (struct ip_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_ip = (struct ip_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + p_echo = (struct icmp_echo_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + q_echo = (struct icmp_echo_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = p_eth->dest; + q_ip->src.addr = p_ip->dest.addr; + q_ip->dest.addr = p_ip->src.addr; + q_ip->_chksum = 0; + q_ip->_chksum = inet_chksum(q_ip, sizeof(struct ip_hdr)); + q_echo->type = ICMP_ER; + q_echo->code = 0; + q_echo->chksum = 0; + q_echo->chksum = inet_chksum(q_echo, q->tot_len - sizeof(struct eth_hdr) - sizeof(struct ip_hdr)); + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_echo_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake echo reply \n\r"); +} +#endif // #if FAKE_PING_REPLY + +/** +* This function should do the actual transmission of the packet. The packet is +* contained in the pbuf that is passed to the function. This pbuf +* might be chained. +* +* @param netif the lwip network interface structure for this ethernetif +* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) +* @return ERR_OK if the packet could be sent +* an err_t value if the packet couldn't be sent +* +* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to +* strange results. You might consider waiting for space in the DMA queue +* to become availale since the stack doesn't retry to send a packet +* dropped because of memory failure (except for the TCP timers). +*/ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ +#if !CONFIG_WLAN + static xSemaphoreHandle xTxSemaphore = NULL; + struct pbuf *q; + uint32_t l = 0; + u8 *buffer ; + + if (xTxSemaphore == NULL) + { + vSemaphoreCreateBinary (xTxSemaphore); + } + + if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME)) + { + buffer = (u8 *)(DMATxDescToSet->Buffer1Addr); + for(q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)&buffer[l], q->payload, q->len); + l = l + q->len; + } + ETH_Prepare_Transmit_Descriptors(l); + xSemaphoreGive(xTxSemaphore); + } +#else + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + +#if FAKE_PING_REPLY + { + char *header = p->payload; + if(header[12] == 0x08 && header[13] == 0x06) + { // arp packet + if(header[21] == 0x01) + { // arp request packet + printf("\n\rfake_ping: arp request packet."); + if(0) + { + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_arp_reply(netif, p); + return ERR_OK; + } + }else if(header[12] == 0x08 && header[13] == 0x00) + { // ip packet + if(header[15] == 0x00 && header[23] == 0x01) + { // icmp packet + printf("\n\rfake_ping: icmp packet."); + if(0){ + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_echo_reply(netif, p); + return ERR_OK; + } + } + } +#endif // #if FAKE_PING_REPLY + if (sg_len) + rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len); +#endif // #if !CONFIG_WLAN + + return ERR_OK; +} + + +#if !CONFIG_WLAN +/** +* Should allocate a pbuf and transfer the bytes of the incoming +* packet from the interface into the pbuf. +* +* @param netif the lwip network interface structure for this ethernetif +* @return a pbuf filled with the received packet (including MAC header) +* NULL on memory error +*/ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + u16_t len; + uint32_t l=0,i =0; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxNextDesc; + + p = NULL; + + /* Get received frame */ + frame = ETH_Get_Received_Frame_interrupt(); + + /* check that frame has no error */ + if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) + { + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + /* Copy received frame from ethernet driver buffer to stack buffer */ + if (p != NULL) + { + for (q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len); + l = l + q->len; + } + } + } + + /* Release descriptors to DMA */ + /* Check if received frame with multiple DMA buffer segments */ + if (DMA_RX_FRAME_infos->Seg_Count > 1) + { + DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; + } + else + { + DMARxNextDesc = frame.descriptor; + } + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxNextDesc->Status = ETH_DMARxDesc_OWN; + DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + + +/** +* This function is the ethernetif_input task, it is processed when a packet +* is ready to be read from the interface. It uses the function low_level_input() +* that should handle the actual reception of bytes from the network +* interface. Then the type of the received packet is determined and +* the appropriate input function is called. +* +* @param netif the lwip network interface structure for this ethernetif +*/ +void ethernetif_input( void * pvParameters ) +{ + struct pbuf *p; + + for( ;; ) + { + if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) + { + p = low_level_input( s_pxNetIf ); + if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) + { + pbuf_free(p); + p=NULL; + } + } + } +} +#endif + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ +#if CONFIG_WLAN + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); +#endif +} + +/** +* Should be called at the beginning of the program to set up the +* network interface. It calls the function low_level_init() to do the +* actual setup of the hardware. +* +* This function should be passed as a parameter to netif_add(). +* +* @param netif the lwip network interface structure for this ethernetif +* @return ERR_OK if the loopif is initialized +* ERR_MEM if private data couldn't be allocated +* any other err_t on error +*/ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if !CONFIG_WLAN + /* ethernet */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#else + /* wlan interface*/ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + +#endif // WLAN + + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + + return ERR_OK; +} + + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.c new file mode 100644 index 0000000..3d111c6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "lwip/lwip_timers.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +err_t sys_mbox_new(sys_mbox_t *mbox, int size) +{ + (void ) size; + + *mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + if (*mbox == NULL) + return ERR_MEM; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t *mbox) +{ + if( uxQueueMessagesWaiting( *mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( *mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t *mbox, void *data) +{ + while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} +/*----------------------------------------------------------------------------------*/ +int sys_mbox_valid(sys_mbox_t *mbox) +{ + if (*mbox == SYS_MBOX_NULL) + return 0; + else + return 1; +} +/*-----------------------------------------------------------------------------------*/ +void sys_mbox_set_invalid(sys_mbox_t *mbox) +{ + *mbox = SYS_MBOX_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Creates a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +err_t sys_sem_new(sys_sem_t *sem, u8_t count) +{ + vSemaphoreCreateBinary(*sem ); + if(*sem == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(*sem,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t *sem) +{ + xSemaphoreGive(*sem); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t *sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete(*sem); +} +/*-----------------------------------------------------------------------------------*/ +int sys_sem_valid(sys_sem_t *sem) +{ + if (*sem == SYS_SEM_NULL) + return 0; + else + return 1; +} + +/*-----------------------------------------------------------------------------------*/ +void sys_sem_set_invalid(sys_sem_t *sem) +{ + *sem = SYS_SEM_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts* sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} +/*-----------------------------------------------------------------------------------*/ + /* Mutexes*/ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_COMPAT_MUTEX == 0 +/* Create a new mutex*/ +err_t sys_mutex_new(sys_mutex_t *mutex) { + + *mutex = xSemaphoreCreateMutex(); + if(*mutex == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.mutex.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + +#if SYS_STATS + ++lwip_stats.sys.mutex.used; + if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) { + lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used; + } +#endif /* SYS_STATS */ + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* Deallocate a mutex*/ +void sys_mutex_free(sys_mutex_t *mutex) +{ +#if SYS_STATS + --lwip_stats.sys.mutex.used; +#endif /* SYS_STATS */ + + vQueueDelete(*mutex); +} +/*-----------------------------------------------------------------------------------*/ +/* Lock a mutex*/ +void sys_mutex_lock(sys_mutex_t *mutex) +{ + sys_arch_sem_wait(*mutex, 0); +} + +/*-----------------------------------------------------------------------------------*/ +/* Unlock a mutex*/ +void sys_mutex_unlock(sys_mutex_t *mutex) +{ + xSemaphoreGive(*mutex); +} +#endif /*LWIP_COMPAT_MUTEX*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( signed portCHAR * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.c b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.c new file mode 100644 index 0000000..f3b74ee --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.c @@ -0,0 +1,366 @@ +/** + * @file + * Ethernet Interface for standalone applications (without RTOS) - works only for + * ethernet polling mode (polling for ethernet frame reception) + * + */ + +/* + * 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/mem.h" +#include "netif/etharp.h" +#include "ethernetif.h" +#include "stm32f2x7_eth.h" +#include "main.h" +#include + +/* Network interface name */ +#define IFNAME0 's' +#define IFNAME1 't' + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Driver Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Driver Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void low_level_init(struct netif *netif) +{ +#ifdef CHECKSUM_BY_HARDWARE + int i; +#endif + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + +#ifdef CHECKSUM_BY_HARDWARE + /* Enable the TCP, UDP and ICMP checksum insertion for the Tx frames */ + for(i=0; iBuffer1Addr); + __IO ETH_DMADESCTypeDef *DmaTxDesc; + uint16_t framelength = 0; + uint32_t bufferoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t payloadoffset = 0; + + DmaTxDesc = DMATxDescToSet; + bufferoffset = 0; + + /* copy frame from pbufs to driver buffers */ + for(q = p; q != NULL; q = q->next) + { + /* Is this buffer available? If not, goto error */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + errval = ERR_BUF; + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) + { + /* Copy data to Tx buffer*/ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + errval = ERR_USE; + goto error; + } + + buffer = (u8 *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy ); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Note: padding and CRC for transmitted frame + are automatically inserted by DMA */ + + /* Prepare transmit descriptors to give to DMA*/ + ETH_Prepare_Transmit_Descriptors(framelength); + + errval = ERR_OK; + +error: + + /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ + if ((ETH->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) + { + /* Clear TUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_TUS; + + /* Resume DMA transmission*/ + ETH->DMATPDR = 0; + } + return errval; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + uint32_t len; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxDesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i=0; + + /* get received frame */ + frame = ETH_Get_Received_Frame(); + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) + { + DMARxDesc = frame.descriptor; + bufferoffset = 0; + for(q = p; q != NULL; q = q->next) + { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) + { + /* Copy data to pbuf*/ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + buffer = (unsigned char *)(DMARxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + /* Copy remaining data in pbuf */ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + } + + /* Release descriptors to DMA */ + DMARxDesc =frame.descriptor; + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxDesc->Status = ETH_DMARxDesc_OWN; + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +err_t ethernetif_input(struct netif *netif) +{ + err_t err; + struct pbuf *p; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + + /* no packet could be read, silently ignore this */ + if (p == NULL) return ERR_MEM; + + /* entry point to the LwIP stack */ + err = netif->input(p, netif); + + if (err != ERR_OK) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + } + return err; +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.h b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.h new file mode 100644 index 0000000..9ff1408 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/port/stm32f2x7/standalone/ethernetif.h @@ -0,0 +1,11 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +err_t ethernetif_init(struct netif *netif); +err_t ethernetif_input(struct netif *netif); + +#endif diff --git a/component/common/network/lwip/lwip_v1.4.1/src/FILES b/component/common/network/lwip/lwip_v1.4.1/src/FILES new file mode 100644 index 0000000..952aeab --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/FILES @@ -0,0 +1,13 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here, + as well as the ARP module. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/component/common/network/lwip/lwip_v1.4.1/src/api/api_lib.c b/component/common/network/lwip/lwip_v1.4.1/src/api/api_lib.c new file mode 100644 index 0000000..38e49a4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/api/api_lib.c @@ -0,0 +1,792 @@ +/** + * @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 aborted */ + NETCONN_SET_SAFE_ERR(conn, ERR_ABRT); + return ERR_ABRT; + } +#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 (LWIP_UDP || LWIP_RAW) + if (conn->type == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + 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 (LWIP_UDP || LWIP_RAW) + if (conn->type == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + 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; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ + { +#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 + * @param bytes_written pointer to a location that receives the number of written bytes + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) +{ + struct api_msg msg; + err_t err; + u8_t dontblock; + + 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; + } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + if (dontblock && !bytes_written) { + /* This implies netconn_write() cannot be used for non-blocking send, since + it has no way to return the number of bytes written. */ + return ERR_VAL; + } + + /* non-blocking write sends as much */ + 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; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + msg.msg.msg.w.time_started = sys_now(); + } else { + msg.msg.msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + + /* 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); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock +#if LWIP_SO_SNDTIMEO + || (conn->send_timeout != 0) +#endif /* LWIP_SO_SNDTIMEO */ + ) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = msg.msg.msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } + + 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*/ + +//Realtek add +err_t netconn_abort(struct netconn *conn) +{ + if (conn->acceptmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->acceptmbox, NULL); + } + return ERR_OK; +} +//Realtek add end + +#endif /* LWIP_NETCONN */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/api/api_msg.c b/component/common/network/lwip/lwip_v1.4.1/src/api/api_msg.c new file mode 100644 index 0000000..ffcdc43 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/api/api_msg.c @@ -0,0 +1,1565 @@ +/** + * @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! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 4); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ + 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 = 0; + + 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); + goto free_and_return; + } +#endif + + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + goto free_and_return; + } + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + sys_sem_free(&conn->op_completed); + goto free_and_return; + } + +#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_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ +#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; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; +} + +/** + * 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 (close) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx); + } + if (err == ERR_OK) { + /* Closing succeeded */ + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (close) { + /* 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! */ + 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) { + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + } + 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; + } + } + } + } else { + msg->err = ERR_ARG; + } + } + } + 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; + 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); + +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + 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 (dontblock){ + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + 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 OK or memory error, check available space */ + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((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) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if ((err == ERR_MEM) && !dontblock) { + /* 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; + conn->current_msg->msg.w.len = 0; + } + } + 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/component/common/network/lwip/lwip_v1.4.1/src/api/err.c b/component/common/network/lwip/lwip_v1.4.1/src/api/err.c new file mode 100644 index 0000000..92fa8b7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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 */ + "Address in use.", /* ERR_USE -8 */ + "Already connected.", /* ERR_ISCONN -9 */ + "Connection aborted.", /* ERR_ABRT -10 */ + "Connection reset.", /* ERR_RST -11 */ + "Connection closed.", /* ERR_CLSD -12 */ + "Not connected.", /* ERR_CONN -13 */ + "Illegal argument.", /* ERR_ARG -14 */ + "Low-level netif error.", /* ERR_IF -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/component/common/network/lwip/lwip_v1.4.1/src/api/netbuf.c b/component/common/network/lwip/lwip_v1.4.1/src/api/netbuf.c new file mode 100644 index 0000000..9390c9e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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! + * + * @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(). + * + * @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. + * + * @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 + * + * @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/component/common/network/lwip/lwip_v1.4.1/src/api/netdb.c b/component/common/network/lwip/lwip_v1.4.1/src/api/netdb.c new file mode 100644 index 0000000..6a4bac5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/api/netdb.c @@ -0,0 +1,353 @@ +/** + * @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 *addr_list[2]; + 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 == NULL)) { + /* 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 = HOST_NOT_FOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; + h->aliases = NULL; + ret->h_name = hostname; + ret->h_aliases = &h->aliases; + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&h->addr_list; + + /* 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/component/common/network/lwip/lwip_v1.4.1/src/api/netifapi.c b/component/common/network/lwip/lwip_v1.4.1/src/api/netifapi.c new file mode 100644 index 0000000..43e4720 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/api/sockets.c b/component/common/network/lwip/lwip_v1.4.1/src/api/sockets.c new file mode 100644 index 0000000..2f8765b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/api/sockets.c @@ -0,0 +1,2444 @@ +/** + * @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. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ISCONN -9 Already connected. */ + ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */ + ECONNRESET, /* ERR_RST -11 Connection reset. */ + ENOTCONN, /* ERR_CLSD -12 Connection closed. */ + ENOTCONN, /* ERR_CONN -13 Not connected. */ + EIO, /* ERR_ARG -14 Illegal argument. */ + -1, /* ERR_IF -15 Low-level netif error */ +}; + +#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)); + if (netconn_type(sock->conn) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + 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)); + if (netconn_type(sock->conn) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + 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; + size_t written; + + 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) */ + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)written : -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 = &sock->conn->pcb.ip->remote_ip; +#if LWIP_UDP + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { + remote_port = sock->conn->pcb.udp->remote_port; + } else +#endif /* LWIP_UDP */ + { + remote_port = 0; + } + } + + LOCK_TCPIP_CORE(); + if (netconn_type(sock->conn) == NETCONN_RAW) { +#if LWIP_RAW + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, remote_addr); +#else /* LWIP_RAW */ + err = ERR_ARG; +#endif /* LWIP_RAW */ + } +#if LWIP_UDP && LWIP_RAW + else +#endif /* LWIP_UDP && LWIP_RAW */ + { +#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=%"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: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_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 = ip_get_option(sock->conn->pcb.ip, 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_SNDTIMEO + case SO_SNDTIMEO: + *(int *)optval = netconn_get_sendtimeout(sock->conn); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#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: */ +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: +#endif /* LWIP_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) { + ip_set_option(sock->conn->pcb.ip, optname); + } else { + ip_reset_option(sock->conn->pcb.ip, 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_SNDTIMEO + case SO_SNDTIMEO: + netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#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; +} + +/************************************************************** +* Added by Realtek Begin * +**************************************************************/ +int lwip_allocsocketsd() +{ + struct netconn *conn; + int i; + + /*new a netconn due to avoid some socket->conn check*/ + conn = netconn_new_with_proto_and_callback(NETCONN_RAW, 0, NULL); + if (!conn) { + printf("\r\n could not create netconn"); + return -1; + } + + /*alloc a socket*/ + i = alloc_socket(conn, 1); + if (i == -1) { + netconn_delete(conn); + printf("\r\n alloc socket fail!"); + return -1; + } + + conn->socket = i; + return i; +} +void lwip_setsockrcvevent(int fd, int rcvevent) +{ + struct lwip_sock *sock = get_socket(fd); + + if(sock){ + if(rcvevent) + sock->rcvevent = 1; + else + sock->rcvevent = 0; + } +} +void lwip_selectevindicate(int fd) +{ + struct lwip_select_cb *scb; + struct lwip_sock *sock; + + sock = get_socket(fd); + SYS_ARCH_DECL_PROTECT(lev); + while (1) { + SYS_ARCH_PROTECT(lev); + for (scb = select_cb_list; scb; scb = scb->next) { + if (scb->sem_signalled == 0) { + /* Test this select call for our socket */ + if (scb->readset && FD_ISSET(fd, scb->readset)) + if (sock->rcvevent > 0) + break; + if (scb->writeset && FD_ISSET(fd, scb->writeset)) + if (sock->sendevent) + break; + } + } + if (scb) { + scb->sem_signalled = 1; + sys_sem_signal(&scb->sem); + SYS_ARCH_UNPROTECT(lev); + } else { + SYS_ARCH_UNPROTECT(lev); + break; + } + } +} +/************************************************************** +* Added by Realtek end * +**************************************************************/ + +#endif /* LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/api/tcpip.c b/component/common/network/lwip/lwip_v1.4.1/src/api/tcpip.c new file mode 100644 index 0000000..18d5f67 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/api/tcpip.c @@ -0,0 +1,511 @@ +/** + * @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: + 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)) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ETHERNET */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + 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 */ + +#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 */ + + 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; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + + 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)) { + return ERR_VAL; + } + 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; +#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 */ + +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + return sys_mbox_trypost(&mbox, msg); +} + +/** + * 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/component/common/network/lwip/lwip_v1.4.1/src/core/def.c b/component/common/network/lwip/lwip_v1.4.1/src/core/def.c new file mode 100644 index 0000000..352b552 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/core/dhcp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/dhcp.c new file mode 100644 index 0000000..d51bd03 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/dhcp.c @@ -0,0 +1,1827 @@ +/** + * @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/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + +/** 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]; + +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + +#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); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ +/* 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 +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 +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 +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 +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 + dhcp_option_hostname(dhcp, netif); +#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 +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 +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 +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 +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 +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 +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 +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 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 +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; + } + ip_set_option(dhcp->pcb, 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 +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; + ip_set_option(dhcp.pcb, 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 +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 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 +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 +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 +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. */ + 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(0xff000000UL)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); + } + } + + 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(0x00000001UL)); + } + +#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))); + 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 +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 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 + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* 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 +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 + dhcp_option_hostname(dhcp, netif); +#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 +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 +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")); + + /* 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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release_unicast(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_RELEASING); + /* clean old DHCP offer *//* + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0;*/ + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif,netif->dhcp,DHCP_REQUEST); //Evan modified + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); + + 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(netif->dhcp);//Evan modified + 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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +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 +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 +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 +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 +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 +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)); +} + +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + u8_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +/** + * 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 +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_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + 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); + if (!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_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + 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_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + 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) { + 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 +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 +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 */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ +#else + 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))); + + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + xid = LWIP_RAND(); +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + } + 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) ? 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 +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 +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)) { + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/dns.c b/component/common/network/lwip/lwip_v1.4.1/src/core/dns.c new file mode 100644 index 0000000..d633612 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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 +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 +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 +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 +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 +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 +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 +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 +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 +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 * +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 +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 +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 +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 +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 +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 +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/component/common/network/lwip/lwip_v1.4.1/src/core/init.c b/component/common/network/lwip/lwip_v1.4.1/src/core/init.c new file mode 100644 index 0000000..8aea49d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/init.c @@ -0,0 +1,332 @@ +/** + * @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/lwip_timers.h" +#include "netif/etharp.h" +#include "lwip/api.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_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 !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#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_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_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 +/* 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 +#endif /* !MEMP_MEM_MALLOC */ +#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_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) + #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 +#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 (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 +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_ACCEPTCONN != SOF_ACCEPTCONN + #error "SO_ACCEPTCONN != SOF_ACCEPTCONN" +#endif +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#if SO_LINGER != SOF_LINGER + #error "WARNING: SO_LINGER != SOF_LINGER" +#endif +#endif /* LWIP_SOCKET */ + + +/* 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 + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "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. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ +#endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + 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 + 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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/autoip.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/autoip.c new file mode 100644 index 0000000..b122da2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/autoip.c @@ -0,0 +1,528 @@ +/** + * @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); + + +/** 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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/icmp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/icmp.c new file mode 100644 index 0000000..47ba857 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/icmp.c @@ -0,0 +1,339 @@ +/** + * @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 "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); +#if CHECKSUM_GEN_ICMP + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* 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 +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 */ + 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; + 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; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + 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; + 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); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/igmp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/igmp.c new file mode 100644 index 0000000..45bb5d9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/igmp.c @@ -0,0 +1,805 @@ +/** + * @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 0x9404U +#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); +static err_t igmp_remove_group(struct igmp_group *group); +static void igmp_timeout( struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); +static void igmp_send(struct igmp_group *group, u8_t type); + + +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; +} + +/** + * 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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet.c new file mode 100644 index 0000000..e283a57 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet_chksum.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet_chksum.c new file mode 100644 index 0000000..960252f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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 +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 +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 +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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip.c new file mode 100644 index 0000000..95d2db4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip.c @@ -0,0 +1,924 @@ +/** + * @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(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; + +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + /* 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_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; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = ip4_addr_get_u32(ip_current_dest_addr()); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == (IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * 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 +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + if (!ip_canforward(p)) { + goto return_noroute; + } + + /* 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))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* 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; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* 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(0xffffU - 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"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } + /* 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; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* 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_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); +#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()")); + 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_SET_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, 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; + + 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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_addr.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_addr.c new file mode 100644 index 0000000..8f633ff --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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 = 1UL << 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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_frag.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_frag.c new file mode 100644 index 0000000..8d18434 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * 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* +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 +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 +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* +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 +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 +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/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/README b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/README new file mode 100644 index 0000000..3620004 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/icmp6.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/icmp6.c new file mode 100644 index 0000000..4fcc895 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/icmp6.c @@ -0,0 +1,179 @@ +/* + * 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.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" + +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + + ICMP_STATS_INC(icmp.recv); + + /* TODO: check length before accessing payload! */ + + type = ((u8_t *)p->payload)[0]; + + switch (type) { + case ICMP6_ECHO: + 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")); + + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + return; + } + iecho = p->payload; + iphdr = (struct ip_hdr *)((u8_t *)p->payload - IP_HLEN); + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.chkerr); + /* return;*/ + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %"S16_F" p->tot_len %"S16_F"\n", p->len, p->tot_len)); + ip_addr_set(&tmpaddr, &(iphdr->src)); + ip_addr_set(&(iphdr->src), &(iphdr->dest)); + ip_addr_set(&(iphdr->dest), &tmpaddr); + iecho->type = ICMP6_ER; + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) { + iecho->chksum += htons(ICMP6_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP6_ECHO << 8); + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%"X16_F")\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len))); + ICMP_STATS_INC(icmp.xmit); + + /* LWIP_DEBUGF("icmp: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ + ip_output_if (p, &(iphdr->src), IP_HDRINCL, + iphdr->hoplim, IP_PROTO_ICMP, inp); + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" not supported.\n", (s16_t)type)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + + pbuf_free(p); +} + +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + idur = q->payload; + idur->type = (u8_t)ICMP6_DUR; + idur->icode = (u8_t)t; + + SMEMCPY((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); + ICMP_STATS_INC(icmp.xmit); + + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n")); + + /* @todo: can this be PBUF_LINK instead of PBUF_IP? */ + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_dest_unreach: failed to allocate pbuf for ICMP packet.\n")); + pbuf_free(p); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp message", + (q->len >= (8 + IP_HLEN + 8))); + + iphdr = p->payload; + + tehdr = q->payload; + tehdr->type = (u8_t)ICMP6_TE; + tehdr->icode = (u8_t)t; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); + ICMP_STATS_INC(icmp.xmit); + ip_output(q, NULL, + (struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/inet6.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/inet6.c new file mode 100644 index 0000000..c3de85c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/inet6.c @@ -0,0 +1,163 @@ +/** + * @file + * Functions common to all TCP/IPv6 modules, such as the Internet checksum and 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/def.h" +#include "lwip/inet.h" + +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ + +static u32_t +chksum(void *dataptr, u16_t len) +{ + u16_t *sdataptr = dataptr; + u32_t acc; + + + for(acc = 0; len > 1; len -= 2) { + acc += *sdataptr++; + } + + /* add up any odd byte */ + if (len == 1) { + acc += htons((u16_t)(*(u8_t *)dataptr) << 8); + } + + return acc; + +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ + +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped, i; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + + for(i = 0; i < 8; i++) { + acc += ((u16_t *)src->addr)[i] & 0xffff; + acc += ((u16_t *)dest->addr)[i] & 0xffff; + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + } + acc += (u16_t)htons((u16_t)proto); + acc += ((u16_t *)&proto_len)[0] & 0xffff; + acc += ((u16_t *)&proto_len)[1] & 0xffff; + + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc, sum; + + acc = chksum(dataptr, len); + sum = (acc & 0xffff) + (acc >> 16); + sum += (sum >> 16); + return ~(sum & 0xffff); +} + +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 += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6.c new file mode 100644 index 0000000..ad59b72 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6.c @@ -0,0 +1,397 @@ +/* + * 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 + * + */ + + + +/* ip.c + * + * This is the code for the IP layer for IPv6. + * + */ + + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" + +/* ip_init: + * + * Initializes the IP layer. + */ + +void +ip_init(void) +{ +} + +/* ip_route: + * + * 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. + */ + +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + return netif; + } + } + + return netif_default; +} + +/* 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. + */ + +static void +ip_forward(struct pbuf *p, struct ip_hdr *iphdr) +{ + struct netif *netif; + + PERF_START; + + if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { + + LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + pbuf_free(p); + return; + } + /* Decrement TTL and send ICMP if ttl == 0. */ + if (--iphdr->hoplim == 0) { +#if LWIP_ICMP + /* Don't send ICMP messages in response to ICMP messages */ + if (iphdr->nexthdr != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + } +#endif /* LWIP_ICMP */ + pbuf_free(p); + return; + } + + /* Incremental update of the IP checksum. */ + /* if (iphdr->chksum >= htons(0xffff - 0x100)) { + iphdr->chksum += htons(0x100) + 1; + } else { + iphdr->chksum += htons(0x100); + }*/ + + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to ")); +#if IP_DEBUG + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); +#endif /* IP_DEBUG */ + LWIP_DEBUGF(IP_DEBUG, ("\n")); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + + PERF_STOP("ip_forward"); + + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); +} + +/* ip_input: + * + * 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. + */ + +void +ip_input(struct pbuf *p, struct netif *inp) { + struct ip_hdr *iphdr; + struct netif *netif; + + + PERF_START; + +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + + IP_STATS_INC(ip.recv); + + /* identify the IP header */ + iphdr = p->payload; + + + if (iphdr->v != 6) { + LWIP_DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number\n")); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + return; + } + + /* is this packet for us? */ + for(netif = netif_list; netif != NULL; netif = netif->next) { +#if IP_DEBUG + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("netif->ip_addr ")); + ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("\n")); +#endif /* IP_DEBUG */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) { + break; + } + } + + + if (netif == NULL) { + /* packet not for us, route or discard */ +#if IP_FORWARD + ip_forward(p, iphdr); +#endif + pbuf_free(p); + return; + } + + pbuf_realloc(p, IP_HLEN + ntohs(iphdr->len)); + + /* send to upper layers */ +#if IP_DEBUG + /* LWIP_DEBUGF("ip_input: \n"); + ip_debug_print(p); + LWIP_DEBUGF("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len);*/ +#endif /* IP_DEBUG */ + + if(pbuf_header(p, -IP_HLEN)) { + LWIP_ASSERT("Can't move over header in packet", 0); + return; + } + + switch (iphdr->nexthdr) { + case IP_PROTO_UDP: + udp_input(p, inp); + break; + case IP_PROTO_TCP: + tcp_input(p, inp); + break; +#if LWIP_ICMP + case IP_PROTO_ICMP: + icmp_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable */ + icmp_dest_unreach(p, ICMP_DUR_PROTO); +#endif /* LWIP_ICMP */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG, ("Unsupported transport protocol %"U16_F"\n", + iphdr->nexthdr)); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + } + PERF_STOP("ip_input"); +} + + +/* ip_output_if: + * + * 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. + */ + +err_t +ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, + u8_t proto, struct netif *netif) +{ + struct ip_hdr *iphdr; + + PERF_START; + + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: not enough room for IP header in pbuf\n")); + IP_STATS_INC(ip.err); + + return ERR_BUF; + } + LWIP_DEBUGF(IP_DEBUG, ("len %"U16_F" tot_len %"U16_F"\n", p->len, p->tot_len)); + + iphdr = p->payload; + + + if (dest != IP_HDRINCL) { + LWIP_DEBUGF(IP_DEBUG, ("!IP_HDRLINCL\n")); + iphdr->hoplim = ttl; + iphdr->nexthdr = proto; + iphdr->len = htons(p->tot_len - IP_HLEN); + ip_addr_set(&(iphdr->dest), dest); + + iphdr->v = 6; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + } else { + dest = &(iphdr->dest); + } + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c (len %"U16_F")\n", netif->name[0], netif->name[1], p->tot_len)); +#if IP_DEBUG + ip_debug_print(p); +#endif /* IP_DEBUG */ + + PERF_STOP("ip_output_if"); + return netif->output(netif, p, dest); +} + +/* ip_output: + * + * Simple interface to ip_output_if. It finds the outgoing network interface and + * calls upon ip_output_if to do the actual work. + */ + +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto) +{ + struct netif *netif; + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + return ip_output_if (p, src, dest, ttl, proto, netif); +} + +#if LWIP_NETIF_HWADDRHINT +err_t +ip_output_hinted(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint) +{ + struct netif *netif; + err_t err; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + IP_STATS_INC(ip.rterr); + return ERR_RTE; + } + + LWIP_NETIF_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + LWIP_NETIF_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if IP_DEBUG +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" | %"X16_F"%"X16_F" | %"X16_F"%"X16_F" | (v, traffic class, flow label)\n", + iphdr->v, + iphdr->tclass1, iphdr->tclass2, + iphdr->flow1, iphdr->flow2)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" | %2"U16_F" | %2"U16_F" | (len, nexthdr, hoplim)\n", + ntohs(iphdr->len), + iphdr->nexthdr, + iphdr->hoplim)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (src)\n", + (ntohl(iphdr->src.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->src.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[0]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[0]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[1]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[1]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[2]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[2]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("| %4"X32_F" | %4"X32_F" | (dest)\n", + (ntohl(iphdr->dest.addr[3]) >> 16) & 0xffff, + ntohl(iphdr->dest.addr[3]) & 0xffff)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6_addr.c b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..2da6cea --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/ipv6/ip6_addr.c @@ -0,0 +1,72 @@ +/* + * 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/inet.h" + +u8_t +ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask) +{ + return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) && + (addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) && + (addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) && + (addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3])); + +} + +u8_t +ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2) +{ + return(addr1->addr[0] == addr2->addr[0] && + addr1->addr[1] == addr2->addr[1] && + addr1->addr[2] == addr2->addr[2] && + addr1->addr[3] == addr2->addr[3]); +} + +void +ip_addr_set(struct ip_addr *dest, struct ip_addr *src) +{ + SMEMCPY(dest, src, sizeof(struct ip_addr)); + /* dest->addr[0] = src->addr[0]; + dest->addr[1] = src->addr[1]; + dest->addr[2] = src->addr[2]; + dest->addr[3] = src->addr[3];*/ +} + +u8_t +ip_addr_isany(struct ip_addr *addr) +{ + if (addr == NULL) return 1; + return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0); +} diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/lwip_timers.c b/component/common/network/lwip/lwip_v1.4.1/src/core/lwip_timers.c new file mode 100644 index 0000000..cf7cb03 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/lwip_timers.c @@ -0,0 +1,487 @@ +/** + * @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/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" +#include "lwip/sys.h" +#include "lwip/pbuf.h" + + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout; +#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 +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); + } +} +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +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 +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 + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_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 NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_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 + +/** 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) +{ + if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout && (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 = sys_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/component/common/network/lwip/lwip_v1.4.1/src/core/mem.c b/component/common/network/lwip/lwip_v1.4.1/src/core/mem.c new file mode 100644 index 0000000..1659a2c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/mem.c @@ -0,0 +1,659 @@ +/** + * @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) +{ + void *ret; + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_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 */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + + return ret; +} + +/** + * 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; + + 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 = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + 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; +}; + +/** 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 */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +#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 */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#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 +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 or mem_trim */ + 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 or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#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 LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + 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/component/common/network/lwip/lwip_v1.4.1/src/core/memp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/memp.c new file mode 100644 index 0000000..24a12b1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/memp.c @@ -0,0 +1,470 @@ +/** + * @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/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] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#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) (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] = { +#define LWIP_MEMPOOL(name,num,size,desc) (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) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; +#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) 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) + ( (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, using "Floyd's cycle-finding algorithm". + */ +static int +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) "/"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 +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 +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 +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 +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 = 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 */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/netif.c b/component/common/network/lwip/lwip_v1.4.1/src/core/netif.c new file mode 100644 index 0000000..4a02e77 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/netif.c @@ -0,0 +1,774 @@ +/** + * @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; + +static u8_t netif_num; + +#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 +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) +{ + + 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 = netif_num++; + netif->input = input; + NETIF_SET_HWADDRHINT(netif, NULL); +#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); + } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + 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 */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_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/component/common/network/lwip/lwip_v1.4.1/src/core/pbuf.c b/component/common/network/lwip/lwip_v1.4.1/src/core/pbuf.c new file mode 100644 index 0000000..1e5e53b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/pbuf.c @@ -0,0 +1,1179 @@ +/** + * @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 LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#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 || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * 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. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 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; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * 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 */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + 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; + /* 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. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @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 */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + 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 = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(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) { + 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 */ + 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); + + 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) { + 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 + { + /* 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); + 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 (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + 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/component/common/network/lwip/lwip_v1.4.1/src/core/raw.c b/component/common/network/lwip/lwip_v1.4.1/src/core/raw.c new file mode 100644 index 0000000..7160c0f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/raw.c @@ -0,0 +1,350 @@ +/** + * @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 +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 (ip_get_option(pcb, 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 +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 +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 +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 +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 (!ip_get_option(pcb, SOF_BROADCAST) && 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); + } + + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* 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 +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 +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 * +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/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_dec.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_dec.c new file mode 100644 index 0000000..1d56582 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, upto 64k + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_enc.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_enc.c new file mode 100644 index 0000000..64dfc5f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = (u8_t)length; + } + else + { + /* most significant length octet */ + *msg_ptr = (u8_t)(length >> 8); + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]); + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib2.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib2.c new file mode 100644 index 0000000..4775ba9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib2.c @@ -0,0 +1,4146 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "lwip/sys.h" +#include "netif/etharp.h" + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10)) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node*)&udp_root, (struct mib_node*)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node*)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 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, 26 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root, + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable, + (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable, + (struct mib_node*)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node*)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node*)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node*)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node*)&sys_tem, + (struct mib_node*)&interfaces, + (struct mib_node*)&at, + (struct mib_node*)&mib2_ip, + (struct mib_node*)&icmp, +#if LWIP_TCP + (struct mib_node*)&tcp, +#endif + (struct mib_node*)&udp, + (struct mib_node*)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#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. */ +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default; +static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + + +/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */ +/** + * Copy octet string. + * + * @param dst points to destination + * @param src points to source + * @param n number of octets to copy. + */ +static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n) +{ + u16_t i = n; + while (i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Copy object identifier (s32_t) array. + * + * @param dst points to destination + * @param src points to source + * @param n number of sub identifiers to copy. + */ +void objectidncpy(s32_t *dst, s32_t *src, u8_t n) +{ + u8_t i = n; + while(i > 0) { + i--; + *dst++ = *src++; + } +} + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdesr(u8_t *str, u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid) +{ + *oid = &sysobjid; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(struct snmp_obj_id *oid) +{ + sysobjid = *oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + insert = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + insert = 1; + } + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t del = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + del = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + del = 1; + } + } + if (del) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(&pcb->local_ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct udp_pcb *npcb; + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(&pcb->local_ip, &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + npcb = udp_pcbs; + while ((npcb != NULL)) + { + if (ip_addr_cmp(&npcb->local_ip, &pcb->local_ip) && + (npcb->local_port == udpidx[4])) + { + bindings++; + } + npcb = npcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid.len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + ocstrncpy((u8_t*)value, sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t))); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime((u32_t*)value); + } + break; + case 4: /* sysContact */ + ocstrncpy((u8_t*)value, syscontact_ptr, len); + break; + case 5: /* sysName */ + ocstrncpy((u8_t*)value, sysname_ptr, len); + break; + case 6: /* sysLocation */ + ocstrncpy((u8_t*)value, syslocation_ptr, len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = sysservices; + } + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid len", len <= 0xff); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + ocstrncpy(syscontact_ptr, (u8_t*)value, len); + *syscontact_len_ptr = (u8_t)len; + break; + case 5: /* sysName */ + ocstrncpy(sysname_ptr, (u8_t*)value, len); + *sysname_len_ptr = (u8_t)len; + break; + case 6: /* sysLocation */ + ocstrncpy(syslocation_ptr, (u8_t*)value, len); + *syslocation_len_ptr = (u8_t)len; + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + ocstrncpy((u8_t*)value, (u8_t*)netif->name, len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + ocstrncpy((u8_t*)value, netif->hwaddr, len); + break; + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t))); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + LWIP_UNUSED_ARG(len); + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + LWIP_UNUSED_ARG(len); + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = iproutingdiscards; + } + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + ip_addr_t ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = (s32_t*)value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = IPADDR_BROADCAST & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + ip_addr_t dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + + if (ip_addr_isany(&dest)) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has 0.0.0.0 dest */ + ip_addr_set_zero(dst); + } + else + { + /* netifs have netaddress dest */ + ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask); + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = (s32_t*)value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = (s32_t*)value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = (s32_t*)value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte use 0.0.0.0 mask */ + ip_addr_set_zero(dst); + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t))); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = (s32_t*)value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + ip_addr_t lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + ip_addr_t ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff)); + port = (u16_t)od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !(ip_addr_cmp(&pcb->local_ip, &ip) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = pcb->local_ip; + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = pcb->local_port; + } + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */ + u8_t *ptr = (u8_t*)value; + *snmpenableauthentraps_ptr = *ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib_structs.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib_structs.c new file mode 100644 index 0000000..2f185cb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/mib_structs.c @@ -0,0 +1,1174 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/memp.h" +#include "lwip/netif.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + s32_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, ip_addr_t *ip) +{ + IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]); +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(ip_addr_t *ip, s32_t *ident) +{ + ident[0] = ip4_addr1(ip); + ident[1] = ip4_addr2(ip); + ident[2] = ip4_addr3(ip); + ident[3] = ip4_addr4(ip); +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + memp_free(MEMP_SNMP_NODE, ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + memp_free(MEMP_SNMP_ROOTNODE, lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (return) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u8_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + struct nse cur_node; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u8_t j; + struct nse cur_node; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = (u8_t)i + 1; + if (j < len) + { + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + } + else + { + cur_node.r_ptr = NULL; + } + push_node(&cur_node); + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_in.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_in.c new file mode 100644 index 0000000..be940c6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_in.c @@ -0,0 +1,1453 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/memp.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + +#ifdef SNMP_PRIVATE_MIB_INIT + /* If defined, this must be a function-like define to initialize the + * private MIB after the stack has been initialized. + * The private MIB can also be initialized in tcpip_callback (or after + * the stack is initialized), this define is only for convenience. */ + SNMP_PRIVATE_MIB_INIT(); +#endif /* SNMP_PRIVATE_MIB_INIT */ + + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + /* move names back from outvb to invb */ + int v; + struct snmp_varbind *vbi = msg_ps->invb.head; + struct snmp_varbind *vbo = msg_ps->outvb.head; + for (v=0; vvb_idx; v++) { + vbi->ident_len = vbo->ident_len; + vbo->ident_len = 0; + vbi->ident = vbo->ident; + vbo->ident = NULL; + vbi = vbi->next; + vbo = vbo->next; + } + /* free outvb */ + snmp_varbind_list_free(&msg_ps->outvb); + /* we send invb back */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + /* error index must be 0 for error too big */ + msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) && + (msg_ps->ext_object_def.access & MIB_ACCESS_READ)) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb->value_len = (u8_t)msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if ((object_def.instance != MIB_OBJECT_NONE) && + (object_def.access & MIB_ACCESS_READ)) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is refereced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb->value_len = (u8_t)object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", + vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + vb->ident = NULL; + vb->ident_len = 0; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + (u8_t)msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the assosicated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access & MIB_ACCESS_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + err_t err_ret; + u16_t payload_len = p->tot_len; + u16_t payload_ofs = 0; + u16_t varbind_ofs = 0; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx == SNMP_CONCURRENT_REQUESTS) + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + return; + } + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + /* Only accept requests and requests without error (be robust) */ + /* Reject response and trap headers or error requests as input! */ + if ((err_ret != ERR_OK) || + ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) || + ((msg_ps->error_status != SNMP_ES_NOERROR) || + (msg_ps->error_index != 0)) ) + { + /* header check failed drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + return; + } + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0)) + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + return; + } + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = (u8_t)len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = (s32_t*)vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = (u32_t*)vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + LWIP_ASSERT("invalid length", len <= 0xff); + vb = snmp_varbind_alloc(&oid, type, (u8_t)len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = (s32_t*)vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH); + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE); + if (vb->ident == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + /* allocate raw bytes for our object value */ + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); + if (vb->ident != NULL) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->value); + } + if (vb->ident != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_out.c b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_out.c new file mode 100644 index 0000000..485f076 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/snmp/msg_out.c @@ -0,0 +1,674 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + + switch (m_stat->error_status) + { + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterpise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + ip_addr_t dst_ip; + struct pbuf *p; + u16_t i,tot_len; + + for (i=0, td = &trap_dst[0]; ienable != 0) && !ip_addr_isany(&td->dip)) + { + /* network order trap destination */ + ip_addr_copy(trap_msg.dip, td->dip); + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + ip_addr_copy(dst_ip, dst_if->ip_addr); + /* @todo: what about IPv6? */ + trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); + trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); + trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); + trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** send to the TRAP destination */ + udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); + + pbuf_free(p); + } + else + { + return ERR_MEM; + } + } + } + return ERR_OK; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required lenght for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required lenght for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required lenght for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = (u8_t*)vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/stats.c b/component/common/network/lwip/lwip_v1.4.1/src/core/stats.c new file mode 100644 index 0000000..8ea8249 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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, const 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, const 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/component/common/network/lwip/lwip_v1.4.1/src/core/sys.c b/component/common/network/lwip/lwip_v1.4.1/src/core/sys.c new file mode 100644 index 0000000..f177737 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/sys.c @@ -0,0 +1,68 @@ +/** + * @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 + +#ifndef sys_msleep +/** + * 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 /* sys_msleep */ + +#endif /* !NO_SYS */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/core/tcp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp.c new file mode 100644 index 0000000..b710d2e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp.c @@ -0,0 +1,1742 @@ +/** + * @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 + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +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" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* 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; + +u8_t tcp_active_pcbs_changed; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * 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 == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + 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); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, 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. */ + err = ERR_OK; + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = 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; + } + 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); + } + 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 + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG + 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 unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. + * + * @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: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + 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, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + 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; +#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; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + TCP_PCB_REMOVE_ACTIVE(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, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port); + } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + } +} + +/** + * 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_VAL if bind failed because the PCB is not in a valid state + * 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_VAL); + +#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 (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } + + /* 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 (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + 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. + * + * @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 (ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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; + ip_set_option(lpcb, 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); + 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; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + 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)); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_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 == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @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 connect 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; + } 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 (pcb->local_port == 0) { + return ERR_BUF; + } + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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; +#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 */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(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; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* 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); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + 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]; + } + + /* 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 this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + 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(ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + 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; + } + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + 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_err_fn err_fn; + void *err_arg; + 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); + } + + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } 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_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* 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_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while(pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (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); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; + 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, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_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) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* 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")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ + pcb->refused_data = refused_data; + } + return ERR_OK; +} + +/** + * 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 the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +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 +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. + * + * @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) { + /* 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)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_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 = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / 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->last_timer = tcp_timer_ctr; + + 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. + * + * @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) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @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) +{ + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @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) +{ + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + * @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) +{ + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @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) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + 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. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#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/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_in.c b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_in.c new file mode 100644 index 0000000..4ec971a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_in.c @@ -0,0 +1,1619 @@ +/** + * @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; +static struct tcp_hdr *tcphdr; +static struct ip_hdr *iphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * 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 + */ +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); + snmp_inc_tcpinsegs(); + + iphdr = (struct ip_hdr *)p->payload; + 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); + goto dropped; + } + + /* 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); + goto dropped; + } + +#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); + goto dropped; + } +#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); + if(pbuf_header(p, -(hdrlen * 4))){ + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + + /* 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); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* 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; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + 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) { + 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); + 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) { + 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); + 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 (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + goto aborted; + } + } + 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); + 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); + 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; + } + + /* 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) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* 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); + 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_rst(ackno, seqno + tcplen, + ip_current_dest_addr(), ip_current_src_addr(), + tcphdr->dest, tcphdr->src); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); +} + +/** + * 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 + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + + /* 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, seqno + tcplen, ip_current_dest_addr(), + ip_current_src_addr(), tcphdr->dest, tcphdr->src); + } else if (flags & TCP_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 */ + 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); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ + 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->snd_wnd_max = 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_ACTIVE(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 + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + 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) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + 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_wnd_max = 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; + tcp_seg_free(rseg); + + /* 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; + } + + /* 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_ACTIVE(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_ACTIVE(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_ACTIVE(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 +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 + * 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 TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + 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; + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < tcphdr->wnd) { + pcb->snd_wnd_max = tcphdr->wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + 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) + * + * 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)) { + 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 ((u8_t)(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) { + /* 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)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* 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. */ + 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->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); + tcp_seg_free(next); + + 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. */ + 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->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + 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); + 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)) { + /* 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); + + 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 */ + 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 unless the pcb already received a FIN. + (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* 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)){ + /* 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.*/ + + 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)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + 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)){ + 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) { + 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); + 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; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#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/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_out.c b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_out.c new file mode 100644 index 0000000..ee19fe0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_out.c @@ -0,0 +1,1485 @@ +/** + * @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/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#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 * +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); + pcb->flags |= TF_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 * +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 * +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 = max_length; +#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 +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 +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 : + * - 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; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); + +#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 = mss_local - (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 = mss_local - 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, mss_local, &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 + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + 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 +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 +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 */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* 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); + } + + /* 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); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + 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 { + 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 */ + + 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 */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->remote_ip); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + 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. */ + 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; + /* last unsent hasn't changed, no need to reset unsent_oversize */ + + /* 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; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + ++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 */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + +#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/component/common/network/lwip/lwip_v1.4.1/src/core/udp.c b/component/common/network/lwip/lwip_v1.4.1/src/core/udp.c new file mode 100644 index 0000000..32c7d38 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/core/udp.c @@ -0,0 +1,1013 @@ +/** + * @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 + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +#if 0 + struct udp_pcb *ipcb = udp_pcbs; + while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == udp_port) { + /* port is already used by another udp_pcb */ + udp_port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + return 0; + } + return udp_port; +#endif +} + +/** + * 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 +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) { + if ( + (!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 && ip_get_option(pcb, SOF_BROADCAST) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_netcmp(&pcb->local_ip, ip_current_dest_addr(), &inp->netmask)))) { +#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)) && + ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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 && ip_get_option(mpcb, 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 +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 +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 +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 +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 +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 +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 (!ip_get_option(pcb, SOF_BROADCAST) && 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")); + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + } 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 */ + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + } + /* 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 +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 (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { +#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) { + port = udp_new_port(); + if (port == 0) { + /* 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 +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 +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 +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 +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 * +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 +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/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/autoip.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/autoip.h new file mode 100644 index 0000000..e62b72e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/autoip.h @@ -0,0 +1,118 @@ +/** + * @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 */ +}; + + +#define autoip_init() /* Compatibility define, no init needed. */ + +/** 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/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/icmp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/icmp.h new file mode 100644 index 0000000..d47a7d8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/icmp.h @@ -0,0 +1,111 @@ +/* + * 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. + */ +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 + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#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); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ICMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/igmp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/igmp.h new file mode 100644 index 0000000..8aabac2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/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); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest); +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +void igmp_tmr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* __LWIP_IGMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet.h new file mode 100644 index 0000000..7bff49b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/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/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet_chksum.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/inet_chksum.h new file mode 100644 index 0000000..79a2d90 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/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); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + ip_addr_t *src, ip_addr_t *dest, + u8_t proto, u16_t proto_len); +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); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip.h new file mode 100644 index 0000000..00c83a0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip.h @@ -0,0 +1,223 @@ +/* + * 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 0x01U Unimplemented: turn on debugging info recording */ +#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ +/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */ +#define SOF_LINGER 0x80U /* linger on close if data present */ +/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */ +/*#define SOF_REUSEPORT 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 */ + PACK_STRUCT_FIELD(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FIELD(u8_t _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 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* dont fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* 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) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#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_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (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); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +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 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); +#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); +#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) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_addr.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_addr.h new file mode 100644 index 0000000..77f84e0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_addr.h @@ -0,0 +1,244 @@ +/* + * 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); + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#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); +int ipaddr_aton(const char *cp, ip_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr); +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_frag.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/lwip/ip_frag.h new file mode 100644 index 0000000..77b5eb1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4/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); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#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); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_FRAG_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/icmp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/icmp.h new file mode 100644 index 0000000..87e9ffd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/icmp.h @@ -0,0 +1,100 @@ +/* + * 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" + +#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ICMP6_DUR 1 +#define ICMP6_TE 3 +#define ICMP6_ECHO 128 /* echo */ +#define ICMP6_ER 129 /* echo 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 */ +}; + +void icmp_input(struct pbuf *p, struct netif *inp); + +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +struct icmp_echo_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u16_t id; + u16_t seqno; +}; + +struct icmp_dur_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +struct icmp_te_hdr { + u8_t type; + u8_t icode; + u16_t chksum; + u32_t unused; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ICMP */ + +#endif /* __LWIP_ICMP_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/inet.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/inet.h new file mode 100644 index 0000000..de1a0b6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/inet.h @@ -0,0 +1,68 @@ +/* + * 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/pbuf.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +u16_t inet_chksum(void *data, u16_t len); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len); + +u32_t inet_addr(const char *cp); +s8_t inet_aton(const char *cp, struct in_addr *addr); + +#ifndef _MACHINE_ENDIAN_H_ +#ifndef _NETINET_IN_H +#ifndef _LINUX_BYTEORDER_GENERIC_H +u16_t htons(u16_t n); +u16_t ntohs(u16_t n); +u32_t htonl(u32_t n); +u32_t ntohl(u32_t n); +#endif /* _LINUX_BYTEORDER_GENERIC_H */ +#endif /* _NETINET_IN_H */ +#endif /* _MACHINE_ENDIAN_H_ */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INET_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip.h new file mode 100644 index 0000000..a01cfc6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip.h @@ -0,0 +1,130 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_HLEN 40 + +#define IP_PROTO_ICMP 58 +#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 struct ip_addr local_ip; \ + struct ip_addr remote_ip; \ + /* Socket options */ \ + u16_t so_options; \ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ + u8_t ttl; \ + /* link layer address resolution hint */ \ + IP_PCB_ADDRHINT + + +/* The IPv6 header. */ +struct ip_hdr { +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t tclass1:4, v:4; + u8_t flow1:4, tclass2:4; +#else + u8_t v:4, tclass1:4; + u8_t tclass2:8, flow1:4; +#endif + u16_t flow2; + u16_t len; /* payload length */ + u8_t nexthdr; /* next header */ + u8_t hoplim; /* hop limit (TTL) */ + struct ip_addr src, dest; /* source and destination IP addresses */ +}; + +#define IPH_PROTO(hdr) (iphdr->nexthdr) + +void ip_init(void); + +#include "lwip/netif.h" + +struct netif *ip_route(struct ip_addr *dest); + +void ip_input(struct pbuf *p, struct netif *inp); + +/* source and destination addresses in network byte order, please */ +err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto); + +err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t proto, + struct netif *netif); + +#define ip_current_netif() NULL +#define ip_current_header() NULL + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_H__ */ + + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip_addr.h b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip_addr.h new file mode 100644 index 0000000..b2d8ae5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/ipv6/lwip/ip_addr.h @@ -0,0 +1,97 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP_ADDR_ANY 0 + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN + struct ip_addr { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* + * 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 + +#define IP6_ADDR(ipaddr, a,b,c,d,e,f,g,h) do { (ipaddr)->addr[0] = htonl((u32_t)((a & 0xffff) << 16) | (b & 0xffff)); \ + (ipaddr)->addr[1] = htonl(((c & 0xffff) << 16) | (d & 0xffff)); \ + (ipaddr)->addr[2] = htonl(((e & 0xffff) << 16) | (f & 0xffff)); \ + (ipaddr)->addr[3] = htonl(((g & 0xffff) << 16) | (h & 0xffff)); } while(0) + +u8_t ip_addr_netcmp(struct ip_addr *addr1, struct ip_addr *addr2, + struct ip_addr *mask); +u8_t ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2); +void ip_addr_set(struct ip_addr *dest, struct ip_addr *src); +u8_t ip_addr_isany(struct ip_addr *addr); + +#define ip_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F":%"X32_F"\n", \ + (ntohl(ipaddr->addr[0]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[0]) & 0xffff, \ + (ntohl(ipaddr->addr[1]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[1]) & 0xffff, \ + (ntohl(ipaddr->addr[2]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[2]) & 0xffff, \ + (ntohl(ipaddr->addr[3]) >> 16) & 0xffff, \ + ntohl(ipaddr->addr[3]) & 0xffff)); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_IP_ADDR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api.h new file mode 100644 index 0000000..5cfcfb1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api.h @@ -0,0 +1,305 @@ +/* + * 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_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 + +// Evan add for port netconn api +#define port_netconn_recv(conn , buf, ret) do{ret = netconn_recv(conn, &buf);}while(0); +#define port_netconn_accept(conn , newconn, ret) do{ret = netconn_accept(conn, &newconn);}while(0); +// Evan add end + +/* 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_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#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_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +err_t netconn_abort(struct netconn *conn);//Realtek add + +#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_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api_msg.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api_msg.h new file mode 100644 index 0000000..f9e1c7d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/api_msg.h @@ -0,0 +1,177 @@ +/* + * 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; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } 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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/arch.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/arch.h new file mode 100644 index 0000000..4d6df77 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/arch.h @@ -0,0 +1,217 @@ +/* + * 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 */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ARCH_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/debug.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/debug.h new file mode 100644 index 0000000..5a0e042 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/debug.h @@ -0,0 +1,102 @@ +/* + * 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" +#include "lwip/opt.h" +#include //Realtek add +/** 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_PLATFORM_DIAG printf //Realtek add +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/def.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/def.h new file mode 100644 index 0000000..73a1b56 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/def.h @@ -0,0 +1,123 @@ +/* + * 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 + +/* 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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dhcp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dhcp.h new file mode 100644 index 0000000..296f520 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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; + ip_addr_t offered_bc_addr;//Evan add for used old version lwip api in lwip_netconf.c + + 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? */ +#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 +#define DHCP_RELEASING 11 //Realtek modified +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dns.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/dns.h new file mode 100644 index 0000000..6c7d9b0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/err.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/err.h new file mode 100644 index 0000000..ac90772 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/err.h @@ -0,0 +1,85 @@ +/* + * 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_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) + +#define ERR_ABRT -10 /* Connection aborted. */ +#define ERR_RST -11 /* Connection reset. */ +#define ERR_CLSD -12 /* Connection closed. */ +#define ERR_CONN -13 /* Not connected. */ + +#define ERR_ARG -14 /* Illegal argument. */ + +#define ERR_IF -15 /* Low-level netif error */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_ERR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/init.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/init.h new file mode 100644 index 0000000..3238534 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * 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 1U +/** 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 255U + +/** 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); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_INIT_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/lwip_timers.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/lwip_timers.h new file mode 100644 index 0000000..dbb9dbc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/lwip_timers.h @@ -0,0 +1,108 @@ +/* + * 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" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#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); + +//Realtek add +struct sys_timeouts { + struct sys_timeo *next; +}; + +struct sys_timeouts *sys_arch_timeouts(void); +//Realtek add end + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#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); +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg); +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/mem.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/mem.h new file mode 100644 index 0000000..5bb906b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/mem.h @@ -0,0 +1,123 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +/* 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 free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#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); +void *mem_trim(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp.h new file mode 100644 index 0000000..f0d0739 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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) 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); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_MEMP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp_std.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp_std.h new file mode 100644 index 0000000..461ed1a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/memp_std.h @@ -0,0 +1,122 @@ +/* + * 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) +#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) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#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") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#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") +#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 LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#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") +#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") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * 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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netbuf.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netbuf.h new file mode 100644 index 0000000..7d247d7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); +void netbuf_delete (struct netbuf *buf); +void * netbuf_alloc (struct netbuf *buf, u16_t size); +void netbuf_free (struct netbuf *buf); +err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +s8_t netbuf_next (struct netbuf *buf); +void netbuf_first (struct netbuf *buf); + + +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netdb.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netdb.h new file mode 100644 index 0000000..7587e2f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netif.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netif.h new file mode 100644 index 0000000..f7e4937 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netif.h @@ -0,0 +1,328 @@ +/* + * 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. */ + 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. */ + 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 */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_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) */ + 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. */ + 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); + +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); + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw); +void netif_remove(struct netif * netif); + +/* 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); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask); +void netif_set_gw(struct netif *netif, ip_addr_t *gw); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** 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); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** 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); +#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); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_NETIF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netifapi.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/netifapi.h new file mode 100644 index 0000000..33318ef --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/opt.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/opt.h new file mode 100644 index 0000000..e387f31 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/opt.h @@ -0,0 +1,2134 @@ +/** + * @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 0 +#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) 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) + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT) +#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 8 +#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. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#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 1 +#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 + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 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 1 //Realtek modified (0->1) +#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). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#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 LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * 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_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 +#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. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#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_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_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 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 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 "TCP_IP" //Realtek modified ("tcpip_thread"->"TCP_IP") +#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 1 +#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 1 +#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_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 1 //Realtek modified(0->1) +#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 +#define ETHARP_STATS 0 //Realtek add + +#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_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 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 + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + +/* + --------------------------------------- + ---------- 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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/pbuf.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/pbuf.h new file mode 100644 index 0000000..99d5443 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/pbuf.h @@ -0,0 +1,178 @@ +/* + * 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 */ +} 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 +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U + +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; +}; + +#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 */ + +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** 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 NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + +/* 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); +#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); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#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); +#endif /* LWIP_CHECKSUM_ON_COPY */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset); +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* __LWIP_PBUF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/raw.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/raw.h new file mode 100644 index 0000000..17d0a1c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); +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); +#define raw_init() /* Compatibility define, not init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* __LWIP_RAW_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sio.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sio.h new file mode 100644 index 0000000..28ae2f2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); +#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); +#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); +#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); +#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); +#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); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __SIO_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp.h new file mode 100644 index 0000000..2ed043d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_asn1.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_asn1.h new file mode 100644 index 0000000..605fa3f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_msg.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_msg.h new file mode 100644 index 0000000..1183e3a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_structs.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/snmp_structs.h new file mode 100644 index 0000000..0d3b46a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sockets.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sockets.h new file mode 100644 index 0000000..3ea32f1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sockets.h @@ -0,0 +1,379 @@ +/* + * 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]; +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_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) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* __LWIP_SOCKETS_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/stats.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/stats.h new file mode 100644 index 0000000..1f5152a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); + +#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); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sys.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/sys.h new file mode 100644 index 0000000..dc93513 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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" + +#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_sem_valid(s) 0 +#define sys_sem_set_invalid(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_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(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_mbox_valid(m) +#define sys_mbox_set_invalid(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); + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void); +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +u32_t sys_now(void); + +/* 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); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp.h new file mode 100644 index 0000000..415945c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp.h @@ -0,0 +1,383 @@ +/* + * 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; + +//Evan add +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ +//Evan add end + +/** 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 */ \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* 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 */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* 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 */ + + /* 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 */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + u16_t cwnd; + u16_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + 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 snd_wnd; /* sender window */ + u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + u16_t acked; + + u16_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-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 */ + u8_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); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); + +#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); +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* 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); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp_impl.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp_impl.h new file mode 100644 index 0000000..985c54e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcp_impl.h @@ -0,0 +1,493 @@ +/* + * 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/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: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* 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); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * 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))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 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)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(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 250 /* 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 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* 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_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#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 */ + 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(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* 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 */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#if LWIP_TCP_DELAY_DISABLE +#define tcp_ack(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) +#else +#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) +#endif + +#define tcp_ack_now(pcb) \ + do { \ + (pcb)->flags |= TF_ACK_NOW; \ + } while (0) + +err_t tcp_send_fin(struct tcp_pcb *pcb); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +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); + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss(u16_t sendmss, ip_addr_t *addr); +#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); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#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); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* __LWIP_TCP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcpip.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcpip.h new file mode 100644 index 0000000..08aa67a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/tcpip.h @@ -0,0 +1,167 @@ +/* + * 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/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); + +/* Forward declarations */ +struct tcpip_callback_msg; + +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) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* 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, + TCPIP_MSG_CALLBACK_STATIC +}; + +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/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/udp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/lwip/udp.h new file mode 100644 index 0000000..f1e6d3f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#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); +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); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +#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); + +void udp_init (void); + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* __LWIP_UDP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/netif/etharp.h b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/etharp.h new file mode 100644 index 0000000..859608d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/etharp.h @@ -0,0 +1,222 @@ +/* + * 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 prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} 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_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) + +/** 5 seconds period */ +#define ARP_TMR_INTERVAL 5000 + +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ + +/** 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); +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); +/** 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); +err_t etharp_remove_static_entry(ip_addr_t *ipaddr); +#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); +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#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 */ + +#ifdef __cplusplus +} +#endif + +#endif /* __NETIF_ARP_H__ */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/netif/loopif.h b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/loopif.h new file mode 100644 index 0000000..e69de29 diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/netif/ppp_oe.h b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/ppp_oe.h new file mode 100644 index 0000000..e1cdfa5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/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/component/common/network/lwip/lwip_v1.4.1/src/include/netif/slipif.h b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/slipif.h new file mode 100644 index 0000000..7b6ce5e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/netif/slipif.h @@ -0,0 +1,81 @@ +/* + * 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 __NETIF_SLIPIF_H__ +#define __NETIF_SLIPIF_H__ + +#include "lwip/opt.h" +#include "lwip/netif.h" + +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serila line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/posix/netdb.h b/component/common/network/lwip/lwip_v1.4.1/src/include/posix/netdb.h new file mode 100644 index 0000000..7134032 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * 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/netdb.h" diff --git a/component/common/network/lwip/lwip_v1.4.1/src/include/posix/sys/socket.h b/component/common/network/lwip/lwip_v1.4.1/src/include/posix/sys/socket.h new file mode 100644 index 0000000..f7c7066 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * 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/sockets.h" diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/FILES b/component/common/network/lwip/lwip_v1.4.1/src/netif/FILES new file mode 100644 index 0000000..099dbf3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/FILES @@ -0,0 +1,29 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The PPP stack has been ported from ucip (http://ucip.sourceforge.net). + It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although + compared to that, it has some modifications for embedded systems and + the source code has been reordered a bit. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/etharp.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/etharp.c new file mode 100644 index 0000000..5e382d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/etharp.c @@ -0,0 +1,1399 @@ +/** + * @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}}; + +/** The 24-bit IANA multicast OUI is 01-00-5e: */ +#define LL_MULTICAST_ADDR_0 0x01 +#define LL_MULTICAST_ADDR_1 0x00 +#define LL_MULTICAST_ADDR_2 0x5e + +#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 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12) + +/** 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, + ETHARP_STATE_STABLE_REREQUESTING +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +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 netif *netif; + struct eth_addr ethaddr; + u8_t state; + u8_t ctime; +}; + +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 +#if ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#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 */ + + +/* 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 +etharp_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_free_entry: 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; +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; + arp_table[i].netif = NULL; + 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 + && (state != ETHARP_STATE_STATIC) +#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 */ + etharp_free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } +#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 +etharp_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, ("etharp_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, ("etharp_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 (state < ETHARP_STATE_STATIC) +#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, ("etharp_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, ("etharp_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, ("etharp_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, ("etharp_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 etharp_free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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, ("etharp_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); + etharp_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; + 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 +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 +etharp_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, ("etharp_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, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = etharp_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].state = ETHARP_STATE_STATIC; + } else +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + + /* record network interface */ + arp_table[i].netif = netif; + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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 etharp_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 = etharp_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_STATIC) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + etharp_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 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)) { + etharp_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 = etharp_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 +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 == PP_HTONS(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. */ + etharp_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 +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 */ + + 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 == PP_HTONS(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))) { + 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")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen)); + 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 */ + etharp_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 */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* 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); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + +/** + * 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; + struct eth_addr mcastaddr; + ip_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + + /* 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; + } + + /* 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] = LL_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; + 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 { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + 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 */ + dst_addr = &(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(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, 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 = etharp_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 +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 */ + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* 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; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + 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 */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef ETHARP_VLAN_CHECK_FN + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ + 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*/ + + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { + if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } + } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + 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/component/common/network/lwip/lwip_v1.4.1/src/netif/ethernetif.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ethernetif.c new file mode 100644 index 0000000..8ec40be --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ethernetif.c @@ -0,0 +1,317 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include +#include +#include "netif/etharp.h" +#include "netif/ppp_oe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.c new file mode 100644 index 0000000..0fd87a3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.c @@ -0,0 +1,1334 @@ +/***************************************************************************** +* auth.c - Network Authentication and Phase Control program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Ported from public pppd code. +*****************************************************************************/ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "lcp.h" +#include "pap.h" +#include "chap.h" +#include "auth.h" +#include "ipcp.h" + +#if CBCP_SUPPORT +#include "cbcp.h" +#endif /* CBCP_SUPPORT */ + +#include "lwip/inet.h" + +#include + +#if 0 /* UNUSED */ +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) +#endif /* UNUSED */ + +#if PAP_SUPPORT || CHAP_SUPPORT +/* The name by which the peer authenticated itself to us. */ +static char peer_authname[MAXNAMELEN]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* Records which authentication operations haven't completed yet. */ +static int auth_pending[NUM_PPP]; + +/* Set if we have successfully called plogin() */ +static int logged_in; + +/* Set if we have run the /etc/ppp/auth-up script. */ +static int did_authup; /* @todo, we don't need this in lwip*/ + +/* List of addresses which the peer may use. */ +static struct wordlist *addresses[NUM_PPP]; + +#if 0 /* UNUSED */ +/* Wordlist giving addresses which the peer may use + without authenticating itself. */ +static struct wordlist *noauth_addrs; + +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; +#endif /* UNUSED */ + +/* Number of network protocols which we have opened. */ +static int num_np_open; + +/* Number of network protocols which have come up. */ +static int num_np_up; + +#if PAP_SUPPORT || CHAP_SUPPORT +/* Set if we got the contents of passwd[] from the pap-secrets file. */ +static int passwd_from_file; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* Set if we require authentication only because we have a default route. */ +static bool default_auth; + +/* Hook to enable a plugin to control the idle time limit */ +int (*idle_time_hook) __P((struct ppp_idle *)) = NULL; + +/* Hook for a plugin to say whether we can possibly authenticate any peer */ +int (*pap_check_hook) __P((void)) = NULL; + +/* Hook for a plugin to check the PAP user and password */ +int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp, + struct wordlist **paddrs, + struct wordlist **popts)) = NULL; + +/* Hook for a plugin to know about the PAP user logout */ +void (*pap_logout_hook) __P((void)) = NULL; + +/* Hook for a plugin to get the PAP password for authenticating us */ +int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL; + +/* + * This is used to ensure that we don't start an auth-up/down + * script while one is already running. + */ +enum script_state { + s_down, + s_up +}; + +static enum script_state auth_state = s_down; +static enum script_state auth_script_state = s_down; +static pid_t auth_script_pid = 0; + +/* + * Option variables. + * lwip: some of these are present in the ppp_settings structure + */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ +bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +bool usehostname = 0; /* Use hostname for our_name */ +bool auth_required = 0; /* Always require authentication from peer */ +bool allow_any_ip = 0; /* Allow peer to use any IP address */ +bool explicit_remote = 0; /* User specified explicit remote name */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ + +#endif /* UNUSED */ + +/* Bits in auth_pending[] */ +#define PAP_WITHPEER 1 +#define PAP_PEER 2 +#define CHAP_WITHPEER 4 +#define CHAP_PEER 8 + +/* @todo, move this somewhere */ +/* Used for storing a sequence of words. Usually malloced. */ +struct wordlist { + struct wordlist *next; + char word[1]; +}; + + +extern char *crypt (const char *, const char *); + +/* Prototypes for procedures local to this file. */ + +static void network_phase (int); +static void check_idle (void *); +static void connect_time_expired (void *); +#if 0 +static int plogin (char *, char *, char **, int *); +#endif +static void plogout (void); +static int null_login (int); +static int get_pap_passwd (int, char *, char *); +static int have_pap_secret (void); +static int have_chap_secret (char *, char *, u32_t); +static int ip_addr_check (u32_t, struct wordlist *); + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +static int scan_authfile (FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *); +static void free_wordlist (struct wordlist *); +static void auth_script (char *); +static void auth_script_done (void *); +static void set_allowed_addrs (int unit, struct wordlist *addrs); +static int some_ip_ok (struct wordlist *); +static int setupapfile (char **); +static int privgroup (char **); +static int set_noauth_addr (char **); +static void check_access (FILE *, char *); +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * Authentication-related options. + */ +option_t auth_options[] = { + { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", 1, &auth_required }, + { "refuse-pap", o_bool, &refuse_pap, + "Don't agree to auth to peer with PAP", 1 }, + { "-pap", o_bool, &refuse_pap, + "Don't allow PAP authentication with peer", 1 }, + { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "+chap", o_bool, &lcp_wantoptions[0].neg_chap, + "Require CHAP authentication from peer", 1, &auth_required }, + { "refuse-chap", o_bool, &refuse_chap, + "Don't agree to auth to peer with CHAP", 1 }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", 1 }, + { "name", o_string, our_name, + "Set local name for authentication", + OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN }, + { "user", o_string, user, + "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN }, + { "usehostname", o_bool, &usehostname, + "Must use hostname for authentication", 1 }, + { "remotename", o_string, remote_name, + "Set remote name for authentication", OPT_STATIC, + &explicit_remote, MAXNAMELEN }, + { "auth", o_bool, &auth_required, + "Require authentication from peer", 1 }, + { "noauth", o_bool, &auth_required, + "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip }, + { "login", o_bool, &uselogin, + "Use system password database for PAP", 1 }, + { "papcrypt", o_bool, &cryptpap, + "PAP passwords are encrypted", 1 }, + { "+ua", o_special, (void *)setupapfile, + "Get PAP user and password from file" }, + { "password", o_string, passwd, + "Password for authenticating us to the peer", OPT_STATIC, + NULL, MAXSECRETLEN }, + { "privgroup", o_special, (void *)privgroup, + "Allow group members to use privileged options", OPT_PRIV }, + { "allow-ip", o_special, (void *)set_noauth_addr, + "Set IP address(es) which can be used without authentication", + OPT_PRIV }, + { NULL } +}; +#endif /* UNUSED */ +#if 0 /* UNUSED */ +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(char **argv) +{ + FILE * ufile; + int l; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + seteuid(getuid()); + ufile = fopen(*argv, "r"); + seteuid(0); + if (ufile == NULL) { + option_error("unable to open user login data file %s", *argv); + return 0; + } + check_access(ufile, *argv); + + /* get username */ + if (fgets(user, MAXNAMELEN - 1, ufile) == NULL + || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){ + option_error("unable to read user login data file %s", *argv); + return 0; + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(user); + if (l > 0 && user[l-1] == '\n') + user[l-1] = 0; + l = strlen(passwd); + if (l > 0 && passwd[l-1] == '\n') + passwd[l-1] = 0; + + return (1); +} +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* + * privgroup - allow members of the group to have privileged access. + */ +static int +privgroup(char **argv) +{ + struct group *g; + int i; + + g = getgrnam(*argv); + if (g == 0) { + option_error("group %s is unknown", *argv); + return 0; + } + for (i = 0; i < ngroups; ++i) { + if (groups[i] == g->gr_gid) { + privileged = 1; + break; + } + } + return 1; +} +#endif + +#if 0 /* UNUSED */ +/* + * set_noauth_addr - set address(es) that can be used without authentication. + * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. + */ +static int +set_noauth_addr(char **argv) +{ + char *addr = *argv; + int l = strlen(addr); + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1); + if (wp == NULL) + novm("allow-ip argument"); + wp->word = (char *) (wp + 1); + wp->next = noauth_addrs; + BCOPY(addr, wp->word, l); + noauth_addrs = wp; + return 1; +} +#endif /* UNUSED */ + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + * Do what's necessary to bring the physical layer up. + */ +void +link_required(int unit) +{ + LWIP_UNUSED_ARG(unit); + + AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit)); +} + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void +link_terminated(int unit) +{ + AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit)); + if (lcp_phase[unit] == PHASE_DEAD) { + return; + } + if (logged_in) { + plogout(); + } + lcp_phase[unit] = PHASE_DEAD; + AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n")); + pppLinkTerminated(unit); +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void +link_down(int unit) +{ + int i; + struct protent *protp; + + AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit)); + + if (did_authup) { + /* XXX Do link down processing. */ + did_authup = 0; + } + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) { + continue; + } + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) { + (*protp->lowerdown)(unit); + } + if (protp->protocol < 0xC000 && protp->close != NULL) { + (*protp->close)(unit, "LCP down"); + } + } + num_np_open = 0; /* number of network protocols we have opened */ + num_np_up = 0; /* Number of network protocols which have come up */ + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + pppLinkDown(unit); +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void +link_established(int unit) +{ + int auth; + int i; + struct protent *protp; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *go = &lcp_gotoptions[unit]; +#if PAP_SUPPORT || CHAP_SUPPORT + lcp_options *ho = &lcp_hisoptions[unit]; +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit)); + /* + * Tell higher-level protocols that LCP is up. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) { + (*protp->lowerup)(unit); + } + } + if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) { + /* + * We wanted the peer to authenticate itself, and it refused: + * treat it as though it authenticated with PAP using a username + * of "" and a password of "". If that's not OK, boot it out. + */ + if (!wo->neg_upap || !null_login(unit)) { + AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n")); + lcp_close(unit, "peer refused to authenticate"); + return; + } + } + + lcp_phase[unit] = PHASE_AUTHENTICATE; + auth = 0; +#if CHAP_SUPPORT + if (go->neg_chap) { + ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype); + auth |= CHAP_PEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(unit); + auth |= PAP_PEER; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype); + auth |= CHAP_WITHPEER; + } +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT && CHAP_SUPPORT + else +#endif /* PAP_SUPPORT && CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + if (ppp_settings.passwd[0] == 0) { + passwd_from_file = 1; + if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) { + AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n")); + } + } + upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd); + auth |= PAP_WITHPEER; + } +#endif /* PAP_SUPPORT */ + auth_pending[unit] = auth; + + if (!auth) { + network_phase(unit); + } +} + +/* + * Proceed to the network phase. + */ +static void +network_phase(int unit) +{ + int i; + struct protent *protp; + lcp_options *go = &lcp_gotoptions[unit]; + + /* + * If the peer had to authenticate, run the auth-up script now. + */ + if ((go->neg_chap || go->neg_upap) && !did_authup) { + /* XXX Do setup for peer authentication. */ + did_authup = 1; + } + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + lcp_phase[unit] = PHASE_CALLBACK; + (*cbcp_protent.open)(unit); + return; + } +#endif /* CBCP_SUPPORT */ + + lcp_phase[unit] = PHASE_NETWORK; + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(unit); + if (protp->protocol != PPP_CCP) { + ++num_np_open; + } + } + } + + if (num_np_open == 0) { + /* nothing to do */ + lcp_close(0, "No network protocols running"); + } +} +/* @todo: add void start_networks(void) here (pppd 2.3.11) */ + +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void +auth_peer_fail(int unit, u16_t protocol) +{ + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol)); + /* + * Authentication failure: take the link down + */ + lcp_close(unit, "Authentication failed"); +} + + +#if PAP_SUPPORT || CHAP_SUPPORT +/* + * The peer has been successfully authenticated using `protocol'. + */ +void +auth_peer_success(int unit, u16_t protocol, char *name, int namelen) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_PEER; + break; + case PPP_PAP: + pbit = PAP_PEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + if (namelen > (int)sizeof(peer_authname) - 1) { + namelen = sizeof(peer_authname) - 1; + } + BCOPY(name, peer_authname, namelen); + peer_authname[namelen] = 0; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void +auth_withpeer_fail(int unit, u16_t protocol) +{ + int errCode = PPPERR_AUTHFAIL; + + LWIP_UNUSED_ARG(protocol); + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol)); + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + + /* + * We've failed to authenticate ourselves to our peer. + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ + pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode); + lcp_close(unit, "Failed to authenticate ourselves to peer"); +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void +auth_withpeer_success(int unit, u16_t protocol) +{ + int pbit; + + AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol)); + switch (protocol) { + case PPP_CHAP: + pbit = CHAP_WITHPEER; + break; + case PPP_PAP: + if (passwd_from_file) { + BZERO(ppp_settings.passwd, MAXSECRETLEN); + } + pbit = PAP_WITHPEER; + break; + default: + AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol)); + pbit = 0; + } + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((auth_pending[unit] &= ~pbit) == 0) { + network_phase(unit); + } +} +#endif /* PAP_SUPPORT || CHAP_SUPPORT */ + + +/* + * np_up - a network protocol has come up. + */ +void +np_up(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto)); + if (num_np_up == 0) { + AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit)); + /* + * At this point we consider that the link has come up successfully. + */ + if (ppp_settings.idle_time_limit > 0) { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit); + } + + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (ppp_settings.maxconnect > 0) { + TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect); + } + } + ++num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void +np_down(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto)); + if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) { + UNTIMEOUT(check_idle, NULL); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void +np_finished(int unit, u16_t proto) +{ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(proto); + + AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto)); + if (--num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(0, "No network protocols running"); + } +} + +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void +check_idle(void *arg) +{ + struct ppp_idle idle; + u_short itime; + + LWIP_UNUSED_ARG(arg); + if (!get_idle_time(0, &idle)) { + return; + } + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + if (itime >= ppp_settings.idle_time_limit) { + /* link is idle: shut it down. */ + AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n")); + lcp_close(0, "Link inactive"); + } else { + TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime); + } +} + +/* + * connect_time_expired - log a message and close the connection. + */ +static void +connect_time_expired(void *arg) +{ + LWIP_UNUSED_ARG(arg); + + AUTHDEBUG(LOG_INFO, ("Connect time expired\n")); + lcp_close(0, "Connect time expired"); /* Close connection */ +} + +#if 0 /* UNUSED */ +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options(void) +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + /* Default our_name to hostname, and user to our_name */ + if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) { + strcpy(ppp_settings.our_name, ppp_settings.hostname); + } + + if (ppp_settings.user[0] == 0) { + strcpy(ppp_settings.user, ppp_settings.our_name); + } + + /* If authentication is required, ask peer for CHAP or PAP. */ + if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) { + wo->neg_chap = 1; + wo->neg_upap = 1; + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. + */ + can_auth = wo->neg_upap && have_pap_secret(); + if (!can_auth && wo->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote); + } + + if (ppp_settings.auth_required && !can_auth) { + ppp_panic("No auth secret"); + } +} +#endif /* UNUSED */ + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void +auth_reset(int unit) +{ + lcp_options *go = &lcp_gotoptions[unit]; + lcp_options *ao = &lcp_allowoptions[0]; + ipcp_options *ipwo = &ipcp_wantoptions[0]; + u32_t remote; + + AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit)); + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL)); + ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/; + + if (go->neg_upap && !have_pap_secret()) { + go->neg_upap = 0; + } + if (go->neg_chap) { + remote = ipwo->accept_remote? 0: ipwo->hisaddr; + if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) { + go->neg_chap = 0; + } + } +} + +#if PAP_SUPPORT +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +u_char +check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen) +{ +#if 1 /* XXX Assume all entries OK. */ + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(auser); + LWIP_UNUSED_ARG(userlen); + LWIP_UNUSED_ARG(apasswd); + LWIP_UNUSED_ARG(passwdlen); + LWIP_UNUSED_ARG(msglen); + *msg = (char *) 0; + return UPAP_AUTHACK; /* XXX Assume all entries OK. */ +#else + u_char ret = 0; + struct wordlist *addrs = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static u_short attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + */ + BCOPY(apasswd, passwd, passwdlen); + passwd[passwdlen] = '\0'; + BCOPY(auser, user, userlen); + user[userlen] = '\0'; + *msg = (char *) 0; + + /* XXX Validate user name and password. */ + ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */ + + if (ret == UPAP_AUTHNAK) { + if (*msg == (char *) 0) { + *msg = "Login incorrect"; + } + *msglen = strlen(*msg); + /* + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user)); + /*ppp_panic("Excess Bad Logins");*/ + } + if (attempts > 3) { + /* @todo: this was sleep(), i.e. seconds, not milliseconds + * I don't think we really need this in lwIP - we would block tcpip_thread! + */ + /*sys_msleep((attempts - 3) * 5);*/ + } + if (addrs != NULL) { + free_wordlist(addrs); + } + } else { + attempts = 0; /* Reset count */ + if (*msg == (char *) 0) { + *msg = "Login ok"; + } + *msglen = strlen(*msg); + set_allowed_addrs(unit, addrs); + } + + BZERO(passwd, sizeof(passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +#endif +} +#endif /* PAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * This function is needed for PAM. + */ + +#ifdef USE_PAM + +/* lwip does not support PAM*/ + +#endif /* USE_PAM */ + +#endif /* UNUSED */ + + +#if 0 /* UNUSED */ +/* + * plogin - Check the user name and password against the system + * password database, and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Login failed. + * UPAP_AUTHACK: Login succeeded. + * In either case, msg points to an appropriate message. + */ +static int +plogin(char *user, char *passwd, char **msg, int *msglen) +{ + + LWIP_UNUSED_ARG(user); + LWIP_UNUSED_ARG(passwd); + LWIP_UNUSED_ARG(msg); + LWIP_UNUSED_ARG(msglen); + + + /* The new lines are here align the file when + * compared against the pppd 2.3.11 code */ + + + + + + + + + + + + + + + + + /* XXX Fail until we decide that we want to support logins. */ + return (UPAP_AUTHNAK); +} +#endif + + + +/* + * plogout - Logout the user. + */ +static void +plogout(void) +{ + logged_in = 0; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* XXX Fail until we decide that we want to support logins. */ + return 0; +} + + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + */ +static int +get_pap_passwd(int unit, char *user, char *passwd) +{ + LWIP_UNUSED_ARG(unit); +/* normally we would reject PAP if no password is provided, + but this causes problems with some providers (like CHT in Taiwan) + who incorrectly request PAP and expect a bogus/empty password, so + always provide a default user/passwd of "none"/"none" + + @todo: This should be configured by the user, instead of being hardcoded here! +*/ + if(user) { + strcpy(user, "none"); + } + if(passwd) { + strcpy(passwd, "none"); + } + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(void) +{ + /* XXX Fail until we set up our passwords. */ + return 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(char *client, char *server, u32_t remote) +{ + LWIP_UNUSED_ARG(client); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(remote); + + /* XXX Fail until we set up our passwords. */ + return 0; +} +#if CHAP_SUPPORT + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs) +{ +#if 1 + int len; + struct wordlist *addrs; + + LWIP_UNUSED_ARG(unit); + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(save_addrs); + + addrs = NULL; + + if(!client || !client[0] || strcmp(client, ppp_settings.user)) { + return 0; + } + + len = (int)strlen(ppp_settings.passwd); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(ppp_settings.passwd, secret, len); + *secret_len = len; + + return 1; +#else + int ret = 0, len; + struct wordlist *addrs; + char secbuf[MAXWORDLEN]; + + addrs = NULL; + secbuf[0] = 0; + + /* XXX Find secret. */ + if (ret < 0) { + return 0; + } + + if (save_addrs) { + set_allowed_addrs(unit, addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server)); + len = MAXSECRETLEN; + } + + BCOPY(secbuf, secret, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} +#endif /* CHAP_SUPPORT */ + + +#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * set_allowed_addrs() - set the list of allowed addresses. + */ +static void +set_allowed_addrs(int unit, struct wordlist *addrs) +{ + if (addresses[unit] != NULL) { + free_wordlist(addresses[unit]); + } + addresses[unit] = addrs; + +#if 0 + /* + * If there's only one authorized address we might as well + * ask our peer for that one right away + */ + if (addrs != NULL && addrs->next == NULL) { + char *p = addrs->word; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t a; + struct hostent *hp; + + if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) { + hp = gethostbyname(p); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + a = inet_addr(p); + } + if (a != (u32_t) -1) { + wo->hisaddr = a; + } + } + } +#endif +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(int unit, u32_t addr) +{ + return ip_addr_check(addr, addresses[unit]); +} + +static int /* @todo: integrate this funtion into auth_ip_addr()*/ +ip_addr_check(u32_t addr, struct wordlist *addrs) +{ + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) { + return 0; + } + + if (addrs == NULL) { + return !ppp_settings.auth_required; /* no addresses authorized */ + } + + /* XXX All other addresses allowed. */ + return 1; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(u32_t addr) +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */ +/* + * some_ip_ok - check a wordlist to see if it authorizes any + * IP address(es). + */ +static int +some_ip_ok(struct wordlist *addrs) +{ + for (; addrs != 0; addrs = addrs->next) { + if (addrs->word[0] == '-') + break; + if (addrs->word[0] != '!') + return 1; /* some IP address is allowed */ + } + return 0; +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +static void +check_access(FILE *f, char *filename) +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + warn("cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + warn("Warning - secret file %s has world and/or group access", + filename); + } +} + + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line up to a "--" (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. Any + * following words (extra options) are placed in a wordlist and + * returned in *opts. + * We assume secret is NULL or points to MAXWORDLEN bytes of space. + */ +static int +scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename) +{ + /* We do not (currently) need this in lwip */ + return 0; /* dummy */ +} +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(struct wordlist *wp) +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} + +/* + * auth_script_done - called when the auth-up or auth-down script + * has finished. + */ +static void +auth_script_done(void *arg) +{ + auth_script_pid = 0; + switch (auth_script_state) { + case s_up: + if (auth_state == s_down) { + auth_script_state = s_down; + auth_script(_PATH_AUTHDOWN); + } + break; + case s_down: + if (auth_state == s_up) { + auth_script_state = s_up; + auth_script(_PATH_AUTHUP); + } + break; + } +} + +/* + * auth_script - execute a script with arguments + * interface-name peer-name real-user tty speed + */ +static void +auth_script(char *script) +{ + char strspeed[32]; + struct passwd *pw; + char struid[32]; + char *user_name; + char *argv[8]; + + if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL) + user_name = pw->pw_name; + else { + slprintf(struid, sizeof(struid), "%d", getuid()); + user_name = struid; + } + slprintf(strspeed, sizeof(strspeed), "%d", baud_rate); + + argv[0] = script; + argv[1] = ifname; + argv[2] = peer_authname; + argv[3] = user_name; + argv[4] = devnam; + argv[5] = strspeed; + argv[6] = NULL; + + auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL); +} +#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */ +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.h new file mode 100644 index 0000000..a8069ec --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/auth.h @@ -0,0 +1,111 @@ +/***************************************************************************** +* auth.h - PPP Authentication and phase control header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD pppd.h. +*****************************************************************************/ +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef AUTH_H +#define AUTH_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* we are starting to use the link */ +void link_required (int); + +/* we are finished with the link */ +void link_terminated (int); + +/* the LCP layer has left the Opened state */ +void link_down (int); + +/* the link is up; authenticate now */ +void link_established (int); + +/* a network protocol has come up */ +void np_up (int, u16_t); + +/* a network protocol has gone down */ +void np_down (int, u16_t); + +/* a network protocol no longer needs link */ +void np_finished (int, u16_t); + +/* peer failed to authenticate itself */ +void auth_peer_fail (int, u16_t); + +/* peer successfully authenticated itself */ +void auth_peer_success (int, u16_t, char *, int); + +/* we failed to authenticate ourselves */ +void auth_withpeer_fail (int, u16_t); + +/* we successfully authenticated ourselves */ +void auth_withpeer_success (int, u16_t); + +/* check authentication options supplied */ +void auth_check_options (void); + +/* check what secrets we have */ +void auth_reset (int); + +/* Check peer-supplied username/password */ +u_char check_passwd (int, char *, int, char *, int, char **, int *); + +/* get "secret" for chap */ +int get_secret (int, char *, char *, char *, int *, int); + +/* check if IP address is authorized */ +int auth_ip_addr (int, u32_t); + +/* check if IP address is unreasonable */ +int bad_ip_adrs (u32_t); + +#endif /* AUTH_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.c new file mode 100644 index 0000000..f10e27d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.c @@ -0,0 +1,908 @@ +/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/ +/***************************************************************************** +* chap.c - Network Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap.c. +*****************************************************************************/ +/* + * chap.c - Challenge Handshake Authentication Protocol. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Gregory M. Christy. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "magic.h" +#include "randm.h" +#include "auth.h" +#include "md5.h" +#include "chap.h" +#include "chpms.h" + +#include + +#if 0 /* UNUSED */ +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap[0].timeouttime, + "Set timeout for CHAP" }, + { "chap-max-challenge", o_int, &chap[0].max_transmits, + "Set max #xmits for challenge" }, + { "chap-interval", o_int, &chap[0].chal_interval, + "Set interval for rechallenge" }, +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif + { NULL } +}; +#endif /* UNUSED */ + +/* + * Protocol entry points. + */ +static void ChapInit (int); +static void ChapLowerUp (int); +static void ChapLowerDown (int); +static void ChapInput (int, u_char *, int); +static void ChapProtocolReject (int); +#if PPP_ADDITIONAL_CALLBACKS +static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *); +#endif + +struct protent chap_protent = { + PPP_CHAP, + ChapInit, + ChapInput, + ChapProtocolReject, + ChapLowerUp, + ChapLowerDown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + ChapPrintPkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "CHAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */ + +static void ChapChallengeTimeout (void *); +static void ChapResponseTimeout (void *); +static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int); +static void ChapRechallenge (void *); +static void ChapReceiveResponse (chap_state *, u_char *, int, int); +static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len); +static void ChapSendStatus (chap_state *, int); +static void ChapSendChallenge (chap_state *); +static void ChapSendResponse (chap_state *); +static void ChapGenChallenge (chap_state *); + +/* + * ChapInit - Initialize a CHAP unit. + */ +static void +ChapInit(int unit) +{ + chap_state *cstate = &chap[unit]; + + BZERO(cstate, sizeof(*cstate)); + cstate->unit = unit; + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; + cstate->timeouttime = CHAP_DEFTIMEOUT; + cstate->max_transmits = CHAP_DEFTRANSMITS; + /* random number generator is initialized in magic_init */ +} + + +/* + * ChapAuthWithPeer - Authenticate us with our peer (start client). + * + */ +void +ChapAuthWithPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->resp_name = our_name; + cstate->resp_type = digest; + + if (cstate->clientstate == CHAPCS_INITIAL || + cstate->clientstate == CHAPCS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->clientstate = CHAPCS_PENDING; + return; + } + + /* + * We get here as a result of LCP coming up. + * So even if CHAP was open before, we will + * have to re-authenticate ourselves. + */ + cstate->clientstate = CHAPCS_LISTEN; +} + + +/* + * ChapAuthPeer - Authenticate our peer (start server). + */ +void +ChapAuthPeer(int unit, char *our_name, u_char digest) +{ + chap_state *cstate = &chap[unit]; + + cstate->chal_name = our_name; + cstate->chal_type = digest; + + if (cstate->serverstate == CHAPSS_INITIAL || + cstate->serverstate == CHAPSS_PENDING) { + /* lower layer isn't up - wait until later */ + cstate->serverstate = CHAPSS_PENDING; + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); /* crank it up dude! */ + cstate->serverstate = CHAPSS_INITIAL_CHAL; +} + + +/* + * ChapChallengeTimeout - Timeout expired on sending challenge. + */ +static void +ChapChallengeTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending challenges, don't worry. then again we */ + /* probably shouldn't be here either */ + if (cstate->serverstate != CHAPSS_INITIAL_CHAL && + cstate->serverstate != CHAPSS_RECHALLENGE) { + return; + } + + if (cstate->chal_transmits >= cstate->max_transmits) { + /* give up on peer */ + CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + return; + } + + ChapSendChallenge(cstate); /* Re-send challenge */ +} + + +/* + * ChapResponseTimeout - Timeout expired on sending response. + */ +static void +ChapResponseTimeout(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->clientstate != CHAPCS_RESPONSE) { + return; + } + + ChapSendResponse(cstate); /* re-send response */ +} + + +/* + * ChapRechallenge - Time to challenge the peer again. + */ +static void +ChapRechallenge(void *arg) +{ + chap_state *cstate = (chap_state *) arg; + + /* if we aren't sending a response, don't worry. */ + if (cstate->serverstate != CHAPSS_OPEN) { + return; + } + + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_RECHALLENGE; +} + + +/* + * ChapLowerUp - The lower layer is up. + * + * Start up if we have pending requests. + */ +static void +ChapLowerUp(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->clientstate == CHAPCS_INITIAL) { + cstate->clientstate = CHAPCS_CLOSED; + } else if (cstate->clientstate == CHAPCS_PENDING) { + cstate->clientstate = CHAPCS_LISTEN; + } + + if (cstate->serverstate == CHAPSS_INITIAL) { + cstate->serverstate = CHAPSS_CLOSED; + } else if (cstate->serverstate == CHAPSS_PENDING) { + ChapGenChallenge(cstate); + ChapSendChallenge(cstate); + cstate->serverstate = CHAPSS_INITIAL_CHAL; + } +} + + +/* + * ChapLowerDown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +ChapLowerDown(int unit) +{ + chap_state *cstate = &chap[unit]; + + /* Timeout(s) pending? Cancel if so. */ + if (cstate->serverstate == CHAPSS_INITIAL_CHAL || + cstate->serverstate == CHAPSS_RECHALLENGE) { + UNTIMEOUT(ChapChallengeTimeout, cstate); + } else if (cstate->serverstate == CHAPSS_OPEN + && cstate->chal_interval != 0) { + UNTIMEOUT(ChapRechallenge, cstate); + } + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + cstate->clientstate = CHAPCS_INITIAL; + cstate->serverstate = CHAPSS_INITIAL; +} + + +/* + * ChapProtocolReject - Peer doesn't grok CHAP. + */ +static void +ChapProtocolReject(int unit) +{ + chap_state *cstate = &chap[unit]; + + if (cstate->serverstate != CHAPSS_INITIAL && + cstate->serverstate != CHAPSS_CLOSED) { + auth_peer_fail(unit, PPP_CHAP); + } + if (cstate->clientstate != CHAPCS_INITIAL && + cstate->clientstate != CHAPCS_CLOSED) { + auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ + } + ChapLowerDown(unit); /* shutdown chap */ +} + + +/* + * ChapInput - Input CHAP packet. + */ +static void +ChapInput(int unit, u_char *inpacket, int packet_len) +{ + chap_state *cstate = &chap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (packet_len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < CHAP_HEADERLEN) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n")); + return; + } + if (len > packet_len) { + CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n")); + return; + } + len -= CHAP_HEADERLEN; + + /* + * Action depends on code (as in fact it usually does :-). + */ + switch (code) { + case CHAP_CHALLENGE: + ChapReceiveChallenge(cstate, inp, id, len); + break; + + case CHAP_RESPONSE: + ChapReceiveResponse(cstate, inp, id, len); + break; + + case CHAP_FAILURE: + ChapReceiveFailure(cstate, inp, id, len); + break; + + case CHAP_SUCCESS: + ChapReceiveSuccess(cstate, inp, id, len); + break; + + default: /* Need code reject? */ + CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code)); + break; + } +} + + +/* + * ChapReceiveChallenge - Receive Challenge and send Response. + */ +static void +ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len) +{ + int rchallenge_len; + u_char *rchallenge; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + MD5_CTX mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id)); + if (cstate->clientstate == CHAPCS_CLOSED || + cstate->clientstate == CHAPCS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n", + cstate->clientstate)); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + + GETCHAR(rchallenge_len, inp); + len -= sizeof (u_char) + rchallenge_len; /* now name field length */ + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n")); + return; + } + rchallenge = inp; + INCPTR(rchallenge_len, inp); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n", + rhostname)); + + /* Microsoft doesn't send their name back in the PPP packet */ + if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) { + strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname)); + rhostname[sizeof(rhostname) - 1] = 0; + CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n", + rhostname)); + } + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(cstate->unit, cstate->resp_name, rhostname, + secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n", + rhostname)); + } + + /* cancel response send timeout if necessary */ + if (cstate->clientstate == CHAPCS_RESPONSE) { + UNTIMEOUT(ChapResponseTimeout, cstate); + } + + cstate->resp_id = id; + cstate->resp_transmits = 0; + + /* generate MD based on negotiated type */ + switch (cstate->resp_type) { + + case CHAP_DIGEST_MD5: + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->resp_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, rchallenge, rchallenge_len); + MD5Final(hash, &mdContext); + BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE); + cstate->resp_length = MD5_SIGNATURE_SIZE; + break; + +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len); + break; +#endif + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type)); + return; + } + + BZERO(secret, sizeof(secret)); + ChapSendResponse(cstate); +} + + +/* + * ChapReceiveResponse - Receive and process response. + */ +static void +ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len) +{ + u_char *remmd, remmd_len; + int secret_len, old_state; + int code; + char rhostname[256]; + MD5_CTX mdContext; + char secret[MAXSECRETLEN]; + u_char hash[MD5_SIGNATURE_SIZE]; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id)); + + if (cstate->serverstate == CHAPSS_CLOSED || + cstate->serverstate == CHAPSS_PENDING) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n", + cstate->serverstate)); + return; + } + + if (id != cstate->chal_id) { + return; /* doesn't match ID of last challenge */ + } + + /* + * If we have received a duplicate or bogus Response, + * we have to send the same answer (Success/Failure) + * as we did for the first Response we saw. + */ + if (cstate->serverstate == CHAPSS_OPEN) { + ChapSendStatus(cstate, CHAP_SUCCESS); + return; + } + if (cstate->serverstate == CHAPSS_BADAUTH) { + ChapSendStatus(cstate, CHAP_FAILURE); + return; + } + + if (len < 2) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + GETCHAR(remmd_len, inp); /* get length of MD */ + remmd = inp; /* get pointer to MD */ + INCPTR(remmd_len, inp); + + len -= sizeof (u_char) + remmd_len; + if (len < 0) { + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n")); + return; + } + + UNTIMEOUT(ChapChallengeTimeout, cstate); + + if (len >= (int)sizeof(rhostname)) { + len = sizeof(rhostname) - 1; + } + BCOPY(inp, rhostname, len); + rhostname[len] = '\000'; + + CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n", + rhostname)); + + /* + * Get secret for authenticating them with us, + * do the hash ourselves, and compare the result. + */ + code = CHAP_FAILURE; + if (!get_secret(cstate->unit, rhostname, cstate->chal_name, + secret, &secret_len, 1)) { + CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n", + rhostname)); + } else { + /* generate MD based on negotiated type */ + switch (cstate->chal_type) { + + case CHAP_DIGEST_MD5: /* only MD5 is defined for now */ + if (remmd_len != MD5_SIGNATURE_SIZE) { + break; /* it's not even the right length */ + } + MD5Init(&mdContext); + MD5Update(&mdContext, &cstate->chal_id, 1); + MD5Update(&mdContext, (u_char*)secret, secret_len); + MD5Update(&mdContext, cstate->challenge, cstate->chal_len); + MD5Final(hash, &mdContext); + + /* compare local and remote MDs and send the appropriate status */ + if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) { + code = CHAP_SUCCESS; /* they are the same! */ + } + break; + + default: + CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type)); + } + } + + BZERO(secret, sizeof(secret)); + ChapSendStatus(cstate, code); + + if (code == CHAP_SUCCESS) { + old_state = cstate->serverstate; + cstate->serverstate = CHAPSS_OPEN; + if (old_state == CHAPSS_INITIAL_CHAL) { + auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len); + } + if (cstate->chal_interval != 0) { + TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval); + } + } else { + CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n")); + cstate->serverstate = CHAPSS_BADAUTH; + auth_peer_fail(cstate->unit, PPP_CHAP); + } +} + +/* + * ChapReceiveSuccess - Receive Success + */ +static void +ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id)); + + if (cstate->clientstate == CHAPCS_OPEN) { + /* presumably an answer to a duplicate response */ + return; + } + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + cstate->clientstate = CHAPCS_OPEN; + + auth_withpeer_success(cstate->unit, PPP_CHAP); +} + + +/* + * ChapReceiveFailure - Receive failure. + */ +static void +ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len) +{ + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(inp); + + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id)); + + if (cstate->clientstate != CHAPCS_RESPONSE) { + /* don't know what this is */ + CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n", + cstate->clientstate)); + return; + } + + UNTIMEOUT(ChapResponseTimeout, cstate); + + /* + * Print message. + */ + if (len > 0) { + PRINTMSG(inp, len); + } + + CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n")); + auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */ +} + + +/* + * ChapSendChallenge - Send an Authenticate challenge. + */ +static void +ChapSendChallenge(chap_state *cstate) +{ + u_char *outp; + int chal_len, name_len; + int outlen; + + chal_len = cstate->chal_len; + name_len = (int)strlen(cstate->chal_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */ + + PUTCHAR(CHAP_CHALLENGE, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + + PUTCHAR(chal_len, outp); /* put length of challenge */ + BCOPY(cstate->challenge, outp, chal_len); + INCPTR(chal_len, outp); + + BCOPY(cstate->chal_name, outp, name_len); /* append hostname */ + + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id)); + + TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime); + ++cstate->chal_transmits; +} + + +/* + * ChapSendStatus - Send a status response (ack or nak). + */ +static void +ChapSendStatus(chap_state *cstate, int code) +{ + u_char *outp; + int outlen, msglen; + char msg[256]; /* @todo: this can be a char*, no strcpy needed */ + + if (code == CHAP_SUCCESS) { + strcpy(msg, "Welcome!"); + } else { + strcpy(msg, "I don't like you. Go 'way."); + } + msglen = (int)strlen(msg); + + outlen = CHAP_HEADERLEN + msglen; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); /* paste in a header */ + + PUTCHAR(code, outp); + PUTCHAR(cstate->chal_id, outp); + PUTSHORT(outlen, outp); + BCOPY(msg, outp, msglen); + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code, + cstate->chal_id)); +} + +/* + * ChapGenChallenge is used to generate a pseudo-random challenge string of + * a pseudo-random length between min_len and max_len. The challenge + * string and its length are stored in *cstate, and various other fields of + * *cstate are initialized. + */ + +static void +ChapGenChallenge(chap_state *cstate) +{ + int chal_len; + u_char *ptr = cstate->challenge; + int i; + + /* pick a random challenge length between MIN_CHALLENGE_LENGTH and + MAX_CHALLENGE_LENGTH */ + chal_len = (unsigned) + ((((magic() >> 16) * + (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16) + + MIN_CHALLENGE_LENGTH); + LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff); + cstate->chal_len = (u_char)chal_len; + cstate->chal_id = ++cstate->id; + cstate->chal_transmits = 0; + + /* generate a random string */ + for (i = 0; i < chal_len; i++ ) { + *ptr++ = (char) (magic() & 0xff); + } +} + +/* + * ChapSendResponse - send a response packet with values as specified + * in *cstate. + */ +/* ARGSUSED */ +static void +ChapSendResponse(chap_state *cstate) +{ + u_char *outp; + int outlen, md_len, name_len; + + md_len = cstate->resp_length; + name_len = (int)strlen(cstate->resp_name); + outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len; + outp = outpacket_buf[cstate->unit]; + + MAKEHEADER(outp, PPP_CHAP); + + PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */ + PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */ + PUTSHORT(outlen, outp); /* packet length */ + + PUTCHAR(md_len, outp); /* length of MD */ + BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */ + INCPTR(md_len, outp); + + BCOPY(cstate->resp_name, outp, name_len); /* append our name */ + + /* send the packet */ + pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN); + + cstate->clientstate = CHAPCS_RESPONSE; + TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime); + ++cstate->resp_transmits; +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *ChapCodenames[] = { + "Challenge", "Response", "Success", "Failure" +}; +/* + * ChapPrintPkt - print the contents of a CHAP packet. + */ +static int +ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len; + int clen, nlen; + u_char x; + + if (plen < CHAP_HEADERLEN) { + return 0; + } + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) { + printer(arg, " %s", ChapCodenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= CHAP_HEADERLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) { + break; + } + clen = p[0]; + if (len < clen + 1) { + break; + } + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = %.*Z", nlen, p); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " %.*Z", len, p); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + } + + return len + CHAP_HEADERLEN; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* CHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.h new file mode 100644 index 0000000..fedcab8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chap.h @@ -0,0 +1,150 @@ +/***************************************************************************** +* chap.h - Network Challenge Handshake Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-03 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1993 The Australian National University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the Australian National University. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Copyright (c) 1991 Gregory M. Christy + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the author. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $ + */ + +#ifndef CHAP_H +#define CHAP_H + +/* Code + ID + length */ +#define CHAP_HEADERLEN 4 + +/* + * CHAP codes. + */ + +#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */ +#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ +#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */ +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ + +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * Challenge lengths (for challenges we send) and other limits. + */ +#define MIN_CHALLENGE_LENGTH 32 +#define MAX_CHALLENGE_LENGTH 64 +#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */ + +/* + * Each interface is described by a chap structure. + */ + +typedef struct chap_state { + int unit; /* Interface unit number */ + int clientstate; /* Client state */ + int serverstate; /* Server state */ + u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */ + u_char chal_len; /* challenge length */ + u_char chal_id; /* ID of last challenge */ + u_char chal_type; /* hash algorithm for challenges */ + u_char id; /* Current id */ + char *chal_name; /* Our name to use with challenge */ + int chal_interval; /* Time until we challenge peer again */ + int timeouttime; /* Timeout time in seconds */ + int max_transmits; /* Maximum # of challenge transmissions */ + int chal_transmits; /* Number of transmissions of challenge */ + int resp_transmits; /* Number of transmissions of response */ + u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */ + u_char resp_length; /* length of response */ + u_char resp_id; /* ID for response messages */ + u_char resp_type; /* hash algorithm for responses */ + char *resp_name; /* Our name to send with response */ +} chap_state; + + +/* + * Client (peer) states. + */ +#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */ +#define CHAPCS_LISTEN 3 /* Listening for a challenge */ +#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */ +#define CHAPCS_OPEN 5 /* We've received Success */ + +/* + * Server (authenticator) states. + */ +#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */ +#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */ +#define CHAPSS_PENDING 2 /* Auth peer when lower up */ +#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */ +#define CHAPSS_OPEN 4 /* We've sent a Success msg */ +#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */ +#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */ + +extern chap_state chap[]; + +void ChapAuthWithPeer (int, char *, u_char); +void ChapAuthPeer (int, char *, u_char); + +extern struct protent chap_protent; + +#endif /* CHAP_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.c new file mode 100644 index 0000000..81a887b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.c @@ -0,0 +1,396 @@ +/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/ +/*** The original PPPD code is written in a way to require either the UNIX DES + encryption functions encrypt(3) and setkey(3) or the DES library libdes. + Since both is not included in lwIP, MSCHAP currently does not work! */ +/***************************************************************************** +* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved. +* +* 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD chap_ms.c. +*****************************************************************************/ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +#define USE_CRYPT + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md4.h" +#ifndef USE_CRYPT +#include "des.h" +#endif +#include "chap.h" +#include "chpms.h" + +#include + + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ +typedef struct { + u_char LANManResp[24]; + u_char NTResp[24]; + u_char UseNT; /* If 1, ignore the LANMan response field */ +} MS_ChapResponse; +/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse), + in case this struct gets padded. */ + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ + +/* XXX Don't know what to do with these. */ +extern void setkey(const char *); +extern void encrypt(char *, int); + +static void DesEncrypt (u_char *, u_char *, u_char *); +static void MakeKey (u_char *, u_char *); + +#ifdef USE_CRYPT +static void Expand (u_char *, u_char *); +static void Collapse (u_char *, u_char *); +#endif + +static void ChallengeResponse( + u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */ +); +static void ChapMS_NT( + char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response +); +static u_char Get7Bits( + u_char *input, + int startBit +); + +static void +ChallengeResponse( u_char *challenge, /* IN 8 octets */ + u_char *pwHash, /* IN 16 octets */ + u_char *response /* OUT 24 octets */) +{ + u_char ZPasswordHash[21]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + BCOPY(pwHash, ZPasswordHash, 16); + +#if 0 + log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG); +#endif + + DesEncrypt(challenge, ZPasswordHash + 0, response + 0); + DesEncrypt(challenge, ZPasswordHash + 7, response + 8); + DesEncrypt(challenge, ZPasswordHash + 14, response + 16); + +#if 0 + log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG); +#endif +} + + +#ifdef USE_CRYPT +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + u_char des_key[8]; + u_char crypt_key[66]; + u_char des_input[66]; + + MakeKey(key, des_key); + + Expand(des_key, crypt_key); + setkey((char*)crypt_key); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + Expand(clear, des_input); + encrypt((char*)des_input, 0); + Collapse(des_input, cipher); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#else /* USE_CRYPT */ + +static void +DesEncrypt( u_char *clear, /* IN 8 octets */ + u_char *key, /* IN 7 octets */ + u_char *cipher /* OUT 8 octets */) +{ + des_cblock des_key; + des_key_schedule key_schedule; + + MakeKey(key, des_key); + + des_set_key(&des_key, key_schedule); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n", + clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7])); +#endif + + des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1); + +#if 0 + CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7])); +#endif +} + +#endif /* USE_CRYPT */ + + +static u_char +Get7Bits( u_char *input, int startBit) +{ + register unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +#ifdef USE_CRYPT + +/* in == 8-byte string (expanded version of the 56-bit key) + * out == 64-byte string where each byte is either 1 or 0 + * Note that the low-order "bit" is always ignored by by setkey() + */ +static void +Expand(u_char *in, u_char *out) +{ + int j, c; + int i; + + for(i = 0; i < 64; in++){ + c = *in; + for(j = 7; j >= 0; j--) { + *out++ = (c >> j) & 01; + } + i += 8; + } +} + +/* The inverse of Expand + */ +static void +Collapse(u_char *in, u_char *out) +{ + int j; + int i; + unsigned int c; + + for (i = 0; i < 64; i += 8, out++) { + c = 0; + for (j = 7; j >= 0; j--, in++) { + c |= *in << j; + } + *out = c & 0xff; + } +} +#endif + +static void +MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */ + u_char *des_key /* OUT 64 bit DES key with parity bits added */) +{ + des_key[0] = Get7Bits(key, 0); + des_key[1] = Get7Bits(key, 7); + des_key[2] = Get7Bits(key, 14); + des_key[3] = Get7Bits(key, 21); + des_key[4] = Get7Bits(key, 28); + des_key[5] = Get7Bits(key, 35); + des_key[6] = Get7Bits(key, 42); + des_key[7] = Get7Bits(key, 49); + +#ifndef USE_CRYPT + des_set_odd_parity((des_cblock *)des_key); +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n", + key[0], key[1], key[2], key[3], key[4], key[5], key[6])); + CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n", + des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7])); +#endif +} + +static void +ChapMS_NT( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + MDstruct md4Context; + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + static int low_byte_first = -1; + + LWIP_UNUSED_ARG(rchallenge_len); + + /* Initialize the Unicode version of the secret (== password). */ + /* This implicitly supports 8-bit ISO8859/1 characters. */ + BZERO(unicodePassword, sizeof(unicodePassword)); + for (i = 0; i < secret_len; i++) { + unicodePassword[i * 2] = (u_char)secret[i]; + } + MDbegin(&md4Context); + MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */ + + if (low_byte_first == -1) { + low_byte_first = (PP_HTONS((unsigned short int)1) != 1); + } + if (low_byte_first == 0) { + /* @todo: arg type - u_long* or u_int* ? */ + MDreverse((unsigned int*)&md4Context); /* sfb 961105 */ + } + + MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */ + + ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void +ChapMS_LANMan( char *rchallenge, + int rchallenge_len, + char *secret, + int secret_len, + MS_ChapResponse *response) +{ + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[16]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) { + UcasePassword[i] = (u_char)toupper(secret[i]); + } + DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 ); + DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 ); + ChallengeResponse(rchallenge, PasswordHash, response->LANManResp); +} +#endif + +void +ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len) +{ + MS_ChapResponse response; +#ifdef MSLANMAN + extern int ms_lanman; +#endif + +#if 0 + CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret)); +#endif + BZERO(&response, sizeof(response)); + + /* Calculate both always */ + ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response); + + /* prefered method is set by option */ + response.UseNT = !ms_lanman; +#else + response.UseNT = 1; +#endif + + BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN); + cstate->resp_length = MS_CHAP_RESPONSE_LEN; +} + +#endif /* MSCHAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.h new file mode 100644 index 0000000..df070fb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/chpms.h @@ -0,0 +1,64 @@ +/***************************************************************************** +* chpms.h - Network Microsoft Challenge Handshake Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-01-30 Guy Lancaster , Global Election Systems Inc. +* Original built from BSD network code. +******************************************************************************/ +/* + * chap.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited. + * http://www.strataware.com/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Eric Rosenquist. 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 ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $ + */ + +#ifndef CHPMS_H +#define CHPMS_H + +#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */ + +void ChapMS (chap_state *, char *, int, char *, int); + +#endif /* CHPMS_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.c new file mode 100644 index 0000000..e8a254e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.c @@ -0,0 +1,890 @@ +/***************************************************************************** +* fsm.c - Network Control Protocol Finite State Machine program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD fsm.c. +*****************************************************************************/ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" + +#include + +#if PPP_DEBUG +static const char *ppperr_strerr[] = { + "LS_INITIAL", /* LS_INITIAL 0 */ + "LS_STARTING", /* LS_STARTING 1 */ + "LS_CLOSED", /* LS_CLOSED 2 */ + "LS_STOPPED", /* LS_STOPPED 3 */ + "LS_CLOSING", /* LS_CLOSING 4 */ + "LS_STOPPING", /* LS_STOPPING 5 */ + "LS_REQSENT", /* LS_REQSENT 6 */ + "LS_ACKRCVD", /* LS_ACKRCVD 7 */ + "LS_ACKSENT", /* LS_ACKSENT 8 */ + "LS_OPENED" /* LS_OPENED 9 */ +}; +#endif /* PPP_DEBUG */ + +static void fsm_timeout (void *); +static void fsm_rconfreq (fsm *, u_char, u_char *, int); +static void fsm_rconfack (fsm *, int, u_char *, int); +static void fsm_rconfnakrej (fsm *, int, int, u_char *, int); +static void fsm_rtermreq (fsm *, int, u_char *, int); +static void fsm_rtermack (fsm *); +static void fsm_rcoderej (fsm *, u_char *, int); +static void fsm_sconfreq (fsm *, int); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +int peer_mru[NUM_PPP]; + + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void +fsm_init(fsm *f) +{ + f->state = LS_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->timeouttime = FSM_DEFTIMEOUT; + f->maxconfreqtransmits = FSM_DEFMAXCONFREQS; + f->maxtermtransmits = FSM_DEFMAXTERMREQS; + f->maxnakloops = FSM_DEFMAXNAKLOOPS; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void +fsm_lowerup(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_CLOSED; + break; + + case LS_STARTING: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void +fsm_lowerdown(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_CLOSED: + f->state = LS_INITIAL; + break; + + case LS_STOPPED: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSING: + f->state = LS_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + f->state = LS_STARTING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } + + FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void +fsm_open(fsm *f) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + switch( f->state ) { + case LS_INITIAL: + f->state = LS_STARTING; + if( f->callbacks->starting ) { + (*f->callbacks->starting)(f); + } + break; + + case LS_CLOSED: + if( f->flags & OPT_SILENT ) { + f->state = LS_STOPPED; + } else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + } + break; + + case LS_CLOSING: + f->state = LS_STOPPING; + /* fall through */ + case LS_STOPPED: + case LS_OPENED: + if( f->flags & OPT_RESTART ) { + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + } + + FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + +#if 0 /* backport pppd 2.4.4b1; */ +/* + * terminate_layer - Start process of shutting down the FSM + * + * Cancel any timeout running, notify upper layers we're done, and + * send a terminate-request message as configured. + */ +static void +terminate_layer(fsm *f, int nextstate) +{ + /* @todo */ +} +#endif + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the LS_CLOSED state. + */ +void +fsm_close(fsm *f, char *reason) +{ + int oldState = f->state; + + LWIP_UNUSED_ARG(oldState); + + f->term_reason = reason; + f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason)); + switch( f->state ) { + case LS_STARTING: + f->state = LS_INITIAL; + break; + case LS_STOPPED: + f->state = LS_CLOSED; + break; + case LS_STOPPING: + f->state = LS_CLOSING; + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + case LS_OPENED: + if( f->state != LS_OPENED ) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + } else if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_CLOSING; + break; + } + + FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n", + PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state])); +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void +fsm_timeout(void *arg) +{ + fsm *f = (fsm *) arg; + + switch (f->state) { + case LS_CLOSING: + case LS_STOPPING: + if( f->retransmits <= 0 ) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + } + break; + + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + if (f->retransmits <= 0) { + FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + f->state = LS_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + } else { + FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) { + (*f->callbacks->retransmit)(f); + } + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } + } + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_input - Input packet. + */ +void +fsm_input(fsm *f, u_char *inpacket, int l) +{ + u_char *inp = inpacket; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + if (l < HEADERLEN) { + FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n", + f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n", + f->protocol)); + return; + } + if (len > l) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n", + f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == LS_INITIAL || f->state == LS_STARTING ) { + FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n", + f->protocol, f->state, ppperr_strerr[f->state])); + return; + } + FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l)); + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f))); + if( !f->callbacks->extcode || + !(*f->callbacks->extcode)(f, code, id, inp, len) ) { + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + } + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void +fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) +{ + int code, reject_if_disagree; + + FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + switch( f->state ) { + case LS_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case LS_CLOSING: + case LS_STOPPING: + return; + + case LS_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + break; + + case LS_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci) { /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) { + code = CONFREJ; /* Reject all CI */ + } else { + code = CONFACK; + } + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, (u_char)code, id, inp, len); + + if (code == CONFACK) { + if (f->state == LS_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + } else { + f->state = LS_ACKSENT; + } + f->nakloops = 0; + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != LS_ACKRCVD) { + f->state = LS_REQSENT; + } + if( code == CONFNAK ) { + ++f->nakloops; + } + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void +fsm_rconfack(fsm *f, int id, u_char *inp, int len) +{ + FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) { + /* Ack is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n", + PROTO_NAME(f), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + f->state = LS_ACKRCVD; + f->retransmits = f->maxconfreqtransmits; + break; + + case LS_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = LS_OPENED; + f->retransmits = f->maxconfreqtransmits; + if (f->callbacks->up) { + (*f->callbacks->up)(f); /* Inform upper layers */ + } + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void +fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) +{ + int (*proc) (fsm *, u_char *, int); + int ret; + + FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + if (id != f->reqid || f->seen_ack) { /* Expected id? */ + return; /* Nope, toss... */ + } + proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci; + if (!proc || !((ret = proc(f, inp, len)))) { + /* Nak/reject is bad - ignore it */ + FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n", + PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len)); + return; + } + f->seen_ack = 1; + + switch (f->state) { + case LS_CLOSED: + case LS_STOPPED: + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); + break; + + case LS_REQSENT: + case LS_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) { + f->state = LS_STOPPED; /* kludge for stopping CCP */ + } else { + fsm_sconfreq(f, 0); /* Send Configure-Request */ + } + break; + + case LS_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = LS_REQSENT; + break; + + case LS_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = LS_REQSENT; + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void +fsm_rtermreq(fsm *f, int id, u_char *p, int len) +{ + LWIP_UNUSED_ARG(p); + + FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n", + PROTO_NAME(f), id, f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_ACKRCVD: + case LS_ACKSENT: + f->state = LS_REQSENT; /* Start over but keep trying */ + break; + + case LS_OPENED: + if (len > 0) { + FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p)); + } else { + FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f))); + } + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + f->retransmits = 0; + f->state = LS_STOPPING; + TIMEOUT(fsm_timeout, f, f->timeouttime); + break; + } + + fsm_sdata(f, TERMACK, (u_char)id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void +fsm_rtermack(fsm *f) +{ + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + switch (f->state) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_ACKRCVD: + f->state = LS_REQSENT; + break; + + case LS_OPENED: + if (f->callbacks->down) { + (*f->callbacks->down)(f); /* Inform upper layers */ + } + fsm_sconfreq(f, 0); + break; + default: + FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void +fsm_rcoderej(fsm *f, u_char *inp, int len) +{ + u_char code, id; + + FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + + if (len < HEADERLEN) { + FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n", + PROTO_NAME(f), code, id)); + + if( f->state == LS_ACKRCVD ) { + f->state = LS_REQSENT; + } +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void +fsm_protreject(fsm *f) +{ + switch( f->state ) { + case LS_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_CLOSED: + f->state = LS_CLOSED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_STOPPING: + case LS_REQSENT: + case LS_ACKRCVD: + case LS_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + case LS_STOPPED: + f->state = LS_STOPPED; + if( f->callbacks->finished ) { + (*f->callbacks->finished)(f); + } + break; + + case LS_OPENED: + if( f->callbacks->down ) { + (*f->callbacks->down)(f); + } + /* Init restart counter, send Terminate-Request */ + f->retransmits = f->maxtermtransmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, f->timeouttime); + --f->retransmits; + + f->state = LS_STOPPING; + break; + + default: + FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n", + PROTO_NAME(f), f->state, ppperr_strerr[f->state])); + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void +fsm_sconfreq(fsm *f, int retransmit) +{ + u_char *outp; + int cilen; + + if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) { + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) { + (*f->callbacks->resetci)(f); + } + f->nakloops = 0; + } + + if( !retransmit ) { + /* New request - reset retransmission counter, use new ID */ + f->retransmits = f->maxconfreqtransmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN; + if( f->callbacks->cilen && f->callbacks->addci ) { + cilen = (*f->callbacks->cilen)(f); + if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) { + cilen = peer_mru[f->unit] - HEADERLEN; + } + if (f->callbacks->addci) { + (*f->callbacks->addci)(f, outp, &cilen); + } + } else { + cilen = 0; + } + + /* send the request to our peer */ + fsm_sdata(f, CONFREQ, f->reqid, outp, cilen); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, f->timeouttime); + + FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n", + PROTO_NAME(f), f->reqid)); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void +fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen) +{ + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + outp = outpacket_buf[f->unit]; + if (datalen > peer_mru[f->unit] - (int)HEADERLEN) { + datalen = peer_mru[f->unit] - HEADERLEN; + } + if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) { + BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen); + } + outlen = datalen + HEADERLEN; + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN); + FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n", + PROTO_NAME(f), code, id, outlen)); +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.h new file mode 100644 index 0000000..8d41b5f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/fsm.h @@ -0,0 +1,157 @@ +/***************************************************************************** +* fsm.h - Network Control Protocol Finite State Machine header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD code. +*****************************************************************************/ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $ + */ + +#ifndef FSM_H +#define FSM_H + +/* + * LCP Packet header = Code, id, length. + */ +#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + int unit; /* Interface unit number */ + u_short protocol; /* Data Link Layer Protocol field value */ + int state; /* State */ + int flags; /* Contains option bits */ + u_char id; /* Current id */ + u_char reqid; /* Current request id */ + u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + int timeouttime; /* Timeout time in milliseconds */ + int maxconfreqtransmits; /* Maximum Configure-Request transmissions */ + int retransmits; /* Number of retransmissions left */ + int maxtermtransmits; /* Maximum Terminate-Request transmissions */ + int nakloops; /* Number of nak loops since last ack */ + int maxnakloops; /* Maximum number of nak loops tolerated */ + struct fsm_callbacks* callbacks; /* Callback routines */ + char* term_reason; /* Reason for closing protocol */ + int term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci)(fsm*); /* Reset our Configuration Information */ + int (*cilen)(fsm*); /* Length of our Configuration Information */ + void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */ + int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */ + int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */ + int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */ + int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */ + void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */ + void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */ + void (*starting)(fsm*); /* Called when we want the lower layer */ + void (*finished)(fsm*); /* Called when we don't want the lower layer */ + void (*protreject)(int); /* Called when Protocol-Reject received */ + void (*retransmit)(fsm*); /* Retransmission is necessary */ + int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */ + char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define LS_INITIAL 0 /* Down, hasn't been opened */ +#define LS_STARTING 1 /* Down, been opened */ +#define LS_CLOSED 2 /* Up, hasn't been opened */ +#define LS_STOPPED 3 /* Open, waiting for down event */ +#define LS_CLOSING 4 /* Terminating the connection, not open */ +#define LS_STOPPING 5 /* Terminating, but open */ +#define LS_REQSENT 6 /* We've sent a Config Request */ +#define LS_ACKRCVD 7 /* We've received a Config Ack */ +#define LS_ACKSENT 8 /* We've sent a Config Ack */ +#define LS_OPENED 9 /* Connection available */ + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Prototypes + */ +void fsm_init (fsm*); +void fsm_lowerup (fsm*); +void fsm_lowerdown (fsm*); +void fsm_open (fsm*); +void fsm_close (fsm*, char*); +void fsm_input (fsm*, u_char*, int); +void fsm_protreject (fsm*); +void fsm_sdata (fsm*, u_char, u_char, u_char*, int); + + +/* + * Variables + */ +extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */ + +#endif /* FSM_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.c new file mode 100644 index 0000000..f0ab2e0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.c @@ -0,0 +1,1411 @@ +/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and + dial-on-demand has been stripped. */ +/***************************************************************************** +* ipcp.c - Network PPP IP Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-08 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "fsm.h" +#include "vj.h" +#include "ipcp.h" + +#include "lwip/inet.h" + +#include + +/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */ + +/* global vars */ +ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ + +/* local vars */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int cis_received[NUM_PPP]; /* # Conf-Reqs received */ + + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci (fsm *); /* Reset our CI */ +static int ipcp_cilen (fsm *); /* Return length of our CI */ +static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */ +static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */ +static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */ +static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */ +static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */ +static void ipcp_up (fsm *); /* We're UP */ +static void ipcp_down (fsm *); /* We're DOWN */ +#if PPP_ADDITIONAL_CALLBACKS +static void ipcp_script (fsm *, char *); /* Run an up/down script */ +#endif +static void ipcp_finished (fsm *); /* Don't need lower layer */ + + +fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */ + + +static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches LS_OPENED state */ + ipcp_down, /* Called when fsm leaves LS_OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Protocol entry points from main code. + */ +static void ipcp_init (int); +static void ipcp_open (int); +static void ipcp_close (int, char *); +static void ipcp_lowerup (int); +static void ipcp_lowerdown (int); +static void ipcp_input (int, u_char *, int); +static void ipcp_protrej (int); + + +struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if PPP_ADDITIONAL_CALLBACKS + ipcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "IPCP", +#if PPP_ADDITIONAL_CALLBACKS + ip_check_options, + NULL, + ip_active_pkt +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +static void ipcp_clear_addrs (int); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + + +/* + * ipcp_init - Initialize IPCP. + */ +static void +ipcp_init(int unit) +{ + fsm *f = &ipcp_fsm[unit]; + ipcp_options *wo = &ipcp_wantoptions[unit]; + ipcp_options *ao = &ipcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(&ipcp_fsm[unit]); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = 1; + wo->ouraddr = 0; +#if VJ_SUPPORT + wo->neg_vj = 1; +#else /* VJ_SUPPORT */ + wo->neg_vj = 0; +#endif /* VJ_SUPPORT */ + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_SLOTS - 1; + wo->cflag = 0; + wo->default_route = 1; + + ao->neg_addr = 1; +#if VJ_SUPPORT + ao->neg_vj = 1; +#else /* VJ_SUPPORT */ + ao->neg_vj = 0; +#endif /* VJ_SUPPORT */ + ao->maxslotindex = MAX_SLOTS - 1; + ao->cflag = 1; + ao->default_route = 1; +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void +ipcp_open(int unit) +{ + fsm_open(&ipcp_fsm[unit]); +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void +ipcp_close(int unit, char *reason) +{ + fsm_close(&ipcp_fsm[unit], reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void +ipcp_lowerup(int unit) +{ + fsm_lowerup(&ipcp_fsm[unit]); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void +ipcp_lowerdown(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void +ipcp_input(int unit, u_char *p, int len) +{ + fsm_input(&ipcp_fsm[unit], p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void +ipcp_protrej(int unit) +{ + fsm_lowerdown(&ipcp_fsm[unit]); +} + + +/* + * ipcp_resetci - Reset our CI. + */ +static void +ipcp_resetci(fsm *f) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr; + if (wo->ouraddr == 0) { + wo->accept_local = 1; + } + if (wo->hisaddr == 0) { + wo->accept_remote = 1; + } + /* Request DNS addresses from the peer */ + wo->req_dns1 = ppp_settings.usepeerdns; + wo->req_dns2 = ppp_settings.usepeerdns; + ipcp_gotoptions[f->unit] = *wo; + cis_received[f->unit] = 0; +} + + +/* + * ipcp_cilen - Return length of our CI. + */ +static int +ipcp_cilen(fsm *f) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0) +#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (wo->neg_addr && !go->neg_addr && !go->old_addrs) { + /* use the old style of address negotiation */ + go->neg_addr = 1; + go->old_addrs = 1; + } + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + if (cis_received[f->unit] == 0) { + /* keep trying the new style until we see some CI from the peer */ + go->neg_vj = 1; + } else { + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + } + + return (LENCIADDR(go->neg_addr, go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2)); +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + */ +static void +ipcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + if (len >= addrlen) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(addrlen, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + if (old) { \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + } \ + len -= addrlen; \ + } else { \ + neg = 0; \ + } \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else { \ + neg = 0; \ + } \ + } + + ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +ipcp_ackci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETSHORT(cishort, p); \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) { \ + goto bad; \ + } \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIADDR(opt, neg, old, val1, val2) \ + if (neg) { \ + int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \ + u32_t l; \ + if ((len -= addrlen) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != addrlen || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) { \ + goto bad; \ + } \ + } \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) { \ + goto bad; \ + } \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) { \ + goto bad; \ + } \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) { \ + goto bad; \ + } \ + } + + ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + return (1); + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +ipcp_nakci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDR(opt, neg, old, code) \ + if (go->neg && \ + len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + if (old) { \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + } else { \ + ciaddr2 = 0; \ + } \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs, + if (go->accept_local && ciaddr1) { /* Do we know our address? */ + try.ouraddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("local IP address %s\n", + inet_ntoa(ciaddr1))); + } + if (go->accept_remote && ciaddr2) { /* Does he know his? */ + try.hisaddr = ciaddr2; + IPCPDEBUG(LOG_INFO, ("remote IP address %s\n", + inet_ntoa(ciaddr2))); + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) { + try.maxslotindex = cimaxslotindex; + } + if (!cicflag) { + try.cflag = 0; + } + } else { + try.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try.old_vj = 1; + try.vj_protocol = cishort; + } else { + try.neg_vj = 0; + } + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + try.dnsaddr[0] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + try.dnsaddr[1] = cidnsaddr; + IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr))); + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if( (len -= cilen) < 0 ) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + goto bad; + } + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) { + goto bad; + } + try.neg_addr = 1; + try.old_addrs = 1; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) { + try.hisaddr = ciaddr2; + } + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) { + goto bad; + } + try.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) { + try.ouraddr = ciaddr1; + } + if (try.ouraddr != 0) { + try.neg_addr = 1; + } + no.neg_addr = 1; + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + */ +static int +ipcp_rejci(fsm *f, u_char *p, int len) +{ + ipcp_options *go = &ipcp_gotoptions[f->unit]; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try; /* options to request next time */ + + try = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDR(opt, neg, old, val1, val2) \ + if (go->neg && \ + len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \ + p[1] == cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) { \ + goto bad; \ + } \ + if (old) { \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) { \ + goto bad; \ + } \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) { \ + goto bad; \ + } \ + } \ + try.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) { \ + goto bad; \ + } \ + try.neg = 0; \ + } + + REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, + go->old_addrs, go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree) +{ + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *ao = &ipcp_allowoptions[f->unit]; +#ifdef OLD_CI_ADDRS + ipcp_options *go = &ipcp_gotoptions[f->unit]; +#endif + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1; /* Parsed address values */ +#ifdef OLD_CI_ADDRS + u32_t ciaddr2; /* Parsed address values */ +#endif + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + cis_received[f->unit] = 1; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = (u_short)l;/* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ +#ifdef OLD_CI_ADDRS /* Need to save space... */ + case CI_ADDRS: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n")); + if (!ao->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1))); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2))); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + go->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->neg_addr = 1; + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; +#endif + + case CI_ADDR: + if (!ao->neg_addr) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1))); + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1))); + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1))); + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1)); + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n", + d+1, inet_ntoa(tl))); + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1)); + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1)); + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen)); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort)); + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag)); + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_SLOTS - 1; + ho->cflag = 1; + } + IPCPDEBUG(LOG_INFO, ( + "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n", + ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag)); + break; + + default: + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) { /* Getting fed up with sending NAKs? */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n")); + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) { + BCOPY(cip, ucp, cilen); /* Move it */ + } + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && + wo->req_addr && !reject_if_disagree) { + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n")); + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = (int)(ucp - inp); /* Compute output length */ + IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options(u_long localAddr) +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Load our default IP address but allow the remote host to give us + * a new address. + */ + if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) { + wo->accept_local = 1; /* don't insist on this default value */ + wo->ouraddr = htonl(localAddr); + } +} +#endif + + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void +ipcp_up(fsm *f) +{ + u32_t mask; + ipcp_options *ho = &ipcp_hisoptions[f->unit]; + ipcp_options *go = &ipcp_gotoptions[f->unit]; + ipcp_options *wo = &ipcp_wantoptions[f->unit]; + + np_up(f->unit, PPP_IP); + IPCPDEBUG(LOG_INFO, ("ipcp: up\n")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr) { + ho->hisaddr = wo->hisaddr; + } + + if (ho->hisaddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n")); + ipcp_close(f->unit, "Could not determine remote IP address"); + return; + } + if (go->ouraddr == 0) { + IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n")); + ipcp_close(f->unit, "Could not determine local IP address"); + return; + } + + if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/ + } + + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (!auth_ip_addr(f->unit, ho->hisaddr)) { + IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n", + inet_ntoa(ho->hisaddr))); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } + + /* set tcp compression */ + sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex); + + /* + * Set IP addresses and (if specified) netmask. + */ + mask = GetMask(go->ouraddr); + + if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) { + IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* bring the interface up for IP */ + if (!sifup(f->unit)) { + IPCPDEBUG(LOG_WARNING, ("sifup failed\n")); + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + sifnpmode(f->unit, PPP_IP, NPMODE_PASS); + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) { + if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) { + default_route_set[f->unit] = 1; + } + } + + IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr))); + IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr))); + if (go->dnsaddr[0]) { + IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0]))); + } + if (go->dnsaddr[1]) { + IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1]))); + } +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void +ipcp_down(fsm *f) +{ + IPCPDEBUG(LOG_INFO, ("ipcp: down\n")); + np_down(f->unit, PPP_IP); + sifvjcomp(f->unit, 0, 0, 0); + + sifdown(f->unit); + ipcp_clear_addrs(f->unit); +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, etc. + */ +static void +ipcp_clear_addrs(int unit) +{ + u32_t ouraddr, hisaddr; + + ouraddr = ipcp_gotoptions[unit].ouraddr; + hisaddr = ipcp_hisoptions[unit].hisaddr; + if (default_route_set[unit]) { + cifdefaultroute(unit, ouraddr, hisaddr); + default_route_set[unit] = 0; + } + cifaddr(unit, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void +ipcp_finished(fsm *f) +{ + np_finished(f->unit, PPP_IP); +} + +#if PPP_ADDITIONAL_CALLBACKS +static int +ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} + +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#define IPPROTO_TCP 6 +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(u_char *pkt, int len) +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) { + return 0; + } + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) { + return 0; + } + if (get_ipproto(pkt) != IPPROTO_TCP) { + return 1; + } + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) { + return 0; + } + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) { + return 0; + } + return 1; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.h new file mode 100644 index 0000000..de03f46 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ipcp.h @@ -0,0 +1,106 @@ +/***************************************************************************** +* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef IPCP_H +#define IPCP_H + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS1 128 /* Primary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS2 130 /* Secondary WINS value */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option */ + +typedef struct ipcp_options { + u_int neg_addr : 1; /* Negotiate IP Address? */ + u_int old_addrs : 1; /* Use old (IP-Addresses) option? */ + u_int req_addr : 1; /* Ask peer to send IP address? */ + u_int default_route : 1; /* Assign default route through interface? */ + u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */ + u_int neg_vj : 1; /* Van Jacobson Compression? */ + u_int old_vj : 1; /* use old (short) form of VJ option? */ + u_int accept_local : 1; /* accept peer's value for ouraddr */ + u_int accept_remote : 1; /* accept peer's value for hisaddr */ + u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */ + u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */ + u_short vj_protocol; /* protocol value to use in VJ option */ + u_char maxslotindex; /* VJ slots - 1. */ + u_char cflag; /* VJ slot compression flag. */ + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ +} ipcp_options; + +extern fsm ipcp_fsm[]; +extern ipcp_options ipcp_wantoptions[]; +extern ipcp_options ipcp_gotoptions[]; +extern ipcp_options ipcp_allowoptions[]; +extern ipcp_options ipcp_hisoptions[]; + +extern struct protent ipcp_protent; + +#endif /* IPCP_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.c new file mode 100644 index 0000000..54f758a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.c @@ -0,0 +1,2066 @@ +/***************************************************************************** +* lcp.c - Network Link Control Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-01 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "fsm.h" +#include "chap.h" +#include "magic.h" +#include "auth.h" +#include "lcp.h" + +#include + +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#else +#define PPPOE_MAXMTU PPP_MAXMRU +#endif + +#if 0 /* UNUSED */ +/* + * LCP-related command-line options. + */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +bool lax_recv = 0; /* accept control chars in asyncmap */ + +static int setescape (char **); + +static option_t lcp_option_list[] = { + /* LCP options */ + /* list stripped for simplicity */ + {NULL} +}; +#endif /* UNUSED */ + +/* options */ +LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ + +/* global vars */ +static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/ +lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */ +lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */ +lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */ +lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */ +ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */ + +static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */ +static u32_t lcp_echo_number = 0; /* ID number of next echo frame */ +static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */ + +/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */ +static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci (fsm*); /* Reset our CI */ +static int lcp_cilen (fsm*); /* Return length of our CI */ +static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */ +static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */ +static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */ +static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */ +static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */ +static void lcp_up (fsm*); /* We're UP */ +static void lcp_down (fsm*); /* We're DOWN */ +static void lcp_starting (fsm*); /* We need lower layer up */ +static void lcp_finished (fsm*); /* We need lower layer down */ +static int lcp_extcode (fsm*, int, u_char, u_char*, int); +static void lcp_rprotrej (fsm*, u_char*, int); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup (int); +static void lcp_echo_lowerdown (int); +static void LcpEchoTimeout (void*); +static void lcp_received_echo_reply (fsm*, int, u_char*, int); +static void LcpSendEchoRequest (fsm*); +static void LcpLinkFailure (fsm*); +static void LcpEchoCheck (fsm*); + +static fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches LS_OPENED state */ + lcp_down, /* Called when fsm leaves LS_OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_input (int, u_char *, int); +static void lcp_protrej (int); + +struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if PPP_ADDITIONAL_CALLBACKS + lcp_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "LCP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +int lcp_loopbackfail = DEFLOOPBACKFAIL; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */ +#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */ +#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */ +#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED */ +/* + * setescape - add chars to the set we escape on transmission. + */ +static int +setescape(argv) + char **argv; +{ + int n, ret; + char *p, *endp; + + p = *argv; + ret = 1; + while (*p) { + n = strtol(p, &endp, 16); + if (p == endp) { + option_error("escape parameter contains invalid hex number '%s'", p); + return 0; + } + p = endp; + if (n < 0 || n == 0x5E || n > 0xFF) { + option_error("can't escape character 0x%x", n); + ret = 0; + } else + xmit_accm[0][n >> 5] |= 1 << (n & 0x1F); + while (*p == ',' || *p == ' ') + ++p; + } + return ret; +} +#endif /* UNUSED */ + +/* + * lcp_init - Initialize LCP. + */ +void +lcp_init(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + lcp_options *ao = &lcp_allowoptions[unit]; + + f->unit = unit; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + wo->passive = 0; + wo->silent = 0; + wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */ + wo->neg_mru = 1; + wo->mru = PPP_DEFMRU; + wo->neg_asyncmap = 1; + wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + wo->neg_chap = 0; /* Set to 1 on server */ + wo->neg_upap = 0; /* Set to 1 on server */ + wo->chap_mdtype = CHAP_DIGEST_MD5; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + wo->neg_lqr = 0; /* no LQR implementation yet */ + wo->neg_cbcp = 0; + + ao->neg_mru = 1; + ao->mru = PPP_MAXMRU; + ao->neg_asyncmap = 1; + ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */ + ao->neg_chap = (CHAP_SUPPORT != 0); + ao->chap_mdtype = CHAP_DIGEST_MD5; + ao->neg_upap = (PAP_SUPPORT != 0); + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_lqr = 0; /* no LQR implementation yet */ + ao->neg_cbcp = (CBCP_SUPPORT != 0); + + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(xmit_accm[unit], 0, sizeof(xmit_accm[0])); + xmit_accm[unit][15] = 0x60; + xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF)); + xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n", + xmit_accm[unit][0], + xmit_accm[unit][1], + xmit_accm[unit][2], + xmit_accm[unit][3])); + + lcp_phase[unit] = PHASE_INITIALIZE; +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void +lcp_open(int unit) +{ + fsm *f = &lcp_fsm[unit]; + lcp_options *wo = &lcp_wantoptions[unit]; + + f->flags = 0; + if (wo->passive) { + f->flags |= OPT_PASSIVE; + } + if (wo->silent) { + f->flags |= OPT_SILENT; + } + fsm_open(f); + + lcp_phase[unit] = PHASE_ESTABLISH; +} + + +/* + * lcp_close - Take LCP down. + */ +void +lcp_close(int unit, char *reason) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_phase[unit] != PHASE_DEAD) { + lcp_phase[unit] = PHASE_TERMINATE; + } + if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do an + * lcp_close() in passive/silent mode when a connection hasn't + * been established. + */ + f->state = LS_CLOSED; + lcp_finished(f); + } else { + fsm_close(f, reason); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void +lcp_lowerup(int unit) +{ + lcp_options *wo = &lcp_wantoptions[unit]; + + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ + ppp_set_xaccm(unit, &xmit_accm[unit]); + ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(unit, PPP_MRU, 0x00000000l, + wo->neg_pcompression, wo->neg_accompression); + peer_mru[unit] = PPP_MRU; + lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0] + | ((u_long)xmit_accm[unit][1] << 8) + | ((u_long)xmit_accm[unit][2] << 16) + | ((u_long)xmit_accm[unit][3] << 24); + LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n", + xmit_accm[unit][3], + xmit_accm[unit][2], + xmit_accm[unit][1], + xmit_accm[unit][0])); + + fsm_lowerup(&lcp_fsm[unit]); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void +lcp_lowerdown(int unit) +{ + fsm_lowerdown(&lcp_fsm[unit]); +} + + +/* + * lcp_input - Input LCP packet. + */ +static void +lcp_input(int unit, u_char *p, int len) +{ + fsm *f = &lcp_fsm[unit]; + + fsm_input(f, p, len); +} + + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int +lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len) +{ + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != LS_OPENED) { + break; + } + LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id)); + magp = inp; + PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void +lcp_rprotrej(fsm *f, u_char *inp, int len) +{ + int i; + struct protent *protp; + u_short prot; + + if (len < (int)sizeof (u_short)) { + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n")); + return; + } + + GETSHORT(prot, inp); + + LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot)); + + /* + * Protocol-Reject packets received in any state other than the LCP + * LS_OPENED state SHOULD be silently discarded. + */ + if( f->state != LS_OPENED ) { + LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state)); + return; + } + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == prot && protp->enabled_flag) { + (*protp->protrej)(f->unit); + return; + } + } + + LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot)); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +static void +lcp_protrej(int unit) +{ + LWIP_UNUSED_ARG(unit); + /* + * Can't reject LCP! + */ + LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n")); + fsm_protreject(&lcp_fsm[unit]); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void +lcp_sprotrej(int unit, u_char *p, int len) +{ + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the LS_OPENED state. + */ + + fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void +lcp_resetci(fsm *f) +{ + lcp_wantoptions[f->unit].magicnumber = magic(); + lcp_wantoptions[f->unit].numloops = 0; + lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit]; + peer_mru[f->unit] = PPP_MRU; + auth_reset(f->unit); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int +lcp_cilen(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP and UPAP, even if we will + * accept either. + */ + return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + + LENCICHAP(go->neg_chap) + + LENCISHORT(!go->neg_chap && go->neg_upap) + + LENCILQR(go->neg_lqr) + + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void +lcp_addci(fsm *f, u_char *ucp, int *lenp) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#define ADDCICHAP(opt, neg, val, digest) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(val, ucp); \ + PUTCHAR(digest, ucp); \ + } +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n")); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int +lcp_ackci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#define ACKCICHAP(opt, neg, val, digest) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != digest) \ + goto bad; \ + } +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap); + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype); + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n")); + return (1); +bad: + LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int +lcp_nakci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *wo = &lcp_wantoptions[f->unit]; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } + + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != PPP_DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort < PPP_DEFMRU) { + try.mru = cishort; + } + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((go->neg_chap || go->neg_upap) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; + no.neg_chap = go->neg_chap; + no.neg_upap = go->neg_upap; + INCPTR(2, p); + GETSHORT(cishort, p); + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { + /* + * If we were asking for CHAP, they obviously don't want to do it. + * If we weren't asking for CHAP, then we were asking for PAP, + * in which case this Nak is bad. + */ + if (!go->neg_chap) { + goto bad; + } + try.neg_chap = 0; + + } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); + if (go->neg_chap) { + /* + * We were asking for CHAP/MD5; they must want a different + * algorithm. If they can't do MD5, we'll have to stop + * asking for CHAP. + */ + if (cichar != go->chap_mdtype) { + try.neg_chap = 0; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ + try.neg_upap = 0; + } + + } else { + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_chap) { + try.neg_chap = 0; + } else { + try.neg_upap = 0; + } + p += cilen - CILEN_SHORT; + } + } + + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) { + try.neg_lqr = 0; + } else { + try.lqr_period = cilong; + } + ); + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try.neg_cbcp = 0; + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression, + try.neg_pcompression = 0; + ); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression, + try.neg_accompression = 0; + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len > CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) { + goto bad; + } + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != PPP_DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) { + goto bad; + } + GETSHORT(cishort, p); + if (cishort < PPP_DEFMRU) { + try.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) + || no.neg_asyncmap || cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_AUTHTYPE: + if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) { + goto bad; + } + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) { + goto bad; + } + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) { + goto bad; + } + break; + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) { + goto bad; + } + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) { + goto bad; + } + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != LS_OPENED) { + if (looped_back) { + if (++try.numloops >= lcp_loopbackfail) { + LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n")); + lcp_close(f->unit, "Loopback detected"); + } + } else { + try.numloops = 0; + } + *go = try; + } + + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the LS_OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int +lcp_rejci(fsm *f, u_char *p, int len) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try; /* options to request next time */ + + try = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \ + } +#define REJCICHAP(opt, neg, val, digest) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cishort != val || cichar != digest) { \ + goto bad; \ + } \ + try.neg = 0; \ + try.neg_upap = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \ + } +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \ + } +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \ + } +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) { \ + goto bad; \ + } \ + try.neg = 0; \ + LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); + REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype); + if (!go->neg_chap) { + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); + } + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) { + goto bad; + } + /* + * Now we can update state. + */ + if (f->state != LS_OPENED) { + *go = try; + } + return 1; + +bad: + LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + */ +static int +lcp_reqci(fsm *f, + u_char *inp, /* Requested CIs */ + int *lenp, /* Length of requested CIs */ + int reject_if_disagree) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype; /* Parsed len, type */ + u_char cichar; /* Parsed char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + u_char *nakp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ +#if TRACELCP > 0 + char traceBuf[80]; + size_t traceNdx = 0; +#endif + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = nak_buffer; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru) { /* Allow option? */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n")); + orc = CONFREJ; /* Reject CI */ + break; + } else if (cilen != CILEN_SHORT) { /* Check CI length */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n")); + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < PPP_MINMRU) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n")); + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_LONG) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n")); + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n", + cilong, ao->asyncmap)); + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(ao->asyncmap | cilong, nakp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong); + traceNdx = strlen(traceBuf); +#endif + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n")); + orc = CONFREJ; + break; + } else if (!(ao->neg_upap || ao->neg_chap)) { + /* + * Reject the option if we're not willing to authenticate. + */ + LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n")); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be UPAP or CHAP. + * + * Note: if both ao->neg_upap and ao->neg_chap are set, + * and the peer sends a Configure-Request with two + * authenticate-protocol requests, one for CHAP and one + * for UPAP, then we will reject the second request. + * Whether we end up doing CHAP or UPAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + + if (cishort == PPP_PAP) { + if (ho->neg_chap) { /* we've already accepted CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_SHORT) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest CHAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } + ho->neg_upap = 1; +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort); + traceNdx = strlen(traceBuf); +#endif + break; + } + if (cishort == PPP_CHAP) { + if (ho->neg_upap) { /* we've already accepted PAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n")); + orc = CONFREJ; + break; + } else if (cilen != CILEN_CHAP) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n")); + orc = CONFNAK; /* NAK it and suggest PAP */ + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + break; + } + GETCHAR(cichar, p); /* get digest type*/ + if (cichar != CHAP_DIGEST_MD5 +#if MSCHAP_SUPPORT + && cichar != CHAP_MICROSOFT +#endif + ) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar)); + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + break; + } +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar); + traceNdx = strlen(traceBuf); +#endif + ho->chap_mdtype = cichar; /* save md type */ + ho->neg_chap = 1; + break; + } + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakp); + if (ao->neg_chap) { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort)); + PUTCHAR(CILEN_CHAP, nakp); + PUTSHORT(PPP_CHAP, nakp); + PUTCHAR(ao->chap_mdtype, nakp); + } else { + LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort)); + PUTCHAR(CILEN_SHORT, nakp); + PUTSHORT(PPP_PAP, nakp); + } + break; + + case CI_QUALITY: + GETSHORT(cishort, p); + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong); + traceNdx = strlen(traceBuf); +#endif + + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakp); + PUTCHAR(CILEN_LQR, nakp); + PUTSHORT(PPP_LQR, nakp); + PUTLONG(ao->lqr_period, nakp); + break; + } + break; + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong); + traceNdx = strlen(traceBuf); +#endif + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakp); + PUTCHAR(CILEN_LONG, nakp); + PUTLONG(cilong, nakp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION"); + traceNdx = strlen(traceBuf); +#endif + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + + case CI_MRRU: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_SSNHF: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + case CI_EPDISC: +#if TRACELCP > 0 + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC"); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + + default: +#if TRACELCP + snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype); + traceNdx = strlen(traceBuf); +#endif + orc = CONFREJ; + break; + } + + endswitch: +#if TRACELCP + if (traceNdx >= 80 - 32) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf)); + traceNdx = 0; + } +#endif + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) { /* but prior CI wasnt? */ + continue; /* Don't send this one */ + } + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) { /* Rejecting prior CI? */ + continue; /* Don't send this one */ + } + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) { /* Need to move rejected CI? */ + BCOPY(cip, rejp, cilen); /* Move it */ + } + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = (int)(next - inp); + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak_buffer to the caller's buffer. + */ + *lenp = (int)(nakp - nak_buffer); + BCOPY(nak_buffer, inp, *lenp); + break; + case CONFREJ: + *lenp = (int)(rejp - inp); + break; + } + +#if TRACELCP > 0 + if (traceNdx > 0) { + LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf)); + } +#endif + LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void +lcp_up(fsm *f) +{ + lcp_options *wo = &lcp_wantoptions[f->unit]; + lcp_options *ho = &lcp_hisoptions[f->unit]; + lcp_options *go = &lcp_gotoptions[f->unit]; + lcp_options *ao = &lcp_allowoptions[f->unit]; + + if (!go->neg_magicnumber) { + go->magicnumber = 0; + } + if (!ho->neg_magicnumber) { + ho->magicnumber = 0; + } + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + */ + ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)), + (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl), + ho->neg_pcompression, ho->neg_accompression); + /* + * If the asyncmap hasn't been negotiated, we really should + * set the receive asyncmap to ffffffff, but we set it to 0 + * for backwards contemptibility. + */ + ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU), + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) { + peer_mru[f->unit] = ho->mru; + } + + lcp_echo_lowerup(f->unit); /* Enable echo messages */ + + link_established(f->unit); /* The link is up; authenticate now */ +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void +lcp_down(fsm *f) +{ + lcp_options *go = &lcp_gotoptions[f->unit]; + + lcp_echo_lowerdown(f->unit); + + link_down(f->unit); + + ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0); + ppp_recv_config(f->unit, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0x00000000), + go->neg_pcompression, go->neg_accompression); + peer_mru[f->unit] = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void +lcp_starting(fsm *f) +{ + link_required(f->unit); /* lwip: currently does nothing */ +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void +lcp_finished(fsm *f) +{ + link_terminated(f->unit); /* we are finished with the link */ +} + + +#if PPP_ADDITIONAL_CALLBACKS +/* + * print_string - print a readable representation of a string using + * printer. + */ +static void +print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg) +{ + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') { + printer(arg, "\\"); + } + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + } + } + } + printer(arg, "\""); +} + + +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq" +}; + +static int +lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg) +{ + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) { + return 0; + } + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) { + return 0; + } + + if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) { + printer(arg, " %s", lcp_codenames[code-1]); + } else { + printer(arg, " code=0x%x", code); + } + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%lx", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_PAP: + printer(arg, "pap"); + break; + case PPP_CHAP: + printer(arg, "chap"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETSHORT(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + print_string((char*)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + p += 4; + len -= 4; + } + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return (int)(p - pstart); +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +/* + * Time to shut down the link because there is nothing out there. + */ +static void +LcpLinkFailure (fsm *f) +{ + if (f->state == LS_OPENED) { + LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending)); + LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n")); + lcp_close(f->unit, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ +static void +LcpEchoCheck (fsm *f) +{ + LcpSendEchoRequest (f); + + /* + * Start the timer for the next interval. + */ + LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0); + + TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval); + lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ +static void +LcpEchoTimeout (void *arg) +{ + if (lcp_echo_timer_running != 0) { + lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ +static void +lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len) +{ + u32_t magic; + + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len)); + return; + } + GETLONG(magic, inp); + if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) { + LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n")); + return; + } + + /* Reset the number of outstanding echo frames */ + lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ +static void +LcpSendEchoRequest (fsm *f) +{ + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (lcp_echo_fails != 0) { + if (lcp_echos_pending >= lcp_echo_fails) { + LcpLinkFailure(f); + lcp_echos_pending = 0; + } + } + + /* + * Make and send the echo request frame. + */ + if (f->state == LS_OPENED) { + lcp_magic = lcp_gotoptions[f->unit].magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt)); + ++lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void +lcp_echo_lowerup (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + /* Clear the parameters for generating echo frames */ + lcp_echos_pending = 0; + lcp_echo_number = 0; + lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (lcp_echo_interval != 0) { + LcpEchoCheck (f); + } +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void +lcp_echo_lowerdown (int unit) +{ + fsm *f = &lcp_fsm[unit]; + + if (lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.h new file mode 100644 index 0000000..b9201ee --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/lcp.h @@ -0,0 +1,151 @@ +/***************************************************************************** +* lcp.h - Network Link Control Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef LCP_H +#define LCP_H +/* + * Options. + */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ + +/* + * LCP-specific packet types (code numbers). + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + u_int passive : 1; /* Don't die if we don't get a response */ + u_int silent : 1; /* Wait for the other end to start first */ + u_int restart : 1; /* Restart vs. exit after close */ + u_int neg_mru : 1; /* Negotiate the MRU? */ + u_int neg_asyncmap : 1; /* Negotiate the async map? */ + u_int neg_upap : 1; /* Ask for UPAP authentication? */ + u_int neg_chap : 1; /* Ask for CHAP authentication? */ + u_int neg_magicnumber : 1; /* Ask for magic number? */ + u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */ + u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */ + u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */ + u_int neg_cbcp : 1; /* Negotiate use of CBCP */ +#ifdef PPP_MULTILINK + u_int neg_mrru : 1; /* Negotiate multilink MRRU */ + u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */ + u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */ +#endif + u_short mru; /* Value of MRU */ +#ifdef PPP_MULTILINK + u_short mrru; /* Value of MRRU, and multilink enable */ +#endif + u_char chap_mdtype; /* which MD type (hashing algorithm) */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + int numloops; /* Number of loops during magic number neg. */ + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#ifdef PPP_MULTILINK + struct epdisc endpoint; /* endpoint discriminator */ +#endif +} lcp_options; + +/* + * Values for phase from BSD pppd.h based on RFC 1661. + */ +typedef enum { + PHASE_DEAD = 0, + PHASE_INITIALIZE, + PHASE_ESTABLISH, + PHASE_AUTHENTICATE, + PHASE_CALLBACK, + PHASE_NETWORK, + PHASE_TERMINATE +} LinkPhase; + + + +extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */ +extern lcp_options lcp_wantoptions[]; +extern lcp_options lcp_gotoptions[]; +extern lcp_options lcp_allowoptions[]; +extern lcp_options lcp_hisoptions[]; +extern ext_accm xmit_accm[]; + + +void lcp_init (int); +void lcp_open (int); +void lcp_close (int, char *); +void lcp_lowerup (int); +void lcp_lowerdown(int); +void lcp_sprotrej (int, u_char *, int); /* send protocol reject */ + +extern struct protent lcp_protent; + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 + +#endif /* LCP_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.c new file mode 100644 index 0000000..3732a42 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.c @@ -0,0 +1,80 @@ +/***************************************************************************** +* magic.c - Network Random Number Generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original based on BSD magic.c. +*****************************************************************************/ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT + +#include "ppp_impl.h" +#include "randm.h" +#include "magic.h" + + +/* + * magicInit - Initialize the magic number generator. + * + * Since we use another random number generator that has its own + * initialization, we do nothing here. + */ +void magicInit() +{ + return; +} + +/* + * magic - Returns the next magic number. + */ +u32_t magic() +{ + return avRandom(); +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.h new file mode 100644 index 0000000..eba70d2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/magic.h @@ -0,0 +1,63 @@ +/***************************************************************************** +* magic.h - Network Random Number Generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $ + */ + +#ifndef MAGIC_H +#define MAGIC_H + +/* Initialize the magic number generator */ +void magicInit(void); + +/* Returns the next magic number */ +u32_t magic(void); + +#endif /* MAGIC_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.c new file mode 100644 index 0000000..dc3cc75 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.c @@ -0,0 +1,320 @@ +/* + *********************************************************************** + ** md5.c -- the source code for MD5 routines ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if CHAP_SUPPORT || MD5_SUPPORT + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "md5.h" + +#include + +/* + *********************************************************************** + ** Message-digest routines: ** + ** To form the message digest for a message M ** + ** (1) Initialize a context buffer mdContext using MD5Init ** + ** (2) Call MD5Update on mdContext and M ** + ** (3) Call MD5Final on mdContext ** + ** The message digest is now in mdContext->digest[0...15] ** + *********************************************************************** + */ + +/* forward declaration */ +static void Transform (u32_t *buf, u32_t *in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* 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) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +#ifdef __STDC__ +#define UL(x) x##UL +#else +#ifdef WIN32 +#define UL(x) x##UL +#else +#define UL(x) x +#endif +#endif + +/* The routine MD5Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void +MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (u32_t)0; + + /* Load magic initialization constants. */ + mdContext->buf[0] = (u32_t)0x67452301UL; + mdContext->buf[1] = (u32_t)0xefcdab89UL; + mdContext->buf[2] = (u32_t)0x98badcfeUL; + mdContext->buf[3] = (u32_t)0x10325476UL; +} + +/* The routine MD5Update updates the message-digest context to + account for the presence of each of the characters inBuf[0..inLen-1] + in the message whose digest is being computed. + */ +void +MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + +#if 0 + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf)); + PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf)); +#endif + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) { + mdContext->i[1]++; + } + mdContext->i[0] += ((u32_t)inLen << 3); + mdContext->i[1] += ((u32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +/* The routine MD5Final terminates the message-digest computation and + ends with the desired message digest in mdContext->digest[0...15]. + */ +void +MD5Final (unsigned char hash[], MD5_CTX *mdContext) +{ + u32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) { + in[i] = (((u32_t)mdContext->in[ii+3]) << 24) | + (((u32_t)mdContext->in[ii+2]) << 16) | + (((u32_t)mdContext->in[ii+1]) << 8) | + ((u32_t)mdContext->in[ii]); + } + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } + SMEMCPY(hash, mdContext->digest, 16); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void +Transform (u32_t *buf, u32_t *in) +{ + u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ + FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ + FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ + FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ + FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ + FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ + FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ + GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ + GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ + GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ + GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ + GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ + GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ + HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ + HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ + HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ + HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ + HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ + HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ + II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ + II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ + II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ + II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ + II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ + II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ + II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ + II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ + II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ + II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ + II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ + II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ + II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ + II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ + II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif /* CHAP_SUPPORT || MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.h new file mode 100644 index 0000000..e129533 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/md5.h @@ -0,0 +1,55 @@ +/* + *********************************************************************** + ** md5.h -- header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + *********************************************************************** + */ + +#ifndef MD5_H +#define MD5_H + +/* Data structure for MD5 (Message-Digest) computation */ +typedef struct { + u32_t i[2]; /* number of _bits_ handled mod 2^64 */ + u32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init ( MD5_CTX *mdContext); +void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final ( unsigned char hash[], MD5_CTX *mdContext); + +#endif /* MD5_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.c new file mode 100644 index 0000000..5fb9f88 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.c @@ -0,0 +1,628 @@ +/***************************************************************************** +* pap.c - Network Password Authentication Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-12 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "auth.h" +#include "pap.h" + +#include + +#if 0 /* UNUSED */ +static bool hide_password = 1; + +/* + * Command-line options. + */ +static option_t pap_option_list[] = { + { "hide-password", o_bool, &hide_password, + "Don't output passwords to log", 1 }, + { "show-password", o_bool, &hide_password, + "Show password string in debug log messages", 0 }, + { "pap-restart", o_int, &upap[0].us_timeouttime, + "Set retransmit timeout for PAP" }, + { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, + "Set max number of transmissions for auth-reqs" }, + { "pap-timeout", o_int, &upap[0].us_reqtimeout, + "Set time limit for peer PAP authentication" }, + { NULL } +}; +#endif + +/* + * Protocol entry points. + */ +static void upap_init (int); +static void upap_lowerup (int); +static void upap_lowerdown (int); +static void upap_input (int, u_char *, int); +static void upap_protrej (int); +#if PPP_ADDITIONAL_CALLBACKS +static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if PPP_ADDITIONAL_CALLBACKS + upap_printpkt, + NULL, +#endif /* PPP_ADDITIONAL_CALLBACKS */ + 1, + "PAP", +#if PPP_ADDITIONAL_CALLBACKS + NULL, + NULL, + NULL +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ + +static void upap_timeout (void *); +static void upap_reqtimeout(void *); +static void upap_rauthreq (upap_state *, u_char *, u_char, int); +static void upap_rauthack (upap_state *, u_char *, int, int); +static void upap_rauthnak (upap_state *, u_char *, int, int); +static void upap_sauthreq (upap_state *); +static void upap_sresp (upap_state *, u_char, u_char, char *, int); + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void +upap_init(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit)); + u->us_unit = unit; + u->us_user = NULL; + u->us_userlen = 0; + u->us_passwd = NULL; + u->us_passwdlen = 0; + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; + u->us_id = 0; + u->us_timeouttime = UPAP_DEFTIMEOUT; + u->us_maxtransmits = 10; + u->us_reqtimeout = UPAP_DEFREQTIME; +} + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void +upap_authwithpeer(int unit, char *user, char *password) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n", + unit, user, password, u->us_clientstate)); + + /* Save the username and password we're given */ + u->us_user = user; + u->us_userlen = (int)strlen(user); + u->us_passwd = password; + u->us_passwdlen = (int)strlen(password); + + u->us_transmits = 0; + + /* Lower layer up yet? */ + if (u->us_clientstate == UPAPCS_INITIAL || + u->us_clientstate == UPAPCS_PENDING) { + u->us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(u); /* Start protocol */ +} + + +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void +upap_authpeer(int unit) +{ + upap_state *u = &upap[unit]; + + /* Lower layer up yet? */ + if (u->us_serverstate == UPAPSS_INITIAL || + u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_PENDING; + return; + } + + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } +} + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void +upap_timeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n", + u->us_unit, u->us_timeouttime, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); + return; + } + + if (u->us_transmits >= u->us_maxtransmits) { + /* give up in disgust */ + UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n")); + u->us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(u->us_unit, PPP_PAP); + return; + } + + upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/ +} + + +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void +upap_reqtimeout(void *arg) +{ + upap_state *u = (upap_state *) arg; + + if (u->us_serverstate != UPAPSS_LISTEN) { + return; /* huh?? */ + } + + auth_peer_fail(u->us_unit, PPP_PAP); + u->us_serverstate = UPAPSS_BADAUTH; +} + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void +upap_lowerup(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_INITIAL) { + u->us_clientstate = UPAPCS_CLOSED; + } else if (u->us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(u); /* send an auth-request */ + /* now client state is UPAPCS__AUTHREQ */ + } + + if (u->us_serverstate == UPAPSS_INITIAL) { + u->us_serverstate = UPAPSS_CLOSED; + } else if (u->us_serverstate == UPAPSS_PENDING) { + u->us_serverstate = UPAPSS_LISTEN; + if (u->us_reqtimeout > 0) { + TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); + } + } +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void +upap_lowerdown(int unit) +{ + upap_state *u = &upap[unit]; + + UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); + + if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + } + if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } + + u->us_clientstate = UPAPCS_INITIAL; + u->us_serverstate = UPAPSS_INITIAL; +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void +upap_protrej(int unit) +{ + upap_state *u = &upap[unit]; + + if (u->us_clientstate == UPAPCS_AUTHREQ) { + UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n")); + auth_withpeer_fail(unit, PPP_PAP); + } + if (u->us_serverstate == UPAPSS_LISTEN) { + UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n")); + auth_peer_fail(unit, PPP_PAP); + } + upap_lowerdown(unit); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void +upap_input(int unit, u_char *inpacket, int l) +{ + upap_state *u = &upap[unit]; + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < (int)UPAP_HEADERLEN) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n")); + return; + } + if (len > l) { + UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: + upap_rauthreq(u, inp, id, len); + break; + + case UPAP_AUTHACK: + upap_rauthack(u, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(u, inp, id, len); + break; + + default: /* XXX Need code reject */ + UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len)); + break; + } +} + + +/* + * upap_rauth - Receive Authenticate. + */ +static void +upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len) +{ + u_char ruserlen, rpasswdlen; + char *ruser, *rpasswd; + u_char retcode; + char *msg; + int msglen; + + UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id)); + + if (u->us_serverstate < UPAPSS_LISTEN) { + return; + } + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (u->us_serverstate == UPAPSS_OPEN) { + upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (u->us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); + return; + } + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); + /* lwip: currently retcode is always UPAP_AUTHACK */ + BZERO(rpasswd, rpasswdlen); + + upap_sresp(u, retcode, id, msg, msglen); + + if (retcode == UPAP_AUTHACK) { + u->us_serverstate = UPAPSS_OPEN; + auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); + } else { + u->us_serverstate = UPAPSS_BADAUTH; + auth_peer_fail(u->us_unit, PPP_PAP); + } + + if (u->us_reqtimeout > 0) { + UNTIMEOUT(upap_reqtimeout, u); + } +} + + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void +upap_rauthack(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n")); + return; + } + + /* + * Parse message. + */ + if (len < (int)sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ + u->us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(u->us_unit, PPP_PAP); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nak. + */ +static void +upap_rauthnak(upap_state *u, u_char *inp, int id, int len) +{ + u_char msglen; + char *msg; + + LWIP_UNUSED_ARG(id); + + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); + + if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ + return; + } + + /* + * Parse message. + */ + if (len < sizeof (u_char)) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n")); + } else { + GETCHAR(msglen, inp); + if(msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + u->us_clientstate = UPAPCS_BADAUTH; + + UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n")); + auth_withpeer_fail(u->us_unit, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void +upap_sauthreq(upap_state *u) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + u->us_userlen + u->us_passwdlen; + outp = outpacket_buf[u->us_unit]; + + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++u->us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(u->us_userlen, outp); + BCOPY(u->us_user, outp, u->us_userlen); + INCPTR(u->us_userlen, outp); + PUTCHAR(u->us_passwdlen, outp); + BCOPY(u->us_passwd, outp, u->us_passwdlen); + + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id)); + + TIMEOUT(upap_timeout, u, u->us_timeouttime); + ++u->us_transmits; + u->us_clientstate = UPAPCS_AUTHREQ; +} + + +/* + * upap_sresp - Send a response (ack or nak). + */ +static void +upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) +{ + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + outp = outpacket_buf[u->us_unit]; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + BCOPY(msg, outp, msglen); + pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); + + UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); +} + +#if PPP_ADDITIONAL_CALLBACKS +static char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static int upap_printpkt( + u_char *p, + int plen, + void (*printer) (void *, char *, ...), + void *arg +) +{ + LWIP_UNUSED_ARG(p); + LWIP_UNUSED_ARG(plen); + LWIP_UNUSED_ARG(printer); + LWIP_UNUSED_ARG(arg); + return 0; +} +#endif /* PPP_ADDITIONAL_CALLBACKS */ + +#endif /* PAP_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.h new file mode 100644 index 0000000..c99a204 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pap.h @@ -0,0 +1,118 @@ +/***************************************************************************** +* pap.h - PPP Password Authentication Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-12-04 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef PAP_H +#define PAP_H + +#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short)) + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + +/* + * Each interface is described by upap structure. + */ +typedef struct upap_state { + int us_unit; /* Interface unit number */ + const char *us_user; /* User */ + int us_userlen; /* User length */ + const char *us_passwd; /* Password */ + int us_passwdlen; /* Password length */ + int us_clientstate; /* Client state */ + int us_serverstate; /* Server state */ + u_char us_id; /* Current id */ + int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */ + int us_transmits; /* Number of auth-reqs sent */ + int us_maxtransmits; /* Maximum number of auth-reqs to send */ + int us_reqtimeout; /* Time to wait for auth-req from peer */ +} upap_state; + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +extern upap_state upap[]; + +void upap_authwithpeer (int, char *, char *); +void upap_authpeer (int); + +extern struct protent pap_protent; + +#endif /* PAP_SUPPORT */ + +#endif /* PAP_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.c new file mode 100644 index 0000000..8e8fae9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.c @@ -0,0 +1,2045 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "lwip/ip.h" /* for ip_input() */ + +#include "pppdebug.h" + +#include "randm.h" +#include "fsm.h" +#if PAP_SUPPORT +#include "pap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap.h" +#endif /* CHAP_SUPPORT */ +#include "ipcp.h" +#include "lcp.h" +#include "magic.h" +#include "auth.h" +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ +#if PPPOE_SUPPORT +#include "netif/ppp_oe.h" +#endif /* PPPOE_SUPPORT */ + +#include "lwip/tcpip.h" +#include "lwip/api.h" +#include "lwip/snmp.h" + +#include + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback(). + * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1. + * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded). + */ +#ifndef PPP_INPROC_MULTITHREADED +#define PPP_INPROC_MULTITHREADED (NO_SYS==0) +#endif + +/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session. + * Default is 0: call pppos_input() for received raw characters, charcater + * reception is up to the port */ +#ifndef PPP_INPROC_OWNTHREAD +#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED +#endif + +#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED + #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1" +#endif + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} PPPDevStates; + +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07]) + +/************************/ +/*** LOCAL DATA TYPES ***/ +/************************/ + +/** RX buffer size: this may be configured smaller! */ +#ifndef PPPOS_RX_BUFSIZE +#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN) +#endif + +typedef struct PPPControlRx_s { + /** unit number / ppp descriptor */ + int pd; + /** the rx file descriptor */ + sio_fd_t fd; + /** receive buffer - encoded data is stored here */ +#if PPP_INPROC_OWNTHREAD + u_char rxbuf[PPPOS_RX_BUFSIZE]; +#endif /* PPP_INPROC_OWNTHREAD */ + + /* The input packet. */ + struct pbuf *inHead, *inTail; + +#if PPPOS_SUPPORT + u16_t inProtocol; /* The input protocol code. */ + u16_t inFCS; /* Input Frame Check Sequence value. */ +#endif /* PPPOS_SUPPORT */ + PPPDevStates inState; /* The input process state. */ + char inEscaped; /* Escape next character. */ + ext_accm inACCM; /* Async-Ctl-Char-Map for input. */ +} PPPControlRx; + +/* + * PPP interface control block. + */ +typedef struct PPPControl_s { + PPPControlRx rx; + char openFlag; /* True when in use. */ +#if PPPOE_SUPPORT + struct netif *ethif; + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ + int if_up; /* True when the interface is up. */ + int errCode; /* Code indicating why interface is down. */ +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ +#endif /* PPPOS_SUPPORT */ + u16_t mtu; /* Peer's mru */ + int pcomp; /* Does peer accept protocol compression? */ + int accomp; /* Does peer accept addr/ctl compression? */ + u_long lastXMit; /* Time of last transmission. */ + ext_accm outACCM; /* Async-Ctl-Char-Map for output. */ +#if PPPOS_SUPPORT && VJ_SUPPORT + int vjEnabled; /* Flag indicating VJ compression enabled. */ + struct vjcompress vjComp; /* Van Jacobson compression header. */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + struct netif netif; + + struct ppp_addrs addrs; + + void (*linkStatusCB)(void *ctx, int errCode, void *arg); + void *linkStatusCtx; + +} PPPControl; + + +/* + * Ioctl definitions. + */ + +struct npioctl { + int protocol; /* PPP procotol, e.g. PPP_IP */ + enum NPmode mode; +}; + + + +/***********************************/ +/*** LOCAL FUNCTION DECLARATIONS ***/ +/***********************************/ +#if PPPOS_SUPPORT +#if PPP_INPROC_OWNTHREAD +static void pppInputThread(void *arg); +#endif /* PPP_INPROC_OWNTHREAD */ +static void pppDrop(PPPControlRx *pcrx); +static void pppInProc(PPPControlRx *pcrx, u_char *s, int l); +static void pppFreeCurrentInputPacket(PPPControlRx *pcrx); +#endif /* PPPOS_SUPPORT */ + + +/******************************/ +/*** PUBLIC DATA STRUCTURES ***/ +/******************************/ +u_long subnetMask; + +static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +struct protent *ppp_protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif /* CBCP_SUPPORT */ + &ipcp_protent, +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ + NULL +}; + + +/* + * Buffers for outgoing packets. This must be accessed only from the appropriate + * PPP task so that it doesn't need to be protected to avoid collisions. + */ +u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ + +#if PPPOS_SUPPORT +/* + * FCS lookup table as calculated by genfcstab. + * @todo: smaller, slower implementation for lower memory footprint? + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char pppACCMMask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; + +#if PPP_INPROC_OWNTHREAD +/** Wake up the task blocked in reading from serial line (if any) */ +static void +pppRecvWakeup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd)); + if (pppControl[pd].openFlag != 0) { + sio_read_abort(pppControl[pd].fd); + } +} +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + +void +pppLinkTerminated(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPControl* pc; +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ + pc = &pppControl[pd]; + + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } + + pc->openFlag = 0;/**/ +#endif /* PPPOS_SUPPORT */ + } + PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n")); +} + +void +pppLinkDown(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd)); + +#if PPPOE_SUPPORT + if (pppControl[pd].ethif) { + pppoe_disconnect(pppControl[pd].pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/ + } +} + +/** Initiate LCP open request */ +static void +pppStart(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd)); + lcp_lowerup(pd); + lcp_open(pd); /* Start protocol */ + PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n")); +} + +/** LCP close request */ +static void +pppStop(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd)); + lcp_close(pd, "User request"); +} + +/** Called when carrier/link is lost */ +static void +pppHup(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd)); + lcp_lowerdown(pd); + link_terminated(pd); +} + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* Initialize the PPP subsystem. */ + +struct ppp_settings ppp_settings; + +void +pppInit(void) +{ + struct protent *protp; + int i, j; + + memset(&ppp_settings, 0, sizeof(ppp_settings)); + ppp_settings.usepeerdns = 1; + pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL); + + magicInit(); + + subnetMask = PP_HTONL(0xffffff00UL); + + for (i = 0; i < NUM_PPP; i++) { + /* Initialize each protocol to the standard option set. */ + for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) { + (*protp->init)(i); + } + } +} + +void +pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd) +{ + switch(authType) { + case PPPAUTHTYPE_NONE: + default: +#ifdef LWIP_PPP_STRICT_PAP_REJECT + ppp_settings.refuse_pap = 1; +#else /* LWIP_PPP_STRICT_PAP_REJECT */ + /* some providers request pap and accept an empty login/pw */ + ppp_settings.refuse_pap = 0; +#endif /* LWIP_PPP_STRICT_PAP_REJECT */ + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_ANY: + /* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 0; + break; + + case PPPAUTHTYPE_PAP: + ppp_settings.refuse_pap = 0; + ppp_settings.refuse_chap = 1; + break; + + case PPPAUTHTYPE_CHAP: + ppp_settings.refuse_pap = 1; + ppp_settings.refuse_chap = 0; + break; + } + + if(user) { + strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1); + ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0'; + } else { + ppp_settings.user[0] = '\0'; + } + + if(passwd) { + strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1); + ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0'; + } else { + ppp_settings.passwd[0] = '\0'; + } +} + +#if PPPOS_SUPPORT +/** Open a new PPP connection using the given I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. If this port + * connects to a modem, the modem connection must be + * established before calling this. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + * + * pppOpen() is directly defined to this function. + */ +int +pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + /* input pbuf left over from last session? */ + pppFreeCurrentInputPacket(&pc->rx); + /* @todo: is this correct or do I overwrite something? */ + memset(pc, 0, sizeof(PPPControl)); + pc->rx.pd = pd; + pc->rx.fd = fd; + + pc->openFlag = 1; + pc->fd = fd; + +#if VJ_SUPPORT + vj_compress_init(&pc->vjComp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */ + pc->outACCM[15] = 0x60; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd)); + pppStart(pd); +#if PPP_INPROC_OWNTHREAD + sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO); +#endif /* PPP_INPROC_OWNTHREAD */ + } + + return pd; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void pppOverEthernetLinkStatusCB(int pd, int up); + +void +pppOverEthernetClose(int pd) +{ + PPPControl* pc = &pppControl[pd]; + + /* *TJL* There's no lcp_deinit */ + lcp_close(pd, NULL); + + pppoe_destroy(&pc->netif); +} + +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx) +{ + PPPControl *pc; + int pd; + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + if (linkStatusCB == NULL) { + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + return PPPERR_PARAM; + } + + /* Find a free PPP session descriptor. Critical region? */ + for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++); + if (pd >= NUM_PPP) { + pd = PPPERR_OPEN; + } else { + pc = &pppControl[pd]; + memset(pc, 0, sizeof(PPPControl)); + pc->openFlag = 1; + pc->ethif = ethif; + + pc->linkStatusCB = linkStatusCB; + pc->linkStatusCtx = linkStatusCtx; + + lcp_wantoptions[pd].mru = PPPOE_MAXMTU; + lcp_wantoptions[pd].neg_asyncmap = 0; + lcp_wantoptions[pd].neg_pcompression = 0; + lcp_wantoptions[pd].neg_accompression = 0; + + lcp_allowoptions[pd].mru = PPPOE_MAXMTU; + lcp_allowoptions[pd].neg_asyncmap = 0; + lcp_allowoptions[pd].neg_pcompression = 0; + lcp_allowoptions[pd].neg_accompression = 0; + + if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) { + pc->openFlag = 0; + return PPPERR_OPEN; + } + + pppoe_connect(pc->pppoe_sc); + } + + return pd; +} +#endif /* PPPOE_SUPPORT */ + + +/* Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. */ +int +pppClose(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + PPPDEBUG(LOG_DEBUG, ("pppClose() called\n")); + + /* Disconnect */ +#if PPPOE_SUPPORT + if(pc->ethif) { + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); + } else +#endif /* PPPOE_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd)); + pc->errCode = PPPERR_USER; + /* This will leave us at PHASE_DEAD. */ + pppStop(pd); +#if PPP_INPROC_OWNTHREAD + pppRecvWakeup(pd); +#endif /* PPP_INPROC_OWNTHREAD */ +#endif /* PPPOS_SUPPORT */ + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +pppSigHUP(int pd) +{ + PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd)); + pppHup(pd); +} + +#if PPPOS_SUPPORT +static void +nPut(PPPControl *pc, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) { + PPPDEBUG(LOG_WARNING, + ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c)); + LINK_STATS_INC(link.err); + pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */ + snmp_inc_ifoutdiscards(&pc->netif); + pbuf_free(nb); + return; + } + } + + snmp_add_ifoutoctets(&pc->netif, nb->tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * pppAppend - append given character to end of given pbuf. If outACCM + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (outACCM && ESCAPE_P(*outACCM, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t +pppifOutputOverEthernet(int pd, struct pbuf *p) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + u_short protocol = PPP_IP; + int i=0; + u16_t tot_len; + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + if (!pc->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); + tot_len = pb->tot_len; + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, tot_len); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + +/* Send a packet on the given connection. */ +static err_t +pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr) +{ + int pd = (int)(size_t)netif->state; + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_short protocol = PPP_IP; + u_int fcsOut = PPP_INITFCS; + struct pbuf *headMB = NULL, *tailMB = NULL, *p; + u_char c; +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(ipaddr); + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n", + pd, PPP_IP, pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (lcp_phase[pd] == PHASE_DEAD) { + PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppifOutputOverEthernet(pd, pb); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + /* Grab an output buffer. */ + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pc->vjEnabled) { + switch (vj_compress_tcp(&pc->vjComp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad IP packet\n", pd)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + pbuf_free(headMB); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tailMB = headMB; + + /* Build the PPP header. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + + pc->lastXMit = sys_jiffies(); + if (!pc->accomp) { + fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS); + tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM); + fcsOut = PPP_FCS(fcsOut, PPP_UI); + tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM); + } + if (!pc->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + c = protocol & 0xFF; + fcsOut = PPP_FCS(fcsOut, c); + tailMB = pppAppend(c, tailMB, &pc->outACCM); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppifOutput[%d]: Alloc err - dropping proto=%d\n", + pd, protocol)); + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol)); + + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return ERR_OK; +} + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +pppIOCtl(int pd, int cmd, void *arg) +{ + PPPControl *pc = &pppControl[pd]; + int st = 0; + + if (pd < 0 || pd >= NUM_PPP) { + st = PPPERR_PARAM; + } else { + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pc->if_up); + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pc->errCode = *(int *)arg; + } else { + st = PPPERR_PARAM; + } + break; + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pc->errCode); + } else { + st = PPPERR_PARAM; + } + break; +#if PPPOS_SUPPORT + case PPPCTLG_FD: /* Get the fd associated with the ppp */ + if (arg) { + *(sio_fd_t *)arg = pc->fd; + } else { + st = PPPERR_PARAM; + } + break; +#endif /* PPPOS_SUPPORT */ + default: + st = PPPERR_PARAM; + break; + } + } + + return st; +} + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short +pppMTU(int pd) +{ + PPPControl *pc = &pppControl[pd]; + u_short st; + + /* Validate parameters. */ + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + } else { + st = pc->mtu; + } + + return st; +} + +#if PPPOE_SUPPORT +int +pppWriteOverEthernet(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; + struct pbuf *pb; + + /* skip address & flags */ + s += 2; + n -= 2; + + LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + pbuf_header(pb, -(s16_t)PPPOE_HDRLEN); + + pc->lastXMit = sys_jiffies(); + + MEMCPY(pb->payload, s, n); + + if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pc->netif, (u16_t)n); + snmp_inc_ifoutucastpkts(&pc->netif); + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int +pppWrite(int pd, const u_char *s, int n) +{ + PPPControl *pc = &pppControl[pd]; +#if PPPOS_SUPPORT + u_char c; + u_int fcsOut; + struct pbuf *headMB, *tailMB; +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT + if(pc->ethif) { + return pppWriteOverEthernet(pd, s, n); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOS_SUPPORT + headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (headMB == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + tailMB = headMB; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) { + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + } + pc->lastXMit = sys_jiffies(); + + fcsOut = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcsOut = PPP_FCS(fcsOut, c); + + /* Copy to output buffer escaping special characters. */ + tailMB = pppAppend(c, tailMB, &pc->outACCM); + } + + /* Add FCS and trailing flag. */ + c = ~fcsOut & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + c = (~fcsOut >> 8) & 0xFF; + tailMB = pppAppend(c, tailMB, &pc->outACCM); + tailMB = pppAppend(PPP_FLAG, tailMB, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tailMB) { + PPPDEBUG(LOG_WARNING, + ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len)); + /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + pbuf_free(headMB); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pc->netif); + return PPPERR_ALLOC; + } + + PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len)); + /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */ + nPut(pc, headMB); +#endif /* PPPOS_SUPPORT */ + + return PPPERR_NONE; +} + +/* + * ppp_send_config - configure the transmit characteristics of + * the ppp interface. + */ +void +ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + + pc->mtu = mtu; + pc->pcomp = pcomp; + pc->accomp = accomp; + + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF); + } + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n", + unit, + pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3])); +} + + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void +ppp_set_xaccm(int unit, ext_accm *accm) +{ + SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm)); + PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n", + unit, + pppControl[unit].outACCM[0], + pppControl[unit].outACCM[1], + pppControl[unit].outACCM[2], + pppControl[unit].outACCM[3])); +} + + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +void +ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp) +{ + PPPControl *pc = &pppControl[unit]; + int i; + SYS_ARCH_DECL_PROTECT(lev); + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ + SYS_ARCH_PROTECT(lev); + for (i = 0; i < 32 / 8; i++) { + /* @todo: does this work? ext_accm has been modified from pppd! */ + pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8)); + } + SYS_ARCH_UNPROTECT(lev); + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n", + unit, + pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3])); +} + +#if 0 +/* + * ccp_test - ask kernel whether a given compression method + * is acceptable for use. Returns 1 if the method and parameters + * are OK, 0 if the method is known but the parameters are not OK + * (e.g. code size should be reduced), or -1 if the method is unknown. + */ +int +ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr) +{ + return 0; /* XXX Currently no compression. */ +} + +/* + * ccp_flags_set - inform kernel about the current state of CCP. + */ +void +ccp_flags_set(int unit, int isopen, int isup) +{ + /* XXX */ +} + +/* + * ccp_fatal_error - returns 1 if decompression was disabled as a + * result of an error detected after decompression of a packet, + * 0 otherwise. This is necessary because of patent nonsense. + */ +int +ccp_fatal_error(int unit) +{ + /* XXX */ + return 0; +} +#endif + +/* + * get_idle_time - return how long the link has been idle. + */ +int +get_idle_time(int u, struct ppp_idle *ip) +{ + /* XXX */ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(ip); + + return 0; +} + + +/* + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t +GetMask(u32_t addr) +{ + u32_t mask, nmask; + + addr = htonl(addr); + if (IP_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IP_CLASSA_NET; + } else if (IP_CLASSB(addr)) { + nmask = IP_CLASSB_NET; + } else { + nmask = IP_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = subnetMask | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + + return mask; +} + +/* + * sifvjcomp - config tcp header compression + */ +int +sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid) +{ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPControl *pc = &pppControl[pd]; + + pc->vjEnabled = vjcomp; + pc->vjComp.compressSlot = cidcomp; + pc->vjComp.maxSlotIndex = maxcid; + PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + LWIP_UNUSED_ARG(pd); + LWIP_UNUSED_ARG(vjcomp); + LWIP_UNUSED_ARG(cidcomp); + LWIP_UNUSED_ARG(maxcid); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +/* + * pppifNetifInit - netif init callback + */ +static err_t +pppifNetifInit(struct netif *netif) +{ + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = pppifOutput; + netif->mtu = pppMTU((int)(size_t)netif->state); + netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP; +#if LWIP_NETIF_HOSTNAME + /* @todo: Initialize interface hostname */ + /* netif_set_hostname(netif, "lwip"); */ +#endif /* LWIP_NETIF_HOSTNAME */ + return ERR_OK; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int +sifup(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_remove(&pc->netif); + if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask, + &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) { + netif_set_up(&pc->netif); + pc->if_up = 1; + pc->errCode = PPPERR_NONE; + + PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs); + } + } else { + st = 0; + PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd)); + } + } + + return st; +} + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int +sifnpmode(int u, int proto, enum NPmode mode) +{ + LWIP_UNUSED_ARG(u); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * sifdown - Config the interface down and disable IP. + */ +int +sifdown(int pd) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd)); + } else { + pc->if_up = 0; + /* make sure the netif status callback is called */ + netif_set_down(&pc->netif); + netif_remove(&pc->netif); + PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode)); + if (pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL); + } + } + return st; +} + +/** + * sifaddr - Config the interface IP addresses and netmask. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h His IP address ??? + * @param m IP subnet mask ??? + * @param ns1 Primary DNS + * @param ns2 Secondary DNS + */ +int +sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o)); + SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h)); + SMEMCPY(&pc->addrs.netmask, &m, sizeof(m)); + SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2)); + } + return st; +} + +/** + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + * @param pd Interface unit ??? + * @param o Our IP address ??? + * @param h IP broadcast address ??? + */ +int +cifaddr( int pd, u32_t o, u32_t h) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(o); + LWIP_UNUSED_ARG(h); + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pc->addrs.netmask, 255,255,255,0); + IP4_ADDR(&pc->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pc->addrs.dns2, 0,0,0,0); + } + return st; +} + +/* + * sifdefaultroute - assign a default route through the address given. + */ +int +sifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(&pc->netif); + } + + /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */ + + return st; +} + +/* + * cifdefaultroute - delete a default route through the address given. + */ +int +cifdefaultroute(int pd, u32_t l, u32_t g) +{ + PPPControl *pc = &pppControl[pd]; + int st = 1; + + LWIP_UNUSED_ARG(l); + LWIP_UNUSED_ARG(g); + + if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) { + st = 0; + PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd)); + } else { + netif_set_default(NULL); + } + + return st; +} + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD +/* The main PPP process function. This implements the state machine according + * to section 4 of RFC 1661: The Point-To-Point Protocol. */ +static void +pppInputThread(void *arg) +{ + int count; + PPPControlRx *pcrx = arg; + + while (lcp_phase[pcrx->pd] != PHASE_DEAD) { + count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE); + if(count > 0) { + pppInProc(pcrx, pcrx->rxbuf, count); + } else { + /* nothing received, give other tasks a chance to run */ + sys_msleep(1); + } + } +} +#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */ + +#if PPPOE_SUPPORT + +void +pppOverEthernetInitFailed(int pd) +{ + PPPControl* pc; + + pppHup(pd); + pppStop(pd); + + pc = &pppControl[pd]; + pppoe_destroy(&pc->netif); + pc->openFlag = 0; + + if(pc->linkStatusCB) { + pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL); + } +} + +static void +pppOverEthernetLinkStatusCB(int pd, int up) +{ + if(up) { + PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd)); + pppStart(pd); + } else { + pppOverEthernetInitFailed(pd); + } +} +#endif /* PPPOE_SUPPORT */ + +struct pbuf * +pppSingleBuf(struct pbuf *p) +{ + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG(LOG_ERR, + ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +struct pppInputHeader { + int unit; + u16_t proto; +}; + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +static void +pppInput(void *arg) +{ + struct pbuf *nb = (struct pbuf *)arg; + u16_t protocol; + int pd; + + pd = ((struct pppInputHeader *)nb->payload)->unit; + protocol = ((struct pppInputHeader *)nb->payload)->proto; + + if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + snmp_inc_ifinucastpkts(&pppControl[pd].netif); + snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) { + if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) || + (lcp_phase[pd] != PHASE_AUTHENTICATE)) { + PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd])); + goto drop; + } + } + + switch(protocol) { + case PPP_VJC_COMP: /* VJ compressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ +#if PPPOS_SUPPORT && VJ_SUPPORT + PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + /* No handler for this protocol so drop the packet. */ + PPPDEBUG(LOG_INFO, + ("pppInput[%d]: drop VJ UnComp in %d:.*H\n", + pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload)); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + break; + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len)); + if (pppControl[pd].netif.input) { + pppControl[pd].netif.input(nb, &pppControl[pd].netif); + return; + } + break; + + default: { + struct protent *protp; + int i; + + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len)); + nb = pppSingleBuf(nb); + (*protp->input)(pd, nb->payload, nb->len); + PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd)); + goto out; + } + } + + /* No handler for this protocol so reject the packet. */ + PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len)); + if (pbuf_header(nb, sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } +#if BYTE_ORDER == LITTLE_ENDIAN + protocol = htons(protocol); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + SMEMCPY(nb->payload, &protocol, sizeof(protocol)); + lcp_sprotrej(pd, nb->payload, nb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + +out: + pbuf_free(nb); + return; +} + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +pppFreeCurrentInputPacket(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { + if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) { + pbuf_free(pcrx->inTail); + } + pbuf_free(pcrx->inHead); + pcrx->inHead = NULL; + } + pcrx->inTail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +pppDrop(PPPControlRx *pcrx) +{ + if (pcrx->inHead != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload)); +#endif + PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead)); + } + pppFreeCurrentInputPacket(pcrx); +#if VJ_SUPPORT + vj_uncompress_err(&pppControl[pcrx->pd].vjComp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); +} + +#if !PPP_INPROC_OWNTHREAD +/** Pass received raw characters to PPPoS to be decoded. This function is + * thread-safe and can be called from a dedicated RX-thread or from a main-loop. + * + * @param pd PPP descriptor index, returned by pppOpen() + * @param data received data + * @param len length of received data + */ +void +pppos_input(int pd, u_char* data, int len) +{ + pppInProc(&pppControl[pd].rx, data, len); +} +#endif + +/** + * Process a received octet string. + */ +static void +pppInProc(PPPControlRx *pcrx, u_char *s, int l) +{ + struct pbuf *nextNBuf; + u_char curChar; + u_char escaped; + SYS_ARCH_DECL_PROTECT(lev); + + PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l)); + while (l-- > 0) { + curChar = *s++; + + SYS_ARCH_PROTECT(lev); + escaped = ESCAPE_P(pcrx->inACCM, curChar); + SYS_ARCH_UNPROTECT(lev); + /* Handle special characters. */ + if (escaped) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (curChar == PPP_ESCAPE) { + pcrx->inEscaped = 1; + /* Check for the flag character. */ + } else if (curChar == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pcrx->inState <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pcrx->inState < PDDATA) { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping incomplete packet %d\n", + pcrx->pd, pcrx->inState)); + LINK_STATS_INC(link.lenerr); + pppDrop(pcrx); + /* If the fcs is invalid, drop the packet. */ + } else if (pcrx->inFCS != PPP_GOODFCS) { + PPPDEBUG(LOG_INFO, + ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n", + pcrx->pd, pcrx->inFCS, pcrx->inProtocol)); + /* Note: If you get lots of these, check for UART frame errors or try different baud rate */ + LINK_STATS_INC(link.chkerr); + pppDrop(pcrx); + /* Otherwise it's a good packet so pass it on. */ + } else { + struct pbuf *inp; + /* Trim off the checksum. */ + if(pcrx->inTail->len >= 2) { + pcrx->inTail->len -= 2; + + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + } else { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + } + + pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + inp = pcrx->inHead; + /* Packet consumed, release our references. */ + pcrx->inHead = NULL; + pcrx->inTail = NULL; +#if PPP_INPROC_MULTITHREADED + if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) { + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd)); + pbuf_free(inp); + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif); + } +#else /* PPP_INPROC_MULTITHREADED */ + pppInput(inp); +#endif /* PPP_INPROC_MULTITHREADED */ + } + + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + pcrx->inState = PDADDRESS; + pcrx->inEscaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pcrx->inEscaped) { + pcrx->inEscaped = 0; + curChar ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pcrx->inState) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (curChar != PPP_ALLSTATIONS) { + break; + } + + /* Fall through */ + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pcrx->inFCS = PPP_INITFCS; + + /* Fall through */ + case PDADDRESS: /* Process address field. */ + if (curChar == PPP_ALLSTATIONS) { + pcrx->inState = PDCONTROL; + break; + } + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (curChar == PPP_UI) { + pcrx->inState = PDPROTOCOL1; + break; + } +#if 0 + else { + PPPDEBUG(LOG_WARNING, + ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar)); + pcrx->inState = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (curChar & 1) { + pcrx->inProtocol = curChar; + pcrx->inState = PDDATA; + } else { + pcrx->inProtocol = (u_int)curChar << 8; + pcrx->inState = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pcrx->inProtocol |= curChar; + pcrx->inState = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) { + if (pcrx->inTail != NULL) { + pcrx->inTail->tot_len = pcrx->inTail->len; + if (pcrx->inTail != pcrx->inHead) { + pbuf_cat(pcrx->inHead, pcrx->inTail); + /* give up the inTail reference now */ + pcrx->inTail = NULL; + } + } + /* If we haven't started a packet, we need a packet header. */ + nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (nextNBuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd)); + LINK_STATS_INC(link.memerr); + pppDrop(pcrx); + pcrx->inState = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pcrx->inHead == NULL) { + struct pppInputHeader *pih = nextNBuf->payload; + + pih->unit = pcrx->pd; + pih->proto = pcrx->inProtocol; + + nextNBuf->len += sizeof(*pih); + + pcrx->inHead = nextNBuf; + } + pcrx->inTail = nextNBuf; + } + /* Load character into buffer. */ + ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar; + break; + } + + /* update the frame check sequence number. */ + pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar); + } + } /* while (l-- > 0), all bytes processed */ + + avRandomize(); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +void +pppInProcOverEthernet(int pd, struct pbuf *pb) +{ + struct pppInputHeader *pih; + u16_t inProtocol; + + if(pb->len < sizeof(inProtocol)) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n")); + goto drop; + } + + inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + + /* make room for pppInputHeader - should not fail */ + if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) { + PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n")); + goto drop; + } + + pih = pb->payload; + + pih->unit = pd; + pih->proto = inProtocol; + + /* Dispatch the packet thereby consuming it. */ + pppInput(pb); + return; + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pppControl[pd].netif); + pbuf_free(pb); + return; +} +#endif /* PPPOE_SUPPORT */ + +#if LWIP_NETIF_STATUS_CALLBACK +/** Set the status callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param status_callback pointer to the status callback function + * + * @see netif_set_status_callback + */ +void +ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback) +{ + netif_set_status_callback(&pppControl[pd].netif, status_callback); +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** Set the link callback of a PPP's netif + * + * @param pd The PPP descriptor returned by pppOpen() + * @param link_callback pointer to the link callback function + * + * @see netif_set_link_callback + */ +void +ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback) +{ + netif_set_link_callback(&pppControl[pd].netif, link_callback); +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.h new file mode 100644 index 0000000..08d6e62 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp.h @@ -0,0 +1,201 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + + +#ifndef __u_char_defined + +/* Type definitions for BSD code. */ +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; + +#endif + + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM -1 /* Invalid parameter. */ +#define PPPERR_OPEN -2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC -4 /* Unable to allocate resources. */ +#define PPPERR_USER -5 /* User interrupt. */ +#define PPPERR_CONNECT -6 /* Connection lost. */ +#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +struct ppp_addrs { + ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2; +}; + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* Initialize the PPP subsystem. */ +void pppInit(void); + +/* Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +enum pppAuthType { + PPPAUTHTYPE_NONE, + PPPAUTHTYPE_ANY, + PPPAUTHTYPE_PAP, + PPPAUTHTYPE_CHAP +}; + +void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd); + +/* Link status callback function prototype */ +typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg); + +#if PPPOS_SUPPORT +/* + * Open a new PPP connection using the given serial I/O device. + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * Return a new PPP connection descriptor on success or + * an error code (negative) on failure. + */ +int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +/* + * Open a new PPP Over Ethernet (PPPOE) connection. + */ +int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name, + pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx); +#endif /* PPPOE_SUPPORT */ + +/* for source code compatibility */ +#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls) + +/* + * Close a PPP connection and release the descriptor. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int pppClose(int pd); + +/* + * Indicate to the PPP process that the line has disconnected. + */ +void pppSigHUP(int pd); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int pppIOCtl(int pd, int cmd, void *arg); + +/* + * Return the Maximum Transmission Unit for the given PPP connection. + */ +u_short pppMTU(int pd); + +#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD +/* + * PPP over Serial: this is the input function to be called for received data. + * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking + * sio_read() is used, so this is deactivated. + */ +void pppos_input(int pd, u_char* data, int len); +#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */ + + +#if LWIP_NETIF_STATUS_CALLBACK +/* Set an lwIP-style status-callback for the selected PPP device */ +void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK +/* Set an lwIP-style link-callback for the selected PPP device */ +void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_impl.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_impl.h new file mode 100644 index 0000000..89aea60 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_impl.h @@ -0,0 +1,363 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#ifndef PPP_IMPL_H +#define PPP_IMPL_H + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp.h" +#include "lwip/def.h" +#include "lwip/sio.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/timers.h" + +/** Some defines for code we skip compared to the original pppd. + * These are just here to minimise the use of the ugly "#if 0". */ +#define PPP_ADDITIONAL_CALLBACKS 0 + +/** Some error checks to test for unsupported code */ +#if CBCP_SUPPORT +#error "CBCP is not supported in lwIP PPP" +#endif +#if CCP_SUPPORT +#error "CCP is not supported in lwIP PPP" +#endif + +/* + * pppd.h - PPP daemon global declarations. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +/* + * ppp_defs.h - PPP definitions. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + + +/* + * Constants and structures defined by the internet system, + * Per RFC 790, September 1981, and numerous additions. + */ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#define PPP_COMP 0xfd /* compressed packet */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp); (cp)++; (s) <<= 8; \ + (s) |= *(cp); (cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s & 0xff); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; (l) <<= 8; \ + (l) |= *(cp); (cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l)) +#define BCOPY(s, d, l) MEMCPY((d), (s), (l)) +#define BZERO(s, n) memset(s, 0, n) + +#if PPP_DEBUG +#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); } +#else /* PPP_DEBUG */ +#define PRINTMSG(m, l) +#endif /* PPP_DEBUG */ + +/* + * MAKEHEADER - Add PPP Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (int unit); + /* Process a received packet */ + void (*input) (int unit, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (int unit); + /* Lower layer has come up */ + void (*lowerup) (int unit); + /* Lower layer has gone down */ + void (*lowerdown) (int unit); + /* Open the protocol */ + void (*open) (int unit); + /* Close the protocol */ + void (*close) (int unit, char *reason); +#if PPP_ADDITIONAL_CALLBACKS + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); + /* Process a received data packet */ + void (*datainput) (int unit, u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ + int enabled_flag; /* 0 if protocol is disabled */ + char *name; /* Text name of protocol */ +#if PPP_ADDITIONAL_CALLBACKS + /* Check requested options, assign defaults */ + void (*check_options) (u_long); + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* PPP_ADDITIONAL_CALLBACKS */ +}; + +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + u_short xmit_idle; /* seconds since last NP packet sent */ + u_short recv_idle; /* seconds since last NP packet received */ +}; + +struct ppp_settings { + + u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */ + u_int auth_required : 1; /* Peer is required to authenticate */ + u_int explicit_remote : 1; /* remote_name specified with remotename opt */ + u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ + u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ + u_int usehostname : 1; /* Use hostname for our_name */ + u_int usepeerdns : 1; /* Ask peer for DNS adds */ + + u_short idle_time_limit; /* Shut down link if idle for this long */ + int maxconnect; /* Maximum connect time (seconds) */ + + char user [MAXNAMELEN + 1]; /* Username for PAP */ + char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */ + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +}; + +/***************************** +*** PUBLIC DATA STRUCTURES *** +*****************************/ + +/* Buffers for outgoing packets. */ +extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN]; + +extern struct ppp_settings ppp_settings; + +extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */ + + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Write n characters to a ppp link. + * RETURN: >= 0 Number of characters written, -1 Failed to write to device. + */ +int pppWrite(int pd, const u_char *s, int n); + +void pppInProcOverEthernet(int pd, struct pbuf *pb); + +struct pbuf *pppSingleBuf(struct pbuf *p); + +void pppLinkTerminated(int pd); + +void pppLinkDown(int pd); + +/* Configure i/f transmit parameters */ +void ppp_send_config (int, u16_t, u32_t, int, int); +/* Set extended transmit ACCM */ +void ppp_set_xaccm (int, ext_accm *); +/* Configure i/f receive parameters */ +void ppp_recv_config (int, int, u32_t, int, int); +/* Find out how long link has been idle */ +int get_idle_time (int, struct ppp_idle *); + +/* Configure VJ TCP header compression */ +int sifvjcomp (int, int, u8_t, u8_t); +/* Configure i/f down (for IP) */ +int sifup (int); +/* Set mode for handling packets for proto */ +int sifnpmode (int u, int proto, enum NPmode mode); +/* Configure i/f down (for IP) */ +int sifdown (int); +/* Configure IP addresses for i/f */ +int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t); +/* Reset i/f IP addresses */ +int cifaddr (int, u32_t, u32_t); +/* Create default route through i/f */ +int sifdefaultroute (int, u32_t, u32_t); +/* Delete default route through i/f */ +int cifdefaultroute (int, u32_t, u32_t); + +/* Get appropriate netmask for address */ +u32_t GetMask (u32_t); + +#endif /* PPP_SUPPORT */ + +#endif /* PPP_IMPL_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_oe.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_oe.c new file mode 100644 index 0000000..fdf52ae --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/ppp_oe.c @@ -0,0 +1,1132 @@ +/***************************************************************************** +* ppp_oe.c - 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. + */ + +#include "lwip/opt.h" + +#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp_oe.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "lwip/timers.h" +#include "lwip/memp.h" + +#include +#include + + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (u8_t)((VAL) / 256); \ + *(PTR)++ = (u8_t)((VAL) % 256) + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#error "PPPOE_SERVER is not yet supported under lwIP!" +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +#ifndef PPPOE_ERRORSTRING_LEN +#define PPPOE_ERRORSTRING_LEN 64 +#endif +static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN]; + + +/* input routines */ +static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *); + +/* management routines */ +static int pppoe_do_disconnect(struct pppoe_softc *); +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *); +static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *); + +/** linked list of created pppoe interfaces */ +static struct pppoe_softc *pppoe_softc_list; + +err_t +pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF); + if (sc == NULL) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->sc_pd = pd; + sc->sc_linkStatusCB = linkStatusCB; + sc->sc_ethif = ethif; + + /* put the new interface at the head of the list */ + sc->next = pppoe_softc_list; + pppoe_softc_list = sc; + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct netif *ifp) +{ + struct pppoe_softc *sc, *prev = NULL; + + for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) { + if (sc->sc_ethif == ifp) { + break; + } + } + + if(!(sc && (sc->sc_ethif == ifp))) { + return ERR_IF; + } + + sys_untimeout(pppoe_timeout, sc); + if (prev == NULL) { + /* remove sc from the head of the list */ + pppoe_softc_list = sc->next; + } else { + /* remove sc from the list */ + prev->next = sc->next; + } + +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } +#endif /* PPPOE_TODO */ + memp_free(MEMP_PPPOE_IF, sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc * +pppoe_find_softc_by_session(u_int session, struct netif *rcvif) +{ + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session) { + if (sc->sc_ethif == rcvif) { + return sc; + } else { + return NULL; + } + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc * +pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) +{ + struct pppoe_softc *sc, *t; + + if (pppoe_softc_list == NULL) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state); + return NULL; + } + if (sc->sc_ethif != rcvif) { + printf("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(struct pppoe_softc *sc) +{ + sc->sc_linkStatusCB(sc->sc_pd, 1); +} + +/* analyze and handle a single received packet while not in session state */ +static void +pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + u8_t *ac_cookie; + u16_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err, errortag; + struct eth_hdr *ethhdr; + + pb = pppSingleBuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + errortag = 0; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < PPPOE_HEADERLEN) { + printf("pppoe: packet too short: %d\n", pb->len); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + errortag = 1; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + errortag = 1; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + errortag = 1; + break; + } + if (err_msg) { + if (errortag && len) { + u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1); + strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len); + pppoe_error_tmp[error_len-1] = '\0'; + printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp); + } else { + printf("%s: %s\n", devname, err_msg); + } + if (errortag) { + goto done; + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* printf("pppoe: free passive interface is not found\n"); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + printf("pppoe: received PADR but not includes ac_cookie\n"); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + printf("pppoe: received PADR but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + printf("pppoe: received PADO but could not find request for it\n"); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + goto done; + } + if (ac_cookie) { + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + sys_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + (u16_t)ph->code, session); + } else { + printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_disc_input(struct netif *netif, struct pbuf *p) +{ + /* avoid error messages if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + pppoe_dispatch_disc_pkt(netif, p); + } else { + pbuf_free(p); + } +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = pppSingleBuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + printf("pppoe_data_input: could not get PPPoE header\n"); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + printf("pppoe: input for unknown session 0x%x, sending PADT\n", session); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + pppInProcOverEthernet(sc->sc_pd, pb); + + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len; +#ifdef PPPOE_TODO + int l1 = 0, l2 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + l1 = (int)strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = (int)strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } +#endif /* PPPOE_TODO */ + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } +#endif /* PPPOE_TODO */ + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + int retry_wait, err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + + /* initialize for quick retry mode */ + retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); + + sc->sc_padi_retried++; + if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_STATE_CLOSING: + pppoe_do_disconnect(sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + err = pppoe_send_padi(sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + /* + * Do not call pppoe_disconnect here, the upper layer state + * machine gets confused by this. We must return from this + * function and defer disconnecting to the timeout handler. + */ + sc->sc_state = PPPOE_STATE_CLOSING; + sys_timeout(20, pppoe_timeout, sc); +} + +static int +pppoe_do_disconnect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state < PPPOE_STATE_SESSION) { + err = EBUSY; + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + } + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + return err; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + sc->sc_state = PPPOE_STATE_CLOSING; + + sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */ + + /* clear connection state */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_INITIAL; +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; +#ifdef PPPOE_TODO + size_t l1 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } +#endif /* PPPOE_TODO */ + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC); + MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload + sizeof (struct eth_hdr); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload + sizeof(struct eth_hdr); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + printf("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_linkStatusCB(sc->sc_pd, 0); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; +} + +#endif /* PPPOE_SUPPORT */ + diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pppdebug.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pppdebug.h new file mode 100644 index 0000000..8134997 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/pppdebug.h @@ -0,0 +1,73 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* 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 (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/* Trace levels. */ +#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_INFO (PPP_DEBUG) +#define LOG_DETAIL (PPP_DEBUG) +#define LOG_DEBUG (PPP_DEBUG) + + +#define TRACELCP PPP_DEBUG + +#if PPP_DEBUG + +#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b) +#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b) +#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b) +#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b) + +#else /* PPP_DEBUG */ + +#define AUTHDEBUG(a, b) +#define IPCPDEBUG(a, b) +#define UPAPDEBUG(a, b) +#define LCPDEBUG(a, b) +#define FSMDEBUG(a, b) +#define CHAPDEBUG(a, b) +#define PPPDEBUG(a, b) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.c new file mode 100644 index 0000000..b736091 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.c @@ -0,0 +1,249 @@ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "md5.h" +#include "randm.h" + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include + +#if MD5_SUPPORT /* this module depends on MD5 */ +#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char randPool[RANDPOOLSZ]; /* Pool of randomness. */ +static long randCount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Since this is to be called on power up, we don't have much + * system randomess to work with. Here all we use is the + * real-time clock. We'll accumulate more randomness as soon + * as things start happening. + */ +void +avRandomInit() +{ + avChurnRand(NULL, 0); +} + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +void +avChurnRand(char *randData, u32_t randLen) +{ + MD5_CTX md5; + + /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */ + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + if (randData) { + MD5Update(&md5, (u_char *)randData, randLen); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + char foobar; + } sysData; + + /* Load sysData fields here. */ + MD5Update(&md5, (u_char *)&sysData, sizeof(sysData)); + } + MD5Final((u_char *)randPool, &md5); +/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */ +} + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Note: It's important that there be sufficient randomness in randPool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call churnRand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * randCount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void +avGenRand(char *buf, u32_t bufLen) +{ + MD5_CTX md5; + u_char tmp[16]; + u32_t n; + + while (bufLen > 0) { + n = LWIP_MIN(bufLen, RANDPOOLSZ); + MD5Init(&md5); + MD5Update(&md5, (u_char *)randPool, sizeof(randPool)); + MD5Update(&md5, (u_char *)&randCount, sizeof(randCount)); + MD5Final(tmp, &md5); + randCount++; + MEMCPY(buf, tmp, n); + buf += n; + bufLen -= n; + } +} + +/* + * Return a new random number. + */ +u32_t +avRandom() +{ + u32_t newRand; + + avGenRand((char *)&newRand, sizeof(newRand)); + + return newRand; +} + +#else /* MD5_SUPPORT */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int avRandomized = 0; /* Set when truely randomized. */ +static u32_t avRandomSeed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void +avRandomInit() +{ +#if 0 + /* Get a pointer into the last 4 bytes of clockBuf. */ + u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]); + + /* + * Initialize our seed using the real-time clock, the idle + * counter, the millisecond timer, and the hardware timer + * tick counter. The real-time clock and the hardware + * tick counter are the best sources of randomness but + * since the tick counter is only 16 bit (and truncated + * at that), the idle counter and millisecond timer + * (which may be small values) are added to help + * randomize the lower 16 bits of the seed. + */ + readClk(); + avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr + + ppp_mtime() + ((u32_t)TM1 << 16) + TM1; +#else + avRandomSeed += sys_jiffies(); /* XXX */ +#endif + + /* Initialize the Borland random number generator. */ + srand((unsigned)avRandomSeed); +} + +/* + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void +avRandomize(void) +{ + static u32_t last_jiffies; + + if (!avRandomized) { + avRandomized = !0; + avRandomInit(); + /* The initialization function also updates the seed. */ + } else { + /* avRandomSeed += (avRandomSeed << 16) + TM1; */ + avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t +avRandom() +{ + return ((((u32_t)rand() << 16) + rand()) + avRandomSeed); +} + +#endif /* MD5_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.h new file mode 100644 index 0000000..a0984b0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/randm.h @@ -0,0 +1,81 @@ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#ifndef RANDM_H +#define RANDM_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ +/* + * Initialize the random number generator. + */ +void avRandomInit(void); + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + */ +void avChurnRand(char *randData, u32_t randLen); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +#if MD5_SUPPORT +#define avRandomize() avChurnRand(NULL, 0) +#else /* MD5_SUPPORT */ +void avRandomize(void); +#endif /* MD5_SUPPORT */ + +/* + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using churnRand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void avGenRand(char *buf, u32_t bufLen); + +/* + * Return a new random number. + */ +u32_t avRandom(void); + + +#endif /* RANDM_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.c new file mode 100644 index 0000000..40fdad1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.c @@ -0,0 +1,652 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" + +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "ppp_impl.h" +#include "pppdebug.h" + +#include "vj.h" + +#include + +#if VJ_SUPPORT + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_char i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp); \ + cp += 3; \ + } else { \ + u32_t tmp = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp); \ + cp += 3; \ + } else { \ + u_short tmp = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip_hdr *ip = (struct ip_hdr *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = IPH_HL(ip); + register struct tcp_hdr *oth; + register struct tcp_hdr *th; + register u_short deltaS, deltaA; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (IPH_PROTO(ip) != IP_PROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcp_hdr *)&((long *)ip)[hlen]; + if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src) + || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip_addr_cmp(&ip->src, &cs->cs_ip.src) + && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (TCPH_FLAGS(th) & TCP_URG) { + deltaS = ntohs(th->urgp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->urgp != oth->urgp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && + ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { + break; + } + + /* (fall through) */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip))); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (TCPH_FLAGS(th) & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->chksum); + BCOPY(ip, &cs->cs_ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)(changes | NEW_C); + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)changes; + } + *cp++ = (u_char)(deltaA >> 8); + *cp++ = (u_char)deltaA; + BCOPY(new_seq, cp, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + BCOPY(ip, &cs->cs_ip, hlen); + IPH_PROTO_SET(ip, cs->cs_id); + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip_hdr *ip; + + ip = (struct ip_hdr *)nb->payload; + hlen = IPH_HL(ip) << 2; + if (IPH_PROTO(ip) >= MAX_SLOTS + || hlen + sizeof(struct tcp_hdr) > nb->len + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + IPH_PROTO(ip), hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; + comp->flags &=~ VJF_TOSS; + IPH_PROTO_SET(ip, IP_PROTO_TCP); + BCOPY(ip, &cs->cs_ip, hlen); + cs->cs_hlen = (u_short)hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcp_hdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = IPH_HL(&cs->cs_ip) << 2; + th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->chksum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + TCPH_SET_FLAG(th, TCP_PSH); + } else { + TCPH_UNSET_FLAG(th, TCP_PSH); + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->ackno) + i; + th->ackno = htonl(tmp); + tmp = ntohl(th->seqno) + i; + th->seqno = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + th->seqno = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + TCPH_SET_FLAG(th, TCP_URG); + DECODEU(th->urgp); + } else { + TCPH_UNSET_FLAG(th, TCP_URG); + } + if (changes & NEW_W) { + DECODES(th->wnd); + } + if (changes & NEW_A) { + DECODEL(th->ackno); + } + if (changes & NEW_S) { + DECODEL(th->seqno); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip._id); + } else { + IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1); + IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip))); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp)); +#else + IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen)); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + IPH_CHKSUM_SET(&cs->cs_ip, 0); + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp)); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* VJ_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.h b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.h new file mode 100644 index 0000000..fad1213 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/ppp/vj.h @@ -0,0 +1,156 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#ifndef VJ_H +#define VJ_H + +#include "lwip/ip.h" +#include "lwip/tcp_impl.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ diff --git a/component/common/network/lwip/lwip_v1.4.1/src/netif/slipif.c b/component/common/network/lwip/lwip_v1.4.1/src/netif/slipif.c new file mode 100644 index 0000000..2777630 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.4.1/src/netif/slipif.c @@ -0,0 +1,510 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * 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. 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 built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + * Simon Goldschmidt + * + * Usage: This netif can be used in three ways: + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received. + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input(). + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" +#include "lwip/sys.h" + +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ + +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE +#define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u8_t state; + u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chaing packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +err_t +slipif_output(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_UNUSED_ARG(ipaddr); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); + priv = netif->state; + + /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + /* normal byte - no need for escaping */ + sio_send(c, priv->sd); + break; + } + } + } + /* End with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) +{ + struct slipif_priv *priv; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + priv->state = SLIP_RECV_NORMAL; + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + return NULL; +} + +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + u8_t c; + struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + + while (1) { + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } + } +} +#endif /* SLIP_USE_RX_THREAD */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + u8_t sio_num; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output; + netif->mtu = SLIP_MAX_SIZE; + netif->flags |= NETIF_FLAG_POINTTOPOINT; + + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); + +#if SLIP_USE_RX_THREAD + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + u8_t c; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while(q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); + } +} +#endif /* SLIP_RX_FROM_ISR */ + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/CHANGELOG b/component/common/network/lwip/lwip_v1.5.0.beta/CHANGELOG new file mode 100644 index 0000000..1d757d3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/CHANGELOG @@ -0,0 +1,3733 @@ +HISTORY + +(git master) + + * [Enter new changes just after this line - do not remove this line] + + ++ New features: + + 2015-01-02: Simon Goldschmidt + * tcp.c: tcp_kill_prio(): prefer nearly-closed connections (waiting for the + last ACK only) over established connections when out of tcp pcbs + + 2015-01-17: Simon Goldschmidt + * api: allow enabling socket API without (public) netconn API - netconn API is + still used by sockets, but keeping it private (static) should allow better + compiler optimizations + + 2015-01-16: Simon Goldschmidt + * tcp_in.c: fixed bug #20506 "Initial congestion window is very small" again + by implementing the calculation formula from RFC3390 + + 2014-12-10: Simon Goldschmidt + * api: added option LWIP_NETCONN_SEM_PER_THREAD to use a semaphore per thread + instead of using one per netconn and per select call + + 2014-12-08: Simon Goldschmidt + * ip6.h: fixed bug #43778: IPv6 header version not set on 16-bit platform + (macro IP6H_VTCFL_SET()) + + 2014-12-08: Simon Goldschmidt + * icmp.c, ip4.c, pbuf.c, udp.c, pbuf.h: task #11472 Support PBUF_REF for RX + (IPv6 and IPv4/v6 reassembly might not work yet) + + 2014-11-06: Simon Goldschmidt + * sockets.c/.h, init.c: lwip_socket_init() is not needed any more + -> compatibility define + + 2014-09-16: Simon Goldschmidt + * dns.c, opt.h: reduced ram usage by parsing DNS responses in place + + 2014-09-16: Simon Goldschmidt + * pbuf.h/.c: added pbuf_take_at() and pbuf_put_at() + + 2014-09-15: Simon Goldschmidt + * dns.c: added source port randomization to make the DNS client more robust + (see bug #43144) + + 2013-09-02: Simon Goldschmidt + * arch.h and many other files: added optional macros PACK_STRUCT_FLD_8() and + PACK_STRUCT_FLD_S() to prevent gcc 4 from warning about struct members that + do not need packing + + 2013-08-19: Simon Goldschmidt + * netif.h: bug #42998: made NETIF_MAX_HWADDR_LEN overridable for some special + networks + + 2013-03-17: Simon Goldschmidt (patch by Ghobad Emadi) + * opt.h, etharp.c: Added LWIP_HOOK_ETHARP_GET_GW to implement IPv4 routing with + multiple gateways + + 2013-04-20: Fatih Asici + * opt.h, etharp.h/.c: patch #7993: Added support for transmitting packets + with VLAN headers via hook function LWIP_HOOK_VLAN_SET and to check them + via hook function LWIP_HOOK_VLAN_CHECK + + 2014-02-20: Simon Goldschmidt (based on patch by Artem Pisarenko) + * patch #7885: modification of api modules to support FreeRTOS-MPU + (don't pass stack-pointers to other threads) + + 2014-02-05: Simon Goldschmidt (patch by "xtian" and "alex_ab") + * patch #6537/#7858: TCP window scaling support + + 2014-01-17: Jiri Engelthaler + * icmp, icmp6, opt.h: patch #8027: Completed HW checksuming for IPv4 and + IPv6 ICMP's + + 2012-08-22: Sylvain Rochet + * New PPP stack for lwIP, developed in ppp-new branch. + Based from pppd 2.4.5, released 2009-11-17, with huge changes to match + code size and memory requirements for embedded devices, including: + - Gluing together the previous low-level PPP code in lwIP to pppd 2.4.5, which + is more or less what pppd sys-* files are, so that we get something working + using the unix port. + - Merged some patchs from lwIP Git repository which add interesting features + or fix bugs. + - Merged some patchs from Debian pppd package which add interesting features + or fix bugs. + - Ported PPP timeout handling to the lwIP timers system + - Disabled all the PPP code using filesystem access, replaced in necessary cases + to configuration variables. + - Disabled all the PPP code forking processes. + - Removed IPX support, lwIP does not support IPX. + - Ported and improved random module from the previous PPP port. + - Removed samba TDB (file-driven database) usage, because it needs a filesystem. + - MS-CHAP required a DES implementation, we added the latest PolarSSL DES + implementation which is under a BSD-ish license. + - Also switched to PolarSSL MD4,MD5,SHA1 implementations, which are meant to be + used in embedded devices with reduced memory footprint. + - Removed PPP configuration file parsing support. + - Added macro definition EAP_SUPPORT to make EAP support optional. + - Added macro definition CHAP_SUPPORT to make CHAP support optional. + - Added macro definition MSCHAP_SUPPORT to make MSCHAP support optional. + - Added macro definition PAP_SUPPORT to make PAP support optional. + - Cleared all Linux syscall calls. + - Disabled demand support using a macro, so that it can be ported later. + - Disabled ECP support using a macro, so that it can be ported later. + - Disabled CCP support using a macro, so that it can be ported later. + - Disabled CBCP support using a macro, so that it can be ported later. + - Disabled LQR support using a macro, so that it can be ported later. + - Print packet debug feature optional, through PRINTPKT_SUPPORT + - Removed POSIX signal usage. + - Fully ported PPPoS code from the previous port. + - Fully ported PPPoE code from the previous port. + - Fully ported VJ compression protocol code from the previous port. + - Removed all malloc()/free() use from PPP, replaced by stack usage or PBUF. + - Disabled PPP server support using a macro, so that it can be ported later. + - Switched all PPP debug to lwIP debug system. + - Created PPP Control Block (PPP PCB), removed PPP unit integer everywhere, + removed all global variables everywhere, did everything necessary for + the PPP stack to support more than one PPP session (pppd only support + one session per process). + - Removed the statically allocated output buffer, now using PBUF. + - Improved structure size of all PPP modules, deep analyze of code to reduce + variables size to the bare minimum. Switched all boolean type (char type in + most architecture) to compiler generated bitfields. + - Added PPP IPv6 support, glued lwIP IPv6 support to PPP. + - Now using a persistent netif interface which can then be used in lwIP + functions requiring a netif. + - Now initializing PPP in lwip_init() function. + - Reworked completely the PPP state machine, so that we don't end up in + anymore in inconsistent state, especially with PPPoE. + - Improved the way we handle PPP reconnection after disconnect, cleaning + everything required so that we start the PPP connection again from a + clean state. + - Added PPP holdoff support, allow the lwIP user to wait a little bit before + reconnecting, prevents connection flood, especially when using PPPoL2TP. + - Added PPPoL2TP LAC support (a.k.a. UDP tunnels), adding a VPN client + feature to lwIP, L2TP being a widely used tunnel protocol. + - Switched all used PPP types to lwIP types (u8t, u16t, u32t, ...) + - Added PPP API "sequential" thread-safe API, based from NETIFAPI. + + 2011-07-21: Simon Goldschmidt + * sockets.c, opt.h: (bug #30185): added LWIP_FIONREAD_LINUXMODE that makes + ioctl/FIONREAD return the size of the next pending datagram. + + 2011-05-25: Simon Goldschmidt + * again nearly the whole stack, renamed ip.c to ip4.c, ip_addr.c to ip4_addr.c, + combined ipv4/ipv6 inet_chksum.c, added ip.h, ip_addr.h: Combined IPv4 + and IPv6 code where possible, added defines to access IPv4/IPv6 in non-IP + code so that the code is more readable. + + 2011-05-17: Patch by Ivan Delamer (only checked in by Simon Goldschmidt) + * nearly the whole stack: Finally, we got decent IPv6 support, big thanks to + Ivan! (this is work in progress: we're just post release anyway :-) + + + ++ Bugfixes: + + 2014-01-27: Simon Goldschmidt + * api_msg.c: fixed that SHUT_RD followed by SHUT_WR was different to SHUT_RDWR, + fixed return value of lwip_netconn_do_close on unconnected netconns + + 2015-01-17: Simon Goldschmidt + * sockets.c: fixed bug #43361 select() crashes with stale FDs + + 2015-01-17: Simon Goldschmidt + * sockets.c/.h, memp_std.h: fixed bug #40788 "lwip_setsockopt_internal() crashes" + by rewriting set/getsockopt functions to combine checks with the actual code + and add more NULL checks; this also fixes that CORE_LOCKING used message + passing for set/getsockopt. + + 2014-12-19: Simon Goldschmidt + * opt.h, dhcp.h/.c: prevent dhcp from starting when netif link is down (only + when LWIP_DHCP_CHECK_LINK_UP==1, which is disabled by default for + compatibility reasons) + + 2014-12-17: Simon Goldschmidt + * tcp_out.c: fixed bug #43840 Checksum error for TCP_CHECKSUM_ON_COPY==1 for + no-copy data with odd length + + 2014-12-10: Simon Goldschmidt + * sockets.c, tcp.c, others: fixed bug #43797 set/getsockopt: SO_SNDTIMEO/SO_RCVTIMEO + take int as option but should take timeval (LWIP_SO_SNDRCVTIMEO_STANDARD==0 can + be used to revert to the old 'winsock' style behaviour) + Fixed implementation of SO_ACCEPTCONN to just look at the pcb state + + 2014-12-09: Simon Goldschmidt + * ip4.c: fixed bug #43596 IGMP queries from 0.0.0.0 are discarded + + 2014-10-21: Simon Goldschmidt (patch by Joel Cunningham and Albert Huitsing) + * sockts.c: fixed bugs #41495 Possible threading issue in select() and #43278 + event_callback() handle context switch when calling sys_sem_signal() + + 2014-10-21: Simon Goldschmidt + * api_msg.c: fixed bug #38219 Assert on TCP netconn_write with sndtimeout set + + 2014-09-16: Kevin Cernekee + * dns.c: patch #8480 Fix handling of dns_seqno wraparound + + 2014-09-16: Simon Goldschmidt + * tcp_out.c: fixed bug #43192 tcp_enqueue_flags() should not check TCP_SND_QUEUELEN + when sending FIN + + 2014-09-03: Simon Goldschmidt + * msg_in.c: fixed bug #39355 SNMP Memory Leak in case of error + + 2014-09-02: Simon Goldschmidt + * err.h/.c, sockets.c, api_msg.c: fixed bug #43110 call getpeername() before + listen() will cause a error + + 2014-09-02: Simon Goldschmidt + * sockets.c: fixed bug #42117 lwip_fcntl does not set errno + + 2014-09-02: Simon Goldschmidt + * tcp.c: fixed bug #42299 tcp_abort() leaves freed pcb on tcp_bound_pcbs list + + 2014-08-20: Simon Goldschmidt + * dns.c: fixed bug #42987 lwIP is vulnerable to DNS cache poisoning due to + non-randomized TXIDs + + 2014-06-03: Simon Goldschmidt + * tcp_impl.h, tcp_in.c: fixed bug #37969 SYN packet dropped as short packet in + tcp_input function + + 2014-05-20: Simon Goldschmidt + * tcp_out.c: fixed bug #37184 tcp_write problem for pcbs in the SYN_SENT state + + 2014-05-19: Simon Goldschmidt + * *.h: Fixed bug #35874 reserved identifier violation (removed leading underscores + from header include guards) + + 2014-04-08: Simon Goldschmidt + * tcp.c: Fixed bug #36167 tcp server crash when client closes (maximum window) + + 2014-04-06: Simon Goldschmidt + * tcp_in.c: Fixed bug #36210 lwIP does not elicit an empty ACK when received + unacceptable ACK + + 2014-04-06: Simon Goldschmidt + * dhcp.c, ip4.c/.h, ip6.c/.h, udp.c/.h, ip.h: Fixed bug #41787 DHCP Discovery + is invalid when an IP is set to thet netif. + + 2014-03-14: Simon Goldschmidt + * tcp_out.c: Fixed bug #36153 TCP Cheksum error if LWIP_CHECKSUM_ON_COPY=1 + + 2014-03-11: Simon Goldschmidt (patch by Mason) + * opt.h, sockets.c: fixed bug #35928 BSD sockets functions must set errno for + POSIX-compliance + + 2014-02-27: Simon Goldschmidt + * dhcp.c: fixed bug #40303 DHCP xid renewed when sending a DHCPREQUEST + + 2014-02-27: Simon Goldschmidt + * raw.c: fixed bug #41680 raw socket can not receive IPv6 packet when + IP_SOF_BROADCAST_RECV==1 + + 2014-02-27: Simon Goldschmidt + * api_msg.c, sockets.c: fixed bug #38404 getpeeraddr returns success on + unconnected/listening TCP sockets + + 2014-02-27: Simon Goldschmidt + * sockets.c: fixed bug #41729 Some socket functions return Exyz instead of -1 + + 2014-02-25: Simon Goldschmidt + * ip4.c: fixed bug #39514 ip_route() may return an IPv6-only interface + + 2014-02-25: Simon Goldschmidt, patch by Fatih Asici + * pbuf.c: fixed bug #39356 Wrong increment in pbuf_memfind() + + 2014-02-25: Simon Goldschmidt + * netif.c/.h, udp.c: fixed bug #39225 udp.c uses netif_matches_ip6_addr() incorrectly; + renamed function netif_matches_ip6_addr() to netif_get_ip6_addr_match() + + 2014-02-25: Simon Goldschmidt + * igmp.c: fixed bug #39145 IGMP membership report for 224.0.0.1 + + 2014-02-22: Simon Goldschmidt (patch by Amir Shalem) + * etharp.c, opt.h: fixed bug #34681 Limit ARP queue length by ARP_QUEUE_LEN (=3) + + 2014-02-22: Simon Goldschmidt (patch by Amir Shalem) + * etharp.h/.c: fixed bug #34682 Limit ARP request flood for unresolved entry + + 2014-02-20: Simon Goldschmidt + * tcp_out.c: fixed bug #39683 Assertion "seg->tcphdr not aligned" failed with + MEM_ALIGNMENT = 8 + + 2014-02-20: Simon Goldschmidt + * sockets.c: fixed bug #39882 No function shall set errno to 0 + + 2014-02-20: Simon Goldschmidt + * mib_structs.c: fixed bug #40050 SNMP problem with MIB arrays > 255 + + 2014-02-20: Simon Goldschmidt + * api.h, sockets.c: fixed bug #41499 netconn::recv_avail can overflow + + 2014-01-08: Stathis Voukelatos + * memp_std.h: patch #7928 Fixed size calculation in MALLOC memory pool + creation macro + + 2014-01-18: Brian Fahs + * tcp_out.c: patch #8237: tcp_rexmit_rto fails to update pcb->unsent_oversize + when necessary + + 2014-01-17: Grant Erickson, Jay Logue, Simon Goldschmidt + * ipv6.c, netif.c: patch #7913 Enable Support for IPv6 Loopback + + 2014-01-16: Stathis Voukelatos + * netif.c: patch #7902 Fixed netif_poll() operation when LWIP_LOOPBACK_MAX_PBUFS > 0 + + 2014-01-14: "Freddie Chopin" + * snmp.h, mib2.c: fixed constness and spelling of sysdescr + + 2014-01-14: Simon Goldschmidt (patch by Thomas Faber) + * tcpip.c: patch #8241: Fix implicit declaration of ip_input with + LWIP_TCPIP_CORE_LOCKING_INPUT disabled + + 2014-01-14: chrysn + * timers.c: patch #8244 make timeouts usable reliably from outside of the + timeout routine + + 2014-01-10: Simon Goldschmidt + * ip_frag.c, ip6_frag.c: fixed bug #41041 Potential use-after-free in IPv6 reassembly + + 2014-01-10: Simon Goldschmidt + * memp.c: fixed bug #41188 Alignment error in memp_init() when MEMP_SEPARATE_POOLS==1 + + 2014-01-10: Simon Goldschmidt + * tcp.c: fixed bug #39898 tcp_fasttmr() possible lock due to infinte queue process loop + + 2013-06-29: Simon Goldschmidt + * inet.h, sockets.h: partially fixed bug #37585: IPv6 compatibility (in socket structs) + + 2013-06-29: Simon Goldschmidt + * inet6.h: bug #37585/task #12600: fixed struct in6_addr.s6_addr to conform to spec + + 2013-04-24: patch by Liam + * api_msg.c: patch #8008 Fix a potential null pointer dereference in assert + + 2013-04-24: Simon Goldschmidt + * igmp.c: fixed possible division by zero + + 2013-04-24: Simon Goldschmidt + * ip6.h, some ipv6 C files: fixed bug #38526 Coverity: Recursive Header Inclusion in ip6.h + + 2013-04-24: Simon Goldschmidt (patch by Emil Ljungdahl): + * netif.c: fixed bug #38586 netif_loop_output() "deadlocks" + + 2013-01-15: Simon Goldschmidt + * ip4.c: fixed bug #37665 ip_canforward operates on address in wrong byte order + + 2013-01-15: Simon Goldschmidt + * pbuf.h: fixed bug #38097 pbuf_free_ooseq() warning + + 2013-01-14: Simon Goldschmidt + * dns.c: fixed bug #37705 Possible memory corruption in DNS query + + 2013-01-11: Simon Goldschmidt + * raw.c: fixed bug #38066 Raw pcbs can alter packet without eating it + + 2012-08-22: Simon Goldschmidt + * memp.c: fixed bug #37166: memp_sanity check loops itself + + 2012-08-13: Simon Goldschmidt + * dhcp.c: fixed bug #36645: Calling dhcp_release before dhcp_start + dereferences NULL + + 2012-08-13: Simon Goldschmidt + * msg_out.c: fixed bug #36840 snmp_send_trap() NULL de-reference if traps + configured but no interfaces available + + 2012-08-13: Simon Goldschmidt + * dns.c: fixed bug #36899 DNS TTL 0 is cached for a long time + + 2012-05-11: Simon Goldschmidt (patch by Marty) + * memp.c: fixed bug #36412: memp.c does not compile when + MEMP_OVERFLOW_CHECK > zero and MEMP_SEPARATE_POOLS == 1 + + 2012-05-03: Simon Goldschmidt (patch by Sylvain Rochet) + * ppp.c: fixed bug #36283 (PPP struct used on header size computation and + not packed) + + 2012-05-03: Simon Goldschmidt (patch by David Empson) + * ppp.c: fixed bug #36388 (PPP: checksum-only in last pbuf leads to pbuf with + zero length) + + 2012-03-25: Simon Goldschmidt + * api_msg.c: Fixed bug #35817: do_connect() invalidly signals op_completed + for UDP/RAW with LWIP_TCPIP_CORE_LOCKING==1 + + 2012-03-25: Simon Goldschmidt + * api_msg.h, api_lib.c, api_msg.c, netifapi.c: fixed bug #35931: Name space + pollution in api_msg.c and netifapi.c + + 2011-08-24: Simon Goldschmidt + * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard + + + +(STABLE-1.4.1) + + ++ New features: + + 2012-03-25: Simon Goldschmidt (idea by Mason) + * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h + which are a simple wrapper to the correct lwIP include files. + + 2012-01-16: Simon Goldschmidt + * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP + + 2011-12-17: Simon Goldschmidt + * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW) + (fixes bug #35061) + + 2011-09-27: Simon Goldschmidt + * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989) + (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h) + + 2011-09-21: Simon Goldschmidt + * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on + send (TCP only, bug #33820) + + 2011-09-21: Simon Goldschmidt + * init.c: Converted runtime-sanity-checks into compile-time checks that can + be disabled (since runtime checks can often not be seen on embedded targets) + + 2011-09-11: Simon Goldschmidt + * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file + to get a clear separation of which functions an application or port may use + (task #11281) + + 2011-09-11: Simon Goldschmidt + * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize + initial local TCP/UDP ports (so that different port ranges are used after + a reboot; bug #33818; this one added tcp_init/udp_init functions again) + + 2011-09-03: Simon Goldschmidt + * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302) + + 2011-08-24: Simon Goldschmidt + * opt.h, netif.h/.c: added netif remove callback (bug #32397) + + 2011-07-26: Simon Goldschmidt + * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter + function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN) + + 2011-07-21: Simon Goldschmidt (patch by hanhui) + * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour: + Added pbuf flags to mark incoming packets as link-layer broadcast/multicast. + Also added code to allow ip_forward() to forward non-broadcast packets to + the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1). + + 2011-06-26: Simon Goldschmidt (patch by Cameron Gutman) + * tcp.c, tcp_out.c: bug #33604: added some more asserts to check that + pcb->state != LISTEN + + 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage) + * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static + memory message + + + ++ Bugfixes: + + 2012-09-26: Simon Goldschmidt + * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7843 Fix corner case with dhcp timeouts + + 2012-09-26: patch by Henrik Persson + * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet + + 2012-08-22: Simon Goldschmidt + * memp.c: fixed bug #37166: memp_sanity check loops itself + + 2012-05-08: Simon Goldschmidt + * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was + a debug-check issue only) + + 2012-03-27: Simon Goldschmidt + * vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c + + 2012-03-27: Simon Goldschmidt (patch by Mason) + * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the + send MSS + + 2012-03-22: Simon Goldschmidt + * ip4.c: fixed bug #35927: missing refragmentaion in ip_forward + + 2012-03-20: Simon Goldschmidt (patch by Mason) + * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list + + 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic) + * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian, + possible bug on little endian system + + 2012-02-23: Simon Goldschmidt + * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt + * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP() + (bug #35541: PPP Memory Leak) + + 2012-02-16: Simon Goldschmidt + * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway + (introduced when fixing bug# 33551) + + 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage) + * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed + + 2012-02-15: Simon Goldschmidt + * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with + MEMP_MEM_MALLOC==1 + + 2012-02-12: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on + MSS > pcb->snd_wnd (by not creating segments bigger than half the window) + + 2012-02-11: Simon Goldschmidt + * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait + queue while closing + + 2012-01-22: Simon Goldschmidt + * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR) + + 2012-01-21: Simon Goldschmidt + * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb + + 2012-01-20: Simon Goldschmidt + * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths + + 2012-01-20: Simon Goldschmidt + * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy + + 2011-11-25: Simon Goldschmidt + * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt + tcp_active_pcbs in some cases + + 2011-11-23: Simon Goldschmidt + * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with + '#ifndef sys_msleep' + + 2011-11-22: Simon Goldschmidt + * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when + netif is brought down + + 2011-10-28: Simon Goldschmidt + * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks + + 2011-10-23: Simon Goldschmidt + * mem.c: fixed bug #34429: possible memory corruption with + LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1 + + 2011-10-18: Simon Goldschmidt + * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard + error value + + 2011-10-18: Simon Goldschmidt + * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small + windows (bug #34176 select after non-blocking send times out) + + 2011-10-18: Simon Goldschmidt + * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't + consider netif->mtu, causes slow network + + 2011-10-18: Simon Goldschmidt + * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code + + 2011-10-18: Simon Goldschmidt + * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS + + 2011-10-17: Simon Goldschmidt + * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api + + 2011-10-13: Simon Goldschmidt + * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no + zero window is received) by starting the persist timer when a zero window is + received, not when we have more data queued for sending than fits into the + window + + 2011-10-13: Simon Goldschmidt + * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex + + 2011-10-13: Simon Goldschmidt + * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is + used and not all protocols are enabled + + 2011-10-12: Simon Goldschmidt + * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4 + + 2011-10-09: Simon Goldschmidt + * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect + byte value when pcb->unacked != NULL + + 2011-10-09: Simon Goldschmidt + * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong + + 2011-09-27: Simon Goldschmidt + * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places... + + 2011-09-27: Simon Goldschmidt + * tcp_in.c: fixed bug #28288: Data after FIN in oos queue + + 2011-09-27: Simon Goldschmidt + * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf + + 2011-09-24: Simon Goldschmidt + * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1 + + 2011-09-23: Simon Goldschmidt + * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for + the last packet including FIN can lose data + + 2011-09-22: Simon Goldschmidt + * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into + account + + 2011-09-21: Simon Goldschmidt + * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks + in init.c + + 2011-09-20: Simon Goldschmidt + * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts) + + 2011-09-11: Simon Goldschmidt + * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs + (bug #34019) + + 2011-09-09: Simon Goldschmidt + * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if + udp port matches + + 2011-09-03: Simon Goldschmidt + * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet + is aggregated and sent to application + + 2011-09-01: Simon Goldschmidt + * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared + to other options + + 2011-09-01: Simon Goldschmidt + * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno + + 2011-08-24: Simon Goldschmidt + * api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling + accept() on UDP connections + + 2011-08-24: Simon Goldschmidt + * sockets.h: fixed bug #34057 socklen_t should be a typedef + + 2011-08-24: Simon Goldschmidt + * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo) + + 2011-08-24: Simon Goldschmidt + * dhcp.c: fixed bug #34122 dhcp: hostname can overflow + + 2011-08-24: Simon Goldschmidt + * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr + + 2011-08-22: Simon Goldschmidt + * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This + merely prevents nagle from not transmitting fast after closing.) + + 2011-07-22: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns + always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now + lwip_send() sends as much as possible for non-blocking sockets + + 2011-07-22: Simon Goldschmidt + * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented + for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level. + + 2011-07-21: Simon Goldschmidt + * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by + sending an ARP request when an ARP entry is used in the last minute before + it would time out. + + 2011-07-04: Simon Goldschmidt + * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0. + + 2011-06-26: Simon Goldschmidt + * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by + updating its documentation only. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an + unaligned pointer. + + 2011-06-26: Simon Goldschmidt + * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1" + + 2011-05-25: Simon Goldschmidt + * tcp.c: fixed bug #33398 (pointless conversion when checking TCP port range) + + + +(STABLE-1.4.0) + + ++ New features: + + 2011-03-27: Simon Goldschmidt + * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and + calculate it in tcp_zero_window_probe (the only place where it was used). + + 2010-11-21: Simon Goldschmidt + * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif + (fixes bug #31525). + + 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage) + * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for + IP_MULTICAST_LOOP at socket- and raw-API level. + + 2010-06-16: Simon Goldschmidt + * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow + link-layer-addressed UDP traffic to be received while a netif is down (just + like DHCP during configuration) + + 2010-05-22: Simon Goldschmidt + * many many files: bug #27352: removed packing from ip_addr_t, the packed + version is now only used in protocol headers. Added global storage for + current src/dest IP address while in input functions. + + 2010-05-16: Simon Goldschmidt + * def.h: task #10391: Add preprocessor-macros for compile-time htonl + calculation (and use them throughout the stack where applicable) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool + instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h) + + 2010-05-16: Simon Goldschmidt + * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own + MEMP pool instead of the heap + + 2010-05-13: Simon Goldschmidt + * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added + new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast + packets to more than one pcb. + + 2010-05-02: Simon Goldschmidt + * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending + UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-04-30: Simon Goldschmidt + * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that + take a precalculated checksum, added pbuf_fill_chksum() to copy data + into a pbuf and at the same time calculating the checksum for that data + + 2010-04-29: Simon Goldschmidt + * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying + 2-byte-aligned IP addresses and MAC addresses + + 2010-04-28: Patch by Bill Auerbach + * ip.c: Inline generating IP checksum to save a function call + + 2010-04-14: Simon Goldschmidt + * tcpip.h/.c, timers.c: Added an overridable define to get informed when the + tcpip_thread processes messages or timeouts to implement a watchdog. + + 2010-03-28: Simon Goldschmidt + * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing + fragment if LWIP_NETIF_TX_SINGLE_PBUF==1 + + 2010-03-27: Simon Goldschmidt + * etharp.c: Speedup TX by moving code from find_entry to etharp_output/ + etharp_query to prevent unnecessary function calls (inspired by + patch #7135). + + 2010-03-20: Simon Goldschmidt + * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code + since the linker cannot do this automatically to save space. + + 2010-03-20: Simon Goldschmidt + * opt.h, etharp.c/.h: Added support for static ARP table entries + + 2010-03-14: Simon Goldschmidt + * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum + when creating TCP segments, not when (re-)transmitting them. + + 2010-03-07: Simon Goldschmidt + * sockets.c: bug #28775 (select/event_callback: only check select_cb_list + on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code. + This should speed up receiving data on sockets as the select code in + event_callback is only executed when select is waiting. + + 2010-03-06: Simon Goldschmidt + * tcp_out.c: task #7013 (Create option to have all packets delivered to + netif->output in one piece): Always copy to try to create single pbufs + in tcp_write. + + 2010-03-06: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv + by not allocating a netbuf): added function netconn_recv_tcp_pbuf() + for tcp netconns to receive pbufs, not netbufs; use that function + for tcp sockets. + + 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt + * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040: + Work on tcp_enqueue: Don't waste memory when chaining segments, + added option TCP_OVERSIZE to prevent creating many small pbufs when + calling tcp_write with many small blocks of data. Instead, pbufs are + allocated larger than needed and the space is used for later calls to + tcp_write. + + 2010-02-21: Simon Goldschmidt + * stats.c/.h: Added const char* name to mem- and memp-stats for easier + debugging. + + 2010-02-21: Simon Goldschmidt + * tcp.h (and usages), added tcp_impl.h: Splitted API and internal + implementation of tcp to make API usage cleare to application programmers + + 2010-02-14: Simon Goldschmidt/Stephane Lesage + * ip_addr.h: Improved some defines working on ip addresses, added faster + macro to copy addresses that cannot be NULL + + 2010-02-13: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non- + blocking send operation) + + 2010-02-12: Simon Goldschmidt + * sockets.c/.h: Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + 2010-02-12: Simon Goldschmidt + * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated + memory): added autoip_set_struct() and dhcp_set_struct() to let autoip + and dhcp work with user-allocated structs instead of callin mem_malloc + + 2010-02-12: Simon Goldschmidt/Jeff Barber + * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has + SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT + + 2010-02-12: Simon Goldschmidt + * sys layer: task #10139 (Prefer statically allocated memory): converted + mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t; + converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX + to let sys.h use binary semaphores instead of mutexes - as before) + + 2010-02-09: Simon Goldschmidt (Simon Kallweit) + * timers.c/.h: Added function sys_restart_timeouts() from patch #7085 + (Restart system timeout handling) + + 2010-02-09: Simon Goldschmidt + * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into + netif.c) - loopif does not have to be created by the port any more, + just define LWIP_HAVE_LOOPIF to 1. + + 2010-02-08: Simon Goldschmidt + * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa + inet_ntoa_r/ipaddr_ntoa_r + + 2010-02-08: Simon Goldschmidt + * netif.h: Added netif_s/get_igmp_mac_filter() macros + + 2010-02-05: Simon Goldschmidt + * netif.h: Added function-like macros to get/set the hostname on a netif + + 2010-02-04: Simon Goldschmidt + * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to + make changing the actual implementation behind the typedef easier. + + 2010-02-01: Simon Goldschmidt + * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool + for allocating memory when getaddrinfo() is called. + + 2010-01-31: Simon Goldschmidt + * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse + them once instead of parsing for every option. This also removes + the need for mem_malloc from dhcp_recv and makes it possible to + correctly retrieve the BOOTP file. + + 2010-01-30: simon Goldschmidt + * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect + the sockets array. + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, api_msg.c, sockets.c: Added except set support in select + (patch #6860) + + 2010-01-29: Simon Goldschmidt (patch by Laura Garrett) + * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c: + Add non-blocking support for connect (partly from patch #6860), + plus many cleanups in socket & netconn API. + + 2010-01-27: Simon Goldschmidt + * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding + to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605 + + 2010-01-26: Simon Goldschmidt + * snmp: Use memp pools for snmp instead of the heap; added 4 new pools. + + 2010-01-14: Simon Goldschmidt + * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback + by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback() + + 2010-01-13: Simon Goldschmidt + * mem.c: The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + (patch #6966 and bug #26133) + + 2010-01-10: Simon Goldschmidt (Bill Auerbach) + * opt.h, memp.c: patch #6822 (Add option to place memory pools in + separate arrays) + + 2010-01-10: Simon Goldschmidt + * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define + LWIP_RAND() for lwip-wide randomization (to be defined in cc.h) + + 2009-12-31: Simon Goldschmidt + * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h + added timers.c/.h: Separated timer implementation from semaphore/mbox + implementation, moved timer implementation to timers.c/.h, timers are + now only called from tcpip_thread or by explicitly checking them. + (TASK#7235) + + 2009-12-27: Simon Goldschmidt + * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option + LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE) + + + ++ Bugfixes: + + 2011-04-20: Simon Goldschmidt + * sys_arch.txt: sys_arch_timeouts() is not needed any more. + + 2011-04-13: Simon Goldschmidt + * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by + using ports in the IANA private/dynamic range (49152 through 65535). + + 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl: + * etharp.h/.c: Fixed broken VLAN support. + + 2011-03-27: Simon Goldschmidt + * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp + pcbs) by checking if the pcb was bound (local_port != 0). + + 2011-03-27: Simon Goldschmidt + * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice) + + 2011-03-27: Simon Goldschmidt + * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and + raw pcbs with LWIP_TCPIP_CORE_LOCKING==1. + + 2011-03-27: Simon Goldschmidt + * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route + is present never times out) by starting retransmission timer before checking + route. + + 2011-03-22: Simon Goldschmidt + * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only + calling sio_read_abort() if the file descriptor is valid. + + 2011-03-14: Simon Goldschmidt + * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect + more than once can render a socket useless) since it mainly involves changing + "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal. + + 2011-03-13: Simon Goldschmidt + * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing + err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN: + use EALRADY instead of -1 + + 2011-03-13: Simon Goldschmidt + * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the + connection has been aborted by err_tcp (since this is not a normal closing + procedure). + + 2011-03-13: Simon Goldschmidt + * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind + with pcb->state != CLOSED + + 2011-02-17: Simon Goldschmidt + * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in + documentation + + 2011-02-17: Simon Goldschmidt + * many files: Added missing U/UL modifiers to fix 16-bit-arch portability. + + 2011-01-24: Simon Goldschmidt + * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems + + 2010-12-02: Simon Goldschmidt + * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal. + + 2010-11-23: Simon Goldschmidt + * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for + LWIP_SO_RCVBUF and ioctl/FIONREAD. + + 2010-11-23: Simon Goldschmidt + * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at + least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet. + + 2010-11-23: Simon Goldschmidt + * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after + refusing 'refused_data' again. + + 2010-11-22: Simon Goldschmidt + * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS + after a successful nonblocking connection. + + 2010-11-22: Simon Goldschmidt + * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr + must be sent link-local + + 2010-11-22: Simon Goldschmidt + * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for + LWIP_TIMERS==0 + + 2010-11-20: Simon Goldschmidt + * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number + + 2010-11-20: Simon Goldschmidt + * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to + resemble other stacks. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else + no-copy TCP writes will never succeed. + + 2010-11-20: Simon Goldschmidt + * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does + not match documentation: return ERR_ARG instead of ERR_VAL if not + initialized or wrong argument. + + 2010-10-20: Simon Goldschmidt + * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16 + + 2010-10-05: Simon Goldschmidt + * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when + replugging the network cable after an AutoIP address was assigned. + + 2010-08-10: Simon Goldschmidt + * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs + + 2010-08-03: Simon Goldschmidt + * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625) + + 2010-08-01: Simon Goldschmidt (patch by Greg Renda) + * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big + endian architectures) + + 2010-07-28: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP + disabled. + + 2010-07-27: Simon Goldschmidt + * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no + harm but never did anything + + 2010-07-21: Simon Goldschmidt + * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not + add IP options) + + 2010-07-16: Kieran Mansley + * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator + + 2010-07-10: Simon Goldschmidt + * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options + + 2010-06-30: Simon Goldschmidt + * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in + netconn_delete) + + 2010-06-28: Kieran Mansley + * timers.c remove unportable printing of C function pointers + + 2010-06-24: Simon Goldschmidt + * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag + NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading + + 2010-06-24: Simon Goldschmidt + * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly + implemented shutdown at socket level. + + 2010-06-21: Simon Goldschmidt + * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has + problems with zero-copy DMA MACs) by adding custom pbufs and implementing + custom pbufs that reference other (original) pbufs. Additionally set + IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side. + + 2010-06-15: Simon Goldschmidt + * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses + + 2010-06-14: Simon Goldschmidt + * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses + + 2010-06-12: Simon Goldschmidt + * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop + state + + 2010-05-17: Simon Goldschmidt + * netdb.c: Correctly NULL-terminate h_addr_list + + 2010-05-16: Simon Goldschmidt + * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent + "symbol already defined" i.e. when linking to winsock + + 2010-05-05: Simon Goldschmidt + * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may + overflow) + + 2010-04-21: Simon Goldschmidt + * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening + connection) + + 2010-03-28: Luca Ceresoli + * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers + + 2010-03-27: Luca Ceresoli + * mib2.c: patch #7130: remove meaningless const qualifiers + + 2010-03-26: Simon Goldschmidt + * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too + + 2010-03-26: Simon Goldschmidt + * various files: Fixed compiling with different options disabled (TCP/UDP), + triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled + + 2010-03-25: Simon Goldschmidt + * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly + + 2010-03-25: Simon Goldschmidt + * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side + overrunning our rcv_wnd in ooseq case. + + 2010-03-22: Simon Goldschmidt + * tcp.c: tcp_listen() did not copy the pcb's prio. + + 2010-03-19: Simon Goldschmidt + * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set + + 2010-03-14: Simon Goldschmidt + * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports + where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h + and basing PBUF_LINK_HLEN on it. + + 2010-03-08: Simon Goldschmidt + * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections + when assiging routable address): when checking incoming packets and + aborting existing connection on address change, filter out link-local + addresses. + + 2010-03-06: Simon Goldschmidt + * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING + + 2010-03-06: Simon Goldschmidt + * ipv4/ip.c: Don't try to forward link-local addresses + + 2010-03-06: Simon Goldschmidt + * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal- + addresses to gw + + 2010-03-05: Simon Goldschmidt + * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type + and state. + + 2010-03-05: Simon Goldschmidt + * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split + into multiple calls to tcp_write. + + 2010-02-21: Simon Goldschmidt + * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep + the implementation of DNS_USES_STATIC_BUF==1) + + 2010-02-20: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement + close() vs. shutdown(). Now the application does not get any more + recv callbacks after calling tcp_close(). Added tcp_shutdown(). + + 2010-02-19: Simon Goldschmidt + * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent + confusion with realloc() + + 2010-02-15: Simon Goldschmidt/Stephane Lesage + * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK + (fixes bug #28899) + + 2010-02-14: Simon Goldschmidt + * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with + LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and + admin-status of a netif are up + + 2010-02-14: Simon Goldschmidt + * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet + reception and is not really necessary + + 2010-02-14: Simon Goldschmidt + * etharp.c/.h: Fixed ARP input processing: only add a new entry if a + request was directed as us (RFC 826, Packet Reception), otherwise + only update existing entries; internalized some functions + + 2010-02-14: Simon Goldschmidt + * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be + disabled on netif used for PPPoE) by adding a new netif flag + (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet + device but prevents usage of ARP (so that ethernet_input can be used + for PPPoE). + + 2010-02-12: Simon Goldschmidt + * netif.c: netif_set_link_up/down: only do something if the link state + actually changes + + 2010-02-12: Simon Goldschmidt/Stephane Lesage + * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking + connect) + + 2010-02-12: Simon Goldschmidt + * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h) + + 2010-02-09: Simon Goldschmidt + * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110 + (recv() makes receive window update for data that wasn't received by + application) + + 2010-02-09: Simon Goldschmidt/Stephane Lesage + * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out + or any netconn_recv() error) + + 2010-02-09: Simon Goldschmidt + * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets) + + 2010-02-09: Simon Goldschmidt + * netif.c: For loopback packets, adjust the stats- and snmp-counters + for the loopback netif. + + 2010-02-08: Simon Goldschmidt + * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity + since they are not used anywhere else. + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats + (patch from bug #28798) + + 2010-02-08: Simon Goldschmidt (Stéphane Lesage) + * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and + another bug when LWIP_RAND() returns zero. + + 2010-02-04: Simon Goldschmidt + * nearly every file: Use macros defined in ip_addr.h (some of them new) + to work with IP addresses (preparation for bug #27352 - Change ip_addr + from struct to typedef (u32_t) - and better code). + + 2010-01-31: Simon Goldschmidt + * netif.c: Don't call the link-callback from netif_set_up/down() since + this invalidly retriggers DHCP. + + 2010-01-29: Simon Goldschmidt + * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the + portability file inet.h and its contents from the stack: moved htonX- + functions to def.h (and the new def.c - they are not ipv4 dependent), + let inet.h depend on ip_addr.h and not the other way round. + This fixes bug #28732. + + 2010-01-28: Kieran Mansley + * tcp.c: Ensure ssthresh >= 2*MSS + + 2010-01-27: Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv + callback can lead to accessing unallocated memory. As a consequence, + ERR_ABRT means the application has called tcp_abort()! + + 2010-01-25: Simon Goldschmidt + * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY + not implemented in SNMP): write-only or not-accessible are still + returned by getnext (though not by get) + + 2010-01-24: Simon Goldschmidt + * snmp: Renamed the private mib node from 'private' to 'mib_private' to + not use reserved C/C++ keywords + + 2010-01-23: Simon Goldschmidt + * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less + than 1 ms + + 2010-01-21: Simon Goldschmidt + * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called + if tcp_enqueue fails) both in raw- and netconn-API + + 2010-01-19: Simon Goldschmidt + * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp + + 2010-01-18: Iordan Neshev/Simon Goldschmidt + * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some + bugfix backports from 2.4.x. + + 2010-01-18: Simon Goldschmidt + * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong + + 2010-01-17: Simon Goldschmidt + * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c): + task #10102: "netconn: clean up conn->err threading issues" by adding + error return value to struct api_msg_msg + + 2010-01-17: Simon Goldschmidt + * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept() + to return err_t (bugs #27709 and #28087) + + 2010-01-14: Simon Goldschmidt + * ...: Use typedef for function prototypes throughout the stack. + + 2010-01-13: Simon Goldschmidt + * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive + window = 0) by correctly draining recvmbox/acceptmbox + + 2010-01-11: Simon Goldschmidt + * pap.c: Fixed bug #13315 (PPP PAP authentication can result in + erroneous callbacks) by copying the code from recent pppd + + 2010-01-10: Simon Goldschmidt + * raw.c: Fixed bug #28506 (raw_bind should filter received packets) + + 2010-01-10: Simon Goldschmidt + * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)()) + + 2010-01-08: Simon Goldschmidt + * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535) + + 2010-01-08: Simon Goldschmidt + * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string + passed to dns_local_addhost() might be volatile + + 2010-01-07: Simon Goldschmidt + * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too + + 2010-01-06: Simon Goldschmidt + * netdb.h: Fixed bug #28496: missing include guards in netdb.h + + 2009-12-31: Simon Goldschmidt + * many ppp files: Reorganised PPP source code from ucip structure to pppd + structure to easily compare our code against the pppd code (around v2.3.1) + + 2009-12-27: Simon Goldschmidt + * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted + unit test + + +(STABLE-1.3.2) + + ++ New features: + + 2009-10-27 Simon Goldschmidt/Stephan Lesage + * netifapi.c/.h: Added netifapi_netif_set_addr() + + 2009-10-07 Simon Goldschmidt/Fabian Koch + * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to + support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO) + + 2009-08-26 Simon Goldschmidt/Simon Kallweit + * slipif.c/.h: bug #26397: SLIP polling support + + 2009-08-25 Simon Goldschmidt + * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN), + New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK. + + 2009-08-25 Simon Goldschmidt + * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*) + + 2009-08-24 Jakob Stoklund Olesen + * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond + to netif_set_link_up(). + + 2009-08-23 Simon Goldschmidt + * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state + to a human-readable string. + + ++ Bugfixes: + + 2009-12-24: Kieran Mansley + * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing + (BUG#28241) + + 2009-12-06: Simon Goldschmidt + * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can + be statically allocated (like in ucip) + + 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev) + * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT + + 2009-12-03: Simon Goldschmidt + * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit + could have non-zero length + + 2009-12-02: Simon Goldschmidt + * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting + tcp_input_pcb until after calling the pcb's callbacks + + 2009-11-29: Simon Goldschmidt + * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of- + sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code + + 2009-11-29: Simon Goldschmidt + * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by + queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty + + 2009-11-26: Simon Goldschmidt + * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending + segment + + 2009-11-26: Simon Goldschmidt + * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle + algorithm at PCB level + + 2009-11-22: Simon Goldschmidt + * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent + + 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach) + * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when + reusing time-wait pcb + + 2009-11-20: Simon Goldschmidt (patch by Albert Bartel) + * sockets.c: Fixed bug #28062: Data received directly after accepting + does not wake up select + + 2009-11-11: Simon Goldschmidt + * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo) + + 2009-10-30: Simon Goldschmidt + * opt.h: Increased default value for TCP_MSS to 536, updated default + value for TCP_WND to 4*TCP_MSS to keep delayed ACK working. + + 2009-10-28: Kieran Mansley + * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code + to follow algorithm from TCP/IP Illustrated + + 2009-10-27: Kieran Mansley + * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK + + 2009-10-25: Simon Goldschmidt + * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if + pcb->recv is NULL to keep rcv_wnd correct) + + 2009-10-25: Simon Goldschmidt + * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state + + 2009-10-23: Simon Goldschmidt (David Empson) + * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes + + 2009-10-21: Simon Goldschmidt + * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and + trailing 1 byte len (SYN/FIN) + + 2009-10-21: Simon Goldschmidt + * tcp_out.c: Fixed bug #27315: zero window probe and FIN + + 2009-10-19: Simon Goldschmidt + * dhcp.c/.h: Minor code simplification (don't store received pbuf, change + conditional code to assert where applicable), check pbuf length before + testing for valid reply + + 2009-10-19: Simon Goldschmidt + * dhcp.c: Removed most calls to udp_connect since they aren't necessary + when using udp_sendto_if() - always stay connected to IP_ADDR_ANY. + + 2009-10-16: Simon Goldschmidt + * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop + valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is + enabled + + 2009-10-15: Simon Goldschmidt (Oleg Tyshev) + * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit + + 2009-10-15: Simon Goldschmidt + * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv() + timeout + + 2009-10-15: Simon Goldschmidt + * autoip.c: Fixed bug #27704: autoip starts with wrong address + LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead + of network byte order + + 2009-10-11 Simon Goldschmidt (Jörg Kesten) + * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments + which are not consecutive when retransmitting unacked segments + + 2009-10-09 Simon Goldschmidt + * opt.h: Fixed default values of some stats to only be enabled if used + Fixes bug #27338: sys_stats is defined when NO_SYS = 1 + + 2009-08-30 Simon Goldschmidt + * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK + function" by checking for loopback before calling ip_frag + + 2009-08-25 Simon Goldschmidt + * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0 + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27078: Possible memory leak in pppInit() + + 2009-08-23 Simon Goldschmidt + * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result + is error. + + 2009-08-23 Simon Goldschmidt + * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF + Fixed wrong parenthesis, added check in init.c + + 2009-08-23 Simon Goldschmidt + * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms + + 2009-08-23 Simon Goldschmidt + * many ppp files: bug #27267: Added include to string.h where needed + + 2009-08-23 Simon Goldschmidt + * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian) + + +(STABLE-1.3.1) + + ++ New features: + + 2009-05-10 Simon Goldschmidt + * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option + LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only + one pbuf to help MACs that don't support scatter-gather DMA. + + 2009-05-09 Simon Goldschmidt + * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming + ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN + + 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen + * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive + extended info about the currently received packet. + + 2009-04-27 Simon Goldschmidt + * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1 + + 2009-04-25 Simon Goldschmidt + * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next + bigger malloc pool if one is empty (only usable with MEM_USE_POOLS). + + 2009-04-21 Simon Goldschmidt + * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static + hosts table. New configuration options DNS_LOCAL_HOSTLIST and + DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined + as an external function for lookup. + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for + TCP timestamp options, off by default. Rework tcp_enqueue() to + take option flags rather than specified option data + + 2009-02-18 Simon Goldschmidt + * cc.h: Added printf formatter for size_t: SZT_F + + 2009-02-16 Simon Goldschmidt (patch by Rishi Khan) + * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast + pings + + 2009-02-12 Simon Goldschmidt + * init.h: Added LWIP_VERSION to get the current version of the stack + + 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler) + * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead + of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc + is otherwise used) + + 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach) + * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial() + is only used by UDPLITE at present, so conditionalise it. + + 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli) + * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP + "seed" address. This should reduce AUTOIP conflicts if + LWIP_AUTOIP_CREATE_SEED_ADDR is overridden. + + 2008-10-02 Jonathan Larmour and Rishi Khan + * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking + socket. + + 2008-06-30 Simon Goldschmidt + * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from + interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows + mem_free to run between mem_malloc iterations. Added illegal counter for + mem stats. + + 2008-06-27 Simon Goldschmidt + * stats.h/.c, some other files: patch #6483: stats module improvement: + Added defines to display each module's statistic individually, added stats + defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter. + + 2008-06-17 Simon Goldschmidt + * err.h: patch #6459: Made err_t overridable to use a more efficient type + (define LWIP_ERR_T in cc.h) + + 2008-06-17 Simon Goldschmidt + * slipif.c: patch #6480: Added a configuration option for slipif for symmetry + to loopif + + 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli) + * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly + modified version of patch # 6370: Moved loopif code to netif.c so that + loopback traffic is supported on all netifs (all local IPs). + Added option to limit loopback packets for each netifs. + + + ++ Bugfixes: + 2009-08-12 Kieran Mansley + * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when + out of window or out of order properly + + 2009-08-12 Kieran Mansley + * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1 + + 2009-07-28 Simon Goldschmidt + * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s + + 2009-07-27 Kieran Mansley + * api.h api_msg.h netdb.h sockets.h: add missing #include directives + + 2009-07-09 Kieran Mansley + * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for + recv_avail and don't increment counters until message successfully + sent to mbox + + 2009-06-25 Kieran Mansley + * api_msg.c api.h: BUG26722: initialise netconn write variables + in netconn_alloc + + 2009-06-25 Kieran Mansley + * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set + + 2009-06-25 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct + simultaneous close behaviour, and make snd_nxt have the same meaning + as in the RFCs. + + 2009-05-12 Simon Goldschmidt + * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on + arp_table / uses etharp_query" by adding etharp_gratuitous() + + 2009-05-12 Simon Goldschmidt + * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options + to the IP header (used by igmp_ip_output_if) + + 2009-05-06 Simon Goldschmidt + * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if + defined) for SWAP_BYTES_IN_WORD to speed up checksumming. + + 2009-05-05 Simon Goldschmidt + * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select() + to crash + + 2009-05-04 Simon Goldschmidt + * init.c: snmp was not initialized in lwip_init() + + 2009-05-04 Frédéric Bernon + * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled. + + 2009-05-03 Simon Goldschmidt + * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full + (and unsent->next == NULL) + + 2009-05-02 Simon Goldschmidt + * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after + 1.3.0 in CVS only) - fixes compilation of ppp_oe.c + + 2009-05-02 Simon Goldschmidt + * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields + + 2009-05-01 Simon Goldschmidt + * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets + + 2009-05-01 Simon Goldschmidt + * ppp.c: bug #24228: Memory corruption with PPP and DHCP + + 2009-04-29 Frédéric Bernon + * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the + SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception + of broadcast packets even when this option wasn't set. Port maintainers + which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h. + If you want this option also filter broadcast on recv operations, you also + have to set IP_SOF_BROADCAST_RECV=1 in opt.h. + + 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen + * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and + DHCP/AUTOIP cooperation + + 2009-04-25 Simon Goldschmidt, Oleg Tyshev + * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd + Fixed by sorting the unsent and unacked queues (segments are inserted at the + right place in tcp_output and tcp_rexmit). + + 2009-04-25 Simon Goldschmidt + * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation + when debugging": memp_sizes contained the wrong sizes (including sanity + regions); memp pools for MEM_USE_POOLS were too small + + 2009-04-24 Simon Goldschmidt, Frédéric Bernon + * inet.c: patch #6765: Fix a small problem with the last changes (incorrect + behavior, with with ip address string not ended by a '\0', a space or a + end of line) + + 2009-04-19 Simon Goldschmidt + * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails, + pcb->err is called, not pcb->connected (with an error code). + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with + no-copy-tcpwrite": deallocate option data, only concat segments with same flags + + 2009-04-19 Simon Goldschmidt + * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated + in the header pbuf, not the data pbuf) + + 2009-04-18 Simon Goldschmidt + * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore() + + 2009-04-15 Simon Goldschmidt + * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp + + 2009-04-15 Simon Goldschmidt + * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in + + 2009-04-15 Simon Goldschmidt + * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function + ip_hinted_output() (for smaller code mainly) + + 2009-04-15 Simon Goldschmidt + * inet.c: patch #6765: Supporting new line characters in inet_aton() + + 2009-04-15 Simon Goldschmidt + * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option; + Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu + is big enough in dhcp_start + + 2009-04-15 Simon Goldschmidt + * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak + + 2009-04-15 Simon Goldschmidt + * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY + + 2009-04-15 Simon Goldschmidt + * sockets.c: bug #26121: set_errno can be overridden + + 2009-04-09 Kieran Mansley (patch from Luca Ceresoli ) + * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when + LWIP_TCP==0 + + 2009-04-09 Kieran Mansley (patch from Roy Lee ) + * tcp.h: Patch#6802 Add do-while-clauses to those function like + macros in tcp.h + + 2009-03-31 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window + updates are calculated and sent (BUG20515) + + * tcp_in.c: cope with SYN packets received during established states, + and retransmission of initial SYN. + + * tcp_out.c: set push bit correctly when tcp segments are merged + + 2009-03-27 Kieran Mansley + * tcp_out.c set window correctly on probes (correcting change made + yesterday) + + 2009-03-26 Kieran Mansley + * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping + connections where no reset required (bug #25622) + + * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes + (bug #20779) + + 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach) + * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be + too small depending on MEM_ALIGNMENT + + 2009-02-16 Simon Goldschmidt + * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard; + converted size argument of netconn_write to 'size_t' + + 2009-02-16 Simon Goldschmidt + * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host + by moving accept callback function pointer to TCP_PCB_COMMON + + 2009-02-12 Simon Goldschmidt + * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size" + option) + + 2009-02-11 Simon Goldschmidt + * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start) + + 2009-02-11 Simon Goldschmidt + * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize: + RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv()) + + 2009-02-10 Simon Goldschmidt + * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD: + Accepts_pending is decrease on a corresponding listen pcb when a connection + in state SYN_RCVD is close. + + 2009-01-28 Jonathan Larmour + * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run + out of pool pbufs. + + 2008-12-19 Simon Goldschmidt + * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2 + + 2008-12-10 Tamas Somogyi, Frédéric Bernon + * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and + port uses deleted netbuf. + + 2008-10-18 Simon Goldschmidt + * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length + in tcp_parseopt + + 2008-10-15 Simon Goldschmidt + * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers + by packing the struct ip_reass_helper. + + 2008-10-03 David Woodhouse, Jonathan Larmour + * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address. + + 2008-10-02 Jonathan Larmour + * dns.c: Hard-code structure sizes, to avoid issues on some compilers where + padding is included. + + 2008-09-30 Jonathan Larmour + * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an + assertion check that addrlen isn't NULL. + + 2008-09-30 Jonathan Larmour + * tcp.c: Fix bug #24227, wrong error message in tcp_bind. + + 2008-08-26 Simon Goldschmidt + * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and + inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h + + 2008-08-14 Simon Goldschmidt + * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when + tcp_close returns != ERR_OK) + + 2008-07-08 Frédéric Bernon + * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters + in macros, mainly if MEM_STATS=0 and MEMP_STATS=0). + + 2008-06-24 Jonathan Larmour + * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused + if tcp_seg_copy fails. + + 2008-06-17 Simon Goldschmidt + * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations) + and created defines for swapping bytes and folding u32 to u16. + + 2008-05-30 Kieran Mansley + * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd + rather than rcv_ann_wnd when deciding if packets are in-window. + Contributed by + + 2008-05-30 Kieran Mansley + * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow + passing as function pointers when MEM_LIBC_MALLOC is defined. + + 2008-05-09 Jonathan Larmour + * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to + stop it being treated as a fatal error. + + 2008-04-15 Simon Goldschmidt + * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP + (flag now cleared) + + 2008-03-27 Simon Goldschmidt + * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free + from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1 + in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs + or heap memory from interrupt context + + 2008-03-26 Simon Goldschmidt + * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote + host sent a zero mss as TCP option. + + +(STABLE-1.3.0) + + ++ New features: + + 2008-03-10 Jonathan Larmour + * inet_chksum.c: Allow choice of one of the sample algorithms to be + made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM. + + 2008-01-22 Frédéric Bernon + * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in + TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names. + + 2008-01-14 Frédéric Bernon + * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable + to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the + tcp_recv callback (see rawapi.txt). + + 2008-01-14 Frédéric Bernon, Marc Chaland + * ip.c: Integrate patch #6369" ip_input : checking before realloc". + + 2008-01-12 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::sem per netconn::op_completed like suggested for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-12 Frédéric Bernon + * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE, + DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues + sizes), like suggested for the task #7490 "Add return value to sys_mbox_post". + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490 + "Add return value to sys_mbox_post". tcpip_callback is always defined as + "blocking" ("block" parameter = 1). + + 2008-01-10 Frédéric Bernon + * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field + netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490 + "Add return value to sys_mbox_post". + + 2008-01-05 Frédéric Bernon + * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h: + Introduce changes for task #7490 "Add return value to sys_mbox_post" with some + modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which + indicate the number of pointers query by the mailbox. There is three defines + in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the + netconn::acceptmbox. Port maintainers, you can decide to just add this new + parameter in your implementation, but to ignore it to keep the previous behavior. + The new sys_mbox_trypost function return a value to know if the mailbox is + full or if the message is posted. Take a look to sys_arch.txt for more details. + This new function is used in tcpip_input (so, can be called in an interrupt + context since the function is not blocking), and in recv_udp and recv_raw. + + 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c, + tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the + "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add + documentation in the rawapi.txt file. + + 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer + + 2007-12-31 Frédéric Bernon, Luca Ceresoli + * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets + in autoip". The change in etharp_raw could be removed, since all calls to + etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be + wrong in the future. + + 2007-12-30 Frédéric Bernon, Tom Evans + * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address + Filtering" reported by Tom Evans. + + 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour + * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c, + sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API + applications have to call 'tcp_accepted(pcb)' in their accept callback to + keep accepting new connections. + + 2007-12-13 Frédéric Bernon + * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result" + by err_t type. Add a new err_t code "ERR_INPROGRESS". + + 2007-12-12 Frédéric Bernon + * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles + are the one which have ram usage. + + 2007-12-05 Frédéric Bernon + * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static + set of variables (=0) or a local one (=1). In this last case, your port should + provide a function "struct hostent* sys_thread_hostent( struct hostent* h)" + which have to do a copy of "h" and return a pointer ont the "per-thread" copy. + + 2007-12-03 Simon Goldschmidt + * ip.c: ip_input: check if a packet is for inp first before checking all other + netifs on netif_list (speeds up packet receiving in most cases) + + 2007-11-30 Simon Goldschmidt + * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access + UDP: move a (connected) pcb selected for input to the front of the list of + pcbs so that it is found faster next time. Same for RAW pcbs that have eaten + a packet. + + 2007-11-28 Simon Goldschmidt + * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS + + 2007-11-25 Simon Goldschmidt + * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy + algorithm. + + 2007-11-24 Simon Goldschmidt + * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c + to the new file netdb.c; included lwip_getaddrinfo. + + 2007-11-21 Simon Goldschmidt + * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss + based on the MTU of the netif used to send. Enabled by default. Disable by + setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492. + + 2007-11-19 Frédéric Bernon + * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name + received match the name query), implement DNS_USES_STATIC_BUF (the place where + copy dns payload to parse the response), return an error if there is no place + for a new query, and fix some minor problems. + + 2007-11-16 Simon Goldschmidt + * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c + removed files: core/inet.c, core/inet6.c + Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into + inet and chksum part; changed includes in all lwIP files as appropriate + + 2007-11-16 Simon Goldschmidt + * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential + dns resolver function for netconn api (netconn_gethostbyname) and socket api + (gethostbyname/gethostbyname_r). + + 2007-11-15 Jim Pettinato, Frédéric Bernon + * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name + requests with RAW api interface. Initialization is done in lwip_init() with + build time options. DNS timer is added in tcpip_thread context. DHCP can set + DNS server ip addresses when options are received. You need to set LWIP_DNS=1 + in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get + some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo" + list with points to improve. + + 2007-11-06 Simon Goldschmidt + * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly + enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status + for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined. + + 2007-11-06 Simon Goldschmidt + * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include + core header files in api.h (ip/tcp/udp/raw.h) to hide the internal + implementation from netconn api applications. + + 2007-11-03 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP & + RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled + by default). Netconn API users can use the netconn_recv_bufsize macro to access + it. This is a first release which have to be improve for TCP. Note it used the + netconn::recv_avail which need to be more "thread-safe" (note there is already + the problem for FIONREAD with lwip_ioctl/ioctlsocket). + + 2007-11-01 Frédéric Bernon, Marc Chaland + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c: + Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api + layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api + layer. This option enable to delayed TCP PUSH flag on multiple "write" calls. + Note that previous "copy" parameter for "write" APIs is now called "apiflags". + + 2007-10-24 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than + TCP_EVENT_xxx macros to get a code more readable. It could also help to remove + some code (like we have talk in "patch #5919 : Create compile switch to remove + select code"), but it could be done later. + + 2007-10-08 Simon Goldschmidt + * many files: Changed initialization: many init functions are not needed any + more since we now rely on the compiler initializing global and static + variables to zero! + + 2007-10-06 Simon Goldschmidt + * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY + to enqueue the received pbufs so that multiple packets can be reassembled + simultaneously and no static reassembly buffer is needed. + + 2007-10-05 Simon Goldschmidt + * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so + all netifs (or ports) can use it. + + 2007-10-05 Frédéric Bernon + * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the + common function to reduce a little bit the footprint (for all functions using + only the "netif" parameter). + + 2007-10-03 Frédéric Bernon + * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down, + netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce + a little bit the footprint (for all functions using only the "netif" parameter). + + 2007-09-15 Frédéric Bernon + * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF + option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for + netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for + IP_MULTICAST_TTL and IP_MULTICAST_IF. + + 2007-09-10 Frédéric Bernon + * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles + even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime() + each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can + decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but + call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime() + or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. + This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside + snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only + when it's queried (any direct call to "sysuptime" is changed by a call to + snmp_get_sysuptime). + + 2007-09-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP, + and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags + if you want IGMP on an interface. igmp_stop() is now called inside netif_remove(). + igmp_report_groups() is now called inside netif_set_link_up() (need to have + LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait + the next query message to receive the matching multicast streams). + + 2007-09-08 Frédéric Bernon + * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains + IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change). + Use this new field to access to common pcb fields (ttl, tos, so_options, etc...). + Enable to access to these fields with LWIP_TCP=0. + + 2007-09-05 Frédéric Bernon + * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h, + ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option + LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default). + Be careful, disabling ICMP make your product non-compliant to RFC1122, but + help to reduce footprint, and to reduce "visibility" on the Internet. + + 2007-09-05 Frédéric Bernon, Bill Florac + * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list + for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new + parameters have to be provided: a task name, and a task stack size. For this + one, since it's platform dependant, you could define the best one for you in + your lwipopts.h. For port maintainers, you can just add these new parameters + in your sys_arch.c file, and but it's not mandatory, use them in your OS + specific functions. + + 2007-09-05 Frédéric Bernon + * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings + inside init.c for task #7142 "Sanity check user-configurable values". + + 2007-09-04 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by + memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the + value). It will avoid potential fragmentation problems, use a counter to know + how many times a group is used on an netif, and free it when all applications + leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity + check if LWIP_IGMP!=0). + + 2007-09-03 Frédéric Bernon + * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement". + Initialize igmp_mac_filter to NULL in netif_add (this field should be set in + the netif's "init" function). Use the "imr_interface" field (for socket layer) + and/or the "interface" field (for netconn layer), for join/leave operations. + The igmp_join/leavegroup first parameter change from a netif to an ipaddr. + This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany). + + 2007-08-30 Frédéric Bernon + * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions + from api/api_lib". Now netbuf API is independant of netconn, and can be used + with other API (application based on raw API, or future "socket2" API). Ports + maintainers just have to add src/api/netbuf.c in their makefile/projects. + + 2007-08-30 Frédéric Bernon, Jonathan Larmour + * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check + user-configurable values". + + 2007-08-29 Frédéric Bernon + * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start. + igmp_start is call inside netif_add. Now, igmp initialization is in the same + spirit than the others modules. Modify some IGMP debug traces. + + 2007-08-29 Frédéric Bernon + * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function" + Add lwip_init function to regroup all modules initializations, and to provide + a place to add code for task #7142 "Sanity check user-configurable values". + Ports maintainers should remove direct initializations calls from their code, + and add init.c in their makefiles. Note that lwip_init() function is called + inside tcpip_init, but can also be used by raw api users since all calls are + disabled when matching options are disabled. Also note that their is new options + in opt.h, you should configure in your lwipopts.h (they are enabled per default). + + 2007-08-26 Marc Boucher + * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL + since they can under certain circumstances be called with an invalid conn + pointer after the connection has been closed (and conn has been freed). + + 2007-08-25 Frédéric Bernon (Artem Migaev's Patch) + * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up". + Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set. + + 2007-08-22 Frédéric Bernon + * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK + to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release. + + 2007-08-22 Frédéric Bernon + * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT & + ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the + name is tcpip_input (we keep the name of 1.2.0 function). + + 2007-08-17 Jared Grubb + * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool + settings into new memp_std.h and optional user file lwippools.h. This adds + more dynamic mempools, and allows the user to create an arbitrary number of + mempools for mem_malloc. + + 2007-08-16 Marc Boucher + * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function; + otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely + close the connection. + + 2007-08-16 Marc Boucher + * sockets.c: lwip_accept(): check netconn_peer() error return. + + 2007-08-16 Marc Boucher + * mem.c, mem.h: Added mem_calloc(). + + 2007-08-16 Marc Boucher + * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT) + for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG + and starving other message types. + Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API + + 2007-08-16 Marc Boucher + * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf + type and flgs (later renamed to flags). + Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*. + Improved lwip_recvfrom(). TCP push now propagated. + + 2007-08-16 Marc Boucher + * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global + provided by etharp. + + 2007-08-16 Marc Boucher + * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h, + etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c: + Added PPPoE support and various PPP improvements. + + 2007-07-25 Simon Goldschmidt + * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial, + making netbuf_copy_partial use this function. + + 2007-07-25 Simon Goldschmidt + * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with + 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and + other stacks. + + 2007-07-13 Jared Grubb (integrated by Frédéric Bernon) + * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add + a link callback in the netif struct, and functions to handle it. Be carefull + for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c) + if you want to be sure to be compatible with future changes... + + 2007-06-30 Frédéric Bernon + * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions. + + 2007-06-21 Simon Goldschmidt + * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both + LWIP_AUTOIP =0 and =1 to remove redundant code. + + 2007-06-21 Simon Goldschmidt + * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option + MEM_USE_POOLS to use 4 pools with different sized elements instead of a + heap. This both prevents memory fragmentation and gives a higher speed + at the cost of more memory consumption. Turned off by default. + + 2007-06-21 Simon Goldschmidt + * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of + netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into + int to be able to send a bigger buffer than 64K with one time (mainly + used from lwip_send). + + 2007-06-21 Simon Goldschmidt + * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write + into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too. + + 2007-06-21 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in + netconn_write from api_lib.c to api_msg.c to also prevent multiple context- + changes on low memory or empty send-buffer. + + 2007-06-18 Simon Goldschmidt + * etharp.c, etharp.h: Changed etharp to use a defined hardware address length + of 6 to avoid loading netif->hwaddr_len every time (since this file is only + used for ethernet and struct eth_addr already had a defined length of 6). + + 2007-06-17 Simon Goldschmidt + * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets + to disable UDP checksum generation on transmit. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid + pointers or parameters, and let the possibility to redefined it in cc.h. Use + this macro to check "conn" parameter in api_msg.c functions. + + 2007-06-11 Simon Goldschmidt + * sockets.c, sockets.h: Added UDP lite support for sockets + + 2007-06-10 Simon Goldschmidt + * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled + by default) to switch off UDP-Lite support if not needed (reduces udp.c code + size) + + 2007-06-09 Dominik Spies (integrated by Frédéric Bernon) + * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h: + AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and + LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt + (see TODO mark in the source code). + + 2007-06-09 Simon Goldschmidt + * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for + etharp_output() to match netif->output so etharp_output() can be used + directly as netif->output to save one function call. + + 2007-06-08 Simon Goldschmidt + * netif.h, ethernetif.c, slipif.c, loopif.c: Added define + NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables, + added initialization of those to ethernetif, slipif and loopif. + + 2007-05-18 Simon Goldschmidt + * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF + (defaulting to off for now) that can be set to 0 to send fragmented + packets by passing PBUF_REFs down the stack. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP + connections, such present in patch #5959. + + 2007-05-23 Frédéric Bernon + * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx + code in only one part... + + 2007-05-18 Simon Goldschmidt + * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp + elements to overflow. This is achieved by adding some bytes before and after + each pool element (increasing their size, of course), filling them with a + prominent value and checking them on freeing the element. + Set it to 2 to also check every element in every pool each time memp_malloc() + or memp_free() is called (slower but more helpful). + + 2007-05-10 Simon Goldschmidt + * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for + PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce + code size. + + 2007-05-11 Frédéric Bernon + * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c: + Include a function pointer instead of a table index in the message to reduce + footprint. Disable some part of lwip_send and lwip_sendto if some options are + not set (LWIP_TCP, LWIP_UDP, LWIP_RAW). + + 2007-05-10 Simon Goldschmidt + * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus + \ extern "C" {' in all header files. Now you can write your application using + the lwIP stack in C++ and simply #include the core files. Note I have left + out the netif/ppp/*h header files for now, since I don't know which files are + included by applications and which are for internal use only. + + 2007-05-09 Simon Goldschmidt + * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library + memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for + situations where some compilers might inline the copy and save a function + call. Also replaced all calls to memcpy() with calls to (S)MEMCPY(). + + 2007-05-08 Simon Goldschmidt + * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc()) + to be overriden in case the C-library malloc implementation is not protected + against concurrent access. + + 2007-05-04 Simon Goldschmidt (Atte Kojo) + * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending + multiple packets to the same host. + + 2007-05-04 Frédéric Bernon, Jonathan Larmour + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible + to corrupt remote addr/port connection state". Reduce problems "not enought memory" with + netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between + sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function. + Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct, + these fields are now renamed "addr" & "port". + + 2007-04-11 Jonathan Larmour + * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new + sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return + with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro + by the port in sys_arch.h if desired. + + 2007-04-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API + allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp + clients, using new functions from netifapi.h. Disable as default (no port change to do). + + 2007-04-05 Frédéric Bernon + * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant. + + 2007-04-04 Simon Goldschmidt + * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x) + use this for and architecture-independent form to tell the compiler you intentionally + are not using this variable. Can be overriden in cc.h. + + 2007-03-28 Frédéric Bernon + * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to + define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded + string, point on one of your's ethernetif field, or alloc a string you will free yourself). + It will be used by DHCP to register a client hostname, but can also be use when you call + snmp_set_sysname. + + 2007-03-28 Frédéric Bernon + * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to + initialize a network interface's flag with. It tell this interface is an ethernet + device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility + Support for IPv4" section 4.6) when interface is "up" with netif_set_up(). + + 2007-03-26 Frédéric Bernon, Jonathan Larmour + * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build + time if you only use PPP or SLIP. The default is enable. Note we don't have to call + etharp_init in your port's initilization sequence if you use tcpip.c, because this call + is done in tcpip_init function. + + 2007-03-22 Frédéric Bernon + * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the + new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in + your lwipopts.h. More, unused counters are not defined in the stats structs, and not + display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined + but never used. Fix msg_in.c with the correct #if test for a stat display. + + 2007-03-21 Kieran Mansley + * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com). + Provides callback on netif up/down state change. + + 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds + * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c, + ip.c, netif.h, tcpip.c, opt.h: + New configuration option LWIP_IGMP to enable IGMP processing. Based on only one + filter per all network interfaces. Declare a new function in netif to enable to + control the MAC filter (to reduce lwIP traffic processing). + + 2007-03-11 Frédéric Bernon + * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can + be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this + unless you know what you're doing (default are RFC1122 compliant). Note + that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds. + + 2007-03-08 Frédéric Bernon + * tcp.h: Keepalive values can be configured at compile time, but don't change + this unless you know what you're doing (default are RFC1122 compliant). + + 2007-03-08 Frédéric Bernon + * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h: + Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO + on UDP sockets/netconn. + + 2007-03-08 Simon Goldschmidt + * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time. + + 2007-03-06 Frédéric Bernon + * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h: + Implement SO_RCVTIMEO on UDP sockets/netconn. + + 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt) + * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated + on the stack and remove the API msg type from memp + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * sockets.h, sockets.c: Move socket initialization to new + lwip_socket_init() function. + NOTE: this changes the API with ports. Ports will have to be + updated to call lwip_socket_init() now. + + 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt) + * api_lib.c: Use memcpy in netbuf_copy_partial. + + + ++ Bug fixes: + + 2008-03-17 Frédéric Bernon, Ed Kerekes + * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have + some problems to fill the IP header on some targets, use now the + ip.h macros to do it). + + 2008-03-13 Frédéric Bernon + * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using + (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a + TCP connection caused a crash. Note that using (lwip_)recvfrom + like this is a bit slow and that using (lwip)getpeername is the + good lwip way to do it (so, using recv is faster on tcp sockets). + + 2008-03-12 Frédéric Bernon, Jonathan Larmour + * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's + recv_raw() does not consume data", and the ping sample (with + LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom + returned the IP payload, without the IP header). + + 2008-03-04 Jonathan Larmour + * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors + and/or warnings on some systems where mem_size_t and size_t differ. + * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc. + + 2008-03-04 Kieran Mansley (contributions by others) + * Numerous small compiler error/warning fixes from contributions to + mailing list after 1.3.0 release candidate made. + + 2008-01-25 Cui hengbin (integrated by Frédéric Bernon) + * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures. + + 2008-01-15 Kieran Mansley + * tcp_out.c: BUG20511. Modify persist timer to start when we are + prevented from sending by a small send window, not just a zero + send window. + + 2008-01-09 Jonathan Larmour + * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid + conflict with Linux system headers. + + 2008-01-06 Jonathan Larmour + * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP + address entirely on receiving a DHCPNAK, and restarting discovery. + + 2007-12-21 Simon Goldschmidt + * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail + is not protected" by using new macros for interlocked access to modify/test + netconn->recv_avail. + + 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev) + * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state) + + 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm) + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling + of silly window avoidance and prevent lwIP from shrinking the window) + + 2007-12-04 Simon Goldschmidt + * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last + data packet was lost): add assert that all segment lists are empty in + tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED + state from LAST_ACK in tcp_process + + 2007-12-02 Simon Goldschmidt + * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET + If including for system-struct timeval, LWIP_TIMEVAL_PRIVATE now + has to be set to 0 in lwipopts.h + + 2007-12-02 Simon Goldschmidt + * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always + allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen + netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox. + This is a fix for thread-safety and allocates all items needed for a netconn + when the netconn is created. + + 2007-11-30 Simon Goldschmidt + * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple + netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed + to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same + port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address) + + 2007-11-27 Simon Goldschmidt + * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by + letting ip_route only use netifs that are up. + + 2007-11-27 Simon Goldschmidt + * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF + and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and + sockets block most operations once they have seen a fatal error. + + 2007-11-27 Simon Goldschmidt + * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the + netif to send as an argument (to be able to send on netifs that are down). + + 2007-11-26 Simon Goldschmidt + * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs + arrive out-of-order + + 2007-11-21 Simon Goldschmidt + * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early + Fixed the nagle algorithm; nagle now also works for all raw API applications + and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY' + + 2007-11-12 Frédéric Bernon + * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most + of the netconn_peer and netconn_addr processing is done inside tcpip_thread + context in do_getaddr. + + 2007-11-10 Simon Goldschmidt + * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can + happen any time). Now the packet simply isn't enqueued when out of memory. + + 2007-11-01 Simon Goldschmidt + * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or + TCP_MSS if that is smaller) as long as no MSS option is received from the + remote host. + + 2007-11-01 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN) + is now based on TCP_MSS instead of pcb->mss (on passive open now effectively + sending our configured TCP_MSS instead of the one received). + + 2007-11-01 Simon Goldschmidt + * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was + calculated based on the configured TCP_MSS, not on the MSS option received + with SYN+ACK. + + 2007-10-09 Simon Goldschmidt + * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too + short and also was generated wrong if checksum coverage != tot_len; + receive: checksum was calculated wrong if checksum coverage != tot_len + + 2007-10-08 Simon Goldschmidt + * mem.c: lfree was not updated in mem_realloc! + + 2007-10-07 Frédéric Bernon + * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential + crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT: + this change cause an API breakage for netconn_addr, since a parameter + type change. Any compiler should cause an error without any changes in + yours netconn_peer calls (so, it can't be a "silent change"). It also + reduce a little bit the footprint for socket layer (lwip_getpeername & + lwip_getsockname use now a common lwip_getaddrname function since + netconn_peer & netconn_addr have the same parameters). + + 2007-09-20 Simon Goldschmidt + * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state) + by checking tcp_tw_pcbs also + + 2007-09-19 Simon Goldschmidt + * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies) + + 2007-09-15 Mike Kleshov + * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used) + + 2007-09-06 Frédéric Bernon + * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove + it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which + already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h" + if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h. + + 2007-08-30 Frédéric Bernon + * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces, + and fix some coding style. + + 2007-08-28 Frédéric Bernon + * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any + kind of packets. These packets are considered like Ethernet packets (payload + pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets + are considered like IP packets (payload pointing to iphdr). + + 2007-08-27 Frédéric Bernon + * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error + problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state + and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT). + + 2007-08-24 Kieran Mansley + * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy + compiler (Paradigm C++) + + 2007-08-09 Frédéric Bernon, Bill Florac + * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement. + Introduce IGMP_STATS to centralize statistics management. + + 2007-08-09 Frédéric Bernon, Bill Florac + * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast + packet on a udp pcb binded on an netif's IP address, and not on "any". + + 2007-08-09 Frédéric Bernon, Bill Florac + * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement. + This is mainly on using lookup/lookfor, and some coding styles... + + 2007-07-26 Frédéric Bernon (and "thedoctor") + * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages. + + 2007-07-25 Simon Goldschmidt + * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if + tcp_output fails in tcp_close, the code in do_close_internal gets simpler + (tcp_output is called again later from tcp timers). + + 2007-07-25 Simon Goldschmidt + * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old + copy_from_pbuf, which illegally modified the given pbuf. + + 2007-07-25 Simon Goldschmidt + * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs: + changed snd_queuelen++ to snd_queuelen += pbuf_clen(p). + + 2007-07-24 Simon Goldschmidt + * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the + correct state (must be CLOSED). + + 2007-07-13 Thomas Taranowski (commited by Jared Grubb) + * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed + allocation. It now returns NULL. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in + all error cases. + + 2007-07-13 Frédéric Bernon + * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed, + because current code doesn't follow rawapi.txt documentation. + + 2007-07-13 Kieran Mansley + * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in + out of sequence processing of received packets + + 2007-07-03 Simon Goldschmidt + * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an + assumption is made that this pbuf is in one piece (i.e. not chained). These + assumptions clash with the possibility of converting to fully pool-based + pbuf implementations, where PBUF_RAM pbufs might be chained. + + 2007-07-03 Simon Goldschmidt + * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems + when closing tcp netconns: removed conn->sem, less context switches when + closing, both netconn_close and netconn_delete should safely close tcp + connections. + + 2007-07-02 Simon Goldschmidt + * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c, + tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off) + to cache ARP table indices with each pcb instead of single-entry cache for + the complete stack. + + 2007-07-02 Simon Goldschmidt + * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent + warnings when assigning to smaller types. + + 2007-06-28 Simon Goldschmidt + * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing. + + 2007-06-28 Simon Goldschmidt + * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if + a segment contained chained pbufs) + + 2007-06-28 Frédéric Bernon + * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute + a "pseudo-random" value based on netif's MAC and some autoip fields. It's always + possible to define this macro in your own lwipopts.h to always use C library's + rand(). Note that autoip_create_rand_addr doesn't use this macro. + + 2007-06-28 Frédéric Bernon + * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option + LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications + in api_lib/api_msg (use pointers and not type with table, etc...) + + 2007-06-26 Simon Goldschmidt + * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload + for udp packets with no matching pcb. + + 2007-06-25 Simon Goldschmidt + * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match + could get udp input packets if the remote side matched. + + 2007-06-13 Simon Goldschmidt + * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get + changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0. + + 2007-06-13 Simon Goldschmidt + * api_msg.c: pcb_new sets conn->err if protocol is not implemented + -> netconn_new_..() does not allocate a new connection for unsupported + protocols. + + 2007-06-13 Frédéric Bernon, Simon Goldschmidt + * api_lib.c: change return expression in netconn_addr and netconn_peer, because + conn->err was reset to ERR_OK without any reasons (and error was lost)... + + 2007-06-13 Frédéric Bernon, Matthias Weisser + * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename + MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid + some macro names collision with some OS macros. + + 2007-06-11 Simon Goldschmidt + * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0, + create checksum over the complete packet. On RX, if it's < 8 (and not 0), + discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both + UDP & UDP Lite. + + 2007-06-11 Srinivas Gollakota & Oleg Tyshev + * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags" + where TCP flags wasn't initialized in tcp_keepalive. + + 2007-06-03 Simon Goldschmidt + * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function + registered, p->payload was modified without modifying p->len if sending + icmp_dest_unreach() (had no negative effect but was definitively wrong). + + 2007-06-03 Simon Goldschmidt + * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp + re-used the input pbuf even if that didn't have enough space to include the + link headers. Now the space is tested and a new pbuf is allocated for the + echo response packet if the echo request pbuf isn't big enough. + + 2007-06-01 Simon Goldschmidt + * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread. + + 2007-05-23 Frédéric Bernon + * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only + allocated by do_listen if success) and netconn_accept errors handling. In + most of api_lib functions, we replace some errors checkings like "if (conn==NULL)" + by ASSERT, except for netconn_delete. + + 2007-05-23 Frédéric Bernon + * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return + an error code if it's impossible to fetch a pbuf on a TCP connection (and not + directly close the recvmbox). + + 2007-05-22 Simon Goldschmidt + * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of + bound but unconnected (and non-listening) tcp_pcbs. + + 2007-05-22 Frédéric Bernon + * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only + used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of + sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features + like "sys_timeout" in their application threads. + + 2007-05-22 Frédéric Bernon + * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see + which parameters are used by which do_xxx function, and to avoid "misusing" + parameters (patch #5938). + + 2007-05-22 Simon Goldschmidt + * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938: + changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto + is only 8 bits wide. This affects the api, as there, the protocol was + u16_t, too. + + 2007-05-18 Simon Goldschmidt + * memp.c: addition to patch #5913: smaller pointer was returned but + memp_memory was the same size -> did not save memory. + + 2007-05-16 Simon Goldschmidt + * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns + != ERR_OK. + + 2007-05-16 Simon Goldschmidt + * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same + as the one of the netif used for sending to prevent sending from old + addresses after a netif address gets changed (partly fixes bug #3168). + + 2007-05-16 Frédéric Bernon + * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work + with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in + tcpip_init) because we have to be sure that network interfaces are already + added (mac filter is updated only in igmp_init for the moment). + + 2007-05-16 Simon Goldschmidt + * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls + into sys_arch_sem_wait calls to prevent timers from running while waiting + for the heap. This fixes bug #19167. + + 2007-05-13 Simon Goldschmidt + * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines + for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from + tcp.h to sockets.h. + + 2007-05-07 Simon Goldschmidt + * mem.c: Another attempt to fix bug #17922. + + 2007-05-04 Simon Goldschmidt + * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy() + implementation so that it can be reused (don't allocate the target + pbuf inside pbuf_copy()). + + 2007-05-04 Simon Goldschmidt + * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem + to save a little RAM (next pointer of memp is not used while not in pool). + + 2007-05-03 "maq" + * sockets.c: Fix ioctl FIONREAD when some data remains from last recv. + (patch #3574). + + 2007-04-23 Simon Goldschmidt + * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results + in NULL reference for incoming TCP packets". Loopif has to be configured + (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input() + (multithreading environments, e.g. netif->input() = tcpip_input()) or + putting packets on a list that is fed to the stack by calling loopif_poll() + (single-thread / NO_SYS / polling environment where e.g. + netif->input() = ip_input). + + 2007-04-17 Jonathan Larmour + * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold + the difference between two u16_t's. + * sockets.h: FD_SETSIZE needs to match number of sockets, which is + MEMP_NUM_NETCONN in sockets.c right now. + + 2007-04-12 Jonathan Larmour + * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580). + + 2007-04-12 Kieran Mansley + * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission + timer is reset to fix bug#19434, with help from Oleg Tyshev. + + 2007-04-11 Simon Goldschmidt + * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than + previously thought need to be copied (everything but PBUF_ROM!). Cleaned up + pbuf.c: removed functions no needed any more (by etharp). + + 2007-04-11 Kieran Mansley + * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix + "Constant is long" warnings with 16bit compilers. Contributed by + avatar@mmlab.cse.yzu.edu.tw + + 2007-04-05 Frédéric Bernon, Jonathan Larmour + * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on + the mailbox is active". Now, the post is only done during a connect, and do_send, + do_write and do_join_leave_group don't do anything if a previous error was signaled. + + 2007-04-03 Frédéric Bernon + * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output + packets. See patch #5834. + + 2007-03-30 Frédéric Bernon + * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add + missing pcb allocations checking (in do_bind, and for each raw_new). Fix style. + + 2007-03-30 Frédéric Bernon + * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with + others environment defines (these were too "generic"). + + 2007-03-28 Frédéric Bernon + * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call + result and can cause a crash. lwip_send now check netbuf_ref result. + + 2007-03-28 Simon Goldschmidt + * sockets.c Remove "#include " from sockets.c to avoid multiple + definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is + defined. This is the way it should have been already (looking at + doc/sys_arch.txt) + + 2007-03-28 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS + + IP and TCP headers *and* physical link headers + + 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov) + * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause + to send some garbage. It is not a definitive solution, but the patch does solve + the problem for most cases. + + 2007-03-22 Frédéric Bernon + * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used). + + 2007-03-22 Frédéric Bernon + * api_lib.c: somes resources couldn't be freed if there was errors during + netconn_new_with_proto_and_callback. + + 2007-03-22 Frédéric Bernon + * ethernetif.c: update netif->input calls to check return value. In older ports, + it's a good idea to upgrade them, even if before, there could be another problem + (access to an uninitialized mailbox). + + 2007-03-21 Simon Goldschmidt + * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed + by casting to unsigned). + + 2007-03-21 Frédéric Bernon + * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from + api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a + dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call. + Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a + faster and more reliable communication between api_lib and tcpip. + + 2007-03-21 Frédéric Bernon + * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0. + + 2007-03-21 Frédéric Bernon + * api_msg.c, igmp.c, igmp.h: Fix C++ style comments + + 2007-03-21 Kieran Mansley + * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS + + IP and TCP headers + + 2007-03-21 Kieran Mansley + * Fix all uses of pbuf_header to check the return value. In some + cases just assert if it fails as I'm not sure how to fix them, but + this is no worse than before when they would carry on regardless + of the failure. + + 2007-03-21 Kieran Mansley + * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and + comment out missing header include in icmp.c + + 2007-03-20 Frédéric Bernon + * memp.h, stats.c: Fix stats_display function where memp_names table wasn't + synchronized with memp.h. + + 2007-03-20 Frédéric Bernon + * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input, + tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with + network interfaces. Also fix a compiler warning. + + 2007-03-20 Kieran Mansley + * udp.c: Only try and use pbuf_header() to make space for headers if + not a ROM or REF pbuf. + + 2007-03-19 Frédéric Bernon + * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg() + and api_msg_post(). + + 2007-03-19 Frédéric Bernon + * Remove unimplemented "memp_realloc" function from memp.h. + + 2007-03-11 Simon Goldschmidt + * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused + memory corruption. + + 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov) + * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251 + (missing `const' qualifier in socket functions), to get more compatible to + standard POSIX sockets. + + 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov) + * sockets.c: Add asserts inside bind, connect and sendto to check input + parameters. Remove excessive set_errno() calls after get_socket(), because + errno is set inside of get_socket(). Move last sock_set_errno() inside + lwip_close. + + 2007-03-09 Simon Goldschmidt + * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory + was allocated too small. + + 2007-03-06 Simon Goldschmidt + * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect + the stack from concurrent access. + + 2007-03-06 Frédéric Bernon, Dmitry Potapov + * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy + call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input(). + + 2007-03-06 Simon Goldschmidt + * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files + if IP_FRAG == 0 and IP_REASSEMBLY == 0 + + 2007-03-06 Frédéric Bernon, Simon Goldschmidt + * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration + option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput. + Allow to do ARP processing for incoming packets inside tcpip_thread + (protecting ARP layer against concurrent access). You can also disable + old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0. + Older ports have to use tcpip_ethinput. + + 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov) + * err.h, err.c: fixed compiler warning "initialization dircards qualifiers + from pointer target type" + + 2007-03-05 Frédéric Bernon + * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES, + ETHARP_TRUST_IP_MAC, review SO_REUSE) + + 2007-03-04 Frédéric Bernon + * api_msg.c: Remove some compiler warnings : parameter "pcb" was never + referenced. + + 2007-03-04 Frédéric Bernon + * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from + Dmitry Potapov). + The api_msg struct stay on the stack (not moved to netconn struct). + + 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov) + * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if + SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available) + Also fixed cast warning in pbuf_alloc() + + 2007-03-04 Simon Goldschmidt + * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt + existing pbuf chain when enqueuing multiple pbufs to a pending ARP request + + 2007-03-03 Frédéric Bernon + * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;" + It is static, and never used in udp.c except udp_init(). + + 2007-03-02 Simon Goldschmidt + * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from + tcpip_thread() to tcpip_init(). This way, raw API connections can be + initialized before tcpip_thread is running (e.g. before OS is started) + + 2007-03-02 Frédéric Bernon + * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call + interval. + + 2007-02-28 Kieran Mansley + * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved + outside the region of the pbuf by pbuf_header() + + 2007-02-28 Kieran Mansley + * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero + when supplied timeout is also non-zero + +(STABLE-1.2.0) + + 2006-12-05 Leon Woestenberg + * CHANGELOG: Mention STABLE-1.2.0 release. + + ++ New features: + + 2006-12-01 Christiaan Simons + * mem.h, opt.h: Added MEM_LIBC_MALLOC option. + Note this is a workaround. Currently I have no other options left. + + 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour) + * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define + to include/lwip/opt.h. + * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL. + Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h. + * opt.h: Add above new options. + + 2006-08-18 Christiaan Simons + * tcp_{in,out}.c: added SNMP counters. + * ipv4/ip.c: added SNMP counters. + * ipv4/ip_frag.c: added SNMP counters. + + 2006-08-08 Christiaan Simons + * etharp.{c,h}: added etharp_find_addr() to read + (stable) ethernet/IP address pair from ARP table + + 2006-07-14 Christiaan Simons + * mib_structs.c: added + * include/lwip/snmp_structs.h: added + * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct + + 2006-07-06 Christiaan Simons + * snmp/asn1_{enc,dec}.c added + * snmp/mib2.c added + * snmp/msg_{in,out}.c added + * include/lwip/snmp_asn1.h added + * include/lwip/snmp_msg.h added + * doc/snmp_agent.txt added + + 2006-03-29 Christiaan Simons + * inet.c, inet.h: Added platform byteswap support. + Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and + optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros. + + ++ Bug fixes: + + 2006-11-30 Christiaan Simons + * dhcp.c: Fixed false triggers of request_timeout. + + 2006-11-28 Christiaan Simons + * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags. + + 2006-10-11 Christiaan Simons + * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h: + Partially accepted patch #5449 for ANSI C compatibility / build fixes. + * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol + identifier from 170 to 136 (bug #17574). + + 2006-10-10 Christiaan Simons + * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice. + + 2006-08-17 Christiaan Simons + * udp.c: Fixed bug #17200, added check for broadcast + destinations for PCBs bound to a unicast address. + + 2006-08-07 Christiaan Simons + * api_msg.c: Flushing TCP output in do_close() (bug #15926). + + 2006-06-27 Christiaan Simons + * api_msg.c: Applied patch for cold case (bug #11135). + In accept_function() ensure newconn->callback is always initialized. + + 2006-06-15 Christiaan Simons + * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748), + facilitate printing of mem_size_t and u16_t statistics. + + 2006-06-14 Christiaan Simons + * api_msg.c: Applied patch #5146 to handle allocation failures + in accept() by Kevin Lawson. + + 2006-05-26 Christiaan Simons + * api_lib.c: Removed conn->sem creation and destruction + from netconn_write() and added sys_sem_new to netconn_new_*. + +(STABLE-1_1_1) + + 2006-03-03 Christiaan Simons + * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap + access and added pbuf_alloc() return value checks. + + 2006-01-01 Leon Woestenberg + * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is + now handled by the checksum routine properly. + + 2006-02-27 Leon Woestenberg + * pbuf.c: Fix alignment; pbuf_init() would not work unless + pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.) + + 2005-12-20 Leon Woestenberg + * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch + submitted by Mitrani Hiroshi. + + 2005-12-15 Christiaan Simons + * inet.c: Disabled the added summing routine to preserve code space. + + 2005-12-14 Leon Woestenberg + * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson. + Added Curt McDowell's optimized checksumming routine for future + inclusion. Need to create test case for unaliged, aligned, odd, + even length combination of cases on various endianess machines. + + 2005-12-09 Christiaan Simons + * inet.c: Rewrote standard checksum routine in proper portable C. + + 2005-11-25 Christiaan Simons + * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only. + * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t, + u32_t, s32_t typedefs. This solves most debug word-length assumes. + + 2005-07-17 Leon Woestenberg + * inet.c: Fixed unaligned 16-bit access in the standard checksum + routine by Peter Jolasson. + * slipif.c: Fixed implementation assumption of single-pbuf datagrams. + + 2005-02-04 Leon Woestenberg + * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch. + * tcp_{out|in}.c: Applied patch fixing unaligned access. + + 2005-01-04 Leon Woestenberg + * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement. + + 2005-01-03 Leon Woestenberg + * udp.c: UDP pcb->recv() was called even when it was NULL. + +(STABLE-1_1_0) + + 2004-12-28 Leon Woestenberg + * etharp.*: Disabled multiple packets on the ARP queue. + This clashes with TCP queueing. + + 2004-11-28 Leon Woestenberg + * etharp.*: Fixed race condition from ARP request to ARP timeout. + Halved the ARP period, doubled the period counts. + ETHARP_MAX_PENDING now should be at least 2. This prevents + the counter from reaching 0 right away (which would allow + too little time for ARP responses to be received). + + 2004-11-25 Leon Woestenberg + * dhcp.c: Decline messages were not multicast but unicast. + * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD. + Do not try hard to insert arbitrary packet's source address, + etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD. + etharp_query() now always DOES call ETHARP_TRY_HARD so that users + querying an address will see it appear in the cache (DHCP could + suffer from this when a server invalidly gave an in-use address.) + * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are + comparing network addresses (identifiers), not the network masks + themselves. + * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given + IP address actually belongs to the network of the given interface. + + 2004-11-24 Kieran Mansley + * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state. + +(STABLE-1_1_0-RC1) + + 2004-10-16 Kieran Mansley + * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately, + even if one is already pending, if the rcv_wnd is above a threshold + (currently TCP_WND/2). This avoids waiting for a timer to expire to send a + delayed ACK in order to open the window if the stack is only receiving data. + + 2004-09-12 Kieran Mansley + * tcp*.*: Retransmit time-out handling improvement by Sam Jansen. + + 2004-08-20 Tony Mountifield + * etharp.c: Make sure the first pbuf queued on an ARP entry + is properly ref counted. + + 2004-07-27 Tony Mountifield + * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler + warnings about comparison. + * pbuf.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. Closed an unclosed comment. + * tcp.c: Stopped compiler complaining of empty if statement + when LWIP_DEBUGF() empty. + * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons(). + * inet.c: Added a couple of casts to quiet the compiler. + No need to test isascii(c) before isdigit(c) or isxdigit(c). + + 2004-07-22 Tony Mountifield + * inet.c: Made data types consistent in inet_ntoa(). + Added casts for return values of checksum routines, to pacify compiler. + * ip_frag.c, tcp_out.c, sockets.c, pbuf.c + Small corrections to some debugging statements, to pacify compiler. + + 2004-07-21 Tony Mountifield + * etharp.c: Removed spurious semicolon and added missing end-of-comment. + * ethernetif.c Updated low_level_output() to match prototype for + netif->linkoutput and changed low_level_input() similarly for consistency. + * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype + of raw_recv() in raw.h and so avoid compiler error. + * sockets.c: Added trivial (int) cast to keep compiler happier. + * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros. + +(STABLE-1_0_0) + + ++ Changes: + + 2004-07-05 Leon Woestenberg + * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure + your cc.h file defines this either 1 or 0. If non-defined, + defaults to 1. + * .c: Added and includes where used. + * etharp.c: Made some array indices unsigned. + + 2004-06-27 Leon Woestenberg + * netif.*: Added netif_set_up()/down(). + * dhcp.c: Changes to restart program flow. + + 2004-05-07 Leon Woestenberg + * etharp.c: In find_entry(), instead of a list traversal per candidate, do a + single-pass lookup for different candidates. Should exploit locality. + + 2004-04-29 Leon Woestenberg + * tcp*.c: Cleaned up source comment documentation for Doxygen processing. + * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC. + * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by + the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option. + + ++ Bug fixes: + + 2004-04-27 Leon Woestenberg + * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution + suggested by Timmy Brolin. Fix for 32-bit processors that cannot access + non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix + is to prefix the 14-bit Ethernet headers with two padding bytes. + + 2004-04-23 Leon Woestenberg + * ip_addr.c: Fix in the ip_addr_isbroadcast() check. + * etharp.c: Fixed the case where the packet that initiates the ARP request + is not queued, and gets lost. Fixed the case where the packets destination + address is already known; we now always queue the packet and perform an ARP + request. + +(STABLE-0_7_0) + + ++ Bug fixes: + + * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition. + * Fixed TCP bug in dequeueing of FIN from out of order segment queue. + * Fixed two possible NULL references in rare cases. + +(STABLE-0_6_6) + + ++ Bug fixes: + + * Fixed DHCP which did not include the IP address in DECLINE messages. + + ++ Changes: + + * etharp.c has been hauled over a bit. + +(STABLE-0_6_5) + + ++ Bug fixes: + + * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic. + * Packets sent from ARP queue had invalid source hardware address. + + ++ Changes: + + * Pass-by ARP requests do now update the cache. + + ++ New features: + + * No longer dependent on ctype.h. + * New socket options. + * Raw IP pcb support. + +(STABLE-0_6_4) + + ++ Bug fixes: + + * Some debug formatters and casts fixed. + * Numereous fixes in PPP. + + ++ Changes: + + * DEBUGF now is LWIP_DEBUGF + * pbuf_dechain() has been re-enabled. + * Mentioned the changed use of CVS branches in README. + +(STABLE-0_6_3) + + ++ Bug fixes: + + * Fixed pool pbuf memory leak in pbuf_alloc(). + Occured if not enough PBUF_POOL pbufs for a packet pbuf chain. + Reported by Savin Zlobec. + + * PBUF_POOL chains had their tot_len field not set for non-first + pbufs. Fixed in pbuf_alloc(). + + ++ New features: + + * Added PPP stack contributed by Marc Boucher + + ++ Changes: + + * Now drops short packets for ICMP/UDP/TCP protocols. More robust. + + * ARP queueuing now queues the latest packet instead of the first. + This is the RFC recommended behaviour, but can be overridden in + lwipopts.h. + +(0.6.2) + + ++ Bugfixes: + + * TCP has been fixed to deal with the new use of the pbuf->ref + counter. + + * DHCP dhcp_inform() crash bug fixed. + + ++ Changes: + + * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed + pbuf_refresh(). This has sped up pbuf pool operations considerably. + Implemented by David Haas. + +(0.6.1) + + ++ New features: + + * The packet buffer implementation has been enhanced to support + zero-copy and copy-on-demand for packet buffers which have their + payloads in application-managed memory. + Implemented by David Haas. + + Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy + if an outgoing packet can be directly sent on the link, or perform + a copy-on-demand when necessary. + + The application can safely assume the packet is sent, and the RAM + is available to the application directly after calling udp_send() + or similar function. + + ++ Bugfixes: + + * ARP_QUEUEING should now correctly work for all cases, including + PBUF_REF. + Implemented by Leon Woestenberg. + + ++ Changes: + + * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer + to a '0.0.0.0' IP address. + + * The packet buffer implementation is changed. The pbuf->ref counter + meaning has changed, and several pbuf functions have been + adapted accordingly. + + * netif drivers have to be changed to set the hardware address length field + that must be initialized correctly by the driver (hint: 6 for Ethernet MAC). + See the contrib/ports/c16x cs8900 driver as a driver example. + + * netif's have a dhcp field that must be initialized to NULL by the driver. + See the contrib/ports/c16x cs8900 driver as a driver example. + +(0.5.x) This file has been unmaintained up to 0.6.1. All changes are + logged in CVS but have not been explained here. + +(0.5.3) Changes since version 0.5.2 + + ++ Bugfixes: + + * memp_malloc(MEMP_API_MSG) could fail with multiple application + threads because it wasn't protected by semaphores. + + ++ Other changes: + + * struct ip_addr now packed. + + * The name of the time variable in arp.c has been changed to ctime + to avoid conflicts with the time() function. + +(0.5.2) Changes since version 0.5.1 + + ++ New features: + + * A new TCP function, tcp_tmr(), now handles both TCP timers. + + ++ Bugfixes: + + * A bug in tcp_parseopt() could cause the stack to hang because of a + malformed TCP option. + + * The address of new connections in the accept() function in the BSD + socket library was not handled correctly. + + * pbuf_dechain() did not update the ->tot_len field of the tail. + + * Aborted TCP connections were not handled correctly in all + situations. + + ++ Other changes: + + * All protocol header structs are now packed. + + * The ->len field in the tcp_seg structure now counts the actual + amount of data, and does not add one for SYN and FIN segments. + +(0.5.1) Changes since version 0.5.0 + + ++ New features: + + * Possible to run as a user process under Linux. + + * Preliminary support for cross platform packed structs. + + * ARP timer now implemented. + + ++ Bugfixes: + + * TCP output queue length was badly initialized when opening + connections. + + * TCP delayed ACKs were not sent correctly. + + * Explicit initialization of BSS segment variables. + + * read() in BSD socket library could drop data. + + * Problems with memory alignment. + + * Situations when all TCP buffers were used could lead to + starvation. + + * TCP MSS option wasn't parsed correctly. + + * Problems with UDP checksum calculation. + + * IP multicast address tests had endianess problems. + + * ARP requests had wrong destination hardware address. + + ++ Other changes: + + * struct eth_addr changed from u16_t[3] array to u8_t[6]. + + * A ->linkoutput() member was added to struct netif. + + * TCP and UDP ->dest_* struct members where changed to ->remote_*. + + * ntoh* macros are now null definitions for big endian CPUs. + +(0.5.0) Changes since version 0.4.2 + + ++ New features: + + * Redesigned operating system emulation layer to make porting easier. + + * Better control over TCP output buffers. + + * Documenation added. + + ++ Bugfixes: + + * Locking issues in buffer management. + + * Bugfixes in the sequential API. + + * IP forwarding could cause memory leakage. This has been fixed. + + ++ Other changes: + + * Directory structure somewhat changed; the core/ tree has been + collapsed. + +(0.4.2) Changes since version 0.4.1 + + ++ New features: + + * Experimental ARP implementation added. + + * Skeleton Ethernet driver added. + + * Experimental BSD socket API library added. + + ++ Bugfixes: + + * In very intense situations, memory leakage could occur. This has + been fixed. + + ++ Other changes: + + * Variables named "data" and "code" have been renamed in order to + avoid name conflicts in certain compilers. + + * Variable++ have in appliciable cases been translated to ++variable + since some compilers generate better code in the latter case. + +(0.4.1) Changes since version 0.4 + + ++ New features: + + * TCP: Connection attempts time out earlier than data + transmissions. Nagle algorithm implemented. Push flag set on the + last segment in a burst. + + * UDP: experimental support for UDP-Lite extensions. + + ++ Bugfixes: + + * TCP: out of order segments were in some cases handled incorrectly, + and this has now been fixed. Delayed acknowledgements was broken + in 0.4, has now been fixed. Binding to an address that is in use + now results in an error. Reset connections sometimes hung an + application; this has been fixed. + + * Checksum calculation sometimes failed for chained pbufs with odd + lengths. This has been fixed. + + * API: a lot of bug fixes in the API. The UDP API has been improved + and tested. Error reporting and handling has been + improved. Logical flaws and race conditions for incoming TCP + connections has been found and removed. + + * Memory manager: alignment issues. Reallocating memory sometimes + failed, this has been fixed. + + * Generic library: bcopy was flawed and has been fixed. + + ++ Other changes: + + * API: all datatypes has been changed from generic ones such as + ints, to specified ones such as u16_t. Functions that return + errors now have the correct type (err_t). + + * General: A lot of code cleaned up and debugging code removed. Many + portability issues have been fixed. + + * The license was changed; the advertising clause was removed. + + * C64 port added. + + * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri + Kosunen, Mikael Caleres, and Frits Wilmink for reporting and + fixing bugs! + +(0.4) Changes since version 0.3.1 + + * Memory management has been radically changed; instead of + allocating memory from a shared heap, memory for objects that are + rapidly allocated and deallocated is now kept in pools. Allocation + and deallocation from those memory pools is very fast. The shared + heap is still present but is used less frequently. + + * The memory, memory pool, and packet buffer subsystems now support + 4-, 2-, or 1-byte alignment. + + * "Out of memory" situations are handled in a more robust way. + + * Stack usage has been reduced. + + * Easier configuration of lwIP parameters such as memory usage, + TTLs, statistics gathering, etc. All configuration parameters are + now kept in a single header file "lwipopts.h". + + * The directory structure has been changed slightly so that all + architecture specific files are kept under the src/arch + hierarchy. + + * Error propagation has been improved, both in the protocol modules + and in the API. + + * The code for the RTXC architecture has been implemented, tested + and put to use. + + * Bugs have been found and corrected in the TCP, UDP, IP, API, and + the Internet checksum modules. + + * Bugs related to porting between a 32-bit and a 16-bit architecture + have been found and corrected. + + * The license has been changed slightly to conform more with the + original BSD license, including the advertisement clause. + +(0.3.1) Changes since version 0.3 + + * Fix of a fatal bug in the buffer management. Pbufs with allocated + RAM never returned the RAM when the pbuf was deallocated. + + * TCP congestion control, window updates and retransmissions did not + work correctly. This has now been fixed. + + * Bugfixes in the API. + +(0.3) Changes since version 0.2 + + * New and improved directory structure. All include files are now + kept in a dedicated include/ directory. + + * The API now has proper error handling. A new function, + netconn_err(), now returns an error code for the connection in + case of errors. + + * Improvements in the memory management subsystem. The system now + keeps a pointer to the lowest free memory block. A new function, + mem_malloc2() tries to allocate memory once, and if it fails tries + to free some memory and retry the allocation. + + * Much testing has been done with limited memory + configurations. lwIP now does a better job when overloaded. + + * Some bugfixes and improvements to the buffer (pbuf) subsystem. + + * Many bugfixes in the TCP code: + + - Fixed a bug in tcp_close(). + + - The TCP receive window was incorrectly closed when out of + sequence segments was received. This has been fixed. + + - Connections are now timed-out of the FIN-WAIT-2 state. + + - The initial congestion window could in some cases be too + large. This has been fixed. + + - The retransmission queue could in some cases be screwed up. This + has been fixed. + + - TCP RST flag now handled correctly. + + - Out of sequence data was in some cases never delivered to the + application. This has been fixed. + + - Retransmitted segments now contain the correct acknowledgment + number and advertised window. + + - TCP retransmission timeout backoffs are not correctly computed + (ala BSD). After a number of retransmissions, TCP now gives up + the connection. + + * TCP connections now are kept on three lists, one for active + connections, one for listening connections, and one for + connections that are in TIME-WAIT. This greatly speeds up the fast + timeout processing for sending delayed ACKs. + + * TCP now provides proper feedback to the application when a + connection has been successfully set up. + + * More comments have been added to the code. The code has also been + somewhat cleaned up. + +(0.2) Initial public release. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/COPYING b/component/common/network/lwip/lwip_v1.5.0.beta/COPYING new file mode 100644 index 0000000..e23898b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/COPYING @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2001, 2002 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 + * + */ + + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/FILES b/component/common/network/lwip/lwip_v1.5.0.beta/FILES new file mode 100644 index 0000000..6625319 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/FILES @@ -0,0 +1,4 @@ +src/ - The source code for the lwIP TCP/IP stack. +doc/ - The documentation for lwIP. + +See also the FILES file in each subdirectory. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/README b/component/common/network/lwip/lwip_v1.5.0.beta/README new file mode 100644 index 0000000..a62cc4f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/README @@ -0,0 +1,89 @@ +INTRODUCTION + +lwIP is a small independent implementation of the TCP/IP protocol +suite that has been developed by Adam Dunkels at the Computer and +Networks Architectures (CNA) lab at the Swedish Institute of Computer +Science (SICS). + +The focus of the lwIP TCP/IP implementation is to reduce the RAM usage +while still having a full scale TCP. This making lwIP suitable for use +in embedded systems with tens of kilobytes of free RAM and room for +around 40 kilobytes of code ROM. + +FEATURES + + * IP (Internet Protocol) including packet forwarding over multiple network + interfaces + * ICMP (Internet Control Message Protocol) for network maintenance and debugging + * IGMP (Internet Group Management Protocol) for multicast traffic management + * UDP (User Datagram Protocol) including experimental UDP-lite extensions + * TCP (Transmission Control Protocol) with congestion control, RTT estimation + and fast recovery/fast retransmit + * Specialized raw/native API for enhanced performance + * Optional Berkeley-like socket API + * DNS (Domain names resolver) + * SNMP (Simple Network Management Protocol) + * DHCP (Dynamic Host Configuration Protocol) + * AUTOIP (for IPv4, conform with RFC 3927) + * PPP (Point-to-Point Protocol) + * ARP (Address Resolution Protocol) for Ethernet + +LICENSE + +lwIP is freely available under a BSD license. + +DEVELOPMENT + +lwIP has grown into an excellent TCP/IP stack for embedded devices, +and developers using the stack often submit bug fixes, improvements, +and additions to the stack to further increase its usefulness. + +Development of lwIP is hosted on Savannah, a central point for +software development, maintenance and distribution. Everyone can +help improve lwIP by use of Savannah's interface, CVS and the +mailing list. A core team of developers will commit changes to the +CVS source tree. + +The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and +contributions (such as platform ports) are in the 'contrib' module. + +See doc/savannah.txt for details on CVS server access for users and +developers. + +Last night's CVS tar ball can be downloaded from: + http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING] + +The current CVS trees are web-browsable: + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/ + http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/ + +Submit patches and bugs via the lwIP project page: + http://savannah.nongnu.org/projects/lwip/ + + +DOCUMENTATION + +The original out-dated homepage of lwIP and Adam Dunkels' papers on +lwIP are at the official lwIP home page: + http://www.sics.se/~adam/lwip/ + +Self documentation of the source code is regularly extracted from the +current CVS sources and is available from this web page: + http://www.nongnu.org/lwip/ + +There is now a constantly growin wiki about lwIP at + http://lwip.wikia.com/wiki/LwIP_Wiki + +Also, there are mailing lists you can subscribe at + http://savannah.nongnu.org/mail/?group=lwip +plus searchable archives: + http://lists.nongnu.org/archive/html/lwip-users/ + http://lists.nongnu.org/archive/html/lwip-devel/ + +Reading Adam's papers, the files in docs/, browsing the source code +documentation and browsing the mailing list archives is a good way to +become familiar with the design of lwIP. + +Adam Dunkels +Leon Woestenberg + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/UPGRADING b/component/common/network/lwip/lwip_v1.5.0.beta/UPGRADING new file mode 100644 index 0000000..6501107 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/UPGRADING @@ -0,0 +1,144 @@ +This file lists major changes between release versions that require +ports or applications to be changed. Use it to update a port or an +application written for an older version of lwIP to correctly work +with newer versions. + + +(CVS HEAD) + + * [Enter new changes just after this line - do not remove this line] + + ++ Application changes: + + * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for + compatibility to old applications, but will be removed in the future). + + * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc() + + +++ Raw API: + * Changed the semantics of tcp_close() (since it was rather a + shutdown before): Now the application does *NOT* get any calls to the recv + callback (aside from NULL/closed) after calling tcp_close() + + * When calling tcp_abort() from a raw API TCP callback function, + make sure you return ERR_ABRT to prevent accessing unallocated memory. + (ERR_ABRT now means the applicaiton has called tcp_abort!) + + +++ Netconn API: + * Changed netconn_receive() and netconn_accept() to return + err_t, not a pointer to new data/netconn. + + +++ Socket API: + * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they + now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT. + + * Added a minimal version of posix fctl() to have a + standardised way to set O_NONBLOCK for nonblocking sockets. + + +++ all APIs: + * correctly implemented SO(F)_REUSEADDR + + ++ Port changes + + +++ new files: + + * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h: + + * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains + the actual application programmer's API + + * Separated timer implementation from sys.h/.c, moved to timers.h/.c; + Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you + still want to use your own timer implementation for NO_SYS==0 (as before). + + +++ sys layer: + + * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/ + sys_sem_t; + + * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t; + + * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use + binary semaphores instead of mutexes - as before) + + +++ new options: + + * Don't waste memory when chaining segments, added option TCP_OVERSIZE to + prevent creating many small pbufs when calling tcp_write with many small + blocks of data. Instead, pbufs are allocated larger than needed and the + space is used for later calls to tcp_write. + + * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs + in tcp_write/udp_send. + + * Added an additional option LWIP_ETHERNET to support ethernet without ARP + (necessary for pure PPPoE) + + * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may + be used to place these pools into user-defined memory by using external + declaration. + + * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT + + +++ new pools: + + * Netdb uses a memp pool for allocating memory when getaddrinfo() is called, + so MEMP_NUM_NETDB has to be set accordingly. + + * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so + MEMP_NUM_LOCALHOSTLIST has to be set accordingly. + + * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have + to be set accordingly. + + * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES + has to be set accordingly + + * Integrated loopif into netif.c - loopif does not have to be created by the + port any more, just define LWIP_HAVE_LOOPIF to 1. + + * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined + in cc.h, e.g. used by igmp) + + * Added printf-formatter X8_F to printf u8_t as hex + + * The heap now may be moved to user-defined memory by defining + LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address + + * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work + with user-allocated structs instead of calling mem_malloc + + * Added const char* name to mem- and memp-stats for easier debugging. + + * Calculate the TCP/UDP checksum while copying to only fetch data once: + Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum + + * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to + more than one pcb. + + * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned + off any more, if this is set to 0, only one packet (the most recent one) is + queued (like demanded by RFC 1122). + + + ++ Major bugfixes/improvements + + * Implemented tcp_shutdown() to only shut down one end of a connection + * Implemented shutdown() at socket- and netconn-level + * Added errorset support to select() + improved select speed overhead + * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x) + * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1 + * Use macros defined in ip_addr.h to work with IP addresses + * Implemented many nonblocking socket/netconn functions + * Fixed ARP input processing: only add a new entry if a request was directed as us + * mem_realloc() to mem_trim() to prevent confusion with realloc() + * Some improvements for AutoIP (don't route/forward link-local addresses, don't break + existing connections when assigning a routable address) + * Correctly handle remote side overrunning our rcv_wnd in ooseq case + * Removed packing from ip_addr_t, the packed version is now only used in protocol headers + * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0 + * Added support for static ARP table entries + +(STABLE-1.3.2) + + * initial version of this file diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/FILES b/component/common/network/lwip/lwip_v1.5.0.beta/doc/FILES new file mode 100644 index 0000000..05d356f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/FILES @@ -0,0 +1,6 @@ +savannah.txt - How to obtain the current development source code. +contrib.txt - How to contribute to lwIP as a developer. +rawapi.txt - The documentation for the core API of lwIP. + Also provides an overview about the other APIs and multithreading. +snmp_agent.txt - The documentation for the lwIP SNMP agent. +sys_arch.txt - The documentation for a system abstraction layer of lwIP. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/contrib.txt b/component/common/network/lwip/lwip_v1.5.0.beta/doc/contrib.txt new file mode 100644 index 0000000..b986020 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/contrib.txt @@ -0,0 +1,59 @@ +1 Introduction + +This document describes some guidelines for people participating +in lwIP development. + +2 How to contribute to lwIP + +Here is a short list of suggestions to anybody working with lwIP and +trying to contribute bug reports, fixes, enhancements, platform ports etc. +First of all as you may already know lwIP is a volunteer project so feedback +to fixes or questions might often come late. Hopefully the bug and patch tracking +features of Savannah help us not lose users' input. + +2.1 Source code style: + +1. do not use tabs. +2. indentation is two spaces per level (i.e. per tab). +3. end debug messages with a trailing newline (\n). +4. one space between keyword and opening bracket. +5. no space between function and opening bracket. +6. one space and no newline before opening curly braces of a block. +7. closing curly brace on a single line. +8. spaces surrounding assignment and comparisons. +9. don't initialize static and/or global variables to zero, the compiler takes care of that. +10. use current source code style as further reference. + +2.2 Source code documentation style: + +1. JavaDoc compliant and Doxygen compatible. +2. Function documentation above functions in .c files, not .h files. + (This forces you to synchronize documentation and implementation.) +3. Use current documentation style as further reference. + +2.3 Bug reports and patches: + +1. Make sure you are reporting bugs or send patches against the latest + sources. (From the latest release and/or the current CVS sources.) +2. If you think you found a bug make sure it's not already filed in the + bugtracker at Savannah. +3. If you have a fix put the patch on Savannah. If it is a patch that affects + both core and arch specific stuff please separate them so that the core can + be applied separately while leaving the other patch 'open'. The prefered way + is to NOT touch archs you can't test and let maintainers take care of them. + This is a good way to see if they are used at all - the same goes for unix + netifs except tapif. +4. Do not file a bug and post a fix to it to the patch area. Either a bug report + or a patch will be enough. + If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area. +5. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other + trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you + change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than + if it's not to the point and long :) so the chances for it to be applied are greater. + +2.4 Platform porters: + +1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and + you think it could benefit others[1] you might want discuss this on the mailing list. You + can also ask for CVS access to submit and maintain your port in the contrib CVS module. + \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/rawapi.txt b/component/common/network/lwip/lwip_v1.5.0.beta/doc/rawapi.txt new file mode 100644 index 0000000..8c19030 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/rawapi.txt @@ -0,0 +1,511 @@ +Raw TCP/IP interface for lwIP + +Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons + +lwIP provides three Application Program's Interfaces (APIs) for programs +to use for communication with the TCP/IP code: +* low-level "core" / "callback" or "raw" API. +* higher-level "sequential" API. +* BSD-style socket API. + +The sequential API provides a way for ordinary, sequential, programs +to use the lwIP stack. It is quite similar to the BSD socket API. The +model of execution is based on the blocking open-read-write-close +paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP +code and the application program must reside in different execution +contexts (threads). + +The socket API is a compatibility API for existing applications, +currently it is built on top of the sequential API. It is meant to +provide all functions needed to run socket API applications running +on other platforms (e.g. unix / windows etc.). However, due to limitations +in the specification of this API, there might be incompatibilities +that require small modifications of existing programs. + +** Threading + +lwIP started targeting single-threaded environments. When adding multi- +threading support, instead of making the core thread-safe, another +approach was chosen: there is one main thread running the lwIP core +(also known as the "tcpip_thread"). The raw API may only be used from +this thread! Application threads using the sequential- or socket API +communicate with this main thread through message passing. + + As such, the list of functions that may be called from + other threads or an ISR is very limited! Only functions + from these API header files are thread-safe: + - api.h + - netbuf.h + - netdb.h + - netifapi.h + - sockets.h + - sys.h + + Additionaly, memory (de-)allocation functions may be + called from multiple threads (not ISR!) with NO_SYS=0 + since they are protected by SYS_LIGHTWEIGHT_PROT and/or + semaphores. + + Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1 + and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1, + pbuf_free() may also be called from another thread or + an ISR (since only then, mem_free - for PBUF_RAM - may + be called from an ISR: otherwise, the HEAP is only + protected by semaphores). + + +** The remainder of this document discusses the "raw" API. ** + +The raw TCP/IP interface allows the application program to integrate +better with the TCP/IP code. Program execution is event based by +having callback functions being called from within the TCP/IP +code. The TCP/IP code and the application program both run in the same +thread. The sequential API has a much higher overhead and is not very +well suited for small systems since it forces a multithreaded paradigm +on the application. + +The raw TCP/IP interface is not only faster in terms of code execution +time but is also less memory intensive. The drawback is that program +development is somewhat harder and application programs written for +the raw TCP/IP interface are more difficult to understand. Still, this +is the preferred way of writing applications that should be small in +code size and memory usage. + +Both APIs can be used simultaneously by different application +programs. In fact, the sequential API is implemented as an application +program using the raw TCP/IP interface. + +--- Callbacks + +Program execution is driven by callbacks. Each callback is an ordinary +C function that is called from within the TCP/IP code. Every callback +function is passed the current TCP or UDP connection state as an +argument. Also, in order to be able to keep program specific state, +the callback functions are called with a program specified argument +that is independent of the TCP/IP state. + +The function for setting the application connection state is: + +- void tcp_arg(struct tcp_pcb *pcb, void *arg) + + Specifies the program specific state that should be passed to all + other callback functions. The "pcb" argument is the current TCP + connection control block, and the "arg" argument is the argument + that will be passed to the callbacks. + + +--- TCP connection setup + +The functions used for setting up connections is similar to that of +the sequential API and of the BSD socket API. A new TCP connection +identifier (i.e., a protocol control block - PCB) is created with the +tcp_new() function. This PCB can then be either set to listen for new +incoming connections or be explicitly connected to another host. + +- struct tcp_pcb *tcp_new(void) + + Creates a new connection identifier (PCB). If memory is not + available for creating the new pcb, NULL is returned. + +- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local IP address and port number. The IP address + can be specified as IP_ADDR_ANY in order to bind the connection to + all local IP addresses. + + If another connection is bound to the same port, the function will + return ERR_USE, otherwise ERR_OK is returned. + +- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb) + + Commands a pcb to start listening for incoming connections. When an + incoming connection is accepted, the function specified with the + tcp_accept() function will be called. The pcb will have to be bound + to a local port with the tcp_bind() function. + + The tcp_listen() function returns a new connection identifier, and + the one passed as an argument to the function will be + deallocated. The reason for this behavior is that less memory is + needed for a connection that is listening, so tcp_listen() will + reclaim the memory needed for the original connection and allocate a + new smaller memory block for the listening connection. + + tcp_listen() may return NULL if no memory was available for the + listening connection. If so, the memory associated with the pcb + passed as an argument to tcp_listen() will not be deallocated. + +- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog) + + Same as tcp_listen, but limits the number of outstanding connections + in the listen queue to the value specified by the backlog argument. + To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h. + +- void tcp_accepted(struct tcp_pcb *pcb) + + Inform lwIP that an incoming connection has been accepted. This would + usually be called from the accept callback. This allows lwIP to perform + housekeeping tasks, such as allowing further incoming connections to be + queued in the listen backlog. + ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed + into the accept callback! + +- void tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, + err_t err)) + + Specified the callback function that should be called when a new + connection arrives on a listening connection. + +- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, err_t (* connected)(void *arg, + struct tcp_pcb *tpcb, + err_t err)); + + Sets up the pcb to connect to the remote host and sends the + initial SYN segment which opens the connection. + + The tcp_connect() function returns immediately; it does not wait for + the connection to be properly setup. Instead, it will call the + function specified as the fourth argument (the "connected" argument) + when the connection is established. If the connection could not be + properly established, either because the other host refused the + connection or because the other host didn't answer, the "err" + callback function of this pcb (registered with tcp_err, see below) + will be called. + + The tcp_connect() function can return ERR_MEM if no memory is + available for enqueueing the SYN segment. If the SYN indeed was + enqueued successfully, the tcp_connect() function returns ERR_OK. + + +--- Sending TCP data + +TCP data is sent by enqueueing the data with a call to +tcp_write(). When the data is successfully transmitted to the remote +host, the application will be notified with a call to a specified +callback function. + +- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len, + u8_t apiflags) + + Enqueues the data pointed to by the argument dataptr. The length of + the data is passed as the len parameter. The apiflags can be one or more of: + - TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated + for the data to be copied into. If this flag is not given, no new memory + should be allocated and the data should only be referenced by pointer. This + also means that the memory behind dataptr must not change until the data is + ACKed by the remote host + - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given, + the PSH flag is set in the last segment created by this call to tcp_write. + If this flag is given, the PSH flag is not set. + + The tcp_write() function will fail and return ERR_MEM if the length + of the data exceeds the current send buffer size or if the length of + the queue of outgoing segment is larger than the upper limit defined + in lwipopts.h. The number of bytes available in the output queue can + be retrieved with the tcp_sndbuf() function. + + The proper way to use this function is to call the function with at + most tcp_sndbuf() bytes of data. If the function returns ERR_MEM, + the application should wait until some of the currently enqueued + data has been successfully received by the other host and try again. + +- void tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, + u16_t len)) + + Specifies the callback function that should be called when data has + successfully been received (i.e., acknowledged) by the remote + host. The len argument passed to the callback function gives the + amount bytes that was acknowledged by the last acknowledgment. + + +--- Receiving TCP data + +TCP data reception is callback based - an application specified +callback function is called when new data arrives. When the +application has taken the data, it has to call the tcp_recved() +function to indicate that TCP can advertise increase the receive +window. + +- void tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, + struct pbuf *p, err_t err)) + + Sets the callback function that will be called when new data + arrives. The callback function will be passed a NULL pbuf to + indicate that the remote host has closed the connection. If + there are no errors and the callback function is to return + ERR_OK, then it must free the pbuf. Otherwise, it must not + free the pbuf so that lwIP core code can store it. + +- void tcp_recved(struct tcp_pcb *pcb, u16_t len) + + Must be called when the application has received the data. The len + argument indicates the length of the received data. + + +--- Application polling + +When a connection is idle (i.e., no data is either transmitted or +received), lwIP will repeatedly poll the application by calling a +specified callback function. This can be used either as a watchdog +timer for killing connections that have stayed idle for too long, or +as a method of waiting for memory to become available. For instance, +if a call to tcp_write() has failed because memory wasn't available, +the application may use the polling functionality to call tcp_write() +again when the connection has been idle for a while. + +- void tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), + u8_t interval) + + Specifies the polling interval and the callback function that should + be called to poll the application. The interval is specified in + number of TCP coarse grained timer shots, which typically occurs + twice a second. An interval of 10 means that the application would + be polled every 5 seconds. + + +--- Closing and aborting connections + +- err_t tcp_close(struct tcp_pcb *pcb) + + Closes the connection. The function may return ERR_MEM if no memory + was available for closing the connection. If so, the application + should wait and try again either by using the acknowledgment + callback or the polling functionality. If the close succeeds, the + function returns ERR_OK. + + The pcb is deallocated by the TCP code after a call to tcp_close(). + +- void tcp_abort(struct 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! + + +If a connection is aborted because of an error, the application is +alerted of this event by the err callback. Errors that might abort a +connection are when there is a shortage of memory. The callback +function to be called is set using the tcp_err() function. + +- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg, + err_t err)) + + The error callback function does not get the pcb passed to it as a + parameter since the pcb may already have been deallocated. + + +--- Lower layer TCP interface + +TCP provides a simple interface to the lower layers of the +system. During system initialization, the function tcp_init() has +to be called before any other TCP function is called. When the system +is running, the two timer functions tcp_fasttmr() and tcp_slowtmr() +must be called with regular intervals. The tcp_fasttmr() should be +called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and +tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds. + + +--- UDP interface + +The UDP interface is similar to that of TCP, but due to the lower +level of complexity of UDP, the interface is significantly simpler. + +- struct udp_pcb *udp_new(void) + + Creates a new UDP pcb which can be used for UDP communication. The + pcb is not active until it has either been bound to a local address + or connected to a remote address. + +- void udp_remove(struct udp_pcb *pcb) + + Removes and deallocates the pcb. + +- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Binds the pcb to a local address. The IP-address argument "ipaddr" + can be IP_ADDR_ANY to indicate that it should listen to any local IP + address. The function currently always return ERR_OK. + +- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port) + + Sets the remote end of the pcb. This function does not generate any + network traffic, but only set the remote address of the pcb. + +- err_t udp_disconnect(struct udp_pcb *pcb) + + Remove the remote end of the pcb. This function does not generate + any network traffic, but only removes the remote address of the pcb. + +- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) + + Sends the pbuf p. The pbuf is not deallocated. + +- void udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, + struct pbuf *p, + ip_addr_t *addr, + u16_t port), + void *recv_arg) + + Specifies a callback function that should be called when a UDP + datagram is received. + + +--- System initalization + +A truly complete and generic sequence for initializing the lwip stack +cannot be given because it depends on the build configuration (lwipopts.h) +and additional initializations for your runtime environment (e.g. timers). + +We can give you some idea on how to proceed when using the raw API. +We assume a configuration using a single Ethernet netif and the +UDP and TCP transport layers, IPv4 and the DHCP client. + +Call these functions in the order of appearance: + +- stats_init() + + Clears the structure where runtime statistics are gathered. + +- sys_init() + + Not of much use since we set the NO_SYS 1 option in lwipopts.h, + to be called for easy configuration changes. + +- mem_init() + + Initializes the dynamic memory heap defined by MEM_SIZE. + +- memp_init() + + Initializes the memory pools defined by MEMP_NUM_x. + +- pbuf_init() + + Initializes the pbuf memory pool defined by PBUF_POOL_SIZE. + +- etharp_init() + + Initializes the ARP table and queue. + Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval + after this initialization. + +- ip_init() + + Doesn't do much, it should be called to handle future changes. + +- udp_init() + + Clears the UDP PCB list. + +- tcp_init() + + Clears the TCP PCB list and clears some internal TCP timers. + Note: you must call tcp_fasttmr() and tcp_slowtmr() at the + predefined regular intervals after this initialization. + +- netif_add(struct netif *netif, ip_addr_t *ipaddr, + ip_addr_t *netmask, ip_addr_t *gw, + void *state, err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) + + Adds your network interface to the netif_list. Allocate a struct + netif and pass a pointer to this structure as the first argument. + Give pointers to cleared ip_addr structures when using DHCP, + or fill them with sane numbers otherwise. The state pointer may be NULL. + + The init function pointer must point to a initialization function for + your ethernet netif interface. The following code illustrates it's use. + + err_t netif_if_init(struct netif *netif) + { + u8_t i; + + for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i]; + init_my_eth_device(); + return ERR_OK; + } + + For ethernet drivers, the input function pointer must point to the lwip + function ethernet_input() declared in "netif/etharp.h". Other drivers + must use ip_input() declared in "lwip/ip.h". + +- netif_set_default(struct netif *netif) + + Registers the default network interface. + +- netif_set_up(struct netif *netif) + + When the netif is fully configured this function must be called. + +- dhcp_start(struct netif *netif) + + Creates a new DHCP client for this interface on the first call. + Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at + the predefined regular intervals after starting the client. + + You can peek in the netif->dhcp struct for the actual DHCP status. + + +--- Optimalization hints + +The first thing you want to optimize is the lwip_standard_checksum() +routine from src/core/inet.c. You can override this standard +function with the #define LWIP_CHKSUM . + +There are C examples given in inet.c or you might want to +craft an assembly function for this. RFC1071 is a good +introduction to this subject. + +Other significant improvements can be made by supplying +assembly or inline replacements for htons() and htonl() +if you're using a little-endian architecture. +#define LWIP_PLATFORM_BYTESWAP 1 +#define LWIP_PLATFORM_HTONS(x) +#define LWIP_PLATFORM_HTONL(x) + +Check your network interface driver if it reads at +a higher speed than the maximum wire-speed. If the +hardware isn't serviced frequently and fast enough +buffer overflows are likely to occur. + +E.g. when using the cs8900 driver, call cs8900if_service(ethif) +as frequently as possible. When using an RTOS let the cs8900 interrupt +wake a high priority task that services your driver using a binary +semaphore or event flag. Some drivers might allow additional tuning +to match your application and network. + +For a production release it is recommended to set LWIP_STATS to 0. +Note that speed performance isn't influenced much by simply setting +high values to the memory options. + +For more optimization hints take a look at the lwIP wiki. + +--- Zero-copy MACs + +To achieve zero-copy on transmit, the data passed to the raw API must +remain unchanged until sent. Because the send- (or write-)functions return +when the packets have been enqueued for sending, data must be kept stable +after that, too. + +This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions +must *not* be reused by the application unless their ref-count is 1. + +For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too, +but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while +PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change). + +Also, data passed to tcp_write without the copy-flag must not be changed! + +Therefore, be careful which type of PBUF you use and if you copy TCP data +or not! diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/savannah.txt b/component/common/network/lwip/lwip_v1.5.0.beta/doc/savannah.txt new file mode 100644 index 0000000..409905b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/savannah.txt @@ -0,0 +1,135 @@ +Daily Use Guide for using Savannah for lwIP + +Table of Contents: + +1 - Obtaining lwIP from the CVS repository +2 - Committers/developers CVS access using SSH (to be written) +3 - Merging from DEVEL branch to main trunk (stable branch) +4 - How to release lwIP + + + +1 Obtaining lwIP from the CVS repository +---------------------------------------- + +To perform an anonymous CVS checkout of the main trunk (this is where +bug fixes and incremental enhancements occur), do this: + +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip + +Or, obtain a stable branch (updated with bug fixes only) as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7 -d lwip-0.7 lwip + +Or, obtain a specific (fixed) release as follows: +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_7_0 -d lwip-0.7.0 lwip + +3 Committers/developers CVS access using SSH +-------------------------------------------- + +The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption. +As such, CVS commits to the server occur through a SSH tunnel for project members. +To create a SSH2 key pair in UNIX-like environments, do this: + +ssh-keygen -t dsa + +Under Windows, a recommended SSH client is "PuTTY", freely available with good +documentation and a graphic user interface. Use its key generator. + +Now paste the id_dsa.pub contents into your Savannah account public key list. Wait +a while so that Savannah can update its configuration (This can take minutes). + +Try to login using SSH: + +ssh -v your_login@cvs.sv.gnu.org + +If it tells you: + +Authenticating with public key "your_key_name"... +Server refused to allocate pty + +then you could login; Savannah refuses to give you a shell - which is OK, as we +are allowed to use SSH for CVS only. Now, you should be able to do this: + +export CVS_RSH=ssh +cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip + +after which you can edit your local files with bug fixes or new features and +commit them. Make sure you know what you are doing when using CVS to make +changes on the repository. If in doubt, ask on the lwip-members mailing list. + +(If SSH asks about authenticity of the host, you can check the key + fingerprint against http://savannah.nongnu.org/cvs/?group=lwip) + + +3 Merging from DEVEL branch to main trunk (stable) +-------------------------------------------------- + +Merging is a delicate process in CVS and requires the +following disciplined steps in order to prevent conflicts +in the future. Conflicts can be hard to solve! + +Merging from branch A to branch B requires that the A branch +has a tag indicating the previous merger. This tag is called +'merged_from_A_to_B'. After merging, the tag is moved in the +A branch to remember this merger for future merge actions. + +IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE +REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE +MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME). + +Merge all changes in DEVEL since our last merge to main: + +In the working copy of the main trunk: +cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL + +(This will apply the changes between 'merged_from_DEVEL_to_main' +and 'DEVEL' to your work set of files) + +We can now commit the merge result. +cvs commit -R -m "Merged from DEVEL to main." + +If this worked out OK, we now move the tag in the DEVEL branch +to this merge point, so we can use this point for future merges: + +cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip + +4 How to release lwIP +--------------------- + +First, checkout a clean copy of the branch to be released. Tag this set with +tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example). + +Login CVS using pserver authentication, then export a clean copy of the +tagged tree. Export is similar to a checkout, except that the CVS metadata +is not created locally. + +export CVS_RSH=ssh +cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \ + -r STABLE-0_6_3 -d lwip-0.6.3 lwip + +Archive this directory using tar, gzip'd, bzip2'd and zip'd. + +tar czvf lwip-0.6.3.tar.gz lwip-0.6.3 +tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3 +zip -r lwip-0.6.3.zip lwip-0.6.3 + +Now, sign the archives with a detached GPG binary signature as follows: + +gpg -b lwip-0.6.3.tar.gz +gpg -b lwip-0.6.3.tar.bz2 +gpg -b lwip-0.6.3.zip + +Upload these files using anonymous FTP: +ncftp ftp://savannah.gnu.org/incoming/savannah/lwip + +ncftp>mput *0.6.3.* + +Additionally, you may post a news item on Savannah, like this: + +A new 0.6.3 release is now available here: +http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3 + +You will have to submit this via the user News interface, then approve +this via the Administrator News interface. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/snmp_agent.txt b/component/common/network/lwip/lwip_v1.5.0.beta/doc/snmp_agent.txt new file mode 100644 index 0000000..2653230 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/snmp_agent.txt @@ -0,0 +1,181 @@ +SNMPv1 agent for lwIP + +Author: Christiaan Simons + +This is a brief introduction how to use and configure the SNMP agent. +Note the agent uses the raw-API UDP interface so you may also want to +read rawapi.txt to gain a better understanding of the SNMP message handling. + +0 Agent Capabilities +==================== + +SNMPv1 per RFC1157 + This is an old(er) standard but is still widely supported. + For SNMPv2c and v3 have a greater complexity and need many + more lines of code. IMHO this breaks the idea of "lightweight IP". + + Note the S in SNMP stands for "Simple". Note that "Simple" is + relative. SNMP is simple compared to the complex ISO network + management protocols CMIP (Common Management Information Protocol) + and CMOT (CMip Over Tcp). + +MIB II per RFC1213 + The standard lwIP stack management information base. + This is a required MIB, so this is always enabled. + When builing lwIP without TCP, the mib-2.tcp group is omitted. + The groups EGP, CMOT and transmission are disabled by default. + + Most mib-2 objects are not writable except: + sysName, sysLocation, sysContact, snmpEnableAuthenTraps. + Writing to or changing the ARP and IP address and route + tables is not possible. + + Note lwIP has a very limited notion of IP routing. It currently + doen't have a route table and doesn't have a notion of the U,G,H flags. + Instead lwIP uses the interface list with only one default interface + acting as a single gateway interface (G) for the default route. + + The agent returns a "virtual table" with the default route 0.0.0.0 + for the default interface and network routes (no H) for each + network interface in the netif_list. + All routes are considered to be up (U). + +Loading additional MIBs + MIBs can only be added in compile-time, not in run-time. + There is no MIB compiler thus additional MIBs must be hand coded. + +Large SNMP message support + The packet decoding and encoding routines are designed + to use pbuf-chains. Larger payloads than the minimum + SNMP requirement of 484 octets are supported if the + PBUF_POOL_SIZE and IP_REASS_BUFSIZE are set to match your + local requirement. + +1 Building the Agent +==================== + +First of all you'll need to add the following define +to your local lwipopts.h: + +#define LWIP_SNMP 1 + +and add the source files in lwip/src/core/snmp +and some snmp headers in lwip/src/include/lwip to your makefile. + +Note you'll might need to adapt you network driver to update +the mib2 variables for your interface. + +2 Running the Agent +=================== + +The following function calls must be made in your program to +actually get the SNMP agent running. + +Before starting the agent you should supply pointers +to non-volatile memory for sysContact, sysLocation, +and snmpEnableAuthenTraps. You can do this by calling + +snmp_set_syscontact() +snmp_set_syslocation() +snmp_set_snmpenableauthentraps() + +Additionally you may want to set + +snmp_set_sysdescr() +snmp_set_sysobjid() (if you have a private MIB) +snmp_set_sysname() + +Also before starting the agent you need to setup +one or more trap destinations using these calls: + +snmp_trap_dst_enable(); +snmp_trap_dst_ip_set(); + +In the lwIP initialisation sequence call snmp_init() just after +the call to udp_init(). + +Exactly every 10 msec the SNMP uptime timestamp must be updated with +snmp_inc_sysuptime(). You should call this from a timer interrupt +or a timer signal handler depending on your runtime environment. + +An alternative way to update the SNMP uptime timestamp is to do a call like +snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but call to +a lower frequency). Another one is to not call snmp_inc_sysuptime() or +snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro. +This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside +snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only +when it's queried (any function which need "sysuptime" have to call +snmp_get_sysuptime). + + +3 Private MIBs +============== + +If want to extend the agent with your own private MIB you'll need to +add the following define to your local lwipopts.h: + +#define SNMP_PRIVATE_MIB 1 + +You must provide the private_mib.h and associated files yourself. +Note we don't have a "MIB compiler" that generates C source from a MIB, +so you're required to do some serious coding if you enable this! + +Note the lwIP enterprise ID (26381) is assigned to the lwIP project, +ALL OBJECT IDENTIFIERS LIVING UNDER THIS ID ARE ASSIGNED BY THE lwIP +MAINTAINERS! + +If you need to create your own private MIB you'll need +to apply for your own enterprise ID with IANA: http://www.iana.org/numbers.html + +You can set it by passing a struct snmp_obj_id to the agent +using snmp_set_sysobjid(&my_object_id), just before snmp_init(). + +Note the object identifiers for thes MIB-2 and your private MIB +tree must be kept in sorted ascending (lexicographical) order. +This to ensure correct getnext operation. + +An example for a private MIB is part of the "minimal Unix" project: +contrib/ports/unix/proj/minimal/lwip_prvmib.c + +The next chapter gives a more detailed description of the +MIB-2 tree and the optional private MIB. + +4 The Gory Details +================== + +4.0 Object identifiers and the MIB tree. + +We have three distinct parts for all object identifiers: + +The prefix + .iso.org.dod.internet + +the middle part + .mgmt.mib-2.ip.ipNetToMediaTable.ipNetToMediaEntry.ipNetToMediaPhysAddress + +and the index part + .1.192.168.0.1 + +Objects located above the .internet hierarchy aren't supported. +Currently only the .mgmt sub-tree is available and +when the SNMP_PRIVATE_MIB is enabled the .private tree +becomes available too. + +Object identifiers from incoming requests are checked +for a matching prefix, middle part and index part +or are expanded(*) for GetNext requests with short +or inexisting names in the request. +(* we call this "expansion" but this also +resembles the "auto-completion" operation) + +The middle part is usually located in ROM (const) +to preserve precious RAM on small microcontrollers. +However RAM location is possible for a dynamically +changing private tree. + +The index part is handled by functions which in +turn use dynamically allocated index trees from RAM. +These trees are updated by e.g. the etharp code +when new entries are made or removed form the ARP cache. + +/** @todo more gory details */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/doc/sys_arch.txt b/component/common/network/lwip/lwip_v1.5.0.beta/doc/sys_arch.txt new file mode 100644 index 0000000..847cd77 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/doc/sys_arch.txt @@ -0,0 +1,267 @@ +sys_arch interface for lwIP 0.6++ + +Author: Adam Dunkels + +The operating system emulation layer provides a common interface +between the lwIP code and the underlying operating system kernel. The +general idea is that porting lwIP to new architectures requires only +small changes to a few header files and a new sys_arch +implementation. It is also possible to do a sys_arch implementation +that does not rely on any underlying operating system. + +The sys_arch provides semaphores and mailboxes to lwIP. For the full +lwIP functionality, multiple threads support can be implemented in the +sys_arch, but this is not required for the basic lwIP +functionality. Previous versions of lwIP required the sys_arch to +implement timer scheduling as well but as of lwIP 0.5 this is +implemented in a higher layer. + +In addition to the source file providing the functionality of sys_arch, +the OS emulation layer must provide several header files defining +macros used throughout lwip. The files required and the macros they +must define are listed below the sys_arch description. + +Semaphores can be either counting or binary - lwIP works with both +kinds. Mailboxes are used for message passing and can be implemented +either as a queue which allows multiple messages to be posted to a +mailbox, or as a rendez-vous point where only one message can be +posted at a time. lwIP works with both kinds, but the former type will +be more efficient. A message in a mailbox is just a pointer, nothing +more. + +Semaphores are represented by the type "sys_sem_t" which is typedef'd +in the sys_arch.h file. Mailboxes are equivalently represented by the +type "sys_mbox_t". lwIP does not place any restrictions on how +sys_sem_t or sys_mbox_t are represented internally. + +Since lwIP 1.4.0, semaphore and mailbox functions are prototyped in a way that +allows both using pointers or actual OS structures to be used. This way, memory +required for such types can be either allocated in place (globally or on the +stack) or on the heap (allocated internally in the "*_new()" functions). + +The following functions must be implemented by the sys_arch: + +- void sys_init(void) + + Is called to initialize the sys_arch layer. + +- err_t sys_sem_new(sys_sem_t *sem, u8_t count) + + Creates a new semaphore. The semaphore is allocated to the memory that 'sem' + points to (which can be both a pointer or the actual OS structure). + The "count" argument specifies the initial state of the semaphore (which is + either 0 or 1). + If the semaphore has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_sem_free(sys_sem_t *sem) + + Deallocates a semaphore. + +- void sys_sem_signal(sys_sem_t *sem) + + Signals a semaphore. + +- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) + + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). If the "timeout" argument is zero, the thread should be + blocked until the semaphore is signalled. + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. + +- int sys_sem_valid(sys_sem_t *sem) + + Returns 1 if the semaphore is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_sem_set_invalid(sys_sem_t *sem) + + Invalidate a semaphore so that sys_sem_valid() returns 0. + ATTENTION: This does NOT mean that the semaphore shall be deallocated: + sys_sem_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +- err_t sys_mbox_new(sys_mbox_t *mbox, int size) + + Creates an empty mailbox for maximum "size" elements. Elements stored + in mailboxes are pointers. You have to define macros "_MBOX_SIZE" + in your lwipopts.h, or ignore this parameter in your implementation + and use a default size. + If the mailbox has been created, ERR_OK should be returned. Returning any + other error will provide a hint what went wrong, but except for assertions, + no real error handling is implemented. + +- void sys_mbox_free(sys_mbox_t *mbox) + + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. + +- void sys_mbox_post(sys_mbox_t *mbox, void *msg) + + Posts the "msg" to the mailbox. This function have to block until + the "msg" is really posted. + +- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) + + Try to post the "msg" to the mailbox. Returns ERR_MEM if this one + is full, else, ERR_OK if the "msg" is posted. + +- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) + + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). If "timeout" is 0, the thread should + be blocked until a message arrives. The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. + +- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) + + This is similar to sys_arch_mbox_fetch, however if a message is not + present in the mailbox, it immediately returns with the code + SYS_MBOX_EMPTY. On success 0 is returned. + + To allow for efficient implementations, this can be defined as a + function-like macro in sys_arch.h instead of a normal function. For + example, a naive implementation could be: + #define sys_arch_mbox_tryfetch(mbox,msg) \ + sys_arch_mbox_fetch(mbox,msg,1) + although this would introduce unnecessary delays. + +- int sys_mbox_valid(sys_mbox_t *mbox) + + Returns 1 if the mailbox is valid, 0 if it is not valid. + When using pointers, a simple way is to check the pointer for != NULL. + When directly using OS structures, implementing this may be more complex. + This may also be a define, in which case the function is not prototyped. + +- void sys_mbox_set_invalid(sys_mbox_t *mbox) + + Invalidate a mailbox so that sys_mbox_valid() returns 0. + ATTENTION: This does NOT mean that the mailbox shall be deallocated: + sys_mbox_free() is always called before calling this function! + This may also be a define, in which case the function is not prototyped. + +If threads are supported by the underlying operating system and if +such functionality is needed in lwIP, the following function will have +to be implemented as well: + +- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio) + + Starts a new thread named "name" with priority "prio" that will begin its + execution in the function "thread()". The "arg" argument will be passed as an + argument to the thread() function. The stack size to used for this thread is + the "stacksize" parameter. The id of the new thread is returned. Both the id + and the priority are system dependent. + +- sys_prot_t sys_arch_protect(void) + + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. + +- void sys_arch_unprotect(sys_prot_t pval) + + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. + +For some configurations, you also need: + +- u32_t sys_now(void) + + This optional function returns the current time in milliseconds (don't care + for wraparound, this is only used for time diffs). + Not implementing this function means you cannot use some modules (e.g. TCP + timestamps, internal timeouts for NO_SYS==1). + + +Note: + +Be carefull with using mem_malloc() in sys_arch. When malloc() refers to +mem_malloc() you can run into a circular function call problem. In mem.c +mem_init() tries to allcate a semaphore using mem_malloc, which of course +can't be performed when sys_arch uses mem_malloc. + +------------------------------------------------------------------------------- +Additional files required for the "OS support" emulation layer: +------------------------------------------------------------------------------- + +cc.h - Architecture environment, some compiler specific, some + environment specific (probably should move env stuff + to sys_arch.h.) + + Typedefs for the types used by lwip - + u8_t, s8_t, u16_t, s16_t, u32_t, s32_t, mem_ptr_t + + Compiler hints for packing lwip's structures - + PACK_STRUCT_FIELD(x) + PACK_STRUCT_STRUCT + PACK_STRUCT_BEGIN + PACK_STRUCT_END + + Platform specific diagnostic output - + LWIP_PLATFORM_DIAG(x) - non-fatal, print a message. + LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution. + Portability defines for printf formatters: + U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F + + "lightweight" synchronization mechanisms - + SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable. + SYS_ARCH_PROTECT(x) - enter protection mode. + SYS_ARCH_UNPROTECT(x) - leave protection mode. + + If the compiler does not provide memset() this file must include a + definition of it, or include a file which defines it. + + This file must either include a system-local which defines + the standard *nix error codes, or it should #define LWIP_PROVIDE_ERRNO + to make lwip/arch.h define the codes which are used throughout. + + +perf.h - Architecture specific performance measurement. + Measurement calls made throughout lwip, these can be defined to nothing. + PERF_START - start measuring something. + PERF_STOP(x) - stop measuring something, and record the result. + +sys_arch.h - Tied to sys_arch.c + + Arch dependent types for the following objects: + sys_sem_t, sys_mbox_t, sys_thread_t, + And, optionally: + sys_prot_t + + Defines to set vars of sys_mbox_t and sys_sem_t to NULL. + SYS_MBOX_NULL NULL + SYS_SEM_NULL NULL diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cc.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cc.h new file mode 100644 index 0000000..85636ef --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cc.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned int u32_t; +typedef signed long s32_t; +typedef u32_t mem_ptr_t; +typedef int sys_prot_t; + +#define U16_F "d" +#define S16_F "d" +#define X16_F "x" +#define U32_F "d" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cpu.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/epstruct.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/init.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/lib.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/perf.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/sys_arch.h new file mode 100644 index 0000000..2e841bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/arch/sys_arch.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xSemaphoreHandle sys_mutex_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.c new file mode 100644 index 0000000..ed3f6a6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.c @@ -0,0 +1,280 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +#include "lwip/ethip6.h" //Evan add for ipv6 +#include +#include + +#define netifMTU (1500) +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) + + +#ifdef CONFIG_CONCURRENT_MODE +#define IF2NAME0 'r' +#define IF2NAME1 '2' +#endif + +static void arp_timer(void *arg); + + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ + +static void low_level_init(struct netif *netif) +{ + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set netif maximum transfer unit */ + netif->mtu = 1500; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Wlan interface is initialized later */ +} + + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + + + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + if (sg_len) + rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len); + + return ERR_OK; +} + + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +//static struct pbuf * low_level_input(struct netif *netif){} + + +/** + * This function is the ethernetif_input task, it is processed when a packet + * is ready to be read from the interface. It uses the function low_level_input() + * that should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +//void ethernetif_input( void * pvParameters ) + + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); + +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + + return ERR_OK; +} + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.c new file mode 100644 index 0000000..a51e2af --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.c @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "lwip/lwip_timers.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +err_t sys_mbox_new(sys_mbox_t *mbox, int size) +{ + (void ) size; + + *mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + if (*mbox == NULL) + return ERR_MEM; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t *mbox) +{ + if( uxQueueMessagesWaiting( *mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( *mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t *mbox, void *data) +{ + while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} +/*----------------------------------------------------------------------------------*/ +int sys_mbox_valid(sys_mbox_t *mbox) +{ + if (*mbox == SYS_MBOX_NULL) + return 0; + else + return 1; +} +/*-----------------------------------------------------------------------------------*/ +void sys_mbox_set_invalid(sys_mbox_t *mbox) +{ + *mbox = SYS_MBOX_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Creates a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +err_t sys_sem_new(sys_sem_t *sem, u8_t count) +{ + vSemaphoreCreateBinary(*sem ); + if(*sem == NULL) + { + +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(*sem,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t *sem) +{ + xSemaphoreGive(*sem); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t *sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete(*sem); +} +/*-----------------------------------------------------------------------------------*/ +int sys_sem_valid(sys_sem_t *sem) +{ + if (*sem == SYS_SEM_NULL) + return 0; + else + return 1; +} + +/*-----------------------------------------------------------------------------------*/ +void sys_sem_set_invalid(sys_sem_t *sem) +{ + *sem = SYS_SEM_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts *sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} +/*-----------------------------------------------------------------------------------*/ + /* Mutexes*/ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_COMPAT_MUTEX == 0 +/* Create a new mutex*/ +err_t sys_mutex_new(sys_mutex_t *mutex) { + + *mutex = xSemaphoreCreateMutex(); + if(*mutex == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.mutex.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + +#if SYS_STATS + ++lwip_stats.sys.mutex.used; + if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) { + lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used; + } +#endif /* SYS_STATS */ + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* Deallocate a mutex*/ +void sys_mutex_free(sys_mutex_t *mutex) +{ +#if SYS_STATS + --lwip_stats.sys.mutex.used; +#endif /* SYS_STATS */ + + vQueueDelete(*mutex); +} +/*-----------------------------------------------------------------------------------*/ +/* Lock a mutex*/ +void sys_mutex_lock(sys_mutex_t *mutex) +{ + sys_arch_sem_wait(*mutex, 0); +} + +/*-----------------------------------------------------------------------------------*/ +/* Unlock a mutex*/ +void sys_mutex_unlock(sys_mutex_t *mutex) +{ + xSemaphoreGive(*mutex); +} +#endif /*LWIP_COMPAT_MUTEX*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( signed portCHAR * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/realtek/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/bpstruct.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/bpstruct.h new file mode 100644 index 0000000..177758c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/bpstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack(1) +#endif + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cc.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cc.h new file mode 100644 index 0000000..bad923c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cc.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2001-2003 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 __CC_H__ +#define __CC_H__ + +#include "cpu.h" + +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 u32_t mem_ptr_t; +typedef int sys_prot_t; + + +#define U16_F "hu" +#define S16_F "d" +#define X16_F "hx" +#define U32_F "u" +#define S32_F "d" +#define X32_F "x" +#define SZT_F "uz" + +//Evan add for ipv6 & IGMP +#define LWIP_RAND 1 + +#ifdef LWIP_RAND +#define u32_t_random() (u32_t)(rand() % 0xFFFFFFFF) +#endif +//Evan add end + + +/* define compiler specific symbols */ +#if defined (__ICCARM__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_USE_INCLUDES + +#elif defined (__CC_ARM) + +#define PACK_STRUCT_BEGIN __packed +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__GNUC__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#elif defined (__TASKING__) + +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_END +#define PACK_STRUCT_FIELD(x) x + +#endif + +#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0) + +#endif /* __CC_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cpu.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cpu.h new file mode 100644 index 0000000..a02f86d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/cpu.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2001-2003 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 __CPU_H__ +#define __CPU_H__ + +#define BYTE_ORDER LITTLE_ENDIAN + +#endif /* __CPU_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/epstruct.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/epstruct.h new file mode 100644 index 0000000..1e1a049 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/epstruct.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +#if defined(__IAR_SYSTEMS_ICC__) +#pragma pack() +#endif + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/init.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/init.h new file mode 100644 index 0000000..e622c73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/init.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2001-2003 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 __ARCH_INIT_H__ +#define __ARCH_INIT_H__ + +#define TCPIP_INIT_DONE(arg) tcpip_init_done(arg) + +void tcpip_init_done(void *); +int wait_for_tcpip_init(void); + +#endif /* __ARCH_INIT_H__ */ + + + + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/lib.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/lib.h new file mode 100644 index 0000000..378f25b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/lib.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __LIB_H__ +#define __LIB_H__ + +#include + + +#endif /* __LIB_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/perf.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/perf.h new file mode 100644 index 0000000..334d42a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/perf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2001-2003 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 __PERF_H__ +#define __PERF_H__ + +#define PERF_START /* null definition */ +#define PERF_STOP(x) /* null definition */ + +#endif /* __PERF_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/sys_arch.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/sys_arch.h new file mode 100644 index 0000000..2e841bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/arch/sys_arch.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xSemaphoreHandle sys_mutex_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/MFC6A0B.tmp b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/MFC6A0B.tmp new file mode 100644 index 0000000..5097f29 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/MFC6A0B.tmp @@ -0,0 +1,489 @@ +/** +* @file +* Ethernet Interface Skeleton +* +*/ + +/* +* 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 file is a skeleton for developing Ethernet network interface +* drivers for lwIP. Add code to the low_level functions and do a +* search-and-replace for the word "ethernetif" to replace it with +* something that better describes your network interface. +*/ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +//#include "lwip/sys.h" +//#include "lwip/tcpip.h" +//#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +//#include "queue.h" + +#include "main.h" // for the definition of CONFIG_WLAN + +#if !CONFIG_WLAN +#include "stm32f2x7_eth.h" +#else +#include +#endif +#include + +#include + + +#define netifMTU (1500) +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) + +#if !CONFIG_WLAN +/* Define those to better describe your network interface. */ +#define IFNAME0 's' +#define IFNAME1 't' + +static struct netif *s_pxNetIf = NULL; +xSemaphoreHandle s_xSemaphore = NULL; + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + + +static void ethernetif_input( void * pvParameters ); +#endif + +static void arp_timer(void *arg); + + +/** +* In this function, the hardware should be initialized. +* Called from ethernetif_init(). +* +* @param netif the already initialized lwip network interface structure +* for this ethernetif +*/ +static void low_level_init(struct netif *netif) +{ + uint32_t i; + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + +#if !CONFIG_WLAN + /* set netif MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; +#endif + + /* set netif maximum transfer unit */ + netif->mtu = 1500; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + +#if !CONFIG_WLAN + s_pxNetIf =netif; + + /* create binary semaphore used for informing ethernetif of frame reception */ + if (s_xSemaphore == NULL) + { + vSemaphoreCreateBinary(s_xSemaphore); + xSemaphoreTake( s_xSemaphore, 0); + } + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* Enable Ethernet Rx interrrupt */ + { + for(i=0; iBuffer1Addr); + bufferoffset = 0; + + for(q = p; q != NULL; q = q->next) + { + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) + { + /* Copy data to Tx buffer*/ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + goto error; + } + + buffer = (u8 *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy ); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Prepare transmit descriptors to give to DMA*/ + ETH_Prepare_Transmit_Descriptors(framelength); + + /* Give semaphore and exit */ + error: + + xSemaphoreGive(xTxSemaphore); + } +#endif // #if !CONFIG_WLAN + + return ERR_OK; +} + + +#if !CONFIG_WLAN +/** +* Should allocate a pbuf and transfer the bytes of the incoming +* packet from the interface into the pbuf. +* +* @param netif the lwip network interface structure for this ethernetif +* @return a pbuf filled with the received packet (including MAC header) +* NULL on memory error +*/ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p= NULL, *q; + u32_t len; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxDesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i=0; + + /* get received frame */ + frame = ETH_Get_Received_Frame_interrupt(); + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + if (len > 0) + { + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + } + + if (p != NULL) + { + DMARxDesc = frame.descriptor; + bufferoffset = 0; + for(q = p; q != NULL; q = q->next) + { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) + { + /* Copy data to pbuf*/ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + buffer = (unsigned char *)(DMARxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy remaining data in pbuf */ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + + /* Release descriptors to DMA */ + DMARxDesc =frame.descriptor; + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxDesc->Status = ETH_DMARxDesc_OWN; + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + /* added for test*/ + } + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + + +/** +* This function is the ethernetif_input task, it is processed when a packet +* is ready to be read from the interface. It uses the function low_level_input() +* that should handle the actual reception of bytes from the network +* interface. Then the type of the received packet is determined and +* the appropriate input function is called. +* +* @param netif the lwip network interface structure for this ethernetif +*/ +void ethernetif_input( void * pvParameters ) +{ + struct pbuf *p; + + for( ;; ) + { + if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) + { +TRY_GET_NEXT_FRAME: + p = low_level_input( s_pxNetIf ); + if (p != NULL) + { + if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) + { + pbuf_free(p); + } + else + { + goto TRY_GET_NEXT_FRAME; + } + } + + } + } +} +#endif +/** +* Should be called at the beginning of the program to set up the +* network interface. It calls the function low_level_init() to do the +* actual setup of the hardware. +* +* This function should be passed as a parameter to netif_add(). +* +* @param netif the lwip network interface structure for this ethernetif +* @return ERR_OK if the loopif is initialized +* ERR_MEM if private data couldn't be allocated +* any other err_t on error +*/ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if !CONFIG_WLAN + /* ethernet */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#else + /* wlan interface*/ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ +#endif // WLAN + + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + + return ERR_OK; +} + + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + +/* +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} +*/ +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.c b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.c new file mode 100644 index 0000000..719bbbd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.c @@ -0,0 +1,656 @@ +/** +* @file +* Ethernet Interface Skeleton +* +*/ + +/* +* 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 file is a skeleton for developing Ethernet network interface +* drivers for lwIP. Add code to the low_level functions and do a +* search-and-replace for the word "ethernetif" to replace it with +* something that better describes your network interface. +*/ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/icmp.h" +#include "lwip/lwip_timers.h" +#include "netif/etharp.h" +#include "err.h" +#include "ethernetif.h" +#include "queue.h" + +#include "lwip/ethip6.h" //Evan add for ipv6 + +#include "main.h" // for the definition of CONFIG_WLAN + + +#if !CONFIG_WLAN +#include "stm32f2x7_eth.h" +#else +#include +#endif +#include + +#include + +#ifdef CONFIG_DONT_CARE_TP +#define netifMTU (576) +#else +#define netifMTU (1500) +#endif +#define netifINTERFACE_TASK_STACK_SIZE ( 350 ) +#define netifINTERFACE_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define netifGUARD_BLOCK_TIME ( 250 ) +/* The time to block waiting for input. */ +#define emacBLOCK_TIME_WAITING_FOR_INPUT ( ( portTickType ) 100 ) +#define FAKE_PING_REPLY 0 + +#if !CONFIG_WLAN +/* Define those to better describe your network interface. */ +#define IFNAME0 's' +#define IFNAME1 't' + +static struct netif *s_pxNetIf = NULL; +xSemaphoreHandle s_xSemaphore = NULL; + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + +static void ethernetif_input( void * pvParameters ); +#endif + +static void arp_timer(void *arg); + + +/** +* In this function, the hardware should be initialized. +* Called from ethernetif_init(). +* +* @param netif the already initialized lwip network interface structure +* for this ethernetif +*/ +static void low_level_init(struct netif *netif) +{ + uint32_t i; + + /* set netif MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + +#if !CONFIG_WLAN + /* set netif MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; +#endif + + /* set netif maximum transfer unit */ + netif->mtu = netifMTU; + + /* Accept broadcast address and ARP traffic */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + +#if !CONFIG_WLAN + s_pxNetIf =netif; + + /* create binary semaphore used for informing ethernetif of frame reception */ + if (s_xSemaphore == NULL) + { + s_xSemaphore= xSemaphoreCreateCounting(20,0); + } + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* Enable Ethernet Rx interrrupt */ + { + for(i=0; itot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_arp = (struct etharp_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_arp = (struct etharp_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = fake_src_mac; + q_arp->opcode = htons(ARP_REPLY); + q_arp->shwaddr = fake_src_mac; + q_arp->sipaddr = p_arp->dipaddr; + q_arp->dhwaddr = p_eth->src; + q_arp->dipaddr = p_arp->sipaddr; + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_arp_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake arp reply \n\r"); +} + +void fake_echo_reply(struct netif *netif, struct pbuf *p) +{ + struct pbuf *q, *tq, *tp; + int q_len, p_len; + struct eth_hdr *p_eth, *q_eth; + struct ip_hdr *p_ip, *q_ip; + struct icmp_echo_hdr *p_echo, *q_echo; + + // Allocate buffer to store received packet + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) + { + p_len = tp->len; + while(p_len) + { + if(q_len > p_len) { + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len); + q_len -= p_len; + p_len = 0; + }else{ + memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len); + p_len -= q_len; + tq = tq->next; + q_len = tq->len; + } + } + } + p_eth = (struct eth_hdr *)p->payload; + q_eth = (struct eth_hdr *)q->payload; + p_ip = (struct ip_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr)); + q_ip = (struct ip_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr)); + p_echo = (struct icmp_echo_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + q_echo = (struct icmp_echo_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr)); + + q_eth->dest = p_eth->src; + q_eth->src = p_eth->dest; + q_ip->src.addr = p_ip->dest.addr; + q_ip->dest.addr = p_ip->src.addr; + q_ip->_chksum = 0; + q_ip->_chksum = inet_chksum(q_ip, sizeof(struct ip_hdr)); + q_echo->type = ICMP_ER; + q_echo->code = 0; + q_echo->chksum = 0; + q_echo->chksum = inet_chksum(q_echo, q->tot_len - sizeof(struct eth_hdr) - sizeof(struct ip_hdr)); + + if(0){ + int i; + char *buf = q->payload; + printf("\n\r"); + for(i=0;itot_len;i++) + printf("0x%02x, ", buf[i]); + printf("\n\r"); + } + if (ERR_OK != netif->input(q, netif)){ + printf("\n\rfake_echo_reply input error"); + pbuf_free(q); + }else + printf("\n\rfake echo reply \n\r"); +} +#endif // #if FAKE_PING_REPLY + +/** +* This function should do the actual transmission of the packet. The packet is +* contained in the pbuf that is passed to the function. This pbuf +* might be chained. +* +* @param netif the lwip network interface structure for this ethernetif +* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) +* @return ERR_OK if the packet could be sent +* an err_t value if the packet couldn't be sent +* +* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to +* strange results. You might consider waiting for space in the DMA queue +* to become availale since the stack doesn't retry to send a packet +* dropped because of memory failure (except for the TCP timers). +*/ + +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ +#if !CONFIG_WLAN + static xSemaphoreHandle xTxSemaphore = NULL; + struct pbuf *q; + uint32_t l = 0; + u8 *buffer ; + + if (xTxSemaphore == NULL) + { + vSemaphoreCreateBinary (xTxSemaphore); + } + + if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME)) + { + buffer = (u8 *)(DMATxDescToSet->Buffer1Addr); + for(q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)&buffer[l], q->payload, q->len); + l = l + q->len; + } + ETH_Prepare_Transmit_Descriptors(l); + xSemaphoreGive(xTxSemaphore); + } +#else + /* Refer to eCos lwip eth_drv_send() */ + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + int sg_len = 0; + struct pbuf *q; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return ERR_IF; + + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + +#if FAKE_PING_REPLY + { + char *header = p->payload; + if(header[12] == 0x08 && header[13] == 0x06) + { // arp packet + if(header[21] == 0x01) + { // arp request packet + printf("\n\rfake_ping: arp request packet."); + if(0) + { + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_arp_reply(netif, p); + return ERR_OK; + } + }else if(header[12] == 0x08 && header[13] == 0x00) + { // ip packet + if(header[15] == 0x00 && header[23] == 0x01) + { // icmp packet + printf("\n\rfake_ping: icmp packet."); + if(0){ + int i; + printf("\n\r"); + for (q = p; q != NULL; q = q->next) + { + char *buf = q->payload; + for(i=0;ilen;i++) + printf("0x%02x, ", buf[i]); + } + printf("\n\r"); + } + fake_echo_reply(netif, p); + return ERR_OK; + } + } + } +#endif // #if FAKE_PING_REPLY + if (sg_len) + rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len); +#endif // #if !CONFIG_WLAN + + return ERR_OK; +} + + +#if !CONFIG_WLAN +/** +* Should allocate a pbuf and transfer the bytes of the incoming +* packet from the interface into the pbuf. +* +* @param netif the lwip network interface structure for this ethernetif +* @return a pbuf filled with the received packet (including MAC header) +* NULL on memory error +*/ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + u16_t len; + uint32_t l=0,i =0; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxNextDesc; + + p = NULL; + + /* Get received frame */ + frame = ETH_Get_Received_Frame_interrupt(); + + /* check that frame has no error */ + if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) + { + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + /* Copy received frame from ethernet driver buffer to stack buffer */ + if (p != NULL) + { + for (q = p; q != NULL; q = q->next) + { + memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len); + l = l + q->len; + } + } + } + + /* Release descriptors to DMA */ + /* Check if received frame with multiple DMA buffer segments */ + if (DMA_RX_FRAME_infos->Seg_Count > 1) + { + DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc; + } + else + { + DMARxNextDesc = frame.descriptor; + } + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxNextDesc->Status = ETH_DMARxDesc_OWN; + DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + + +/** +* This function is the ethernetif_input task, it is processed when a packet +* is ready to be read from the interface. It uses the function low_level_input() +* that should handle the actual reception of bytes from the network +* interface. Then the type of the received packet is determined and +* the appropriate input function is called. +* +* @param netif the lwip network interface structure for this ethernetif +*/ +void ethernetif_input( void * pvParameters ) +{ + struct pbuf *p; + + for( ;; ) + { + if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE) + { + p = low_level_input( s_pxNetIf ); + if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf)) + { + pbuf_free(p); + p=NULL; + } + } + } +} +#endif + +/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */ +void ethernetif_recv(struct netif *netif, int total_len) +{ +#if CONFIG_WLAN + struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; + struct pbuf *p, *q; + int sg_len = 0; + + if(!rltk_wlan_running(netif_get_idx(netif))) + return; + + if ((total_len > MAX_ETH_MSG) || (total_len < 0)) + total_len = MAX_ETH_MSG; + + // Allocate buffer to store received packet + p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL); + if (p == NULL) { + printf("\n\rCannot allocate pbuf to receive packet"); + return; + } + + // Create scatter list + for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) { + sg_list[sg_len].buf = (unsigned int) q->payload; + sg_list[sg_len++].len = q->len; + } + + // Copy received packet to scatter list from wrapper rx skb + //printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len); + rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len); + + // Pass received packet to the interface + if (ERR_OK != netif->input(p, netif)) + pbuf_free(p); +#endif +} + +/** +* Should be called at the beginning of the program to set up the +* network interface. It calls the function low_level_init() to do the +* actual setup of the hardware. +* +* This function should be passed as a parameter to netif_add(). +* +* @param netif the lwip network interface structure for this ethernetif +* @return ERR_OK if the loopif is initialized +* ERR_MEM if private data couldn't be allocated +* any other err_t on error +*/ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if !CONFIG_WLAN + /* ethernet */ + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; +#else + /* wlan interface*/ +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + if(netif->name[1] == '0') + netif->hostname = "lwip0"; + else if(netif->name[1] == '1') + netif->hostname = "lwip1"; +#endif /* LWIP_NETIF_HOSTNAME */ + +#endif // WLAN + + netif->output = etharp_output; + netif->output_ip6 = ethip6_output ;//Evan add for ipv6 + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + etharp_init(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + + return ERR_OK; +} + + +static void arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * For FreeRTOS tickless + */ +int lwip_tickless_used = 0; + + +int arp_timeout_exist(void) +{ + struct sys_timeouts *timeouts; + struct sys_timeo *t; + + timeouts = sys_arch_timeouts(); + + for(t = timeouts->next; t != NULL;t = t->next) + if(t->h == arp_timer) + return 1; + + return 0; +} + +//Called by rltk_wlan_PRE_SLEEP_PROCESSING() +void lwip_PRE_SLEEP_PROCESSING(void) +{ + if(arp_timeout_exist()) { + tcpip_untimeout(arp_timer, NULL); + } + lwip_tickless_used = 1; +} + +//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit +void lwip_POST_SLEEP_PROCESSING(void) +{ + if(lwip_tickless_used) { + tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); + } +} diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.h new file mode 100644 index 0000000..93139d1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/ethernetif.h @@ -0,0 +1,13 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +void ethernetif_recv(struct netif *netif, int total_len); +err_t ethernetif_init(struct netif *netif); +void lwip_PRE_SLEEP_PROCESSING(void); +void lwip_POST_SLEEP_PROCESSING(void); + +#endif diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.c b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.c new file mode 100644 index 0000000..3d111c6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2001-2003 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 + * + */ + +/* lwIP includes. */ +#include "lwip/debug.h" +#include "lwip/def.h" +#include "lwip/sys.h" +#include "lwip/mem.h" +#include "lwip/stats.h" +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "lwip/lwip_timers.h" + +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +struct timeoutlist +{ + struct sys_timeouts timeouts; + xTaskHandle pid; +}; + +/* This is the number of threads that can be started with sys_thread_new() */ +#define SYS_THREAD_MAX 6 + +static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX]; +static u16_t s_nextthread = 0; + + +/*-----------------------------------------------------------------------------------*/ +// Creates an empty mailbox. +err_t sys_mbox_new(sys_mbox_t *mbox, int size) +{ + (void ) size; + + *mbox = xQueueCreate( archMESG_QUEUE_LENGTH, sizeof( void * ) ); + +#if SYS_STATS + ++lwip_stats.sys.mbox.used; + if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used) { + lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used; + } +#endif /* SYS_STATS */ + if (*mbox == NULL) + return ERR_MEM; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Deallocates a mailbox. If there are messages still present in the + mailbox when the mailbox is deallocated, it is an indication of a + programming error in lwIP and the developer should be notified. +*/ +void sys_mbox_free(sys_mbox_t *mbox) +{ + if( uxQueueMessagesWaiting( *mbox ) ) + { + /* Line for breakpoint. Should never break here! */ + portNOP(); +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + // TODO notify the user of failure. + } + + vQueueDelete( *mbox ); + +#if SYS_STATS + --lwip_stats.sys.mbox.used; +#endif /* SYS_STATS */ +} + +/*-----------------------------------------------------------------------------------*/ +// Posts the "msg" to the mailbox. +void sys_mbox_post(sys_mbox_t *mbox, void *data) +{ + while ( xQueueSendToBack(*mbox, &data, portMAX_DELAY ) != pdTRUE ){} +} + + +/*-----------------------------------------------------------------------------------*/ +// Try to post the "msg" to the mailbox. +err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg) +{ +err_t result; + + if ( xQueueSend( *mbox, &msg, 0 ) == pdPASS ) + { + result = ERR_OK; + } + else { + // could not post, queue must be full + result = ERR_MEM; + +#if SYS_STATS + lwip_stats.sys.mbox.err++; +#endif /* SYS_STATS */ + + } + + return result; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread until a message arrives in the mailbox, but does + not block the thread longer than "timeout" milliseconds (similar to + the sys_arch_sem_wait() function). The "msg" argument is a result + parameter that is set by the function (i.e., by doing "*msg = + ptr"). The "msg" parameter maybe NULL to indicate that the message + should be dropped. + + The return values are the same as for the sys_arch_sem_wait() function: + Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a + timeout. + + Note that a function with a similar name, sys_mbox_fetch(), is + implemented by lwIP. +*/ +u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) +{ +void *dummyptr; +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( timeout != 0 ) + { + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), timeout / portTICK_RATE_MS ) ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); + } + else // timed out blocking for message + { + *msg = NULL; + + return SYS_ARCH_TIMEOUT; + } + } + else // block forever for a message. + { + while( pdTRUE != xQueueReceive( *mbox, &(*msg), portMAX_DELAY ) ){} // time is arbitrary + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked TODO test + } +} + +/*-----------------------------------------------------------------------------------*/ +/* + Similar to sys_arch_mbox_fetch, but if message is not ready immediately, we'll + return with SYS_MBOX_EMPTY. On success, 0 is returned. +*/ +u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg) +{ +void *dummyptr; + + if ( msg == NULL ) + { + msg = &dummyptr; + } + + if ( pdTRUE == xQueueReceive( *mbox, &(*msg), 0 ) ) + { + return ERR_OK; + } + else + { + return SYS_MBOX_EMPTY; + } +} +/*----------------------------------------------------------------------------------*/ +int sys_mbox_valid(sys_mbox_t *mbox) +{ + if (*mbox == SYS_MBOX_NULL) + return 0; + else + return 1; +} +/*-----------------------------------------------------------------------------------*/ +void sys_mbox_set_invalid(sys_mbox_t *mbox) +{ + *mbox = SYS_MBOX_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Creates a new semaphore. The "count" argument specifies +// the initial state of the semaphore. +err_t sys_sem_new(sys_sem_t *sem, u8_t count) +{ + vSemaphoreCreateBinary(*sem ); + if(*sem == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.sem.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + + if(count == 0) // Means it can't be taken + { + xSemaphoreTake(*sem,1); + } + +#if SYS_STATS + ++lwip_stats.sys.sem.used; + if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used) { + lwip_stats.sys.sem.max = lwip_stats.sys.sem.used; + } +#endif /* SYS_STATS */ + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + Blocks the thread while waiting for the semaphore to be + signaled. If the "timeout" argument is non-zero, the thread should + only be blocked for the specified time (measured in + milliseconds). + + If the timeout argument is non-zero, the return value is the number of + milliseconds spent waiting for the semaphore to be signaled. If the + semaphore wasn't signaled within the specified time, the return value is + SYS_ARCH_TIMEOUT. If the thread didn't have to wait for the semaphore + (i.e., it was already signaled), the function may return zero. + + Notice that lwIP implements a function with a similar name, + sys_sem_wait(), that uses the sys_arch_sem_wait() function. +*/ +u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) +{ +portTickType StartTime, EndTime, Elapsed; + + StartTime = xTaskGetTickCount(); + + if( timeout != 0) + { + if( xSemaphoreTake( *sem, timeout / portTICK_RATE_MS ) == pdTRUE ) + { + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return (Elapsed); // return time blocked TODO test + } + else + { + return SYS_ARCH_TIMEOUT; + } + } + else // must block without a timeout + { + while( xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE){} + EndTime = xTaskGetTickCount(); + Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + + return ( Elapsed ); // return time blocked + + } +} + +/*-----------------------------------------------------------------------------------*/ +// Signals a semaphore +void sys_sem_signal(sys_sem_t *sem) +{ + xSemaphoreGive(*sem); +} + +/*-----------------------------------------------------------------------------------*/ +// Deallocates a semaphore +void sys_sem_free(sys_sem_t *sem) +{ +#if SYS_STATS + --lwip_stats.sys.sem.used; +#endif /* SYS_STATS */ + + vQueueDelete(*sem); +} +/*-----------------------------------------------------------------------------------*/ +int sys_sem_valid(sys_sem_t *sem) +{ + if (*sem == SYS_SEM_NULL) + return 0; + else + return 1; +} + +/*-----------------------------------------------------------------------------------*/ +void sys_sem_set_invalid(sys_sem_t *sem) +{ + *sem = SYS_SEM_NULL; +} + +/*-----------------------------------------------------------------------------------*/ +// Initialize sys arch +void sys_init(void) +{ + int i; + + // Initialize the the per-thread sys_timeouts structures + // make sure there are no valid pids in the list + for(i = 0; i < SYS_THREAD_MAX; i++) + { + s_timeoutlist[i].pid = 0; + s_timeoutlist[i].timeouts.next = NULL; + } + + // keep track of how many threads have been created + s_nextthread = 0; +} + +/* + Returns a pointer to the per-thread sys_timeouts structure. In lwIP, + each thread has a list of timeouts which is represented as a linked + list of sys_timeout structures. The sys_timeouts structure holds a + pointer to a linked list of timeouts. This function is called by + the lwIP timeout scheduler and must not return a NULL value. + + In a single threaded sys_arch implementation, this function will + simply return a pointer to a global sys_timeouts variable stored in + the sys_arch module. +*/ +struct sys_timeouts* sys_arch_timeouts(void) +{ +int i; +xTaskHandle pid; +struct timeoutlist *tl; + + pid = xTaskGetCurrentTaskHandle(); + + for(i = 0; i < s_nextthread; i++) + { + tl = &(s_timeoutlist[i]); + if(tl->pid == pid) + { + return &(tl->timeouts); + } + } + // Error + return NULL; +} +/*-----------------------------------------------------------------------------------*/ + /* Mutexes*/ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +#if LWIP_COMPAT_MUTEX == 0 +/* Create a new mutex*/ +err_t sys_mutex_new(sys_mutex_t *mutex) { + + *mutex = xSemaphoreCreateMutex(); + if(*mutex == NULL) + { +#if SYS_STATS + ++lwip_stats.sys.mutex.err; +#endif /* SYS_STATS */ + return ERR_MEM; + } + +#if SYS_STATS + ++lwip_stats.sys.mutex.used; + if (lwip_stats.sys.mutex.max < lwip_stats.sys.mutex.used) { + lwip_stats.sys.mutex.max = lwip_stats.sys.mutex.used; + } +#endif /* SYS_STATS */ + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* Deallocate a mutex*/ +void sys_mutex_free(sys_mutex_t *mutex) +{ +#if SYS_STATS + --lwip_stats.sys.mutex.used; +#endif /* SYS_STATS */ + + vQueueDelete(*mutex); +} +/*-----------------------------------------------------------------------------------*/ +/* Lock a mutex*/ +void sys_mutex_lock(sys_mutex_t *mutex) +{ + sys_arch_sem_wait(*mutex, 0); +} + +/*-----------------------------------------------------------------------------------*/ +/* Unlock a mutex*/ +void sys_mutex_unlock(sys_mutex_t *mutex) +{ + xSemaphoreGive(*mutex); +} +#endif /*LWIP_COMPAT_MUTEX*/ +/*-----------------------------------------------------------------------------------*/ +// TODO +/*-----------------------------------------------------------------------------------*/ +/* + Starts a new thread with priority "prio" that will begin its execution in the + function "thread()". The "arg" argument will be passed as an argument to the + thread() function. The id of the new thread is returned. Both the id and + the priority are system dependent. +*/ +sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio) +{ +xTaskHandle CreatedTask; +int result; + + if ( s_nextthread < SYS_THREAD_MAX ) + { + vPortEnterCritical(); + result = xTaskCreate( thread, ( signed portCHAR * ) name, stacksize, arg, prio, &CreatedTask ); + + // For each task created, store the task handle (pid) in the timers array. + // This scheme doesn't allow for threads to be deleted + s_timeoutlist[s_nextthread++].pid = CreatedTask; + vPortExitCritical(); + + if(result == pdPASS) + { + return CreatedTask; + } + else + { + return NULL; + } + } + else + { + return NULL; + } +} + +/* + This optional function does a "fast" critical region protection and returns + the previous protection level. This function is only called during very short + critical regions. An embedded system which supports ISR-based drivers might + want to implement this function by disabling interrupts. Task-based systems + might want to implement this by using a mutex or disabling tasking. This + function should support recursive calls from the same task or interrupt. In + other words, sys_arch_protect() could be called while already protected. In + that case the return value indicates that it is already protected. + + sys_arch_protect() is only required if your port is supporting an operating + system. +*/ +sys_prot_t sys_arch_protect(void) +{ + vPortEnterCritical(); + return 1; +} + +/* + This optional function does a "fast" set of critical region protection to the + value specified by pval. See the documentation for sys_arch_protect() for + more information. This function is only required if your port is supporting + an operating system. +*/ +void sys_arch_unprotect(sys_prot_t pval) +{ + ( void ) pval; + vPortExitCritical(); +} + +/* + * Prints an assertion messages and aborts execution. + */ +void sys_assert( const char *msg ) +{ + ( void ) msg; + /*FSL:only needed for debugging + printf(msg); + printf("\n\r"); + */ + vPortEnterCritical( ); + for(;;) + ; +} diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.h new file mode 100644 index 0000000..83d0c08 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/freertos/sys_arch.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2001-2003 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 __SYS_RTXC_H__ +#define __SYS_RTXC_H__ + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +#define SYS_MBOX_NULL (xQueueHandle)0 +#define SYS_SEM_NULL (xSemaphoreHandle)0 +#define SYS_DEFAULT_THREAD_STACK_DEPTH configMINIMAL_STACK_SIZE + +typedef xSemaphoreHandle sys_sem_t; +typedef xQueueHandle sys_mbox_t; +typedef xTaskHandle sys_thread_t; + +typedef struct _sys_arch_state_t +{ + // Task creation data. + char cTaskName[configMAX_TASK_NAME_LEN]; + unsigned short nStackDepth; + unsigned short nTaskCount; +} sys_arch_state_t; + + + +//extern sys_arch_state_t s_sys_arch_state; + +//void sys_set_default_state(); +//void sys_set_state(signed char *pTaskName, unsigned short nStackSize); + +/* Message queue constants. */ +#define archMESG_QUEUE_LENGTH ( 6 ) +#endif /* __SYS_RTXC_H__ */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.c b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.c new file mode 100644 index 0000000..f3b74ee --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.c @@ -0,0 +1,366 @@ +/** + * @file + * Ethernet Interface for standalone applications (without RTOS) - works only for + * ethernet polling mode (polling for ethernet frame reception) + * + */ + +/* + * 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/mem.h" +#include "netif/etharp.h" +#include "ethernetif.h" +#include "stm32f2x7_eth.h" +#include "main.h" +#include + +/* Network interface name */ +#define IFNAME0 's' +#define IFNAME1 't' + + +/* Ethernet Rx & Tx DMA Descriptors */ +extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; + +/* Ethernet Driver Receive buffers */ +extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; + +/* Ethernet Driver Transmit buffers */ +extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; + +/* Global pointers to track current transmit and receive descriptors */ +extern ETH_DMADESCTypeDef *DMATxDescToSet; +extern ETH_DMADESCTypeDef *DMARxDescToGet; + +/* Global pointer for last received frame infos */ +extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos; + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void low_level_init(struct netif *netif) +{ +#ifdef CHECKSUM_BY_HARDWARE + int i; +#endif + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = MAC_ADDR0; + netif->hwaddr[1] = MAC_ADDR1; + netif->hwaddr[2] = MAC_ADDR2; + netif->hwaddr[3] = MAC_ADDR3; + netif->hwaddr[4] = MAC_ADDR4; + netif->hwaddr[5] = MAC_ADDR5; + + /* initialize MAC address in ethernet MAC */ + ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Initialize Tx Descriptors list: Chain Mode */ + ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + /* Initialize Rx Descriptors list: Chain Mode */ + ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + +#ifdef CHECKSUM_BY_HARDWARE + /* Enable the TCP, UDP and ICMP checksum insertion for the Tx frames */ + for(i=0; iBuffer1Addr); + __IO ETH_DMADESCTypeDef *DmaTxDesc; + uint16_t framelength = 0; + uint32_t bufferoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t payloadoffset = 0; + + DmaTxDesc = DMATxDescToSet; + bufferoffset = 0; + + /* copy frame from pbufs to driver buffers */ + for(q = p; q != NULL; q = q->next) + { + /* Is this buffer available? If not, goto error */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + errval = ERR_BUF; + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) + { + /* Copy data to Tx buffer*/ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADESCTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if((DmaTxDesc->Status & ETH_DMATxDesc_OWN) != (u32)RESET) + { + errval = ERR_USE; + goto error; + } + + buffer = (u8 *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy( (u8_t*)((u8_t*)buffer + bufferoffset), (u8_t*)((u8_t*)q->payload + payloadoffset), byteslefttocopy ); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Note: padding and CRC for transmitted frame + are automatically inserted by DMA */ + + /* Prepare transmit descriptors to give to DMA*/ + ETH_Prepare_Transmit_Descriptors(framelength); + + errval = ERR_OK; + +error: + + /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ + if ((ETH->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) + { + /* Clear TUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_TUS; + + /* Resume DMA transmission*/ + ETH->DMATPDR = 0; + } + return errval; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p, *q; + uint32_t len; + FrameTypeDef frame; + u8 *buffer; + __IO ETH_DMADESCTypeDef *DMARxDesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i=0; + + /* get received frame */ + frame = ETH_Get_Received_Frame(); + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = frame.length; + buffer = (u8 *)frame.buffer; + + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) + { + DMARxDesc = frame.descriptor; + bufferoffset = 0; + for(q = p; q != NULL; q = q->next) + { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) + { + /* Copy data to pbuf*/ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + buffer = (unsigned char *)(DMARxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + /* Copy remaining data in pbuf */ + memcpy( (u8_t*)((u8_t*)q->payload + payloadoffset), (u8_t*)((u8_t*)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + } + + /* Release descriptors to DMA */ + DMARxDesc =frame.descriptor; + + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; iSeg_Count; i++) + { + DMARxDesc->Status = ETH_DMARxDesc_OWN; + DMARxDesc = (ETH_DMADESCTypeDef *)(DMARxDesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + DMA_RX_FRAME_infos->Seg_Count =0; + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + ETH->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + ETH->DMARPDR = 0; + } + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +err_t ethernetif_input(struct netif *netif) +{ + err_t err; + struct pbuf *p; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + + /* no packet could be read, silently ignore this */ + if (p == NULL) return ERR_MEM; + + /* entry point to the LwIP stack */ + err = netif->input(p, netif); + + if (err != ERR_OK) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + } + return err; +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.h b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.h new file mode 100644 index 0000000..9ff1408 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/port/stm32f2x7/standalone/ethernetif.h @@ -0,0 +1,11 @@ +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + + +#include "lwip/err.h" +#include "lwip/netif.h" + +err_t ethernetif_init(struct netif *netif); +err_t ethernetif_input(struct netif *netif); + +#endif diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/FILES b/component/common/network/lwip/lwip_v1.5.0.beta/src/FILES new file mode 100644 index 0000000..952aeab --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/FILES @@ -0,0 +1,13 @@ +api/ - The code for the high-level wrapper API. Not needed if + you use the lowel-level call-back/raw API. + +core/ - The core of the TPC/IP stack; protocol implementations, + memory and buffer management, and the low-level raw API. + +include/ - lwIP include files. + +netif/ - Generic network interface device drivers are kept here, + as well as the ARP module. + +For more information on the various subdirectories, check the FILES +file in each directory. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_lib.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_lib.c new file mode 100644 index 0000000..7791b56 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_lib.c @@ -0,0 +1,903 @@ +/** + * @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/lwip_ip.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include + +#define API_MSG_VAR_REF(name) API_VAR_REF(name) +#define API_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct api_msg, name) +#define API_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct api_msg, MEMP_API_MSG, name) +#define API_MSG_VAR_ALLOC_DONTFAIL(name) API_VAR_ALLOC_DONTFAIL(struct api_msg, MEMP_API_MSG, name) +#define API_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_API_MSG, name) + +/** + * 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; + API_MSG_VAR_DECLARE(msg); + + conn = netconn_alloc(t, callback); + if (conn != NULL) { + err_t err; + API_MSG_VAR_ALLOC_DONTFAIL(msg); + API_MSG_VAR_REF(msg).msg.msg.n.proto = proto; + API_MSG_VAR_REF(msg).msg.conn = conn; + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_newconn, err); + API_MSG_VAR_FREE(msg); + if (err != ERR_OK) { + LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); + 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 */ +#if !LWIP_NETCONN_SEM_PER_THREAD + LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed)); + sys_sem_free(&conn->op_completed); +#endif /* !LWIP_NETCONN_SEM_PER_THREAD */ + 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) +{ + err_t err; + API_MSG_VAR_DECLARE(msg); + + /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */ + if (conn == NULL) { + return ERR_OK; + } + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).function = lwip_netconn_do_delconn; + API_MSG_VAR_REF(msg).msg.conn = conn; + err = tcpip_apimsg(&API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(msg); + + if (err != ERR_OK) { + return err; + } + + netconn_free(conn); + + 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) +{ + API_MSG_VAR_DECLARE(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;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.ad.local = local; +#if LWIP_MPU_COMPATIBLE + TCPIP_APIMSG(msg, lwip_netconn_do_getaddr, err); + *addr = *ipX_2_ip(&(msg->msg.msg.ad.ipaddr)); + *port = msg->msg.msg.ad.port; +#else /* LWIP_MPU_COMPATIBLE */ + msg.msg.msg.ad.ipaddr = ip_2_ipX(addr); + msg.msg.msg.ad.port = port; + TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err); +#endif /* LWIP_MPU_COMPATIBLE */ + API_MSG_VAR_FREE(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) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); +#if LWIP_MPU_COMPATIBLE + if (addr == NULL) { + addr = IP_ADDR_ANY; + } +#endif /* LWIP_MPU_COMPATIBLE */ + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.bc.ipaddr = API_MSG_VAR_REF(addr); + API_MSG_VAR_REF(msg).msg.msg.bc.port = port; + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_bind, err); + API_MSG_VAR_FREE(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) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); +#if LWIP_MPU_COMPATIBLE + if (addr == NULL) { + addr = IP_ADDR_ANY; + } +#endif /* LWIP_MPU_COMPATIBLE */ + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.bc.ipaddr = API_MSG_VAR_REF(addr); + API_MSG_VAR_REF(msg).msg.msg.bc.port = port; +#if LWIP_TCP +#if (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + /* The TCP version waits for the connect to succeed, + so always needs to use message passing. */ + API_MSG_VAR_REF(msg).function = lwip_netconn_do_connect; + err = tcpip_apimsg(&API_MSG_VAR_REF(msg)); + } +#endif /* LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP + else +#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */ +#if (LWIP_UDP || LWIP_RAW) + { + /* UDP and RAW only set flags, so we can use core-locking. */ + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_connect, err); + } +#endif /* (LWIP_UDP || LWIP_RAW) */ + API_MSG_VAR_FREE(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) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_disconnect, err); + API_MSG_VAR_FREE(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 + API_MSG_VAR_DECLARE(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;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; +#if TCP_LISTEN_BACKLOG + API_MSG_VAR_REF(msg).msg.msg.lb.backlog = backlog; +#endif /* TCP_LISTEN_BACKLOG */ + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_listen, err); + API_MSG_VAR_FREE(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 + API_MSG_VAR_DECLARE(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 aborted */ + NETCONN_SET_SAFE_ERR(conn, ERR_ABRT); + return ERR_ABRT; + } +#if TCP_LISTEN_BACKLOG + /* Let the stack know that we have accepted the connection. */ + API_MSG_VAR_ALLOC_DONTFAIL(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&API_MSG_VAR_REF(msg), lwip_netconn_do_recv); + API_MSG_VAR_FREE(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 + API_MSG_VAR_DECLARE(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 (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + 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). */ + API_MSG_VAR_ALLOC_DONTFAIL(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + if (buf != NULL) { + API_MSG_VAR_REF(msg).msg.msg.r.len = ((struct pbuf *)buf)->tot_len; + } else { + API_MSG_VAR_REF(msg).msg.msg.r.len = 1; + } + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&API_MSG_VAR_REF(msg), lwip_netconn_do_recv); + API_MSG_VAR_FREE(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) && + NETCONNTYPE_GROUP(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 (LWIP_UDP || LWIP_RAW) + if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) +#endif /* (LWIP_UDP || LWIP_RAW) */ + { + 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; + ipX_addr_set_any(LWIP_IPV6, &buf->addr); + *new_buf = buf; + /* don't set conn->last_err: it's only ERR_OK, anyway */ + return ERR_OK; + } +#endif /* LWIP_TCP */ +#if LWIP_TCP && (LWIP_UDP || LWIP_RAW) + else +#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */ + { +#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) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && + (netconn_get_noautorecved(conn))) { + API_MSG_VAR_DECLARE(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). */ + API_MSG_VAR_ALLOC_DONTFAIL(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.r.len = length; + /* don't care for the return value of lwip_netconn_do_recv */ + TCPIP_APIMSG_NOERR(&API_MSG_VAR_REF(msg), lwip_netconn_do_recv); + API_MSG_VAR_FREE(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) { + ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &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) +{ + API_MSG_VAR_DECLARE(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)); + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.b = buf; + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_send, err); + API_MSG_VAR_FREE(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 data can be written at once + * @param bytes_written pointer to a location that receives the number of written bytes + * @return ERR_OK if data was sent, any other err_t on error + */ +err_t +netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + u8_t dontblock; + + LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;); + if (size == 0) { + return ERR_OK; + } + dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK); + if (dontblock && !bytes_written) { + /* This implies netconn_write() cannot be used for non-blocking send, since + it has no way to return the number of bytes written. */ + return ERR_VAL; + } + + API_MSG_VAR_ALLOC(msg); + /* non-blocking write sends as much */ + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.w.dataptr = dataptr; + API_MSG_VAR_REF(msg).msg.msg.w.apiflags = apiflags; + API_MSG_VAR_REF(msg).msg.msg.w.len = size; +#if LWIP_SO_SNDTIMEO + if (conn->send_timeout != 0) { + /* get the time we started, which is later compared to + sys_now() + conn->send_timeout */ + API_MSG_VAR_REF(msg).msg.msg.w.time_started = sys_now(); + } else { + API_MSG_VAR_REF(msg).msg.msg.w.time_started = 0; + } +#endif /* LWIP_SO_SNDTIMEO */ + + /* 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. */ + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_write, err); + if ((err == ERR_OK) && (bytes_written != NULL)) { + if (dontblock +#if LWIP_SO_SNDTIMEO + || (conn->send_timeout != 0) +#endif /* LWIP_SO_SNDTIMEO */ + ) { + /* nonblocking write: maybe the data has been sent partly */ + *bytes_written = API_MSG_VAR_REF(msg).msg.msg.w.len; + } else { + /* blocking call succeeded: all data has been sent if it */ + *bytes_written = size; + } + } + API_MSG_VAR_FREE(msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} + +/** + * Close or 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) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); + API_MSG_VAR_REF(msg).function = lwip_netconn_do_close; + API_MSG_VAR_REF(msg).msg.conn = conn; + /* shutting down both ends is the same as closing */ + API_MSG_VAR_REF(msg).msg.msg.sd.shut = how; + /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close, + don't use TCPIP_APIMSG here */ + err = tcpip_apimsg(&API_MSG_VAR_REF(msg)); + API_MSG_VAR_FREE(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 || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * 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) +{ + API_MSG_VAR_DECLARE(msg); + err_t err; + + LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;); + + API_MSG_VAR_ALLOC(msg); +#if LWIP_MPU_COMPATIBLE + if (multiaddr == NULL) { + multiaddr = IP_ADDR_ANY; + } + if (netif_addr == NULL) { + netif_addr = IP_ADDR_ANY; + } +#endif /* LWIP_MPU_COMPATIBLE */ + API_MSG_VAR_REF(msg).msg.conn = conn; + API_MSG_VAR_REF(msg).msg.msg.jl.multiaddr = API_MSG_VAR_REF(ip_2_ipX(multiaddr)); + API_MSG_VAR_REF(msg).msg.msg.jl.netif_addr = API_MSG_VAR_REF(ip_2_ipX(netif_addr)); + API_MSG_VAR_REF(msg).msg.msg.jl.join_or_leave = join_or_leave; + TCPIP_APIMSG(&API_MSG_VAR_REF(msg), lwip_netconn_do_join_leave_group, err); + API_MSG_VAR_FREE(msg); + + NETCONN_SET_SAFE_ERR(conn, err); + return err; +} +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#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) +{ + API_VAR_DECLARE(struct dns_api_msg, msg); +#if !LWIP_MPU_COMPATIBLE + sys_sem_t sem; +#endif /* LWIP_MPU_COMPATIBLE */ + err_t err; + + LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;); + LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;); +#if LWIP_MPU_COMPATIBLE + if (strlen(name) >= DNS_MAX_NAME_LENGTH) { + return ERR_ARG; + } +#endif + + API_VAR_ALLOC(struct dns_api_msg, MEMP_DNS_API_MSG, msg); +#if LWIP_MPU_COMPATIBLE + strncpy(API_VAR_REF(msg).name, name, DNS_MAX_NAME_LENGTH-1); + API_VAR_REF(msg).name[DNS_MAX_NAME_LENGTH-1] = 0; +#else /* LWIP_MPU_COMPATIBLE */ + msg.err = &err; + msg.sem = &sem; + API_VAR_REF(msg).addr = API_VAR_REF(addr); + API_VAR_REF(msg).name = name; +#endif /* LWIP_MPU_COMPATIBLE */ + err = sys_sem_new(API_EXPR_REF(API_VAR_REF(msg).sem), 0); + if (err != ERR_OK) { + API_VAR_FREE(MEMP_DNS_API_MSG, msg); + return err; + } + + tcpip_callback(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg)); + sys_sem_wait(API_EXPR_REF(API_VAR_REF(msg).sem)); + sys_sem_free(API_EXPR_REF(API_VAR_REF(msg).sem)); + +#if LWIP_MPU_COMPATIBLE + *addr = msg->addr; + err = msg->err; +#endif /* LWIP_MPU_COMPATIBLE */ + + API_VAR_FREE(MEMP_DNS_API_MSG, msg); + return err; +} +#endif /* LWIP_DNS*/ + +#if LWIP_NETCONN_SEM_PER_THREAD +void netconn_thread_init(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if (sem == SYS_SEM_NULL) { + /* call alloc only once */ + LWIP_NETCONN_THREAD_SEM_ALLOC(); + LWIP_ASSERT("LWIP_NETCONN_THREAD_SEM_ALLOC() failed", LWIP_NETCONN_THREAD_SEM_GET() != SYS_SEM_NULL); + } +} + +void netconn_thread_cleanup(void) +{ + sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET(); + if (sem == SYS_SEM_NULL) { + /* call free only once */ + LWIP_NETCONN_THREAD_SEM_FREE(); + } +} +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +//Realtek add +err_t netconn_abort(struct netconn *conn) +{ + if (conn->acceptmbox != SYS_MBOX_NULL) { + /* Register event with callback */ + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + sys_mbox_post(conn->acceptmbox, NULL); + } + return ERR_OK; +} +//Realtek add end + +#endif /* LWIP_NETCONN */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_msg.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_msg.c new file mode 100644 index 0000000..42a4f8e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/api_msg.c @@ -0,0 +1,1657 @@ +/** + * @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/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 "lwip/mld6.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 lwip_netconn_do_writemore(struct netconn *conn); +static void lwip_netconn_do_close_internal(struct netconn *conn); +#endif + +#if LWIP_RAW +/** + * Receive callback function for RAW netconns. + * Doesn't 'eat' the packet, only copies 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; + ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_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; + ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr); + buf->port = port; +#if LWIP_NETBUF_RECVINFO + { + /* get the UDP header - always in the first pbuf, ensured by udp_input */ + const struct udp_hdr* udphdr = ipX_next_header_ptr(); +#if LWIP_CHECKSUM_ON_COPY + buf->flags = NETBUF_FLAG_DESTADDR; +#endif /* LWIP_CHECKSUM_ON_COPY */ + ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_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; + + if (conn == NULL) { + return ERR_VAL; + } + LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb); + + 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) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_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) { + lwip_netconn_do_writemore(conn); + } else if (conn->state == NETCONN_CLOSE) { + lwip_netconn_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; + + /* @todo: the type of NETCONN_EVT created should depend on 'old_state' */ + + /* 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 lwip_netconn_do_writemore/lwip_netconn_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) { + sys_sem_t* op_completed_sem; + /* set error return code */ + LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + conn->current_msg->err = err; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + LWIP_ASSERT("inavlid op_completed_sem", op_completed_sem != SYS_SEM_NULL); + conn->current_msg = NULL; + /* wake up the waiting task */ + sys_sem_signal(op_completed_sem); + } + } 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! */ + /* remove all references to this netconn from the pcb */ + struct tcp_pcb* pcb = newconn->pcb.tcp; + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_sent(pcb, NULL); + tcp_poll(pcb, NULL, 4); + tcp_err(pcb, NULL); + /* remove reference from to the pcb from this netconn */ + 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 lwip_netconn_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) { + 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) { +#if LWIP_UDPLITE + if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) { + udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE); + } +#endif /* LWIP_UDPLITE */ + if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) { + 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) { + setup_tcp(msg->conn); + } + break; +#endif /* LWIP_TCP */ + default: + /* Unsupported netconn type, e.g. protocol disabled */ + msg->err = ERR_VAL; + return; + } + if (msg->conn->pcb.ip == NULL) { + msg->err = ERR_MEM; + } +#if LWIP_IPV6 + else { + if (NETCONNTYPE_ISIPV6(msg->conn->type)) { + ip_set_v6(msg->conn->pcb.ip, 1); + } + } +#endif /* LWIP_IPV6 */ +} + +/** + * 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 +lwip_netconn_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 all sizes are the same, every compiler should optimize this switch to nothing */ + 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); + goto free_and_return; + } + + if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) { + goto free_and_return; + } +#if !LWIP_NETCONN_SEM_PER_THREAD + if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) { + sys_mbox_free(&conn->recvmbox); + goto free_and_return; + } +#endif + +#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_SNDTIMEO + conn->send_timeout = 0; +#endif /* LWIP_SO_SNDTIMEO */ +#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; +free_and_return: + memp_free(MEMP_NETCONN, conn); + return NULL; +} + +/** + * 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 */ + +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(&conn->op_completed); + sys_sem_set_invalid(&conn->op_completed); +#endif + + 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 (NETCONNTYPE_GROUP(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 +lwip_netconn_do_close_internal(struct netconn *conn) +{ + err_t err; + u8_t shut, shut_rx, shut_tx, close; + struct tcp_pcb* tpcb = conn->pcb.tcp; + + LWIP_ASSERT("invalid conn", (conn != NULL)); + LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(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 + (also if RD or WR side was shut down before already) */ + if (shut == NETCONN_SHUT_RDWR) { + close = 1; + } else if (shut_rx && + ((tpcb->state == FIN_WAIT_1) || + (tpcb->state == FIN_WAIT_2) || + (tpcb->state == CLOSING))) { + close = 1; + } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) { + close = 1; + } else { + close = 0; + } + + /* Set back some callback pointers */ + if (close) { + tcp_arg(tpcb, NULL); + } + if (tpcb->state == LISTEN) { + tcp_accept(tpcb, NULL); + } else { + /* some callbacks have to be reset if tcp_close is not successful */ + if (shut_rx) { + tcp_recv(tpcb, NULL); + tcp_accept(tpcb, NULL); + } + if (shut_tx) { + tcp_sent(tpcb, NULL); + } + if (close) { + tcp_poll(tpcb, NULL, 4); + tcp_err(tpcb, NULL); + } + } + /* Try to close the connection */ + if (close) { + err = tcp_close(tpcb); + } else { + err = tcp_shutdown(tpcb, shut_rx, shut_tx); + } + if (err == ERR_OK) { + /* Closing succeeded */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + conn->current_msg->err = ERR_OK; + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (close) { + /* 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! */ + 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(op_completed_sem); + } 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 +lwip_netconn_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("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(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)); + msg->err = ERR_OK; + /* 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; + lwip_netconn_do_close_internal(msg->conn); + /* API_EVENT is called inside lwip_netconn_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(LWIP_API_MSG_SEM(msg))) { + sys_sem_signal(LWIP_API_MSG_SEM(msg)); + } +} + +/** + * 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 +lwip_netconn_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, API_EXPR_REF(msg->msg.bc.ipaddr)); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_bind(msg->conn->pcb.udp, API_EXPR_REF(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, API_EXPR_REF(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/lwip_netconn_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 +lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err) +{ + struct netconn *conn; + int was_blocking; + sys_sem_t* op_completed_sem = NULL; + + 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; + op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + } + if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) { + setup_tcp(conn); + } + was_blocking = !IN_NONBLOCKING_CONNECT(conn); + SET_NONBLOCKING_CONNECT(conn, 0); + LWIP_ASSERT("blocking connect state error", + (was_blocking && op_completed_sem != NULL) || + (!was_blocking && op_completed_sem == NULL)); + conn->current_msg = NULL; + conn->state = NETCONN_NONE; + if (!was_blocking) { + NETCONN_SET_SAFE_ERR(conn, ERR_OK); + } + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + + if (was_blocking) { + sys_sem_signal(op_completed_sem); + } + 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 +lwip_netconn_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; + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(LWIP_API_MSG_SEM(msg)); + return; + } + } else { + switch (NETCONNTYPE_GROUP(msg->conn->type)) { +#if LWIP_RAW + case NETCONN_RAW: + msg->err = raw_connect(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr)); + break; +#endif /* LWIP_RAW */ +#if LWIP_UDP + case NETCONN_UDP: + msg->err = udp_connect(msg->conn->pcb.udp, API_EXPR_REF(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, API_EXPR_REF(msg->msg.bc.ipaddr), + msg->msg.bc.port, lwip_netconn_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 lwip_netconn_do_connected (or err_tcp()), + * when the connection is established! */ + return; + } + } + } + /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */ + sys_sem_signal(LWIP_API_MSG_SEM(msg)); + return; +#endif /* LWIP_TCP */ + default: + LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0)); + break; + } + } + /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(), + so use TCPIP_APIMSG_ACK() here. */ + TCPIP_APIMSG_ACK(msg); +} + +/** + * 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 +lwip_netconn_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 +lwip_netconn_do_listen(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL_LISTENCONNECT(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) { + if (msg->conn->state == NETCONN_NONE) { + struct tcp_pcb* lpcb; + if (msg->conn->pcb.tcp->state != CLOSED) { + /* connection is not closed, cannot listen */ + msg->err = ERR_VAL; + } else { +#if LWIP_IPV6 + if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + lpcb = tcp_listen_dual(msg->conn->pcb.tcp); +#endif /* TCP_LISTEN_BACKLOG */ + } else +#endif /* LWIP_IPV6 */ + { +#if TCP_LISTEN_BACKLOG + lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog); +#else /* TCP_LISTEN_BACKLOG */ + 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; + } + } + } + } + } else { + msg->err = ERR_ARG; + } + } + } + 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 +lwip_netconn_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 (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, ipX_2_ip(&msg->msg.b->addr)); + } + break; +#endif +#if LWIP_UDP + case NETCONN_UDP: +#if LWIP_CHECKSUM_ON_COPY + if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, + ipX_2_ip(&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 (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &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, ipX_2_ip(&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 +lwip_netconn_do_recv(struct api_msg_msg *msg) +{ + msg->err = ERR_OK; + if (msg->conn->pcb.tcp != NULL) { + if (NETCONNTYPE_GROUP(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 lwip_netconn_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 +lwip_netconn_do_writemore(struct netconn *conn) +{ + err_t err; + void *dataptr; + u16_t len, available; + u8_t write_finished = 0; + size_t diff; + u8_t dontblock; + u8_t 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); + + dontblock = netconn_is_nonblocking(conn) || + (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK); + apiflags = conn->current_msg->msg.w.apiflags; + +#if LWIP_SO_SNDTIMEO + if ((conn->send_timeout != 0) && + ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) { + write_finished = 1; + if (conn->write_offset == 0) { + /* nothing has been written */ + err = ERR_WOULDBLOCK; + conn->current_msg->msg.w.len = 0; + } else { + /* partial write */ + err = ERR_OK; + conn->current_msg->msg.w.len = conn->write_offset; + conn->write_offset = 0; + } + } else +#endif /* LWIP_SO_SNDTIMEO */ + { + 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 (dontblock) { + if (!len) { + err = ERR_WOULDBLOCK; + goto err_mem; + } + } else { +#if LWIP_TCPIP_CORE_LOCKING + conn->flags |= NETCONN_FLAG_WRITE_DELAYED; +#endif + apiflags |= TCP_WRITE_FLAG_MORE; + } + } + LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len)); + err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags); + /* if OK or memory error, check available space */ + if ((err == ERR_OK) || (err == ERR_MEM)) { +err_mem: + if (dontblock && (len < conn->current_msg->msg.w.len)) { + /* non-blocking write did not write everything: mark the pcb non-writable + and let poll_tcp check writable space to mark the pcb writable again */ + API_EVENT(conn, NETCONN_EVT_SENDMINUS, len); + conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE; + } else if ((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) || dontblock) { + /* return sent length */ + conn->current_msg->msg.w.len = conn->write_offset; + /* everything was written */ + write_finished = 1; + conn->write_offset = 0; + } + tcp_output(conn->pcb.tcp); + } else if ((err == ERR_MEM) && !dontblock) { + /* 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; + conn->current_msg->msg.w.len = 0; + } + } + if (write_finished) { + /* everything was written: set back connection state + and back to application task */ + sys_sem_t* op_completed_sem = LWIP_API_MSG_SEM(conn->current_msg); + 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(op_completed_sem); + } + } +#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 +lwip_netconn_do_write(struct api_msg_msg *msg) +{ + if (ERR_IS_FATAL(msg->conn->last_err)) { + msg->err = msg->conn->last_err; + } else { + if (NETCONNTYPE_GROUP(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 lwip_netconn_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 (lwip_netconn_do_writemore(msg->conn) != ERR_OK) { + LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE); + UNLOCK_TCPIP_CORE(); + sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0); + LOCK_TCPIP_CORE(); + LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE); + } +#else /* LWIP_TCPIP_CORE_LOCKING */ + lwip_netconn_do_writemore(msg->conn); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG + since lwip_netconn_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 +lwip_netconn_do_getaddr(struct api_msg_msg *msg) +{ + if (msg->conn->pcb.ip != NULL) { + if (msg->msg.ad.local) { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), API_EXPR_DEREF(msg->msg.ad.ipaddr), + msg->conn->pcb.ip->local_ip); + } else { + ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), API_EXPR_DEREF(msg->msg.ad.ipaddr), + 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) { + API_EXPR_DEREF(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) { + API_EXPR_DEREF(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 { + API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port; + } + } + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case NETCONN_TCP: + if ((msg->msg.ad.local == 0) && + ((msg->conn->pcb.tcp->state == CLOSED) || (msg->conn->pcb.tcp->state == LISTEN))) { + /* pcb is not connected and remote name is requested */ + msg->err = ERR_CONN; + } else { + API_EXPR_DEREF(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 or half-shutdown a TCP pcb contained in a netconn + * Called from netconn_close + * In contrast to closing sockets, the netconn is not deallocated. + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_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("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP", + NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP); + msg->err = ERR_INPROGRESS; + } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(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; + lwip_netconn_do_close_internal(msg->conn); + /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */ + return; + } + } else +#endif /* LWIP_TCP */ + { + msg->err = ERR_CONN; + } + sys_sem_signal(LWIP_API_MSG_SEM(msg)); +} + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** + * Join multicast groups for UDP netconns. + * Called from netconn_join_leave_group + * + * @param msg the api_msg_msg pointing to the connection + */ +void +lwip_netconn_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 LWIP_IPV6 && LWIP_IPV6_MLD + if (PCB_ISIPV6(msg->conn->pcb.udp)) { + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mld6_joingroup(ipX_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), + ipX_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); + } else { + msg->err = mld6_leavegroup(ipX_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), + ipX_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); + } + } + else +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + { +#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = igmp_joingroup(ipX_2_ip(API_EXPR_REF(msg->msg.jl.netif_addr)), + ipX_2_ip(API_EXPR_REF(msg->msg.jl.multiaddr))); + } else { + msg->err = igmp_leavegroup(ipX_2_ip(API_EXPR_REF(msg->msg.jl.netif_addr)), + ipX_2_ip(API_EXPR_REF(msg->msg.jl.multiaddr))); + } +#endif /* LWIP_IGMP */ + } +#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 || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#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 +lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + /* we trust the internal implementation to be correct :-) */ + LWIP_UNUSED_ARG(name); + + if (ipaddr == NULL) { + /* timeout or memory error */ + API_EXPR_DEREF(msg->err) = ERR_VAL; + } else { + /* address was resolved */ + API_EXPR_DEREF(msg->err) = ERR_OK; + API_EXPR_DEREF(msg->addr) = *ipaddr; + } + /* wake up the application task waiting in netconn_gethostbyname */ + sys_sem_signal(API_EXPR_REF(msg->sem)); +} + +/** + * Execute a DNS query + * Called from netconn_gethostbyname + * + * @param arg the dns_api_msg pointing to the query + */ +void +lwip_netconn_do_gethostbyname(void *arg) +{ + struct dns_api_msg *msg = (struct dns_api_msg*)arg; + + API_EXPR_DEREF(msg->err) = dns_gethostbyname(msg->name, API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg); + if (API_EXPR_DEREF(msg->err) != ERR_INPROGRESS) { + /* on error or immediate success, wake up the application + * task waiting in netconn_gethostbyname */ + sys_sem_signal(API_EXPR_REF(msg->sem)); + } +} +#endif /* LWIP_DNS */ + +#endif /* LWIP_NETCONN */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/err.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/err.c new file mode 100644 index 0000000..e970101 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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 */ + "Address in use.", /* ERR_USE -8 */ + "Already connected.", /* ERR_ISCONN -9 */ + "Not connected.", /* ERR_CONN -10 */ + "Connection aborted.", /* ERR_ABRT -11 */ + "Connection reset.", /* ERR_RST -12 */ + "Connection closed.", /* ERR_CLSD -13 */ + "Illegal argument.", /* ERR_ARG -14 */ + "Low-level netif error.", /* ERR_IF -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/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netbuf.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netbuf.c new file mode 100644 index 0000000..4a417b0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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! + * + * @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; + ipX_addr_set_any(LWIP_IPV6, &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 + ipX_addr_set_any(LWIP_IPV6, &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(). + * + * @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. + * + * @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 + * + * @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 retrieved, + * 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/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netdb.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netdb.c new file mode 100644 index 0000000..ad93944 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netdb.c @@ -0,0 +1,349 @@ +/** + * @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 *addr_list[2]; + 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_aliases = NULL; + 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)); + /* h_aliases are always empty */ + 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 == NULL)) { + /* 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 = HOST_NOT_FOUND; + return -1; + } + + /* copy the hostname into buf */ + MEMCPY(hostname, name, namelen); + hostname[namelen] = 0; + + /* fill hostent */ + h->addr_list[0] = &h->addr; + h->addr_list[1] = NULL; + h->aliases = NULL; + ret->h_name = hostname; + ret->h_aliases = &h->aliases; + ret->h_addrtype = AF_INET; + ret->h_length = sizeof(ip_addr_t); + ret->h_addr_list = (char**)&h->addr_list; + + /* 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 (hints != NULL) { + if ((hints->ai_family != AF_UNSPEC) && (hints->ai_family != AF_INET)) { + return EAI_FAMILY; + } + } + + 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) { + return EAI_MEMORY; + } + 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; +} + +#endif /* LWIP_DNS && LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netifapi.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netifapi.c new file mode 100644 index 0000000..715d515 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/netifapi.c @@ -0,0 +1,204 @@ +/** + * @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" +#include "lwip/memp.h" + +#define NETIFAPI_VAR_REF(name) API_VAR_REF(name) +#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name) +#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name) +#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name) + +/** + * Call netif_add() inside the tcpip_thread context. + */ +static void +netifapi_do_netif_add(struct netifapi_msg_msg *msg) +{ + if (!netif_add( msg->netif, + API_EXPR_REF(msg->msg.add.ipaddr), + API_EXPR_REF(msg->msg.add.netmask), + API_EXPR_REF(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. + */ +static void +netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg) +{ + netif_set_addr( msg->netif, + API_EXPR_REF(msg->msg.add.ipaddr), + API_EXPR_REF(msg->msg.add.netmask), + API_EXPR_REF(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. + */ +static void +netifapi_do_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) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); +#if LWIP_MPU_COMPATIBLE + if (ipaddr == NULL) { + ipaddr = IP_ADDR_ANY; + } + if (netmask == NULL) { + netmask = IP_ADDR_ANY; + } + if (gw == NULL) { + gw = IP_ADDR_ANY; + } +#endif /* LWIP_MPU_COMPATIBLE */ + NETIFAPI_VAR_REF(msg).function = netifapi_do_netif_add; + NETIFAPI_VAR_REF(msg).msg.netif = netif; + NETIFAPI_VAR_REF(msg).msg.msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr); + NETIFAPI_VAR_REF(msg).msg.msg.add.netmask = NETIFAPI_VAR_REF(netmask); + NETIFAPI_VAR_REF(msg).msg.msg.add.gw = NETIFAPI_VAR_REF(gw); + NETIFAPI_VAR_REF(msg).msg.msg.add.state = state; + NETIFAPI_VAR_REF(msg).msg.msg.add.init = init; + NETIFAPI_VAR_REF(msg).msg.msg.add.input = input; + TCPIP_NETIFAPI(&API_VAR_REF(msg)); + + err = NETIFAPI_VAR_REF(msg).msg.err; + NETIFAPI_VAR_FREE(msg); + return 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) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); +#if LWIP_MPU_COMPATIBLE + if (ipaddr == NULL) { + ipaddr = IP_ADDR_ANY; + } + if (netmask == NULL) { + netmask = IP_ADDR_ANY; + } + if (gw == NULL) { + gw = IP_ADDR_ANY; + } +#endif /* LWIP_MPU_COMPATIBLE */ + NETIFAPI_VAR_REF(msg).function = netifapi_do_netif_set_addr; + NETIFAPI_VAR_REF(msg).msg.netif = netif; + NETIFAPI_VAR_REF(msg).msg.msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr); + NETIFAPI_VAR_REF(msg).msg.msg.add.netmask = NETIFAPI_VAR_REF(netmask); + NETIFAPI_VAR_REF(msg).msg.msg.add.gw = NETIFAPI_VAR_REF(gw); + TCPIP_NETIFAPI(&API_VAR_REF(msg)); + + err = NETIFAPI_VAR_REF(msg).msg.err; + NETIFAPI_VAR_FREE(msg); + return 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) +{ + err_t err; + NETIFAPI_VAR_DECLARE(msg); + NETIFAPI_VAR_ALLOC(msg); + + NETIFAPI_VAR_REF(msg).function = netifapi_do_netif_common; + NETIFAPI_VAR_REF(msg).msg.netif = netif; + NETIFAPI_VAR_REF(msg).msg.msg.common.voidfunc = voidfunc; + NETIFAPI_VAR_REF(msg).msg.msg.common.errtfunc = errtfunc; + TCPIP_NETIFAPI(&API_VAR_REF(msg)); + + err = NETIFAPI_VAR_REF(msg).msg.err; + NETIFAPI_VAR_FREE(msg); + return err; +} + +#endif /* LWIP_NETIF_API */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/pppapi.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/pppapi.c new file mode 100644 index 0000000..10e4b82 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/pppapi.c @@ -0,0 +1,439 @@ +/** + * @file + * Point To Point Protocol 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_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pppapi.h" +#include "lwip/tcpip.h" + +/** + * Call ppp_new() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_new(struct pppapi_msg_msg *msg) +{ + msg->ppp = ppp_new(); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_new() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +ppp_pcb* +pppapi_new(void) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_new; + TCPIP_PPPAPI(&msg); + return msg.msg.ppp; +} + + +/** + * Call ppp_set_default() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_default(struct pppapi_msg_msg *msg) +{ + ppp_set_default(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_default() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_default(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_default; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); +} + + +/** + * Call ppp_set_auth() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_auth(struct pppapi_msg_msg *msg) +{ + ppp_set_auth(msg->ppp, msg->msg.setauth.authtype, + msg->msg.setauth.user, msg->msg.setauth.passwd); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_auth() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_auth; + msg.msg.ppp = pcb; + msg.msg.msg.setauth.authtype = authtype; + msg.msg.msg.setauth.user = user; + msg.msg.msg.setauth.passwd = passwd; + TCPIP_PPPAPI(&msg); +} + + +#if PPP_NOTIFY_PHASE +/** + * Call ppp_set_notify_phase_callback() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_notify_phase_callback(struct pppapi_msg_msg *msg) +{ + ppp_set_notify_phase_callback(msg->ppp, msg->msg.setnotifyphasecb.notify_phase_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_notify_phase_callback; + msg.msg.ppp = pcb; + msg.msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb; + TCPIP_PPPAPI(&msg); +} +#endif /* PPP_NOTIFY_PHASE */ + + +#if PPPOS_SUPPORT +/** + * Call ppp_over_serial_create() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_over_serial_create(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_over_serial_create(msg->ppp, msg->msg.serialcreate.fd, + msg->msg.serialcreate.link_status_cb, msg->msg.serialcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_over_serial_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_over_serial_create(ppp_pcb *pcb, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_over_serial_create; + msg.msg.ppp = pcb; + msg.msg.msg.serialcreate.fd = fd; + msg.msg.msg.serialcreate.link_status_cb = link_status_cb; + msg.msg.msg.serialcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} +#endif /* PPPOS_SUPPORT */ + + +#if PPPOE_SUPPORT +/** + * Call ppp_over_ethernet_create() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_over_ethernet_create(struct pppapi_msg_msg *msg) +{ + + msg->err = ppp_over_ethernet_create(msg->ppp, msg->msg.ethernetcreate.ethif, + msg->msg.ethernetcreate.service_name, msg->msg.ethernetcreate.concentrator_name, + msg->msg.ethernetcreate.link_status_cb, msg->msg.ethernetcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_over_ethernet_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_over_ethernet_create(ppp_pcb *pcb, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_over_ethernet_create; + msg.msg.ppp = pcb; + msg.msg.msg.ethernetcreate.ethif = ethif; + msg.msg.msg.ethernetcreate.service_name = service_name; + msg.msg.msg.ethernetcreate.concentrator_name = concentrator_name; + msg.msg.msg.ethernetcreate.link_status_cb = link_status_cb; + msg.msg.msg.ethernetcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} +#endif /* PPPOE_SUPPORT */ + + +#if PPPOL2TP_SUPPORT +/** + * Call ppp_over_l2tp_create() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_over_l2tp_create(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_over_l2tp_create(msg->ppp, + msg->msg.l2tpcreate.netif, msg->msg.l2tpcreate.ipaddr, msg->msg.l2tpcreate.port, +#if PPPOL2TP_AUTH_SUPPORT + msg->msg.l2tpcreate.secret, + msg->msg.l2tpcreate.secret_len, +#else /* PPPOL2TP_AUTH_SUPPORT */ + NULL, +#endif /* PPPOL2TP_AUTH_SUPPORT */ + msg->msg.l2tpcreate.link_status_cb, msg->msg.l2tpcreate.ctx_cb); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_over_l2tp_create() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_over_l2tp_create(ppp_pcb *pcb, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_over_l2tp_create; + msg.msg.ppp = pcb; + msg.msg.msg.l2tpcreate.netif = netif; + msg.msg.msg.l2tpcreate.ipaddr = ipaddr; + msg.msg.msg.l2tpcreate.port = port; +#if PPPOL2TP_AUTH_SUPPORT + msg.msg.msg.l2tpcreate.secret = secret; + msg.msg.msg.l2tpcreate.secret_len = secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + msg.msg.msg.l2tpcreate.link_status_cb = link_status_cb; + msg.msg.msg.l2tpcreate.ctx_cb = ctx_cb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} +#endif /* PPPOL2TP_SUPPORT */ + + +/** + * Call ppp_open() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_open(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_open(msg->ppp, msg->msg.open.holdoff); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_open() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_open(ppp_pcb *pcb, u16_t holdoff) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_open; + msg.msg.ppp = pcb; + msg.msg.msg.open.holdoff = holdoff; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +/** + * Call ppp_close() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_close(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_close(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_close() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_close(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_close; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +/** + * Call ppp_sighup() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_sighup(struct pppapi_msg_msg *msg) +{ + ppp_sighup(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_sighup() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_sighup(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_sighup; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); +} + + +/** + * Call ppp_delete() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_delete(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_delete(msg->ppp); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_delete() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_delete(ppp_pcb *pcb) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_delete; + msg.msg.ppp = pcb; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +/** + * Call ppp_ioctl() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_ioctl(struct pppapi_msg_msg *msg) +{ + msg->err = ppp_ioctl(msg->ppp, msg->msg.ioctl.cmd, msg->msg.ioctl.arg); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_ioctl() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +int +pppapi_ioctl(ppp_pcb *pcb, int cmd, void *arg) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_ioctl; + msg.msg.ppp = pcb; + msg.msg.msg.ioctl.cmd = cmd; + msg.msg.msg.ioctl.arg = arg; + TCPIP_PPPAPI(&msg); + return msg.msg.err; +} + + +#if LWIP_NETIF_STATUS_CALLBACK +/** + * Call ppp_set_netif_statuscallback() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_netif_statuscallback(struct pppapi_msg_msg *msg) +{ + ppp_set_netif_statuscallback(msg->ppp, msg->msg.netifstatuscallback.status_callback); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_netif_statuscallback() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_netif_statuscallback(ppp_pcb *pcb, netif_status_callback_fn status_callback) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_netif_statuscallback; + msg.msg.ppp = pcb; + msg.msg.msg.netifstatuscallback.status_callback = status_callback; + TCPIP_PPPAPI(&msg); +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + + +#if LWIP_NETIF_LINK_CALLBACK +/** + * Call ppp_set_netif_linkcallback() inside the tcpip_thread context. + */ +static void +pppapi_do_ppp_set_netif_linkcallback(struct pppapi_msg_msg *msg) +{ + ppp_set_netif_linkcallback(msg->ppp, msg->msg.netiflinkcallback.link_callback); + TCPIP_PPPAPI_ACK(msg); +} + +/** + * Call ppp_set_netif_linkcallback() in a thread-safe way by running that function inside the + * tcpip_thread context. + */ +void +pppapi_set_netif_linkcallback(ppp_pcb *pcb, netif_status_callback_fn link_callback) +{ + struct pppapi_msg msg; + msg.function = pppapi_do_ppp_set_netif_linkcallback; + msg.msg.ppp = pcb; + msg.msg.msg.netiflinkcallback.link_callback = link_callback; + TCPIP_PPPAPI(&msg); +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* LWIP_PPP_API */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/sockets.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/sockets.c new file mode 100644 index 0000000..60a8367 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/sockets.c @@ -0,0 +1,2480 @@ +/** + * @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/arch.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/memp.h" +#include "lwip/pbuf.h" +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +/* If the netconn API is not required publicly, then we include the necessary + files here to get the implementation */ +#if !LWIP_NETCONN +#undef LWIP_NETCONN +#define LWIP_NETCONN 1 +#include "api_msg.c" +#include "api_lib.c" +#include "netbuf.c" +#undef LWIP_NETCONN +#define LWIP_NETCONN 0 +#endif + +#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \ + (sin)->sin_len = sizeof(struct sockaddr_in); \ + (sin)->sin_family = AF_INET; \ + (sin)->sin_port = htons((port)); \ + inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \ + memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0) +#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \ + inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \ + (port) = ntohs((sin)->sin_port); }while(0) + +#if LWIP_IPV6 +#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \ + ((namelen) == sizeof(struct sockaddr_in6))) +#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \ + ((name)->sa_family == AF_INET6)) +#define SOCK_ADDR_TYPE_MATCH(name, sock) \ + ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \ + (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type)))) +#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \ + (sin6)->sin6_len = sizeof(struct sockaddr_in6); \ + (sin6)->sin6_family = AF_INET6; \ + (sin6)->sin6_port = htons((port)); \ + (sin6)->sin6_flowinfo = 0; \ + inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0) +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \ + inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \ + (port) = ntohs((sin6)->sin6_port); }while(0) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \ + if (isipv6) { \ + SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \ + } else { \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \ + } } while(0) +#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \ + (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6)) +#else /* LWIP_IPV6 */ +#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in)) +#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET) +#define SOCK_ADDR_TYPE_MATCH(name, sock) 1 +#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \ + IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \ + SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port) +#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type) +#endif /* LWIP_IPV6 */ + +#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \ + IS_SOCK_ADDR_TYPE_VALID(name)) +#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \ + SOCK_ADDR_TYPE_MATCH(name, sock)) +#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0) + + +#define LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype) do { if((optlen) < sizeof(opttype)) { return EINVAL; }}while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if ((sock)->conn == NULL) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN(optlen, opttype); \ + if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { return EINVAL; } }while(0) +#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, opttype, netconntype) do { \ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype); \ + if (NETCONNTYPE_GROUP(netconn_type((sock)->conn)) != netconntype) { return ENOPROTOOPT; } }while(0) + + +#define LWIP_SETGETSOCKOPT_DATA_VAR_REF(name) API_VAR_REF(name) +#define LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_setgetsockopt_data, name) +#define LWIP_SETGETSOCKOPT_DATA_VAR_FREE(name) API_VAR_FREE(MEMP_SOCKET_SETGETSOCKOPT_DATA, name) +#if LWIP_MPU_COMPATIBLE +#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) do { \ + name = (struct lwip_setgetsockopt_data *)memp_malloc(MEMP_SOCKET_SETGETSOCKOPT_DATA); \ + if (name == NULL) { \ + sock_set_errno(sock, ENOMEM); \ + return -1; \ + } }while(0) +#else /* LWIP_MPU_COMPATIBLE */ +#define LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(name, sock) +#endif /* LWIP_MPU_COMPATIBLE */ + +#if LWIP_SO_SNDRCVTIMEO_NONSTANDARD +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE int +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) (*(int *)(optval) = (val)) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((s32_t)*(int*)(optval)) +#else +#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval +#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \ + s32_t loc = (val); \ + ((struct timeval *)(optval))->tv_sec = (loc) / 1000U; \ + ((struct timeval *)(optval))->tv_sec = ((loc) % 1000U) * 1000U; }while(0) +#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((struct timeval *)(optval))->tv_sec * 1000U) + (((struct timeval *)(optval))->tv_usec / 1000U)) +#endif + +#define NUM_SOCKETS MEMP_NUM_NETCONN + +/** This is overridable for the rare case where more than 255 threads + * select on the same socket... + */ +#ifndef SELWAIT_T +#define SELWAIT_T u8_t +#endif + +/** 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 (in fact, all our errnos fit into an u8_t) */ + u8_t err; + /** counter of how many threads are waiting for this socket using select */ + SELWAIT_T select_waiting; +}; + +#if LWIP_NETCONN_SEM_PER_THREAD +#define SELECT_SEM_T sys_sem_t* +#define SELECT_SEM_PTR(sem) (sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define SELECT_SEM_T sys_sem_t +#define SELECT_SEM_PTR(sem) (&(sem)) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +/** 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 */ + SELECT_SEM_T sem; +}; + +/** A struct sockaddr replacement that has the same alignment as sockaddr_in/ + * sockaddr_in6 if instantiated. + */ +union sockaddr_aligned { + struct sockaddr sa; +#if LWIP_IPV6 + struct sockaddr_in6 sin6; +#endif /* LWIP_IPV6 */ + struct sockaddr_in sin; +}; + + +/** 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. */ + EADDRINUSE, /* ERR_USE -8 Address in use. */ + EALREADY, /* ERR_ISCONN -9 Already connected. */ + ENOTCONN, /* ERR_CONN -10 Not connected. */ + ECONNABORTED, /* ERR_ABRT -11 Connection aborted. */ + ECONNRESET, /* ERR_RST -12 Connection reset. */ + ENOTCONN, /* ERR_CLSD -13 Connection closed. */ + EIO, /* ERR_ARG -14 Illegal argument. */ + -1, /* ERR_IF -15 Low-level netif error */ +}; + +#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) + +#if LWIP_SOCKET_SET_ERRNO +#ifndef set_errno +#define set_errno(err) do { if (err) { errno = (err); } } while(0) +#endif +#else /* LWIP_SOCKET_SET_ERRNO */ +#define set_errno(err) +#endif /* LWIP_SOCKET_SET_ERRNO */ + +#define sock_set_errno(sk, e) do { \ + sk->err = (e); \ + set_errno(sk->err); \ +} while (0) + +/* Forward declaration of some functions */ +static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len); +#if !LWIP_TCPIP_CORE_LOCKING +static void lwip_getsockopt_callback(void *arg); +static void lwip_setsockopt_callback(void *arg); +#endif +static u8_t lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen); +static u8_t lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen); + +/** + * 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 = (NETCONNTYPE_GROUP(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; + ipX_addr_t naddr; + u16_t port = 0; + int newsock; + 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)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + 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); + + /* Note that POSIX only requires us to check addr is non-NULL. addrlen must + * not be NULL if addr is valid. + */ + if (addr != NULL) { + union sockaddr_aligned tempaddr; + /* get the IP address and port of the remote host */ + err = netconn_peer(newconn, ipX_2_ip(&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; + } + LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); + + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port); + if (*addrlen > tempaddr.sa.sa_len) { + *addrlen = tempaddr.sa.sa_len; + } + MEMCPY(addr, &tempaddr, *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", s, newsock)); + if (addr != NULL) { + LWIP_DEBUGF(SOCKETS_DEBUG, (" addr=")); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), 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; + ipX_addr_t local_addr; + u16_t local_port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* check size, family and alignment of 'name' */ + LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(namelen); + + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port)); + + err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), 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 = NETCONNTYPE_GROUP(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; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + LWIP_UNUSED_ARG(namelen); + if (name->sa_family == AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + ipX_addr_t remote_addr; + u16_t remote_port; + + /* check size, family and alignment of 'name' */ + LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) && + IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + + SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s)); + ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port)); + + err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), 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)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return -1; + } + 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; + 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 (NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(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) { +#if !SOCKETS_DEBUG + if (from && fromlen) +#endif /* !SOCKETS_DEBUG */ + { + u16_t port; + ipX_addr_t tmpaddr; + ipX_addr_t *fromaddr; + union sockaddr_aligned saddr; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s)); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + fromaddr = &tmpaddr; + /* @todo: this does not work for IPv6, yet */ + netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0); + } else { + port = netbuf_fromport((struct netbuf *)buf); + fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf); + } + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, fromaddr, port); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, fromaddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off)); +#if SOCKETS_DEBUG + if (from && fromlen) +#endif /* SOCKETS_DEBUG */ + { + if (*fromlen > saddr.sa.sa_len) { + *fromlen = saddr.sa.sa_len; + } + MEMCPY(from, &saddr, *fromlen); + } + } + } + + /* 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 ((NETCONNTYPE_GROUP(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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) { + pbuf_free((struct pbuf *)buf); + } else { + netbuf_delete((struct netbuf *)buf); + } + } + } + } while (!done); + + if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) { + /* 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; + size_t written; + + 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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) */ + } + + write_flags = NETCONN_COPY | + ((flags & MSG_MORE) ? NETCONN_MORE : 0) | + ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0); + written = 0; + err = netconn_write_partly(sock->conn, data, size, write_flags, &written); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written)); + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? (int)written : -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; + u16_t remote_port; +#if !LWIP_TCPIP_CORE_LOCKING + struct netbuf buf; +#endif + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == 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 */ + } + + if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) { + /* sockaddr does not match socket type (IPv4/IPv6) */ + sock_set_errno(sock, err_to_errno(ERR_VAL)); + return -1; + } + + /* @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)) || + (IS_SOCK_ADDR_LEN_VALID(tolen) && + IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))), + sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;); + LWIP_UNUSED_ARG(tolen); + +#if LWIP_TCPIP_CORE_LOCKING + /* Special speedup for fast UDP/RAW sending: call the raw API directly + instead of using the netconn functions. */ + { + struct pbuf* p; + ipX_addr_t *remote_addr; + ipX_addr_t remote_addr_tmp; + +#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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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 != NULL) { + SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6, + to, &remote_addr_tmp, remote_port); + remote_addr = &remote_addr_tmp; + } else { + remote_addr = &sock->conn->pcb.ip->remote_ip; +#if LWIP_UDP + if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) { + remote_port = sock->conn->pcb.udp->remote_port; + } else +#endif /* LWIP_UDP */ + { + remote_port = 0; + } + } + + LOCK_TCPIP_CORE(); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { +#if LWIP_RAW + err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr)); +#else /* LWIP_RAW */ + err = ERR_ARG; +#endif /* LWIP_RAW */ + } +#if LWIP_UDP && LWIP_RAW + else +#endif /* LWIP_UDP && LWIP_RAW */ + { +#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, + ipX_2_ip(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, + ipX_2_ip(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) { + SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port); + } else { + remote_port = 0; + ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr); + } + netbuf_fromport(&buf) = remote_port; + + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=", + s, data, short_size, flags)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != 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; + +#if !LWIP_IPV6 + LWIP_UNUSED_ARG(domain); /* @todo: check this */ +#endif /* LWIP_IPV6 */ + + /* create a netconn */ + switch (type) { + case SOCK_RAW: + conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, 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(DOMAIN_TO_NETCONN_TYPE(domain, + ((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(DOMAIN_TO_NETCONN_TYPE(domain, 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; + int i; + int maxfdp2; + 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; +#if LWIP_NETCONN_SEM_PER_THREAD + select_cb.sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ + if (sys_sem_new(&select_cb.sem, 0) != ERR_OK) { + /* failed to create semaphore */ + set_errno(ENOMEM); + return -1; + } +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + /* 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 */ + maxfdp2 = maxfdp1; + 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; + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + sock->select_waiting++; + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + } else { + /* Not a valid socket */ + nready = -1; + maxfdp2 = i; + SYS_ARCH_UNPROTECT(lev); + break; + } + SYS_ARCH_UNPROTECT(lev); + } + } + + if (nready >= 0) { + /* Call lwip_selscan again: there could have been events between + the last scan (without 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_SEM_PTR(select_cb.sem), msectimeout); + } + } + + /* Decrease select_waiting for each socket we are interested in */ + for (i = 0; i < maxfdp2; i++) { + if ((readset && FD_ISSET(i, readset)) || + (writeset && FD_ISSET(i, writeset)) || + (exceptset && FD_ISSET(i, exceptset))) { + struct lwip_sock *sock; + SYS_ARCH_PROTECT(lev); + sock = tryget_socket(i); + if (sock != NULL) { + /* @todo: what if this is a new socket (reallocated?) in this case, + select_waiting-- would be wrong (a global 'sockalloc' counter, + stored per socket could help) */ + LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0); + if (sock->select_waiting > 0) { + sock->select_waiting--; + } + } else { + /* Not a valid socket */ + nready = -1; + } + 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); + +#if !LWIP_NETCONN_SEM_PER_THREAD + sys_sem_free(&select_cb.sem); +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + if (nready < 0) { + /* This happens when a socket got closed while waiting */ + set_errno(EBADF); + return -1; + } + + 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) { + /* remember the state of select_cb_list to detect changes */ + last_select_cb_ctr = select_cb_ctr; + 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, invalidating the semaphore. */ + sys_sem_signal(SELECT_SEM_PTR(scb->sem)); + } + } + /* unlock interrupts with each step */ + 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return -1; + } + } else { + sock_set_errno(sock, ENOTCONN); + return -1; + } + + 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 -1; + } + 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; + union sockaddr_aligned saddr; + ipX_addr_t naddr; + u16_t port; + err_t err; + + sock = get_socket(s); + if (!sock) { + return -1; + } + + /* get the IP address and port */ + /* @todo: this does not work for IPv6, yet */ + err = netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local); + if (err != ERR_OK) { + sock_set_errno(sock, err_to_errno(err)); + return -1; + } + IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + &saddr, &naddr, port); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s)); + ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), + SOCKETS_DEBUG, &naddr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port)); + + if (*namelen > saddr.sa.sa_len) { + *namelen = saddr.sa.sa_len; + } + MEMCPY(name, &saddr, *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) +{ + u8_t err; + struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING + LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + + if (!sock) { + return -1; + } + + if ((NULL == optval) || (NULL == optlen)) { + sock_set_errno(sock, EFAULT); + return -1; + } + +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_getsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); + +#else /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (*optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); + return -1; + } +#endif /* LWIP_MPU_COMPATIBLE */ + + LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = *optlen; +#if !LWIP_MPU_COMPATIBLE + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = optval; +#endif /* !LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif + tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); + + /* write back optlen and optval */ + *optlen = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen; +#if LWIP_MPU_COMPATIBLE + memcpy(optval, LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen); +#endif /* LWIP_MPU_COMPATIBLE */ + + /* maybe lwip_getsockopt_internal has changed err */ + err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_getsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ +static void +lwip_getsockopt_callback(void *arg) +{ + struct lwip_setgetsockopt_data *data; + LWIP_ASSERT("arg != NULL", arg != NULL); + data = (struct lwip_setgetsockopt_data*)arg; + + data->err = lwip_getsockopt_impl(data->s, data->level, data->optname, data->optval, + &data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_getsockopt_impl: the actual implementation of getsockopt: + * same argument as lwip_getsockopt, either called directly or through callback + */ +static u8_t +lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + case SO_ACCEPTCONN: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) { + return ENOPROTOOPT; + } + if ((sock->conn->pcb.tcp != NULL) && (sock->conn->pcb.tcp->state == LISTEN)) { + *(int*)optval = 1; + } else { + *(int*)optval = 0; + } + break; + + /* The option flags */ + case SO_BROADCAST: + case SO_KEEPALIVE: +#if SO_REUSE + case SO_REUSEADDR: +#endif /* SO_REUSE */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(int*)optval = ip_get_option(sock->conn->pcb.ip, 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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) { + 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 = netconn_type(sock->conn); + LWIP_DEBUGF(SOCKETS_DEBUG, + ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n", + s, *(int *)optval)); + } /* switch (netconn_type(sock->conn)) */ + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n", + s, *(int *)optval)); + break; + + case SO_ERROR: + LWIP_SOCKOPT_CHECK_OPTLEN(*optlen, int); + /* only overwrite ERR_OK or temporary errors */ + if (((sock->err == 0) || (sock->err == EINPROGRESS)) && (sock->conn != NULL)) { + 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_SNDTIMEO + case SO_SNDTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_sendtimeout(sock->conn)); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + LWIP_SO_SNDRCVTIMEO_SET(optval, netconn_get_recvtimeout(sock->conn)); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + *(int *)optval = netconn_get_recvbufsize(sock->conn); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ + *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0; + break; +#endif /* LWIP_UDP*/ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + *(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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } + *(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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, struct in_addr); + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { + return ENOPROTOOPT; + } + 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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); + 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_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_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_TCP, 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_TCP, 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_TCP, 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_TCP, TCP_KEEPCNT) = %d\n", + s, *(int *)optval)); + break; +#endif /* LWIP_TCP_KEEPALIVE */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP */ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, *optlen, int); + /* @todo: this does not work for datagram sockets, yet */ + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + return ENOPROTOOPT; + } + *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0); + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n", + s, *(int *)optval)); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } + 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_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, *optlen, int, NETCONN_RAW); + if (sock->conn->pcb.raw->chksum_reqd == 0) { + *(int *)optval = -1; + } else { + *(int *)optval = sock->conn->pcb.raw->chksum_offset; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM) = %d\n", + s, (*(int*)optval)) ); + break; +#endif /* LWIP_IPV6 */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + break; + } /* switch (level) */ + + return err; +} + +int +lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = get_socket(s); +#if !LWIP_TCPIP_CORE_LOCKING + LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data); +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + + if (!sock) { + return -1; + } + + if (NULL == optval) { + sock_set_errno(sock, EFAULT); + return -1; + } + +#if LWIP_TCPIP_CORE_LOCKING + /* core-locking can just call the -impl function */ + LOCK_TCPIP_CORE(); + err = lwip_setsockopt_impl(s, level, optname, optval, optlen); + UNLOCK_TCPIP_CORE(); + +#else /* LWIP_TCPIP_CORE_LOCKING */ + +#if LWIP_MPU_COMPATIBLE + /* MPU_COMPATIBLE copies the optval data, so check for max size here */ + if (optlen > LWIP_SETGETSOCKOPT_MAXOPTLEN) { + sock_set_errno(sock, ENOBUFS); + return -1; + } +#endif /* LWIP_MPU_COMPATIBLE */ + + LWIP_SETGETSOCKOPT_DATA_VAR_ALLOC(data, sock); + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).s = s; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).level = level; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optname = optname; + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optlen = optlen; +#if LWIP_MPU_COMPATIBLE + memcpy(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval, optval, optlen); +#else /* LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).optval = (void*)optval; +#endif /* LWIP_MPU_COMPATIBLE */ + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err = 0; +#if LWIP_NETCONN_SEM_PER_THREAD + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); +#else + LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem = &sock->conn->op_completed; +#endif + tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data)); + sys_arch_sem_wait((sys_sem_t*)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0); + + /* maybe lwip_getsockopt_internal has changed err */ + err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err; + LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data); +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + sock_set_errno(sock, err); + return err ? -1 : 0; +} + +#if !LWIP_TCPIP_CORE_LOCKING +/** lwip_setsockopt_callback: only used without CORE_LOCKING + * to get into the tcpip_thread + */ +static void +lwip_setsockopt_callback(void *arg) +{ + struct lwip_setgetsockopt_data *data; + LWIP_ASSERT("arg != NULL", arg != NULL); + data = (struct lwip_setgetsockopt_data*)arg; + + data->err = lwip_setsockopt_impl(data->s, data->level, data->optname, data->optval, + data->optlen); + + sys_sem_signal((sys_sem_t*)(data->completed_sem)); +} +#endif /* LWIP_TCPIP_CORE_LOCKING */ + +/** lwip_setsockopt_impl: the actual implementation of setsockopt: + * same argument as lwip_setsockopt, either called directly or through callback + */ +static u8_t +lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen) +{ + u8_t err = 0; + struct lwip_sock *sock = tryget_socket(s); + if (!sock) { + return EBADF; + } + + switch (level) { + +/* Level: SOL_SOCKET */ + case SOL_SOCKET: + switch (optname) { + + /* SO_ACCEPTCONN is get-only */ + + /* The option flags */ + case SO_BROADCAST: + case SO_KEEPALIVE: +#if SO_REUSE + case SO_REUSEADDR: +#endif /* SO_REUSE */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + if (*(int*)optval) { + ip_set_option(sock->conn->pcb.ip, optname); + } else { + ip_reset_option(sock->conn->pcb.ip, optname); + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", + s, optname, (*(int*)optval?"on":"off"))); + break; + + /* SO_TYPE is get-only */ + /* SO_ERROR is get-only */ + +#if LWIP_SO_SNDTIMEO + case SO_SNDTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_sendtimeout(sock->conn, LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); + break; +#endif /* LWIP_SO_SNDTIMEO */ +#if LWIP_SO_RCVTIMEO + case SO_RCVTIMEO: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE); + netconn_set_recvtimeout(sock->conn, (int)LWIP_SO_SNDRCVTIMEO_GET_MS(optval)); + break; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVBUF + case SO_RCVBUF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, int); + netconn_set_recvbufsize(sock->conn, *(int*)optval); + break; +#endif /* LWIP_SO_RCVBUF */ +#if LWIP_UDP + case SO_NO_CHECK: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP); +#if LWIP_UDPLITE + if ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0) { + /* this flag is only available for UDP, not for UDP lite */ + return EAFNOSUPPORT; + } +#endif /* LWIP_UDPLITE */ + 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_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +/* Level: IPPROTO_IP */ + case IPPROTO_IP: + switch (optname) { + case IP_TTL: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + 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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + 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: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); + sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval); + break; + case IP_MULTICAST_IF: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct in_addr, NETCONN_UDP); + inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval); + break; + case IP_MULTICAST_LOOP: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); + 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. */ + /* @todo: assign membership to this socket so that it is dropped when closing the socket */ + err_t igmp_err; + struct ip_mreq *imr = (struct ip_mreq *)optval; + ip_addr_t if_addr; + ip_addr_t multi_addr; + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP); + inet_addr_to_ipaddr(&if_addr, &imr->imr_interface); + inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr); + if(optname == IP_ADD_MEMBERSHIP){ + igmp_err = igmp_joingroup(&if_addr, &multi_addr); + } else { + igmp_err = igmp_leavegroup(&if_addr, &multi_addr); + } + if (igmp_err != ERR_OK) { + err = EADDRNOTAVAIL; + } + } + break; +#endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + +#if LWIP_TCP +/* Level: IPPROTO_TCP */ + case IPPROTO_TCP: + /* Special case: all IPPROTO_TCP option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_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_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_TCP*/ + +#if LWIP_IPV6 +/* Level: IPPROTO_IPV6 */ + case IPPROTO_IPV6: + switch (optname) { + case IPV6_V6ONLY: + /* @todo: this does not work for datagram sockets, yet */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_TCP); + if (*(int*)optval) { + sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY; + } else { + sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n", + s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0))); + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_IPV6 */ + +#if LWIP_UDP && LWIP_UDPLITE + /* Level: IPPROTO_UDPLITE */ + case IPPROTO_UDPLITE: + /* Special case: all IPPROTO_UDPLITE option take an int */ + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int); + /* If this is no UDP lite socket, ignore any options. */ + if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) { + return ENOPROTOOPT; + } + 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_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; +#endif /* LWIP_UDP */ + /* Level: IPPROTO_RAW */ + case IPPROTO_RAW: + switch (optname) { +#if LWIP_IPV6 + case IPV6_CHECKSUM: + LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_RAW); + if (*(int *)optval < 0) { + sock->conn->pcb.raw->chksum_reqd = 0; + } else if (*(int *)optval & 1) { + /* Per RFC3542, odd offsets are not allowed */ + return EINVAL; + } else { + sock->conn->pcb.raw->chksum_reqd = 1; + sock->conn->pcb.raw->chksum_offset = *(int *)optval; + } + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, IPV6_CHECKSUM, ..) -> %d\n", + s, sock->conn->pcb.raw->chksum_reqd)); + break; +#endif /* LWIP_IPV6 */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_RAW, UNIMPL: optname=0x%x, ..)\n", + s, optname)); + err = ENOPROTOOPT; + break; + } /* switch (optname) */ + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", + s, level, optname)); + err = ENOPROTOOPT; + break; + } /* switch (level) */ + + return err; +} + +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; + int recv_avail; +#endif /* LWIP_SO_RCVBUF */ + + if (!sock) { + return -1; + } + + switch (cmd) { +#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE + case FIONREAD: + if (!argp) { + sock_set_errno(sock, EINVAL); + return -1; + } +#if LWIP_FIONREAD_LINUXMODE + if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + struct pbuf *p; + if (sock->lastdata) { + p = ((struct netbuf *)sock->lastdata)->p; + } else { + struct netbuf *rxbuf; + err_t err; + if (sock->rcvevent <= 0) { + *((u16_t*)argp) = 0; + } else { + err = netconn_recv(sock->conn, &rxbuf); + if (err != ERR_OK) { + *((u16_t*)argp) = 0; + } else { + sock->lastdata = rxbuf; + *((u16_t*)argp) = rxbuf->p->tot_len; + } + } + } + return 0; + } +#endif /* LWIP_FIONREAD_LINUXMODE */ + +#if LWIP_SO_RCVBUF + /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */ + SYS_ARCH_GET(sock->conn->recv_avail, recv_avail); + if (recv_avail < 0) { + recv_avail = 0; + } + *((int*)argp) = 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 (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) { + p = ((struct netbuf *)p)->p; + } + buflen = p->tot_len; + buflen -= sock->lastoffset; + + *((int*)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; +#else /* LWIP_SO_RCVBUF */ + break; +#endif /* LWIP_SO_RCVBUF */ +#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */ + + case (long)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: + break; + } /* switch (cmd) */ + 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; +} + +/** 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) { + return -1; + } + + switch (cmd) { + case F_GETFL: + ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0; + sock_set_errno(sock, 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; + sock_set_errno(sock, 0); + } else { + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + } + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val)); + sock_set_errno(sock, ENOSYS); /* not yet implemented */ + break; + } + return ret; +} + +#endif /* LWIP_SOCKET */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/api/tcpip.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/tcpip.c new file mode 100644 index 0000000..43ef3bd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/api/tcpip.c @@ -0,0 +1,571 @@ +/** + * @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 "lwip/lwip_ip.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" + +#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name) +#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name) +#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name) +#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name) + +/* 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(); + if (msg == NULL) { + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n")); + LWIP_ASSERT("tcpip_thread: invalid message", 0); + continue; + } + switch (msg->type) { +#if LWIP_NETCONN || LWIP_SOCKET + case TCPIP_MSG_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 || LWIP_SOCKET */ + +#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)) { + ethernet_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_ETHERNET */ +#if LWIP_IPV6 + if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) { + ip6_input(msg->msg.inp.p, msg->msg.inp.netif); + } else +#endif /* LWIP_IPV6 */ + { + ip_input(msg->msg.inp.p, msg->msg.inp.netif); + } + 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 */ + +#if LWIP_PPP_API + case TCPIP_MSG_PPPAPI: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PPP API message %p\n", (void *)msg)); + msg->msg.pppapimsg->function(&(msg->msg.pppapimsg->msg)); + break; +#endif /* LWIP_PPP_API */ + +#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 */ + + 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; + + case TCPIP_MSG_CALLBACK_STATIC: + LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg)); + msg->msg.cb.function(msg->msg.cb.ctx); + break; + + 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)) { + return ERR_VAL; + } + 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; +#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 || LWIP_SOCKET +/** + * 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) +{ + TCPIP_MSG_VAR_DECLARE(msg); +#ifdef LWIP_DEBUG + /* catch functions that don't set err */ + apimsg->msg.err = ERR_VAL; +#endif + + if (sys_mbox_valid(&mbox)) { + TCPIP_MSG_VAR_ALLOC(msg); + TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API; + TCPIP_MSG_VAR_REF(msg).msg.apimsg = apimsg; +#if LWIP_NETCONN_SEM_PER_THREAD + apimsg->msg.op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET(); + LWIP_ASSERT("netconn semaphore not initialized", + apimsg->msg.op_completed_sem != SYS_SEM_NULL); +#endif + sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg)); + sys_arch_sem_wait(LWIP_API_MSG_SEM(&apimsg->msg), 0); + TCPIP_MSG_VAR_FREE(msg); + return apimsg->msg.err; + } + return ERR_VAL; +} + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#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) +{ + TCPIP_MSG_VAR_DECLARE(msg); + + if (sys_mbox_valid(&mbox)) { + err_t err; + TCPIP_MSG_VAR_ALLOC(msg); + + err = sys_sem_new(&netifapimsg->msg.sem, 0); + if (err != ERR_OK) { + netifapimsg->msg.err = err; + return err; + } + + TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_NETIFAPI; + TCPIP_MSG_VAR_REF(msg).msg.netifapimsg = netifapimsg; + sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg)); + sys_sem_wait(&netifapimsg->msg.sem); + sys_sem_free(&netifapimsg->msg.sem); + TCPIP_MSG_VAR_FREE(msg); + 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 */ + +#if LWIP_PPP_API +#if !LWIP_TCPIP_CORE_LOCKING +/** + * Much like tcpip_apimsg, but calls the lower part of a pppapi_* + * function. + * + * @param pppapimsg a struct containing the function to call and its parameters + * @return error code given back by the function that was called + */ +err_t +tcpip_pppapi(struct pppapi_msg* pppapimsg) +{ + struct tcpip_msg msg; + + if (sys_mbox_valid(&mbox)) { + err_t err = sys_sem_new(&pppapimsg->msg.sem, 0); + if (err != ERR_OK) { + pppapimsg->msg.err = err; + return err; + } + + msg.type = TCPIP_MSG_PPPAPI; + msg.msg.pppapimsg = pppapimsg; + sys_mbox_post(&mbox, &msg); + sys_sem_wait(&pppapimsg->msg.sem); + sys_sem_free(&pppapimsg->msg.sem); + return pppapimsg->msg.err; + } + return ERR_VAL; +} +#else /* !LWIP_TCPIP_CORE_LOCKING */ +/** + * Call the lower part of a pppapi_* function + * This function has exclusive access to lwIP core code by locking it + * before the function is called. + * + * @param pppapimsg a struct containing the function to call and its parameters + * @return ERR_OK (only for compatibility fo tcpip_pppapi()) + */ +err_t +tcpip_pppapi_lock(struct pppapi_msg* pppapimsg) +{ + LOCK_TCPIP_CORE(); + pppapimsg->function(&(pppapimsg->msg)); + UNLOCK_TCPIP_CORE(); + return pppapimsg->msg.err; +} +#endif /* !LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_PPP_API */ + +/** + * Allocate a structure for a static callback message and initialize it. + * This is intended to be used to send "static" messages from interrupt context. + * + * @param function the function to call + * @param ctx parameter passed to function + * @return a struct pointer to pass to tcpip_trycallback(). + */ +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx) +{ + struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API); + if (msg == NULL) { + return NULL; + } + msg->type = TCPIP_MSG_CALLBACK_STATIC; + msg->msg.cb.function = function; + msg->msg.cb.ctx = ctx; + return (struct tcpip_callback_msg*)msg; +} + +/** + * Free a callback message allocated by tcpip_callbackmsg_new(). + * + * @param msg the message to free + */ +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg) +{ + memp_free(MEMP_TCPIP_MSG_API, msg); +} + +/** + * Try to post a callback-message to the tcpip_thread mbox + * This is intended to be used to send "static" messages from interrupt context. + * + * @param msg pointer to the message to post + * @return sys_mbox_trypost() return code + */ +err_t +tcpip_trycallback(struct tcpip_callback_msg* msg) +{ + if (!sys_mbox_valid(&mbox)) { + return ERR_VAL; + } + return sys_mbox_trypost(&mbox, msg); +} + +/** + * 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/def.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/def.c new file mode 100644 index 0000000..352b552 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dhcp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dhcp.c new file mode 100644 index 0000000..2dbde23 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dhcp.c @@ -0,0 +1,1844 @@ +/** + * @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/dhcp.h" +#include "lwip/autoip.h" +#include "lwip/dns.h" +#include "netif/etharp.h" + +#include + +/** DHCP_CREATE_RAND_XID: if this is set to 1, the xid is created using + * LWIP_RAND() (this overrides DHCP_GLOBAL_XID) + */ +#ifndef DHCP_CREATE_RAND_XID +#define DHCP_CREATE_RAND_XID 1 +#endif + +/** 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]; + +#ifdef DHCP_GLOBAL_XID +static u32_t xid; +static u8_t xid_initialised; +#endif /* DHCP_GLOBAL_XID */ + +#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); +#if LWIP_NETIF_HOSTNAME +static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif); +#endif /* LWIP_NETIF_HOSTNAME */ +/* 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 +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 +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 +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 +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 + dhcp_option_hostname(dhcp, netif); +#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_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY); + 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 +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 +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 +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 +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 +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 +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 */ + for(n = 0; (n < DNS_MAX_SERVERS) && dhcp_option_given(dhcp, DHCP_OPTION_IDX_DNS_SERVER + n); n++) { + 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); + } +#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 +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 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 +dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result; + + 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)); + + /* 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; + } + + /* Remove the flag that says this netif is handled by DHCP, + it is set when we succeeded starting. */ + netif->flags &= ~NETIF_FLAG_DHCP; + + /* 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; + } + ip_set_option(dhcp->pcb, 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")); + +#if LWIP_DHCP_CHECK_LINK_UP + if (!netif_is_link_up(netif)) { + /* set state INIT and wait for dhcp_network_changed() to call dhcp_discover() */ + dhcp_set_state(dhcp, DHCP_INIT); + netif->flags |= NETIF_FLAG_DHCP; + return ERR_OK; + } +#endif /* LWIP_DHCP_CHECK_LINK_UP */ + + /* (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 +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; + ip_set_option(dhcp.pcb, 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 +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: + /* INIT/REQUESTING/CHECKING/BACKING_OFF restart with new 'rid' because the + state changes, SELECTING: continue with current 'rid' as we stay in the + same state */ +#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 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 +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_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY); + 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 +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_src(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif, IP_ADDR_ANY); + 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 +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. */ + 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(0xff000000UL)); + } else if (first_octet >= 192) { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL)); + } else { + ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL)); + } + } + + 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(0x00000001UL)); + } + +#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))); + 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 +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 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 + +#if LWIP_NETIF_HOSTNAME + dhcp_option_hostname(dhcp, netif); +#endif /* LWIP_NETIF_HOSTNAME */ + + /* 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 +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 + dhcp_option_hostname(dhcp, netif); +#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 +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 +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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t +dhcp_release_unicast(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_RELEASING); + /* clean old DHCP offer *//* + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0;*/ + + /* create and initialize the DHCP message header */ + result = dhcp_create_msg(netif,netif->dhcp,DHCP_REQUEST); //Evan modified + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); + + 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(netif->dhcp);//Evan modified + 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); + + /* TODO: netif_down(netif); */ + return result; +} + +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void +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 +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 +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 +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 +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 +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)); +} + +#if LWIP_NETIF_HOSTNAME +static void +dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif) +{ + if (netif->hostname != NULL) { + size_t namelen = strlen(netif->hostname); + if (namelen > 0) { + u8_t len; + const char *p = netif->hostname; + /* Shrink len to available bytes (need 2 bytes for OPTION_HOSTNAME + and 1 byte for trailer) */ + size_t available = DHCP_OPTIONS_LEN - dhcp->options_out_len - 3; + LWIP_ASSERT("DHCP: hostname is too long!", namelen <= available); + len = LWIP_MIN(namelen, available); + dhcp_option(dhcp, DHCP_OPTION_HOSTNAME, len); + while (len--) { + dhcp_option_byte(dhcp, *p++); + } + } + } +} +#endif /* LWIP_NETIF_HOSTNAME */ + +/** + * 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 contiguous piece of memory, and + * use that further on. + * + */ +static err_t +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_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SUBNET_MASK; + break; + case(DHCP_OPTION_ROUTER): + decode_len = 4; /* only copy the first given router */ + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_ROUTER; + break; + case(DHCP_OPTION_DNS_SERVER): + /* special case: there might be more than one server */ + LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;); + /* limit number of DNS servers */ + decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS); + LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_DNS_SERVER; + break; + case(DHCP_OPTION_LEASE_TIME): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_LEASE_TIME; + break; + case(DHCP_OPTION_OVERLOAD): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_OVERLOAD; + break; + case(DHCP_OPTION_MESSAGE_TYPE): + LWIP_ERROR("len == 1", len == 1, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_MSG_TYPE; + break; + case(DHCP_OPTION_SERVER_ID): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_SERVER_ID; + break; + case(DHCP_OPTION_T1): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + decode_idx = DHCP_OPTION_IDX_T1; + break; + case(DHCP_OPTION_T2): + LWIP_ERROR("len == 4", len == 4, return ERR_VAL;); + 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); + if (!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_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;); + 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_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;); + 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) { + 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 +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 < NETIF_MAX_HWADDR_LEN && i < DHCP_CHADDR_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 +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 */ +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + static u32_t xid; +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + static u32_t xid = 0xABCD0000; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ +#else + 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 */ + if (message_type != DHCP_REQUEST) { + /* reuse transaction identifier in retransmissions */ + if (dhcp->tries == 0) { +#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND) + xid = LWIP_RAND(); +#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + xid++; +#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */ + } + 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 || dhcp->state == DHCP_RELEASING))) { + 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 +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 +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)) { + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +#endif /* LWIP_DHCP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dns.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dns.c new file mode 100644 index 0000000..45b416b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/dns.c @@ -0,0 +1,1302 @@ +/** + * @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 + + * security fixes and more by Simon Goldschmidt + + * 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 + +/* A list of DNS security features follows */ +#define LWIP_DNS_SECURE_RAND_XID 1 +#define LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING 2 +#define LWIP_DNS_SECURE_RAND_SRC_PORT 4 +/** Use all DNS security features by default. + * This is overridable but should only be needed by very small targets + * or when using against non standard DNS servers. */ +#ifndef LWIP_DNS_SECURE +#define LWIP_DNS_SECURE (LWIP_DNS_SECURE_RAND_XID | LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT) +#endif + +/** Random generator function to create random TXIDs and source ports for queries */ +#ifndef DNS_RAND_TXID +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0) +#define DNS_RAND_TXID LWIP_RAND +#else +static u16_t dns_txid; +#define DNS_RAND_TXID() (++dns_txid) +#endif +#endif + +/** Limits the source port to be >= 1024 by default */ +#ifndef DNS_PORT_ALLOWED +#define DNS_PORT_ALLOWED(port) ((port) >= 1024) +#endif + +/** 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 + +/* The number of parallel requests (i.e. calls to dns_gethostbyname + * that cannot be answered from the DNS table. + * This is set to the table size by default. + */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) +#ifndef DNS_MAX_REQUESTS +#define DNS_MAX_REQUESTS DNS_TABLE_SIZE +#endif +#else +/* In this configuration, both arrays have to have the same size and are used + * like one entry (used/free) */ +#define DNS_MAX_REQUESTS DNS_TABLE_SIZE +#endif + +/* The number of UDP source ports used in parallel */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +#ifndef DNS_MAX_SOURCE_PORTS +#define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS +#endif +#else +#ifdef DNS_MAX_SOURCE_PORTS +#undef DNS_MAX_SOURCE_PORTS +#endif +#define DNS_MAX_SOURCE_PORTS 1 +#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_FLD_8(u8_t flags1); + PACK_STRUCT_FLD_8(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 +/* maximum allowed size for the struct due to non-packed */ +#define SIZEOF_DNS_ANSWER_ASSERT 12 + +/** DNS table entry */ +struct dns_table_entry { + u32_t ttl; + ip_addr_t ipaddr; + u16_t txid; + u8_t state; + u8_t server_idx; + u8_t tmr; + u8_t retries; + u8_t seqno; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + u8_t pcb_idx; +#endif + char name[DNS_MAX_NAME_LENGTH]; +}; + +/** DNS request table entry: used when dns_gehostbyname cannot answer the + * request from the DNS table */ +struct dns_req_entry { + /* pointer to callback on DNS query done */ + dns_found_callback found; + /* argument passed to the callback function */ + void *arg; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + u8_t dns_table_idx; +#endif +}; + +#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); + +/*----------------------------------------------------------------------------- + * Globals + *----------------------------------------------------------------------------*/ + +/* DNS variables */ +static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS]; +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +static u8_t dns_last_pcb_idx; +#endif +static u8_t dns_seqno; +static struct dns_table_entry dns_table[DNS_TABLE_SIZE]; +static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS]; +static ip_addr_t dns_servers[DNS_MAX_SERVERS]; + +#ifndef LWIP_DNS_STRICMP +#define LWIP_DNS_STRICMP(str1, str2) dns_stricmp(str1, str2) +/** + * A small but sufficient implementation for case insensitive strcmp. + * This can be defined to e.g. stricmp for windows or strcasecmp for linux. */ +static int +dns_stricmp(const char* str1, const char* str2) +{ + char c1, c2; + + do { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) { + char c1_upc = c1 | 0x20; + if ((c1_upc >= 'a') && (c1_upc <= 'z')) { + /* characters are not equal an one is in the alphabet range: + downcase both chars and check again */ + char c2_upc = c2 | 0x20; + if (c1_upc != c2_upc) { + /* still not equal */ + /* don't care for < or > */ + return 1; + } + } else { + /* characters are not equal but none is in the alphabet range */ + return 1; + } + } + } while (c1 != 0); + return 0; +} +#endif /* LWIP_DNS_STRICMP */ + +/** + * Initialize the resolver: set up the UDP pcb and configure the default server + * (DNS_SERVER_ADDRESS). + */ +void +dns_init() +{ + ip_addr_t dnsserver; + + LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY", + sizeof(struct dns_query) == SIZEOF_DNS_QUERY); + LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER", + sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT); + + /* initialize default DNS server address */ + DNS_SERVER_ADDRESS(&dnsserver); + + LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n")); + + /* if dns client not yet initialized... */ +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) + if (dns_pcbs[0] == NULL) { + dns_pcbs[0] = udp_new(); + LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != 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_pcbs[0], IP_ADDR_ANY, 0); + udp_recv(dns_pcbs[0], dns_recv, NULL); + } +#endif + /* 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 +dns_setserver(u8_t numdns, ip_addr_t *dnsserver) +{ + if (numdns < DNS_MAX_SERVERS) { + if (dnsserver != NULL) { + dns_servers[numdns] = (*dnsserver); + } else { + dns_servers[numdns] = *IP_ADDR_ANY; + } + } +} + +/** + * 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 +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) +{ + LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n")); + dns_check_entries(); +} + +#if DNS_LOCAL_HOSTLIST +static void +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 +dns_lookup_local(const char *hostname) +{ +#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC + struct local_hostlist_entry *entry = local_hostlist_dynamic; + while(entry != NULL) { + if (LWIP_DNS_STRICMP(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 (LWIP_DNS_STRICMP(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 address + * + * @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 +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) || !LWIP_DNS_STRICMP(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 +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 +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) && + (LWIP_DNS_STRICMP(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; +} + +/** + * 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 p pbuf containing the encoded hostname in the DNS response + * @param start_offset offset into p where the name starts + * @return 0xFFFF: names differ, other: names equal -> offset behind name + */ +static u16_t +dns_compare_name(char *query, struct pbuf* p, u16_t start_offset) +{ + unsigned char n; + u16_t response_offset = start_offset; + + do { + n = pbuf_get_at(p, response_offset++); + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name: cannot be equal since we don't send them */ + return 0xFFFF; + } else { + /* Not compressed name */ + while (n > 0) { + if ((*query) != pbuf_get_at(p, response_offset)) { + return 0xFFFF; + } + ++response_offset; + ++query; + --n; + }; + ++query; + } + } while (pbuf_get_at(p, response_offset) != 0); + + return response_offset + 1; +} + +/** + * Walk through a compact encoded DNS name and return the end of the name. + * + * @param p pbuf containing the name + * @param query_idx start index into p pointing to encoded DNS name in the DNS server response + * @return index to end of the name + */ +static u16_t +dns_parse_name(struct pbuf* p, u16_t query_idx) +{ + unsigned char n; + + do { + n = pbuf_get_at(p, query_idx++); + /** @see RFC 1035 - 4.1.4. Message compression */ + if ((n & 0xc0) == 0xc0) { + /* Compressed name */ + break; + } else { + /* Not compressed name */ + while (n > 0) { + ++query_idx; + --n; + }; + } + } while (pbuf_get_at(p, query_idx) != 0); + + return query_idx + 1; +} + +/** + * Send a DNS query packet. + * + * @param entry the DNS table entry for which to send a request + * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise + */ +static err_t +dns_send(struct dns_table_entry* entry) +{ + err_t err; + struct dns_hdr hdr; + struct dns_query qry; + struct pbuf *p; + u16_t query_idx, copy_len; + const char *hostname, *hostname_part; + u8_t n; + u8_t pcb_idx; + + LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n", + (u16_t)(entry->server_idx), entry->name)); + LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS); + LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[entry->server_idx])); + + /* 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 + strlen(entry->name) + 2 + + SIZEOF_DNS_QUERY, PBUF_RAM); + if (p != NULL) { + /* fill dns header */ + memset(&hdr, 0, SIZEOF_DNS_HDR); + hdr.id = htons(entry->txid); + hdr.flags1 = DNS_FLAG1_RD; + hdr.numquestions = PP_HTONS(1); + pbuf_take(p, &hdr, SIZEOF_DNS_HDR); + hostname = entry->name; + --hostname; + + /* convert hostname into suitable query format. */ + query_idx = SIZEOF_DNS_HDR; + do { + ++hostname; + hostname_part = hostname; + for(n = 0; *hostname != '.' && *hostname != 0; ++hostname) { + ++n; + } + copy_len = hostname - hostname_part; + pbuf_put_at(p, query_idx, n); + pbuf_take_at(p, hostname_part, copy_len, query_idx + 1); + query_idx += n + 1; + } while(*hostname != 0); + pbuf_put_at(p, query_idx, 0); + query_idx++; + + /* fill dns query */ + qry.type = PP_HTONS(DNS_RRTYPE_A); + qry.cls = PP_HTONS(DNS_RRCLASS_IN); + pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx); + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + pcb_idx = entry->pcb_idx; +#else + pcb_idx = 0; +#endif + /* send dns packet */ + LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n", + entry->txid, entry->name, entry->server_idx)); + err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT); + + /* free pbuf */ + pbuf_free(p); + } else { + err = ERR_MEM; + } + + return err; +} + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) +static struct udp_pcb* +dns_alloc_random_port(void) +{ + err_t err; + struct udp_pcb* ret; + + ret = udp_new(); + if (ret == NULL) { + /* out of memory, have to reuse an existing pcb */ + return NULL; + } + do { + u16_t port = DNS_RAND_TXID(); + if (!DNS_PORT_ALLOWED(port)) { + /* this port is not allowed, try again */ + err = ERR_USE; + continue; + } + err = udp_bind(ret, IP_ADDR_ANY, port); + } while(err == ERR_USE); + if ((err != ERR_OK) && (err != ERR_USE)) { + udp_remove(ret); + return NULL; + } + udp_recv(ret, dns_recv, NULL); + return ret; +} + +/** + * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used + * for sending a request + * + * @return an index into dns_pcbs + */ +static u8_t +dns_alloc_pcb(void) +{ + u8_t i; + u8_t idx; + + for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) { + if (dns_pcbs[i] == NULL) { + break; + } + } + if (i < DNS_MAX_SOURCE_PORTS) { + dns_pcbs[i] = dns_alloc_random_port(); + if (dns_pcbs[i] != NULL) { + /* succeeded */ + dns_last_pcb_idx = i; + return i; + } + } + /* if we come here, creating a new UDP pcb failed, so we have to use + an already existing one */ + idx = dns_last_pcb_idx + 1; + for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) { + if (idx >= DNS_MAX_SOURCE_PORTS) { + idx = 0; + } + if (dns_pcbs[idx] != NULL) { + dns_last_pcb_idx = idx; + return idx; + } + } + return DNS_MAX_SOURCE_PORTS; +} +#endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */ + +/** + * dns_call_found() - call the found callback and check if there are duplicate + * entries for the given hostname. If there are any, their found callback will + * be called and they will be removed. + * + * @param idx dns table index of the entry that is resolved or removed + * @param addr IP address for the hostname (or NULL on error or memory shortage) + */ +static void +dns_call_found(u8_t idx, ip_addr_t* addr) +{ + u8_t i; + LWIP_UNUSED_ARG(i); + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + for (i = 0; i < DNS_MAX_REQUESTS; i++) { + if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) { + (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg); + /* flush this entry */ + dns_requests[i].found = NULL; + } + } +#else + if (dns_requests[idx].found) { + (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg); + } + dns_requests[idx].found = NULL; +#endif +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + /* close the pcb used unless other request are using it */ + for (i = 0; i < DNS_MAX_REQUESTS; i++) { + if (i == idx) { + continue; /* only check other requests */ + } + if (dns_table[i].state == DNS_STATE_ASKING) { + if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) { + /* another request is still using the same pcb */ + dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; + break; + } + } + } + if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) { + /* if we come here, the pcb is not used any more and can be removed */ + udp_remove(dns_pcbs[dns_table[idx].pcb_idx]); + dns_pcbs[dns_table[idx].pcb_idx] = NULL; + dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS; + } +#endif +} + +/* Create a query transmission ID that is unique for all outstanding queries */ +static u16_t +dns_create_txid(void) +{ + u16_t txid; + u8_t i; + +again: + txid = DNS_RAND_TXID(); + + /* check whether the ID is unique */ + for (i = 0; i < DNS_TABLE_SIZE; i++) { + if ((dns_table[i].state == DNS_STATE_ASKING) && + (dns_table[i].txid == txid)) { + /* ID already used by another pending query */ + goto again; + } + } + + return txid; +} + +/** + * dns_check_entry() - see if entry 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 +dns_check_entry(u8_t i) +{ + err_t err; + struct dns_table_entry *entry = &dns_table[i]; + + LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE); + + switch (entry->state) { + + case DNS_STATE_NEW: { + u16_t txid; + /* initialize new entry */ + txid = dns_create_txid(); + entry->txid = txid; + entry->state = DNS_STATE_ASKING; + entry->server_idx = 0; + entry->tmr = 1; + entry->retries = 0; + + /* send DNS packet for this entry */ + err = dns_send(entry); + 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 (--entry->tmr == 0) { + if (++entry->retries == DNS_MAX_RETRIES) { + if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[entry->server_idx + 1])) { + /* change of server */ + entry->server_idx++; + entry->tmr = 1; + entry->retries = 0; + break; + } else { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name)); + /* call specified callback function if provided */ + dns_call_found(i, NULL); + /* flush this entry */ + entry->state = DNS_STATE_UNUSED; + break; + } + } + + /* wait longer for the next retry */ + entry->tmr = entry->retries; + + /* send DNS packet for this entry */ + err = dns_send(entry); + 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 ((entry->ttl == 0) || (--entry->ttl == 0)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name)); + /* flush this entry, there cannot be any related pending entries in this state */ + entry->state = DNS_STATE_UNUSED; + } + 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 +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 +dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + u8_t i, entry_idx = DNS_TABLE_SIZE; + u16_t txid; + u16_t res_idx; + struct dns_hdr hdr; + struct dns_answer ans; + struct dns_query qry; + u16_t nquestions, nanswers; + + LWIP_UNUSED_ARG(arg); + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(port); + + /* 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, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) { + /* Match the ID in the DNS header with the name table. */ + txid = htons(hdr.id); + for (i = 0; i < DNS_TABLE_SIZE; i++) { + struct dns_table_entry *entry = &dns_table[i]; + entry_idx = i; + if ((entry->state == DNS_STATE_ASKING) && + (entry->txid == txid)) { + u8_t dns_err; + /* This entry is now completed. */ + entry->state = DNS_STATE_DONE; + dns_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) || (dns_err != 0) || (nquestions != 1)) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + + /* Check whether response comes from the same network address to which the + question was sent. (RFC 5452) */ + if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) { + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + + /* Check if the name in the "question" part match with the name in the entry and + skip it if equal. */ + res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR); + if (res_idx == 0xFFFF) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + + /* check if "question" part matches the request */ + pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx); + if((qry.type != PP_HTONS(DNS_RRTYPE_A)) || (qry.cls != PP_HTONS(DNS_RRCLASS_IN))) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name)); + /* call callback to indicate error, clean up memory and return */ + goto responseerr; + } + /* skip the rest of the "question" part */ + res_idx += SIZEOF_DNS_QUERY; + + while (nanswers > 0) { + /* skip answer resource record's host name */ + res_idx = dns_parse_name(p, res_idx); + + /* Check for IP address type and Internet class. Others are discarded. */ + pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx); + if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) && + (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) { + res_idx += SIZEOF_DNS_ANSWER; + /* read the answer resource record's TTL, and maximize it if needed */ + entry->ttl = ntohl(ans.ttl); + if (entry->ttl > DNS_MAX_TTL) { + entry->ttl = DNS_MAX_TTL; + } + /* read the IP address after answer resource record's header */ + pbuf_copy_partial(p, &(entry->ipaddr), sizeof(entry->ipaddr), res_idx); + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name)); + ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr))); + LWIP_DEBUGF(DNS_DEBUG, ("\n")); + /* call specified callback function if provided */ + dns_call_found(entry_idx, &entry->ipaddr); + if (entry->ttl == 0) { + /* RFC 883, page 29: "Zero values are + interpreted to mean that the RR can only be used for the + transaction in progress, and should not be cached." + -> flush this entry now */ + goto flushentry; + } + /* deallocate memory and return */ + goto memerr; + } else { + res_idx += SIZEOF_DNS_ANSWER + htons(ans.len); + } + --nanswers; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->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 */ + dns_call_found(entry_idx, NULL); +flushentry: + /* flush this entry */ + dns_table[entry_idx].state = DNS_STATE_UNUSED; + +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 hostnamelen length of the hostname + * @param found a callback function 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 +dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found, + void *callback_arg) +{ + u8_t i; + u8_t lseq, lseqi; + struct dns_table_entry *entry = NULL; + size_t namelen; + struct dns_req_entry* req; + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + u8_t r; + /* check for duplicate entries */ + for (i = 0; i < DNS_TABLE_SIZE; i++) { + if ((dns_table[i].state == DNS_STATE_ASKING) && + (LWIP_DNS_STRICMP(name, dns_table[i].name) == 0)) { + /* this is a duplicate entry, find a free request entry */ + for (r = 0; r < DNS_MAX_REQUESTS; r++) { + if (dns_requests[r].found == 0) { + dns_requests[r].found = found; + dns_requests[r].arg = callback_arg; + dns_requests[r].dns_table_idx = i; + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name)); + return ERR_INPROGRESS; + } + } + } + } + /* no duplicate entries found */ +#endif + + /* search an unused entry, or the oldest one */ + lseq = 0; + lseqi = DNS_TABLE_SIZE; + for (i = 0; i < DNS_TABLE_SIZE; ++i) { + entry = &dns_table[i]; + /* is it an unused entry ? */ + if (entry->state == DNS_STATE_UNUSED) { + break; + } + /* check if this is the oldest completed entry */ + if (entry->state == DNS_STATE_DONE) { + if ((u8_t)(dns_seqno - entry->seqno) > lseq) { + lseq = dns_seqno - entry->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 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; + entry = &dns_table[i]; + } + } + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0) + /* find a free request entry */ + req = NULL; + for (r = 0; r < DNS_MAX_REQUESTS; r++) { + if (dns_requests[r].found == NULL) { + req = &dns_requests[r]; + break; + } + } + if (req == NULL) { + /* no request entry can be used now, table is full */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name)); + return ERR_MEM; + } + req->dns_table_idx = i; +#else + /* in this configuration, the entry index is the same as the request index */ + req = &dns_requests[i]; +#endif + + /* use this entry */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i))); + + /* fill the entry */ + entry->state = DNS_STATE_NEW; + entry->seqno = dns_seqno; + req->found = found; + req->arg = callback_arg; + namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1); + MEMCPY(entry->name, name, namelen); + entry->name[namelen] = 0; + +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) + entry->pcb_idx = dns_alloc_pcb(); + if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) { + /* failed to get a UDP pcb */ + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name)); + entry->state = DNS_STATE_UNUSED; + req->found = NULL; + return ERR_MEM; + } + LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx))); +#endif + + dns_seqno++; + + /* 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 +dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found, + void *callback_arg) +{ + u32_t ipaddr; + size_t hostnamelen; + /* not initialized or no valid server yet, or invalid addr pointer + * or invalid hostname or invalid hostname length */ + if ((addr == NULL) || + (!hostname) || (!hostname[0])) { + return ERR_ARG; + } +#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0) + if (dns_pcbs[0] == NULL) { + return ERR_ARG; + } +#endif + hostnamelen = strlen(hostname); + if (hostnamelen >= DNS_MAX_NAME_LENGTH) { + LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve")); + 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, hostnamelen, found, callback_arg); +} + +#endif /* LWIP_DNS */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/inet_chksum.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/inet_chksum.c new file mode 100644 index 0000000..01a8fa9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/inet_chksum.c @@ -0,0 +1,545 @@ +/** + * @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 +u16_t lwip_standard_chksum(void *dataptr, int len); +#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) + */ +u16_t +lwip_standard_chksum(void *dataptr, int 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) + */ + +u16_t +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 probably 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 + */ + +u16_t +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 probably 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 + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc) +{ + struct pbuf *q; + u8_t 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); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is probably 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(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + 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); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 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 ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_base(p, proto, proto_len, acc); +} +#endif /* LWIP_IPV6 */ + +/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */ +static u16_t +inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, u32_t acc) +{ + struct pbuf *q; + u8_t swapped = 0; + u16_t chklen; + + /* 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); + } + + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + /* Fold 32-bit sum to 16 bits + calling this twice is probably 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_partial: + * + * 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, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest) +{ + u32_t acc; + u32_t addr; + + 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); + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} + +#if LWIP_IPV6 +/** + * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain. + * IPv6 addresses are expected to be in network byte order. Will only compute for a + * portion of the payload. + * + * @param p chain of pbufs over that a checksum should be calculated (ip data part) + * @param src source ipv6 address (used for checksum of pseudo header) + * @param dst destination ipv6 address (used for checksum of pseudo header) + * @param proto ipv6 protocol/next header (used for checksum of pseudo header) + * @param proto_len length of the ipv6 payload (used for checksum of pseudo header) + * @param chksum_len number of payload bytes used to compute chksum + * @return checksum (as u16_t) to be saved directly in the protocol header + */ +u16_t +ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest) +{ + u32_t acc = 0; + u32_t addr; + u8_t addr_part; + + for (addr_part = 0; addr_part < 4; addr_part++) { + addr = src->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + addr = dest->addr[addr_part]; + acc += (addr & 0xffffUL); + acc += ((addr >> 16) & 0xffffUL); + } + /* fold down to 16 bits */ + acc = FOLD_U32T(acc); + acc = FOLD_U32T(acc); + + return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc); +} +#endif /* LWIP_IPV6 */ + +/* 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/init.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/init.c new file mode 100644 index 0000000..43e4c4a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/init.c @@ -0,0 +1,349 @@ +/** + * @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/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/lwip_timers.h" // Evan modified timers.h to lwip_timers.h +#include "netif/etharp.h" +#include "lwip/ip6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/api.h" +#include "netif/ppp/ppp_impl.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_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_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 !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */ +#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_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_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 +/* 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 + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0))) + #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 +#endif /* !MEMP_MEM_MALLOC */ +#if LWIP_WND_SCALE +#if (LWIP_TCP && (TCP_WND > 0xffffffff)) + #error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h" +#endif +#if (LWIP_TCP && LWIP_WND_SCALE > 14) + #error "The maximum valid window scale value is 14!" +#endif +#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE))) + #error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!" +#endif +#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0)) + #error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!" +#endif +#else /* LWIP_WND_SCALE */ +#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 (or enable window scaling)" +#endif +#endif /* LWIP_WND_SCALE */ +#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_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_PPP_API && (NO_SYS==1)) + #error "If you want to use PPP API, you have to define NO_SYS=0 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 +#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 (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 || LWIP_IPV6) && !defined(LWIP_RAND) + #error "When using IGMP or IPv6, 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 +#if LWIP_NETCONN && LWIP_TCP +#if NETCONN_COPY != TCP_WRITE_FLAG_COPY + #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY" +#endif +#if NETCONN_MORE != TCP_WRITE_FLAG_MORE + #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE" +#endif +#endif /* LWIP_NETCONN && LWIP_TCP */ +#if LWIP_SOCKET +/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */ +#if SO_REUSEADDR != SOF_REUSEADDR + #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR" +#endif +#if SO_KEEPALIVE != SOF_KEEPALIVE + #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE" +#endif +#if SO_BROADCAST != SOF_BROADCAST + #error "WARNING: SO_BROADCAST != SOF_BROADCAST" +#endif +#endif /* LWIP_SOCKET */ + + +/* 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 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 + +#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS +#define LWIP_DISABLE_TCP_SANITY_CHECKS 0 +#endif +#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS +#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0 +#endif + +/* MEMP sanity checks */ +#if !LWIP_DISABLE_MEMP_SANITY_CHECKS +#if LWIP_NETCONN +#if MEMP_MEM_MALLOC +#if !MEMP_NUM_NETCONN && LWIP_SOCKET +#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!" +#endif +#else /* MEMP_MEM_MALLOC */ +#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB) +#error "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. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* MEMP_MEM_MALLOC */ +#endif /* LWIP_NETCONN */ +#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */ + +/* TCP sanity checks */ +#if !LWIP_DISABLE_TCP_SANITY_CHECKS +#if LWIP_TCP +#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN) + #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_BUF < (2 * TCP_MSS) + #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS)) + #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDLOWAT >= TCP_SND_BUF + #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN + #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) + #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)))) + #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#if TCP_WND < TCP_MSS + #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error." +#endif +#endif /* LWIP_TCP */ +#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */ + +/** + * Perform Sanity check of user-configurable values, and initialize all modules. + */ +void +lwip_init(void) +{ + /* Modules initialization */ + stats_init(); +#if !NO_SYS + sys_init(); +#endif /* !NO_SYS */ + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); + 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 + 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_IPV6 + ip6_init(); + nd6_init(); +#if LWIP_IPV6_MLD + mld6_init(); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ +#if PPP_SUPPORT + ppp_init(); +#endif + +#if LWIP_TIMERS + sys_timeouts_init(); +#endif /* LWIP_TIMERS */ +} diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/autoip.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/autoip.c new file mode 100644 index 0000000..3052f6b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/autoip.c @@ -0,0 +1,528 @@ +/** + * @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); + + +/** 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 + * chosen 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 + * acquiring 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/icmp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/icmp.c new file mode 100644 index 0000000..cd8f652 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/icmp.c @@ -0,0 +1,339 @@ +/** + * @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/lwip_ip.h" +#include "lwip/def.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 icmp 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 *)ip_current_header(); + hlen = IPH_HL(iphdr) * 4; + if (p->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(ip_current_dest_addr())) { + accepted = 0; + } +#endif /* LWIP_MULTICAST_PING */ +#if !LWIP_BROADCAST_PING + /* broadcast destination address? */ + if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) { + accepted = 0; + } +#endif /* LWIP_BROADCAST_PING */ + /* broadcast or multicast destination address not accepted? */ + 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 CHECKSUM_CHECK_ICMP + 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; + } +#endif +#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; + /* allocate new packet buffer with space for link headers */ + r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, 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 ip header */ + MEMCPY(r->payload, iphdr, hlen); + iphdr = (struct ip_hdr *)r->payload; + /* switch r->payload back to icmp header */ + if (pbuf_header(r, -hlen)) { + LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0); + goto memerr; + } + /* copy the rest of the packet without ip header */ + if (pbuf_copy(r, p) != ERR_OK) { + LWIP_ASSERT("icmp_input: copying to new pbuf 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); +#if CHECKSUM_GEN_ICMP + /* adjust the checksum */ + if (iecho->chksum >= PP_HTONS(0xffffU - (ICMP_ECHO << 8))) { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += PP_HTONS(ICMP_ECHO << 8); + } +#else /* CHECKSUM_GEN_ICMP */ + iecho->chksum = 0; +#endif /* CHECKSUM_GEN_ICMP */ + + /* 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 current 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 +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 */ + 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; + 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; + icmphdr->seqno = 0; + + /* copy fields from original packet */ + 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; +#if CHECKSUM_GEN_ICMP + icmphdr->chksum = inet_chksum(icmphdr, q->len); +#endif + 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); + pbuf_free(q); +} + +#endif /* LWIP_ICMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/igmp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/igmp.c new file mode 100644 index 0000000..0d09a8c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/igmp.c @@ -0,0 +1,816 @@ +/** + * @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/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 0x9404U +#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_FLD_8(u8_t igmp_msgtype); + PACK_STRUCT_FLD_8(u8_t igmp_maxresp); + PACK_STRUCT_FIELD(u16_t igmp_checksum); + PACK_STRUCT_FLD_S(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); +static err_t igmp_remove_group(struct igmp_group *group); +static void igmp_timeout( struct igmp_group *group); +static void igmp_start_timer(struct igmp_group *group, u8_t max_time); +static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp); +static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif); +static void igmp_send(struct igmp_group *group, u8_t type); + + +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) && (!(ip_addr_cmp(&(group->group_address), &allsystems)))) { + 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 igmp 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 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 */ + if (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, &(ip_current_header()->src)); + LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); + ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->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 enough 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 + (unless it is the allsystems group) */ + if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) && + (!(ip_addr_cmp(&(group->group_address), &allsystems)))) { + 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 */ +#ifdef LWIP_RAND + if (max_time == 0) { + max_time = 1; + } + /* ensure the random value is > 0 */ + group->timer = (LWIP_RAND() % max_time); + if (group->timer == 0) { + group->timer = 1; + } +#else /* LWIP_RAND */ + /* ATTENTION: use this only if absolutely necessary! */ + group->timer = max_time / 2; + if (group->timer == 0) { + group->timer = 1; + } +#endif /* LWIP_RAND */ +} + +/** + * 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4.c new file mode 100644 index 0000000..c7cb0d7 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4.c @@ -0,0 +1,972 @@ +/** + * @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/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(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 */ + +/** Global data for both IPv4 and IPv6 */ +struct ip_globals ip_data; + +/** 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; + +#ifdef LWIP_HOOK_IP4_ROUTE + netif = LWIP_HOOK_IP4_ROUTE(dest); + if (netif != NULL) { + return netif; + } +#endif + + /* iterate through netifs */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if ((netif_is_up(netif)) +#if LWIP_IPV6 + /* prevent using IPv6-only interfaces */ + && (!ip_addr_isany(&(netif->ip_addr))) +#endif /* LWIP_IPV6 */ + ) { + if (ip_addr_netcmp(dest, &(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 */ + return netif_default; +} + +#if IP_FORWARD +/** + * Determine whether an IP address is in a reserved set of addresses + * that may not be forwarded, or whether datagrams to that destination + * may be forwarded. + * @param p the packet to forward + * @param dest the destination IP address + * @return 1: can forward 0: discard + */ +static int +ip_canforward(struct pbuf *p) +{ + u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr())); + + if (p->flags & PBUF_FLAG_LLBCAST) { + /* don't route link-layer broadcasts */ + return 0; + } + if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) { + /* don't route link-layer multicasts unless the destination address is an IP + multicast address */ + return 0; + } + if (IP_EXPERIMENTAL(addr)) { + return 0; + } + if (IP_CLASSA(addr)) { + u32_t net = addr & IP_CLASSA_NET; + if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) { + /* don't route loopback packets */ + return 0; + } + } + return 1; +} + +/** + * 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 +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + + if (!ip_canforward(p)) { + goto return_noroute; + } + + /* RFC3927 2.7: do not forward link-local addresses */ + if (ip_addr_islinklocal(ip_current_dest_addr())) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + goto return_noroute; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip_route(ip_current_dest_addr()); + 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(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + /* @todo: send ICMP_DUR_NET? */ + goto return_noroute; + } +#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF + /* 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; + } +#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */ + + /* 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(0xffffU - 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(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()), + ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr()))); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) { + if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) { +#if IP_FRAG + ip_frag(p, netif, ip_current_dest_addr()); +#else /* IP_FRAG */ + /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */ +#endif /* IP_FRAG */ + } else { + /* send ICMP Destination Unreachable code 4: "Fragmentation Needed and DF Set" */ + icmp_dest_unreach(p, ICMP_DUR_FRAG); + } + return; + } + /* transmit pbuf on chosen interface */ + netif->output(netif, p, ip_current_dest_addr()); + 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 || LWIP_IGMP + int check_ip_src = 1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING || LWIP_IGMP */ + + 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; + } + +#ifdef LWIP_HOOK_IP4_INPUT + if (LWIP_HOOK_IP4_INPUT(p, inp)) { + /* the packet has been eaten */ + return ERR_OK; + } +#endif + + /* 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(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest); + ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src); + + /* match packet against an interface, i.e. is this packet for us? */ +#if LWIP_IGMP + if (ip_addr_ismulticast(ip_current_dest_addr())) { + if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) { + /* IGMP snooping switches need 0.0.0.0 to be allowed as source address (RFC 4541) */ + ip_addr_t allsystems; + IP4_ADDR(&allsystems, 224, 0, 0, 1); + if (ip_addr_cmp(ip_current_dest_addr(), &allsystems) && + ip_addr_isany(ip_current_src_addr())) { + check_ip_src = 0; + } + 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(ip_current_dest_addr(), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(ip_current_dest_addr(), 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(ip_current_dest_addr(), &(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 LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING + if (check_ip_src +#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) */ + && !ip_addr_isany(ip_current_src_addr()) +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + ) +#endif /* LWIP_IGMP || IP_ACCEPT_LINK_LAYER_ADDRESSING */ + { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) || + (ip_addr_ismulticast(ip_current_src_addr()))) { + /* 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(ip_current_dest_addr(), 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)); + + //Evan add for debug + //printf("\n\r[Evan] header len = %"U16_F" , total len = %"U16_F"\n", iphdr_hlen, p->tot_len); + //char temp[1000] = {"\0"}; + //memcpy(temp, p->payload, p->tot_len); + //printf("\n\rReceive ipv4 socket = %s\n", &temp[28]); + //Evan add end + + ip_data.current_netif = inp; + ip_data.current_ip4_header = iphdr; + ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4; + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + pbuf_header(p, -(s16_t)iphdr_hlen); /* Move to payload, no check necessary. */ + + 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, ip_current_dest_addr()); + break; +#endif /* LWIP_IGMP */ + default: +#if LWIP_ICMP + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) && + !ip_addr_ismulticast(ip_current_dest_addr())) { + pbuf_header_force(p, iphdr_hlen); /* Move to ip header, no check necessary. */ + 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(); + } + } + + /* @todo: this is not really necessary... */ + ip_data.current_netif = NULL; + ip_data.current_ip4_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip_addr_set_any(ip_current_src_addr()); + ip_addr_set_any(ip_current_dest_addr()); + + 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 */ + ip_addr_t *src_used = src; + if (dest != IP_HDRINCL) { + if (ip_addr_isany(src)) { + src_used = &netif->ip_addr; + } + } + +#if IP_OPTIONS_SEND + return ip_output_if_opt_src(p, src_used, dest, ttl, tos, proto, netif, + ip_options, optlen); +#else /* IP_OPTIONS_SEND */ + return ip_output_if_src(p, src_used, dest, ttl, tos, proto, netif); +#endif /* IP_OPTIONS_SEND */ +} + +/** + * Same as ip_output_if() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t +ip_output_if_src(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_src(p, src, dest, ttl, tos, proto, netif, NULL, 0); +} + +/** + * Same as ip_output_if_opt() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t ip_output_if_opt_src(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 */ + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + 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_VHL_SET(iphdr, 4, ip_hlen / 4); + IPH_TOS_SET(iphdr, tos); +#if CHECKSUM_GEN_IP_INLINE + chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl); +#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 (src == NULL) { + ip_addr_copy(iphdr->src, ip_addr_any); + } 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); + } +#if LWIP_IGMP + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p); + } +#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()")); + 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; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + 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; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + 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_SET_HWADDRHINT(netif, addr_hint); + err = ip_output_if(p, src, dest, ttl, tos, proto, netif); + NETIF_SET_HWADDRHINT(netif, 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; + + 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4_addr.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4_addr.c new file mode 100644 index 0000000..3836f27 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip4_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 = 1UL << 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 representation (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 representation (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 + * representation 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip_frag.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip_frag.c new file mode 100644 index 0000000..3450927 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv4/ip_frag.c @@ -0,0 +1,878 @@ +/** + * @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); +static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); + +/** + * 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* +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 +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 +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 ((ipr->p == NULL) || (((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; + + 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; + } + } + + 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)) { + struct ip_reassdata *ipr_prev; + /* 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? */ +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + + 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 succeeding fragment */ + pbuf_header(r, -IP_HLEN); + pbuf_cat(p, r); + r = iprh->next_pbuf; + } + + /* find the previous entry in the linked list */ + if (ipr == reassdatagrams) { + ipr_prev = NULL; + } else { + for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } + + /* 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* +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 +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 +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); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif /* CHECKSUM_GEN_IP */ + +#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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/README b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/README new file mode 100644 index 0000000..3620004 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/README @@ -0,0 +1 @@ +IPv6 support in lwIP is very experimental. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/dhcp6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/dhcp6.c new file mode 100644 index 0000000..9656c3b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/dhcp6.c @@ -0,0 +1,50 @@ +/** + * @file + * + * DHCPv6. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + + +#endif /* LWIP_IPV6_DHCP6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ethip6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ethip6.c new file mode 100644 index 0000000..d9ac30f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ethip6.c @@ -0,0 +1,193 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET + +#include "lwip/ethip6.h" +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" + +#include + +#define ETHTYPE_IPV6 0x86DD + +/** The ethernet address */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_addr { + PACK_STRUCT_FLD_8(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Ethernet header */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct eth_hdr { +#if ETH_PAD_SIZE + PACK_STRUCT_FLD_8(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FLD_S(struct eth_addr dest); + PACK_STRUCT_FLD_S(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) + +/** + * Send an IPv6 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 +ethip6_send(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 6 for ethip6!", + (netif->hwaddr_len == 6)); + SMEMCPY(ðhdr->dest, dst, 6); + SMEMCPY(ðhdr->src, src, 6); + ethhdr->type = PP_HTONS(ETHTYPE_IPV6); + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p)); + /* send the packet */ + return netif->linkoutput(netif, p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing IPv6 packet. + * + * For IPv6 multicast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, ... + * + * @TODO anycast addresses + * + * @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 ip6addr 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 +ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr) +{ + struct eth_addr dest; + s8_t i; + + /* 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")); + return ERR_BUF; + } + + /* multicast destination IP address? */ + if (ip6_addr_ismulticast(ip6addr)) { + /* Hash IP multicast address to MAC address.*/ + dest.addr[0] = 0x33; + dest.addr[1] = 0x33; + dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0]; + dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1]; + dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2]; + dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3]; + + /* Send out. */ + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We have a unicast destination IP address */ + /* TODO anycast? */ + /* Get next hop record. */ + i = nd6_get_next_hop_entry(ip6addr, netif); + if (i < 0) { + /* failed to get a next hop neighbor record. */ + return ERR_MEM; + } + + /* Now that we have a destination record, send or queue the packet. */ + if (neighbor_cache[i].state == ND6_STALE) { + /* Switch to delay state. */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */ + if ((neighbor_cache[i].state == ND6_REACHABLE) || + (neighbor_cache[i].state == ND6_DELAY) || + (neighbor_cache[i].state == ND6_PROBE)) { + + /* Send out. */ + SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6); + return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest); + } + + /* We should queue packet on this interface. */ + pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR); + return nd6_queue_packet(i, q); +} + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/icmp6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/icmp6.c new file mode 100644 index 0000000..5cd1bbb --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/icmp6.c @@ -0,0 +1,342 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/lwip_ip.h" +#include "lwip/stats.h" + +#include + +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif +#if LWIP_ICMP6_DATASIZE == 0 +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/* Forward declarations */ +static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type); + + +/** + * Process an input ICMPv6 message. Called by ip6_input. + * + * Will generate a reply for echo requests. Other messages are forwarded + * to nd6_input, or mld6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +icmp6_input(struct pbuf *p, struct netif *inp) +{ + struct icmp6_hdr *icmp6hdr; + struct pbuf * r; + ip6_addr_t * reply_src; + + ICMP6_STATS_INC(icmp6.recv); + + /* Check that ICMPv6 header fits in payload */ + if (p->len < sizeof(struct icmp6_hdr)) { + /* drop short packets */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.lenerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + +#if CHECKSUM_CHECK_ICMP6 + if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(), + ip6_current_dest_addr()) != 0) { + /* Checksum failed */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.chkerr); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* CHECKSUM_CHECK_ICMP6 */ + + switch (icmp6hdr->type) { + case ICMP6_TYPE_NA: /* Neighbor advertisement */ + case ICMP6_TYPE_NS: /* Neighbor solicitation */ + case ICMP6_TYPE_RA: /* Router advertisement */ + case ICMP6_TYPE_RD: /* Redirect */ + case ICMP6_TYPE_PTB: /* Packet too big */ + nd6_input(p, inp); + return; + break; + case ICMP6_TYPE_RS: +#if LWIP_IPV6_FORWARD + /* TODO implement router functionality */ +#endif + break; +#if LWIP_IPV6_MLD + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: + mld6_input(p, inp); + return; + break; +#endif + case ICMP6_TYPE_EREQ: +#if !LWIP_MULTICAST_PING + /* multicast destination address? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.drop); + return; + } +#endif /* LWIP_MULTICAST_PING */ + + /* Allocate reply. */ + r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM); + if (r == NULL) { + /* drop */ + pbuf_free(p); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + + /* Copy echo request. */ + if (pbuf_copy(r, p) != ERR_OK) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.err); + return; + } + + /* Determine reply source IPv6 address. */ +#if LWIP_MULTICAST_PING + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + reply_src = ip6_select_source_address(inp, ip6_current_src_addr()); + if (reply_src == NULL) { + /* drop */ + pbuf_free(p); + pbuf_free(r); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else +#endif /* LWIP_MULTICAST_PING */ + { + reply_src = ip6_current_dest_addr(); + } + + /* Set fields in reply. */ + ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP; + ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0; +#if CHECKSUM_GEN_ICMP6 + ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r, + IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr()); +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send reply. */ + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(r, reply_src, ip6_current_src_addr(), + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp); + pbuf_free(r); + + break; + default: + ICMP6_STATS_INC(icmp6.proterr); + ICMP6_STATS_INC(icmp6.drop); + break; + } + + pbuf_free(p); +} + + +/** + * Send an icmpv6 'destination unreachable' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the unreachable type + */ +void +icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR); +} + +/** + * Send an icmpv6 'packet too big' packet. + * + * @param p the input packet for which the 'packet too big' should be sent, + * p->payload pointing to the IPv6 header + * @param mtu the maximum mtu that we can accept + */ +void +icmp6_packet_too_big(struct pbuf *p, u32_t mtu) +{ + icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB); +} + +/** + * Send an icmpv6 'time exceeded' packet. + * + * @param p the input packet for which the 'unreachable' should be sent, + * p->payload pointing to the IPv6 header + * @param c ICMPv6 code for the time exceeded type + */ +void +icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c) +{ + icmp6_send_response(p, c, 0, ICMP6_TYPE_TE); +} + +/** + * Send an icmpv6 'parameter problem' packet. + * + * @param p the input packet for which the 'param problem' should be sent, + * p->payload pointing to the IP header + * @param c ICMPv6 code for the param problem type + * @param pointer the pointer to the byte where the parameter is found + */ +void +icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer) +{ + icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP); +} + +/** + * Send an ICMPv6 packet in response to an incoming packet. + * + * @param p the input packet for which the response should be sent, + * p->payload pointing to the IPv6 header + * @param code Code of the ICMPv6 header + * @param data Additional 32-bit parameter in the ICMPv6 header + * @param type Type of the ICMPv6 header + */ +static void +icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type) +{ + struct pbuf *q; + struct icmp6_hdr *icmp6hdr; + ip6_addr_t *reply_src, *reply_dest; + ip6_addr_t reply_src_local, reply_dest_local; + struct ip6_hdr *ip6hdr; + struct netif *netif; + + /* ICMPv6 header + IPv6 header + data */ + q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE, + PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n")); + ICMP6_STATS_INC(icmp6.memerr); + return; + } + LWIP_ASSERT("check that first pbuf can hold icmp 6message", + (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE))); + + icmp6hdr = (struct icmp6_hdr *)q->payload; + icmp6hdr->type = type; + icmp6hdr->code = code; + icmp6hdr->data = data; + + /* copy fields from original packet */ + SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload, + IP6_HLEN + LWIP_ICMP6_DATASIZE); + + /* Get the destination address and netif for this ICMP message. */ + if ((ip_current_netif() == NULL) || + ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) { + /* Special case, as ip6_current_xxx is either NULL, or points + * to a different packet than the one that expired. + * We must use the addresses that are stored in the expired packet. */ + ip6hdr = (struct ip6_hdr *)p->payload; + /* copy from packed address to aligned address */ + ip6_addr_copy(reply_dest_local, ip6hdr->src); + ip6_addr_copy(reply_src_local, ip6hdr->dest); + reply_dest = &reply_dest_local; + reply_src = &reply_src_local; + netif = ip6_route(reply_src, reply_dest); + if (netif == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + else { + netif = ip_current_netif(); + reply_dest = ip6_current_src_addr(); + + /* Select an address to use as source. */ + reply_src = ip6_select_source_address(netif, reply_dest); + if (reply_src == NULL) { + /* drop */ + pbuf_free(q); + ICMP6_STATS_INC(icmp6.rterr); + return; + } + } + + /* calculate checksum */ + icmp6hdr->chksum = 0; +#if CHECKSUM_GEN_ICMP6 + icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len, + reply_src, reply_dest); +#endif /* CHECKSUM_GEN_ICMP6 */ + + ICMP6_STATS_INC(icmp6.xmit); + ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(q); +} + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/inet6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/inet6.c new file mode 100644 index 0000000..bdf4ff4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/inet6.c @@ -0,0 +1,51 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/inet6.h" + +/** @see ip6_addr.c for implementation of functions. */ + +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6.c new file mode 100644 index 0000000..f29b09b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6.c @@ -0,0 +1,1052 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/ip6_frag.h" +#include "lwip/icmp6.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp_impl.h" +#include "lwip/dhcp6.h" +#include "lwip/nd6.h" +#include "lwip/mld6.h" +#include "lwip/debug.h" +#include "lwip/stats.h" + + +/** + * Finds the appropriate network interface for a given IPv6 address. It tries to select + * a netif following a sequence of heuristics: + * 1) if there is only 1 netif, return it + * 2) if the destination is a link-local address, try to match the src address to a netif. + * this is a tricky case because with multiple netifs, link-local addresses only have + * meaning within a particular subnet/link. + * 3) tries to match the destination subnet to a configured address + * 4) tries to find a router + * 5) tries to match the source address to the netif + * 6) returns the default netif, if configured + * + * @param src the source IPv6 address, if known + * @param dest the destination IPv6 address for which to find the route + * @return the netif on which to send to reach dest + */ +struct netif * +ip6_route(struct ip6_addr *src, struct ip6_addr *dest) +{ + struct netif *netif; + s8_t i; + + /* If single netif configuration, fast return. */ + if ((netif_list != NULL) && (netif_list->next == NULL)) { + return netif_list; + } + + /* Special processing for link-local addresses. */ + if (ip6_addr_islinklocal(dest)) { + if (ip6_addr_isany(src)) { + /* Use default netif. */ + return netif_default; + } + + /* Try to find the netif for the source address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* netif not found, use default netif */ + return netif_default; + } + + /* See if the destination subnet matches a configured address. */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + + /* Get the netif for a suitable router. */ + i = nd6_select_router(dest, NULL); + if (i >= 0) { + if (default_router_list[i].neighbor_entry != NULL) { + if (default_router_list[i].neighbor_entry->netif != NULL) { + return default_router_list[i].neighbor_entry->netif; + } + } + } + + /* try with the netif that matches the source address. */ + if (!ip6_addr_isany(src)) { + for(netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(src, netif_ip6_addr(netif, i))) { + return netif; + } + } + } + } + + /* no matching netif found, use default netif */ + return netif_default; +} + +/** + * Select the best IPv6 source address for a given destination + * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior + * is assumed. + * + * @param netif the netif on which to send a packet + * @param dest the destination we are trying to reach + * @return the most suitable source address to use, or NULL if no suitable + * source address is found + */ +ip6_addr_t * +ip6_select_source_address(struct netif *netif, ip6_addr_t * dest) +{ + ip6_addr_t * src = NULL; + u8_t i; + + /* If dest is link-local, choose a link-local source. */ + if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_islinklocal(netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a site-local with matching prefix. */ + if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_issitelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a unique-local with matching prefix. */ + if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + } + + /* Choose a global with best matching prefix. */ + if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_isglobal(netif_ip6_addr(netif, i))) { + if (src == NULL) { + src = netif_ip6_addr(netif, i); + } + else { + /* Replace src only if we find a prefix match. */ + /* TODO find longest matching prefix. */ + if ((!(ip6_addr_netcmp(src, dest))) && + ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) { + src = netif_ip6_addr(netif, i); + } + } + } + } + if (src != NULL) { + return src; + } + } + + /* Last resort: see if arbitrary prefix matches. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) { + return netif_ip6_addr(netif, i); + } + } + + return NULL; +} + +#if LWIP_IPV6_FORWARD +/** + * Forwards an IPv6 packet. It finds an appropriate route for the + * packet, decrements the HL value of the packet, and outputs + * the packet on the appropriate interface. + * + * @param p the packet to forward (p->payload points to IP header) + * @param iphdr the IPv6 header of the input packet + * @param inp the netif on which this packet was received + */ +static void +ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + /* do not forward link-local addresses */ + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* Find network interface where to forward this IP packet to. */ + netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr()); + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n")); + IP6_STATS_INC(ip6.rterr); + IP6_STATS_INC(ip6.drop); + return; + } + + /* decrement HL */ + IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1); + /* send ICMP6 if HL == 0 */ + if (IP6H_HOPLIM(iphdr) == 0) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_time_exceeded(p, ICMP6_TE_HL); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + if (netif->mtu && (p->tot_len > netif->mtu)) { +#if LWIP_ICMP6 + /* Don't send ICMP messages in response to ICMP messages */ + if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) { + icmp6_packet_too_big(p, netif->mtu); + } +#endif /* LWIP_ICMP6 */ + IP6_STATS_INC(ip6.drop); + return; + } + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(ip6_current_dest_addr()), + IP6_ADDR_BLOCK2(ip6_current_dest_addr()), + IP6_ADDR_BLOCK3(ip6_current_dest_addr()), + IP6_ADDR_BLOCK4(ip6_current_dest_addr()), + IP6_ADDR_BLOCK5(ip6_current_dest_addr()), + IP6_ADDR_BLOCK6(ip6_current_dest_addr()), + IP6_ADDR_BLOCK7(ip6_current_dest_addr()), + IP6_ADDR_BLOCK8(ip6_current_dest_addr()))); + + /* transmit pbuf on chosen interface */ + netif->output_ip6(netif, p, ip6_current_dest_addr()); + IP6_STATS_INC(ip6.fw); + IP6_STATS_INC(ip6.xmit); + return; +} +#endif /* LWIP_IPV6_FORWARD */ + + +/** + * This function is called by the network interface device driver when + * an IPv6 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 ip6_forward). + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * @param p the received IPv6 packet (p->payload points to IPv6 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 +ip6_input(struct pbuf *p, struct netif *inp) +{ + struct ip6_hdr *ip6hdr; + struct netif *netif; + u8_t nexth; + u16_t hlen; /* the current header length */ + u8_t i; +#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/ + @todo + int check_ip_src=1; +#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */ + + IP6_STATS_INC(ip6.recv); + + /* identify the IP header */ + ip6hdr = (struct ip6_hdr *)p->payload; + if (IP6H_V(ip6hdr) != 6) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n", + IP6H_V(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.err); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ + if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) { + if (IP6_HLEN > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", + IP6_HLEN, p->len)); + } + if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n", + IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len)); + } + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + return ERR_OK; + } + + /* 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, IP6_HLEN + IP6H_PLEN(ip6hdr)); + + /* copy IP addresses to aligned ip6_addr_t */ + ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest); + ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src); + + /* current header pointer. */ + ip_data.current_ip6_header = ip6hdr; + + /* In netif, used in case we need to send ICMPv6 packets back. */ + ip_data.current_netif = inp; + + /* match packet against an interface, i.e. is this packet for us? */ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* Always joined to multicast if-local and link-local all-nodes group. */ + if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) || + ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) { + netif = inp; + } +#if LWIP_IPV6_MLD + else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) { + netif = inp; + } +#else /* LWIP_IPV6_MLD */ + else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) { + /* Filter solicited node packets when MLD is not enabled + * (for Neighbor discovery). */ + netif = NULL; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) && + ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + netif = inp; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + break; + } + } + } +#endif /* LWIP_IPV6_MLD */ + else { + netif = NULL; + } + } + else { + /* 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 { + /* interface is up? */ + if (netif_is_up(netif)) { + /* unicast to this interface address? address configured? */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) { + /* exit outer loop */ + goto netif_found; + } + } + } + if (ip6_addr_islinklocal(ip6_current_dest_addr())) { + /* Do not match link-local addresses to other netifs. */ + netif = NULL; + break; + } + if (first) { + first = 0; + netif = netif_list; + } else { + netif = netif->next; + } + if (netif == inp) { + netif = netif->next; + } + } while(netif != NULL); +netif_found: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n", + netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X')); + } + + /* "::" packet source address? (used in duplicate address detection) */ + if (ip6_addr_isany(ip6_current_src_addr()) && + (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) { + /* packet source is not valid */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n")); +#if LWIP_IPV6_FORWARD + /* non-multicast packet? */ + if (!ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* try to forward IP packet on (other) interfaces */ + ip6_forward(p, ip6hdr, inp); + } +#endif /* LWIP_IPV6_FORWARD */ + pbuf_free(p); + goto ip6_input_cleanup; + } + + /* current netif pointer. */ + ip_data.current_netif = netif; + + /* Save next header type. */ + nexth = IP6H_NEXTH(ip6hdr); + + /* Init header length. */ + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + + /* Move to payload. */ + pbuf_header(p, -IP6_HLEN); + + /* Process known option extension headers, if present. */ + while (nexth != IP6_NEXTH_NONE) + { + switch (nexth) { + case IP6_NEXTH_HOPBYHOP: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_DESTOPTS: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + case IP6_NEXTH_ROUTING: + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n")); + /* Get next header type. */ + nexth = *((u8_t *)p->payload); + + /* Get the header length. */ + hlen = 8 * (1 + *((u8_t *)p->payload + 1)); + ip_data.current_ip_header_tot_len += hlen; + + /* Skip over this header. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_STATS_INC(ip6.lenerr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; + } + + pbuf_header(p, -hlen); + break; + + case IP6_NEXTH_FRAGMENT: + { + struct ip6_frag_hdr * frag_hdr; + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n")); + + frag_hdr = (struct ip6_frag_hdr *)p->payload; + + /* Get next header type. */ + nexth = frag_hdr->_nexth; + + /* Fragment Header length. */ + hlen = 8; + ip_data.current_ip_header_tot_len += hlen; + + /* Make sure this header fits in current pbuf. */ + if (hlen > p->len) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n", + hlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP6_FRAG_STATS_INC(ip6_frag.lenerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto ip6_input_cleanup; + } + + /* Offset == 0 and more_fragments == 0? */ + if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) && + ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) { + + /* This is a 1-fragment packet, usually a packet that we have + * already reassembled. Skip this header anc continue. */ + pbuf_header(p, -hlen); + } + else { +#if LWIP_IPV6_REASS + + /* reassemble the packet */ + p = ip6_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + goto ip6_input_cleanup; + } + + /* Returned p point to IPv6 header. + * Update all our variables and pointers and continue. */ + ip6hdr = (struct ip6_hdr *)p->payload; + nexth = IP6H_NEXTH(ip6hdr); + hlen = ip_data.current_ip_header_tot_len = IP6_HLEN; + pbuf_header(p, -IP6_HLEN); + +#else /* LWIP_IPV6_REASS */ + /* free (drop) packet pbufs */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n")); + pbuf_free(p); + IP6_STATS_INC(ip6.opterr); + IP6_STATS_INC(ip6.drop); + goto ip6_input_cleanup; +#endif /* LWIP_IPV6_REASS */ + } + break; + } + default: + goto options_done; + break; + } + } +options_done: + + /* p points to IPv6 header again. */ + /* @todo: this does not work for PBUF_REF pbufs */ + pbuf_header(p, ip_data.current_ip_header_tot_len); + + /* send to upper layers */ + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n")); + ip6_debug_print(p); + LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + + //Evan add for debug + //printf("\n\r[Evan] header len = %"U16_F", total len = %"U16_F"\n", hlen, p->tot_len); + //char temp[1000] = {"\0"}; + //memcpy(temp, p->payload, p->tot_len); + //printf("\n\rReceive ipv6 socket = %s\n", &temp[48]); + //Evan add end + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) +#endif /* LWIP_RAW */ + { + switch (nexth) { + case IP6_NEXTH_NONE: + pbuf_free(p); + break; +#if LWIP_UDP + case IP6_NEXTH_UDP: +#if LWIP_UDPLITE + case IP6_NEXTH_UDPLITE: +#endif /* LWIP_UDPLITE */ + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP6_NEXTH_TCP: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ +#if LWIP_ICMP6 + case IP6_NEXTH_ICMP6: + /* Point to payload. */ + pbuf_header(p, -ip_data.current_ip_header_tot_len); + icmp6_input(p, inp); + break; +#endif /* LWIP_ICMP */ + default: +#if LWIP_ICMP6 + /* send ICMP parameter problem unless it was a multicast or ICMPv6 */ + if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) && + (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) { + icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen); + } +#endif /* LWIP_ICMP */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr))); + pbuf_free(p); + IP6_STATS_INC(ip6.proterr); + IP6_STATS_INC(ip6.drop); + break; + } + } + +ip6_input_cleanup: + ip_data.current_netif = NULL; + ip_data.current_ip6_header = NULL; + ip_data.current_ip_header_tot_len = 0; + ip6_addr_set_any(&ip_data.current_iphdr_src.ip6); + ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6); + + return ERR_OK; +} + + +/** + * Sends an IPv6 packet on a network interface. This function constructs + * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is + * used as source (usually during network startup). If the source IPv6 address it + * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network + * interface is filled in as source address. If the destination IPv6 address is + * IP_HDRINCL, p is assumed to already include an IPv6 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 + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 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 IPv6/LINK headers + * returns errors returned by netif->output + */ +err_t +ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + ip6_addr_t *src_used = src; + if (dest != IP_HDRINCL) { + if (src != NULL && ip6_addr_isany(src)) { + src = ip6_select_source_address(netif, dest); + if ((src == NULL) || ip6_addr_isany(src)) { + /* No appropriate source address was found for this packet. */ + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n")); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + } + } + return ip6_output_if_src(p, src_used, dest, hl, tc, nexth, netif); +} + +/** + * Same as ip6_output_if() but 'src' address is not replaced by netif address + * when it is 'any'. + */ +err_t +ip6_output_if_src(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, + u8_t nexth, struct netif *netif) +{ + struct ip6_hdr *ip6hdr; + ip6_addr_t dest_addr; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + /* Should the IPv6 header be generated or is it already included in p? */ + if (dest != IP_HDRINCL) { + /* generate IPv6 header */ + if (pbuf_header(p, IP6_HLEN)) { + LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + ip6hdr = (struct ip6_hdr *)p->payload; + LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr", + (p->len >= sizeof(struct ip6_hdr))); + + IP6H_HOPLIM_SET(ip6hdr, hl); + IP6H_NEXTH_SET(ip6hdr, nexth); + + /* dest cannot be NULL here */ + ip6_addr_copy(ip6hdr->dest, *dest); + + IP6H_VTCFL_SET(ip6hdr, 6, tc, 0); + IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN); + + if (src == NULL) { + src = IP6_ADDR_ANY; + } + /* src cannot be NULL here */ + ip6_addr_copy(ip6hdr->src, *src); + + } else { + /* IP header already included in p */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(dest_addr, ip6hdr->dest); + dest = &dest_addr; + } + + IP6_STATS_INC(ip6.xmit); + + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip6_debug_print(p); + +#if ENABLE_LOOPBACK + { + int i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) { + /* Packet to self, enqueue it for loopback */ + LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n")); + return netif_loop_output(netif, p); + } + } + } +#endif /* ENABLE_LOOPBACK */ +#if LWIP_IPV6_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) { + return ip6_frag(p, netif, dest); + } +#endif /* LWIP_IPV6_FRAG */ + + LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n")); + return netif->output_ip6(netif, p, dest); +} + +/** + * Simple interface to ip6_output_if. It finds the outgoing network + * interface and calls upon ip6_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 + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 header + * + * @return ERR_RTE if no route is found + * see ip_output_if() for more return values + */ +err_t +ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + return ip6_output_if(p, src, dest, hl, tc, nexth, netif); +} + + +#if LWIP_NETIF_HWADDRHINT +/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint + * before calling ip6_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 + IPv6 header and p->payload points to that IPv6 header) + * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an + * IP address of the netif is selected and used as source address. + * if src == NULL, IP6_ADDR_ANY is used as source) + * @param dest the destination IPv6 address to send the packet to + * @param hl the Hop Limit value to be set in the IPv6 header + * @param tc the Traffic Class value to be set in the IPv6 header + * @param nexth the Next Header to be set in the IPv6 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 +ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint) +{ + struct netif *netif; + struct ip6_hdr *ip6hdr; + ip6_addr_t src_addr, dest_addr; + err_t err; + + LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p); + + if (dest != IP_HDRINCL) { + netif = ip6_route(src, dest); + } else { + /* IP header included in p, read addresses. */ + ip6hdr = (struct ip6_hdr *)p->payload; + ip6_addr_copy(src_addr, ip6hdr->src); + ip6_addr_copy(dest_addr, ip6hdr->dest); + netif = ip6_route(&src_addr, &dest_addr); + } + + if (netif == NULL) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n", + IP6_ADDR_BLOCK1(dest), + IP6_ADDR_BLOCK2(dest), + IP6_ADDR_BLOCK3(dest), + IP6_ADDR_BLOCK4(dest), + IP6_ADDR_BLOCK5(dest), + IP6_ADDR_BLOCK6(dest), + IP6_ADDR_BLOCK7(dest), + IP6_ADDR_BLOCK8(dest))); + IP6_STATS_INC(ip6.rterr); + return ERR_RTE; + } + + NETIF_SET_HWADDRHINT(netif, addr_hint); + err = ip6_output_if(p, src, dest, hl, tc, nexth, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + return err; +} +#endif /* LWIP_NETIF_HWADDRHINT*/ + +#if LWIP_IPV6_MLD +/** + * Add a hop-by-hop options header with a router alert option and padding. + * + * Used by MLD when sending a Multicast listener report/done message. + * + * @param p the packet to which we will prepend the options header + * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6) + * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD) + * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise + */ +err_t +ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value) +{ + struct ip6_hbh_hdr * hbh_hdr; + + /* Move pointer to make room for hop-by-hop options header. */ + if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) { + LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n")); + IP6_STATS_INC(ip6.err); + return ERR_BUF; + } + + hbh_hdr = (struct ip6_hbh_hdr *)p->payload; + + /* Set fields. */ + hbh_hdr->_nexth = nexth; + hbh_hdr->_hlen = 0; + hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION; + hbh_hdr->_ra_opt_dlen = 2; + hbh_hdr->_ra_opt_data = value; + hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION; + hbh_hdr->_padn_opt_dlen = 0; + + return ERR_OK; +} +#endif /* LWIP_IPV6_MLD */ + +#if IP6_DEBUG +/* Print an IPv6 header by using LWIP_DEBUGF + * @param p an IPv6 packet, p->payload pointing to the IPv6 header + */ +void +ip6_debug_print(struct pbuf *p) +{ + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + + LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n")); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n", + IP6H_V(ip6hdr), + IP6H_TC(ip6hdr), + IP6H_FL(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n", + IP6H_PLEN(ip6hdr), + IP6H_NEXTH(ip6hdr), + IP6H_HOPLIM(ip6hdr))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->src)), + IP6_ADDR_BLOCK2(&(ip6hdr->src)), + IP6_ADDR_BLOCK3(&(ip6hdr->src)), + IP6_ADDR_BLOCK4(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->src)), + IP6_ADDR_BLOCK6(&(ip6hdr->src)), + IP6_ADDR_BLOCK7(&(ip6hdr->src)), + IP6_ADDR_BLOCK8(&(ip6hdr->src)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n", + IP6_ADDR_BLOCK1(&(ip6hdr->dest)), + IP6_ADDR_BLOCK2(&(ip6hdr->dest)), + IP6_ADDR_BLOCK3(&(ip6hdr->dest)), + IP6_ADDR_BLOCK4(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n", + IP6_ADDR_BLOCK5(&(ip6hdr->dest)), + IP6_ADDR_BLOCK6(&(ip6hdr->dest)), + IP6_ADDR_BLOCK7(&(ip6hdr->dest)), + IP6_ADDR_BLOCK8(&(ip6hdr->dest)))); + LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP6_DEBUG */ + +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_addr.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_addr.c new file mode 100644 index 0000000..66c1e0c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_addr.c @@ -0,0 +1,251 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * Functions for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +/* used by IP6_ADDR_ANY in ip6_addr.h */ +const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } }; + +#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') +#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) +#endif + +/** + * Check whether "cp" is a valid ascii representation + * of an IPv6 address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * + * @param cp IPv6 address in ascii representation (e.g. "FF01::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 +ip6addr_aton(const char *cp, ip6_addr_t *addr) +{ + u32_t addr_index, zero_blocks, current_block_index, current_block_value; + const char * s; + + /* Count the number of colons, to count the number of blocks in a "::" sequence + zero_blocks may be 1 even if there are no :: sequences */ + zero_blocks = 8; + for (s = cp; *s != 0; s++) { + if (*s == ':') + zero_blocks--; + else if (!isxdigit(*s)) + break; + } + + /* parse each block */ + addr_index = 0; + current_block_index = 0; + current_block_value = 0; + for (s = cp; *s != 0; s++) { + if (*s == ':') { + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + current_block_index++; + current_block_value = 0; + if (current_block_index > 7) { + /* address too long! */ + return 0; + } if (s[1] == ':') { + s++; + /* "::" found, set zeros */ + while (zero_blocks-- > 0) { + if (current_block_index & 0x1) { + addr_index++; + } + else { + if (addr) { + addr->addr[addr_index] = 0; + } + } + current_block_index++; + } + } + } else if (isxdigit(*s)) { + /* add current digit */ + current_block_value = (current_block_value << 4) + + (isdigit(*s) ? *s - '0' : + 10 + (islower(*s) ? *s - 'a' : *s - 'A')); + } else { + /* unexpected digit, space? CRLF? */ + break; + } + } + + if (addr) { + if (current_block_index & 0x1) { + addr->addr[addr_index++] |= current_block_value; + } + else { + addr->addr[addr_index] = current_block_value << 16; + } + } + + /* convert to network byte order. */ + if (addr) { + for (addr_index = 0; addr_index < 4; addr_index++) { + addr->addr[addr_index] = htonl(addr->addr[addr_index]); + } + } + + if (current_block_index != 7) { + return 0; + } + + return 1; +} + +/** + * Convert numeric IPv6 address into ASCII representation. + * returns ptr to static buffer; not reentrant! + * + * @param addr ip6 address in network order to convert + * @return pointer to a global static (!) buffer that holds the ASCII + * representation of addr + */ +char * +ip6addr_ntoa(const ip6_addr_t *addr) +{ + static char str[40]; + return ip6addr_ntoa_r(addr, str, 40); +} + +/** + * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used. + * + * @param addr ip6 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 * +ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen) +{ + u32_t current_block_index, current_block_value; + s32_t zero_flag, i; + + i = 0; + zero_flag = 0; /* used to indicate a zero chain for "::' */ + + for (current_block_index = 0; current_block_index < 8; current_block_index++) { + /* get the current 16-bit block */ + current_block_value = htonl(addr->addr[current_block_index >> 1]); + if ((current_block_index & 0x1) == 0) { + current_block_value = current_block_value >> 16; + } + current_block_value &= 0xffff; + + if (current_block_value == 0) { + /* generate empty block "::" */ + if (!zero_flag) { + if (current_block_index > 0) { + zero_flag = 1; + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + } + } + else { + if (current_block_index > 0) { + buf[i++] = ':'; + if (i >= buflen) return NULL; + } + + if ((current_block_value & 0xf000) == 0) { + zero_flag = 1; + } + else { + buf[i++] = xchar(((current_block_value & 0xf000) >> 12)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf00) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf00) >> 8)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + if (((current_block_value & 0xf0) == 0) && (zero_flag)) { + /* do nothing */ + } + else { + buf[i++] = xchar(((current_block_value & 0xf0) >> 4)); + zero_flag = 0; + if (i >= buflen) return NULL; + } + + buf[i++] = xchar((current_block_value & 0xf)); + if (i >= buflen) return NULL; + + zero_flag = 0; + } + } + + buf[i] = 0; + + return buf; +} +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_frag.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_frag.c new file mode 100644 index 0000000..4829ac3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/ip6_frag.c @@ -0,0 +1,716 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" +#include "lwip/ip6_frag.h" +#include "lwip/ip6.h" +#include "lwip/icmp6.h" +#include "lwip/nd6.h" +#include "lwip/lwip_ip.h" + +#include "lwip/pbuf.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + + +/** 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 IPv6 header, since it replaces + * the Fragment Header in memory in incoming fragments to keep + * track of the various fragments. + */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_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 + +/* static variables */ +static struct ip6_reassdata *reassdatagrams; +static u16_t ip6_reass_pbufcount; + +/* Forward declarations. */ +static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr); +#if IP_REASS_FREE_OLDEST +static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed); +#endif /* IP_REASS_FREE_OLDEST */ + +void +ip6_reass_tmr(void) +{ + struct ip6_reassdata *r, *tmp; + + r = reassdatagrams; + while (r != NULL) { + /* Decrement the timer. Once it reaches 0, + * clean up the incomplete fragment assembly */ + if (r->timer > 0) { + r->timer--; + r = r->next; + } else { + /* reassembly timed out */ + tmp = r; + /* get the next pointer before freeing */ + r = r->next; + /* free the helper struct and all enqueued pbufs */ + ip6_reass_free_complete_datagram(tmp); + } + } +} + +/** + * Free a datagram (struct ip6_reassdata) and all its pbufs. + * Updates the total count of enqueued pbufs (ip6_reass_pbufcount), + * sends an ICMP time exceeded packet. + * + * @param ipr datagram to free + */ +static void +ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr) +{ + struct ip6_reassdata *prev; + u16_t pbufs_freed = 0; + u8_t clen; + struct pbuf *p; + struct ip6_reass_helper *iprh; + +#if LWIP_ICMP6 + iprh = (struct ip6_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, move back to the original header (we are now pointing to Fragment header). */ + if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) { + LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0); + } + else { + icmp6_time_exceeded(p, ICMP6_TE_FRAG); + } + clen = pbuf_clen(p); + LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); + pbufs_freed += clen; + pbuf_free(p); + } +#endif /* LWIP_ICMP6 */ + + /* 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 ip6_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 ip6_reassdata from the list and free it. */ + if (ipr == reassdatagrams) { + reassdatagrams = ipr->next; + } else { + prev = reassdatagrams; + while (prev != NULL) { + if (prev->next == ipr) { + break; + } + prev = prev->next; + } + if (prev != NULL) { + prev->next = ipr->next; + } + } + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* Finally, update number of pbufs in reassembly queue */ + LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed); + ip6_reass_pbufcount -= pbufs_freed; +} + +#if IP_REASS_FREE_OLDEST +/** + * Free the oldest datagram to make room for enqueueing new fragments. + * The datagram ipr is not freed! + * + * @param ipr ip6_reassdata for the current fragment + * @param pbufs_needed number of pbufs needed to enqueue + * (used for freeing other datagrams if not enough space) + */ +static void +ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed) +{ + struct ip6_reassdata *r, *oldest; + + /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, + * but don't free the current datagram! */ + do { + r = oldest = reassdatagrams; + while (r != NULL) { + if (r != ipr) { + if (r->timer <= oldest->timer) { + /* older than the previous oldest */ + oldest = r; + } + } + r = r->next; + } + if (oldest != NULL) { + ip6_reass_free_complete_datagram(oldest); + } + } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL)); +} +#endif /* IP_REASS_FREE_OLDEST */ + +/** + * Reassembles incoming IPv6 fragments into an IPv6 datagram. + * + * @param p points to the IPv6 Fragment Header + * @param len the length of the payload (after Fragment Header) + * @return NULL if reassembly is incomplete, pbuf pointing to + * IPv6 Header if reassembly is complete + */ +struct pbuf * +ip6_reass(struct pbuf *p) +{ + struct ip6_reassdata *ipr, *ipr_prev; + struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; + struct ip6_frag_hdr * frag_hdr; + u16_t offset, len; + u8_t clen, valid = 1; + struct pbuf *q; + + IP6_FRAG_STATS_INC(ip6_frag.recv); + + frag_hdr = (struct ip6_frag_hdr *) p->payload; + + clen = pbuf_clen(p); + + offset = ntohs(frag_hdr->_fragment_offset); + + /* Calculate fragment length from IPv6 payload length. + * Adjust for headers before Fragment Header. + * And finally adjust by Fragment Header length. */ + len = ntohs(ip6_current_header()->_plen); + len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN; + len -= IP6_FRAG_HLEN; + + /* 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_prev = NULL; 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 ((frag_hdr->_identification == ipr->identification) && + ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) && + ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) { + IP6_FRAG_STATS_INC(ip6_frag.cachehit); + break; + } + ipr_prev = ipr; + } + + if (ipr == NULL) { + /* Enqueue a new datagram into the datagram queue */ + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr == NULL) { +#if IP_REASS_FREE_OLDEST + /* Make room and try again. */ + ip6_reass_remove_oldest_datagram(ipr, clen); + ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA); + if (ipr != NULL) { + /* re-search ipr_prev since it might have been removed */ + for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } else +#endif /* IP_REASS_FREE_OLDEST */ + { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + memset(ipr, 0, sizeof(struct ip6_reassdata)); + ipr->timer = IP_REASS_MAXAGE; + + /* enqueue the new structure to the front of the list */ + ipr->next = reassdatagrams; + reassdatagrams = ipr; + + /* Use the current IPv6 header for src/dest address reference. + * Eventually, we will replace it when we get the first fragment + * (it might be this one, in any case, it is done later). */ + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + + /* copy the fragmented packet id. */ + ipr->identification = frag_hdr->_identification; + + /* copy the nexth field */ + ipr->nexth = frag_hdr->_nexth; + } + + /* Check if we are allowed to enqueue more datagrams. */ + if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { +#if IP_REASS_FREE_OLDEST + ip6_reass_remove_oldest_datagram(ipr, clen); + if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) { + /* re-search ipr_prev since it might have been removed */ + for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) { + if (ipr_prev->next == ipr) { + break; + } + } + } else +#endif /* IP_REASS_FREE_OLDEST */ + { + /* @todo: send ICMPv6 time exceeded here? */ + /* drop this pbuf */ + IP6_FRAG_STATS_INC(ip6_frag.memerr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } + + /* Overwrite Fragment Header with our own helper struct. */ + iprh = (struct ip6_reass_helper *)p->payload; + LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN", + sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN); + iprh->next_pbuf = NULL; + iprh->start = (offset & IP6_FRAG_OFFSET_MASK); + iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len; + + /* find the right place to insert this pbuf */ + /* 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 ip6_reass_helper*)q->payload; + if (iprh->start < iprh_tmp->start) { +#if IP_REASS_CHECK_OVERLAP + if (iprh->end > iprh_tmp->start) { + /* fragment overlaps with following, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + if (iprh_prev != NULL) { + if (iprh->start < iprh_prev->end) { + /* fragment overlaps with previous, throw away */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; + } + } +#endif /* IP_REASS_CHECK_OVERLAP */ + /* the new pbuf should be inserted before this */ + iprh->next_pbuf = q; + if (iprh_prev != NULL) { + /* not the fragment with the lowest offset */ + iprh_prev->next_pbuf = p; + } else { + /* fragment with the lowest offset */ + ipr->p = p; + } + break; + } else if(iprh->start == iprh_tmp->start) { + /* received the same datagram twice: no need to keep the datagram */ + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#if IP_REASS_CHECK_OVERLAP + } else if(iprh->start < iprh_tmp->end) { + /* overlap: no need to keep the new datagram */ + IP6_FRAG_STATS_INC(ip6_frag.proterr); + IP6_FRAG_STATS_INC(ip6_frag.drop); + goto nullreturn; +#endif /* IP_REASS_CHECK_OVERLAP */ + } else { + /* Check if the fragments received so far have no gaps. */ + 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 = 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 = p; + } + } + + /* 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 */ + ip6_reass_pbufcount += clen; + + /* Remember IPv6 header if this is the first fragment. */ + if (iprh->start == 0) { + ipr->iphdr = (struct ip6_hdr *)ip6_current_header(); + } + + /* If this is the last fragment, calculate total packet length. */ + if ((offset & IP6_FRAG_MORE_FLAG) == 0) { + ipr->datagram_len = iprh->end; + } + + /* Additional validity tests: we have received first and last fragment. */ + iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload; + if (iprh_tmp->start != 0) { + valid = 0; + } + if (ipr->datagram_len == 0) { + valid = 0; + } + + /* Final validity test: no gaps between current and last fragment. */ + iprh_prev = iprh; + q = iprh->next_pbuf; + while ((q != NULL) && valid) { + iprh = (struct ip6_reass_helper*)q->payload; + if (iprh_prev->end != iprh->start) { + valid = 0; + break; + } + iprh_prev = iprh; + q = iprh->next_pbuf; + } + + if (valid) { + /* All fragments have been received */ + u8_t* iphdr_ptr; + + /* chain together the pbufs contained within the ip6_reassdata list. */ + iprh = (struct ip6_reass_helper*) ipr->p->payload; + while(iprh != NULL) { + + if (iprh->next_pbuf != NULL) { + /* Save next helper struct (will be hidden in next step). */ + iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload; + + /* hide the fragment header for every succeeding fragment */ + pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN); + pbuf_cat(ipr->p, iprh->next_pbuf); + } + else { + iprh_tmp = NULL; + } + + iprh = iprh_tmp; + } + + /* Adjust datagram length by adding header lengths. */ + ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr) + + IP6_FRAG_HLEN + - IP6_HLEN ; + + /* Set payload length in ip header. */ + ipr->iphdr->_plen = htons(ipr->datagram_len); + + /* Get the first pbuf. */ + p = ipr->p; + + /* Restore Fragment Header in first pbuf. Mark as "single fragment" + * packet. Restore nexth. */ + frag_hdr = (struct ip6_frag_hdr *) p->payload; + frag_hdr->_nexth = ipr->nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = 0; + frag_hdr->_identification = 0; + + /* release the sources allocate for the fragment queue entry */ + 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", ipr_prev != NULL); + ipr_prev->next = ipr->next; + } + iphdr_ptr = (u8_t*)ipr->iphdr; + memp_free(MEMP_IP6_REASSDATA, ipr); + + /* adjust the number of pbufs currently queued for reassembly. */ + ip6_reass_pbufcount -= pbuf_clen(p); + + /* Move pbuf back to IPv6 header. */ + if (pbuf_header(p, (u8_t*)p->payload - iphdr_ptr)) { + LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0); + pbuf_free(p); + return NULL; + } + + /* Return the pbuf chain */ + return p; + } + /* the datagram is not (yet?) reassembled completely */ + return NULL; + +nullreturn: + pbuf_free(p); + return NULL; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG + +/** Allocate a new struct pbuf_custom_ref */ +static struct pbuf_custom_ref* +ip6_frag_alloc_pbuf_custom_ref(void) +{ + return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); +} + +/** Free a struct pbuf_custom_ref */ +static void +ip6_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 +ip6_frag_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); + } + ip6_frag_free_pbuf_custom_ref(pcr); +} + +/** + * Fragment an IPv6 datagram if too large for the netif or path MTU. + * + * Chop the datagram in MTU sized chunks and send them in order + * by pointing PBUF_REFs into p + * + * @param p ipv6 packet to send + * @param netif the netif on which to send + * @param dest destination ipv6 address to which to send + * + * @return ERR_OK if sent successfully, err_t otherwise + */ +err_t +ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest) +{ + struct ip6_hdr *original_ip6hdr; + struct ip6_hdr *ip6hdr; + struct ip6_frag_hdr * frag_hdr; + struct pbuf *rambuf; + struct pbuf *newpbuf; + static u32_t identification; + u16_t nfb; + u16_t left, cop; + u16_t mtu; + u16_t fragment_offset = 0; + u16_t last; + u16_t poff = IP6_HLEN; + u16_t newpbuflen = 0; + u16_t left_to_copy; + + identification++; + + original_ip6hdr = (struct ip6_hdr *)p->payload; + + mtu = nd6_get_destination_mtu(dest, netif); + + /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */ + left = p->tot_len - IP6_HLEN; + + nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK; + + while (left) { + last = (left <= nfb); + + /* Fill this fragment */ + cop = last ? left : nfb; + + /* When not using a static buffer, create a chain of pbufs. + * The first will be a PBUF_RAM holding the link, IPv6, and Fragment 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, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM); + if (rambuf == NULL) { + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + LWIP_ASSERT("this needs a pbuf in one piece!", + (p->len >= (IP6_HLEN + IP6_FRAG_HLEN))); + SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN); + ip6hdr = (struct ip6_hdr *)rambuf->payload; + frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN); + + /* Can just adjust p directly for needed offset. */ + p->payload = (u8_t *)p->payload + poff; + p->len -= poff; + p->tot_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 = ip6_frag_alloc_pbuf_custom_ref(); + if (pcr == NULL) { + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + 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) { + ip6_frag_free_pbuf_custom_ref(pcr); + pbuf_free(rambuf); + IP6_FRAG_STATS_INC(ip6_frag.memerr); + return ERR_MEM; + } + pbuf_ref(p); + pcr->original = p; + pcr->pc.custom_free_function = ip6_frag_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; + + /* Set headers */ + frag_hdr->_nexth = original_ip6hdr->_nexth; + frag_hdr->reserved = 0; + frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG)); + frag_hdr->_identification = htonl(identification); + + IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT); + IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN); + + /* No need for separate header pbuf - we allowed room for it in rambuf + * when allocated. + */ + IP6_FRAG_STATS_INC(ip6_frag.xmit); + netif->output_ip6(netif, rambuf, dest); + + /* 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); + left -= cop; + fragment_offset += cop; + } + return ERR_OK; +} + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/mld6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/mld6.c new file mode 100644 index 0000000..a884a39 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/mld6.c @@ -0,0 +1,586 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +/* Based on igmp.c implementation of igmp v2 protocol */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/mld6.h" +#include "lwip/icmp6.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/lwip_ip.h" +#include "lwip/inet_chksum.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include + + +/* + * MLD constants + */ +#define MLD6_HL 1 +#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500) + +#define MLD6_GROUP_NON_MEMBER 0 +#define MLD6_GROUP_DELAYING_MEMBER 1 +#define MLD6_GROUP_IDLE_MEMBER 2 + + +/* The list of joined groups. */ +static struct mld_group* mld_group_list; + + +/* Forward declarations. */ +static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr); +static err_t mld6_free_group(struct mld_group *group); +static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); +static void mld6_send(struct mld_group *group, u8_t type); + + +/** + * Stop MLD processing on interface + * + * @param netif network interface on which stop MLD processing + */ +err_t +mld6_stop(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + struct mld_group *prev = NULL; + struct mld_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 == mld_group_list) { + mld_group_list = next; + } + /* is there a "previous" group defined? */ + if (prev != NULL) { + prev->next = next; + } + /* disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER); + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + } else { + /* change the "previous" */ + prev = group; + } + /* move to "next" */ + group = next; + } + return ERR_OK; +} + +/** + * Report MLD memberships for this interface + * + * @param netif network interface on which report MLD memberships + */ +void +mld6_report_groups(struct netif *netif) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->netif == netif) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + group = group->next; + } +} + +/** + * Search for a group that is joined on a netif + * + * @param ifp the network interface for which to look + * @param addr the group ipv6 address to search for + * @return a struct mld_group* if the group has been found, + * NULL if the group wasn't found. + */ +struct mld_group * +mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) { + return group; + } + group = group->next; + } + + return NULL; +} + + +/** + * create a new group + * + * @param ifp the network interface for which to create + * @param addr the new group ipv6 + * @return a struct mld_group*, + * NULL on memory error. + */ +static struct mld_group * +mld6_new_group(struct netif *ifp, ip6_addr_t *addr) +{ + struct mld_group *group; + + group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP); + if (group != NULL) { + group->netif = ifp; + ip6_addr_set(&(group->group_address), addr); + group->timer = 0; /* Not running */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; + group->next = mld_group_list; + + mld_group_list = group; + } + + return group; +} + +/** + * Remove a group in the mld_group_list and free + * + * @param group the group to remove + * @return ERR_OK if group was removed from the list, an err_t otherwise + */ +static err_t +mld6_free_group(struct mld_group *group) +{ + err_t err = ERR_OK; + + /* Is it the first group? */ + if (mld_group_list == group) { + mld_group_list = group->next; + } else { + /* look for group further down the list */ + struct mld_group *tmpGroup; + for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) { + if (tmpGroup->next == group) { + tmpGroup->next = group->next; + break; + } + } + /* Group not find group */ + if (tmpGroup == NULL) + err = ERR_ARG; + } + /* free group */ + memp_free(MEMP_MLD6_GROUP, group); + + return err; +} + + +/** + * Process an input MLD message. Called by icmp6_input. + * + * @param p the mld packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +mld6_input(struct pbuf *p, struct netif *inp) +{ + struct mld_header * mld_hdr; + struct mld_group* group; + + MLD6_STATS_INC(mld6.recv); + + /* Check that mld header fits in packet. */ + if (p->len < sizeof(struct mld_header)) { + /* TODO debug message */ + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + MLD6_STATS_INC(mld6.drop); + return; + } + + mld_hdr = (struct mld_header *)p->payload; + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ + { + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { + MLD6_STATS_INC(mld6.rx_general); + /* Report all groups, except all nodes group, and if-local groups. */ + group = mld_group_list; + while (group != NULL) { + if ((group->netif == inp) && + (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && + (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + group = group->next; + } + } + else { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_group); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* Schedule a report. */ + mld6_delayed_report(group, mld_hdr->max_resp_delay); + } + } + break; /* ICMP6_TYPE_MLQ */ + } + case ICMP6_TYPE_MLR: /* Multicast listener report. */ + { + /* Have we joined this group? + * We use IP6 destination address to have a memory aligned copy. + * mld_hdr->multicast_address should be the same. */ + MLD6_STATS_INC(mld6.rx_report); + group = mld6_lookfor_group(inp, ip6_current_dest_addr()); + if (group != NULL) { + /* If we are waiting to report, cancel it. */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + group->timer = 0; /* stopped */ + group->group_state = MLD6_GROUP_IDLE_MEMBER; + group->last_reporter_flag = 0; + } + } + break; /* ICMP6_TYPE_MLR */ + } + case ICMP6_TYPE_MLD: /* Multicast listener done. */ + { + /* Do nothing, router will query us. */ + break; /* ICMP6_TYPE_MLD */ + } + default: + MLD6_STATS_INC(mld6.proterr); + MLD6_STATS_INC(mld6.drop); + break; + } + + pbuf_free(p); +} + +/** + * Join a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * join a new group. If IP6_ADDR_ANY, join on all netifs + * @param groupaddr the ipv6 address of the group to join + * @return ERR_OK if group was joined on the netif(s), an err_t otherwise + */ +err_t +mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we join this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group or create a new one if not found */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group == NULL) { + /* Joining a new group. Create a new group entry. */ + group = mld6_new_group(netif, groupaddr); + if (group == NULL) { + return ERR_MEM; + } + + /* Activate this address on the MAC layer. */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER); + } + + /* Report our membership. */ + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); + } + + /* Increment group use */ + group->use++; + err = ERR_OK; + } + + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + +/** + * Leave a group on a network interface. + * + * @param srcaddr ipv6 address of the network interface which should + * leave the group. If IP6_ISANY, leave on all netifs + * @param groupaddr the ipv6 address of the group to leave + * @return ERR_OK if group was left on the netif(s), an err_t otherwise + */ +err_t +mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr) +{ + err_t err = ERR_VAL; /* no matching interface */ + struct mld_group *group; + struct netif *netif; + u8_t match; + u8_t i; + + /* loop through netif's */ + netif = netif_list; + while (netif != NULL) { + /* Should we leave this interface ? */ + match = 0; + if (ip6_addr_isany(srcaddr)) { + match = 1; + } + else { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) { + match = 1; + break; + } + } + } + if (match) { + /* find group */ + group = mld6_lookfor_group(netif, groupaddr); + + if (group != NULL) { + /* Leave 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) { + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(group, ICMP6_TYPE_MLD); + } + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { + netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER); + } + + /* Free the group */ + mld6_free_group(group); + } else { + /* Decrement group use */ + group->use--; + } + /* Leave on this interface */ + err = ERR_OK; + } + } + /* proceed to next network interface */ + netif = netif->next; + } + + return err; +} + + +/** + * Periodic timer for mld processing. Must be called every + * MLD6_TMR_INTERVAL milliseconds (100). + * + * When a delaying member expires, a membership report is sent. + */ +void +mld6_tmr(void) +{ + struct mld_group *group = mld_group_list; + + while (group != NULL) { + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { + /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ + if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { + MLD6_STATS_INC(mld6.tx_report); + mld6_send(group, ICMP6_TYPE_MLR); + group->group_state = MLD6_GROUP_IDLE_MEMBER; + } + } + } + group = group->next; + } +} + +/** + * Schedule a delayed membership report for a group + * + * @param group the mld_group for which "delaying" membership report + * should be sent + * @param maxresp the max resp delay provided in the query + */ +static void +mld6_delayed_report(struct mld_group *group, u16_t maxresp) +{ + /* Convert maxresp from milliseconds to tmr ticks */ + maxresp = maxresp / MLD6_TMR_INTERVAL; + if (maxresp == 0) { + maxresp = 1; + } + +#ifdef LWIP_RAND + /* Randomize maxresp. (if LWIP_RAND is supported) */ + maxresp = LWIP_RAND() % maxresp; + if (maxresp == 0) { + maxresp = 1; + } +#endif /* LWIP_RAND */ + + /* Apply timer value if no report has been scheduled already. */ + if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) || + ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) && + ((group->timer == 0) || (maxresp < group->timer)))) { + group->timer = maxresp; + group->group_state = MLD6_GROUP_DELAYING_MEMBER; + } +} + +/** + * Send a MLD message (report or done). + * + * An IPv6 hop-by-hop options header with a router alert option + * is prepended. + * + * @param group the group to report or quit + * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) + */ +static void +mld6_send(struct mld_group *group, u8_t type) +{ + struct mld_header * mld_hdr; + struct pbuf * p; + ip6_addr_t * src_addr; + + /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) { + /* We couldn't allocate a suitable pbuf. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + MLD6_STATS_INC(mld6.memerr); + return; + } + + /* Move to make room for Hop-by-hop options header. */ + if (pbuf_header(p, -IP6_HBH_HLEN)) { + pbuf_free(p); + MLD6_STATS_INC(mld6.lenerr); + return; + } + + /* Select our source address. */ + if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) { + /* This is a special case, when we are performing duplicate address detection. + * We must join the multicast group, but we don't have a valid address yet. */ + src_addr = IP6_ADDR_ANY; + } else { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(group->netif, 0); + } + + /* MLD message header pointer. */ + mld_hdr = (struct mld_header *)p->payload; + + /* Set fields. */ + mld_hdr->type = type; + mld_hdr->code = 0; + mld_hdr->chksum = 0; + mld_hdr->max_resp_delay = 0; + mld_hdr->reserved = 0; + ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address)); + +#if CHECKSUM_GEN_ICMP6 + mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, + src_addr, &(group->group_address)); +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Add hop-by-hop headers options: router alert with MLD value. */ + ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); + + /* Send the packet out. */ + MLD6_STATS_INC(mld6.xmit); + ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), + MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif); + pbuf_free(p); +} + + + +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/nd6.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/nd6.c new file mode 100644 index 0000000..2a46045 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/ipv6/nd6.c @@ -0,0 +1,1793 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/nd6.h" +#include "lwip/pbuf.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp6.h" +#include "lwip/mld6.h" +#include "lwip/lwip_ip.h" +#include "lwip/stats.h" + +#include + + +/* Router tables. */ +struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS]; +struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS]; +struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES]; +struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS]; + +/* Default values, can be updated by a RA message. */ +u32_t reachable_time = LWIP_ND6_REACHABLE_TIME; +u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */ + +/* Index for cache entries. */ +static u8_t nd6_cached_neighbor_index; +static u8_t nd6_cached_destination_index; + +/* Multicast address holder. */ +static ip6_addr_t multicast_address; + +/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */ +static u8_t nd6_ra_buffer[sizeof(struct prefix_option)]; + +/* Forward declarations. */ +static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_neighbor_cache_entry(void); +static void nd6_free_neighbor_cache_entry(s8_t i); +static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr); +static s8_t nd6_new_destination_cache_entry(void); +static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif); +static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif); +static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); +static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif); + +#define ND6_SEND_FLAG_MULTICAST_DEST 0x01 +#define ND6_SEND_FLAG_ALLNODES_DEST 0x02 +static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags); +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +static void nd6_send_rs(struct netif * netif); +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +#if LWIP_ND6_QUEUEING +static void nd6_free_q(struct nd6_q_entry *q); +#else /* LWIP_ND6_QUEUEING */ +#define nd6_free_q(q) pbuf_free(q) +#endif /* LWIP_ND6_QUEUEING */ +static void nd6_send_q(s8_t i); + + +/** + * Process an incoming neighbor discovery message + * + * @param p the nd packet, p->payload pointing to the icmpv6 header + * @param inp the netif on which this packet was received + */ +void +nd6_input(struct pbuf *p, struct netif *inp) +{ + u8_t msg_type; + s8_t i; + + ND6_STATS_INC(nd6.recv); + + msg_type = *((u8_t *)p->payload); + switch (msg_type) { + case ICMP6_TYPE_NA: /* Neighbor Advertisement. */ + { + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that na header fits in packet. */ + if (p->len < (sizeof(struct na_header))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + na_hdr = (struct na_header *)p->payload; + + /* Unsolicited NA?*/ + if (ip6_addr_ismulticast(ip6_current_dest_addr())) { + /* This is an unsolicited NA. + * link-layer changed? + * part of DAD mechanism? */ + + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* If the target address matches this netif, it is a DAD response. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) { + /* We are using a duplicate address. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + +#if LWIP_IPV6_MLD + /* Leave solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]); + mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address); +#endif /* LWIP_IPV6_MLD */ + + + + +#if LWIP_IPV6_AUTOCONFIG + /* Check to see if this address was autoconfigured. */ + if (!ip6_addr_islinklocal(ip6_current_dest_addr())) { + i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (i >= 0) { + /* Mark this prefix as duplicate, so that we don't use it + * to generate this address again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE; + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + + pbuf_free(p); + return; + } + } +#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */ + + /* This is an unsolicited NA, most likely there was a LLADDR change. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i >= 0) { + if (na_hdr->flags & ND6_FLAG_OVERRIDE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + } + } + else { + /* This is a solicited NA. + * neighbor address resolution response? + * neighbor unreachability detection response? */ + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address)); + + /* Find the cache entry corresponding to this na. */ + i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr()); + if (i < 0) { + /* We no longer care about this target address. drop it. */ + pbuf_free(p); + return; + } + + /* Update cache entry. */ + neighbor_cache[i].netif = inp; + neighbor_cache[i].counter.reachable_time = reachable_time; + if ((na_hdr->flags & ND6_FLAG_OVERRIDE) || + (neighbor_cache[i].state == ND6_INCOMPLETE)) { + /* Check that link-layer address option also fits in packet. */ + if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + } + neighbor_cache[i].state = ND6_REACHABLE; + + /* Send queued packets, if any. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + } + + break; /* ICMP6_TYPE_NA */ + } + case ICMP6_TYPE_NS: /* Neighbor solicitation. */ + { + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + u8_t accepted; + + /* Check that ns header fits in packet. */ + if (p->len < sizeof(struct ns_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ns_hdr = (struct ns_header *)p->payload; + + /* Check if there is a link-layer address provided. Only point to it if in this buffer. */ + lladdr_opt = NULL; + if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + } + + /* Check if the target address is configured on the receiving netif. */ + accepted = 0; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) || + (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) && + ip6_addr_isany(ip6_current_src_addr()))) && + ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + accepted = 1; + break; + } + } + + /* NS not for us? */ + if (!accepted) { + pbuf_free(p); + return; + } + + /* Check for ANY address in src (DAD algorithm). */ + if (ip6_addr_isany(ip6_current_src_addr())) { + /* Sender is validating this address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) { + /* Send a NA back so that the sender does not use this address. */ + nd6_send_na(inp, netif_ip6_addr(inp, i), ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST); + if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) { + /* We shouldn't use this address either. */ + netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID); + } + } + } + } + else { + /* Sender is trying to resolve our address. */ + /* Verify that they included their own link-layer address. */ + if (lladdr_opt == NULL) { + /* Not a valid message. */ + pbuf_free(p); + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + return; + } + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if ( i>= 0) { + /* We already have a record for the solicitor. */ + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + + /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + else + { + /* Add their IPv6 address and link-layer address to neighbor cache. + * We will need it at least to send a unicast NA message, but most + * likely we will also be communicating with this node soon. */ + i = nd6_new_neighbor_cache_entry(); + if (i < 0) { + /* We couldn't assign a cache entry for this neighbor. + * we won't be able to reply. drop it. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address)); + + /* Send back a NA for us. Allocate the reply pbuf. */ + nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE); + } + + break; /* ICMP6_TYPE_NS */ + } + case ICMP6_TYPE_RA: /* Router Advertisement. */ + { + struct ra_header * ra_hdr; + u8_t * buffer; /* Used to copy options. */ + u16_t offset; + + /* Check that RA header fits in packet. */ + if (p->len < sizeof(struct ra_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + ra_hdr = (struct ra_header *)p->payload; + + /* If we are sending RS messages, stop. */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + inp->rs_count = 0; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + /* Get the matching default router entry. */ + i = nd6_get_router(ip6_current_src_addr(), inp); + if (i < 0) { + /* Create a new router entry. */ + i = nd6_new_router(ip6_current_src_addr(), inp); + } + + if (i < 0) { + /* Could not create a new router entry. */ + pbuf_free(p); + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Re-set invalidation timer. */ + default_router_list[i].invalidation_timer = ra_hdr->router_lifetime; + + /* Re-set default timer values. */ +#if LWIP_ND6_ALLOW_RA_UPDATES + if (ra_hdr->retrans_timer > 0) { + retrans_timer = ra_hdr->retrans_timer; + } + if (ra_hdr->reachable_time > 0) { + reachable_time = ra_hdr->reachable_time; + } +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + + /* TODO set default hop limit... */ + /* ra_hdr->current_hop_limit;*/ + + /* Update flags in local entry (incl. preference). */ + default_router_list[i].flags = ra_hdr->flags; + + /* Offset to options. */ + offset = sizeof(struct ra_header); + + /* Process each option. */ + while ((p->tot_len - offset) > 0) { + if (p->len == p->tot_len) { + /* no need to copy from contiguous pbuf */ + buffer = &((u8_t*)p->payload)[offset]; + } else { + buffer = nd6_ra_buffer; + pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset); + } + switch (buffer[0]) { + case ND6_OPTION_TYPE_SOURCE_LLADDR: + { + struct lladdr_option * lladdr_opt; + lladdr_opt = (struct lladdr_option *)buffer; + if ((default_router_list[i].neighbor_entry != NULL) && + (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) { + SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len); + default_router_list[i].neighbor_entry->state = ND6_REACHABLE; + default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time; + } + break; + } + case ND6_OPTION_TYPE_MTU: + { + struct mtu_option * mtu_opt; + mtu_opt = (struct mtu_option *)buffer; + if (mtu_opt->mtu >= 1280) { +#if LWIP_ND6_ALLOW_RA_UPDATES + inp->mtu = mtu_opt->mtu; +#endif /* LWIP_ND6_ALLOW_RA_UPDATES */ + } + break; + } + case ND6_OPTION_TYPE_PREFIX_INFO: + { + struct prefix_option * prefix_opt; + prefix_opt = (struct prefix_option *)buffer; + + if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) { + /* Add to on-link prefix list. */ + s8_t prefix; + + /* Get a memory-aligned copy of the prefix. */ + ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix)); + + /* find cache entry for this prefix. */ + prefix = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp); + if (prefix < 0) { + /* Create a new cache entry. */ + prefix = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp); + } + if (prefix >= 0) { + prefix_list[prefix].invalidation_timer = prefix_opt->valid_lifetime; + +#if LWIP_IPV6_AUTOCONFIG + if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) { + /* Mark prefix as autonomous, so that address autoconfiguration can take place. + * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/ + prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS; + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + break; + } + case ND6_OPTION_TYPE_ROUTE_INFO: + { + /* TODO implement preferred routes. + struct route_option * route_opt; + route_opt = (struct route_option *)buffer;*/ + + break; + } + default: + /* Unrecognized option, abort. */ + ND6_STATS_INC(nd6.proterr); + break; + } + offset += 8 * ((u16_t)buffer[1]); + } + + break; /* ICMP6_TYPE_RA */ + } + case ICMP6_TYPE_RD: /* Redirect */ + { + struct redirect_header * redir_hdr; + struct lladdr_option * lladdr_opt; + + /* Check that Redir header fits in packet. */ + if (p->len < sizeof(struct redirect_header)) { + /* TODO debug message */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + redir_hdr = (struct redirect_header *)p->payload; + + lladdr_opt = NULL; + if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) { + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header)); + } + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address)); + + /* Find dest address in cache */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Set the new target address. */ + ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address)); + + /* If Link-layer address of other router is given, try to add to neighbor cache. */ + if (lladdr_opt != NULL) { + if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) { + /* Copy target address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address)); + + i = nd6_find_neighbor_cache_entry(ip6_current_src_addr()); + if (i < 0) { + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + neighbor_cache[i].netif = inp; + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr()); + + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + if (i >= 0) { + if (neighbor_cache[i].state == ND6_INCOMPLETE) { + MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len); + /* Receiving a message does not prove reachability: only in one direction. + * Delay probe in case we get confirmation of reachability from upper layer (TCP). */ + neighbor_cache[i].state = ND6_DELAY; + neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME; + } + } + } + } + break; /* ICMP6_TYPE_RD */ + } + case ICMP6_TYPE_PTB: /* Packet too big */ + { + struct icmp6_hdr *icmp6hdr; /* Packet too big message */ + struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */ + + /* Check that ICMPv6 header + IPv6 header fit in payload */ + if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) { + /* drop short packets */ + pbuf_free(p); + ND6_STATS_INC(nd6.lenerr); + ND6_STATS_INC(nd6.drop); + return; + } + + icmp6hdr = (struct icmp6_hdr *)p->payload; + ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr)); + + /* Copy original destination address to current source address, to have an aligned copy. */ + ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest)); + + /* Look for entry in destination cache. */ + i = nd6_find_destination_cache_entry(ip6_current_src_addr()); + if (i < 0) { + /* Destination not in cache, drop packet. */ + pbuf_free(p); + return; + } + + /* Change the Path MTU. */ + destination_cache[i].pmtu = icmp6hdr->data; + + break; /* ICMP6_TYPE_PTB */ + } + + default: + ND6_STATS_INC(nd6.proterr); + ND6_STATS_INC(nd6.drop); + break; /* default */ + } + + pbuf_free(p); +} + + +/** + * Periodic timer for Neighbor discovery functions: + * + * - Update neighbor reachability states + * - Update destination cache entries age + * - Update invalidation timers of default routers and on-link prefixes + * - Perform duplicate address detection (DAD) for our addresses + * - Send router solicitations + */ +void +nd6_tmr(void) +{ + s8_t i, j; + struct netif * netif; + + /* Process neighbor entries. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + switch (neighbor_cache[i].state) { + case ND6_INCOMPLETE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST); + } + break; + case ND6_REACHABLE: + /* Send queued packets, if any are left. Should have been sent already. */ + if (neighbor_cache[i].q != NULL) { + nd6_send_q(i); + } + if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) { + /* Change to stale state. */ + neighbor_cache[i].state = ND6_STALE; + neighbor_cache[i].counter.stale_time = 0; + } + else { + neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_STALE: + neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL; + break; + case ND6_DELAY: + if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) { + /* Change to PROBE state. */ + neighbor_cache[i].state = ND6_PROBE; + neighbor_cache[i].counter.probes_sent = 0; + } + else { + neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL; + } + break; + case ND6_PROBE: + if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) { + /* Retries exceeded. */ + nd6_free_neighbor_cache_entry(i); + } + else { + /* Send a NS for this entry. */ + neighbor_cache[i].counter.probes_sent++; + nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0); + } + break; + case ND6_NO_ENTRY: + default: + /* Do nothing. */ + break; + } + } + + /* Process destination entries. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + destination_cache[i].age++; + } + + /* Process router entries. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (default_router_list[i].neighbor_entry != NULL) { + /* Active entry. */ + if (default_router_list[i].invalidation_timer > 0) { + default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + } + if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + /* Less than 1 second remaining. Clear this entry. */ + default_router_list[i].neighbor_entry->isrouter = 0; + default_router_list[i].neighbor_entry = NULL; + default_router_list[i].invalidation_timer = 0; + default_router_list[i].flags = 0; + } + } + } + + /* Process prefix entries. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) { + prefix_list[i].invalidation_timer = 0; + } + if ((prefix_list[i].invalidation_timer > 0) && + (prefix_list[i].netif != NULL)) { + prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000; + +#if LWIP_IPV6_AUTOCONFIG + /* Initiate address autoconfiguration for this prefix, if conditions are met. */ + if (prefix_list[i].netif->ip6_autoconfig_enabled && + (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) && + !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) { + /* Try to get an address on this netif that is invalid. + * Skip 0 index (link-local address) */ + for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) { + if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDR_INVALID) { + /* Generate an address using this prefix and interface ID from link-local address. */ + prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0]; + prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1]; + prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2]; + prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3]; + + /* Mark it as tentative (DAD will be performed if configured). */ + netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE); + + /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */ + prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED; + + /* Exit loop. */ + break; + } + } + } +#endif /* LWIP_IPV6_AUTOCONFIG */ + } + } + + + /* Process our own addresses, if DAD configured. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) { + if (ip6_addr_istentative(netif->ip6_addr_state[i])) { + if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) { + /* No NA received in response. Mark address as valid. */ + netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED; + /* TODO implement preferred and valid lifetimes. */ + } + else if (netif->flags & NETIF_FLAG_UP) { +#if LWIP_IPV6_MLD + if ((netif->ip6_addr_state[i] & 0x07) == 0) { + /* Join solicited node multicast group. */ + ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]); + mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address); + } +#endif /* LWIP_IPV6_MLD */ + /* Send a NS for this address. */ + nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST); + (netif->ip6_addr_state[i])++; + /* TODO send max 1 NS per tmr call? enable return*/ + /*return;*/ + } + } + } + } + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send router solicitation messages, if necessary. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) { + nd6_send_rs(netif); + netif->rs_count--; + } + } +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +} + +/** + * Send a neighbor solicitation message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct ns_header * ns_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + + if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) { + /* Use link-local address as source address. */ + src_addr = netif_ip6_addr(netif, 0); + } else { + src_addr = IP6_ADDR_ANY; + } + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + ns_hdr = (struct ns_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header)); + + ns_hdr->type = ICMP6_TYPE_NS; + ns_hdr->code = 0; + ns_hdr->chksum = 0; + ns_hdr->reserved = 0; + ip6_addr_set(&(ns_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + target_addr = &multicast_address; + } + +#if CHECKSUM_GEN_ICMP6 + ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + target_addr); +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, (src_addr == IP6_ADDR_ANY) ? NULL : src_addr, target_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +/** + * Send a neighbor advertisement message + * + * @param netif the netif on which to send the message + * @param target_addr the IPv6 target address for the ND message + * @param flags one of ND6_SEND_FLAG_* + */ +static void +nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags) +{ + struct na_header * na_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + ip6_addr_t * dest_addr; + + /* Use link-local address as source address. */ + /* src_addr = &(netif->ip6_addr[0]); */ + /* Use target address as source address. */ + src_addr = target_addr; + + /* Allocate a packet. */ + p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM); + if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + na_hdr = (struct na_header *)p->payload; + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header)); + + na_hdr->type = ICMP6_TYPE_NA; + na_hdr->code = 0; + na_hdr->chksum = 0; + na_hdr->flags = flags & 0xf0; + na_hdr->reserved[0] = 0; + na_hdr->reserved[1] = 0; + na_hdr->reserved[2] = 0; + ip6_addr_set(&(na_hdr->target_address), target_addr); + + lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + + /* Generate the solicited node address for the target address. */ + if (flags & ND6_SEND_FLAG_MULTICAST_DEST) { + ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]); + dest_addr = &multicast_address; + } + else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) { + ip6_addr_set_allnodes_linklocal(&multicast_address); + dest_addr = &multicast_address; + } + else { + dest_addr = ip6_current_src_addr(); + } + +#if CHECKSUM_GEN_ICMP6 + na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + dest_addr); +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, dest_addr, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT +/** + * Send a router solicitation message + * + * @param netif the netif on which to send the message + */ +static void +nd6_send_rs(struct netif * netif) +{ + struct rs_header * rs_hdr; + struct lladdr_option * lladdr_opt; + struct pbuf * p; + ip6_addr_t * src_addr; + u16_t packet_len; + + /* Link-local source address, or unspecified address? */ + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { + src_addr = netif_ip6_addr(netif, 0); + } + else { + src_addr = IP6_ADDR_ANY; + } + + /* Generate the all routers target address. */ + ip6_addr_set_allrouters_linklocal(&multicast_address); + + /* Allocate a packet. */ + packet_len = sizeof(struct rs_header); + if (src_addr != IP6_ADDR_ANY) { + packet_len += sizeof(struct lladdr_option); + } + p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM); + if ((p == NULL) || (p->len < packet_len)) { + /* We couldn't allocate a suitable pbuf for the ns. drop it. */ + if (p != NULL) { + pbuf_free(p); + } + ND6_STATS_INC(nd6.memerr); + return; + } + + /* Set fields. */ + rs_hdr = (struct rs_header *)p->payload; + + rs_hdr->type = ICMP6_TYPE_RS; + rs_hdr->code = 0; + rs_hdr->chksum = 0; + rs_hdr->reserved = 0; + + if (src_addr != IP6_ADDR_ANY) { + /* Include our hw address. */ + lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header)); + lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR; + lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0); + SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len); + } + +#if CHECKSUM_GEN_ICMP6 + rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr, + &multicast_address); +#endif /* CHECKSUM_GEN_ICMP6 */ + + /* Send the packet out. */ + ND6_STATS_INC(nd6.xmit); + ip6_output_if(p, src_addr, &multicast_address, + LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif); + pbuf_free(p); +} +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + +/** + * Search for a neighbor cache entry + * + * @param ip6addr the IPv6 address of the neighbor + * @return The neighbor cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) { + return i; + } + } + return -1; +} + +/** + * Create a new neighbor cache entry. + * + * If no unused entry is found, will try to recycle an old entry + * according to ad-hoc "age" heuristic. + * + * @return The neighbor cache entry index that was created, -1 if no + * entry could be created + */ +static s8_t +nd6_new_neighbor_cache_entry(void) +{ + s8_t i; + s8_t j; + u32_t time; + + + /* First, try to find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if (neighbor_cache[i].state == ND6_NO_ENTRY) { + return i; + } + } + + /* We need to recycle an entry. in general, do not recycle if it is a router. */ + + /* Next, try to find a Stale entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_STALE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Probe entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_PROBE) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find a Delayed entry. */ + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_DELAY) && + (!neighbor_cache[i].isrouter)) { + nd6_free_neighbor_cache_entry(i); + return i; + } + } + + /* Next, try to find the oldest reachable entry. */ + time = 0xfffffffful; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_REACHABLE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.reachable_time < time) { + j = i; + time = neighbor_cache[i].counter.reachable_time; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry without queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ( + (neighbor_cache[i].q == NULL) && + (neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* Next, find oldest incomplete entry with queued packets. */ + time = 0; + j = -1; + for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) { + if ((neighbor_cache[i].state == ND6_INCOMPLETE) && + (!neighbor_cache[i].isrouter)) { + if (neighbor_cache[i].counter.probes_sent >= time) { + j = i; + time = neighbor_cache[i].counter.probes_sent; + } + } + } + if (j >= 0) { + nd6_free_neighbor_cache_entry(j); + return j; + } + + /* No more entries to try. */ + return -1; +} + +/** + * Will free any resources associated with a neighbor cache + * entry, and will mark it as unused. + * + * @param i the neighbor cache entry index to free + */ +static void +nd6_free_neighbor_cache_entry(s8_t i) +{ + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + + /* Free any queued packets. */ + if (neighbor_cache[i].q != NULL) { + nd6_free_q(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } + + neighbor_cache[i].state = ND6_NO_ENTRY; + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = NULL; + neighbor_cache[i].counter.reachable_time = 0; + ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address)); +} + +/** + * Search for a destination cache entry + * + * @param ip6addr the IPv6 address of the destination + * @return The destination cache entry index that matched, -1 if no + * entry is found + */ +static s8_t +nd6_find_destination_cache_entry(ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) { + return i; + } + } + return -1; +} + +/** + * Create a new destination cache entry. If no unused entry is found, + * will recycle oldest entry. + * + * @return The destination cache entry index that was created, -1 if no + * entry was created + */ +static s8_t +nd6_new_destination_cache_entry(void) +{ + s8_t i, j; + u32_t age; + + /* Find an empty entry. */ + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (ip6_addr_isany(&(destination_cache[i].destination_addr))) { + return i; + } + } + + /* Find oldest entry. */ + age = 0; + j = LWIP_ND6_NUM_DESTINATIONS - 1; + for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) { + if (destination_cache[i].age > age) { + j = i; + } + } + + return j; +} + +/** + * Determine whether an address matches an on-link prefix. + * + * @param ip6addr the IPv6 address to match + * @return 1 if the address is on-link, 0 otherwise + */ +static s8_t +nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) { + if ((prefix_list[i].netif == netif) && + (prefix_list[i].invalidation_timer > 0) && + ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) { + return 1; + } + } + /* Check to see if address prefix matches a (manually?) configured address. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { + return 1; + } + } + return 0; +} + +/** + * Select a default router for a destination. + * + * @param ip6addr the destination address + * @param netif the netif for the outgoing packet, if known + * @return the default router entry index, or -1 if no suitable + * router is found + */ +s8_t +nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + /* last_router is used for round-robin router selection (as recommended + * in RFC). This is more robust in case one router is not reachable, + * we are not stuck trying to resolve it. */ + static s8_t last_router; + (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */ + + /* TODO: implement default router preference */ + + /* Look for reachable routers. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0) && + (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) { + return i; + } + } + + /* Look for router in other reachability states, but still valid according to timer. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if ((default_router_list[i].neighbor_entry != NULL) && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) && + (default_router_list[i].invalidation_timer > 0)) { + return i; + } + } + + /* Look for any router for which we have any information at all. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if (++last_router >= LWIP_ND6_NUM_ROUTERS) { + last_router = 0; + } + if (default_router_list[i].neighbor_entry != NULL && + (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) { + return i; + } + } + + /* no suitable router found. */ + return -1; +} + +/** + * Find an entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is found, if known + * @return the index of the router entry, or -1 if not found + */ +static s8_t +nd6_get_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t i; + + /* Look for router. */ + for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) { + if ((default_router_list[i].neighbor_entry != NULL) && + ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) && + ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) { + return i; + } + } + + /* router not found. */ + return -1; +} + +/** + * Create a new entry for a default router. + * + * @param router_addr the IPv6 address of the router + * @param netif the netif on which the router is connected, if known + * @return the index on the router table, or -1 if could not be created + */ +static s8_t +nd6_new_router(ip6_addr_t * router_addr, struct netif * netif) +{ + s8_t router_index; + s8_t neighbor_index; + + /* Do we have a neighbor entry for this router? */ + neighbor_index = nd6_find_neighbor_cache_entry(router_addr); + if (neighbor_index < 0) { + /* Create a neighbor entry for this router. */ + neighbor_index = nd6_new_neighbor_cache_entry(); + if (neighbor_index < 0) { + /* Could not create neighbor entry for this router. */ + return -1; + } + ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr); + neighbor_cache[neighbor_index].netif = netif; + neighbor_cache[neighbor_index].q = NULL; + neighbor_cache[neighbor_index].state = ND6_INCOMPLETE; + neighbor_cache[neighbor_index].counter.probes_sent = 0; + } + + /* Mark neighbor as router. */ + neighbor_cache[neighbor_index].isrouter = 1; + + /* Look for empty entry. */ + for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) { + if (default_router_list[router_index].neighbor_entry == NULL) { + default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]); + return router_index; + } + } + + /* Could not create a router entry. */ + + /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */ + neighbor_cache[neighbor_index].isrouter = 0; + + /* router not found. */ + return -1; +} + +/** + * Find the cached entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not found + */ +static s8_t +nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Look for prefix in list. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) && + (prefix_list[i].netif == netif)) { + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Creates a new entry for an on-link prefix. + * + * @param prefix the IPv6 prefix that is on-link + * @param netif the netif on which the prefix is on-link + * @return the index on the prefix table, or -1 if not created + */ +static s8_t +nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif) +{ + s8_t i; + + /* Create new entry. */ + for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) { + if ((prefix_list[i].netif == NULL) || + (prefix_list[i].invalidation_timer == 0)) { + /* Found empty prefix entry. */ + prefix_list[i].netif = netif; + ip6_addr_set(&(prefix_list[i].prefix), prefix); + prefix_list[i].flags = 0; + return i; + } + } + + /* Entry not available. */ + return -1; +} + +/** + * Determine the next hop for a destination. Will determine if the + * destination is on-link, else a suitable on-link router is selected. + * + * The last entry index is cached for fast entry search. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the neighbor cache entry for the next hop, ERR_RTE if no + * suitable next hop was found, ERR_MEM if no cache entry + * could be created + */ +s8_t +nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + u8_t addr_hint = *(netif->addr_hint); + if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) { + nd6_cached_destination_index = addr_hint; + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look for ip6addr in destination cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + /* the cached entry index is the right one! */ + /* do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + /* Search destination cache. */ + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + /* found destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } + else { + /* Not found. Create a new destination entry. */ + i = nd6_new_destination_cache_entry(); + if (i >= 0) { + /* got new destination entry. make it our new cached index. */ + nd6_cached_destination_index = i; + } else { + /* Could not create a destination cache entry. */ + return ERR_MEM; + } + + /* Copy dest address to destination cache. */ + ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr); + + /* Now find the next hop. is it a neighbor? */ + if (ip6_addr_islinklocal(ip6addr) || + nd6_is_prefix_in_netif(ip6addr, netif)) { + /* Destination in local link. */ + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr); + } + else { + /* We need to select a router. */ + i = nd6_select_router(ip6addr, netif); + if (i < 0) { + /* No router found. */ + ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr)); + return ERR_RTE; + } + destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */ + ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address); + } + } + } + +#if LWIP_NETIF_HWADDRHINT + if (netif->addr_hint != NULL) { + /* per-pcb cached entry was given */ + *(netif->addr_hint) = nd6_cached_destination_index; + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* Look in neighbor cache for the next-hop address. */ + if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr), + &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + /* Cache hit. */ + /* Do nothing. */ + ND6_STATS_INC(nd6.cachehit); + } else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr)); + if (i >= 0) { + /* Found a matching record, make it new cached entry. */ + nd6_cached_neighbor_index = i; + } + else { + /* Neighbor not in cache. Make a new entry. */ + i = nd6_new_neighbor_cache_entry(); + if (i >= 0) { + /* got new neighbor entry. make it our new cached index. */ + nd6_cached_neighbor_index = i; + } else { + /* Could not create a neighbor cache entry. */ + return ERR_MEM; + } + + /* Initialize fields. */ + ip6_addr_copy(neighbor_cache[i].next_hop_address, + destination_cache[nd6_cached_destination_index].next_hop_addr); + neighbor_cache[i].isrouter = 0; + neighbor_cache[i].netif = netif; + neighbor_cache[i].state = ND6_INCOMPLETE; + neighbor_cache[i].counter.probes_sent = 0; + } + } + + /* Reset this destination's age. */ + destination_cache[nd6_cached_destination_index].age = 0; + + return nd6_cached_neighbor_index; +} + +/** + * Queue a packet for a neighbor. + * + * @param neighbor_index the index in the neighbor cache table + * @param q packet to be queued + * @return ERR_OK if succeeded, ERR_MEM if out of memory + */ +err_t +nd6_queue_packet(s8_t neighbor_index, struct pbuf * q) +{ + err_t result = ERR_MEM; + struct pbuf *p; + int copy_needed = 0; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *new_entry, *r; +#endif /* LWIP_ND6_QUEUEING */ + + if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) { + return ERR_ARG; + } + + /* 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) { + 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_LINK, q->tot_len, PBUF_RAM); + while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ +#if LWIP_ND6_QUEUEING + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); +#else /* LWIP_ND6_QUEUEING */ + pbuf_free(neighbor_cache[neighbor_index].q); + neighbor_cache[neighbor_index].q = NULL; +#endif /* LWIP_ND6_QUEUEING */ + p = pbuf_alloc(PBUF_LINK, q->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 was copied/ref'd? */ + if (p != NULL) { + /* queue packet ... */ +#if LWIP_ND6_QUEUEING + /* allocate a new nd6 queue entry */ + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) { + /* Free oldest packet (as per RFC recommendation) */ + r = neighbor_cache[neighbor_index].q; + neighbor_cache[neighbor_index].q = r->next; + r->next = NULL; + nd6_free_q(r); + new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE); + } + if (new_entry != NULL) { + new_entry->next = NULL; + new_entry->p = p; + if(neighbor_cache[neighbor_index].q != NULL) { + /* queue was already existent, append the new entry to the end */ + r = neighbor_cache[neighbor_index].q; + while (r->next != NULL) { + r = r->next; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + neighbor_cache[neighbor_index].q = new_entry; + } + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; + } else { + /* the pool MEMP_ND6_QUEUE is empty */ + pbuf_free(p); + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p)); + /* { result == ERR_MEM } through initialization */ + } +#else /* LWIP_ND6_QUEUEING */ + /* Queue a single packet. If an older packet is already queued, free it as per RFC. */ + if (neighbor_cache[neighbor_index].q != NULL) { + pbuf_free(neighbor_cache[neighbor_index].q); + } + neighbor_cache[neighbor_index].q = p; + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index)); + result = ERR_OK; +#endif /* LWIP_ND6_QUEUEING */ + } else { + LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } + + return result; +} + +#if LWIP_ND6_QUEUEING +/** + * Free a complete queue of nd6 q entries + * + * @param q a queue of nd6_q_entry to free + */ +static void +nd6_free_q(struct nd6_q_entry *q) +{ + struct nd6_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_ND6_QUEUE, r); + } +} +#endif /* LWIP_ND6_QUEUEING */ + +/** + * Send queued packets for a neighbor + * + * @param i the neighbor to send packets to + */ +static void +nd6_send_q(s8_t i) +{ + struct ip6_hdr *ip6hdr; +#if LWIP_ND6_QUEUEING + struct nd6_q_entry *q; +#endif /* LWIP_ND6_QUEUEING */ + + if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) { + return; + } + +#if LWIP_ND6_QUEUEING + while (neighbor_cache[i].q != NULL) { + /* remember first in queue */ + q = neighbor_cache[i].q; + /* pop first item off the queue */ + neighbor_cache[i].q = q->next; + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(q->p->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(q->p); + /* now queue entry can be freed */ + memp_free(MEMP_ND6_QUEUE, q); + } +#else /* LWIP_ND6_QUEUEING */ + if (neighbor_cache[i].q != NULL) { + /* Get ipv6 header. */ + ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload); + /* Override ip6_current_dest_addr() so that we have an aligned copy. */ + ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest)); + /* send the queued IPv6 packet */ + (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr()); + /* free the queued IP packet */ + pbuf_free(neighbor_cache[i].q); + neighbor_cache[i].q = NULL; + } +#endif /* LWIP_ND6_QUEUEING */ +} + + +/** + * Get the Path MTU for a destination. + * + * @param ip6addr the destination address + * @param netif the netif on which the packet will be sent + * @return the Path MTU, if known, or the netif default MTU + */ +u16_t +nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif) +{ + s8_t i; + + i = nd6_find_destination_cache_entry(ip6addr); + if (i >= 0) { + if (destination_cache[i].pmtu > 0) { + return destination_cache[i].pmtu; + } + } + + if (netif != NULL) { + return netif->mtu; + } + + return 1280; /* Minimum MTU */ +} + + +#if LWIP_ND6_TCP_REACHABILITY_HINTS +/** + * Provide the Neighbor discovery process with a hint that a + * destination is reachable. Called by tcp_receive when ACKs are + * received or sent (as per RFC). This is useful to avoid sending + * NS messages every 30 seconds. + * + * @param ip6addr the destination address which is know to be reachable + * by an upper layer protocol (TCP) + */ +void +nd6_reachability_hint(ip6_addr_t * ip6addr) +{ + s8_t i; + + /* Find destination in cache. */ + if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) { + i = nd6_cached_destination_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_destination_cache_entry(ip6addr); + } + if (i < 0) { + return; + } + + /* Find next hop neighbor in cache. */ + if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) { + i = nd6_cached_neighbor_index; + ND6_STATS_INC(nd6.cachehit); + } + else { + i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr)); + } + if (i < 0) { + return; + } + + /* Set reachability state. */ + neighbor_cache[i].state = ND6_REACHABLE; + neighbor_cache[i].counter.reachable_time = reachable_time; +} +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/lwip_timers.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/lwip_timers.c new file mode 100644 index 0000000..670dc1f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/lwip_timers.c @@ -0,0 +1,562 @@ +/** + * @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/lwip_timers.h" // Evan modified timers.h to 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" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.h" +#include "lwip/sys.h" +#include "lwip/pbuf.h" + +/** The one and only timeout list */ +static struct sys_timeo *next_timeout; +#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 +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); + } +} +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +/** + * Timer callback function that calls ip_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +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 +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 + */ +static void +dhcp_timer_coarse(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n")); + dhcp_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 */ + +#if LWIP_IPV6 +/** + * Timer callback function that calls nd6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +nd6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n")); + nd6_tmr(); + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +} + +#if LWIP_IPV6_REASS +/** + * Timer callback function that calls ip6_reass_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +ip6_reass_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n")); + ip6_reass_tmr(); + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +} +#endif /* LWIP_IPV6_REASS */ + +#if LWIP_IPV6_MLD +/** + * Timer callback function that calls mld6_tmr() and reschedules itself. + * + * @param arg unused argument + */ +static void +mld6_timer(void *arg) +{ + LWIP_UNUSED_ARG(arg); + LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n")); + mld6_tmr(); + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +} +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +/** 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_IPV6 + sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL); +#if LWIP_IPV6_REASS + sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL); +#endif /* LWIP_IPV6_REASS */ +#if LWIP_IPV6_MLD + sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL); +#endif /* LWIP_IPV6_MLD */ +#endif /* LWIP_IPV6 */ + +#if NO_SYS + /* Initialise timestamp for sys_check_timeouts */ + timeouts_last_time = sys_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; +#if NO_SYS + u32_t now, diff; +#endif + + 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; + } + +#if NO_SYS + now = sys_now(); + if (next_timeout == NULL) { + diff = 0; + timeouts_last_time = now; + } else { + diff = now - timeouts_last_time; + } +#endif + + timeout->next = NULL; + timeout->h = handler; + timeout->arg = arg; +#if NO_SYS + timeout->time = msecs + diff; +#else + timeout->time = msecs; +#endif +#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 (subsequent entries remain untouched), even though the timeout has not + * triggered yet. + * + * @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 + +/** 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) +{ + if (next_timeout) { + struct sys_timeo *tmptimeout; + u32_t diff; + sys_timeout_handler handler; + void *arg; + u8_t had_one; + u32_t now; + + now = sys_now(); + /* this cares for wraparounds */ + diff = now - timeouts_last_time; + do + { +#if PBUF_POOL_FREE_OOSEQ + PBUF_CHECK_FREE_OOSEQ(); +#endif /* PBUF_POOL_FREE_OOSEQ */ + had_one = 0; + tmptimeout = next_timeout; + if (tmptimeout && (tmptimeout->time <= diff)) { + /* timeout has expired */ + had_one = 1; + timeouts_last_time += tmptimeout->time; + 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 = sys_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 occurred 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/mem.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/mem.c new file mode 100644 index 0000000..634e760 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/mem.c @@ -0,0 +1,677 @@ +/** + * @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) +{ + void *ret; + struct memp_malloc_helper *element; + memp_t poolnr; + mem_size_t required_size = size + LWIP_MEM_ALIGN_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 */ + ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)); + +#if MEMP_OVERFLOW_CHECK + /* initialize unused memory */ + element->size = size; + memset((u8_t*)ret + size, 0xcd, memp_sizes[poolnr] - size); +#endif /* MEMP_OVERFLOW_CHECK */ + return ret; +} + +/** + * 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; + + 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 = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))); + + 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)); + +#if MEMP_OVERFLOW_CHECK + { + u16_t i; + LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size", + hmem->size <= memp_sizes[hmem->poolnr]); + /* check that unused memory remained untouched */ + for (i = hmem->size; i < memp_sizes[hmem->poolnr]; i++) { + u8_t data = *((u8_t*)rmem + i); + LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd); + } + } +#endif /* MEMP_OVERFLOW_CHECK */ + + /* 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 aligns. + */ +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; +}; + +/** 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 */ +u8_t ram_heap[MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT]; +#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 */ +#if !NO_SYS +static sys_mutex_t mem_mutex; +#endif + +#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 +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 or mem_trim */ + 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 or mem_trim to run */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem. */ + local_mem_free_count = 1; + break; + } +#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 exact 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 LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT +mem_malloc_adjust_lfree: +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + if (mem == lfree) { + struct mem *cur = lfree; + /* Find next free block after mem and update lowest free pointer */ + while (cur->used && cur != ram_end) { +#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT + mem_free_count = 0; + LWIP_MEM_ALLOC_UNPROTECT(); + /* prevent high interrupt latency... */ + LWIP_MEM_ALLOC_PROTECT(); + if (mem_free_count != 0) { + /* If mem_free or mem_trim have run, we have to restart since they + could have altered our current struct mem or lfree. */ + goto mem_malloc_adjust_lfree; + } +#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */ + cur = (struct mem *)(void *)&ram[cur->next]; + } + lfree = cur; + 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/memp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/memp.c new file mode 100644 index 0000000..d7ba6ad --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/memp.c @@ -0,0 +1,489 @@ +/** + * @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/sockets.h" +#include "lwip/tcpip.h" +#include "lwip/sys.h" +#include "lwip/lwip_timers.h" // Evan modified timers.h to 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 "lwip/netdb.h" +#include "netif/ppp/ppp.h" +#include "netif/ppp/pppoe.h" +#include "netif/ppp/pppol2tp.h" +#include "lwip/nd6.h" +#include "lwip/ip6_frag.h" +#include "lwip/mld6.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] = { +#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size), +#include "lwip/memp_std.h" +}; + +#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) (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] = { +#define LWIP_MEMPOOL(name,num,size,desc) (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) u8_t memp_memory_ ## name ## _base \ + [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; +#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) 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) + ( (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, using "Floyd's cycle-finding algorithm". + */ +static int +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) "/"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 +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 +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 +memp_overflow_check_all(void) +{ + u16_t i, j; + struct memp *p; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + 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); + } + } +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + 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 +memp_overflow_init(void) +{ + u16_t i, j; + struct memp *p; + u8_t *m; + +#if !MEMP_SEPARATE_POOLS + p = (struct memp *)LWIP_MEM_ALIGN(memp_memory); +#endif /* !MEMP_SEPARATE_POOLS */ + for (i = 0; i < MEMP_MAX; ++i) { +#if MEMP_SEPARATE_POOLS + p = (struct memp *)(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + 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*)LWIP_MEM_ALIGN(memp_bases[i]); +#endif /* MEMP_SEPARATE_POOLS */ + /* create a linked list of memp elements */ + for (j = 0; j < memp_num[i]; ++j) { + memp->next = 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 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/netif.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/netif.c new file mode 100644 index 0000000..36e801b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/netif.c @@ -0,0 +1,940 @@ +/** + * @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/ip6_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_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ +#if LWIP_IPV6_MLD +#include "lwip/mld6.h" +#endif /* LWIP_IPV6_MLD */ + +#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; + +static u8_t netif_num; + +#if LWIP_IPV6 +static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define ipX_input(in, netif) (IP6H_V((const struct ip6_hdr *)in->payload) == 6) ? ip6_input(in, netif) : ip_input(in, netif) +#else +#define ipX_input(in, netif) ip_input(in, netif) +#endif + +#if LWIP_HAVE_LOOPIF +static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, ip_addr_t* addr); +#if LWIP_IPV6 +static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, ip6_addr_t* addr); +#endif + + +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 +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_ipv4; +#if LWIP_IPV6 + netif->output_ip6 = netif_loop_output_ipv6; +#endif + 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 */ + +#if LWIP_IPV6 + loop_netif.ip6_addr[0].addr[0] = 0; + loop_netif.ip6_addr[0].addr[1] = 0; + loop_netif.ip6_addr[0].addr[2] = 0; + loop_netif.ip6_addr[0].addr[3] = PP_HTONL(0x00000001UL); + loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID; +#endif /* LWIP_IPV6 */ + + 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) +{ +#if LWIP_IPV6 + u32_t i; +#endif + + 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); +#if LWIP_IPV6 + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + ip6_addr_set_zero(&netif->ip6_addr[i]); + netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); + } + netif->output_ip6 = netif_null_output_ip6; +#endif /* LWIP_IPV6 */ + 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_IPV6_AUTOCONFIG + /* IPv6 address autoconfiguration not enabled by default */ + netif->ip6_autoconfig_enabled = 0; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /* netif not under DHCPv6 control by default */ + netif->dhcp6 = NULL; +#endif /* LWIP_IPV6_DHCP6 */ +#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 LWIP_IPV6 && LWIP_IPV6_MLD + netif->mld_mac_filter = NULL; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#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 = netif_num++; + netif->input = input; + NETIF_SET_HWADDRHINT(netif, NULL); +#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 LWIP_IPV6 && LWIP_IPV6_MLD + /* stop MLD processing */ + mld6_stop(netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + 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); + } +#if LWIP_NETIF_REMOVE_CALLBACK + if (netif->remove_callback) { + netif->remove_callback(netif); + } +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + 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(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr)) +#if LWIP_AUTOIP + /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */ + && !ip_addr_islinklocal(ipX_2_ip(&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(ipX_2_ip(&lpcb->local_ip)))) && + (ip_addr_cmp(ipX_2_ip(&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(ipX_2_ip(&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 */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /* Send Router Solicitation messages. */ + netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ + + } + } +} + +/** + * 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 */ + +#if LWIP_NETIF_REMOVE_CALLBACK +/** + * Set callback to be called when the interface has been removed + */ +void +netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback) +{ + if (netif) { + netif->remove_callback = remove_callback; + } +} +#endif /* LWIP_NETIF_REMOVE_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 */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /* send mld memberships */ + mld6_report_groups( netif); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + } + 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' + * @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) +{ + 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); + + /* 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_with_block((tcpip_callback_fn)netif_poll, netif, 0); +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ + + return ERR_OK; +} + +static err_t +netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, ip_addr_t* addr) +{ + LWIP_UNUSED_ARG(addr); + return netif_loop_output(netif, p); +} + +#if LWIP_IPV6 +static err_t +netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, ip6_addr_t* addr) +{ + LWIP_UNUSED_ARG(addr); + return netif_loop_output(netif, p); +} +#endif + + +/** + * 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 = 1; +#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; +#if LWIP_LOOPBACK_MAX_PBUFS + clen++; +#endif /* LWIP_LOOPBACK_MAX_PBUFS */ + } +#if LWIP_LOOPBACK_MAX_PBUFS + /* 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 */ + + /* '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 (ipX_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 */ + +#if LWIP_IPV6 +s8_t +netif_get_ip6_addr_match(struct netif * netif, ip6_addr_t * ip6addr) +{ + s8_t i; + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { + if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) { + return i; + } + } + return -1; +} + +void +netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit) +{ + u8_t i, addr_index; + + /* Link-local prefix. */ + netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul); + netif->ip6_addr[0].addr[1] = 0; + + /* Generate interface ID. */ + if (from_mac_48bit) { + /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */ + netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) | + ((u32_t)(netif->hwaddr[1]) << 16) | + ((u32_t)(netif->hwaddr[2]) << 8) | + (0xff)); + netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) | + ((u32_t)(netif->hwaddr[3]) << 16) | + ((u32_t)(netif->hwaddr[4]) << 8) | + (netif->hwaddr[5])); + } + else { + /* Use hwaddr directly as interface ID. */ + netif->ip6_addr[0].addr[2] = 0; + netif->ip6_addr[0].addr[3] = 0; + + addr_index = 3; + for (i = 0; i < 8; i++) { + if (i == 4) { + addr_index--; + } + netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03)); + } + } + + /* Set address state. */ +#if LWIP_IPV6_DUP_DETECT_ATTEMPTS + /* Will perform duplicate address detection (DAD). */ + netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE; +#else + /* Consider address valid. */ + netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED; +#endif /* LWIP_IPV6_AUTOCONFIG */ +} + +static err_t +netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + (void)netif; + (void)p; + (void)ipaddr; + + return ERR_IF; +} +#endif /* LWIP_IPV6 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/pbuf.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/pbuf.c new file mode 100644 index 0000000..1a91d75 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/pbuf.c @@ -0,0 +1,1329 @@ +/** + * @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 LWIP_TCP && TCP_QUEUE_OOSEQ +#include "lwip/tcp_impl.h" +#endif +#if LWIP_CHECKSUM_ON_COPY +#include "lwip/inet_chksum.h" +#endif + +#include + +#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 || !PBUF_POOL_FREE_OOSEQ +#define PBUF_POOL_IS_EMPTY() +#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +#if !NO_SYS +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL +#include "lwip/tcpip.h" +#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ + if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \ + SYS_ARCH_PROTECT(old_level); \ + pbuf_free_ooseq_pending = 0; \ + SYS_ARCH_UNPROTECT(old_level); \ + } } while(0) +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +#endif /* !NO_SYS */ + +volatile u8_t pbuf_free_ooseq_pending; +#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() + +/** + * 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. + */ +#if !NO_SYS +static +#endif /* !NO_SYS */ +void +pbuf_free_ooseq(void) +{ + struct tcp_pcb* pcb; + SYS_ARCH_DECL_PROTECT(old_level); + + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 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; + } + } +} + +#if !NO_SYS +/** + * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq(). + */ +static void +pbuf_free_ooseq_callback(void *arg) +{ + LWIP_UNUSED_ARG(arg); + pbuf_free_ooseq(); +} +#endif /* !NO_SYS */ + +/** Queue a call to pbuf_free_ooseq if not already queued. */ +static void +pbuf_pool_is_empty(void) +{ +#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); +#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ + u8_t queued; + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + queued = pbuf_free_ooseq_pending; + pbuf_free_ooseq_pending = 1; + SYS_ARCH_UNPROTECT(old_level); + + if(!queued) { + /* queue a call to pbuf_free_ooseq if not already queued */ + PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); + } +#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ +} +#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ + +/** + * 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 */ + switch (layer) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + 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 unsuccessfully */ + 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; + /* 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. + * ATTENTION: The caller is responsible for correct alignment of this buffer!! + * @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 */ + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN; + break; + case PBUF_IP: + /* add room for IP layer header */ + offset = PBUF_LINK_HLEN + PBUF_IP_HLEN; + break; + case PBUF_LINK: + /* add room for link layer header */ + offset = PBUF_LINK_HLEN; + break; + case PBUF_RAW: + offset = 0; + 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 = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(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. + * @see pbuf_header. + * + * @param p pbuf to change the header size. + * @param header_size_increment Number of bytes to increment header size. + * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types + * + * @return non-zero on failure, zero on success. + * + */ +static u8_t +pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) +{ + 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) { + 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 unsuccessfully */ + return 1; + } + /* pbuf types referring 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 if ((header_size_increment > 0) && force) { + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccessfully */ + 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; +} + +/** + * 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 successful. + * + * 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) +{ + return pbuf_header_impl(p, header_size_increment, 0); +} + +/** + * Same as pbuf_header but does not check if 'header_size > 0' is allowed. + * This is used internally only, to allow PBUF_REF for RX. + */ +u8_t +pbuf_header_force(struct pbuf *p, s16_t header_size_increment) +{ + return pbuf_header_impl(p, header_size_increment, 1); +} + +/** + * 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); + + 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) { + 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 + { + /* 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); + 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 (offset_to == p_to->len) { + /* on to next p_to (if any) */ + offset_to = 0; + p_to = p_to->next; + LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;); + } + + 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!", + (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!", + (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; +} + +#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE +/** + * This method modifies a 'pbuf chain', so that its total length is + * smaller than 64K. The remainder of the original pbuf chain is stored + * in *rest. + * This function never creates new pbufs, but splits an existing chain + * in two parts. The tot_len of the modified packet queue will likely be + * smaller than 64K. + * 'packet queues' are not supported by this function. + * + * @param p the pbuf queue to be split + * @param rest pointer to store the remainder (after the first 64K) + */ +void pbuf_split_64k(struct pbuf *p, struct pbuf **rest) +{ + *rest = NULL; + if ((p != NULL) && (p->next != NULL)) { + u16_t tot_len_front = p->len; + struct pbuf *i = p; + struct pbuf *r = p->next; + + /* continue until the total length (summed up as u16_t) overflows */ + while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) { + tot_len_front += r->len; + i = r; + r = r->next; + } + /* i now points to last packet of the first segment. Set next + pointer to NULL */ + i->next = NULL; + + if (r != NULL) { + /* Update the tot_len field in the first part */ + for (i = p; i != NULL; i = i->next) { + i->tot_len -= r->tot_len; + LWIP_ASSERT("tot_len/len mismatch in last pbuf", + (i->next != NULL) || (i->tot_len == i->len)); + } + if (p->flags & PBUF_FLAG_TCP_FIN) { + r->flags |= PBUF_FLAG_TCP_FIN; + } + + /* tot_len field in rest does not need modifications */ + /* reference counters do not need modifications */ + *rest = r; + } + } +} +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + +/** + * Skip a number of bytes at the start of a pbuf + * + * @param in input pbuf + * @param in_offset offset to skip + * @param out_offset resulting offset in the returned pbuf + * @return the pbuf in the queue where the offset is + */ +static struct pbuf* +pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset) +{ + u16_t offset_left = in_offset; + struct pbuf* q = in; + + /* get the correct pbuf */ + while ((q != NULL) && (q->len <= offset_left)) { + offset_left -= q->len; + q = q->next; + } + if (out_offset != NULL) { + *out_offset = offset_left; + } + return q; +} + +/** + * 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; +} + +/** + * Same as pbuf_take() but puts data at an offset + * + * @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_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset) +{ + u16_t target_offset; + struct pbuf* q = pbuf_skip(buf, offset, &target_offset); + + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->tot_len >= target_offset + len)) { + u16_t remaining_len = len; + u8_t* src_ptr = (u8_t*)dataptr; + if (target_offset > 0) { + /* copy the part that goes into the first pbuf */ + u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len); + MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len); + remaining_len -= first_copy_len; + src_ptr += first_copy_len; + } + if (remaining_len > 0) { + return pbuf_take(q->next, src_ptr, remaining_len); + } + return ERR_OK; + } + return ERR_MEM; +} + +/** + * 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 q_idx; + struct pbuf* q = pbuf_skip(p, offset, &q_idx); + + /* return requested data if pbuf is OK */ + if ((q != NULL) && (q->len > q_idx)) { + return ((u8_t*)q->payload)[q_idx]; + } + return 0; +} + + /** Put one byte to the specified position in a pbuf + * WARNING: silently ignores offset >= p->tot_len + * + * @param p pbuf to fill + * @param offset offset into p of the byte to write + * @param data byte to write at an offset into p + */ +void +pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data) +{ + u16_t q_idx; + struct pbuf* q = pbuf_skip(p, offset, &q_idx); + + /* write requested data if pbuf is OK */ + if ((q != NULL) && (q->len > q_idx)) { + ((u8_t*)q->payload)[q_idx] = data; + } +} + +/** Compare pbuf contents at specified offset with memory s2, both of length n + * + * @param p pbuf to compare + * @param offset offset into p at which 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; i++) { + u16_t plus = pbuf_memcmp(p, i, mem, mem_len); + if (plus == 0) { + return i; + } + } + } + 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/raw.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/raw.c new file mode 100644 index 0000000..243dd66 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/raw.c @@ -0,0 +1,436 @@ +/** + * @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 "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.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 +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; +#if LWIP_IPV6 + if (IPH_V(iphdr) == 6) { + struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; + proto = IP6H_NEXTH(ip6hdr); + iphdr = NULL; + } + else +#endif /* LWIP_IPV6 */ + { + 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_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) { +#if IP_SOF_BROADCAST_RECV + /* broadcast filter? */ + if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) +#if LWIP_IPV6 + || PCB_ISIPV6(pcb) +#endif /* LWIP_IPV6 */ + ) +#endif /* IP_SOF_BROADCAST_RECV */ + { + /* receive callback function available? */ + if (pcb->recv.ip4 != NULL) { +#ifndef LWIP_NOASSERT + void* old_payload = p->payload; +#endif + /* the receive callback function did not eat the packet? */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + eaten = pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr()); + } else +#endif /* LWIP_IPV6 */ + { + eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr()); + } + if (eaten != 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; + } + } else { + /* sanity-check that the receive callback did not alter the pbuf */ + LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", + p->payload == old_payload); + } + } + /* 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 occurred. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &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 +raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr) +{ + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &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 +raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = 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 +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr) +{ + err_t err; + struct netif *netif; + ipX_addr_t *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + s16_t header_size; + ipX_addr_t *dst_ip = ip_2_ipX(ipaddr); + + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); + + header_size = ( +#if LWIP_IPV6 + PCB_ISIPV6(pcb) ? IP6_HLEN : +#endif /* LWIP_IPV6 */ + IP_HLEN); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, header_size)) { + /* 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, -header_size)) { + LWIP_ASSERT("Can't restore header we just removed!", 0); + return ERR_MEM; + } + } + + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip); + if (netif == NULL) { + LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + +#if IP_SOF_BROADCAST +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && 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 (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip); +#if LWIP_IPV6 + if (src_ip == NULL) { + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } +#endif /* LWIP_IPV6 */ + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &pcb->local_ip; + } + +#if LWIP_IPV6 + /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, + compute the checksum and update the checksum in the payload. */ + if (PCB_ISIPV6(pcb) && pcb->chksum_reqd) { + u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ipX_2_ip6(src_ip), ipX_2_ip6(dst_ip)); + LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2)); + SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t)); + } +#endif + + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); + err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* 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 +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, ipX_2_ip(&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 +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 * +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; +} + +#if LWIP_IPV6 +/** + * Create a RAW PCB for IPv6. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number (next header) of the IPv6 packet payload + * (e.g. IP6_NEXTH_ICMP6) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new_ip6(u8_t proto) +{ + struct raw_pcb *pcb; + pcb = raw_new(proto); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_RAW */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_dec.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_dec.c new file mode 100644 index 0000000..39b70bc --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_dec.c @@ -0,0 +1,657 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) decoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Retrieves type field from incoming pbuf chain. + * + * @param p points to a pbuf holding an ASN1 coded type field + * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field + * @param type return ASN1 type + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *type = *msg_ptr; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes length field from incoming pbuf chain into host length. + * + * @param p points to a pbuf holding an ASN1 coded length + * @param ofs points to the offset within the pbuf chain of the ASN1 coded length + * @param octets_used returns number of octets used by the length code + * @param length return host order length, up to 64k + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (*msg_ptr < 0x80) + { + /* primitive definite length format */ + *octets_used = 1; + *length = *msg_ptr; + return ERR_OK; + } + else if (*msg_ptr == 0x80) + { + /* constructed indefinite length format, termination with two zero octets */ + u8_t zeros; + u8_t i; + + *length = 0; + zeros = 0; + while (zeros != 2) + { + i = 2; + while (i > 0) + { + i--; + (*length) += 1; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (*msg_ptr == 0) + { + zeros++; + if (zeros == 2) + { + /* stop while (i > 0) */ + i = 0; + } + } + else + { + zeros = 0; + } + } + } + *octets_used = 1; + return ERR_OK; + } + else if (*msg_ptr == 0x81) + { + /* constructed definite length format, one octet */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *length = *msg_ptr; + *octets_used = 2; + return ERR_OK; + } + else if (*msg_ptr == 0x82) + { + u8_t i; + + /* constructed definite length format, two octets */ + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *length |= *msg_ptr; + } + else + { + /* most significant length octet */ + *length = (*msg_ptr) << 8; + } + } + *octets_used = 3; + return ERR_OK; + } + else + { + /* constructed definite length format 3..127 octets, this is too big (>64k) */ + /** @todo: do we need to accept inefficient codings with many leading zero's? */ + *octets_used = 1 + ((*msg_ptr) & 0x7f); + return ERR_ARG; + } + } + p = p->next; + } + + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes positive integer (counter, gauge, timeticks) into u32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +err_t +snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 6)) + { + /* start from zero */ + *value = 0; + if (*msg_ptr & 0x80) + { + /* negative, expecting zero sign bit! */ + return ERR_ARG; + } + else + { + /* positive */ + if ((len > 1) && (*msg_ptr == 0)) + { + /* skip leading "sign byte" octet 0x00 */ + len--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + } + /* OR octets with value */ + while (len > 1) + { + len--; + *value |= *msg_ptr; + *value <<= 8; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + *value |= *msg_ptr; + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes integer into s32_t. + * + * @param p points to a pbuf holding an ASN1 coded integer + * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer + * @param len length of the coded integer field + * @param value return host order integer + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + * + * @note ASN coded integers are _always_ signed! + */ +err_t +snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value) +{ + u16_t plen, base; + u8_t *msg_ptr; +#if BYTE_ORDER == LITTLE_ENDIAN + u8_t *lsb_ptr = (u8_t*)value; +#endif +#if BYTE_ORDER == BIG_ENDIAN + u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1; +#endif + u8_t sign; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if ((len > 0) && (len < 5)) + { + if (*msg_ptr & 0x80) + { + /* negative, start from -1 */ + *value = -1; + sign = 1; + } + else + { + /* positive, start from 0 */ + *value = 0; + sign = 0; + } + /* OR/AND octets with value */ + while (len > 1) + { + len--; + if (sign) + { + *lsb_ptr &= *msg_ptr; + *value <<= 8; + *lsb_ptr |= 255; + } + else + { + *lsb_ptr |= *msg_ptr; + *value <<= 8; + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (sign) + { + *lsb_ptr &= *msg_ptr; + } + else + { + *lsb_ptr |= *msg_ptr; + } + return ERR_OK; + } + else + { + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes object identifier from incoming message into array of s32_t. + * + * @param p points to a pbuf holding an ASN1 coded object identifier + * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier + * @param len length of the coded object identifier + * @param oid return object identifier struct + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid) +{ + u16_t plen, base; + u8_t *msg_ptr; + s32_t *oid_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + oid->len = 0; + oid_ptr = &oid->id[0]; + if (len > 0) + { + /* first compressed octet */ + if (*msg_ptr == 0x2B) + { + /* (most) common case 1.3 (iso.org) */ + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = 3; + oid_ptr++; + } + else if (*msg_ptr < 40) + { + *oid_ptr = 0; + oid_ptr++; + *oid_ptr = *msg_ptr; + oid_ptr++; + } + else if (*msg_ptr < 80) + { + *oid_ptr = 1; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 40; + oid_ptr++; + } + else + { + *oid_ptr = 2; + oid_ptr++; + *oid_ptr = (*msg_ptr) - 80; + oid_ptr++; + } + oid->len = 2; + } + else + { + /* accepting zero length identifiers e.g. for + getnext operation. uncommon but valid */ + return ERR_OK; + } + len--; + if (len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN)) + { + /* sub-identifier uses multiple octets */ + if (*msg_ptr & 0x80) + { + s32_t sub_id = 0; + + while ((*msg_ptr & 0x80) && (len > 1)) + { + len--; + sub_id = (sub_id << 7) + (*msg_ptr & ~0x80); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (!(*msg_ptr & 0x80) && (len > 0)) + { + /* last octet sub-identifier */ + len--; + sub_id = (sub_id << 7) + *msg_ptr; + *oid_ptr = sub_id; + } + } + else + { + /* !(*msg_ptr & 0x80) sub-identifier uses single octet */ + len--; + *oid_ptr = *msg_ptr; + } + if (len > 0) + { + /* remaining oid bytes available ... */ + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + oid_ptr++; + oid->len++; + } + if (len == 0) + { + /* len == 0, end of oid */ + return ERR_OK; + } + else + { + /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */ + return ERR_ARG; + } + + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding) + * from incoming message into array. + * + * @param p points to a pbuf holding an ASN1 coded raw data + * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data + * @param len length of the coded raw data (zero is valid, e.g. empty string!) + * @param raw_len length of the raw return value + * @param raw return raw bytes + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode + */ +err_t +snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + if (len > 0) + { + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + if (raw_len >= len) + { + while (len > 1) + { + /* copy len - 1 octets */ + len--; + *raw = *msg_ptr; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* copy last octet */ + *raw = *msg_ptr; + return ERR_OK; + } + else + { + /* raw_len < len, not enough dst space */ + return ERR_ARG; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; + } + else + { + /* len == 0, empty string */ + return ERR_OK; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_enc.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_enc.c new file mode 100644 index 0000000..d24a5c4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/asn1_enc.c @@ -0,0 +1,611 @@ +/** + * @file + * Abstract Syntax Notation One (ISO 8824, 8825) encoding + * + * @todo not optimised (yet), favor correctness over speed, favor speed over size + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_asn1.h" + +/** + * Returns octet count for length. + * + * @param length + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed) +{ + if (length < 0x80U) + { + *octets_needed = 1; + } + else if (length < 0x100U) + { + *octets_needed = 2; + } + else + { + *octets_needed = 3; + } +} + +/** + * Returns octet count for an u32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded + * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value + * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!! + */ +void +snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed) +{ + if (value < 0x80UL) + { + *octets_needed = 1; + } + else if (value < 0x8000UL) + { + *octets_needed = 2; + } + else if (value < 0x800000UL) + { + *octets_needed = 3; + } + else if (value < 0x80000000UL) + { + *octets_needed = 4; + } + else + { + *octets_needed = 5; + } +} + +/** + * Returns octet count for an s32_t. + * + * @param value + * @param octets_needed points to the return value + * + * @note ASN coded integers are _always_ signed. + */ +void +snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed) +{ + if (value < 0) + { + value = ~value; + } + if (value < 0x80L) + { + *octets_needed = 1; + } + else if (value < 0x8000L) + { + *octets_needed = 2; + } + else if (value < 0x800000L) + { + *octets_needed = 3; + } + else + { + *octets_needed = 4; + } +} + +/** + * Returns octet count for an object identifier. + * + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @param octets_needed points to the return value + */ +void +snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed) +{ + s32_t sub_id; + u8_t cnt; + + cnt = 0; + if (ident_len > 1) + { + /* compressed prefix in one octet */ + cnt++; + ident_len -= 2; + ident += 2; + } + while(ident_len > 0) + { + ident_len--; + sub_id = *ident; + + sub_id >>= 7; + cnt++; + while(sub_id > 0) + { + sub_id >>= 7; + cnt++; + } + ident++; + } + *octets_needed = cnt; +} + +/** + * Encodes ASN type field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param type input ASN1 type + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + *msg_ptr = type; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes host order length field into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode length into + * @param ofs points to the offset within the pbuf chain + * @param length is the host order length to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (length < 0x80) + { + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else if (length < 0x100) + { + *msg_ptr = 0x81; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + *msg_ptr = (u8_t)length; + return ERR_OK; + } + else + { + u8_t i; + + /* length >= 0x100 && length <= 0xFFFF */ + *msg_ptr = 0x82; + i = 2; + while (i > 0) + { + i--; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + if (i == 0) + { + /* least significant length octet */ + *msg_ptr = (u8_t)length; + } + else + { + /* most significant length octet */ + *msg_ptr = (u8_t)(length >> 8); + } + } + return ERR_OK; + } + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt()) + * @param value is the host order u32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_u32t_cnt() + */ +err_t +snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (octets_needed == 5) + { + /* not enough bits in 'value' add leading 0x00 */ + octets_needed--; + *msg_ptr = 0x00; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes s32_t integer into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode value into + * @param ofs points to the offset within the pbuf chain + * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt()) + * @param value is the host order s32_t value to be encoded + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + * + * @see snmp_asn1_enc_s32t_cnt() + */ +err_t +snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (octets_needed > 1) + { + octets_needed--; + *msg_ptr = (u8_t)(value >> (octets_needed << 3)); + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* (only) one least significant octet */ + *msg_ptr = (u8_t)value; + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes object identifier into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode oid into + * @param ofs points to the offset within the pbuf chain + * @param ident_len object identifier array length + * @param ident points to object identifier array + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + if (ident_len > 1) + { + if ((ident[0] == 1) && (ident[1] == 3)) + { + /* compressed (most common) prefix .iso.org */ + *msg_ptr = 0x2b; + } + else + { + /* calculate prefix */ + *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]); + } + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + ident_len -= 2; + ident += 2; + } + else + { +/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */ + /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */ + return ERR_ARG; + } + while (ident_len > 0) + { + s32_t sub_id; + u8_t shift, tail; + + ident_len--; + sub_id = *ident; + tail = 0; + shift = 28; + while(shift > 0) + { + u8_t code; + + code = (u8_t)(sub_id >> shift); + if ((code != 0) || (tail != 0)) + { + tail = 1; + *msg_ptr = code | 0x80; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + shift -= 7; + } + *msg_ptr = (u8_t)sub_id & 0x7F; + if (ident_len > 0) + { + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + /* proceed to next sub-identifier */ + ident++; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +/** + * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg. + * + * @param p points to output pbuf to encode raw data into + * @param ofs points to the offset within the pbuf chain + * @param raw_len raw data length + * @param raw points raw data + * @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode + */ +err_t +snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw) +{ + u16_t plen, base; + u8_t *msg_ptr; + + plen = 0; + while (p != NULL) + { + base = plen; + plen += p->len; + if (ofs < plen) + { + msg_ptr = (u8_t*)p->payload; + msg_ptr += ofs - base; + + while (raw_len > 1) + { + /* copy raw_len - 1 octets */ + raw_len--; + *msg_ptr = *raw; + raw++; + ofs += 1; + if (ofs >= plen) + { + /* next octet in next pbuf */ + p = p->next; + if (p == NULL) { return ERR_ARG; } + msg_ptr = (u8_t*)p->payload; + plen += p->len; + } + else + { + /* next octet in same pbuf */ + msg_ptr++; + } + } + if (raw_len > 0) + { + /* copy last or single octet */ + *msg_ptr = *raw; + } + return ERR_OK; + } + p = p->next; + } + /* p == NULL, ofs >= plen */ + return ERR_ARG; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib2.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib2.c new file mode 100644 index 0000000..553b4de --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib2.c @@ -0,0 +1,4160 @@ +/** + * @file + * Management Information Base II (RFC1213) objects and functions. + * + * @note the object identifiers for this MIB-2 and private MIB tree + * must be kept in sorted ascending order. This to ensure correct getnext operation. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/netif.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/mem.h" +#include "lwip/tcp_impl.h" +#include "lwip/udp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_structs.h" +#include "lwip/sys.h" +#include "netif/etharp.h" + +#include + +/** + * IANA assigned enterprise ID for lwIP is 26381 + * @see http://www.iana.org/assignments/enterprise-numbers + * + * @note this enterprise ID is assigned to the lwIP project, + * all object identifiers living under this ID are assigned + * by the lwIP maintainers (contact Christiaan Simons)! + * @note don't change this define, use snmp_set_sysobjid() + * + * If you need to create your own private MIB you'll need + * to apply for your own enterprise ID with IANA: + * http://www.iana.org/numbers.html + */ +#define SNMP_ENTERPRISE_ID 26381 +#define SNMP_SYSOBJID_LEN 7 +#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID} + +#ifndef SNMP_SYSSERVICES +#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2)) +#endif + +#ifndef SNMP_GET_SYSUPTIME +#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10)) +#endif + +static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void system_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t system_set_test(struct obj_def *od, u16_t len, void *value); +static void system_set_value(struct obj_def *od, u16_t len, void *value); +static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void interfaces_get_value(struct obj_def *od, u16_t len, void *value); +static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ifentry_get_value(struct obj_def *od, u16_t len, void *value); +#if !SNMP_SAFE_REQUESTS +static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value); +static void ifentry_set_value (struct obj_def *od, u16_t len, void *value); +#endif /* SNMP_SAFE_REQUESTS */ +static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void atentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value); +static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value); +static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value); +static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void icmp_get_value(struct obj_def *od, u16_t len, void *value); +#if LWIP_TCP +static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcp_get_value(struct obj_def *od, u16_t len, void *value); +#ifdef THIS_SEEMS_UNUSED +static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value); +#endif +#endif +static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udp_get_value(struct obj_def *od, u16_t len, void *value); +static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void udpentry_get_value(struct obj_def *od, u16_t len, void *value); +static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od); +static void snmp_get_value(struct obj_def *od, u16_t len, void *value); +static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value); +static void snmp_set_value(struct obj_def *od, u16_t len, void *value); + + +/* snmp .1.3.6.1.2.1.11 */ +const mib_scalar_node snmp_scalar = { + &snmp_get_object_def, + &snmp_get_value, + &snmp_set_test, + &snmp_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t snmp_ids[28] = { + 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30 +}; +struct mib_node* const snmp_nodes[28] = { + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar, + (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar +}; +const struct mib_array_node snmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 28, + snmp_ids, + snmp_nodes +}; + +/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */ +/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */ +/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */ + +/* udp .1.3.6.1.2.1.7 */ +/** index root node for udpTable */ +struct mib_list_rootnode udp_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t udpentry_ids[2] = { 1, 2 }; +struct mib_node* const udpentry_nodes[2] = { + (struct mib_node*)&udp_root, (struct mib_node*)&udp_root, +}; +const struct mib_array_node udpentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + udpentry_ids, + udpentry_nodes +}; + +s32_t udptable_id = 1; +struct mib_node* udptable_node = (struct mib_node*)&udpentry; +struct mib_ram_array_node udptable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &udptable_id, + &udptable_node +}; + +const mib_scalar_node udp_scalar = { + &udp_get_object_def, + &udp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const udp_nodes[5] = { + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar, + (struct mib_node*)&udptable +}; +const struct mib_array_node udp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + udp_ids, + udp_nodes +}; + +/* tcp .1.3.6.1.2.1.6 */ +#if LWIP_TCP +/* only if the TCP protocol is available may implement this group */ +/** index root node for tcpConnTable */ +struct mib_list_rootnode tcpconntree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const tcpconnentry_nodes[5] = { + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root, + (struct mib_node*)&tcpconntree_root +}; +const struct mib_array_node tcpconnentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + tcpconnentry_ids, + tcpconnentry_nodes +}; + +s32_t tcpconntable_id = 1; +struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry; +struct mib_ram_array_node tcpconntable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, +/** @todo update maxlength when inserting / deleting from table + 0 when table is empty, 1 when more than one entry */ + 0, + &tcpconntable_id, + &tcpconntable_node +}; + +const mib_scalar_node tcp_scalar = { + &tcp_get_object_def, + &tcp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +struct mib_node* const tcp_nodes[15] = { + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar, + (struct mib_node*)&tcp_scalar +}; +const struct mib_array_node tcp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 15, + tcp_ids, + tcp_nodes +}; +#endif + +/* icmp .1.3.6.1.2.1.5 */ +const mib_scalar_node icmp_scalar = { + &icmp_get_object_def, + &icmp_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t icmp_ids[26] = { 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, 26 }; +struct mib_node* const icmp_nodes[26] = { + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar, + (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar +}; +const struct mib_array_node icmp = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 26, + icmp_ids, + icmp_nodes +}; + +/** index root node for ipNetToMediaTable */ +struct mib_list_rootnode ipntomtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 }; +struct mib_node* const ipntomentry_nodes[4] = { + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root, + (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root +}; +const struct mib_array_node ipntomentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 4, + ipntomentry_ids, + ipntomentry_nodes +}; + +s32_t ipntomtable_id = 1; +struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry; +struct mib_ram_array_node ipntomtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipntomtable_id, + &ipntomtable_node +}; + +/** index root node for ipRouteTable */ +struct mib_list_rootnode iprtetree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; +struct mib_node* const iprteentry_nodes[13] = { + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root, + (struct mib_node*)&iprtetree_root +}; +const struct mib_array_node iprteentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 13, + iprteentry_ids, + iprteentry_nodes +}; + +s32_t iprtetable_id = 1; +struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry; +struct mib_ram_array_node iprtetable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iprtetable_id, + &iprtetable_node +}; + +/** index root node for ipAddrTable */ +struct mib_list_rootnode ipaddrtree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 }; +struct mib_node* const ipaddrentry_nodes[5] = { + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root, + (struct mib_node*)&ipaddrtree_root +}; +const struct mib_array_node ipaddrentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 5, + ipaddrentry_ids, + ipaddrentry_nodes +}; + +s32_t ipaddrtable_id = 1; +struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry; +struct mib_ram_array_node ipaddrtable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &ipaddrtable_id, + &ipaddrtable_node +}; + +/* ip .1.3.6.1.2.1.4 */ +const mib_scalar_node ip_scalar = { + &ip_get_object_def, + &ip_get_value, + &ip_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; +struct mib_node* const ip_nodes[23] = { + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar, + (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable, + (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable, + (struct mib_node*)&ip_scalar +}; +const struct mib_array_node mib2_ip = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 23, + ip_ids, + ip_nodes +}; + +/** index root node for atTable */ +struct mib_list_rootnode arptree_root = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t atentry_ids[3] = { 1, 2, 3 }; +struct mib_node* const atentry_nodes[3] = { + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root, + (struct mib_node*)&arptree_root +}; +const struct mib_array_node atentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 3, + atentry_ids, + atentry_nodes +}; + +const s32_t attable_id = 1; +struct mib_node* const attable_node = (struct mib_node*)&atentry; +const struct mib_array_node attable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + &attable_id, + &attable_node +}; + +/* at .1.3.6.1.2.1.3 */ +s32_t at_id = 1; +struct mib_node* mib2_at_node = (struct mib_node*)&attable; +struct mib_ram_array_node at = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &at_id, + &mib2_at_node +}; + +/** index root node for ifTable */ +struct mib_list_rootnode iflist_root = { + &ifentry_get_object_def, + &ifentry_get_value, +#if SNMP_SAFE_REQUESTS + &noleafs_set_test, + &noleafs_set_value, +#else /* SNMP_SAFE_REQUESTS */ + &ifentry_set_test, + &ifentry_set_value, +#endif /* SNMP_SAFE_REQUESTS */ + MIB_NODE_LR, + 0, + NULL, + NULL, + 0 +}; +const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 }; +struct mib_node* const ifentry_nodes[22] = { + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root, + (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root +}; +const struct mib_array_node ifentry = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 22, + ifentry_ids, + ifentry_nodes +}; + +s32_t iftable_id = 1; +struct mib_node* iftable_node = (struct mib_node*)&ifentry; +struct mib_ram_array_node iftable = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_RA, + 0, + &iftable_id, + &iftable_node +}; + +/* interfaces .1.3.6.1.2.1.2 */ +const mib_scalar_node interfaces_scalar = { + &interfaces_get_object_def, + &interfaces_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t interfaces_ids[2] = { 1, 2 }; +struct mib_node* const interfaces_nodes[2] = { + (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable +}; +const struct mib_array_node interfaces = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + interfaces_ids, + interfaces_nodes +}; + + +/* 0 1 2 3 4 5 6 */ +/* system .1.3.6.1.2.1.1 */ +const mib_scalar_node sys_tem_scalar = { + &system_get_object_def, + &system_get_value, + &system_set_test, + &system_set_value, + MIB_NODE_SC, + 0 +}; +const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 }; +struct mib_node* const sys_tem_nodes[7] = { + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar, + (struct mib_node*)&sys_tem_scalar +}; +/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */ +const struct mib_array_node sys_tem = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 7, + sys_tem_ids, + sys_tem_nodes +}; + +/* mib-2 .1.3.6.1.2.1 */ +#if LWIP_TCP +#define MIB2_GROUPS 8 +#else +#define MIB2_GROUPS 7 +#endif +const s32_t mib2_ids[MIB2_GROUPS] = +{ + 1, + 2, + 3, + 4, + 5, +#if LWIP_TCP + 6, +#endif + 7, + 11 +}; +struct mib_node* const mib2_nodes[MIB2_GROUPS] = { + (struct mib_node*)&sys_tem, + (struct mib_node*)&interfaces, + (struct mib_node*)&at, + (struct mib_node*)&mib2_ip, + (struct mib_node*)&icmp, +#if LWIP_TCP + (struct mib_node*)&tcp, +#endif + (struct mib_node*)&udp, + (struct mib_node*)&snmp +}; + +const struct mib_array_node mib2 = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + MIB2_GROUPS, + mib2_ids, + mib2_nodes +}; + +/* mgmt .1.3.6.1.2 */ +const s32_t mgmt_ids[1] = { 1 }; +struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 }; +const struct mib_array_node mgmt = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + mgmt_ids, + mgmt_nodes +}; + +/* internet .1.3.6.1 */ +#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. */ +s32_t internet_ids[2] = { 2, 4 }; +struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 2, + internet_ids, + internet_nodes +}; +#else +const s32_t internet_ids[1] = { 2 }; +struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt }; +const struct mib_array_node internet = { + &noleafs_get_object_def, + &noleafs_get_value, + &noleafs_set_test, + &noleafs_set_value, + MIB_NODE_AR, + 1, + internet_ids, + internet_nodes +}; +#endif + +/** mib-2.system.sysObjectID */ +static const struct snmp_obj_id sysobjid_default = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID}; +static const struct snmp_obj_id* sysobjid_ptr = &sysobjid_default; +/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */ +static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}}; +/** mib-2.system.sysServices */ +static const s32_t sysservices = SNMP_SYSSERVICES; + +/** mib-2.system.sysDescr */ +static const u8_t sysdescr_len_default = 4; +static const u8_t sysdescr_default[] = "lwIP"; +static const u8_t* sysdescr_len_ptr = &sysdescr_len_default; +static const u8_t* sysdescr_ptr = &sysdescr_default[0]; +/** mib-2.system.sysContact */ +static const u8_t syscontact_len_default = 0; +static const u8_t syscontact_default[] = ""; +static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default; +static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0]; +/** mib-2.system.sysName */ +static const u8_t sysname_len_default = 8; +static const u8_t sysname_default[] = "FQDN-unk"; +static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default; +static u8_t* sysname_ptr = (u8_t*)&sysname_default[0]; +/** mib-2.system.sysLocation */ +static const u8_t syslocation_len_default = 0; +static const u8_t syslocation_default[] = ""; +static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default; +static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0]; +/** mib-2.snmp.snmpEnableAuthenTraps */ +static const u8_t snmpenableauthentraps_default = 2; /* disabled */ +static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default; + +/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */ +static const struct snmp_obj_id ifspecific = {2, {0, 0}}; +/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */ +static const struct snmp_obj_id iprouteinfo = {2, {0, 0}}; + + + +/* mib-2.system counter(s) */ +static u32_t sysuptime = 0; + +/* mib-2.ip counter(s) */ +static u32_t ipinreceives = 0, + ipinhdrerrors = 0, + ipinaddrerrors = 0, + ipforwdatagrams = 0, + ipinunknownprotos = 0, + ipindiscards = 0, + ipindelivers = 0, + ipoutrequests = 0, + ipoutdiscards = 0, + ipoutnoroutes = 0, + ipreasmreqds = 0, + ipreasmoks = 0, + ipreasmfails = 0, + ipfragoks = 0, + ipfragfails = 0, + ipfragcreates = 0, + iproutingdiscards = 0; +/* mib-2.icmp counter(s) */ +static u32_t icmpinmsgs = 0, + icmpinerrors = 0, + icmpindestunreachs = 0, + icmpintimeexcds = 0, + icmpinparmprobs = 0, + icmpinsrcquenchs = 0, + icmpinredirects = 0, + icmpinechos = 0, + icmpinechoreps = 0, + icmpintimestamps = 0, + icmpintimestampreps = 0, + icmpinaddrmasks = 0, + icmpinaddrmaskreps = 0, + icmpoutmsgs = 0, + icmpouterrors = 0, + icmpoutdestunreachs = 0, + icmpouttimeexcds = 0, + icmpoutparmprobs = 0, + icmpoutsrcquenchs = 0, + icmpoutredirects = 0, + icmpoutechos = 0, + icmpoutechoreps = 0, + icmpouttimestamps = 0, + icmpouttimestampreps = 0, + icmpoutaddrmasks = 0, + icmpoutaddrmaskreps = 0; +/* mib-2.tcp counter(s) */ +static u32_t tcpactiveopens = 0, + tcppassiveopens = 0, + tcpattemptfails = 0, + tcpestabresets = 0, + tcpinsegs = 0, + tcpoutsegs = 0, + tcpretranssegs = 0, + tcpinerrs = 0, + tcpoutrsts = 0; +/* mib-2.udp counter(s) */ +static u32_t udpindatagrams = 0, + udpnoports = 0, + udpinerrors = 0, + udpoutdatagrams = 0; +/* mib-2.snmp counter(s) */ +static u32_t snmpinpkts = 0, + snmpoutpkts = 0, + snmpinbadversions = 0, + snmpinbadcommunitynames = 0, + snmpinbadcommunityuses = 0, + snmpinasnparseerrs = 0, + snmpintoobigs = 0, + snmpinnosuchnames = 0, + snmpinbadvalues = 0, + snmpinreadonlys = 0, + snmpingenerrs = 0, + snmpintotalreqvars = 0, + snmpintotalsetvars = 0, + snmpingetrequests = 0, + snmpingetnexts = 0, + snmpinsetrequests = 0, + snmpingetresponses = 0, + snmpintraps = 0, + snmpouttoobigs = 0, + snmpoutnosuchnames = 0, + snmpoutbadvalues = 0, + snmpoutgenerrs = 0, + snmpoutgetrequests = 0, + snmpoutgetnexts = 0, + snmpoutsetrequests = 0, + snmpoutgetresponses = 0, + snmpouttraps = 0; + + +/** + * Initializes sysDescr pointers. + * + * @param str if non-NULL then copy str pointer + * @param len points to string length, excluding zero terminator + */ +void snmp_set_sysdescr(const u8_t *str, const u8_t *len) +{ + if (str != NULL) + { + sysdescr_ptr = str; + sysdescr_len_ptr = len; + } +} + +void snmp_get_sysobjid_ptr(const struct snmp_obj_id **oid) +{ + *oid = sysobjid_ptr; +} + +/** + * Initializes sysObjectID value. + * + * @param oid points to stuct snmp_obj_id to copy + */ +void snmp_set_sysobjid(const struct snmp_obj_id *oid) +{ + sysobjid_ptr = oid; +} + +/** + * Must be called at regular 10 msec interval from a timer interrupt + * or signal handler depending on your runtime environment. + */ +void snmp_inc_sysuptime(void) +{ + sysuptime++; +} + +void snmp_add_sysuptime(u32_t value) +{ + sysuptime+=value; +} + +void snmp_get_sysuptime(u32_t *value) +{ + SNMP_GET_SYSUPTIME(sysuptime); + *value = sysuptime; +} + +/** + * Initializes sysContact pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syscontact_ptr = ocstr; + syscontact_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysName pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + sysname_ptr = ocstr; + sysname_len_ptr = ocstrlen; + } +} + +/** + * Initializes sysLocation pointers, + * e.g. ptrs to non-volatile memory external to lwIP. + * + * @param ocstr if non-NULL then copy str pointer + * @param ocstrlen points to string length, excluding zero terminator + */ +void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen) +{ + if (ocstr != NULL) + { + syslocation_ptr = ocstr; + syslocation_len_ptr = ocstrlen; + } +} + + +void snmp_add_ifinoctets(struct netif *ni, u32_t value) +{ + ni->ifinoctets += value; +} + +void snmp_inc_ifinucastpkts(struct netif *ni) +{ + (ni->ifinucastpkts)++; +} + +void snmp_inc_ifinnucastpkts(struct netif *ni) +{ + (ni->ifinnucastpkts)++; +} + +void snmp_inc_ifindiscards(struct netif *ni) +{ + (ni->ifindiscards)++; +} + +void snmp_add_ifoutoctets(struct netif *ni, u32_t value) +{ + ni->ifoutoctets += value; +} + +void snmp_inc_ifoutucastpkts(struct netif *ni) +{ + (ni->ifoutucastpkts)++; +} + +void snmp_inc_ifoutnucastpkts(struct netif *ni) +{ + (ni->ifoutnucastpkts)++; +} + +void snmp_inc_ifoutdiscards(struct netif *ni) +{ + (ni->ifoutdiscards)++; +} + +void snmp_inc_iflist(void) +{ + struct mib_list_node *if_node = NULL; + + snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node); + /* enable getnext traversal on filled table */ + iftable.maxlength = 1; +} + +void snmp_dec_iflist(void) +{ + snmp_mib_node_delete(&iflist_root, iflist_root.tail); + /* disable getnext traversal on empty table */ + if(iflist_root.count == 0) iftable.maxlength = 0; +} + +/** + * Inserts ARP table indexes (.xIfIndex.xNetAddress) + * into arp table index trees (both atTable and ipNetToMediaTable). + */ +void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn; + struct mib_list_node *at_node; + s32_t arpidx[5]; + u8_t level, tree; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + for (level = 0; level < 5; level++) + { + at_node = NULL; + snmp_mib_node_insert(at_rn, arpidx[level], &at_node); + if ((level != 4) && (at_node != NULL)) + { + if (at_node->nptr == NULL) + { + at_rn = snmp_mib_lrn_alloc(); + at_node->nptr = (struct mib_node*)at_rn; + if (at_rn != NULL) + { + if (level == 3) + { + if (tree == 0) + { + at_rn->get_object_def = atentry_get_object_def; + at_rn->get_value = atentry_get_value; + } + else + { + at_rn->get_object_def = ip_ntomentry_get_object_def; + at_rn->get_value = ip_ntomentry_get_value; + } + at_rn->set_test = noleafs_set_test; + at_rn->set_value = noleafs_set_value; + } + } + else + { + /* at_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full")); + break; + } + } + else + { + at_rn = (struct mib_list_rootnode*)at_node->nptr; + } + } + } + } + /* enable getnext traversal on filled tables */ + at.maxlength = 1; + ipntomtable.maxlength = 1; +} + +/** + * Removes ARP table indexes (.xIfIndex.xNetAddress) + * from arp table index trees. + */ +void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip) +{ + struct mib_list_rootnode *at_rn, *next, *del_rn[5]; + struct mib_list_node *at_n, *del_n[5]; + s32_t arpidx[5]; + u8_t fc, tree, level, del_cnt; + + snmp_netiftoifindex(ni, &arpidx[0]); + snmp_iptooid(ip, &arpidx[1]); + + for (tree = 0; tree < 2; tree++) + { + /* mark nodes for deletion */ + if (tree == 0) + { + at_rn = &arptree_root; + } + else + { + at_rn = &ipntomtree_root; + } + level = 0; + del_cnt = 0; + while ((level < 5) && (at_rn != NULL)) + { + fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n); + if (fc == 0) + { + /* arpidx[level] does not exist */ + del_cnt = 0; + at_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = at_rn; + del_n[del_cnt] = at_n; + del_cnt++; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + at_rn = (struct mib_list_rootnode*)(at_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + at_rn = del_rn[del_cnt]; + at_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(at_rn, at_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty tables */ + if(arptree_root.count == 0) at.maxlength = 0; + if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0; +} + +void snmp_inc_ipinreceives(void) +{ + ipinreceives++; +} + +void snmp_inc_ipinhdrerrors(void) +{ + ipinhdrerrors++; +} + +void snmp_inc_ipinaddrerrors(void) +{ + ipinaddrerrors++; +} + +void snmp_inc_ipforwdatagrams(void) +{ + ipforwdatagrams++; +} + +void snmp_inc_ipinunknownprotos(void) +{ + ipinunknownprotos++; +} + +void snmp_inc_ipindiscards(void) +{ + ipindiscards++; +} + +void snmp_inc_ipindelivers(void) +{ + ipindelivers++; +} + +void snmp_inc_ipoutrequests(void) +{ + ipoutrequests++; +} + +void snmp_inc_ipoutdiscards(void) +{ + ipoutdiscards++; +} + +void snmp_inc_ipoutnoroutes(void) +{ + ipoutnoroutes++; +} + +void snmp_inc_ipreasmreqds(void) +{ + ipreasmreqds++; +} + +void snmp_inc_ipreasmoks(void) +{ + ipreasmoks++; +} + +void snmp_inc_ipreasmfails(void) +{ + ipreasmfails++; +} + +void snmp_inc_ipfragoks(void) +{ + ipfragoks++; +} + +void snmp_inc_ipfragfails(void) +{ + ipfragfails++; +} + +void snmp_inc_ipfragcreates(void) +{ + ipfragcreates++; +} + +void snmp_inc_iproutingdiscards(void) +{ + iproutingdiscards++; +} + +/** + * Inserts ipAddrTable indexes (.ipAdEntAddr) + * into index tree. + */ +void snmp_insert_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn; + struct mib_list_node *ipa_node; + s32_t ipaddridx[4]; + u8_t level; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + level = 0; + ipa_rn = &ipaddrtree_root; + while (level < 4) + { + ipa_node = NULL; + snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node); + if ((level != 3) && (ipa_node != NULL)) + { + if (ipa_node->nptr == NULL) + { + ipa_rn = snmp_mib_lrn_alloc(); + ipa_node->nptr = (struct mib_node*)ipa_rn; + if (ipa_rn != NULL) + { + if (level == 2) + { + ipa_rn->get_object_def = ip_addrentry_get_object_def; + ipa_rn->get_value = ip_addrentry_get_value; + ipa_rn->set_test = noleafs_set_test; + ipa_rn->set_value = noleafs_set_value; + } + } + else + { + /* ipa_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full")); + break; + } + } + else + { + ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr; + } + } + level++; + } + /* enable getnext traversal on filled table */ + ipaddrtable.maxlength = 1; +} + +/** + * Removes ipAddrTable indexes (.ipAdEntAddr) + * from index tree. + */ +void snmp_delete_ipaddridx_tree(struct netif *ni) +{ + struct mib_list_rootnode *ipa_rn, *next, *del_rn[4]; + struct mib_list_node *ipa_n, *del_n[4]; + s32_t ipaddridx[4]; + u8_t fc, level, del_cnt; + + LWIP_ASSERT("ni != NULL", ni != NULL); + snmp_iptooid(&ni->ip_addr, &ipaddridx[0]); + + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + ipa_rn = &ipaddrtree_root; + while ((level < 4) && (ipa_rn != NULL)) + { + fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n); + if (fc == 0) + { + /* ipaddridx[level] does not exist */ + del_cnt = 0; + ipa_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = ipa_rn; + del_n[del_cnt] = ipa_n; + del_cnt++; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + ipa_rn = del_rn[del_cnt]; + ipa_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(ipa_rn, ipa_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + /* disable getnext traversal on empty table */ + if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0; +} + +/** + * Inserts ipRouteTable indexes (.ipRouteDest) + * into index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte + * + * @todo record sysuptime for _this_ route when it is installed + * (needed for ipRouteAge) in the netif. + */ +void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t insert = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + insert = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + insert = 1; + } + } + if (insert) + { + struct mib_list_rootnode *iprte_rn; + struct mib_list_node *iprte_node; + s32_t iprteidx[4]; + u8_t level; + + snmp_iptooid(&dst, &iprteidx[0]); + level = 0; + iprte_rn = &iprtetree_root; + while (level < 4) + { + iprte_node = NULL; + snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node); + if ((level != 3) && (iprte_node != NULL)) + { + if (iprte_node->nptr == NULL) + { + iprte_rn = snmp_mib_lrn_alloc(); + iprte_node->nptr = (struct mib_node*)iprte_rn; + if (iprte_rn != NULL) + { + if (level == 2) + { + iprte_rn->get_object_def = ip_rteentry_get_object_def; + iprte_rn->get_value = ip_rteentry_get_value; + iprte_rn->set_test = noleafs_set_test; + iprte_rn->set_value = noleafs_set_value; + } + } + else + { + /* iprte_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full")); + break; + } + } + else + { + iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr; + } + } + level++; + } + } + /* enable getnext traversal on filled table */ + iprtetable.maxlength = 1; +} + +/** + * Removes ipRouteTable indexes (.ipRouteDest) + * from index tree. + * + * @param dflt non-zero for the default rte, zero for network rte + * @param ni points to network interface for this rte or NULL + * for default route to be removed. + */ +void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni) +{ + u8_t del = 0; + ip_addr_t dst; + + if (dflt != 0) + { + /* the default route 0.0.0.0 */ + ip_addr_set_any(&dst); + del = 1; + } + else + { + /* route to the network address */ + ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask); + /* exclude 0.0.0.0 network (reserved for default rte) */ + if (!ip_addr_isany(&dst)) { + del = 1; + } + } + if (del) + { + struct mib_list_rootnode *iprte_rn, *next, *del_rn[4]; + struct mib_list_node *iprte_n, *del_n[4]; + s32_t iprteidx[4]; + u8_t fc, level, del_cnt; + + snmp_iptooid(&dst, &iprteidx[0]); + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + iprte_rn = &iprtetree_root; + while ((level < 4) && (iprte_rn != NULL)) + { + fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n); + if (fc == 0) + { + /* iprteidx[level] does not exist */ + del_cnt = 0; + iprte_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = iprte_rn; + del_n[del_cnt] = iprte_n; + del_cnt++; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + iprte_rn = del_rn[del_cnt]; + iprte_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(iprte_rn, iprte_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (iprtetree_root.count == 0) iprtetable.maxlength = 0; +} + + +void snmp_inc_icmpinmsgs(void) +{ + icmpinmsgs++; +} + +void snmp_inc_icmpinerrors(void) +{ + icmpinerrors++; +} + +void snmp_inc_icmpindestunreachs(void) +{ + icmpindestunreachs++; +} + +void snmp_inc_icmpintimeexcds(void) +{ + icmpintimeexcds++; +} + +void snmp_inc_icmpinparmprobs(void) +{ + icmpinparmprobs++; +} + +void snmp_inc_icmpinsrcquenchs(void) +{ + icmpinsrcquenchs++; +} + +void snmp_inc_icmpinredirects(void) +{ + icmpinredirects++; +} + +void snmp_inc_icmpinechos(void) +{ + icmpinechos++; +} + +void snmp_inc_icmpinechoreps(void) +{ + icmpinechoreps++; +} + +void snmp_inc_icmpintimestamps(void) +{ + icmpintimestamps++; +} + +void snmp_inc_icmpintimestampreps(void) +{ + icmpintimestampreps++; +} + +void snmp_inc_icmpinaddrmasks(void) +{ + icmpinaddrmasks++; +} + +void snmp_inc_icmpinaddrmaskreps(void) +{ + icmpinaddrmaskreps++; +} + +void snmp_inc_icmpoutmsgs(void) +{ + icmpoutmsgs++; +} + +void snmp_inc_icmpouterrors(void) +{ + icmpouterrors++; +} + +void snmp_inc_icmpoutdestunreachs(void) +{ + icmpoutdestunreachs++; +} + +void snmp_inc_icmpouttimeexcds(void) +{ + icmpouttimeexcds++; +} + +void snmp_inc_icmpoutparmprobs(void) +{ + icmpoutparmprobs++; +} + +void snmp_inc_icmpoutsrcquenchs(void) +{ + icmpoutsrcquenchs++; +} + +void snmp_inc_icmpoutredirects(void) +{ + icmpoutredirects++; +} + +void snmp_inc_icmpoutechos(void) +{ + icmpoutechos++; +} + +void snmp_inc_icmpoutechoreps(void) +{ + icmpoutechoreps++; +} + +void snmp_inc_icmpouttimestamps(void) +{ + icmpouttimestamps++; +} + +void snmp_inc_icmpouttimestampreps(void) +{ + icmpouttimestampreps++; +} + +void snmp_inc_icmpoutaddrmasks(void) +{ + icmpoutaddrmasks++; +} + +void snmp_inc_icmpoutaddrmaskreps(void) +{ + icmpoutaddrmaskreps++; +} + +void snmp_inc_tcpactiveopens(void) +{ + tcpactiveopens++; +} + +void snmp_inc_tcppassiveopens(void) +{ + tcppassiveopens++; +} + +void snmp_inc_tcpattemptfails(void) +{ + tcpattemptfails++; +} + +void snmp_inc_tcpestabresets(void) +{ + tcpestabresets++; +} + +void snmp_inc_tcpinsegs(void) +{ + tcpinsegs++; +} + +void snmp_inc_tcpoutsegs(void) +{ + tcpoutsegs++; +} + +void snmp_inc_tcpretranssegs(void) +{ + tcpretranssegs++; +} + +void snmp_inc_tcpinerrs(void) +{ + tcpinerrs++; +} + +void snmp_inc_tcpoutrsts(void) +{ + tcpoutrsts++; +} + +void snmp_inc_udpindatagrams(void) +{ + udpindatagrams++; +} + +void snmp_inc_udpnoports(void) +{ + udpnoports++; +} + +void snmp_inc_udpinerrors(void) +{ + udpinerrors++; +} + +void snmp_inc_udpoutdatagrams(void) +{ + udpoutdatagrams++; +} + +/** + * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort) + * into index tree. + */ +void snmp_insert_udpidx_tree(struct udp_pcb *pcb) +{ + struct mib_list_rootnode *udp_rn; + struct mib_list_node *udp_node; + s32_t udpidx[5]; + u8_t level; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + udp_rn = &udp_root; + for (level = 0; level < 5; level++) + { + udp_node = NULL; + snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node); + if ((level != 4) && (udp_node != NULL)) + { + if (udp_node->nptr == NULL) + { + udp_rn = snmp_mib_lrn_alloc(); + udp_node->nptr = (struct mib_node*)udp_rn; + if (udp_rn != NULL) + { + if (level == 3) + { + udp_rn->get_object_def = udpentry_get_object_def; + udp_rn->get_value = udpentry_get_value; + udp_rn->set_test = noleafs_set_test; + udp_rn->set_value = noleafs_set_value; + } + } + else + { + /* udp_rn == NULL, malloc failure */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full")); + break; + } + } + else + { + udp_rn = (struct mib_list_rootnode*)udp_node->nptr; + } + } + } + udptable.maxlength = 1; +} + +/** + * Removes udpTable indexes (.udpLocalAddress.udpLocalPort) + * from index tree. + */ +void snmp_delete_udpidx_tree(struct udp_pcb *pcb) +{ + struct udp_pcb *npcb; + struct mib_list_rootnode *udp_rn, *next, *del_rn[5]; + struct mib_list_node *udp_n, *del_n[5]; + s32_t udpidx[5]; + u8_t bindings, fc, level, del_cnt; + + LWIP_ASSERT("pcb != NULL", pcb != NULL); + snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]); + udpidx[4] = pcb->local_port; + + /* count PCBs for a given binding + (e.g. when reusing ports or for temp output PCBs) */ + bindings = 0; + npcb = udp_pcbs; + while ((npcb != NULL)) + { + if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) && + (npcb->local_port == udpidx[4])) + { + bindings++; + } + npcb = npcb->next; + } + if (bindings == 1) + { + /* selectively remove */ + /* mark nodes for deletion */ + level = 0; + del_cnt = 0; + udp_rn = &udp_root; + while ((level < 5) && (udp_rn != NULL)) + { + fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n); + if (fc == 0) + { + /* udpidx[level] does not exist */ + del_cnt = 0; + udp_rn = NULL; + } + else if (fc == 1) + { + del_rn[del_cnt] = udp_rn; + del_n[del_cnt] = udp_n; + del_cnt++; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + else if (fc == 2) + { + /* reset delete (2 or more childs) */ + del_cnt = 0; + udp_rn = (struct mib_list_rootnode*)(udp_n->nptr); + } + level++; + } + /* delete marked index nodes */ + while (del_cnt > 0) + { + del_cnt--; + + udp_rn = del_rn[del_cnt]; + udp_n = del_n[del_cnt]; + + next = snmp_mib_node_delete(udp_rn, udp_n); + if (next != NULL) + { + LWIP_ASSERT("next_count == 0",next->count == 0); + snmp_mib_lrn_free(next); + } + } + } + /* disable getnext traversal on empty table */ + if (udp_root.count == 0) udptable.maxlength = 0; +} + + +void snmp_inc_snmpinpkts(void) +{ + snmpinpkts++; +} + +void snmp_inc_snmpoutpkts(void) +{ + snmpoutpkts++; +} + +void snmp_inc_snmpinbadversions(void) +{ + snmpinbadversions++; +} + +void snmp_inc_snmpinbadcommunitynames(void) +{ + snmpinbadcommunitynames++; +} + +void snmp_inc_snmpinbadcommunityuses(void) +{ + snmpinbadcommunityuses++; +} + +void snmp_inc_snmpinasnparseerrs(void) +{ + snmpinasnparseerrs++; +} + +void snmp_inc_snmpintoobigs(void) +{ + snmpintoobigs++; +} + +void snmp_inc_snmpinnosuchnames(void) +{ + snmpinnosuchnames++; +} + +void snmp_inc_snmpinbadvalues(void) +{ + snmpinbadvalues++; +} + +void snmp_inc_snmpinreadonlys(void) +{ + snmpinreadonlys++; +} + +void snmp_inc_snmpingenerrs(void) +{ + snmpingenerrs++; +} + +void snmp_add_snmpintotalreqvars(u8_t value) +{ + snmpintotalreqvars += value; +} + +void snmp_add_snmpintotalsetvars(u8_t value) +{ + snmpintotalsetvars += value; +} + +void snmp_inc_snmpingetrequests(void) +{ + snmpingetrequests++; +} + +void snmp_inc_snmpingetnexts(void) +{ + snmpingetnexts++; +} + +void snmp_inc_snmpinsetrequests(void) +{ + snmpinsetrequests++; +} + +void snmp_inc_snmpingetresponses(void) +{ + snmpingetresponses++; +} + +void snmp_inc_snmpintraps(void) +{ + snmpintraps++; +} + +void snmp_inc_snmpouttoobigs(void) +{ + snmpouttoobigs++; +} + +void snmp_inc_snmpoutnosuchnames(void) +{ + snmpoutnosuchnames++; +} + +void snmp_inc_snmpoutbadvalues(void) +{ + snmpoutbadvalues++; +} + +void snmp_inc_snmpoutgenerrs(void) +{ + snmpoutgenerrs++; +} + +void snmp_inc_snmpoutgetrequests(void) +{ + snmpoutgetrequests++; +} + +void snmp_inc_snmpoutgetnexts(void) +{ + snmpoutgetnexts++; +} + +void snmp_inc_snmpoutsetrequests(void) +{ + snmpoutsetrequests++; +} + +void snmp_inc_snmpoutgetresponses(void) +{ + snmpoutgetresponses++; +} + +void snmp_inc_snmpouttraps(void) +{ + snmpouttraps++; +} + +void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid) +{ + *oid = &snmpgrp_id; +} + +void snmp_set_snmpenableauthentraps(u8_t *value) +{ + if (value != NULL) + { + snmpenableauthentraps_ptr = value; + } +} + +void snmp_get_snmpenableauthentraps(u8_t *value) +{ + *value = *snmpenableauthentraps_ptr; +} + +void +noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + LWIP_UNUSED_ARG(ident_len); + LWIP_UNUSED_ARG(ident); + od->instance = MIB_OBJECT_NONE; +} + +void +noleafs_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + +u8_t +noleafs_set_test(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); + /* can't set */ + return 0; +} + +void +noleafs_set_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(od); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value); +} + + +/** + * Returns systems object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param od points to object definition. + */ +static void +system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* sysDescr */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysdescr_len_ptr; + break; + case 2: /* sysObjectID */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = sysobjid_ptr->len * sizeof(s32_t); + break; + case 3: /* sysUpTime */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 4: /* sysContact */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syscontact_len_ptr; + break; + case 5: /* sysName */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *sysname_len_ptr; + break; + case 6: /* sysLocation */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = *syslocation_len_ptr; + break; + case 7: /* sysServices */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns system object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +system_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* sysDescr */ + MEMCPY(value, sysdescr_ptr, len); + break; + case 2: /* sysObjectID */ + MEMCPY(value, sysobjid_ptr->id, len); + break; + case 3: /* sysUpTime */ + { + snmp_get_sysuptime((u32_t*)value); + } + break; + case 4: /* sysContact */ + MEMCPY(value, syscontact_ptr, len); + break; + case 5: /* sysName */ + MEMCPY(value, sysname_ptr, len); + break; + case 6: /* sysLocation */ + MEMCPY(value, syslocation_ptr, len); + break; + case 7: /* sysServices */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = sysservices; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %d\n", id)); + break; + }; +} + +static u8_t +system_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(value); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + if ((syscontact_ptr != syscontact_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 5: /* sysName */ + if ((sysname_ptr != sysname_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + case 6: /* sysLocation */ + if ((syslocation_ptr != syslocation_default) && + (len <= 255)) + { + set_ok = 1; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %d\n", id)); + break; + }; + return set_ok; +} + +static void +system_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_ASSERT("invalid len", len <= 0xff); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 4: /* sysContact */ + MEMCPY(syscontact_ptr, value, len); + *syscontact_len_ptr = (u8_t)len; + break; + case 5: /* sysName */ + MEMCPY(sysname_ptr, value, len); + *sysname_len_ptr = (u8_t)len; + break; + case 6: /* sysLocation */ + MEMCPY(syslocation_ptr, value, len); + *syslocation_len_ptr = (u8_t)len; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %d\n", id)); + break; + }; +} + +/** + * Returns interfaces.ifnumber object definition. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns interfaces.ifnumber object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +interfaces_get_value(struct obj_def *od, u16_t len, void *value) +{ + LWIP_UNUSED_ARG(len); + if (od->id_inst_ptr[0] == 1) + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = iflist_root.count; + } +} + +/** + * Returns ifentry object definitions. + * + * @param ident_len the address length (2) + * @param ident points to objectname.index + * @param od points to object definition. + */ +static void +ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id)); + switch (id) + { + case 1: /* ifIndex */ + case 3: /* ifType */ + case 4: /* ifMtu */ + case 8: /* ifOperStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ifDescr */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + /** @todo this should be some sort of sizeof(struct netif.name) */ + od->v_len = 2; + break; + case 5: /* ifSpeed */ + case 21: /* ifOutQLen */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + case 6: /* ifPhysAddress */ + { + struct netif *netif; + + snmp_ifindextonetif(ident[1], &netif); + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = netif->hwaddr_len; + } + break; + case 7: /* ifAdminStatus */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ifLastChange */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS); + od->v_len = sizeof(u32_t); + break; + case 10: /* ifInOctets */ + case 11: /* ifInUcastPkts */ + case 12: /* ifInNUcastPkts */ + case 13: /* ifInDiscarts */ + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + case 16: /* ifOutOctets */ + case 17: /* ifOutUcastPkts */ + case 18: /* ifOutNUcastPkts */ + case 19: /* ifOutDiscarts */ + case 20: /* ifOutErrors */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 22: /* ifSpecific */ + /** @note returning zeroDotZero (0.0) no media specific MIB support */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = ifspecific.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +/** + * Returns ifentry object value. + * + * @param ident_len the address length (2) + * @param ident points to objectname.0 (object id trailer) + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value into. + */ +static void +ifentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ifIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ifDescr */ + MEMCPY(value, netif->name, len); + break; + case 3: /* ifType */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->link_type; + } + break; + case 4: /* ifMtu */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = netif->mtu; + } + break; + case 5: /* ifSpeed */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->link_speed; + } + break; + case 6: /* ifPhysAddress */ + MEMCPY(value, netif->hwaddr, len); + break; + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + if (netif_is_link_up(netif)) + { + *sint_ptr = 1; /* up */ + } + else + { + *sint_ptr = 7; /* lowerLayerDown */ + } + } + else + { + *sint_ptr = 2; /* down */ + } + } + break; + case 8: /* ifOperStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (netif_is_up(netif)) + { + *sint_ptr = 1; + } + else + { + *sint_ptr = 2; + } + } + break; + case 9: /* ifLastChange */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ts; + } + break; + case 10: /* ifInOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinoctets; + } + break; + case 11: /* ifInUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinucastpkts; + } + break; + case 12: /* ifInNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifinnucastpkts; + } + break; + case 13: /* ifInDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifindiscards; + } + break; + case 14: /* ifInErrors */ + case 15: /* ifInUnkownProtos */ + /** @todo add these counters! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 16: /* ifOutOctets */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutoctets; + } + break; + case 17: /* ifOutUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutucastpkts; + } + break; + case 18: /* ifOutNUcastPkts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutnucastpkts; + } + break; + case 19: /* ifOutDiscarts */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = netif->ifoutdiscards; + } + break; + case 20: /* ifOutErrors */ + /** @todo add this counter! */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 21: /* ifOutQLen */ + /** @todo figure out if this must be 0 (no queue) or 1? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = 0; + } + break; + case 22: /* ifSpecific */ + MEMCPY(value, ifspecific.id, len); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_value(): unknown id: %d\n", id)); + break; + }; +} + +#if !SNMP_SAFE_REQUESTS +static u8_t +ifentry_set_test(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id, set_ok; + LWIP_UNUSED_ARG(len); + + set_ok = 0; + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1 || *sint_ptr == 2) + set_ok = 1; + } + break; + } + return set_ok; +} + +static void +ifentry_set_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + u8_t id; + LWIP_UNUSED_ARG(len); + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 7: /* ifAdminStatus */ + { + s32_t *sint_ptr = (s32_t*)value; + if (*sint_ptr == 1) + { + netif_set_up(netif); + } + else if (*sint_ptr == 2) + { + netif_set_down(netif); + } + } + break; + } +} +#endif /* SNMP_SAFE_REQUESTS */ + +/** + * Returns atentry object definitions. + * + * @param ident_len the address length (6) + * @param ident points to objectname.atifindex.atnetaddress + * @param od points to object definition. + */ +static void +atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* atIfIndex */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* atPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* atNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +atentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* atIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* atPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* atNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_value(): unknown id: %d\n", id)); + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id)); + switch (id) + { + case 1: /* ipForwarding */ + case 2: /* ipDefaultTTL */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 3: /* ipInReceives */ + case 4: /* ipInHdrErrors */ + case 5: /* ipInAddrErrors */ + case 6: /* ipForwDatagrams */ + case 7: /* ipInUnknownProtos */ + case 8: /* ipInDiscards */ + case 9: /* ipInDelivers */ + case 10: /* ipOutRequests */ + case 11: /* ipOutDiscards */ + case 12: /* ipOutNoRoutes */ + case 14: /* ipReasmReqds */ + case 15: /* ipReasmOKs */ + case 16: /* ipReasmFails */ + case 17: /* ipFragOKs */ + case 18: /* ipFragFails */ + case 19: /* ipFragCreates */ + case 23: /* ipRoutingDiscards */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 13: /* ipReasmTimeout */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_FORWARD + /* forwarding */ + *sint_ptr = 1; +#else + /* not-forwarding */ + *sint_ptr = 2; +#endif + } + break; + case 2: /* ipDefaultTTL */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = IP_DEFAULT_TTL; + } + break; + case 3: /* ipInReceives */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinreceives; + } + break; + case 4: /* ipInHdrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinhdrerrors; + } + break; + case 5: /* ipInAddrErrors */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinaddrerrors; + } + break; + case 6: /* ipForwDatagrams */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipforwdatagrams; + } + break; + case 7: /* ipInUnknownProtos */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipinunknownprotos; + } + break; + case 8: /* ipInDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindiscards; + } + break; + case 9: /* ipInDelivers */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipindelivers; + } + break; + case 10: /* ipOutRequests */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutrequests; + } + break; + case 11: /* ipOutDiscards */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutdiscards; + } + break; + case 12: /* ipOutNoRoutes */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipoutnoroutes; + } + break; + case 13: /* ipReasmTimeout */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + *sint_ptr = IP_REASS_MAXAGE; +#else + *sint_ptr = 0; +#endif + } + break; + case 14: /* ipReasmReqds */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmreqds; + } + break; + case 15: /* ipReasmOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmoks; + } + break; + case 16: /* ipReasmFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipreasmfails; + } + break; + case 17: /* ipFragOKs */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragoks; + } + break; + case 18: /* ipFragFails */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragfails; + } + break; + case 19: /* ipFragCreates */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = ipfragcreates; + } + break; + case 23: /* ipRoutingDiscards */ + /** @todo can lwIP discard routes at all?? hardwire this to 0?? */ + { + u32_t *uint_ptr = (u32_t*)value; + *uint_ptr = iproutingdiscards; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %d\n", id)); + break; + }; +} + +/** + * Test ip object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + * + * @note we allow set if the value matches the hardwired value, + * otherwise return badvalue. + */ +static u8_t +ip_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + s32_t *sint_ptr = (s32_t*)value; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipForwarding */ +#if IP_FORWARD + /* forwarding */ + if (*sint_ptr == 1) +#else + /* not-forwarding */ + if (*sint_ptr == 2) +#endif + { + set_ok = 1; + } + break; + case 2: /* ipDefaultTTL */ + if (*sint_ptr == IP_DEFAULT_TTL) + { + set_ok = 1; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %d\n", id)); + break; + }; + return set_ok; +} + +static void +ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + case 3: /* ipAdEntNetMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipAdEntIfIndex */ + case 4: /* ipAdEntBcastAddr */ + case 5: /* ipAdEntReasmMaxSize */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + u16_t ifidx; + ip_addr_t ip; + struct netif *netif = netif_list; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], &ip); + ifidx = 0; + while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr)) + { + netif = netif->next; + ifidx++; + } + + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipAdEntAddr */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->ip_addr; + } + break; + case 2: /* ipAdEntIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = ifidx + 1; + } + break; + case 3: /* ipAdEntNetMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + *dst = netif->netmask; + } + break; + case 4: /* ipAdEntBcastAddr */ + { + s32_t *sint_ptr = (s32_t*)value; + + /* lwIP oddity, there's no broadcast + address in the netif we can rely on */ + *sint_ptr = IPADDR_BROADCAST & 1; + } + break; + case 5: /* ipAdEntReasmMaxSize */ + { + s32_t *sint_ptr = (s32_t*)value; +#if IP_REASSEMBLY + /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs, + * but only if receiving one fragmented packet at a time. + * The current solution is to calculate for 2 simultaneous packets... + */ + *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) * + (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN))); +#else + /** @todo returning MTU would be a bad thing and + returning a wild guess like '576' isn't good either */ + *sint_ptr = 0; +#endif + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_value(): unknown id: %d\n", id)); + break; + } + } +} + +/** + * @note + * lwIP IP routing is currently using the network addresses in netif_list. + * if no suitable network IP is found in netif_list, the default_netif is used. + */ +static void +ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (4) */ + ident_len += 4; + ident -= 4; + + if (ident_len == 5) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + case 7: /* ipRouteNextHop */ + case 11: /* ipRouteMask */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* ipRouteIfIndex */ + case 3: /* ipRouteMetric1 */ + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 8: /* ipRouteType */ + case 10: /* ipRouteAge */ + case 12: /* ipRouteMetric5 */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 9: /* ipRouteProto */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 13: /* ipRouteInfo */ + /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID); + od->v_len = iprouteinfo.len * sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + struct netif *netif; + ip_addr_t dest; + s32_t *ident; + u8_t id; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &dest); + + if (ip_addr_isany(&dest)) + { + /* ip_route() uses default netif for default route */ + netif = netif_default; + } + else + { + /* not using ip_route(), need exact match! */ + netif = netif_list; + while ((netif != NULL) && + !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) ) + { + netif = netif->next; + } + } + if (netif != NULL) + { + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipRouteDest */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has 0.0.0.0 dest */ + ip_addr_set_zero(dst); + } + else + { + /* netifs have netaddress dest */ + ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask); + } + } + break; + case 2: /* ipRouteIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + + snmp_netiftoifindex(netif, sint_ptr); + } + break; + case 3: /* ipRouteMetric1 */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte has metric 1 */ + *sint_ptr = 1; + } + else + { + /* other rtes have metric 0 */ + *sint_ptr = 0; + } + } + break; + case 4: /* ipRouteMetric2 */ + case 5: /* ipRouteMetric3 */ + case 6: /* ipRouteMetric4 */ + case 12: /* ipRouteMetric5 */ + { + s32_t *sint_ptr = (s32_t*)value; + /* not used */ + *sint_ptr = -1; + } + break; + case 7: /* ipRouteNextHop */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte: gateway */ + *dst = netif->gw; + } + else + { + /* other rtes: netif ip_addr */ + *dst = netif->ip_addr; + } + } + break; + case 8: /* ipRouteType */ + { + s32_t *sint_ptr = (s32_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte is indirect */ + *sint_ptr = 4; + } + else + { + /* other rtes are direct */ + *sint_ptr = 3; + } + } + break; + case 9: /* ipRouteProto */ + { + s32_t *sint_ptr = (s32_t*)value; + /* locally defined routes */ + *sint_ptr = 2; + } + break; + case 10: /* ipRouteAge */ + { + s32_t *sint_ptr = (s32_t*)value; + /** @todo (sysuptime - timestamp last change) / 100 + @see snmp_insert_iprteidx_tree() */ + *sint_ptr = 0; + } + break; + case 11: /* ipRouteMask */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + if (ip_addr_isany(&dest)) + { + /* default rte use 0.0.0.0 mask */ + ip_addr_set_zero(dst); + } + else + { + /* other rtes use netmask */ + *dst = netif->netmask; + } + } + break; + case 13: /* ipRouteInfo */ + MEMCPY(value, iprouteinfo.id, len); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_value(): unknown id: %d\n", id)); + break; + } + } +} + +static void +ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + case 4: /* ipNetToMediaType */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* ipNetToMediaPhysAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR); + od->v_len = 6; /** @todo try to use netif::hwaddr_len */ + break; + case 3: /* ipNetToMediaNetAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value) +{ +#if LWIP_ARP + u8_t id; + struct eth_addr* ethaddr_ret; + ip_addr_t* ipaddr_ret; +#endif /* LWIP_ARP */ + ip_addr_t ip; + struct netif *netif; + + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */ + + snmp_ifindextonetif(od->id_inst_ptr[1], &netif); + snmp_oidtoip(&od->id_inst_ptr[2], &ip); + +#if LWIP_ARP /** @todo implement a netif_find_addr */ + if (etharp_find_addr(netif, &ip, ðaddr_ret, &ipaddr_ret) > -1) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* ipNetToMediaIfIndex */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = od->id_inst_ptr[1]; + } + break; + case 2: /* ipNetToMediaPhysAddress */ + { + struct eth_addr *dst = (struct eth_addr*)value; + + *dst = *ethaddr_ret; + } + break; + case 3: /* ipNetToMediaNetAddress */ + { + ip_addr_t *dst = (ip_addr_t*)value; + + *dst = *ipaddr_ret; + } + break; + case 4: /* ipNetToMediaType */ + { + s32_t *sint_ptr = (s32_t*)value; + /* dynamic (?) */ + *sint_ptr = 3; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_value(): unknown id: %d\n", id)); + break; + } + } +#endif /* LWIP_ARP */ +} + +static void +icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 27)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +icmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* icmpInMsgs */ + *uint_ptr = icmpinmsgs; + break; + case 2: /* icmpInErrors */ + *uint_ptr = icmpinerrors; + break; + case 3: /* icmpInDestUnreachs */ + *uint_ptr = icmpindestunreachs; + break; + case 4: /* icmpInTimeExcds */ + *uint_ptr = icmpintimeexcds; + break; + case 5: /* icmpInParmProbs */ + *uint_ptr = icmpinparmprobs; + break; + case 6: /* icmpInSrcQuenchs */ + *uint_ptr = icmpinsrcquenchs; + break; + case 7: /* icmpInRedirects */ + *uint_ptr = icmpinredirects; + break; + case 8: /* icmpInEchos */ + *uint_ptr = icmpinechos; + break; + case 9: /* icmpInEchoReps */ + *uint_ptr = icmpinechoreps; + break; + case 10: /* icmpInTimestamps */ + *uint_ptr = icmpintimestamps; + break; + case 11: /* icmpInTimestampReps */ + *uint_ptr = icmpintimestampreps; + break; + case 12: /* icmpInAddrMasks */ + *uint_ptr = icmpinaddrmasks; + break; + case 13: /* icmpInAddrMaskReps */ + *uint_ptr = icmpinaddrmaskreps; + break; + case 14: /* icmpOutMsgs */ + *uint_ptr = icmpoutmsgs; + break; + case 15: /* icmpOutErrors */ + *uint_ptr = icmpouterrors; + break; + case 16: /* icmpOutDestUnreachs */ + *uint_ptr = icmpoutdestunreachs; + break; + case 17: /* icmpOutTimeExcds */ + *uint_ptr = icmpouttimeexcds; + break; + case 18: /* icmpOutParmProbs */ + *uint_ptr = icmpoutparmprobs; + break; + case 19: /* icmpOutSrcQuenchs */ + *uint_ptr = icmpoutsrcquenchs; + break; + case 20: /* icmpOutRedirects */ + *uint_ptr = icmpoutredirects; + break; + case 21: /* icmpOutEchos */ + *uint_ptr = icmpoutechos; + break; + case 22: /* icmpOutEchoReps */ + *uint_ptr = icmpoutechoreps; + break; + case 23: /* icmpOutTimestamps */ + *uint_ptr = icmpouttimestamps; + break; + case 24: /* icmpOutTimestampReps */ + *uint_ptr = icmpouttimestampreps; + break; + case 25: /* icmpOutAddrMasks */ + *uint_ptr = icmpoutaddrmasks; + break; + case 26: /* icmpOutAddrMaskReps */ + *uint_ptr = icmpoutaddrmaskreps; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %d\n", id)); + break; + } +} + +#if LWIP_TCP +/** @todo tcp grp */ +static void +tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + u8_t id; + + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpRtoAlgorithm */ + case 2: /* tcpRtoMin */ + case 3: /* tcpRtoMax */ + case 4: /* tcpMaxConn */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 5: /* tcpActiveOpens */ + case 6: /* tcpPassiveOpens */ + case 7: /* tcpAttemptFails */ + case 8: /* tcpEstabResets */ + case 10: /* tcpInSegs */ + case 11: /* tcpOutSegs */ + case 12: /* tcpRetransSegs */ + case 14: /* tcpInErrs */ + case 15: /* tcpOutRsts */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 9: /* tcpCurrEstab */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE); + od->v_len = sizeof(u32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + s32_t *sint_ptr = (s32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* tcpRtoAlgorithm, vanj(4) */ + *sint_ptr = 4; + break; + case 2: /* tcpRtoMin */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 1000; + break; + case 3: /* tcpRtoMax */ + /* @todo not the actual value, a guess, + needs to be calculated */ + *sint_ptr = 60000; + break; + case 4: /* tcpMaxConn */ + *sint_ptr = MEMP_NUM_TCP_PCB; + break; + case 5: /* tcpActiveOpens */ + *uint_ptr = tcpactiveopens; + break; + case 6: /* tcpPassiveOpens */ + *uint_ptr = tcppassiveopens; + break; + case 7: /* tcpAttemptFails */ + *uint_ptr = tcpattemptfails; + break; + case 8: /* tcpEstabResets */ + *uint_ptr = tcpestabresets; + break; + case 9: /* tcpCurrEstab */ + { + u16_t tcpcurrestab = 0; + struct tcp_pcb *pcb = tcp_active_pcbs; + while (pcb != NULL) + { + if ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT)) + { + tcpcurrestab++; + } + pcb = pcb->next; + } + *uint_ptr = tcpcurrestab; + } + break; + case 10: /* tcpInSegs */ + *uint_ptr = tcpinsegs; + break; + case 11: /* tcpOutSegs */ + *uint_ptr = tcpoutsegs; + break; + case 12: /* tcpRetransSegs */ + *uint_ptr = tcpretranssegs; + break; + case 14: /* tcpInErrs */ + *uint_ptr = tcpinerrs; + break; + case 15: /* tcpOutRsts */ + *uint_ptr = tcpoutrsts; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %d\n", id)); + break; + } +} +#ifdef THIS_SEEMS_UNUSED +static void +tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (10) */ + ident_len += 10; + ident -= 10; + + if (ident_len == 11) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + id = ident[0]; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id)); + + switch (id) + { + case 1: /* tcpConnState */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + case 2: /* tcpConnLocalAddress */ + case 4: /* tcpConnRemAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 3: /* tcpConnLocalPort */ + case 5: /* tcpConnRemPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + ip_addr_t lip, rip; + u16_t lport, rport; + s32_t *ident; + + ident = od->id_inst_ptr; + snmp_oidtoip(&ident[1], &lip); + lport = ident[5]; + snmp_oidtoip(&ident[6], &rip); + rport = ident[10]; + + /** @todo find matching PCB */ +} +#endif /* if 0 */ +#endif + +static void +udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if ((ident_len == 2) && + (ident[0] > 0) && (ident[0] < 6)) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpInDatagrams */ + *uint_ptr = udpindatagrams; + break; + case 2: /* udpNoPorts */ + *uint_ptr = udpnoports; + break; + case 3: /* udpInErrors */ + *uint_ptr = udpinerrors; + break; + case 4: /* udpOutDatagrams */ + *uint_ptr = udpoutdatagrams; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %d\n", id)); + break; + } +} + +static void +udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (5) */ + ident_len += 5; + ident -= 5; + + if (ident_len == 6) + { + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + switch (ident[0]) + { + case 1: /* udpLocalAddress */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR); + od->v_len = 4; + break; + case 2: /* udpLocalPort */ + od->instance = MIB_OBJECT_TAB; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + } + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +udpentry_get_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + struct udp_pcb *pcb; + ipX_addr_t ip; + u16_t port; + + LWIP_UNUSED_ARG(len); + snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip); + LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff)); + port = (u16_t)od->id_inst_ptr[5]; + + pcb = udp_pcbs; + while ((pcb != NULL) && + !(ipX_addr_cmp(0, &pcb->local_ip, &ip) && + (pcb->local_port == port))) + { + pcb = pcb->next; + } + + if (pcb != NULL) + { + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* udpLocalAddress */ + { + ipX_addr_t *dst = (ipX_addr_t*)value; + ipX_addr_copy(0, *dst, pcb->local_ip); + } + break; + case 2: /* udpLocalPort */ + { + s32_t *sint_ptr = (s32_t*)value; + *sint_ptr = pcb->local_port; + } + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_value(): unknown id: %d\n", id)); + break; + } + } +} + +static void +snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od) +{ + /* return to object name, adding index depth (1) */ + ident_len += 1; + ident -= 1; + if (ident_len == 2) + { + u8_t id; + + od->id_inst_len = ident_len; + od->id_inst_ptr = ident; + + LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff)); + id = (u8_t)ident[0]; + switch (id) + { + case 1: /* snmpInPkts */ + case 2: /* snmpOutPkts */ + case 3: /* snmpInBadVersions */ + case 4: /* snmpInBadCommunityNames */ + case 5: /* snmpInBadCommunityUses */ + case 6: /* snmpInASNParseErrs */ + case 8: /* snmpInTooBigs */ + case 9: /* snmpInNoSuchNames */ + case 10: /* snmpInBadValues */ + case 11: /* snmpInReadOnlys */ + case 12: /* snmpInGenErrs */ + case 13: /* snmpInTotalReqVars */ + case 14: /* snmpInTotalSetVars */ + case 15: /* snmpInGetRequests */ + case 16: /* snmpInGetNexts */ + case 17: /* snmpInSetRequests */ + case 18: /* snmpInGetResponses */ + case 19: /* snmpInTraps */ + case 20: /* snmpOutTooBigs */ + case 21: /* snmpOutNoSuchNames */ + case 22: /* snmpOutBadValues */ + case 24: /* snmpOutGenErrs */ + case 25: /* snmpOutGetRequests */ + case 26: /* snmpOutGetNexts */ + case 27: /* snmpOutSetRequests */ + case 28: /* snmpOutGetResponses */ + case 29: /* snmpOutTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_ONLY; + od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER); + od->v_len = sizeof(u32_t); + break; + case 30: /* snmpEnableAuthenTraps */ + od->instance = MIB_OBJECT_SCALAR; + od->access = MIB_OBJECT_READ_WRITE; + od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG); + od->v_len = sizeof(s32_t); + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n")); + od->instance = MIB_OBJECT_NONE; + break; + }; + } + else + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n")); + od->instance = MIB_OBJECT_NONE; + } +} + +static void +snmp_get_value(struct obj_def *od, u16_t len, void *value) +{ + u32_t *uint_ptr = (u32_t*)value; + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + switch (id) + { + case 1: /* snmpInPkts */ + *uint_ptr = snmpinpkts; + break; + case 2: /* snmpOutPkts */ + *uint_ptr = snmpoutpkts; + break; + case 3: /* snmpInBadVersions */ + *uint_ptr = snmpinbadversions; + break; + case 4: /* snmpInBadCommunityNames */ + *uint_ptr = snmpinbadcommunitynames; + break; + case 5: /* snmpInBadCommunityUses */ + *uint_ptr = snmpinbadcommunityuses; + break; + case 6: /* snmpInASNParseErrs */ + *uint_ptr = snmpinasnparseerrs; + break; + case 8: /* snmpInTooBigs */ + *uint_ptr = snmpintoobigs; + break; + case 9: /* snmpInNoSuchNames */ + *uint_ptr = snmpinnosuchnames; + break; + case 10: /* snmpInBadValues */ + *uint_ptr = snmpinbadvalues; + break; + case 11: /* snmpInReadOnlys */ + *uint_ptr = snmpinreadonlys; + break; + case 12: /* snmpInGenErrs */ + *uint_ptr = snmpingenerrs; + break; + case 13: /* snmpInTotalReqVars */ + *uint_ptr = snmpintotalreqvars; + break; + case 14: /* snmpInTotalSetVars */ + *uint_ptr = snmpintotalsetvars; + break; + case 15: /* snmpInGetRequests */ + *uint_ptr = snmpingetrequests; + break; + case 16: /* snmpInGetNexts */ + *uint_ptr = snmpingetnexts; + break; + case 17: /* snmpInSetRequests */ + *uint_ptr = snmpinsetrequests; + break; + case 18: /* snmpInGetResponses */ + *uint_ptr = snmpingetresponses; + break; + case 19: /* snmpInTraps */ + *uint_ptr = snmpintraps; + break; + case 20: /* snmpOutTooBigs */ + *uint_ptr = snmpouttoobigs; + break; + case 21: /* snmpOutNoSuchNames */ + *uint_ptr = snmpoutnosuchnames; + break; + case 22: /* snmpOutBadValues */ + *uint_ptr = snmpoutbadvalues; + break; + case 24: /* snmpOutGenErrs */ + *uint_ptr = snmpoutgenerrs; + break; + case 25: /* snmpOutGetRequests */ + *uint_ptr = snmpoutgetrequests; + break; + case 26: /* snmpOutGetNexts */ + *uint_ptr = snmpoutgetnexts; + break; + case 27: /* snmpOutSetRequests */ + *uint_ptr = snmpoutsetrequests; + break; + case 28: /* snmpOutGetResponses */ + *uint_ptr = snmpoutgetresponses; + break; + case 29: /* snmpOutTraps */ + *uint_ptr = snmpouttraps; + break; + case 30: /* snmpEnableAuthenTraps */ + *uint_ptr = *snmpenableauthentraps_ptr; + break; + default: + LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %d\n", id)); + break; + }; +} + +/** + * Test snmp object value before setting. + * + * @param od is the object definition + * @param len return value space (in bytes) + * @param value points to (varbind) space to copy value from. + */ +static u8_t +snmp_set_test(struct obj_def *od, u16_t len, void *value) +{ + u8_t id, set_ok; + + LWIP_UNUSED_ARG(len); + set_ok = 0; + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + s32_t *sint_ptr = (s32_t*)value; + + if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default) + { + /* we should have writable non-volatile mem here */ + if ((*sint_ptr == 1) || (*sint_ptr == 2)) + { + set_ok = 1; + } + } + else + { + /* const or hardwired value */ + if (*sint_ptr == snmpenableauthentraps_default) + { + set_ok = 1; + } + } + } + return set_ok; +} + +static void +snmp_set_value(struct obj_def *od, u16_t len, void *value) +{ + u8_t id; + + LWIP_UNUSED_ARG(len); + LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff)); + id = (u8_t)od->id_inst_ptr[0]; + if (id == 30) + { + /* snmpEnableAuthenTraps */ + /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */ + u8_t *ptr = (u8_t*)value; + *snmpenableauthentraps_ptr = *ptr; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib_structs.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib_structs.c new file mode 100644 index 0000000..fa796c5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/mib_structs.c @@ -0,0 +1,1175 @@ +/** + * @file + * MIB tree access/construction functions. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp_structs.h" +#include "lwip/memp.h" +#include "lwip/netif.h" + +/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */ +const s32_t prefix[4] = {1, 3, 6, 1}; + +#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN) +/** node stack entry (old news?) */ +struct nse +{ + /** right child */ + struct mib_node* r_ptr; + /** right child identifier */ + s32_t r_id; + /** right child next level */ + u8_t r_nl; +}; +static u8_t node_stack_cnt; +static struct nse node_stack[NODE_STACK_SIZE]; +const struct nse node_null = {NULL, 0, 0}; + +/** + * Pushes nse struct onto stack. + */ +static void +push_node(const struct nse* node) +{ + LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id)); + if (node_stack_cnt < NODE_STACK_SIZE) + { + node_stack[node_stack_cnt] = *node; + node_stack_cnt++; + } +} + +/** + * Pops nse struct from stack. + */ +static void +pop_node(struct nse* node) +{ + if (node_stack_cnt > 0) + { + node_stack_cnt--; + *node = node_stack[node_stack_cnt]; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id)); +} + +/** + * Conversion from ifIndex to lwIP netif + * @param ifindex is a s32_t object sub-identifier + * @param netif points to returned netif struct pointer + */ +void +snmp_ifindextonetif(s32_t ifindex, struct netif **netif) +{ + struct netif *nif = netif_list; + s32_t i, ifidx; + + ifidx = ifindex - 1; + i = 0; + while ((nif != NULL) && (i < ifidx)) + { + nif = nif->next; + i++; + } + *netif = nif; +} + +/** + * Conversion from lwIP netif to ifIndex + * @param netif points to a netif struct + * @param ifidx points to s32_t object sub-identifier + */ +void +snmp_netiftoifindex(struct netif *netif, s32_t *ifidx) +{ + struct netif *nif = netif_list; + u16_t i; + + i = 0; + while ((nif != NULL) && (nif != netif)) + { + nif = nif->next; + i++; + } + *ifidx = i+1; +} + +/** + * Conversion from oid to lwIP ip_addr + * @param ident points to s32_t ident[4] input + * @param ip points to output struct + */ +void +snmp_oidtoip(s32_t *ident, ip_addr_t *ip) +{ + IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]); +} + +/** + * Conversion from lwIP ip_addr to oid + * @param ip points to input struct + * @param ident points to s32_t ident[4] output + */ +void +snmp_iptooid(ip_addr_t *ip, s32_t *ident) +{ + ident[0] = ip4_addr1(ip); + ident[1] = ip4_addr2(ip); + ident[2] = ip4_addr3(ip); + ident[3] = ip4_addr4(ip); +} + +struct mib_list_node * +snmp_mib_ln_alloc(s32_t id) +{ + struct mib_list_node *ln; + + ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE); + if (ln != NULL) + { + ln->prev = NULL; + ln->next = NULL; + ln->objid = id; + ln->nptr = NULL; + } + return ln; +} + +void +snmp_mib_ln_free(struct mib_list_node *ln) +{ + memp_free(MEMP_SNMP_NODE, ln); +} + +struct mib_list_rootnode * +snmp_mib_lrn_alloc(void) +{ + struct mib_list_rootnode *lrn; + + lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE); + if (lrn != NULL) + { + lrn->get_object_def = noleafs_get_object_def; + lrn->get_value = noleafs_get_value; + lrn->set_test = noleafs_set_test; + lrn->set_value = noleafs_set_value; + lrn->node_type = MIB_NODE_LR; + lrn->maxlength = 0; + lrn->head = NULL; + lrn->tail = NULL; + lrn->count = 0; + } + return lrn; +} + +void +snmp_mib_lrn_free(struct mib_list_rootnode *lrn) +{ + memp_free(MEMP_SNMP_ROOTNODE, lrn); +} + +/** + * Inserts node in idx list in a sorted + * (ascending order) fashion and + * allocates the node if needed. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param insn points to a pointer to the inserted node + * used for constructing the tree. + * @return -1 if failed, 1 if inserted, 2 if present. + */ +s8_t +snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn) +{ + struct mib_list_node *nn; + s8_t insert; + + LWIP_ASSERT("rn != NULL",rn != NULL); + + /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */ + insert = 0; + if (rn->head == NULL) + { + /* empty list, add first node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + rn->head = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + insert = -1; + } + } + else + { + struct mib_list_node *n; + /* at least one node is present */ + n = rn->head; + while ((n != NULL) && (insert == 0)) + { + if (n->objid == objid) + { + /* node is already there */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid)); + *insn = n; + insert = 2; + } + else if (n->objid < objid) + { + if (n->next == NULL) + { + /* alloc and insert at the tail */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + nn->next = NULL; + nn->prev = n; + n->next = nn; + rn->tail = nn; + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + else + { + /* there's more to explore: traverse list */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n")); + n = n->next; + } + } + else + { + /* n->objid > objid */ + /* alloc and insert between n->prev and n */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid)); + nn = snmp_mib_ln_alloc(objid); + if (nn != NULL) + { + if (n->prev == NULL) + { + /* insert at the head */ + nn->next = n; + nn->prev = NULL; + rn->head = nn; + n->prev = nn; + } + else + { + /* insert in the middle */ + nn->next = n; + nn->prev = n->prev; + n->prev->next = nn; + n->prev = nn; + } + *insn = nn; + insert = 1; + } + else + { + /* insertion failure */ + insert = -1; + } + } + } + } + if (insert == 1) + { + rn->count += 1; + } + LWIP_ASSERT("insert != 0",insert != 0); + return insert; +} + +/** + * Finds node in idx list and returns deletion mark. + * + * @param rn points to the root node + * @param objid is the object sub identifier + * @param fn returns pointer to found node + * @return 0 if not found, 1 if deletable, + * 2 can't delete (2 or more children), 3 not a list_node + */ +s8_t +snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn) +{ + s8_t fc; + struct mib_list_node *n; + + LWIP_ASSERT("rn != NULL",rn != NULL); + n = rn->head; + while ((n != NULL) && (n->objid != objid)) + { + n = n->next; + } + if (n == NULL) + { + fc = 0; + } + else if (n->nptr == NULL) + { + /* leaf, can delete node */ + fc = 1; + } + else + { + struct mib_list_rootnode *r; + + if (n->nptr->node_type == MIB_NODE_LR) + { + r = (struct mib_list_rootnode *)n->nptr; + if (r->count > 1) + { + /* can't delete node */ + fc = 2; + } + else + { + /* count <= 1, can delete node */ + fc = 1; + } + } + else + { + /* other node type */ + fc = 3; + } + } + *fn = n; + return fc; +} + +/** + * Removes node from idx list + * if it has a single child left. + * + * @param rn points to the root node + * @param n points to the node to delete + * @return the nptr to be freed by caller + */ +struct mib_list_rootnode * +snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n) +{ + struct mib_list_rootnode *next; + + LWIP_ASSERT("rn != NULL",rn != NULL); + LWIP_ASSERT("n != NULL",n != NULL); + + /* caller must remove this sub-tree */ + next = (struct mib_list_rootnode*)(n->nptr); + rn->count -= 1; + + if (n == rn->head) + { + rn->head = n->next; + if (n->next != NULL) + { + /* not last node, new list begin */ + n->next->prev = NULL; + } + } + else if (n == rn->tail) + { + rn->tail = n->prev; + if (n->prev != NULL) + { + /* not last node, new list end */ + n->prev->next = NULL; + } + } + else + { + /* node must be in the middle */ + n->prev->next = n->next; + n->next->prev = n->prev; + } + LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid)); + snmp_mib_ln_free(n); + if (rn->count == 0) + { + rn->head = NULL; + rn->tail = NULL; + } + return next; +} + + + +/** + * Searches tree for the supplied (scalar?) object identifier. + * + * @param node points to the root of the tree ('.internet') + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param np points to the found object instance (return) + * @return pointer to the requested parent (!) node if success, NULL otherwise + */ +struct mib_node * +snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np) +{ + u8_t node_type, ext_level; + + ext_level = 0; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident)); + while (node != NULL) + { + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + if (ident_len > 0) + { + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + i = 0; + while ((i < an->maxlength) && (an->objid[i] != *ident)) + { + i++; + } + if (i < an->maxlength) + { + /* found it, if available proceed to child, otherwise inspect leaf */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + if (an->nptr[i] == NULL) + { + /* a scalar leaf OR table, + inspect remaining instance number / table index */ + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)an; + } + else + { + /* follow next child pointer */ + ident++; + ident_len--; + node = an->nptr[i]; + } + } + else + { + /* search failed, identifier mismatch (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + if (ident_len > 0) + { + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid != *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + /* found it, proceed to child */; + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + if (ln->nptr == NULL) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)lrn; + } + else + { + /* follow next child pointer */ + ident_len--; + ident++; + node = ln->nptr; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n")); + return NULL; + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + u16_t i, len; + + if (ident_len > 0) + { + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0)) + { + i++; + } + if (i < len) + { + s32_t debug_id; + + en->get_objid(en->addr_inf,ext_level,i,&debug_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident)); + if ((ext_level + 1) == en->tree_levels) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)en; + } + else + { + /* found it, proceed to child */ + ident_len--; + ident++; + ext_level++; + } + } + else + { + /* search failed */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident)); + return NULL; + } + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n")); + return NULL; + } + } + else if (node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + sn = (mib_scalar_node *)node; + if ((ident_len == 1) && (*ident == 0)) + { + np->ident_len = ident_len; + np->ident = ident; + return (struct mib_node*)sn; + } + else + { + /* search failed, short object identifier (nosuchname) */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n")); + return NULL; + } + } + else + { + /* unknown node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test table for presence of at least one table entry. + */ +static u8_t +empty_table(struct mib_node *node) +{ + u8_t node_type; + u8_t empty = 0; + + if (node != NULL) + { + node_type = node->node_type; + if (node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + lrn = (struct mib_list_rootnode *)node; + if ((lrn->count == 0) || (lrn->head == NULL)) + { + empty = 1; + } + } + else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + an = (struct mib_array_node *)node; + if ((an->maxlength == 0) || (an->nptr == NULL)) + { + empty = 1; + } + } + else if (node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + en = (struct mib_external_node *)node; + if (en->tree_levels == 0) + { + empty = 1; + } + } + } + return empty; +} + +/** + * Tree expansion. + */ +struct mib_node * +snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + u8_t node_type, ext_level, climb_tree; + + ext_level = 0; + /* reset node stack */ + node_stack_cnt = 0; + while (node != NULL) + { + climb_tree = 0; + node_type = node->node_type; + if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA)) + { + struct mib_array_node *an; + u16_t i; + + /* array node (internal ROM or RAM, fixed length) */ + an = (struct mib_array_node *)node; + if (ident_len > 0) + { + i = 0; + while ((i < an->maxlength) && (an->objid[i] < *ident)) + { + i++; + } + if (i < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident)); + /* add identifier to oidret */ + oidret->id[oidret->len] = an->objid[i]; + (oidret->len)++; + + if (an->nptr[i] == NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node (e.g. in a fixed size table) */ + if (an->objid[i] > *ident) + { + return (struct mib_node*)an; + } + else if ((i + 1) < an->maxlength) + { + /* an->objid[i] == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = an->objid[i + 1]; + (oidret->len)++; + return (struct mib_node*)an; + } + else + { + /* (i + 1) == an->maxlength */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u16_t j; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = i + 1; + while ((j < an->maxlength) && (empty_table(an->nptr[j]))) + { + j++; + } + if (j < an->maxlength) + { + struct nse cur_node; + cur_node.r_ptr = an->nptr[j]; + cur_node.r_id = an->objid[j]; + cur_node.r_nl = 0; + push_node(&cur_node); + } + else + { + push_node(&node_null); + } + if (an->objid[i] == *ident) + { + ident_len--; + ident++; + } + else + { + /* an->objid[i] < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = an->nptr[i]; + } + } + else + { + /* i == an->maxlength */ + climb_tree = 1; + } + } + else + { + u16_t j; + /* ident_len == 0, complete with leftmost '.thing' */ + j = 0; + while ((j < an->maxlength) && empty_table(an->nptr[j])) + { + j++; + } + if (j < an->maxlength) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j])); + oidret->id[oidret->len] = an->objid[j]; + (oidret->len)++; + if (an->nptr[j] == NULL) + { + /* leaf node */ + return (struct mib_node*)an; + } + else + { + /* no leaf, continue */ + node = an->nptr[j]; + } + } + else + { + /* j == an->maxlength */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_LR) + { + struct mib_list_rootnode *lrn; + struct mib_list_node *ln; + + /* list root node (internal 'RAM', variable length) */ + lrn = (struct mib_list_rootnode *)node; + if (ident_len > 0) + { + ln = lrn->head; + /* iterate over list, head to tail */ + while ((ln != NULL) && (ln->objid < *ident)) + { + ln = ln->next; + } + if (ln != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident)); + oidret->id[oidret->len] = ln->objid; + (oidret->len)++; + if (ln->nptr == NULL) + { + /* leaf node */ + if (ln->objid > *ident) + { + return (struct mib_node*)lrn; + } + else if (ln->next != NULL) + { + /* ln->objid == *ident */ + (oidret->len)--; + oidret->id[oidret->len] = ln->next->objid; + (oidret->len)++; + return (struct mib_node*)lrn; + } + else + { + /* ln->next == NULL */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + + /* non-leaf, store right child ptr and id */ + jn = ln->next; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + struct nse cur_node; + cur_node.r_ptr = jn->nptr; + cur_node.r_id = jn->objid; + cur_node.r_nl = 0; + push_node(&cur_node); + } + else + { + push_node(&node_null); + } + if (ln->objid == *ident) + { + ident_len--; + ident++; + } + else + { + /* ln->objid < *ident */ + ident_len = 0; + } + /* follow next child pointer */ + node = ln->nptr; + } + + } + else + { + /* ln == NULL */ + climb_tree = 1; + } + } + else + { + struct mib_list_node *jn; + /* ident_len == 0, complete with leftmost '.thing' */ + jn = lrn->head; + while ((jn != NULL) && empty_table(jn->nptr)) + { + jn = jn->next; + } + if (jn != NULL) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid)); + oidret->id[oidret->len] = jn->objid; + (oidret->len)++; + if (jn->nptr == NULL) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n")); + return (struct mib_node*)lrn; + } + else + { + /* no leaf, continue */ + node = jn->nptr; + } + } + else + { + /* jn == NULL */ + climb_tree = 1; + } + } + } + else if(node_type == MIB_NODE_EX) + { + struct mib_external_node *en; + s32_t ex_id; + + /* external node (addressing and access via functions) */ + en = (struct mib_external_node *)node; + if (ident_len > 0) + { + u16_t i, len; + + i = 0; + len = en->level_length(en->addr_inf,ext_level); + while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0)) + { + i++; + } + if (i < len) + { + /* add identifier to oidret */ + en->get_objid(en->addr_inf,ext_level,i,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + + if ((ext_level + 1) == en->tree_levels) + { + LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n")); + /* leaf node */ + if (ex_id > *ident) + { + return (struct mib_node*)en; + } + else if ((i + 1) < len) + { + /* ex_id == *ident */ + en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id); + (oidret->len)--; + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + return (struct mib_node*)en; + } + else + { + /* (i + 1) == len */ + (oidret->len)--; + climb_tree = 1; + } + } + else + { + u16_t j; + + LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n")); + /* non-leaf, store right child ptr and id */ + LWIP_ASSERT("i < 0xff", i < 0xff); + j = i + 1; + if (j < len) + { + struct nse cur_node; + /* right node is the current external node */ + cur_node.r_ptr = node; + en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id); + cur_node.r_nl = ext_level + 1; + push_node(&cur_node); + } + else + { + push_node(&node_null); + } + if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0) + { + ident_len--; + ident++; + } + else + { + /* external id < *ident */ + ident_len = 0; + } + /* proceed to child */ + ext_level++; + } + } + else + { + /* i == len (en->level_len()) */ + climb_tree = 1; + } + } + else + { + /* ident_len == 0, complete with leftmost '.thing' */ + en->get_objid(en->addr_inf,ext_level,0,&ex_id); + LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id)); + oidret->id[oidret->len] = ex_id; + (oidret->len)++; + if ((ext_level + 1) == en->tree_levels) + { + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n")); + return (struct mib_node*)en; + } + else + { + /* no leaf, proceed to child */ + ext_level++; + } + } + } + else if(node_type == MIB_NODE_SC) + { + mib_scalar_node *sn; + + /* scalar node */ + sn = (mib_scalar_node *)node; + if (ident_len > 0) + { + /* at .0 */ + climb_tree = 1; + } + else + { + /* ident_len == 0, complete object identifier */ + oidret->id[oidret->len] = 0; + (oidret->len)++; + /* leaf node */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n")); + return (struct mib_node*)sn; + } + } + else + { + /* unknown/unhandled node_type */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type)); + return NULL; + } + + if (climb_tree) + { + struct nse child; + + /* find right child ptr */ + child.r_ptr = NULL; + child.r_id = 0; + child.r_nl = 0; + while ((node_stack_cnt > 0) && (child.r_ptr == NULL)) + { + pop_node(&child); + /* trim returned oid */ + (oidret->len)--; + } + if (child.r_ptr != NULL) + { + /* incoming ident is useless beyond this point */ + ident_len = 0; + oidret->id[oidret->len] = child.r_id; + oidret->len++; + node = child.r_ptr; + ext_level = child.r_nl; + } + else + { + /* tree ends here ... */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n")); + return NULL; + } + } + } + /* done, found nothing */ + LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node)); + return NULL; +} + +/** + * Test object identifier for the iso.org.dod.internet prefix. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @return 1 if it matches, 0 otherwise + */ +u8_t +snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident) +{ + if ((ident_len > 3) && + (ident[0] == 1) && (ident[1] == 3) && + (ident[2] == 6) && (ident[3] == 1)) + { + return 1; + } + else + { + return 0; + } +} + +/** + * Expands object identifier to the iso.org.dod.internet + * prefix for use in getnext operation. + * + * @param ident_len the length of the supplied object identifier + * @param ident points to the array of sub identifiers + * @param oidret points to returned expanded object identifier + * @return 1 if it matches, 0 otherwise + * + * @note ident_len 0 is allowed, expanding to the first known object id!! + */ +u8_t +snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret) +{ + const s32_t *prefix_ptr; + s32_t *ret_ptr; + u8_t i; + + i = 0; + prefix_ptr = &prefix[0]; + ret_ptr = &oidret->id[0]; + ident_len = ((ident_len < 4)?ident_len:4); + while ((i < ident_len) && ((*ident) <= (*prefix_ptr))) + { + *ret_ptr++ = *prefix_ptr++; + ident++; + i++; + } + if (i == ident_len) + { + /* match, complete missing bits */ + while (i < 4) + { + *ret_ptr++ = *prefix_ptr++; + i++; + } + oidret->len = i; + return 1; + } + else + { + /* i != ident_len */ + return 0; + } +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_in.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_in.c new file mode 100644 index 0000000..da9b78c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_in.c @@ -0,0 +1,1463 @@ +/** + * @file + * SNMP input message processing (RFC1157). + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" +#include "lwip/snmp_structs.h" +#include "lwip/ip_addr.h" +#include "lwip/memp.h" +#include "lwip/udp.h" +#include "lwip/stats.h" + +#include + +/* public (non-static) constants */ +/** SNMP v1 == 0 */ +const s32_t snmp_version = 0; +/** default SNMP community string */ +const char snmp_publiccommunity[7] = "public"; + +/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */ +struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS]; +/* UDP Protocol Control Block */ +struct udp_pcb *snmp1_pcb; + +static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port); +static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); +static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat); + + +/** + * Starts SNMP Agent. + * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161. + */ +void +snmp_init(void) +{ + struct snmp_msg_pstat *msg_ps; + u8_t i; + + snmp1_pcb = udp_new(); + if (snmp1_pcb != NULL) + { + udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT); + udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT); + } + msg_ps = &msg_input_list[0]; + for (i=0; istate = SNMP_MSG_EMPTY; + msg_ps->error_index = 0; + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps++; + } + trap_msg.pcb = snmp1_pcb; + +#ifdef SNMP_PRIVATE_MIB_INIT + /* If defined, this must be a function-like define to initialize the + * private MIB after the stack has been initialized. + * The private MIB can also be initialized in tcpip_callback (or after + * the stack is initialized), this define is only for convenience. */ + SNMP_PRIVATE_MIB_INIT(); +#endif /* SNMP_PRIVATE_MIB_INIT */ + + /* The coldstart trap will only be output + if our outgoing interface is up & configured */ + snmp_coldstart_trap(); +} + +static void +snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error) +{ + /* move names back from outvb to invb */ + int v; + struct snmp_varbind *vbi = msg_ps->invb.head; + struct snmp_varbind *vbo = msg_ps->outvb.head; + for (v=0; vvb_idx; v++) { + if (vbi->ident != NULL) { + /* free previously allocated value before overwriting the pointer */ + memp_free(MEMP_SNMP_VALUE, vbi->ident); + } + vbi->ident_len = vbo->ident_len; + vbo->ident_len = 0; + vbi->ident = vbo->ident; + vbo->ident = NULL; + vbi = vbi->next; + vbo = vbo->next; + } + /* free outvb */ + snmp_varbind_list_free(&msg_ps->outvb); + /* we send invb back */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + msg_ps->error_status = error; + /* error index must be 0 for error too big */ + msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0; + snmp_send_response(msg_ps); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +static void +snmp_ok_response(struct snmp_msg_pstat *msg_ps) +{ + err_t err_ret; + + err_ret = snmp_send_response(msg_ps); + if (err_ret == ERR_MEM) + { + /* serious memory problem, can't return tooBig */ + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status)); + } + /* free varbinds (if available) */ + snmp_varbind_list_free(&msg_ps->invb); + snmp_varbind_list_free(&msg_ps->outvb); + msg_ps->state = SNMP_MSG_EMPTY; +} + +/** + * Service an internal or external event for SNMP GET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the associated message process state + */ +static void +snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) && + (msg_ps->ext_object_def.access & MIB_ACCESS_READ)) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is referenced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = msg_ps->ext_object_def.asn_type; + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb->value_len = (u8_t)msg_ps->ext_object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL); + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + /* search again (if vb_idx < msg_ps->invb.count) */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if ((object_def.instance != MIB_OBJECT_NONE) && + (object_def.access & MIB_ACCESS_READ)) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + /* allocate output varbind */ + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + vb->next = NULL; + vb->prev = NULL; + + /* move name from invb to outvb */ + vb->ident = msg_ps->vb_ptr->ident; + vb->ident_len = msg_ps->vb_ptr->ident_len; + /* ensure this memory is referenced once only */ + msg_ps->vb_ptr->ident = NULL; + msg_ps->vb_ptr->ident_len = 0; + + vb->value_type = object_def.asn_type; + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb->value_len = (u8_t)object_def.v_len; + if (vb->value_len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", + vb->value_len <= SNMP_MAX_VALUE_SIZE); + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value != NULL) + { + mn->get_value(&object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n")); + msg_ps->vb_ptr->ident = vb->ident; + msg_ps->vb_ptr->ident_len = vb->ident_len; + vb->ident = NULL; + vb->ident_len = 0; + memp_free(MEMP_SNMP_VARBIND, vb); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + else + { + /* vb->value_len == 0, empty value (e.g. empty string) */ + vb->value = NULL; + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP GETNEXT. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the associated message process state + */ +static void +snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE; + en->get_value_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE) + { + struct mib_external_node *en; + struct snmp_varbind *vb; + + /* get_value() answer */ + en = msg_ps->ext_mib_node; + + LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&msg_ps->ext_oid, + msg_ps->ext_object_def.asn_type, + (u8_t)msg_ps->ext_object_def.v_len); + if (vb != NULL) + { + en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->get_value_pc(request_id, &msg_ps->ext_object_def); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_obj_id oid; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid)) + { + if (msg_ps->vb_ptr->ident_len > 3) + { + /* can offset ident_len and ident */ + mn = snmp_expand_tree((struct mib_node*)&internet, + msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &oid); + } + else + { + /* can't offset ident_len -4, ident + 4 */ + mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid); + } + } + else + { + mn = NULL; + } + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_oid = oid; + + en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]); + } + else + { + /* internal object */ + struct obj_def object_def; + struct snmp_varbind *vb; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(1, &oid.id[oid.len - 1], &object_def); + + LWIP_ASSERT("invalid length", object_def.v_len <= 0xff); + vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len); + if (vb != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE; + mn->get_value(&object_def, object_def.v_len, vb->value); + snmp_varbind_tail_add(&msg_ps->outvb, vb); + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n")); + snmp_error_response(msg_ps,SNMP_ES_TOOBIG); + } + } + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + snmp_ok_response(msg_ps); + } +} + +/** + * Service an internal or external event for SNMP SET. + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + * @param msg_ps points to the associated message process state + */ +static void +snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps) +{ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state)); + + if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST; + en->set_test_q(request_id, &msg_ps->ext_object_def); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* search failed, object id points to unknown object (nosuchname) */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST) + { + struct mib_external_node *en; + + /* set_test() answer*/ + en = msg_ps->ext_mib_node; + + if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE) + { + if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) && + (en->set_test_a(request_id,&msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + en->set_test_pc(request_id,&msg_ps->ext_object_def); + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S) + { + struct mib_external_node *en; + struct snmp_name_ptr np; + + /* get_object_def() answer*/ + en = msg_ps->ext_mib_node; + np = msg_ps->ext_name_ptr; + + /* translate answer into a known lifeform */ + en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def); + if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) + { + msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE; + en->set_value_q(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + } + else + { + en->get_object_def_pc(request_id, np.ident_len, np.ident); + /* set_value failed, object has disappeared for some odd reason?? */ + snmp_error_response(msg_ps,SNMP_ES_GENERROR); + } + } + else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE) + { + struct mib_external_node *en; + + /** set_value_a() */ + en = msg_ps->ext_mib_node; + en->set_value_a(request_id, &msg_ps->ext_object_def, + msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value); + + /** @todo use set_value_pc() if toobig */ + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + msg_ps->vb_idx += 1; + } + + /* test all values before setting */ + while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /** test object identifier for .iso.org.dod.internet prefix */ + if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident)) + { + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF; + mn->get_object_def(np.ident_len, np.ident, &object_def); + if (object_def.instance != MIB_OBJECT_NONE) + { + mn = mn; + } + else + { + /* search failed, object id points to unknown object (nosuchname) */ + mn = NULL; + } + if (mn != NULL) + { + msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST; + + if (object_def.access & MIB_ACCESS_WRITE) + { + if ((object_def.asn_type == msg_ps->vb_ptr->value_type) && + (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0)) + { + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + msg_ps->vb_idx += 1; + } + else + { + /* bad value */ + snmp_error_response(msg_ps,SNMP_ES_BADVALUE); + } + } + else + { + /* object not available for set */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + } + } + } + else + { + mn = NULL; + } + if (mn == NULL) + { + /* mn == NULL, noSuchName */ + snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME); + } + } + + if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + msg_ps->vb_idx = 0; + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + } + + /* set all values "atomically" (be as "atomic" as possible) */ + while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx < msg_ps->invb.count)) + { + struct mib_node *mn; + struct snmp_name_ptr np; + + if (msg_ps->vb_idx == 0) + { + msg_ps->vb_ptr = msg_ps->invb.head; + } + else + { + msg_ps->vb_ptr = msg_ps->vb_ptr->next; + } + /* skip iso prefix test, was done previously while settesting() */ + mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4, + msg_ps->vb_ptr->ident + 4, &np); + /* check if object is still available + (e.g. external hot-plug thingy present?) */ + if (mn != NULL) + { + if (mn->node_type == MIB_NODE_EX) + { + /* external object */ + struct mib_external_node *en = (struct mib_external_node*)mn; + + msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S; + /* save en && args in msg_ps!! */ + msg_ps->ext_mib_node = en; + msg_ps->ext_name_ptr = np; + + en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident); + } + else + { + /* internal object */ + struct obj_def object_def; + + msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S; + mn->get_object_def(np.ident_len, np.ident, &object_def); + msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE; + mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value); + msg_ps->vb_idx += 1; + } + } + } + if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) && + (msg_ps->vb_idx == msg_ps->invb.count)) + { + /* simply echo the input if we can set it + @todo do we need to return the actual value? + e.g. if value is silently modified or behaves sticky? */ + msg_ps->outvb = msg_ps->invb; + msg_ps->invb.head = NULL; + msg_ps->invb.tail = NULL; + msg_ps->invb.count = 0; + snmp_ok_response(msg_ps); + } +} + + +/** + * Handle one internal or external event. + * Called for one async event. (recv external/private answer) + * + * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1) + */ +void +snmp_msg_event(u8_t request_id) +{ + struct snmp_msg_pstat *msg_ps; + + if (request_id < SNMP_CONCURRENT_REQUESTS) + { + msg_ps = &msg_input_list[request_id]; + if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) + { + snmp_msg_getnext_event(request_id, msg_ps); + } + else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) + { + snmp_msg_get_event(request_id, msg_ps); + } + else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_msg_set_event(request_id, msg_ps); + } + } +} + + +/* lwIP UDP receive callback function */ +static void +snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port) +{ + struct snmp_msg_pstat *msg_ps; + u8_t req_idx; + err_t err_ret; + u16_t payload_len = p->tot_len; + u16_t payload_ofs = 0; + u16_t varbind_ofs = 0; + + /* suppress unused argument warning */ + LWIP_UNUSED_ARG(arg); + + /* traverse input message process list, look for SNMP_MSG_EMPTY */ + msg_ps = &msg_input_list[0]; + req_idx = 0; + while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY)) + { + req_idx++; + msg_ps++; + } + if (req_idx == SNMP_CONCURRENT_REQUESTS) + { + /* exceeding number of concurrent requests */ + pbuf_free(p); + return; + } + + /* accepting request */ + snmp_inc_snmpinpkts(); + /* record used 'protocol control block' */ + msg_ps->pcb = pcb; + /* source address (network order) */ + msg_ps->sip = *addr; + /* source port (host order (lwIP oddity)) */ + msg_ps->sp = port; + + /* check total length, version, community, pdu type */ + err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps); + /* Only accept requests and requests without error (be robust) */ + /* Reject response and trap headers or error requests as input! */ + if ((err_ret != ERR_OK) || + ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) && + (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) || + ((msg_ps->error_status != SNMP_ES_NOERROR) || + (msg_ps->error_index != 0)) ) + { + /* header check failed drop request silently, do not return error! */ + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n")); + return; + } + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community)); + + /* Builds a list of variable bindings. Copy the varbinds from the pbuf + chain to glue them when these are divided over two or more pbuf's. */ + err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps); + /* we've decoded the incoming message, release input msg now */ + pbuf_free(p); + if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0)) + { + /* varbind-list decode failed, or varbind list empty. + drop request silently, do not return error! + (errors are only returned for a specific varbind failure) */ + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n")); + return; + } + + msg_ps->error_status = SNMP_ES_NOERROR; + msg_ps->error_index = 0; + /* find object for each variable binding */ + msg_ps->state = SNMP_MSG_SEARCH_OBJ; + /* first variable binding from list to inspect */ + msg_ps->vb_idx = 0; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count)); + + /* handle input event and as much objects as possible in one go */ + snmp_msg_event(req_idx); +} + +/** + * Checks and decodes incoming SNMP message header, logs header errors. + * + * @param p points to pbuf chain of SNMP message (UDP payload) + * @param ofs points to first octet of SNMP message + * @param pdu_len the length of the UDP payload + * @param ofs_ret returns the ofset of the variable bindings + * @param m_stat points to the current message request state return + * @return + * - ERR_OK SNMP header is sane and accepted + * - ERR_ARG SNMP header is either malformed or rejected + */ +static err_t +snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, ofs_base; + u8_t len_octets; + u8_t type; + s32_t version; + + ofs_base = ofs; + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (pdu_len != (1 + len_octets + len)) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (version) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + if (version != 0) + { + /* not version 1 */ + snmp_inc_snmpinbadversions(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR))) + { + /* can't decode or no octet string (community) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* add zero terminator */ + len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN)); + m_stat->community[len] = 0; + m_stat->com_strlen = (u8_t)len; + if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0) + { + /** @todo: move this if we need to check more names */ + snmp_inc_snmpinbadcommunitynames(); + snmp_authfail_trap(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch(type) + { + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ): + /* GetRequest PDU */ + snmp_inc_snmpingetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ): + /* GetNextRequest PDU */ + snmp_inc_snmpingetnexts(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP): + /* GetResponse PDU */ + snmp_inc_snmpingetresponses(); + derr = ERR_ARG; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ): + /* SetRequest PDU */ + snmp_inc_snmpinsetrequests(); + derr = ERR_OK; + break; + case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP): + /* Trap PDU */ + snmp_inc_snmpintraps(); + derr = ERR_ARG; + break; + default: + snmp_inc_snmpinasnparseerrs(); + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + /* unsupported input PDU for this agent (no parse error) */ + return ERR_ARG; + } + m_stat->rt = type & 0x1F; + ofs += (1 + len_octets); + if (len != (pdu_len - (ofs - ofs_base))) + { + /* decoded PDU length does not equal actual payload length */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (request ID) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-status) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be noError (0) for incoming requests. + log errors for mib-2 completeness and for debug purposes */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + switch (m_stat->error_status) + { + case SNMP_ES_NOERROR: + /* nothing to do */ + break; + case SNMP_ES_TOOBIG: + snmp_inc_snmpintoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpinnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpinbadvalues(); + break; + case SNMP_ES_READONLY: + snmp_inc_snmpinreadonlys(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpingenerrs(); + break; + default: + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check(): unknown error_status: %d\n", (int)m_stat->error_status)); + break; + } + ofs += (1 + len_octets + len); + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG))) + { + /* can't decode or no integer (error-index) */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + /* must be 0 for incoming requests. + decode anyway to catch bad integers (and dirty tricks) */ + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index); + if (derr != ERR_OK) + { + /* can't decode */ + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + *ofs_ret = ofs; + return ERR_OK; +} + +static err_t +snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat) +{ + err_t derr; + u16_t len, vb_len; + u8_t len_octets; + u8_t type; + + /* variable binding list */ + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ))) + { + snmp_inc_snmpinasnparseerrs(); + return ERR_ARG; + } + ofs += (1 + len_octets); + + /* start with empty list */ + m_stat->invb.count = 0; + m_stat->invb.head = NULL; + m_stat->invb.tail = NULL; + + while (vb_len > 0) + { + struct snmp_obj_id oid, oid_value; + struct snmp_varbind *vb; + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || + (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) || + (len == 0) || (len > vb_len)) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets); + vb_len -= (1 + len_octets); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID))) + { + /* can't decode object name length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid); + if (derr != ERR_OK) + { + /* can't decode object name */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + + snmp_asn1_dec_type(p, ofs, &type); + derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len); + if (derr != ERR_OK) + { + /* can't decode object value length */ + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + + switch (type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t)); + if (vb != NULL) + { + s32_t *vptr = (s32_t*)vb->value; + + derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t)); + if (vb != NULL) + { + u32_t *vptr = (u32_t*)vb->value; + + derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + LWIP_ASSERT("invalid length", len <= 0xff); + vb = snmp_varbind_alloc(&oid, type, (u8_t)len); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + vb = snmp_varbind_alloc(&oid, type, 0); + if (vb != NULL) + { + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value); + if (derr == ERR_OK) + { + vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t)); + if (vb != NULL) + { + u8_t i = oid_value.len; + s32_t *vptr = (s32_t*)vb->value; + + while(i > 0) + { + i--; + vptr[i] = oid_value.id[i]; + } + snmp_varbind_tail_add(&m_stat->invb, vb); + derr = ERR_OK; + } + else + { + derr = ERR_ARG; + } + } + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + if (len == 4) + { + /* must be exactly 4 octets! */ + vb = snmp_varbind_alloc(&oid, type, 4); + if (vb != NULL) + { + derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value); + snmp_varbind_tail_add(&m_stat->invb, vb); + } + else + { + derr = ERR_ARG; + } + } + else + { + derr = ERR_ARG; + } + break; + default: + derr = ERR_ARG; + break; + } + if (derr != ERR_OK) + { + snmp_inc_snmpinasnparseerrs(); + /* free varbinds (if available) */ + snmp_varbind_list_free(&m_stat->invb); + return ERR_ARG; + } + ofs += (1 + len_octets + len); + vb_len -= (1 + len_octets + len); + } + + if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ) + { + snmp_add_snmpintotalsetvars(m_stat->invb.count); + } + else + { + snmp_add_snmpintotalreqvars(m_stat->invb.count); + } + + *ofs_ret = ofs; + return ERR_OK; +} + +struct snmp_varbind* +snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len) +{ + struct snmp_varbind *vb; + + vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND); + if (vb != NULL) + { + u8_t i; + + vb->next = NULL; + vb->prev = NULL; + i = oid->len; + vb->ident_len = i; + if (i > 0) + { + LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH); + /* allocate array of s32_t for our object identifier */ + vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE); + if (vb->ident == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n")); + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + while(i > 0) + { + i--; + vb->ident[i] = oid->id[i]; + } + } + else + { + /* i == 0, pass zero length object identifier */ + vb->ident = NULL; + } + vb->value_type = type; + vb->value_len = len; + if (len > 0) + { + LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE); + /* allocate raw bytes for our object value */ + vb->value = memp_malloc(MEMP_SNMP_VALUE); + if (vb->value == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n")); + if (vb->ident != NULL) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); + return NULL; + } + } + else + { + /* ASN1_NUL type, or zero length ASN1_OC_STR */ + vb->value = NULL; + } + } + else + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n")); + } + return vb; +} + +void +snmp_varbind_free(struct snmp_varbind *vb) +{ + if (vb->value != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->value); + } + if (vb->ident != NULL ) + { + memp_free(MEMP_SNMP_VALUE, vb->ident); + } + memp_free(MEMP_SNMP_VARBIND, vb); +} + +void +snmp_varbind_list_free(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb, *prev; + + vb = root->tail; + while ( vb != NULL ) + { + prev = vb->prev; + snmp_varbind_free(vb); + vb = prev; + } + root->count = 0; + root->head = NULL; + root->tail = NULL; +} + +void +snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb) +{ + if (root->count == 0) + { + /* add first varbind to list */ + root->head = vb; + root->tail = vb; + } + else + { + /* add nth varbind to list tail */ + root->tail->next = vb; + vb->prev = root->tail; + root->tail = vb; + } + root->count += 1; +} + +struct snmp_varbind* +snmp_varbind_tail_remove(struct snmp_varbind_root *root) +{ + struct snmp_varbind* vb; + + if (root->count > 0) + { + /* remove tail varbind */ + vb = root->tail; + root->tail = vb->prev; + vb->prev->next = NULL; + root->count -= 1; + } + else + { + /* nothing to remove */ + vb = NULL; + } + return vb; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_out.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_out.c new file mode 100644 index 0000000..ecc524c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/snmp/msg_out.c @@ -0,0 +1,684 @@ +/** + * @file + * SNMP output message processing (RFC1157). + * + * Output responses and traps are build in two passes: + * + * Pass 0: iterate over the output message backwards to determine encoding lengths + * Pass 1: the actual forward encoding of internal form into ASN1 + * + * The single-pass encoding method described by Comer & Stevens + * requires extra buffer space and copying for reversal of the packet. + * The buffer requirement can be prohibitively large for big payloads + * (>= 484) therefore we use the two encoding passes. + */ + +/* + * 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 + */ + +#include "lwip/opt.h" + +#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/udp.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/snmp_asn1.h" +#include "lwip/snmp_msg.h" + +struct snmp_trap_dst +{ + /* destination IP address in network order */ + ip_addr_t dip; + /* set to 0 when disabled, >0 when enabled */ + u8_t enable; +}; +struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; + +/** TRAP message structure */ +struct snmp_msg_trap trap_msg; + +static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len); +static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len); +static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root); + +static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p); +static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p); +static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs); + +/** + * Sets enable switch for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param enable switch if 0 destination is disabled >0 enabled. + */ +void +snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + trap_dst[dst_idx].enable = enable; + } +} + +/** + * Sets IPv4 address for this trap destination. + * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 + * @param dst IPv4 address in host order. + */ +void +snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst) +{ + if (dst_idx < SNMP_TRAP_DESTINATIONS) + { + ip_addr_set(&trap_dst[dst_idx].dip, dst); + } +} + +/** + * Sends a 'getresponse' message to the request originator. + * + * @param m_stat points to the current message request state source + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the m_stat + * and provide error-status and index (except for tooBig errors) ... + */ +err_t +snmp_send_response(struct snmp_msg_pstat *m_stat) +{ + struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0}; + struct pbuf *p; + u16_t tot_len; + err_t err; + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&m_stat->outvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + + /* try allocating pbuf(s) for complete response */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p == NULL) + { + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n")); + + /* can't construct reply, return error-status tooBig */ + m_stat->error_status = SNMP_ES_TOOBIG; + m_stat->error_index = 0; + /* pass 0, recalculate lengths, for empty varbind-list */ + tot_len = snmp_varbind_list_sum(&emptyvb); + tot_len = snmp_resp_header_sum(m_stat, tot_len); + /* retry allocation once for header and empty varbind-list */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + } + if (p != NULL) + { + /* first pbuf alloc try or retry alloc success */ + u16_t ofs; + + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n")); + + /* pass 1, size error, encode packet ino the pbuf(s) */ + ofs = snmp_resp_header_enc(m_stat, p); + snmp_varbind_list_enc(&m_stat->outvb, p, ofs); + + switch (m_stat->error_status) + { + case SNMP_ES_NOERROR: + /* nothing to do */ + break; + case SNMP_ES_TOOBIG: + snmp_inc_snmpouttoobigs(); + break; + case SNMP_ES_NOSUCHNAME: + snmp_inc_snmpoutnosuchnames(); + break; + case SNMP_ES_BADVALUE: + snmp_inc_snmpoutbadvalues(); + break; + case SNMP_ES_GENERROR: + snmp_inc_snmpoutgenerrs(); + break; + default: + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_send_response(): unknown error_status: %d\n", (int)m_stat->error_status)); + break; + } + snmp_inc_snmpoutgetresponses(); + snmp_inc_snmpoutpkts(); + + /** @todo do we need separate rx and tx pcbs for threaded case? */ + /** connect to the originating source */ + udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp); + err = udp_send(m_stat->pcb, p); + if (err == ERR_MEM) + { + /** @todo release some memory, retry and return tooBig? tooMuchHassle? */ + err = ERR_MEM; + } + else + { + err = ERR_OK; + } + /** disassociate remote address and port with this pcb */ + udp_disconnect(m_stat->pcb); + + pbuf_free(p); + LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n")); + return err; + } + else + { + /* first pbuf alloc try or retry alloc failed + very low on memory, couldn't return tooBig */ + return ERR_MEM; + } +} + + +/** + * Sends an generic or enterprise specific trap message. + * + * @param generic_trap is the trap code + * @param eoid points to enterprise object identifier + * @param specific_trap used for enterprise traps when generic_trap == 6 + * @return ERR_OK when success, ERR_MEM if we're out of memory + * + * @note the caller is responsible for filling in outvb in the trap_msg + * @note the use of the enterprise identifier field + * is per RFC1215. + * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps + * and .iso.org.dod.internet.private.enterprises.yourenterprise + * (sysObjectID) for specific traps. + */ +err_t +snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) +{ + struct snmp_trap_dst *td; + struct netif *dst_if; + ip_addr_t dst_ip; + struct pbuf *p; + u16_t i,tot_len; + err_t err = ERR_OK; + + for (i=0, td = &trap_dst[0]; ienable != 0) && !ip_addr_isany(&td->dip)) + { + /* network order trap destination */ + ip_addr_copy(trap_msg.dip, td->dip); + /* lookup current source address for this dst */ + dst_if = ip_route(&td->dip); + if (dst_if != NULL) { + ip_addr_copy(dst_ip, dst_if->ip_addr); + /* @todo: what about IPv6? */ + trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); + trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); + trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); + trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); + trap_msg.gen_trap = generic_trap; + trap_msg.spc_trap = specific_trap; + if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) + { + /* enterprise-Specific trap */ + trap_msg.enterprise = eoid; + } + else + { + /* generic (MIB-II) trap */ + snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); + } + snmp_get_sysuptime(&trap_msg.ts); + + /* pass 0, calculate length fields */ + tot_len = snmp_varbind_list_sum(&trap_msg.outvb); + tot_len = snmp_trap_header_sum(&trap_msg, tot_len); + + /* allocate pbuf(s) */ + p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); + if (p != NULL) + { + u16_t ofs; + + /* pass 1, encode packet ino the pbuf(s) */ + ofs = snmp_trap_header_enc(&trap_msg, p); + snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); + + snmp_inc_snmpouttraps(); + snmp_inc_snmpoutpkts(); + + /** send to the TRAP destination */ + udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); + + pbuf_free(p); + } else { + err = ERR_MEM; + } + } else { + /* routing error */ + err = ERR_RTE; + } + } + } + return err; +} + +void +snmp_coldstart_trap(void) +{ + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_COLDSTART, NULL, 0); +} + +void +snmp_authfail_trap(void) +{ + u8_t enable; + snmp_get_snmpenableauthentraps(&enable); + if (enable == 1) + { + trap_msg.outvb.head = NULL; + trap_msg.outvb.tail = NULL; + trap_msg.outvb.count = 0; + snmp_send_trap(SNMP_GENTRAP_AUTHFAIL, NULL, 0); + } +} + +/** + * Sums response header field lengths from tail to head and + * returns resp_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param rhl points to returned header lengths + * @return the required length for encoding the response header + */ +static u16_t +snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_resp_header_lengths *rhl; + + rhl = &m_stat->rhl; + tot_len = vb_len; + snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen); + snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen); + tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen; + + snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen); + snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen); + tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen; + + snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen); + snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen); + tot_len += 1 + rhl->ridlenlen + rhl->ridlen; + + rhl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen); + tot_len += 1 + rhl->pdulenlen; + + rhl->comlen = m_stat->com_strlen; + snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen); + tot_len += 1 + rhl->comlenlen + rhl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen); + snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen); + tot_len += 1 + rhl->verlen + rhl->verlenlen; + + rhl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen); + tot_len += 1 + rhl->seqlenlen; + + return tot_len; +} + +/** + * Sums trap header field lengths from tail to head and + * returns trap_header_lengths for second encoding pass. + * + * @param vb_len varbind-list length + * @param thl points to returned header lengths + * @return the required length for encoding the trap header + */ +static u16_t +snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len) +{ + u16_t tot_len; + struct snmp_trap_header_lengths *thl; + + thl = &m_trap->thl; + tot_len = vb_len; + + snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen); + snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen); + tot_len += 1 + thl->tslen + thl->tslenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen); + snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen); + tot_len += 1 + thl->strplen + thl->strplenlen; + + snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen); + snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen); + tot_len += 1 + thl->gtrplen + thl->gtrplenlen; + + thl->aaddrlen = 4; + snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen); + tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen; + + snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen); + snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen); + tot_len += 1 + thl->eidlen + thl->eidlenlen; + + thl->pdulen = tot_len; + snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen); + tot_len += 1 + thl->pdulenlen; + + thl->comlen = sizeof(snmp_publiccommunity) - 1; + snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen); + tot_len += 1 + thl->comlenlen + thl->comlen; + + snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen); + snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen); + tot_len += 1 + thl->verlen + thl->verlenlen; + + thl->seqlen = tot_len; + snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen); + tot_len += 1 + thl->seqlenlen; + + return tot_len; +} + +/** + * Sums varbind lengths from tail to head and + * annotates lengths in varbind for second encoding pass. + * + * @param root points to the root of the variable binding list + * @return the required length for encoding the variable bindings + */ +static u16_t +snmp_varbind_list_sum(struct snmp_varbind_root *root) +{ + struct snmp_varbind *vb; + u32_t *uint_ptr; + s32_t *sint_ptr; + u16_t tot_len; + + tot_len = 0; + vb = root->tail; + while ( vb != NULL ) + { + /* encoded value lenght depends on type */ + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + vb->vlen = vb->value_len; + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen); + break; + default: + /* unsupported type */ + vb->vlen = 0; + break; + }; + /* encoding length of value length field */ + snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen); + snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen); + snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen); + + vb->seqlen = 1 + vb->vlenlen + vb->vlen; + vb->seqlen += 1 + vb->olenlen + vb->olen; + snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen); + + /* varbind seq */ + tot_len += 1 + vb->seqlenlen + vb->seqlen; + + vb = vb->prev; + } + + /* varbind-list seq */ + root->seqlen = tot_len; + snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen); + tot_len += 1 + root->seqlenlen; + + return tot_len; +} + +/** + * Encodes response header from head to tail. + */ +static u16_t +snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen); + ofs += m_stat->rhl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen); + ofs += m_stat->rhl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version); + ofs += m_stat->rhl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen); + ofs += m_stat->rhl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community); + ofs += m_stat->rhl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen); + ofs += m_stat->rhl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen); + ofs += m_stat->rhl.ridlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid); + ofs += m_stat->rhl.ridlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen); + ofs += m_stat->rhl.errstatlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status); + ofs += m_stat->rhl.errstatlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen); + ofs += m_stat->rhl.erridxlenlen; + snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index); + ofs += m_stat->rhl.erridxlen; + + return ofs; +} + +/** + * Encodes trap header from head to tail. + */ +static u16_t +snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p) +{ + u16_t ofs; + + ofs = 0; + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen); + ofs += m_trap->thl.seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen); + ofs += m_trap->thl.verlenlen; + snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version); + ofs += m_trap->thl.verlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen); + ofs += m_trap->thl.comlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]); + ofs += m_trap->thl.comlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen); + ofs += m_trap->thl.pdulenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen); + ofs += m_trap->thl.eidlenlen; + snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]); + ofs += m_trap->thl.eidlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen); + ofs += m_trap->thl.aaddrlenlen; + snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]); + ofs += m_trap->thl.aaddrlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen); + ofs += m_trap->thl.gtrplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap); + ofs += m_trap->thl.gtrplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen); + ofs += m_trap->thl.strplenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap); + ofs += m_trap->thl.strplen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen); + ofs += m_trap->thl.tslenlen; + snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts); + ofs += m_trap->thl.tslen; + + return ofs; +} + +/** + * Encodes varbind list from head to tail. + */ +static u16_t +snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs) +{ + struct snmp_varbind *vb; + s32_t *sint_ptr; + u32_t *uint_ptr; + u8_t *raw_ptr; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, root->seqlen); + ofs += root->seqlenlen; + + vb = root->head; + while ( vb != NULL ) + { + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->seqlen); + ofs += vb->seqlenlen; + + snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->olen); + ofs += vb->olenlen; + snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]); + ofs += vb->olen; + + snmp_asn1_enc_type(p, ofs, vb->value_type); + ofs += 1; + snmp_asn1_enc_length(p, ofs, vb->vlen); + ofs += vb->vlenlen; + + switch (vb->value_type) + { + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr); + break; + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS): + uint_ptr = (u32_t*)vb->value; + snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR): + case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE): + raw_ptr = (u8_t*)vb->value; + snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr); + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL): + break; + case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID): + sint_ptr = (s32_t*)vb->value; + snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr); + break; + default: + /* unsupported type */ + break; + }; + ofs += vb->vlen; + vb = vb->next; + } + return ofs; +} + +#endif /* LWIP_SNMP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/stats.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/stats.c new file mode 100644 index 0000000..ff97853 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/stats.c @@ -0,0 +1,181 @@ +/** + * @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, const 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 || MLD6_STATS +void +stats_display_igmp(struct stats_igmp *igmp, const char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + 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\t", igmp->rx_group)); + LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", 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 || MLD6_STATS */ + +#if MEM_STATS || MEMP_STATS +void +stats_display_mem(struct stats_mem *mem, const 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(); + IP6_FRAG_STATS_DISPLAY(); + IP_STATS_DISPLAY(); + ND6_STATS_DISPLAY(); + IP6_STATS_DISPLAY(); + IGMP_STATS_DISPLAY(); + MLD6_STATS_DISPLAY(); + ICMP_STATS_DISPLAY(); + ICMP6_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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/sys.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/sys.c new file mode 100644 index 0000000..f177737 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/sys.c @@ -0,0 +1,68 @@ +/** + * @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 + +#ifndef sys_msleep +/** + * 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 /* sys_msleep */ + +#endif /* !NO_SYS */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp.c new file mode 100644 index 0000000..5edc73c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp.c @@ -0,0 +1,1880 @@ +/** + * @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 "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/nd6.h" + +#include + +#ifndef TCP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define TCP_LOCAL_PORT_RANGE_START 0xc000 +#define TCP_LOCAL_PORT_RANGE_END 0xffff +#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START) +#endif + +#if LWIP_TCP_KEEPALIVE +#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl) +#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl) +#else /* LWIP_TCP_KEEPALIVE */ +#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE +#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT +#endif /* LWIP_TCP_KEEPALIVE */ + +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" +}; + +/* last local TCP port */ +static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START; + +/* 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; + +u8_t tcp_active_pcbs_changed; + +/** Timer counter to handle calling slow-timer from tcp_tmr() */ +static u8_t tcp_timer; +static u8_t tcp_timer_ctr; +static u16_t tcp_new_port(void); + +/** + * Initialize this module. + */ +void +tcp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * 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 == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + 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, PCB_ISIPV6(pcb)); + + tcp_pcb_purge(pcb); + TCP_RMV_ACTIVE(pcb); + if (pcb->state == ESTABLISHED) { + /* move to TIME_WAIT since we close actively */ + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */ + memp_free(MEMP_TCP_PCB, 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. */ + err = ERR_OK; + if (pcb->local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + TCP_PCB_REMOVE_ACTIVE(pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + snmp_inc_tcpattemptfails(); + break; + case SYN_RCVD: + err = tcp_send_fin(pcb); + if (err == ERR_OK) { + snmp_inc_tcpattemptfails(); + pcb->state = 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; + } + 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. */ + tcp_output(pcb); + } + 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 + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ +#if TCP_DEBUG + 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 unless shutting down both sides! + * Shutting down both sides is the same as calling tcp_close, so if it succeds, + * the PCB should not be referenced any more. + * + * @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: set a flag not to receive any more data... */ + pcb->flags |= TF_RXCLOSED; + if (shut_tx) { + /* shutting down the tx AND rx side is the same as closing for the raw API */ + return tcp_close_shutdown(pcb, 1); + } + /* ... and free buffered data */ + if (pcb->refused_data != NULL) { + pbuf_free(pcb->refused_data); + pcb->refused_data = NULL; + } + } + 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, shut_rx); + default: + /* Not (yet?) connected, cannot shutdown the TX side as that would bring us + into CLOSED state, where the PCB is deallocated. */ + return ERR_CONN; + } + } + 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; +#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 { + int send_rst = reset && (pcb->state != CLOSED); + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + if ((pcb->state == CLOSED) && (pcb->local_port != 0)) { + /* bound, not yet opened */ + TCP_RMV(&tcp_bound_pcbs, pcb); + } else { + TCP_PCB_REMOVE_ACTIVE(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 (send_rst) { + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n")); + tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb)); + } + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + } +} + +/** + * 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 port number 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_VAL if bind failed because the PCB is not in a valid state + * 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_VAL); + +#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 (ip_get_option(pcb, SOF_REUSEADDR)) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ + + if (port == 0) { + port = tcp_new_port(); + if (port == 0) { + return ERR_BUF; + } + } + + /* 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 (!ip_get_option(pcb, SOF_REUSEADDR) || + !ip_get_option(cpcb, SOF_REUSEADDR)) +#endif /* SO_REUSE */ + { + /* @todo: check accept_any_ip_version */ + if (IP_PCB_IPVER_EQ(pcb, cpcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) || + ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) { + return ERR_USE; + } + } + } + } + } + + if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(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. + * + * @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 (ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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) && + IP_PCB_IPVER_EQ(pcb, lpcb)) { + if (ipX_addr_cmp(PCB_ISIPV6(pcb), &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->ttl = pcb->ttl; + lpcb->tos = pcb->tos; +#if LWIP_IPV6 + PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb); + lpcb->accept_any_ip_version = 0; +#endif /* LWIP_IPV6 */ + ipX_addr_copy(PCB_ISIPV6(pcb), 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); + return (struct tcp_pcb *)lpcb; +} + +#if LWIP_IPV6 +/** + * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6 + * connections, if the pcb's local address is set to ANY. + */ +struct tcp_pcb * +tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog) +{ + struct tcp_pcb *lpcb; + + lpcb = tcp_listen_with_backlog(pcb, backlog); + if ((lpcb != NULL) && + ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* The default behavior is to accept connections on either + * IPv4 or IPv6, if not bound. */ + /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */ + ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1; + } + return lpcb; +} +#endif /* LWIP_IPV6 */ + +/** + * 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; +#if !LWIP_WND_SCALE + LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff); +#endif + pcb->rcv_ann_wnd = (tcpwnd_size_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; + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_recved for listen-pcbs", + pcb->state != LISTEN); + + pcb->rcv_wnd += len; + if (pcb->rcv_wnd > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } else if(pcb->rcv_wnd == 0) { + /* rcv_wnd overflowed */ + if ((pcb->state == CLOSE_WAIT) || (pcb->state == LAST_ACK)) { + /* In passive close, we allow this, since the FIN bit is added to rcv_wnd + by the stack itself, since it is not mandatory for an application + to call tcp_recved() for the FIN bit, but e.g. the netconn API does so. */ + pcb->rcv_wnd = TCP_WND; + } else { + LWIP_ASSERT("tcp_recved: len wrapped rcv_wnd\n", 0); + } + } + + 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: received %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * Allocate a new local TCP port. + * + * @return a new (free) local TCP port number + */ +static u16_t +tcp_new_port(void) +{ + u8_t i; + u16_t n = 0; + struct tcp_pcb *pcb; + +again: + if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) { + tcp_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 == tcp_port) { + if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + } + return tcp_port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + * @param pcb the tcp_pcb used to establish the connection + * @param ipaddr the remote ip address to connect to + * @param port the remote tcp port to connect to + * @param connected callback function to call when connected (on error, + the err calback will be called) + * @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 connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr)); + } else { + return ERR_VAL; + } + pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + /* no local IP address set, yet. */ + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the address as local address of the pcb. */ + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + old_local_port = pcb->local_port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + if (pcb->local_port == 0) { + return ERR_BUF; + } + } +#if SO_REUSE + if (ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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_PCB_IPVER_EQ(cpcb, pcb) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) && + ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(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, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; +#if LWIP_CALLBACK_API + pcb->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 */ + pcb->state = SYN_SENT; + if (old_local_port != 0) { + TCP_RMV(&tcp_bound_pcbs, pcb); + } + TCP_REG_ACTIVE(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; + tcpwnd_size_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; + ++tcp_timer_ctr; + +tcp_slowtmr_start: + /* 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); + if (pcb->last_timer == tcp_timer_ctr) { + /* skip this pcb, we have already processed it */ + pcb = pcb->next; + continue; + } + pcb->last_timer = tcp_timer_ctr; + + 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]; + } + + /* 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 < (tcpwnd_size_t)(pcb->mss << 1)) { + pcb->ssthresh = (pcb->mss << 1); + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_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 this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */ + if (pcb->flags & TF_RXCLOSED) { + /* PCB was fully closed (either through close() or SHUT_RDWR): + normal FIN-WAIT timeout handling. */ + 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(ip_get_option(pcb, SOF_KEEPALIVE) && + ((pcb->state == ESTABLISHED) || + (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) + { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + ++pcb_remove; + ++pcb_reset; + } + else if((u32_t)(tcp_ticks - pcb->tmr) > + (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb)) + / TCP_SLOW_INTERVAL) + { + 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_err_fn err_fn; + void *err_arg; + 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, PCB_ISIPV6(pcb)); + } + + err_fn = pcb->errf; + err_arg = pcb->callback_arg; + pcb2 = pcb; + pcb = pcb->next; + memp_free(MEMP_TCP_PCB, pcb2); + + tcp_active_pcbs_changed = 0; + TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + } 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_active_pcbs_changed = 0; + TCP_EVENT_POLL(prev, err); + if (tcp_active_pcbs_changed) { + goto tcp_slowtmr_start; + } + /* 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_timer_ctr; + +tcp_fasttmr_start: + pcb = tcp_active_pcbs; + + while(pcb != NULL) { + if (pcb->last_timer != tcp_timer_ctr) { + struct tcp_pcb *next; + pcb->last_timer = tcp_timer_ctr; + /* send delayed ACKs */ + if (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); + } + + next = pcb->next; + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + tcp_active_pcbs_changed = 0; + tcp_process_refused_data(pcb); + if (tcp_active_pcbs_changed) { + /* application callback has changed the pcb list: restart the loop */ + goto tcp_fasttmr_start; + } + } + pcb = next; + } else { + pcb = pcb->next; + } + } +} + +/** Pass pcb->refused_data to the recv callback */ +err_t +tcp_process_refused_data(struct tcp_pcb *pcb) +{ +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + struct pbuf *rest; + while (pcb->refused_data != NULL) +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + { + err_t err; + u8_t refused_flags = pcb->refused_data->flags; + /* set pcb->refused_data to NULL in case the callback frees it and then + closes the pcb */ + struct pbuf *refused_data = pcb->refused_data; +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + pbuf_split_64k(refused_data, &rest); + pcb->refused_data = rest; +#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = NULL; +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + /* Notify again application with data previously received. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); + TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err); + if (err == ERR_OK) { + /* did refused_data include a FIN? */ + if (refused_flags & PBUF_FLAG_TCP_FIN +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + && (rest == NULL) +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + ) { + /* 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) { + return ERR_ABRT; + } + } + } else if (err == ERR_ABRT) { + /* 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")); + return ERR_ABRT; + } else { + /* data is still refused, pbuf is still valid (go on for ACK-only packets) */ +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_cat(refused_data, rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = refused_data; + return ERR_INPROGRESS; + } + } + return ERR_OK; +} + +/** + * 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 the same or lower priority than + * 'prio'. + * + * @param prio minimum priority + */ +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive, *lastack; + u32_t inactivity, inactivity_lastack; + u8_t minprio, minprio_lastack; + + minprio = prio; + minprio_lastack = prio; + + /* We kill the oldest active connection that has lower priority than prio. + However, already closed connections waiting for the last ack are closed first + since they don't lose data. */ + inactivity = 0; + inactive = NULL; + inactivity_lastack = 0; + lastack = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if ((lastack != NULL) || (pcb->state == LAST_ACK) || (pcb->state == CLOSING)) { + /* found at least one pcb in last ack phase */ + if ((pcb->prio < minprio_lastack) || + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity_lastack) { + inactivity_lastack = tcp_ticks - pcb->tmr; + lastack = pcb; + minprio_lastack = pcb->prio; + } + } else if (pcb->prio <= minprio) { + if ((pcb->prio < minprio) || + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + minprio = pcb->prio; + } + } + } + if (lastack != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB in LAST_ACK or CLOSING %p (%"S32_F")\n", + (void *)lastack, inactivity_lastack)); + tcp_abort(lastack); + } else 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 +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. + * + * @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) { + /* 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)); + pcb->prio = prio; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->rcv_ann_wnd = TCP_WND; +#if LWIP_WND_SCALE + /* snd_scale and rcv_scale are zero unless both sides agree to use scaling */ + pcb->snd_scale = 0; + pcb->rcv_scale = 0; +#endif + pcb->tos = 0; + pcb->ttl = TCP_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 = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / 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->last_timer = tcp_timer_ctr; + + 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); +} + +#if LWIP_IPV6 +/** + * Creates a new TCP-over-IPv6 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(). + * + * @return a new tcp_pcb that initially is in state CLOSED + */ +struct tcp_pcb * +tcp_new_ip6(void) +{ + struct tcp_pcb * pcb; + pcb = tcp_alloc(TCP_PRIO_NORMAL); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +/** + * Used to specify the argument that should be passed callback + * functions. + * + * @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) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + * @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) +{ + LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN); + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + * @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) +{ + LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN); + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occurred on the connection. + * + * @param pcb tcp_pcb to set the err callback + * @param err callback function to call for this pcb when a fatal error + * has occurred on the connection + */ +void +tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) +{ + LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN); + pcb->errf = err; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + * @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) +{ + /* This function is allowed to be called for both listen pcbs and + connection pcbs. */ + 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. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval) +{ + LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN); +#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_PCB_IPVER_EQ(pcb, lpcb) && + (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) || + ipX_addr_cmp(PCB_ISIPV6(lpcb), &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; + /* reset the local port to prevent the pcb from being 'bound' */ + pcb->local_port = 0; + + 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 +/** + * Calculates the effective send mss that can be used for a specific IP address + * by using ip_route to determine 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_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + u16_t mss_s; + struct netif *outif; + s16_t mtu; + + outif = ipX_route(isipv6, src, dest); +#if LWIP_IPV6 + if (isipv6) { + /* First look in destination cache, to see if there is a Path MTU. */ + mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif); + } else +#endif /* LWIP_IPV6 */ + { + if (outif == NULL) { + return sendmss; + } + mtu = outif->mtu; + } + + if (mtu != 0) { + mss_s = mtu - IP_HLEN - TCP_HLEN; +#if LWIP_IPV6 + if (isipv6) { + /* for IPv6, subtract the difference in header size */ + mss_s -= (IP6_HLEN - IP_HLEN); + } +#endif /* LWIP_IPV6 */ + /* 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_in.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_in.c new file mode 100644 index 0000000..9eedfc2 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_in.c @@ -0,0 +1,1770 @@ +/** + * @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" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_ND6_TCP_REACHABILITY_HINTS +#include "lwip/nd6.h" +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#define LWIP_TCP_CALC_INITIAL_CWND(mss) LWIP_MIN((4U * (mss)), LWIP_MAX((2U * (mss)), 4380U)); + +/* 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; +static struct tcp_hdr *tcphdr; +static u16_t tcphdr_opt1len; +static u8_t* tcphdr_opt2; +static u16_t tcp_optidx; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + +/** + * 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 TCP header) + * @param inp network interface on which this segment was received + */ +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; +#if CHECKSUM_CHECK_TCP + u16_t chksum; +#endif /* CHECKSUM_CHECK_TCP */ + + PERF_START; + + TCP_STATS_INC(tcp.recv); + snmp_inc_tcpinsegs(); + + tcphdr = (struct tcp_hdr *)p->payload; + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* Check that TCP header fits in payload */ + if (p->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); + goto dropped; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) || + ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) { + TCP_STATS_INC(tcp.proterr); + goto dropped; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len, + ipX_current_src_addr(), ipX_current_dest_addr()); + if (chksum != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + chksum)); + tcp_debug_print(tcphdr); + TCP_STATS_INC(tcp.chkerr); + goto dropped; + } +#endif /* CHECKSUM_CHECK_TCP */ + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + tcphdr_opt1len = (hdrlen * 4) - TCP_HLEN; + tcphdr_opt2 = NULL; + if (p->len < hdrlen * 4) { + if (p->len >= TCP_HLEN) { + /* TCP header fits into first pbuf, options don't - data is in the next pbuf */ + u16_t optlen = tcphdr_opt1len; + pbuf_header(p, -TCP_HLEN); /* cannot fail */ + LWIP_ASSERT("tcphdr_opt1len >= p->len", tcphdr_opt1len >= p->len); + LWIP_ASSERT("p->next != NULL", p->next != NULL); + tcphdr_opt1len = p->len; + if (optlen > tcphdr_opt1len) { + s16_t opt2len; + /* options continue in the next pbuf: set p to zero length and hide the + options in the next pbuf (adjusting p->tot_len) */ + u8_t phret = pbuf_header(p, -(s16_t)tcphdr_opt1len); + LWIP_ASSERT("phret == 0", phret == 0); + tcphdr_opt2 = (u8_t*)p->next->payload; + opt2len = optlen - tcphdr_opt1len; + phret = pbuf_header(p->next, -opt2len); + LWIP_ASSERT("phret == 0", phret == 0); + /* p->next->payload now points to the TCP data */ + /* manually adjust p->tot_len to changed p->next->tot_len change */ + p->tot_len -= opt2len; + } + LWIP_ASSERT("p->len == 0", p->len == 0); + } else { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); + TCP_STATS_INC(tcp.lenerr); + goto dropped; + } + } else { + pbuf_header(p, -(hdrlen * 4)); /* cannot fail */ + } + + /* 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); + + flags = TCPH_FLAGS(tcphdr); + tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0); + + /* 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_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* 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; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + 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) { + 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_PCB_IPVER_INPUT_MATCH(pcb) && + ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) && + ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) { + /* 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); + 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) { + if (lpcb->local_port == tcphdr->dest) { +#if LWIP_IPV6 + if (lpcb->accept_any_ip_version) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + break; +#endif /* SO_REUSE */ + } else +#endif /* LWIP_IPV6 */ + if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) { + if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) { + /* found an exact match */ + break; + } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) { + /* found an ANY-match */ +#if SO_REUSE + lpcb_any = lpcb; + lpcb_prev = prev; +#else /* SO_REUSE */ + 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); + 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 (flags & TCP_PSH) { + p->flags |= PBUF_FLAG_PUSH; + } + + /* If there is data which was previously "refused" by upper layer */ + if (pcb->refused_data != NULL) { + if ((tcp_process_refused_data(pcb) == ERR_ABRT) || + ((pcb->refused_data != NULL) && (tcplen > 0))) { + /* pcb has been aborted or refused data is still refused and the new + segment contains data */ + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + goto aborted; + } + } + 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); + 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) { + u16_t acked; +#if LWIP_WND_SCALE + /* pcb->acked is u32_t but the sent callback only takes a u16_t, + so we might have to call it multiple times. */ + u32_t pcb_acked = pcb->acked; + while(pcb_acked > 0) { + acked = (u16_t)LWIP_MIN(pcb_acked, 0xffffu); + pcb_acked -= acked; +#else + { + acked = pcb->acked; +#endif + TCP_EVENT_SENT(pcb, (u16_t)acked, err); + if (err == ERR_ABRT) { + goto aborted; + } + } + } + +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + while (recv_data != NULL) { + struct pbuf *rest = NULL; + pbuf_split_64k(recv_data, &rest); +#else /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + if (recv_data != NULL) { +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + + 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); +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_free(rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + tcp_abort(pcb); + goto aborted; + } + + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + if (err == ERR_ABRT) { +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_free(rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + goto aborted; + } + + /* If the upper layer can't receive this data, store it */ + if (err != ERR_OK) { +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + if (rest != NULL) { + pbuf_cat(recv_data, rest); + } +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + pcb->refused_data = recv_data; + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); +#if TCP_QUEUE_OOSEQ && LWIP_WND_SCALE + break; + } else { + /* Upper layer received the data, go on with the rest if > 64K */ + recv_data = rest; +#endif /* TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + } + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + if (pcb->refused_data != NULL) { + /* Delay this if we have refused data. */ + pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN; + } else { + /* 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); + 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_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); + return; +dropped: + TCP_STATS_INC(tcp.drop); + snmp_inc_tcpinerrs(); + pbuf_free(p); +} + +/** + * 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 + */ +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + err_t rc; + + if (flags & TCP_RST) { + /* An incoming RST should be ignored. Return. */ + return ERR_OK; + } + + /* 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, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } else if (flags & TCP_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 */ + 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); + return ERR_MEM; + } +#if TCP_LISTEN_BACKLOG + pcb->accepts_pending++; +#endif /* TCP_LISTEN_BACKLOG */ + /* Set up the new PCB. */ +#if LWIP_IPV6 + PCB_ISIPV6(npcb) = ip_current_is_v6(); +#endif /* LWIP_IPV6 */ + ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr()); + ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr()); + npcb->local_port = pcb->local_port; + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->rcv_ann_right_edge = npcb->rcv_nxt; + 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_ACTIVE(npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); + npcb->snd_wnd = SND_WND_SCALE(npcb, tcphdr->wnd); + npcb->snd_wnd_max = npcb->snd_wnd; + npcb->ssthresh = npcb->snd_wnd; + +#if TCP_CALCULATE_EFF_SEND_MSS + npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, + &npcb->remote_ip, PCB_ISIPV6(npcb)); +#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 + */ +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */ + /* RFC 793 3.9 Event Processing - Segment Arrives: + * - first check sequence number - we skip that one in TIME_WAIT (always + * acceptable since we only send ACKs) + * - second check the RST bit (... return) */ + if (flags & TCP_RST) { + return ERR_OK; + } + /* - fourth, check the SYN bit, */ + if (flags & TCP_SYN) { + /* If an incoming segment is not acceptable, an acknowledgment + should be sent in reply */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + /* If the SYN is in the window it is an error, send a reset */ + tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + return ERR_OK; + } + } else if (flags & TCP_FIN) { + /* - eighth, check the FIN bit: Remain in the TIME-WAIT state. + Restart the 2 MSL time-wait timeout.*/ + pcb->tmr = tcp_ticks; + } + + if ((tcplen > 0)) { + /* Acknowledge data, FIN or out-of-window SYN */ + pcb->flags |= TF_ACK_NOW; + 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 = SND_WND_SCALE(pcb, tcphdr->wnd); + pcb->snd_wnd_max = pcb->snd_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->local_ip, &pcb->remote_ip, + PCB_ISIPV6(pcb)); +#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 = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SENT): cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_F"\n", + pcb->cwnd, pcb->ssthresh)); + LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0)); + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"TCPWNDSIZE_F"\n", (tcpwnd_size_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* 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; + } + + /* Call the user specified function to call when successfully + * 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, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + 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; + } + /* 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 = LWIP_TCP_CALC_INITIAL_CWND(pcb->mss); + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_process (SYN_RCVD): cwnd %"TCPWNDSIZE_F + " ssthresh %"TCPWNDSIZE_F"\n", + pcb->cwnd, pcb->ssthresh)); + + 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, ipX_current_dest_addr(), + ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6()); + } + } 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_ACTIVE(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_ACTIVE(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_ACTIVE(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 +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 + * 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 TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + u32_t ooseq_blen; + u16_t ooseq_qlen; +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ + + LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED); + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2; + + /* Update window. */ + 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 = SND_WND_SCALE(pcb, tcphdr->wnd); + /* keep track of the biggest window announced by the remote host to calculate + the maximum segment size */ + if (pcb->snd_wnd_max < pcb->snd_wnd) { + pcb->snd_wnd_max = pcb->snd_wnd; + } + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + if (pcb->snd_wnd == 0) { + if (pcb->persist_backoff == 0) { + /* start persist timer */ + pcb->persist_cnt = 0; + pcb->persist_backoff = 1; + } + } else if (pcb->persist_backoff > 0) { + /* stop persist timer */ + 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) + * + * 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)) { + 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 ((u8_t)(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 ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } else if (pcb->dupacks == 3) { + /* 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)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* 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 + unless window scaling is used. */ + pcb->acked = (tcpwnd_size_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 ((tcpwnd_size_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd)); + } else { + tcpwnd_size_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 %"TCPWNDSIZE_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 acknowledges them. */ + 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->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_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); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unacked)\n", (tcpwnd_size_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; + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + } else { + /* Out of sequence ACK, didn't really ack anything */ + pcb->acked = 0; + tcp_send_empty_ack(pcb); + } + + /* 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. */ + 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->unsent = pcb->unsent->next; +#if TCP_OVERSIZE + if (pcb->unsent == NULL) { + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"TCPWNDSIZE_F" ... ", (tcpwnd_size_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); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing unsent)\n", (tcpwnd_size_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)) { + /* 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); + + 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 */ + 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 unless the pcb already received a FIN. + (RFC 793, chapter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING, + LAST-ACK and TIME-WAIT: "Ignore the segment text.") */ + if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) { + /* 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)){ + /* 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.*/ + + 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)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + 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)){ + 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. */ + /* With window scaling, this can overflow recv_data->tot_len, but + that's not a problem since we explicitly fix that before passing + recv_data 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); + +#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS + if (PCB_ISIPV6(pcb)) { + /* Inform neighbor reachability of forward progress. */ + nd6_reachability_hint(ip6_current_src_addr()); + } +#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/ + + } 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) { + 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); + if (cseg != NULL) { + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + tcp_oos_insert_segment(cseg, next); + } + break; + } else { + /* Either the lengths 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; + } + } +#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS + /* Check that the data on ooseq doesn't exceed one of the limits + and throw away everything above that limit. */ + ooseq_blen = 0; + ooseq_qlen = 0; + prev = NULL; + for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) { + struct pbuf *p = next->p; + ooseq_blen += p->tot_len; + ooseq_qlen += pbuf_clen(p); + if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) || + (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) { + /* too much ooseq data, dump this and everything after it */ + tcp_segs_free(next); + if (prev == NULL) { + /* first ooseq segment is too much, dump the whole queue */ + pcb->ooseq = NULL; + } else { + /* just dump 'next' and everything after it */ + prev->next = NULL; + } + break; + } + } +#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */ +#endif /* TCP_QUEUE_OOSEQ */ + } + } else { + /* The incoming segment is not within 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); + } + } +} + +static u8_t tcp_getoptbyte(void) +{ + if ((tcphdr_opt2 == NULL) || (tcp_optidx < tcphdr_opt1len)) { + u8_t* opts = (u8_t *)tcphdr + TCP_HLEN; + return opts[tcp_optidx++]; + } else { + u8_t idx = tcp_optidx++ - tcphdr_opt1len; + return tcphdr_opt2[idx]; + } +} + +/** + * 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) +{ + u8_t data; + u16_t mss; +#if LWIP_TCP_TIMESTAMPS + u32_t tsval; +#endif + + /* Parse the TCP MSS option, if present. */ + if (TCPH_HDRLEN(tcphdr) > 0x5) { + u16_t max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2; + for (tcp_optidx = 0; tcp_optidx < max_c; ) { + u8_t opt = tcp_getoptbyte(); + switch (opt) { + case LWIP_TCP_OPT_EOL: + /* End of options. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n")); + return; + case LWIP_TCP_OPT_NOP: + /* NOP option. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n")); + break; + case LWIP_TCP_OPT_MSS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_MSS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_MSS) > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* An MSS option with the right option length. */ + mss = (tcp_getoptbyte() << 8); + mss |= tcp_getoptbyte(); + /* Limit the mss to the configured TCP_MSS and prevent division by zero */ + pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss; + break; +#if LWIP_WND_SCALE + case LWIP_TCP_OPT_WS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: WND_SCALE\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_WS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_WS) > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* If syn was received with wnd scale option, + activate wnd scale opt */ + data = tcp_getoptbyte(); + if (flags & TCP_SYN) { + /* An WND_SCALE option with the right option length. */ + pcb->snd_scale = data; + if (pcb->snd_scale > 14U) { + pcb->snd_scale = 14U; + } + pcb->rcv_scale = TCP_RCV_SCALE; + pcb->flags |= TF_WND_SCALE; + } + break; +#endif +#if LWIP_TCP_TIMESTAMPS + case LWIP_TCP_OPT_TS: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n")); + if (tcp_getoptbyte() != LWIP_TCP_OPT_LEN_TS || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_TS) > max_c) { + /* Bad length */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n")); + return; + } + /* TCP timestamp option with valid length */ + tsval = tcp_getoptbyte(); + tsval |= (tcp_getoptbyte() << 8); + tsval |= (tcp_getoptbyte() << 16); + tsval |= (tcp_getoptbyte() << 24); + if (flags & TCP_SYN) { + pcb->ts_recent = ntohl(tsval); + /* Enable sending timestamps in every segment now that we know + the remote host supports it. */ + pcb->flags |= TF_TIMESTAMP; + } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) { + pcb->ts_recent = ntohl(tsval); + } + /* Advance to next option (6 bytes already read) */ + tcp_optidx += LWIP_TCP_OPT_LEN_TS - 6; + break; +#endif + default: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n")); + data = tcp_getoptbyte(); + if (data < 2) { + 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. */ + tcp_optidx += data - 2; + } + } + } +} + +#endif /* LWIP_TCP */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_out.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_out.c new file mode 100644 index 0000000..10ed021 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/tcp_out.c @@ -0,0 +1,1574 @@ +/** + * @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/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet_chksum.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#if LWIP_TCP_TIMESTAMPS +#include "lwip/sys.h" +#endif + +#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 +/* Allow to override the failure of sanity check from warning to e.g. hard failure */ +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK +#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL +#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL(msg) LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING, msg) +#endif +#endif + +#if TCP_OVERSIZE +/** The size of segment pbufs created when TCP_OVERSIZE is enabled */ +#ifndef TCP_OVERSIZE_CALC_LENGTH +#define TCP_OVERSIZE_CALC_LENGTH(length) ((length) + TCP_OVERSIZE) +#endif +#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 * +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(RCV_WND_SCALE(pcb, 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); + pcb->flags |= TF_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 * +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; + LWIP_ASSERT("p->tot_len >= optlen", p->tot_len >= optlen); + 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 * +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 = max_length; +#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(TCP_OVERSIZE_CALC_LENGTH(length))); + } + } +#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 +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 +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: %"TCPWNDSIZE_F"\n", (tcpwnd_size_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 %"TCPWNDSIZE_F" (max %"TCPWNDSIZE_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 : + * - 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; + /* don't allocate segments bigger than half the maximum window we ever received */ + u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2); + mss_local = mss_local ? mss_local : pcb->mss; + +#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)) { + /* Make sure the timestamp option is only included in data segments if we + agreed about it with the remote host. */ + optflags = TF_SEG_OPTS_TS; + optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS); + /* ensure that segments can hold at least one data byte... */ + mss_local = LWIP_MAX(mss_local, LWIP_TCP_OPT_LEN_TS + 1); + } +#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); + LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen); + space = mss_local - (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 = mss_local - 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, mss_local, &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); + if (seglen & 1) { + chksum_swapped = 1; + chksum = SWAP_BYTES_IN_WORD(chksum); + } +#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 %"TCPWNDSIZE_F" (%"TCPWNDSIZE_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 + LWIP_ASSERT("last_unsent->oversize_left >= oversize_used", + last_unsent->oversize_left >= oversize_used); + 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) { + /*if concat checksumm swapped - swap it back */ + if (concat_chksum_swapped){ + concat_chksum = SWAP_BYTES_IN_WORD(concat_chksum); + } + 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 (FIN flag should always come through!) */ + if (((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) && + ((flags & TCP_FIN) == 0)) { + 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_WND_SCALE + if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_WND_SCALE)) { + /* In a (sent in state SYN_RCVD), the window scale option may only + be sent if we received a window scale option from the remote host. */ + optflags |= TF_SEG_OPTS_WND_SCALE; + } +#endif /* LWIP_WND_SCALE */ + } +#if LWIP_TCP_TIMESTAMPS + if ((pcb->flags & TF_TIMESTAMP)) { + /* Make sure the timestamp option is only included in data segments if we + agreed about it with the remote host. */ + 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 % 4) == 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 +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 + +#if LWIP_WND_SCALE +/** Build a window scale option (3 bytes long) at the specified options pointer) + * + * @param opts option pointer where to store the window scale option + */ +static void +tcp_build_wnd_scale_option(u32_t *opts) +{ + /* Pad with one NOP option to make everything nicely aligned */ + opts[0] = PP_HTONL(0x01030300 | TCP_RCV_SCALE); +} +#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; + u8_t optlen = 0; +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + +#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; + } +#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; +#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */ + 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 and window scale options are only sent on SYNs, so ignore them 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 = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, + IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), 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 +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 */ + + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_output for listen-pcbs", + pcb->state != LISTEN); + + /* 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); + } + + /* 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 %"TCPWNDSIZE_F + ", cwnd %"TCPWNDSIZE_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 %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_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 %"TCPWNDSIZE_F", cwnd %"TCPWNDSIZE_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); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + +#if TCP_OVERSIZE_DBGCHECK + seg->oversize_left = 0; +#endif /* TCP_OVERSIZE_DBGCHECK */ + tcp_output_segment(seg, pcb); + snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) { + pcb->snd_nxt = snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + 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 { + 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 */ + + 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; + 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 */ +#if LWIP_WND_SCALE + if (seg->flags & TF_SEG_OPTS_WND_SCALE) { + /* The Window field in a SYN segment itself (the only type where we send + the window scale option) is never scaled. */ + seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd); + } else +#endif /* LWIP_WND_SCALE */ + { + seg->tcphdr->wnd = htons(RCV_WND_SCALE(pcb, 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 */ + opts = (u32_t *)(void *)(seg->tcphdr + 1); + if (seg->flags & TF_SEG_OPTS_MSS) { + u16_t mss; +#if TCP_CALCULATE_EFF_SEND_MSS + mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb)); +#else /* TCP_CALCULATE_EFF_SEND_MSS */ + mss = TCP_MSS; +#endif /* TCP_CALCULATE_EFF_SEND_MSS */ + *opts = TCP_BUILD_MSS_OPTION(mss); + 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 +#if LWIP_WND_SCALE + if (seg->flags & TF_SEG_OPTS_WND_SCALE) { + tcp_build_wnd_scale_option(opts); + opts += 1; + } +#endif + + /* Set retransmission timer running if it is not currently enabled + This must be set before checking the route. */ + if (pcb->rtime == -1) { + pcb->rtime = 0; + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) { + struct netif *netif; + ipX_addr_t *local_ip; + ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip); + if ((netif == NULL) || (local_ip == NULL)) { + return; + } + ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip); + } + + 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 TCP_CHECKSUM_ON_COPY + { + u32_t acc; +#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK + u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#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 = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip); + /* 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) { + TCP_CHECKSUM_ON_COPY_SANITY_CHECK_FAIL( + ("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 */ +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP, + seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ +#endif /* TCP_CHECKSUM_ON_COPY */ + TCP_STATS_INC(tcp.xmit); + +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), 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_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ) +{ + 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); +#if LWIP_WND_SCALE + tcphdr->wnd = PP_HTONS(((TCP_WND >> TCP_RCV_SCALE) & 0xFFFF)); +#else + tcphdr->wnd = PP_HTONS(TCP_WND); +#endif + tcphdr->chksum = 0; + tcphdr->urgp = 0; + + TCP_STATS_INC(tcp.xmit); + snmp_inc_tcpoutrsts(); + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len, + local_ip, remote_ip); +#endif + /* Send output with hardcoded TTL/HL since we have no access to the pcb */ + ipX_output(isipv6, 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; +#if TCP_OVERSIZE && TCP_OVERSIZE_DBGCHECK + /* if last unsent changed, we need to update unsent_oversize */ + if (pcb->unsent == NULL) { + pcb->unsent_oversize = seg->oversize_left; + } +#endif /* TCP_OVERSIZE && TCP_OVERSIZE_DBGCHECK*/ + /* 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; +#if TCP_OVERSIZE + if (seg->next == NULL) { + /* the retransmitted segment is last in unsent, so reset unsent_oversize */ + pcb->unsent_oversize = 0; + } +#endif /* TCP_OVERSIZE */ + + ++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 < (2U * 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; +#if CHECKSUM_GEN_TCP + struct tcp_hdr *tcphdr; +#endif /* CHECKSUM_GEN_TCP */ + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + 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; + } +#if CHECKSUM_GEN_TCP + tcphdr = (struct tcp_hdr *)p->payload; + + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif /* CHECKSUM_GEN_TCP */ + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, + pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), 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 ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); + + 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 */ + char *d = ((char *)p->payload + TCP_HLEN); + /* Depending on whether the segment has already been sent (unacked) or not + (unsent), seg->p->payload points to the IP header or TCP header. + Ensure we copy the first TCP data byte: */ + pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len); + } + +#if CHECKSUM_GEN_TCP + tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len, + &pcb->local_ip, &pcb->remote_ip); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ +#if LWIP_NETIF_HWADDRHINT + ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, + 0, IP_PROTO_TCP, &pcb->addr_hint); +#else /* LWIP_NETIF_HWADDRHINT*/ + ipX_output(PCB_ISIPV6(pcb), 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/component/common/network/lwip/lwip_v1.5.0.beta/src/core/udp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/udp.c new file mode 100644 index 0000000..39a07e3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/core/udp.c @@ -0,0 +1,1164 @@ +/** + * @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/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/inet_chksum.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/icmp6.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "arch/perf.h" +#include "lwip/dhcp.h" + +#include + +#ifndef UDP_LOCAL_PORT_RANGE_START +/* From http://www.iana.org/assignments/port-numbers: + "The Dynamic and/or Private Ports are those from 49152 through 65535" */ +#define UDP_LOCAL_PORT_RANGE_START 0xc000 +#define UDP_LOCAL_PORT_RANGE_END 0xffff +#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START) +#endif + +/* last local UDP port */ +static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START; + +/* The list of UDP PCBs */ +/* exported in udp.h (was static) */ +struct udp_pcb *udp_pcbs; + +/** + * Initialize this module. + */ +void +udp_init(void) +{ +#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) + udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND()); +#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */ +} + +/** + * Allocate a new local UDP port. + * + * @return a new (free) local UDP port number + */ +static u16_t +udp_new_port(void) +{ + u16_t n = 0; + struct udp_pcb *pcb; + +again: + if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) { + udp_port = UDP_LOCAL_PORT_RANGE_START; + } + /* Check all PCBs. */ + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == udp_port) { + if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) { + return 0; + } + goto again; + } + } + return udp_port; +#if 0 + struct udp_pcb *ipcb = udp_pcbs; + while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == udp_port) { + /* port is already used by another udp_pcb */ + udp_port++; + /* restart scanning all udp pcbs */ + ipcb = udp_pcbs; + } else { + /* go on with next udp pcb */ + ipcb = ipcb->next; + } + } + if (ipcb != NULL) { + return 0; + } + return udp_port; +#endif +} + +/** + * 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 (p->payload pointing to the UDP header) + * @param inp network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb, *prev; + struct udp_pcb *uncon_pcb; + u16_t src, dest; + u8_t local_match; + u8_t broadcast; + u8_t for_us; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + /* Check minimum length (UDP header) */ + if (p->len < UDP_HLEN) { + /* 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 ? */ +#if LWIP_IPV6 + broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#else /* LWIP_IPV6 */ + broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp); +#endif /* LWIP_IPV6 */ + + 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 (")); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); + ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr()); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", 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 packet if + (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! + - inp->dhcp->pcb->remote == ANY or iphdr->src + (no need to check for IPv6 since the dhcp struct always uses IPv4) */ + if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) || + ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) { + 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 (")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if (pcb->local_port == dest) { + if ( +#if LWIP_IPV6 + ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) && + (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) || +#if LWIP_IPV6_MLD + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6_MLD */ + ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(pcb) && + (ip_current_header() != NULL) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(pcb, SOF_BROADCAST) && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#else /* IP_SOF_BROADCAST_RECV */ + (broadcast && + (ipX_addr_isany(0, &pcb->local_ip) || + ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) { +#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_PCB_IPVER_INPUT_MATCH(pcb) && + (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) || + ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) { + /* 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; + } +/* This part of code has been modified by ST's MCD Application Team */ +/* To use the UPnP responder for device discovery */ +#if LWIP_UPNP + if((local_match != 0) && (dest == 1900)) { + break; + } +#endif /* LWIP_UPNP */ + 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) { + for_us = 1; + } else { +#if LWIP_IPV6 + if (ip_current_is_v6()) { + for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0; + } else +#endif /* LWIP_IPV6 */ + { + for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr()); + } + } + if (for_us) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); +#if CHECKSUM_CHECK_UDP +#if LWIP_UDPLITE + if (ip_current_header_proto() == IP_PROTO_UDPLITE) { + /* Do the UDP Lite checksum */ + 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) */ + goto chkerr; + } + } + if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE, + p->tot_len, chklen, + ipX_current_src_addr(), ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } else +#endif /* LWIP_UDPLITE */ + { + if (udphdr->chksum != 0) { + if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len, + ipX_current_src_addr(), + ipX_current_dest_addr()) != 0) { + goto chkerr; + } + } + } +#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 || +#if LWIP_IPV6 + ip6_addr_ismulticast(ip6_current_dest_addr()) || +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(ip_current_dest_addr())) && + ip_get_option(pcb, SOF_REUSEADDR)) { + /* 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; + s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); + 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) && +#if LWIP_IPV6 + ((PCB_ISIPV6(mpcb) && + (ip6_addr_ismulticast(ip6_current_dest_addr()) || + ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) || + (!PCB_ISIPV6(mpcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) || + ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) || +#if LWIP_IGMP + ip_addr_ismulticast(ip_current_dest_addr()) || +#endif /* LWIP_IGMP */ +#if IP_SOF_BROADCAST_RECV + (broadcast && ip_get_option(mpcb, 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.ip4 != NULL) { + struct pbuf *q; + /* for that, move payload to IP header again */ + if (p_header_changed == 0) { + pbuf_header_force(p, hdrs_len); + 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, -hdrs_len); +#if LWIP_IPV6 + if (PCB_ISIPV6(mpcb)) { + mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); + } + } + } + } + } + } + } + if (p_header_changed) { + /* and move payload to UDP data again */ + pbuf_header(p, -hdrs_len); + } + } +#endif /* SO_REUSE && SO_REUSE_RXTOALL */ + /* callback */ + if (pcb->recv.ip4 != NULL) { + /* now the recv function is responsible for freeing p */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src); + } + else +#endif /* LWIP_IPV6 */ + { + pcb->recv.ip4(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 || LWIP_ICMP6 + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + if (!broadcast && +#if LWIP_IPV6 + !ip6_addr_ismulticast(ip6_current_dest_addr()) && +#endif /* LWIP_IPV6 */ + !ip_addr_ismulticast(ip_current_dest_addr())) { + /* move payload pointer back to ip header */ + pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN); + icmp_port_unreach(ip_current_is_v6(), p); + } +#endif /* LWIP_ICMP || LWIP_ICMP6 */ + 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"); + return; +#if CHECKSUM_CHECK_UDP +chkerr: + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, + ("udp_input: UDP (or 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); + PERF_STOP("udp_input"); +#endif /* CHECKSUM_CHECK_UDP */ +} + +/** + * 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 occurred. + * - 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 +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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port); +} + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +/** Same as udp_send() but with checksum + */ +err_t +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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port, + have_chksum, chksum); +} +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +/** + * 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 +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); +} + +/** Same as udp_sendto(), but with checksum */ +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) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct netif *netif; + ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip); + + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); + +#if LWIP_IPV6 || LWIP_IGMP + if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) { + /* For multicast, find a netif based on source address. */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + dst_ip_route = &pcb->local_ip; + } else +#endif /* LWIP_IPV6 */ + { +#if LWIP_IGMP + dst_ip_route = ip_2_ipX(&pcb->multicast_ip); +#endif /* LWIP_IGMP */ + } + } +#endif /* LWIP_IPV6 || LWIP_IGMP */ + + /* find the outgoing network interface for this packet */ + netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route); + + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip)); + LWIP_DEBUGF(UDP_DEBUG, ("\n")); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** + * 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 +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 && CHECKSUM_GEN_UDP + return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); +} + +/** Same as udp_sendto_if(), but with checksum */ +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) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + ip_addr_t *src_ip; + + /* PCB local address is IP_ANY_ADDR? */ +#if LWIP_IPV6 + if (PCB_ISIPV6(pcb)) { + if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) { + src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip))); + if (src_ip == NULL) { + /* No suitable source address was found. */ + return ERR_RTE; + } + } else { + /* use UDP PCB local IPv6 address as source address, if still valid. */ + if (netif_get_ip6_addr_match(netif, ipX_2_ip6(&pcb->local_ip)) < 0) { + /* Address isn't valid anymore. */ + return ERR_RTE; + } + src_ip = ipX_2_ip(&pcb->local_ip); + } + } + else +#endif /* LWIP_IPV6 */ + if (ip_addr_isany(ipX_2_ip(&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(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) { + /* local_ip doesn't match, drop the packet */ + return ERR_VAL; + } + /* use UDP PCB local IP address as source address */ + src_ip = ipX_2_ip(&(pcb->local_ip)); + } +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum, src_ip); +#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + return udp_sendto_if_src(pcb, p, dst_ip, dst_port, netif, src_ip); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +} + +/** Same as udp_sendto_if(), but with source address */ +err_t +udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, ip_addr_t *src_ip) +{ +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP + return udp_sendto_if_src_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0, src_ip); +} + +/** Same as udp_sendto_if_src(), but with checksum */ +err_t +udp_sendto_if_src_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, ip_addr_t *src_ip) +{ +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + struct udp_hdr *udphdr; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + u8_t ip_proto; + +#if IP_SOF_BROADCAST + /* broadcast filter? */ + if (!ip_get_option(pcb, SOF_BROADCAST) && +#if LWIP_IPV6 + !PCB_ISIPV6(pcb) && +#endif /* LWIP_IPV6 */ + 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, ipX_2_ip(&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 (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && +#if LWIP_IPV6 + ( +#if LWIP_IPV6_MLD + (PCB_ISIPV6(pcb) && + ip6_addr_ismulticast(ip_2_ip6(dst_ip))) || +#endif /* LWIP_IPV6_MLD */ + (!PCB_ISIPV6(pcb) && +#else /* LWIP_IPV6 */ + (( +#endif /* LWIP_IPV6 */ + ip_addr_ismulticast(dst_ip)))) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_IGMP */ + + 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 +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + chklen = UDP_HLEN; + } +#endif /* LWIP_CHECKSUM_ON_COPY */ + udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE, + q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); +#if LWIP_CHECKSUM_ON_COPY + 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 */ + + ip_proto = IP_PROTO_UDPLITE; + } 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 + /* Checksum is mandatory over IPv6. */ + if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + u16_t udpchksum; +#if LWIP_CHECKSUM_ON_COPY + if (have_chksum) { + u32_t acc; + udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, + q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + acc = udpchksum + (u16_t)~(chksum); + udpchksum = FOLD_U32T(acc); + } else +#endif /* LWIP_CHECKSUM_ON_COPY */ + { + udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len, + ip_2_ipX(src_ip), ip_2_ipX(dst_ip)); + } + + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udpchksum == 0x0000) { + udpchksum = 0xffff; + } + udphdr->chksum = udpchksum; + } +#endif /* CHECKSUM_GEN_UDP */ + ip_proto = IP_PROTO_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 (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto)); + /* output to IP */ + NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint)); + err = ipX_output_if_src(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif); + NETIF_SET_HWADDRHINT(netif, NULL); + + /* TODO: must this be increased even if error occurred? */ + 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 occurred. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +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 = ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(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 already bound to, unless *all* PCBs with that port have tha + REUSEADDR flag set. */ +#if SO_REUSE + else if (!ip_get_option(pcb, SOF_REUSEADDR) && + !ip_get_option(ipcb, SOF_REUSEADDR)) { +#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_PCB_IPVER_EQ(pcb, ipcb) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) || + ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) || + ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(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; + } + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr); + + /* no port specified? */ + if (port == 0) { + port = udp_new_port(); + if (port == 0) { + /* 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 ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", 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 +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, ipX_2_ip(&pcb->local_ip), pcb->local_port); + if (err != ERR_OK) { + return err; + } + } + + ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO +#if LWIP_IPV6 + if (!PCB_ISIPV6(pcb)) +#endif /* LWIP_IPV6 */ + { + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + struct netif *netif; + + if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", + ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip)))); + 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! */ + ipX_addr_copy(0, pcb->local_ip, netif->ip_addr); + } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) { + ipX_addr_set_any(0, &pcb->local_ip); + } + } +#endif + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to ")); + ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, + &pcb->remote_ip); + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_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 +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ipX_addr_set_any(PCB_ISIPV6(pcb), &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 which 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 +udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv.ip4 = 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 +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 * +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 LWIP_IPV6 +/** + * Create a UDP PCB for IPv6. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new_ip6(void) +{ + struct udp_pcb *pcb; + pcb = udp_new(); + ip_set_v6(pcb, 1); + return pcb; +} +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +/** + * Print UDP header information for debug purposes. + * + * @param udphdr pointer to the udp header in memory. + */ +void +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/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api.h new file mode 100644 index 0000000..fa9cfa5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api.h @@ -0,0 +1,364 @@ +/* + * 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_HDR_API_H +#define LWIP_HDR_API_H + +#include "lwip/opt.h" + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +/* don't export the netconn functions when socket API is enabled but netconn API is disabled */ +#if LWIP_NETCONN +#define LWIP_NETCONN_SCOPE +#else /* LWIP_NETCONN */ +#define LWIP_NETCONN_SCOPE static +#endif /* LWIP_NETCONN */ + +#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 + +// Evan add for port netconn api +#define port_netconn_recv(conn , buf, ret) do{ret = netconn_recv(conn, &buf);}while(0); +#define port_netconn_accept(conn , newconn, ret) do{ret = netconn_accept(conn, &newconn);}while(0); +// Evan add end + +/* 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 +#if LWIP_IPV6 +/** If this flag is set then only IPv6 communication is allowed on the + netconn. As per RFC#3493 this features defaults to OFF allowing + dual-stack usage by default. */ +#define NETCONN_FLAG_IPV6_V6ONLY 0x20 +#endif /* LWIP_IPV6 */ + + +/* Helpers to process several netconn_types by the same code */ +#define NETCONNTYPE_GROUP(t) ((t)&0xF0) +#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0) +#if LWIP_IPV6 +#define NETCONN_TYPE_IPV6 0x08 +#define NETCONNTYPE_ISIPV6(t) ((t)&0x08) +#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM) +#else /* LWIP_IPV6 */ +#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM) +#endif /* LWIP_IPV6 */ + +/** Protocol family and type of the netconn */ +enum netconn_type { + NETCONN_INVALID = 0, + /* NETCONN_TCP Group */ + NETCONN_TCP = 0x10, +#if LWIP_IPV6 + NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */, +#endif /* LWIP_IPV6 */ + /* NETCONN_UDP Group */ + NETCONN_UDP = 0x20, + NETCONN_UDPLITE = 0x21, + NETCONN_UDPNOCHKSUM = 0x22, +#if LWIP_IPV6 + NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */, + NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */, + NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */, +#endif /* LWIP_IPV6 */ + /* NETCONN_RAW Group */ + NETCONN_RAW = 0x40 +#if LWIP_IPV6 + , + NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */ +#endif /* LWIP_IPV6 */ +}; + +/** 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 || (LWIP_IPV6 && LWIP_IPV6_MLD) +/** Used for netconn_join_leave_group() */ +enum netconn_igmp { + NETCONN_JOIN, + NETCONN_LEAVE +}; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +/* 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; +#if !LWIP_NETCONN_SEM_PER_THREAD + /** sem that is used to synchronously execute functions in the core context */ + sys_sem_t op_completed; +#endif + /** 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_SNDTIMEO + /** timeout to wait for sending data (which means enqueueing data for sending + in internal buffers) in milliseconds */ + s32_t send_timeout; +#endif /* LWIP_SO_RCVTIMEO */ +#if LWIP_SO_RCVTIMEO + /** timeout in milliseconds 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 */ + int 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); + +// Realtek add +err_t netconn_abort(struct netconn *conn); +// Realtek add end + +/* 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) +LWIP_NETCONN_SCOPE struct +netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, + netconn_callback callback); +LWIP_NETCONN_SCOPE err_t netconn_delete(struct netconn *conn); +/** Get the type of a netconn (as enum netconn_type). */ +#define netconn_type(conn) (conn->type) + +LWIP_NETCONN_SCOPE 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) + +LWIP_NETCONN_SCOPE err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port); +LWIP_NETCONN_SCOPE err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port); +LWIP_NETCONN_SCOPE err_t netconn_disconnect (struct netconn *conn); +LWIP_NETCONN_SCOPE 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) +LWIP_NETCONN_SCOPE err_t netconn_accept(struct netconn *conn, struct netconn **new_conn); +LWIP_NETCONN_SCOPE err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf); +LWIP_NETCONN_SCOPE err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf); +LWIP_NETCONN_SCOPE void netconn_recved(struct netconn *conn, u32_t length); +LWIP_NETCONN_SCOPE err_t netconn_sendto(struct netconn *conn, struct netbuf *buf, + ip_addr_t *addr, u16_t port); +LWIP_NETCONN_SCOPE err_t netconn_send(struct netconn *conn, struct netbuf *buf); +LWIP_NETCONN_SCOPE err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size, + u8_t apiflags, size_t *bytes_written); +#define netconn_write(conn, dataptr, size, apiflags) \ + netconn_write_partly(conn, dataptr, size, apiflags, NULL) +LWIP_NETCONN_SCOPE err_t netconn_close(struct netconn *conn); +LWIP_NETCONN_SCOPE err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); + +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +LWIP_NETCONN_SCOPE 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 || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if LWIP_DNS +err_t netconn_gethostbyname(const char *name, ip_addr_t *addr); +#endif /* LWIP_DNS */ +#if LWIP_IPV6 + +#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL) +#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL) +#if LWIP_IPV6_MLD +#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \ + netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\ + ERR_VAL) +#endif /* LWIP_IPV6_MLD*/ +#endif /* LWIP_IPV6 */ + +#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_SNDTIMEO +/** Set the send timeout in milliseconds */ +#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout)) +/** Get the send timeout in milliseconds */ +#define netconn_get_sendtimeout(conn) ((conn)->send_timeout) +#endif /* LWIP_SO_SNDTIMEO */ +#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*/ + +#if LWIP_NETCONN_SEM_PER_THREAD +void netconn_thread_init(void); +void netconn_thread_cleanup(void); +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define netconn_thread_init() +#define netconn_thread_cleanup() +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_API_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api_msg.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api_msg.h new file mode 100644 index 0000000..3505bf3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/api_msg.h @@ -0,0 +1,204 @@ +/* + * 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_HDR_API_MSG_H +#define LWIP_HDR_API_MSG_H + +#include "lwip/opt.h" + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +/* don't export the netconn functions when socket API is enabled but netconn API is disabled */ +#if LWIP_NETCONN +#define LWIP_NETCONN_SCOPE +#else /* LWIP_NETCONN */ +#define LWIP_NETCONN_SCOPE static +#endif /* LWIP_NETCONN */ + +#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 + +#if LWIP_MPU_COMPATIBLE +#define API_MSG_M_DEF(m) m +#else /* LWIP_MPU_COMPATIBLE */ +#define API_MSG_M_DEF(m) *m +#endif /* LWIP_MPU_COMPATIBLE */ + +/* 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 lwip_netconn_do_send */ + struct netbuf *b; + /** used for lwip_netconn_do_newconn */ + struct { + u8_t proto; + } n; + /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */ + struct { + ip_addr_t API_MSG_M_DEF(ipaddr); + u16_t port; + } bc; + /** used for lwip_netconn_do_getaddr */ + struct { + ipX_addr_t API_MSG_M_DEF(ipaddr); + u16_t API_MSG_M_DEF(port); + u8_t local; + } ad; + /** used for lwip_netconn_do_write */ + struct { + const void *dataptr; + size_t len; + u8_t apiflags; +#if LWIP_SO_SNDTIMEO + u32_t time_started; +#endif /* LWIP_SO_SNDTIMEO */ + } w; + /** used for lwip_netconn_do_recv */ + struct { + u32_t len; + } r; + /** used for lwip_netconn_do_close (/shutdown) */ + struct { + u8_t shut; + } sd; +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** used for lwip_netconn_do_join_leave_group */ + struct { + ipX_addr_t API_MSG_M_DEF(multiaddr); + ipX_addr_t API_MSG_M_DEF(netif_addr); + enum netconn_igmp join_or_leave; + } jl; +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#if TCP_LISTEN_BACKLOG + struct { + u8_t backlog; + } lb; +#endif /* TCP_LISTEN_BACKLOG */ + } msg; +#if LWIP_NETCONN_SEM_PER_THREAD + sys_sem_t* op_completed_sem; +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ +}; + +#if LWIP_NETCONN_SEM_PER_THREAD +#define LWIP_API_MSG_SEM(msg) ((msg)->op_completed_sem) +#else /* LWIP_NETCONN_SEM_PER_THREAD */ +#define LWIP_API_MSG_SEM(msg) (&(msg)->conn->op_completed) +#endif /* LWIP_NETCONN_SEM_PER_THREAD */ + + +/** 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 lwip_netconn_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). + lwip_netconn_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 */ +#if LWIP_MPU_COMPATIBLE + char name[DNS_MAX_NAME_LENGTH]; +#else /* LWIP_MPU_COMPATIBLE */ + const char *name; +#endif /* LWIP_MPU_COMPATIBLE */ + /** Rhe resolved address is stored here */ + ip_addr_t API_MSG_M_DEF(addr); + /** This semaphore is posted when the name is resolved, the application thread + should wait on it. */ + sys_sem_t API_MSG_M_DEF(sem); + /** Errors are given back here */ + err_t API_MSG_M_DEF(err); +}; +#endif /* LWIP_DNS */ + +LWIP_NETCONN_SCOPE void lwip_netconn_do_newconn ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_delconn ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_bind ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_connect ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_disconnect ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_listen ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_send ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_recv ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_write ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_getaddr ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_close ( struct api_msg_msg *msg); +LWIP_NETCONN_SCOPE void lwip_netconn_do_shutdown ( struct api_msg_msg *msg); +#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) +LWIP_NETCONN_SCOPE void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg); +#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +#if LWIP_DNS +LWIP_NETCONN_SCOPE void lwip_netconn_do_gethostbyname(void *arg); +#endif /* LWIP_DNS */ + +LWIP_NETCONN_SCOPE struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback); +LWIP_NETCONN_SCOPE void netconn_free(struct netconn *conn); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_API_MSG_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/arch.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/arch.h new file mode 100644 index 0000000..4cdb764 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/arch.h @@ -0,0 +1,233 @@ +/* + * 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_HDR_ARCH_H +#define LWIP_HDR_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_STRUCT +#define PACK_STRUCT_STRUCT +#endif /* PACK_STRUCT_STRUCT */ + +#ifndef PACK_STRUCT_FIELD +#define PACK_STRUCT_FIELD(x) x +#endif /* PACK_STRUCT_FIELD */ + +/* Used for struct fields of u8_t, + * where some compilers warn that packing is not necessary */ +#ifndef PACK_STRUCT_FLD_8 +#define PACK_STRUCT_FLD_8(x) PACK_STRUCT_FIELD(x) +#endif /* PACK_STRUCT_FLD_8 */ + +/* Used for struct fields of that are packed structs themself, + * where some compilers warn that packing is not necessary */ +#ifndef PACK_STRUCT_FLD_S +#define PACK_STRUCT_FLD_S(x) PACK_STRUCT_FIELD(x) +#endif /* PACK_STRUCT_FLD_S */ + + +#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 */ + +#ifndef errno +extern int errno; +#endif + +#endif /* LWIP_PROVIDE_ERRNO */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ARCH_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/autoip.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/autoip.h new file mode 100644 index 0000000..9a062c6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/autoip.h @@ -0,0 +1,121 @@ +/** + * @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_HDR_AUTOIP_H +#define LWIP_HDR_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 */ +}; + + +#define autoip_init() /* Compatibility define, no init needed. */ + +/** Set a struct autoip allocated by the application to work with */ +void autoip_set_struct(struct netif *netif, struct autoip *autoip); + +/** Remove a struct autoip previously set to the netif using autoip_set_struct() */ +#define autoip_remove_struct(netif) do { (netif)->autoip = NULL; } while (0) + +/** 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_HDR_AUTOIP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/debug.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/debug.h new file mode 100644 index 0000000..8844e73 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/debug.h @@ -0,0 +1,102 @@ +/* + * 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_HDR_DEBUG_H +#define LWIP_HDR_DEBUG_H + +#include "lwip/arch.h" +#include "lwip/opt.h" +#include //Realtek add +/** 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_PLATFORM_DIAG printf //Realtek add +#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_HDR_DEBUG_H */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/def.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/def.h new file mode 100644 index 0000000..c985a87 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/def.h @@ -0,0 +1,123 @@ +/* + * 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_HDR_DEF_H +#define LWIP_HDR_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 + +/* 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_HDR_DEF_H */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp.h new file mode 100644 index 0000000..6cfbf90 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp.h @@ -0,0 +1,242 @@ +/** @file + */ + +#ifndef LWIP_HDR_DHCP_H +#define LWIP_HDR_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; + ip_addr_t offered_bc_addr;//Evan add for used old version lwip api in lwip_netconf.c + + 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) */ +#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_FLD_8(u8_t op); + PACK_STRUCT_FLD_8(u8_t htype); + PACK_STRUCT_FLD_8(u8_t hlen); + PACK_STRUCT_FLD_8(u8_t hops); + PACK_STRUCT_FIELD(u32_t xid); + PACK_STRUCT_FIELD(u16_t secs); + PACK_STRUCT_FIELD(u16_t flags); + PACK_STRUCT_FLD_S(ip_addr_p_t ciaddr); + PACK_STRUCT_FLD_S(ip_addr_p_t yiaddr); + PACK_STRUCT_FLD_S(ip_addr_p_t siaddr); + PACK_STRUCT_FLD_S(ip_addr_p_t giaddr); + PACK_STRUCT_FLD_8(u8_t chaddr[DHCP_CHADDR_LEN]); + PACK_STRUCT_FLD_8(u8_t sname[DHCP_SNAME_LEN]); + PACK_STRUCT_FLD_8(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_FLD_8(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); +err_t dhcp_release_unicast(struct netif *netif); //Realtek add +/** 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 +/* not yet implemented #define DHCP_INFORMING 7*/ +#define DHCP_CHECKING 8 +/* not yet implemented #define DHCP_PERMANENT 9*/ +#define DHCP_BOUND 10 +#define DHCP_RELEASING 11 //Realtek modified +#define DHCP_BACKING_OFF 12 + +/** AUTOIP cooperation 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_HDR_DHCP_H*/ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp6.h new file mode 100644 index 0000000..345bcf0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dhcp6.h @@ -0,0 +1,58 @@ +/** + * @file + * + * IPv6 address autoconfiguration as per RFC 4862. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * IPv6 address autoconfiguration as per RFC 4862. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_IP6_DHCP6_H +#define LWIP_HDR_IP6_DHCP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */ + + +struct dhcp6 +{ + /*TODO: implement DHCP6*/ +}; + +#endif /* LWIP_IPV6_DHCP6 */ + +#endif /* LWIP_HDR_IP6_DHCP6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dns.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dns.h new file mode 100644 index 0000000..eb81c90 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/dns.h @@ -0,0 +1,117 @@ +/** + * 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_HDR_DNS_H +#define LWIP_HDR_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 */ + +#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_HDR_DNS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/err.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/err.h new file mode 100644 index 0000000..a48430d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/err.h @@ -0,0 +1,87 @@ +/* + * 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_HDR_ERR_H +#define LWIP_HDR_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_USE -8 /* Address in use. */ +#define ERR_ISCONN -9 /* Already connected. */ + +#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN) + +#define ERR_CONN -10 /* Not connected. */ +#define ERR_IS_FATAL_LISTENCONNECT(e) ((e) < ERR_CONN) + +#define ERR_ABRT -11 /* Connection aborted. */ +#define ERR_RST -12 /* Connection reset. */ +#define ERR_CLSD -13 /* Connection closed. */ + +#define ERR_ARG -14 /* Illegal argument. */ + +#define ERR_IF -15 /* Low-level netif error */ + + +#ifdef LWIP_DEBUG +extern const char *lwip_strerr(err_t err); +#else +#define lwip_strerr(x) "" +#endif /* LWIP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ERR_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ethip6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ethip6.h new file mode 100644 index 0000000..31fed18 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ethip6.h @@ -0,0 +1,68 @@ +/** + * @file + * + * Ethernet output for IPv6. Uses ND tables for link-layer addressing. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_ETHIP6_H +#define LWIP_HDR_ETHIP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 && LWIP_ETHERNET */ + +#endif /* LWIP_HDR_ETHIP6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp.h new file mode 100644 index 0000000..0ca6522 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp.h @@ -0,0 +1,125 @@ +/* + * 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_HDR_ICMP_H +#define LWIP_HDR_ICMP_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" + +#if LWIP_IPV6 && LWIP_ICMP6 +#include "lwip/icmp6.h" +#endif + +#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 split 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. + */ +PACK_STRUCT_BEGIN +struct icmp_echo_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(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 + +#define ICMPH_TYPE(hdr) ((hdr)->type) +#define ICMPH_CODE(hdr) ((hdr)->code) + +/** Combines type and code to an u16_t */ +#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); +void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t); +void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t); + +#endif /* LWIP_ICMP */ + +#if (LWIP_IPV6 && LWIP_ICMP6) +#define icmp_port_unreach(isipv6, pbuf) ((isipv6) ? \ + icmp6_dest_unreach(pbuf, ICMP6_DUR_PORT) : \ + icmp_dest_unreach(pbuf, ICMP_DUR_PORT)) +#elif LWIP_ICMP +#define icmp_port_unreach(isipv6, pbuf) icmp_dest_unreach(pbuf, ICMP_DUR_PORT) +#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ +#define icmp_port_unreach(isipv6, pbuf) +#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_ICMP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp6.h new file mode 100644 index 0000000..9d57103 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/icmp6.h @@ -0,0 +1,152 @@ +/** + * @file + * + * IPv6 version of ICMP, as per RFC 4443. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_ICMP6_H +#define LWIP_HDR_ICMP6_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum icmp6_type { + ICMP6_TYPE_DUR = 1, /* Destination unreachable */ + ICMP6_TYPE_PTB = 2, /* Packet too big */ + ICMP6_TYPE_TE = 3, /* Time exceeded */ + ICMP6_TYPE_PP = 4, /* Parameter problem */ + ICMP6_TYPE_PE1 = 100, /* Private experimentation */ + ICMP6_TYPE_PE2 = 101, /* Private experimentation */ + ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */ + + ICMP6_TYPE_EREQ = 128, /* Echo request */ + ICMP6_TYPE_EREP = 129, /* Echo reply */ + ICMP6_TYPE_MLQ = 130, /* Multicast listener query */ + ICMP6_TYPE_MLR = 131, /* Multicast listener report */ + ICMP6_TYPE_MLD = 132, /* Multicast listener done */ + ICMP6_TYPE_RS = 133, /* Router solicitation */ + ICMP6_TYPE_RA = 134, /* Router advertisement */ + ICMP6_TYPE_NS = 135, /* Neighbor solicitation */ + ICMP6_TYPE_NA = 136, /* Neighbor advertisement */ + ICMP6_TYPE_RD = 137, /* Redirect */ + ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */ + ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */ + ICMP6_TYPE_MRT = 153, /* Multicast router termination */ + ICMP6_TYPE_PE3 = 200, /* Private experimentation */ + ICMP6_TYPE_PE4 = 201, /* Private experimentation */ + ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */ +}; + +enum icmp6_dur_code { + ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */ + ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */ + ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */ + ICMP6_DUR_ADDRESS = 3, /* Address unreachable */ + ICMP6_DUR_PORT = 4, /* Port unreachable */ + ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */ + ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */ +}; + +enum icmp6_te_code { + ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */ + ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */ +}; + +enum icmp6_pp_code { + ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */ + ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */ + ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */ +}; + +/** This is the standard ICMP6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t data); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** This is the ICMP6 header adapted for echo req/resp. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct icmp6_echo_hdr { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(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 + + +#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +void icmp6_input(struct pbuf *p, struct netif *inp); +void icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c); +void icmp6_packet_too_big(struct pbuf *p, u32_t mtu); +void icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c); +void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer); + +#endif /* LWIP_ICMP6 && LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* LWIP_HDR_ICMP6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/igmp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/igmp.h new file mode 100644 index 0000000..4687b1f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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_HDR_IGMP_H +#define LWIP_HDR_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); +err_t igmp_start(struct netif *netif); +err_t igmp_stop(struct netif *netif); +void igmp_report_groups(struct netif *netif); +struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr); +void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest); +err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr); +void igmp_tmr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IGMP */ + +#endif /* LWIP_HDR_IGMP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet.h new file mode 100644 index 0000000..2895d61 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet.h @@ -0,0 +1,121 @@ +/* + * 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_HDR_INET_H +#define LWIP_HDR_INET_H + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/ip_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If your port already typedef's in_addr_t, define IN_ADDR_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_addr_t) && !defined(IN_ADDR_T_DEFINED) +typedef u32_t in_addr_t; +#endif +/** For compatibility with BSD code */ +struct in_addr { + in_addr_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 + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN IP4ADDR_STRLEN_MAX +#endif +#if LWIP_IPV6 +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN IP6ADDR_STRLEN_MAX +#endif +#endif + +#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_HDR_INET_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet6.h new file mode 100644 index 0000000..afaa923 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet6.h @@ -0,0 +1,92 @@ +/** + * @file + * + * INET v6 addresses. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_INET6_H +#define LWIP_HDR_INET6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** For compatibility with BSD code */ +struct in6_addr { + union { + u8_t u8_addr[16]; + u32_t u32_addr[4]; + } un; +#define s6_addr un.u8_addr +}; + +#define IN6ADDR_ANY_INIT {0,0,0,0} +#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)} + + +#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \ + (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \ + (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \ + (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];} +#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \ + (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \ + (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \ + (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];} +/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */ +#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr)) + +/* directly map this to the lwip internal functions */ +#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr) +#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr)) +#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen) + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_INET6_H */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet_chksum.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet_chksum.h new file mode 100644 index 0000000..4171d0a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/inet_chksum.h @@ -0,0 +1,112 @@ +/* + * 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_HDR_INET_CHKSUM_H +#define LWIP_HDR_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); +u16_t inet_chksum_pbuf(struct pbuf *p); +u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip_addr_t *src, ip_addr_t *dest); +u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, + u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest); +#if LWIP_CHKSUM_COPY_ALGORITHM +u16_t lwip_chksum_copy(void *dst, const void *src, u16_t len); +#endif /* LWIP_CHKSUM_COPY_ALGORITHM */ + +#if LWIP_IPV6 +u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len, + ip6_addr_t *src, ip6_addr_t *dest); +u16_t ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len, + u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest); + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest))) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + ((isipv6) ? \ + ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest))) + +#else /* LWIP_IPV6 */ + +#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \ + inet_chksum_pseudo(p, proto, proto_len, src, dest) +#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \ + inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, dest) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_INET_H */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/init.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/init.h new file mode 100644 index 0000000..0a35993 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/init.h @@ -0,0 +1,72 @@ +/* + * 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_HDR_INIT_H +#define LWIP_HDR_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 1U +/** 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 0U + +/** 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); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_INIT_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4.h new file mode 100644 index 0000000..c49a771 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4.h @@ -0,0 +1,150 @@ +/* + * 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_HDR_IP4_H +#define LWIP_HDR_IP4_H + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_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 + + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip_hdr { + /* version / header length */ + PACK_STRUCT_FLD_8(u8_t _v_hl); + /* type of service */ + PACK_STRUCT_FLD_8(u8_t _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 0x8000U /* reserved fragment flag */ +#define IP_DF 0x4000U /* don't fragment flag */ +#define IP_MF 0x2000U /* more fragments flag */ +#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */ + /* time to live */ + PACK_STRUCT_FLD_8(u8_t _ttl); + /* protocol*/ + PACK_STRUCT_FLD_8(u8_t _proto); + /* checksum */ + PACK_STRUCT_FIELD(u16_t _chksum); + /* source and destination IP addresses */ + PACK_STRUCT_FLD_S(ip_addr_p_t src); + PACK_STRUCT_FLD_S(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) ((hdr)->_v_hl >> 4) +#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f) +#define IPH_TOS(hdr) ((hdr)->_tos) +#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_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl)) +#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (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) + + +#define ip_init() /* Compatibility define, no init needed. */ +struct netif *ip_route(ip_addr_t *dest); +err_t ip_input(struct pbuf *p, struct netif *inp); +err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto); +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); +err_t ip_output_if_src(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, + u8_t ttl, u8_t tos, u8_t proto, struct netif *netif); +#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); +#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); +err_t ip_output_if_opt_src(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 */ + +#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL) + +#if IP_DEBUG +void ip_debug_print(struct pbuf *p); +#else +#define ip_debug_print(p) +#endif /* IP_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_H */ + + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4_addr.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4_addr.h new file mode 100644 index 0000000..6f6318b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip4_addr.h @@ -0,0 +1,247 @@ +/* + * 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_HDR_IP4_ADDR_H +#define LWIP_HDR_IP4_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); + +#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr) +u8_t ip4_addr_netmask_valid(u32_t netmask); + +#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)) + +#define IP4ADDR_STRLEN_MAX 16 +#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX + +/** For backwards compatibility */ +#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr) + +u32_t ipaddr_addr(const char *cp); +int ipaddr_aton(const char *cp, ip_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ipaddr_ntoa(const ip_addr_t *addr); +char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_ADDR_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6.h new file mode 100644 index 0000000..a7b264f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6.h @@ -0,0 +1,198 @@ +/** + * @file + * + * IPv6 layer. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_H +#define LWIP_HDR_IP6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/ip6_addr.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" + +#include "lwip/err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define IP6_HLEN 40 + +#define IP6_NEXTH_HOPBYHOP 0 +#define IP6_NEXTH_TCP 6 +#define IP6_NEXTH_UDP 17 +#define IP6_NEXTH_ENCAPS 41 +#define IP6_NEXTH_ROUTING 43 +#define IP6_NEXTH_FRAGMENT 44 +#define IP6_NEXTH_ICMP6 58 +#define IP6_NEXTH_NONE 59 +#define IP6_NEXTH_DESTOPTS 60 +#define IP6_NEXTH_UDPLITE 136 + + +/* The IPv6 header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hdr { + /* version / traffic class / flow label */ + PACK_STRUCT_FIELD(u32_t _v_tc_fl); + /* payload length */ + PACK_STRUCT_FIELD(u16_t _plen); + /* next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /* hop limit */ + PACK_STRUCT_FLD_8(u8_t _hoplim); + /* source and destination IP addresses */ + PACK_STRUCT_FLD_S(ip6_addr_p_t src); + PACK_STRUCT_FLD_S(ip6_addr_p_t dest); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Hop-by-hop router alert option. */ +#define IP6_HBH_HLEN 8 +#define IP6_PAD1_OPTION 0 +#define IP6_PADN_ALERT_OPTION 1 +#define IP6_ROUTER_ALERT_OPTION 5 +#define IP6_ROUTER_ALERT_VALUE_MLD 0 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_hbh_hdr { + /* next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /* header length */ + PACK_STRUCT_FLD_8(u8_t _hlen); + /* router alert option type */ + PACK_STRUCT_FLD_8(u8_t _ra_opt_type); + /* router alert option data len */ + PACK_STRUCT_FLD_8(u8_t _ra_opt_dlen); + /* router alert option data */ + PACK_STRUCT_FIELD(u16_t _ra_opt_data); + /* PadN option type */ + PACK_STRUCT_FLD_8(u8_t _padn_opt_type); + /* PadN option data len */ + PACK_STRUCT_FLD_8(u8_t _padn_opt_dlen); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/* Fragment header. */ +#define IP6_FRAG_HLEN 8 +#define IP6_FRAG_OFFSET_MASK 0xfff8 +#define IP6_FRAG_MORE_FLAG 0x0001 +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_frag_hdr { + /* next header */ + PACK_STRUCT_FLD_8(u8_t _nexth); + /* reserved */ + PACK_STRUCT_FLD_8(u8_t reserved); + /* fragment offset */ + PACK_STRUCT_FIELD(u16_t _fragment_offset); + /* fragmented packet identification */ + PACK_STRUCT_FIELD(u32_t _identification); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f) +#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff) +#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff) +#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen)) +#define IP6H_NEXTH(hdr) ((hdr)->_nexth) +#define IP6H_NEXTH_P(hdr) ((u8_t *)(hdr) + 6) +#define IP6H_HOPLIM(hdr) ((hdr)->_hoplim) + +#define IP6H_VTCFL_SET(hdr, v, tc, fl) (hdr)->_v_tc_fl = (htonl((((u32_t)(v)) << 28) | (((u32_t)(tc)) << 20) | (fl))) +#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen) +#define IP6H_NEXTH_SET(hdr, nexth) (hdr)->_nexth = (nexth) +#define IP6H_HOPLIM_SET(hdr, hl) (hdr)->_hoplim = (u8_t)(hl) + + +#define ip6_init() /* TODO should we init current addresses and header pointer? */ +struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest); +ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest); +err_t ip6_input(struct pbuf *p, struct netif *inp); +err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth); +err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +err_t ip6_output_if_src(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest, + u8_t hl, u8_t tc, u8_t nexth, struct netif *netif); +#if LWIP_NETIF_HWADDRHINT +err_t ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest, + u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint); +#endif /* LWIP_NETIF_HWADDRHINT */ +#if LWIP_IPV6_MLD +err_t ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value); +#endif /* LWIP_IPV6_MLD */ + +#define ip6_netif_get_local_ipX(netif, dest) (((netif) != NULL) ? \ + ip6_2_ipX(ip6_select_source_address(netif, dest)) : NULL) + +#if IP6_DEBUG +void ip6_debug_print(struct pbuf *p); +#else +#define ip6_debug_print(p) +#endif /* IP6_DEBUG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_IP6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_addr.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_addr.h new file mode 100644 index 0000000..ace219c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_addr.h @@ -0,0 +1,289 @@ +/** + * @file + * + * IPv6 addresses. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * Structs and macros for handling IPv6 addresses. + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_ADDR_H +#define LWIP_HDR_IP6_ADDR_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* This is the aligned version of ip6_addr_t, + used as local variable, on the stack, etc. */ +struct ip6_addr { + u32_t addr[4]; +}; + +/* This is the packed version of ip6_addr_t, + used in network headers that are itself packed */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ip6_addr_packed { + PACK_STRUCT_FIELD(u32_t addr[4]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** ip6_addr_t uses a struct for convenience only, so that the same defines can + * operate both on ip6_addr_t as well as on ip6_addr_p_t. */ +typedef struct ip6_addr ip6_addr_t; +typedef struct ip6_addr_packed ip6_addr_p_t; + + +/** IP6_ADDR_ANY can be used as a fixed IPv6 address + * for the wildcard + */ +extern const ip6_addr_t ip6_addr_any; +#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any) + + +#if BYTE_ORDER == BIG_ENDIAN +/** Set an IPv6 partial address given by byte-parts. */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \ + ((u32_t)((b) & 0xff) << 16) | \ + ((u32_t)((c) & 0xff) << 8) | \ + (u32_t)((d) & 0xff) +#else +/** Set an IPv6 partial address given by byte-parts. +Little-endian version, stored in network order (no htonl). */ +#define IP6_ADDR(ip6addr, index, a,b,c,d) \ + (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \ + ((u32_t)((c) & 0xff) << 16) | \ + ((u32_t)((b) & 0xff) << 8) | \ + (u32_t)((a) & 0xff) +#endif + +/** Access address in 16-bit block */ +#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff) +#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff) +#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff) +#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff) +#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3])) & 0xffff) + +/** Copy IPv6 address - faster than ip6_addr_set: no NULL check */ +#define ip6_addr_copy(dest, src) do{(dest).addr[0] = (src).addr[0]; \ + (dest).addr[1] = (src).addr[1]; \ + (dest).addr[2] = (src).addr[2]; \ + (dest).addr[3] = (src).addr[3];}while(0) +/** Safely copy one IPv6 address to another (src may be NULL) */ +#define ip6_addr_set(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : (src)->addr[0]; \ + (dest)->addr[1] = (src) == NULL ? 0 : (src)->addr[1]; \ + (dest)->addr[2] = (src) == NULL ? 0 : (src)->addr[2]; \ + (dest)->addr[3] = (src) == NULL ? 0 : (src)->addr[3];}while(0) + +/** Set complete address to zero */ +#define ip6_addr_set_zero(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = 0;}while(0) + +/** Set address to ipv6 'any' (no need for htonl()) */ +#define ip6_addr_set_any(ip6addr) ip6_addr_set_zero(ip6addr) +/** Set address to ipv6 loopback address */ +#define ip6_addr_set_loopback(ip6addr) do{(ip6addr)->addr[0] = 0; \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) +/** Safely copy one IPv6 address to another and change byte order + * from host- to network-order. */ +#define ip6_addr_set_hton(dest, src) do{(dest)->addr[0] = (src) == NULL ? 0 : htonl((src)->addr[0]); \ + (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \ + (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \ + (dest)->addr[3] = (src) == NULL ? 0 : htonl((src)->addr[3]);}while(0) + + +/** + * Determine if two IPv6 address are on the same network. + * + * @arg addr1 IPv6 address 1 + * @arg addr2 IPv6 address 2 + * @return !0 if the network identifiers of both address match + */ +#define ip6_addr_netcmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1])) + +#define ip6_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) + +#define ip6_get_subnet_id(ip6addr) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL) + +#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \ + (((ip6addr)->addr[0] == 0) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == 0) && \ + ((ip6addr)->addr[3] == 0))) + +#define ip6_addr_isloopback(ip6addr) (((ip6addr)->addr[0] == 0UL) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isglobal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xe0000000UL)) == PP_HTONL(0x20000000UL)) + +#define ip6_addr_islinklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfe800000UL)) + +#define ip6_addr_issitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffc00000UL)) == PP_HTONL(0xfec00000UL)) + +#define ip6_addr_isuniquelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xfe000000UL)) == PP_HTONL(0xfc000000UL)) + +#define ip6_addr_ismulticast(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) +#define ip6_addr_multicast_transient_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00100000UL)) +#define ip6_addr_multicast_prefix_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00200000UL)) +#define ip6_addr_multicast_rendezvous_flag(ip6addr) ((ip6addr)->addr[0] & PP_HTONL(0x00400000UL)) +#define ip6_addr_multicast_scope(ip6addr) ((htonl((ip6addr)->addr[0]) >> 16) & 0xf) +#define IP6_MULTICAST_SCOPE_RESERVED 0x0 +#define IP6_MULTICAST_SCOPE_RESERVED0 0x0 +#define IP6_MULTICAST_SCOPE_INTERFACE_LOCAL 0x1 +#define IP6_MULTICAST_SCOPE_LINK_LOCAL 0x2 +#define IP6_MULTICAST_SCOPE_RESERVED3 0x3 +#define IP6_MULTICAST_SCOPE_ADMIN_LOCAL 0x4 +#define IP6_MULTICAST_SCOPE_SITE_LOCAL 0x5 +#define IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL 0x8 +#define IP6_MULTICAST_SCOPE_GLOBAL 0xe +#define IP6_MULTICAST_SCOPE_RESERVEDF 0xf +#define ip6_addr_ismulticast_iflocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff010000UL)) +#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL)) +#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL)) +#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL)) +#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL)) +#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff0e0000UL)) + +/* TODO define get/set for well-know multicast addresses, e.g. ff02::1 */ +#define ip6_addr_isallnodes_iflocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff010000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) + +#define ip6_addr_isallnodes_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000001UL))) +#define ip6_addr_set_allnodes_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000001UL);}while(0) + +#define ip6_addr_isallrouters_linklocal(ip6addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0UL) && \ + ((ip6addr)->addr[2] == 0UL) && \ + ((ip6addr)->addr[3] == PP_HTONL(0x00000002UL))) +#define ip6_addr_set_allrouters_linklocal(ip6addr) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = 0; \ + (ip6addr)->addr[3] = PP_HTONL(0x00000002UL);}while(0) + +#define ip6_addr_issolicitednode(ip6addr) ( ((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + (((ip6addr)->addr[3] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ) + +#define ip6_addr_set_solicitednode(ip6addr, if_id) do{(ip6addr)->addr[0] = PP_HTONL(0xff020000UL); \ + (ip6addr)->addr[1] = 0; \ + (ip6addr)->addr[2] = PP_HTONL(0x00000001UL); \ + (ip6addr)->addr[3] = (PP_HTONL(0xff000000UL) | (if_id));}while(0) + +#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \ + ((ip6addr)->addr[1] == 0) && \ + ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \ + ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3]))) + +/* IPv6 address states. */ +#define IP6_ADDR_INVALID 0x00 +#define IP6_ADDR_TENTATIVE 0x08 +#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */ +#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */ +#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */ +#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */ +#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */ +#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */ +#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */ +#define IP6_ADDR_VALID 0x10 +#define IP6_ADDR_PREFERRED 0x30 +#define IP6_ADDR_DEPRECATED 0x50 + +#define ip6_addr_isinvalid(addr_state) (addr_state == IP6_ADDR_INVALID) +#define ip6_addr_istentative(addr_state) (addr_state & IP6_ADDR_TENTATIVE) +#define ip6_addr_isvalid(addr_state) (addr_state & IP6_ADDR_VALID) /* Include valid, preferred, and deprecated. */ +#define ip6_addr_ispreferred(addr_state) (addr_state == IP6_ADDR_PREFERRED) +#define ip6_addr_isdeprecated(addr_state) (addr_state == IP6_ADDR_DEPRECATED) + +#define ip6_addr_debug_print(debug, ipaddr) \ + LWIP_DEBUGF(debug, ("%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F ":%" X16_F, \ + ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \ + ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0)) + +#define IP6ADDR_STRLEN_MAX 46 + +int ip6addr_aton(const char *cp, ip6_addr_t *addr); +/** returns ptr to static buffer; not reentrant! */ +char *ip6addr_ntoa(const ip6_addr_t *addr); +char *ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen); + + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_IP6_ADDR_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_frag.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_frag.h new file mode 100644 index 0000000..9987914 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip6_frag.h @@ -0,0 +1,102 @@ +/** + * @file + * + * IPv6 fragmentation and reassembly. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ +#ifndef LWIP_HDR_IP6_FRAG_H +#define LWIP_HDR_IP6_FRAG_H + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */ + +/* The IPv6 reassembly timer interval in milliseconds. */ +#define IP6_REASS_TMR_INTERVAL 1000 + +/* IPv6 reassembly helper struct. + * This is exported because memp needs to know the size. + */ +struct ip6_reassdata { + struct ip6_reassdata *next; + struct pbuf *p; + struct ip6_hdr * iphdr; + u32_t identification; + u16_t datagram_len; + u8_t nexth; + u8_t timer; +}; + +#define ip6_reass_init() /* Compatibility define */ +void ip6_reass_tmr(void); +struct pbuf * ip6_reass(struct pbuf *p); + +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_FRAG /* don't build if not configured for use in lwipopts.h */ + +/** 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. */ +#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED +#define LWIP_PBUF_CUSTOM_REF_DEFINED +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */ + +err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest); + +#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP6_FRAG_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_addr.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_addr.h new file mode 100644 index 0000000..1ded93f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_addr.h @@ -0,0 +1,130 @@ +/* + * 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_HDR_IP_ADDR_H__ +#define LWIP_HDR_IP_ADDR_H__ + +#include "lwip/opt.h" +#include "lwip/def.h" + +#include "lwip/ip4_addr.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if LWIP_IPV6 +/* A union struct for both IP version's addresses. */ +typedef union { + ip_addr_t ip4; + ip6_addr_t ip6; +} ipX_addr_t; + +/** These functions only exist for type-safe conversion from ip_addr_t to + ip6_addr_t and back */ +#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER +static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr) +{ return (ip6_addr_t*)ipaddr;} +static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr) +{ return (ip_addr_t*)ip6addr; } +static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr) +{ return (ipX_addr_t*)ipaddr; } +static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr) +{ return (ipX_addr_t*)ip6addr; } +#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */ +#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr)) +#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr)) +#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr) +#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr) +#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/ +#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6)) +#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4)) + +#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_copy((dest).ip6, (src).ip6); }else{ \ + ip_addr_copy((dest).ip4, (src).ip4); }}while(0) +#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \ + ip_addr_set(ipX_2_ip(dest), src); }}while(0) +#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \ + ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \ + ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0) +#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \ + ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \ + ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \ + ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \ + ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2))) +#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_isany(ipX_2_ip6(ipaddr)) : \ + ip_addr_isany(ipX_2_ip(ipaddr))) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \ + ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \ + ip_addr_ismulticast(ipX_2_ip(ipaddr))) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \ + ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \ + ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0) + +#else /* LWIP_IPV6 */ + +typedef ip_addr_t ipX_addr_t; +#define ipX_2_ip(ipaddr) (ipaddr) +#define ip_2_ipX(ipaddr) (ipaddr) + +#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src) +#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src) +#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr) +#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr) +#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr) +#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src) +#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2) +#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr) +#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr) +#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr) + +#endif /* LWIP_IPV6 */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_ADDR_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_frag.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_frag.h new file mode 100644 index 0000000..143b1e5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/ip_frag.h @@ -0,0 +1,91 @@ +/* + * 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_HDR_IP_FRAG_H +#define LWIP_HDR_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/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); +void ip_reass_tmr(void); +struct pbuf * ip_reass(struct pbuf *p); +#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. */ +#ifndef LWIP_PBUF_CUSTOM_REF_DEFINED +#define LWIP_PBUF_CUSTOM_REF_DEFINED +struct pbuf_custom_ref { + /** 'base class' */ + struct pbuf_custom pc; + /** pointer to the original pbuf that is referenced */ + struct pbuf *original; +}; +#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */ +#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); +#endif /* IP_FRAG */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_FRAG_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_ip.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_ip.h new file mode 100644 index 0000000..522e98d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_ip.h @@ -0,0 +1,259 @@ +/* + * 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_HDR_IP_H__ +#define LWIP_HDR_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" +#include "lwip/ip4.h" +#include "lwip/ip6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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 + +/** 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 */ +#ifndef LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX +#define LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p) LWIP_ASSERT("p->ref == 1", (p)->ref == 1) +#endif + +#if LWIP_NETIF_HWADDRHINT +#define IP_PCB_ADDRHINT ;u8_t addr_hint +#else +#define IP_PCB_ADDRHINT +#endif /* LWIP_NETIF_HWADDRHINT */ + +#if LWIP_IPV6 +#define IP_PCB_ISIPV6_MEMBER u8_t isipv6; +#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6) +#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \ + ((pcb)->isipv6 != 0) : \ + ((pcb)->isipv6 == 0)) +#define PCB_ISIPV6(pcb) ((pcb)->isipv6) +#else +#define IP_PCB_ISIPV6_MEMBER +#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1 +#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1 +#define PCB_ISIPV6(pcb) 0 +#endif /* LWIP_IPV6 */ + +/* 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_PCB_ISIPV6_MEMBER \ + /* ip addresses in network byte order */ \ + ipX_addr_t local_ip; \ + ipX_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 in sockets.h + */ +#define SOF_REUSEADDR 0x04U /* allow local address reuse */ +#define SOF_KEEPALIVE 0x08U /* keep connections alive */ +#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ + +/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */ +#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE) + +/* Global variables of this module, kept in a struct for efficient access using base+index. */ +struct ip_globals +{ + /** 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_ip4_header; +#if LWIP_IPV6 + /** Header of the input IPv6 packet currently being processed. */ + const struct ip6_hdr *current_ip6_header; +#endif /* LWIP_IPV6 */ + /** Total header length of current_ip4/6_header (i.e. after this, the UDP/TCP header starts) */ + u16_t current_ip_header_tot_len; + /** Source IP address of current_header */ + ipX_addr_t current_iphdr_src; + /** Destination IP address of current_header */ + ipX_addr_t current_iphdr_dest; +}; +extern struct ip_globals ip_data; + + +/** 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() (ip_data.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() (ip_data.current_ip4_header) +/** Total header length of ip(6)_current_header() (i.e. after this, the UDP/TCP header starts) */ +#define ip_current_header_tot_len() (ip_data.current_ip_header_tot_len) +/** Source IP address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +#if LWIP_IPV6 +/** Get the IPv6 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 ip6_current_header() (ip_data.current_ip6_header) +/** Returns TRUE if the current IP input packet is IPv6, FALSE if it is IPv4 */ +#define ip_current_is_v6() (ip6_current_header() != NULL) +/** Source IPv6 address of current_header */ +#define ip6_current_src_addr() (ipX_2_ip6(&ip_data.current_iphdr_src)) +/** Destination IPv6 address of current_header */ +#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest)) +/** Get the transport layer protocol */ +#define ip_current_header_proto() (ip_current_is_v6() ? \ + IP6H_NEXTH(ip6_current_header()) :\ + IPH_PROTO(ip_current_header())) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \ + (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len())) + +/** Set an IP_PCB to IPv6 (IPv4 is the default) */ +#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0) + +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src)) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest)) + +#else /* LWIP_IPV6 */ + +/** Always returns FALSE when only supporting IPv4 */ +#define ip_current_is_v6() 0 +/** Get the transport layer protocol */ +#define ip_current_header_proto() IPH_PROTO(ip_current_header()) +/** Get the transport layer header */ +#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len())) +/** Source IP4 address of current_header */ +#define ip_current_src_addr() (&ip_data.current_iphdr_src) +/** Destination IP4 address of current_header */ +#define ip_current_dest_addr() (&ip_data.current_iphdr_dest) + +#endif /* LWIP_IPV6 */ + +/** Union source address of current_header */ +#define ipX_current_src_addr() (&ip_data.current_iphdr_src) +/** Union destination address of current_header */ +#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest) + +/** Gets an IP pcb option (SOF_* flags) */ +#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt)) +/** Sets an IP pcb option (SOF_* flags) */ +#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt)) +/** Resets an IP pcb option (SOF_* flags) */ +#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt)) + +#if LWIP_IPV6 +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ((isipv6) ? \ + ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \ + ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto)) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_if_src(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ((isipv6) ? \ + ip6_output_if_src(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif) : \ + ip_output_if_src(p, (src), (dest), ttl, tos, proto, netif)) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ((isipv6) ? \ + ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \ + ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint)) +#define ipX_route(isipv6, src, dest) \ + ((isipv6) ? \ + ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \ + ip_route(ipX_2_ip(dest))) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ((isipv6) ? \ + ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \ + ip_netif_get_local_ipX(netif)) +#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p)) +#else /* LWIP_IPV6 */ +#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \ + ip_output(p, src, dest, ttl, tos, proto) +#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_if_src(isipv6, p, src, dest, ttl, tos, proto, netif) \ + ip_output_if_src(p, src, dest, ttl, tos, proto, netif) +#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \ + ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint) +#define ipX_route(isipv6, src, dest) \ + ip_route(ipX_2_ip(dest)) +#define ipX_netif_get_local_ipX(isipv6, netif, dest) \ + ip_netif_get_local_ipX(netif) +#define ipX_debug_print(is_ipv6, p) ip_debug_print(p) +#endif /* LWIP_IPV6 */ + +#define ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \ + (netif) = ipX_route(isipv6, src, dest); \ + (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \ +}while(0) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_IP_H__ */ + + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_timers.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_timers.h new file mode 100644 index 0000000..e125777 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/lwip_timers.h @@ -0,0 +1,108 @@ +/* + * 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_HDR_TIMERS_H +#define LWIP_HDR_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" +#if !NO_SYS +#include "lwip/sys.h" +#endif + +#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); + +//Realtek add +struct sys_timeouts { + struct sys_timeo *next; +}; + +struct sys_timeouts *sys_arch_timeouts(void); +//Realtek add end + +#if LWIP_DEBUG_TIMERNAMES +void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name); +#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); +#endif /* LWIP_DEBUG_TIMERNAMES */ + +void sys_untimeout(sys_timeout_handler handler, void *arg); +#if NO_SYS +void sys_check_timeouts(void); +void sys_restart_timeouts(void); +#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_HDR_TIMERS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mem.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mem.h new file mode 100644 index 0000000..7fea25a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mem.h @@ -0,0 +1,123 @@ +/* + * 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_HDR_MEM_H +#define LWIP_HDR_MEM_H + +#include "lwip/opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if MEM_LIBC_MALLOC + +#include /* for size_t */ + +typedef size_t mem_size_t; +#define MEM_SIZE_F SZT_F + +/* 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 free +#endif +#ifndef mem_malloc +#define mem_malloc malloc +#endif +#ifndef mem_calloc +#define mem_calloc calloc +#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); +void *mem_trim(void *mem, mem_size_t size); +#endif /* MEM_USE_POOLS */ +void *mem_malloc(mem_size_t size); +void *mem_calloc(mem_size_t count, mem_size_t size); +void mem_free(void *mem); +#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 - 1U) & ~(MEM_ALIGNMENT-1U)) +#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 - 1U)) +#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_HDR_MEM_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp.h new file mode 100644 index 0000000..8bcc43a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp.h @@ -0,0 +1,119 @@ +/* + * 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_HDR_MEMP_H +#define LWIP_HDR_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) 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; +#if MEMP_OVERFLOW_CHECK + u16_t size; +#endif /* MEMP_OVERFLOW_CHECK */ +}; +#endif /* MEM_USE_POOLS */ + +void memp_init(void); + +#if MEMP_OVERFLOW_CHECK +void *memp_malloc_fn(memp_t type, const char* file, const int line); +#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__) +#else +void *memp_malloc(memp_t type); +#endif +void memp_free(memp_t type, void *mem); + +#endif /* MEMP_MEM_MALLOC */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_MEMP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp_std.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp_std.h new file mode 100644 index 0000000..ce303da --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/memp_std.h @@ -0,0 +1,154 @@ +/* + * 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 + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size) +#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) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc) +#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") +#endif /* LWIP_RAW */ + +#if LWIP_UDP +LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB") +#endif /* LWIP_UDP */ + +#if LWIP_TCP +LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB") +LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN") +LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG") +#endif /* LWIP_TCP */ + +#if IP_REASSEMBLY +LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA") +#endif /* IP_REASSEMBLY */ +#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG +LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF") +#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */ + +#if LWIP_NETCONN || LWIP_SOCKET +LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF") +LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN") +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#if NO_SYS==0 +LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API") +#if LWIP_MPU_COMPATIBLE +LWIP_MEMPOOL(API_MSG, MEMP_NUM_API_MSG, sizeof(struct api_msg), "API_MSG") +#if LWIP_DNS +LWIP_MEMPOOL(DNS_API_MSG, MEMP_NUM_DNS_API_MSG, sizeof(struct dns_api_msg), "DNS_API_MSG") +#endif +#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING +LWIP_MEMPOOL(SOCKET_SETGETSOCKOPT_DATA, MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA, sizeof(struct lwip_setgetsockopt_data), "SOCKET_SETGETSOCKOPT_DATA") +#endif +#if LWIP_NETIF_API +LWIP_MEMPOOL(NETIFAPI_MSG, MEMP_NUM_NETIFAPI_MSG, sizeof(struct netifapi_msg), "NETIFAPI_MSG") +#endif +#endif /* LWIP_MPU_COMPATIBLE */ +#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 LWIP_ARP && ARP_QUEUEING +LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE") +#endif /* LWIP_ARP && ARP_QUEUEING */ + +#if LWIP_IGMP +LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP") +#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") +#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 +LWIP_MEMPOOL(PPP_PCB, MEMP_NUM_PPP_PCB, sizeof(ppp_pcb), "PPP_PCB") +#if PPPOE_SUPPORT +LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF") +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +LWIP_MEMPOOL(PPPOL2TP_PCB, MEMP_NUM_PPPOL2TP_INTERFACES, sizeof(pppol2tp_pcb), "PPPOL2TP_PCB") +#endif /* PPPOL2TP_SUPPORT */ +#endif /* PPP_SUPPORT */ + +#if LWIP_IPV6 && LWIP_ND6_QUEUEING +LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE") +#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */ + +#if LWIP_IPV6 && LWIP_IPV6_REASS +LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA") +#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */ + +#if LWIP_IPV6 && LWIP_IPV6_MLD +LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP") +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + + +/* + * 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") +LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL") + + +/* + * 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/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mld6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mld6.h new file mode 100644 index 0000000..f629c3d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/mld6.h @@ -0,0 +1,118 @@ +/** + * @file + * + * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710. + * No support for MLDv2. + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_MLD6_H +#define LWIP_HDR_MLD6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct mld_group { + /** next link */ + struct mld_group *next; + /** interface on which the group is active */ + struct netif *netif; + /** multicast address */ + ip6_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 */ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; +}; + +/** Multicast listener report/query/done message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mld_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t max_resp_delay); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +#define MLD6_TMR_INTERVAL 100 /* Milliseconds */ + +/* MAC Filter Actions, these are passed to a netif's + * mld_mac_filter callback function. */ +#define MLD6_DEL_MAC_FILTER 0 +#define MLD6_ADD_MAC_FILTER 1 + + +#define mld6_init() /* TODO should we init tables? */ +err_t mld6_stop(struct netif *netif); +void mld6_report_groups(struct netif *netif); +void mld6_tmr(void); +struct mld_group *mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr); +void mld6_input(struct pbuf *p, struct netif *inp); +err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); +err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */ + +#endif /* LWIP_HDR_MLD6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/nd6.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/nd6.h new file mode 100644 index 0000000..2a8401f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/nd6.h @@ -0,0 +1,362 @@ +/** + * @file + * + * Neighbor discovery and stateless address autoconfiguration for IPv6. + * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862 + * (Address autoconfiguration). + */ + +/* + * Copyright (c) 2010 Inico 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. 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: Ivan Delamer + * + * + * Please coordinate changes and requests with Ivan Delamer + * + */ + +#ifndef LWIP_HDR_ND6_H +#define LWIP_HDR_ND6_H + +#include "lwip/opt.h" + +#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" +#include "lwip/netif.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct for tables. */ +struct nd6_neighbor_cache_entry { + ip6_addr_t next_hop_address; + struct netif * netif; + u8_t lladdr[NETIF_MAX_HWADDR_LEN]; + /*u32_t pmtu;*/ +#if LWIP_ND6_QUEUEING + /** Pointer to queue of pending outgoing packets on this entry. */ + struct nd6_q_entry *q; +#else /* LWIP_ND6_QUEUEING */ + /** Pointer to a single pending outgoing packet on this entry. */ + struct pbuf *q; +#endif /* LWIP_ND6_QUEUEING */ + u8_t state; + u8_t isrouter; + union { + u32_t reachable_time; + u32_t delay_time; + u32_t probes_sent; + u32_t stale_time; + } counter; +}; + +struct nd6_destination_cache_entry { + ip6_addr_t destination_addr; + ip6_addr_t next_hop_addr; + u32_t pmtu; + u32_t age; +}; + +struct nd6_prefix_list_entry { + ip6_addr_t prefix; + struct netif * netif; + u32_t invalidation_timer; +#if LWIP_IPV6_AUTOCONFIG + u8_t flags; +#define ND6_PREFIX_AUTOCONFIG_AUTONOMOUS 0x01 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED 0x02 +#define ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE 0x04 +#endif /* LWIP_IPV6_AUTOCONFIG */ +}; + +struct nd6_router_list_entry { + struct nd6_neighbor_cache_entry * neighbor_entry; + u32_t invalidation_timer; + u8_t flags; +}; + + +enum nd6_neighbor_cache_entry_state { + ND6_NO_ENTRY = 0, + ND6_INCOMPLETE, + ND6_REACHABLE, + ND6_STALE, + ND6_DELAY, + ND6_PROBE +}; + +#if LWIP_ND6_QUEUEING +/** struct for queueing outgoing packets for unknown address + * defined here to be accessed by memp.h + */ +struct nd6_q_entry { + struct nd6_q_entry *next; + struct pbuf *p; +}; +#endif /* LWIP_ND6_QUEUEING */ + +/** Neighbor solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ns_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Neighbor advertisement message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct na_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FLD_8(u8_t reserved[3]); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#define ND6_FLAG_ROUTER (0x80) +#define ND6_FLAG_SOLICITED (0x40) +#define ND6_FLAG_OVERRIDE (0x20) + +/** Router solicitation message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct rs_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Router advertisement message header. */ +#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80) +#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40) +#define ND6_RA_FLAG_HOME_AGENT (0x20) +#define ND6_RA_PREFERENCE_MASK (0x18) +#define ND6_RA_PREFERENCE_HIGH (0x08) +#define ND6_RA_PREFERENCE_MEDIUM (0x00) +#define ND6_RA_PREFERENCE_LOW (0x18) +#define ND6_RA_PREFERENCE_DISABLED (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct ra_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FLD_8(u8_t current_hop_limit); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FIELD(u16_t router_lifetime); + PACK_STRUCT_FIELD(u32_t reachable_time); + PACK_STRUCT_FIELD(u32_t retrans_timer); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirect message header. */ +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirect_header { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t reserved); + PACK_STRUCT_FLD_S(ip6_addr_p_t target_address); + PACK_STRUCT_FLD_S(ip6_addr_p_t destination_address); + /* Options follow. */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Link-layer address option. */ +#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01) +#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct lladdr_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t addr[NETIF_MAX_HWADDR_LEN]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Prefix information option. */ +#define ND6_OPTION_TYPE_PREFIX_INFO (0x03) +#define ND6_PREFIX_FLAG_ON_LINK (0x80) +#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40) +#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20) +#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct prefix_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t prefix_length); + PACK_STRUCT_FLD_8(u8_t flags); + PACK_STRUCT_FIELD(u32_t valid_lifetime); + PACK_STRUCT_FIELD(u32_t preferred_lifetime); + PACK_STRUCT_FLD_8(u8_t reserved2[3]); + PACK_STRUCT_FLD_8(u8_t site_prefix_length); + PACK_STRUCT_FLD_S(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Redirected header option. */ +#define ND6_OPTION_TYPE_REDIR_HDR (0x04) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct redirected_header_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t reserved[6]); + /* Portion of redirected packet follows. */ + /* PACK_STRUCT_FLD_8(u8_t redirected[8]); */ +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** MTU option. */ +#define ND6_OPTION_TYPE_MTU (0x05) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct mtu_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FIELD(u16_t reserved); + PACK_STRUCT_FIELD(u32_t mtu); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** Route information option. */ +#define ND6_OPTION_TYPE_ROUTE_INFO (24) +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct route_option { + PACK_STRUCT_FLD_8(u8_t type); + PACK_STRUCT_FLD_8(u8_t length); + PACK_STRUCT_FLD_8(u8_t prefix_length); + PACK_STRUCT_FLD_8(u8_t preference); + PACK_STRUCT_FIELD(u32_t route_lifetime); + PACK_STRUCT_FLD_S(ip6_addr_p_t prefix); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif + +/** 1 second period */ +#define ND6_TMR_INTERVAL 1000 + +/* Router tables. */ +/* TODO make these static? and entries accessible through API? */ +extern struct nd6_neighbor_cache_entry neighbor_cache[]; +extern struct nd6_destination_cache_entry destination_cache[]; +extern struct nd6_prefix_list_entry prefix_list[]; +extern struct nd6_router_list_entry default_router_list[]; + +/* Default values, can be updated by a RA message. */ +extern u32_t reachable_time; +extern u32_t retrans_timer; + +#define nd6_init() /* TODO should we init tables? */ +void nd6_tmr(void); +void nd6_input(struct pbuf *p, struct netif *inp); +s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif); +s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif); +u16_t nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif); +err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p); +#if LWIP_ND6_TCP_REACHABILITY_HINTS +void nd6_reachability_hint(ip6_addr_t * ip6addr); +#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_HDR_ND6_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netbuf.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netbuf.h new file mode 100644 index 0000000..54ef1e6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netbuf.h @@ -0,0 +1,123 @@ +/* + * 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_HDR_NETBUF_H +#define LWIP_HDR_NETBUF_H + +#if LWIP_NETCONN || LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +/* don't export the netbuf functions when socket API is enabled but netconn API is disabled */ +#if LWIP_NETCONN +#define LWIP_NETCONN_SCOPE +#else /* LWIP_NETCONN */ +#define LWIP_NETCONN_SCOPE static +#endif /* LWIP_NETCONN */ + +#include "lwip/opt.h" +#include "lwip/pbuf.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_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; + ipX_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 + ipX_addr_t toaddr; +#endif /* LWIP_NETBUF_RECVINFO */ +#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ +}; + +/* Network buffer functions: */ +LWIP_NETCONN_SCOPE struct netbuf * netbuf_new (void); +LWIP_NETCONN_SCOPE void netbuf_delete (struct netbuf *buf); +LWIP_NETCONN_SCOPE void * netbuf_alloc (struct netbuf *buf, u16_t size); +LWIP_NETCONN_SCOPE void netbuf_free (struct netbuf *buf); +LWIP_NETCONN_SCOPE err_t netbuf_ref (struct netbuf *buf, + const void *dataptr, u16_t size); +LWIP_NETCONN_SCOPE void netbuf_chain (struct netbuf *head, + struct netbuf *tail); + +LWIP_NETCONN_SCOPE err_t netbuf_data (struct netbuf *buf, + void **dataptr, u16_t *len); +LWIP_NETCONN_SCOPE s8_t netbuf_next (struct netbuf *buf); +LWIP_NETCONN_SCOPE void netbuf_first (struct netbuf *buf); + + +#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) (ipX_2_ip(&((buf)->addr))) +#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr) +#define netbuf_fromport(buf) ((buf)->port) +#if LWIP_NETBUF_RECVINFO +#define netbuf_destaddr(buf) (ipX_2_ip(&((buf)->toaddr))) +#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), 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 */ + +#if LWIP_IPV6 +#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr))) +#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr) +#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr))) +#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr) +#endif /* LWIP_IPV6 */ + +#define netbuf_fromaddr_ipX(buf) (&((buf)->addr)) +#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr)) + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +#endif /* LWIP_HDR_NETBUF_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netdb.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netdb.h new file mode 100644 index 0000000..d7acdc9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netdb.h @@ -0,0 +1,127 @@ +/* + * 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_HDR_NETDB_H +#define LWIP_HDR_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 EAI_FAMILY 204 + +#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 */ + +#define NETDB_ELEM_SIZE (sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1) + +#if LWIP_DNS_API_DECLARE_H_ERRNO +/* application accessible 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_HDR_NETDB_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netif.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netif.h new file mode 100644 index 0000000..fd2939d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netif.h @@ -0,0 +1,401 @@ +/* + * 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_HDR_NETIF_H +#define LWIP_HDR_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/ip6_addr.h" + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#if LWIP_DHCP +struct dhcp; +#endif +#if LWIP_AUTOIP +struct autoip; +#endif +#if LWIP_IPV6_DHCP6 +#include "lwip/dhcp6.h" +#endif /* LWIP_IPV6_DHCP6 */ + +#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. + This does not have to be changed, normally. */ +#ifndef NETIF_MAX_HWADDR_LEN +#define NETIF_MAX_HWADDR_LEN 6U +#endif + +/** 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 + +/* Realtek Modified Start */ +/** if set, the netif has IP layer switch capability */ +#ifdef CONFIG_DONT_CARE_TP +#define NETIF_FLAG_IPSWITCH 0x100U +#endif +/* Realtek Modified End */ + +/** 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); +#if LWIP_IPV6 +/** Function prototype for netif->output_ip6 functions. Called by lwIP when a packet + * shall be sent. For ethernet netif, set this to 'ethip6_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 IPv6 address to which the packet shall be sent + */ +typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p, + ip6_addr_t *ipaddr); +#endif /* LWIP_IPV6 */ +/** 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); +#if LWIP_IPV6 && LWIP_IPV6_MLD +/** Function prototype for netif mld_mac_filter functions */ +typedef err_t (*netif_mld_mac_filter_fn)(struct netif *netif, + ip6_addr_t *group, u8_t action); +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + +/** 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; + +#if LWIP_IPV6 + /** Array of IPv6 addresses for this netif. */ + ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + /** The state of each IPv6 address (Tentative, Preferred, etc). + * @see ip6_addr.h */ + u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES]; +#endif /* LWIP_IPV6 */ + /** This function is called by the network device driver + * to pass a packet up the TCP/IP stack. */ + 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. */ + 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_IPV6 + /** This function is called by the IPv6 module when it wants + * to send a packet on the interface. This function typically + * first resolves the hardware address, then sends the packet. */ + netif_output_ip6_fn output_ip6; +#endif /* LWIP_IPV6 */ +#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 */ +#if LWIP_NETIF_REMOVE_CALLBACK + /** This function is called when the netif has been removed */ + netif_status_callback_fn remove_callback; +#endif /* LWIP_NETIF_REMOVE_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_IPV6_AUTOCONFIG + /** is this netif enabled for IPv6 autoconfiguration */ + u8_t ip6_autoconfig_enabled; +#endif /* LWIP_IPV6_AUTOCONFIG */ +#if LWIP_IPV6_SEND_ROUTER_SOLICIT + /** Number of Router Solicitation messages that remain to be sent. */ + u8_t rs_count; +#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */ +#if LWIP_IPV6_DHCP6 + /** the DHCPv6 client state information for this netif */ + struct dhcp6 *dhcp6; +#endif /* LWIP_IPV6_DHCP6 */ +#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) */ + 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) */ + //Realtek modified u8_t to u16_t for NETIF_FLAG_IPSWITCH extend flag + //u8_t flags; + u16_t flags; + //Realtek modified end + + /** 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 an entry in the multicast + filter table of the ethernet MAC.*/ + netif_igmp_mac_filter_fn igmp_mac_filter; +#endif /* LWIP_IGMP */ +#if LWIP_IPV6 && LWIP_IPV6_MLD + /** This function could be called to add or delete an entry in the IPv6 multicast + filter table of the ethernet MAC. */ + netif_mld_mac_filter_fn mld_mac_filter; +#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +#if LWIP_NETIF_HWADDRHINT + u8_t *addr_hint; +#endif /* LWIP_NETIF_HWADDRHINT */ +#if ENABLE_LOOPBACK + /* List of packets to be queued for ourselves. */ + 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); + +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); + +void +netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, + ip_addr_t *gw); +void netif_remove(struct netif * netif); + +/* 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); + +void netif_set_default(struct netif *netif); + +void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr); +void netif_set_netmask(struct netif *netif, ip_addr_t *netmask); +void netif_set_gw(struct netif *netif, ip_addr_t *gw); + +void netif_set_up(struct netif *netif); +void netif_set_down(struct netif *netif); +/** 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); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_REMOVE_CALLBACK +void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback); +#endif /* LWIP_NETIF_REMOVE_CALLBACK */ + +void netif_set_link_up(struct netif *netif); +void netif_set_link_down(struct netif *netif); +/** 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); +#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); +void netif_poll(struct netif *netif); +#if !LWIP_NETIF_LOOPBACK_MULTITHREADING +void netif_poll_all(void); +#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */ +#endif /* ENABLE_LOOPBACK */ + +#if LWIP_IPV6 +#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)])) +#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)]) +#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state)) +s8_t netif_get_ip6_addr_match(struct netif * netif, ip6_addr_t * ip6addr); +void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit); +#endif /* LWIP_IPV6 */ + +#if LWIP_NETIF_HWADDRHINT +#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint)) +#else /* LWIP_NETIF_HWADDRHINT */ +#define NETIF_SET_HWADDRHINT(netif, hint) +#endif /* LWIP_NETIF_HWADDRHINT */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_NETIF_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netifapi.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netifapi.h new file mode 100644 index 0000000..5d7738a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/netifapi.h @@ -0,0 +1,117 @@ +/* + * 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_HDR_NETIFAPI_H +#define LWIP_HDR_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 + +#if LWIP_MPU_COMPATIBLE +#define NETIFAPI_IPADDR_DEF(m) m +#else /* LWIP_MPU_COMPATIBLE */ +#define NETIFAPI_IPADDR_DEF(m) *m +#endif /* LWIP_MPU_COMPATIBLE */ + +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 NETIFAPI_IPADDR_DEF(ipaddr); + ip_addr_t NETIFAPI_IPADDR_DEF(netmask); + ip_addr_t NETIFAPI_IPADDR_DEF(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_dhcp_inform(n) netifapi_netif_common(n, dhcp_inform, NULL) +#define netifapi_dhcp_renew(n) netifapi_netif_common(n, NULL, dhcp_renew) +#define netifapi_dhcp_release(n) netifapi_netif_common(n, NULL, dhcp_release) +#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_HDR_NETIFAPI_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/opt.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/opt.h new file mode 100644 index 0000000..7e8511d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/opt.h @@ -0,0 +1,2773 @@ +/** + * @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_HDR_OPT_H +#define LWIP_HDR_OPT_H + +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you don't 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 0 +#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) 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 + +/** + * LWIP_MPU_COMPATIBLE: enables special memory management mechanism + * which makes lwip able to work on MPU (Memory Protection Unit) system + * by not passing stack-pointers to other threads + * (this decreases performance) + */ +#ifndef LWIP_MPU_COMPATIBLE +#define LWIP_MPU_COMPATIBLE 0 +#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 + * include 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 simultaneously 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 simultaneously 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 at 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 simultaneously active timeouts. + * The default number of timeouts is calculated here for all enabled modules. + * The formula expects settings to be either '0' or '1'. + */ +#ifndef MEMP_NUM_SYS_TIMEOUT +#define MEMP_NUM_SYS_TIMEOUT (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + (PPP_SUPPORT*6*MEMP_NUM_PPP_PCB) + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)) +#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_PPP_PCB: the number of simultaneously active PPP + * connections (requires the PPP_SUPPORT option) + */ +#ifndef MEMP_NUM_PPP_PCB +#define MEMP_NUM_PPP_PCB 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 + +/** + * MEMP_NUM_PPPOL2TP_INTERFACES: the number of concurrently active PPPoL2TP + * interfaces (only used with PPPOL2TP_SUPPORT==1) + */ +#ifndef MEMP_NUM_PPPOL2TP_INTERFACES +#define MEMP_NUM_PPPOL2TP_INTERFACES 1 +#endif + +/** + * PBUF_POOL_SIZE: the number of buffers in the pbuf pool. + */ +#ifndef PBUF_POOL_SIZE +#define PBUF_POOL_SIZE 16 +#endif + +/** MEMP_NUM_API_MSG: the number of concurrently active calls to various + * socket, netconn, and tcpip functions + */ +#ifndef MEMP_NUM_API_MSG +#define MEMP_NUM_API_MSG MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname + */ +#ifndef MEMP_NUM_DNS_API_MSG +#define MEMP_NUM_DNS_API_MSG MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls + * to getsockopt/setsockopt + */ +#ifndef MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA +#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API +#endif + +/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the + * netifapi functions + */ +#ifndef MEMP_NUM_NETIFAPI_MSG +#define MEMP_NUM_NETIFAPI_MSG MEMP_NUM_TCPIP_MSG_API +#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 //Realtek changed 0 -> 1 +#endif + +/** The maximum number of packets which may be queued for each + * unresolved address by other network layers. Defaults to 3, 0 means disabled. + * Old packets are dropped, new packets are queued. + */ +#ifndef ARP_QUEUE_LEN +#define ARP_QUEUE_LEN 0 //Realtek changed 3 -> 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 1 //Realtek changed 0 -> 1 +#endif + +/** + * ETHARP_SUPPORT_VLAN==1: support receiving and sending ethernet packets with + * VLAN header. See the description of LWIP_HOOK_VLAN_CHECK and + * LWIP_HOOK_VLAN_SET hooks to check/set VLAN headers. + * 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. + * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan) + * that returns 1 to accept a packet or 0 to drop a packet. + */ +#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 1 +#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 + +/** + * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back + * out on the netif where it was received. This should only be used for + * wireless networks. + * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming + * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags! + */ +#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF +#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0 +#endif + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 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 + +/** + * LWIP_DHCP_CHECK_LINK_UP==1: dhcp_start() only really starts if the netif has + * NETIF_FLAG_LINK_UP set in its flags. As this is only an optimization and + * netif drivers might not set this flag, the default is off. If enabled, + * netif_set_link_up() must be called to continue dhcp starting. + */ +#ifndef LWIP_DHCP_CHECK_LINK_UP +#define LWIP_DHCP_CHECK_LINK_UP 0 +#endif + +/** + * LWIP_DHCP_BOOTP_FILE==1: Store offered_si_addr and boot_file_name. + */ +#ifndef LWIP_DHCP_BOOTP_FILE +#define LWIP_DHCP_BOOTP_FILE 0 +#endif + +/* + ------------------------------------ + ---------- AUTOIP options ---------- + ------------------------------------ +*/ +/** + * LWIP_AUTOIP==1: Enable AUTOIP module. + */ +#ifndef LWIP_AUTOIP +#define LWIP_AUTOIP 1 //Realtek modified (0->1) +#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. disabling 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_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). + * To achieve good performance, this should be at least 2 * TCP_MSS. + */ +#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 LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1) +#endif + +/** + * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less + * 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_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_BYTES +#define TCP_OOSEQ_MAX_BYTES 0 +#endif + +/** + * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb. + * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0. + */ +#ifndef TCP_OOSEQ_MAX_PBUFS +#define TCP_OOSEQ_MAX_PBUFS 0 +#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 0 //Realtek changed TCP_MSS -> 0 +#endif + +/** + * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option. + * The timestamp option is currently only used to help remote hosts, it is not + * really used locally. Therefore, it is only enabled when a TS option is + * received in the initial SYN packet from a remote host. + */ +#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. This is the default. + */ +#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API) +#define LWIP_EVENT_API 0 +#define LWIP_CALLBACK_API 1 +#endif + +/** + * LWIP_WND_SCALE and TCP_RCV_SCALE: + * Set LWIP_WND_SCALE to 1 to enable window scaling. + * Set TCP_RCV_SCALE to the desired scaling factor (shift count in the + * range of [0..14]). + * When LWIP_WND_SCALE is enabled but TCP_RCV_SCALE is 0, we can use a large + * send window while having a small receive window only. + */ +#ifndef LWIP_WND_SCALE +#define LWIP_WND_SCALE 0 +#define TCP_RCV_SCALE 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 +#ifdef LWIP_HOOK_VLAN_SET +#define PBUF_LINK_HLEN (18 + ETH_PAD_SIZE) +#else /* LWIP_HOOK_VLAN_SET */ +#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE) +#endif /* LWIP_HOOK_VLAN_SET */ +#endif + +/** + * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is + * designed to accommodate 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 acquisition) + */ +#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_REMOVE_CALLBACK==1: Support a callback function that is called + * when a netif has been removed + */ +#ifndef LWIP_NETIF_REMOVE_CALLBACK +#define LWIP_NETIF_REMOVE_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 0 +#endif /* LWIP_NETIF_TX_SINGLE_PBUF */ + +/* + ------------------------------------ + ---------- LOOPIF options ---------- + ------------------------------------ +*/ +/** + * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) + */ +#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 "TCP_IP" // Realtek modified ("tcpip_thread" -> "TCP_IP") +#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 + +/** + * 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 1 +#endif + +/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create + * timers running in tcpip_thread from another thread. + */ +#ifndef LWIP_TCPIP_TIMEOUT +#define LWIP_TCPIP_TIMEOUT 1 +#endif + +/** LWIP_NETCONN_SEM_PER_THREAD==1: Use one (thread-local) semaphore per + * thread calling socket/netconn functions instead of allocating one + * semaphore per netconn (and per select etc.) + * ATTENTION: a thread-local semaphore for API calls is needed: + * - LWIP_NETCONN_THREAD_SEM_GET() returning a sys_sem_t* + * - LWIP_NETCONN_THREAD_SEM_ALLOC() creating the semaphore + * - LWIP_NETCONN_THREAD_SEM_FREE() freeing the semaphore + * The latter 2 can be invoked up by calling netconn_thread_init()/netconn_thread_cleanup(). + * Ports may call these for threads created with sys_thread_new(). + */ +#ifndef LWIP_NETCONN_SEM_PER_THREAD +#define LWIP_NETCONN_SEM_PER_THREAD 0 +#endif + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 1 +#endif + +/* LWIP_SOCKET_SET_ERRNO==1: Set errno when socket functions cannot complete + * successfully, as required by POSIX. Default is POSIX-compliant. + */ +#ifndef LWIP_SOCKET_SET_ERRNO +#define LWIP_SOCKET_SET_ERRNO 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_SNDTIMEO==1: Enable send timeout for sockets/netconns and + * SO_SNDTIMEO processing. + */ +#ifndef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 0 +#endif + +/** + * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and + * SO_RCVTIMEO processing. + */ +#ifndef LWIP_SO_RCVTIMEO +#define LWIP_SO_RCVTIMEO 1 //Realtek modified(0->1) +#endif + +/** + * LWIP_SO_SNDRCVTIMEO_NONSTANDARD==1: SO_RCVTIMEO/SO_SNDTIMEO take an int + * (milliseconds, much like winsock does) instead of a struct timeval (default). + */ +#ifndef LWIP_SO_SNDRCVTIMEO_NONSTANDARD +#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 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 + +/** + * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of + * pending data in the network buffer. This is the way windows does it. It's + * the default for lwIP since it is smaller. + * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next + * pending datagram in bytes. This is the way linux does it. This code is only + * here for compatibility. + */ +#ifndef LWIP_FIONREAD_LINUXMODE +#define LWIP_FIONREAD_LINUXMODE 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 + +/** + * IP6_STATS==1: Enable IPv6 stats. + */ +#ifndef IP6_STATS +#define IP6_STATS (LWIP_IPV6) +#endif + +/** + * ICMP6_STATS==1: Enable ICMP for IPv6 stats. + */ +#ifndef ICMP6_STATS +#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6) +#endif + +/** + * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats. + */ +#ifndef IP6_FRAG_STATS +#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS)) +#endif + +/** + * MLD6_STATS==1: Enable MLD for IPv6 stats. + */ +#ifndef MLD6_STATS +#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD) +#endif + +/** + * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats. + */ +#ifndef ND6_STATS +#define ND6_STATS (LWIP_IPV6) +#endif + +#else + +#define LINK_STATS 0 +#define ETHARP_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 +#define IP6_STATS 0 +#define ICMP6_STATS 0 +#define IP6_FRAG_STATS 0 +#define MLD6_STATS 0 +#define ND6_STATS 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 + +/** + * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP + */ +#ifndef PPPOL2TP_SUPPORT +#define PPPOL2TP_SUPPORT 0 +#endif + +/** + * PPPOL2TP_AUTH_SUPPORT==1: Enable PPP Over L2TP Auth (enable MD5 support) + */ +#ifndef PPPOL2TP_AUTH_SUPPORT +#define PPPOL2TP_AUTH_SUPPORT PPPOL2TP_SUPPORT +#endif + +/** + * PPPOS_SUPPORT==1: Enable PPP Over Serial + */ +#ifndef PPPOS_SUPPORT +#define PPPOS_SUPPORT PPP_SUPPORT +#endif + +/** + * LWIP_PPP_API==1: Enable PPP API (in pppapi.c) + */ +#ifndef LWIP_PPP_API +#define LWIP_PPP_API 0 +#endif + +#if PPP_SUPPORT + +/** + * PPP_INPROC_MULTITHREADED==1 call ppp_input() using tcpip_callback(). + * Set this to 0 if pppos_input() is called inside tcpip_thread or with NO_SYS==1. + * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded). + */ +#ifndef PPP_INPROC_MULTITHREADED +#define PPP_INPROC_MULTITHREADED (NO_SYS==0) +#endif + +/** + * PRINTPKT_SUPPORT==1: Enable PPP print packet support + * + * Mandatory for debugging, it displays exchanged packet content in debug trace. + */ +#ifndef PRINTPKT_SUPPORT +#define PRINTPKT_SUPPORT 0 +#endif + +/** + * PPP_IPV6_SUPPORT==1: Enable PPP IPv6 support + */ +#ifndef PPP_IPV6_SUPPORT +#define PPP_IPV6_SUPPORT 0 +#endif + +/** + * PPP_NOTIFY_PHASE==1: Support PPP notify phase support + * + * PPP notify phase support allows you to set a callback which is + * called on change of the internal PPP state machine. + * + * This can be used for example to set a LED pattern depending on the + * current phase of the PPP session. + */ +#ifndef PPP_NOTIFY_PHASE +#define PPP_NOTIFY_PHASE 0 +#endif + +/** + * pbuf_type PPP is using for LCP, PAP, CHAP, EAP, IPCP and IP6CP packets. + * + * Memory allocated must be single buffered for PPP to works, it requires pbuf + * that are not going to be chained when allocated. This requires setting + * PBUF_POOL_BUFSIZE to at least 512 bytes, which is quite huge for small systems. + * + * Setting PPP_USE_PBUF_RAM to 1 makes PPP use memory from heap where continuous + * buffers are required, allowing you to use a smaller PBUF_POOL_BUFSIZE. + */ +#ifndef PPP_USE_PBUF_RAM +#define PPP_USE_PBUF_RAM 0 +#endif + +/** + * PPP_FCS_TABLE: Keep a 256*2 byte table to speed up FCS calculation + */ +#ifndef PPP_FCS_TABLE +#define PPP_FCS_TABLE 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. + */ +#ifndef MSCHAP_SUPPORT +#define MSCHAP_SUPPORT 0 +#endif +#if MSCHAP_SUPPORT +#undef CHAP_SUPPORT +#define CHAP_SUPPORT 1 /* MSCHAP requires CHAP support */ +#endif /* MSCHAP_SUPPORT */ + +/** + * EAP_SUPPORT==1: Support EAP. + */ +#ifndef EAP_SUPPORT +#define EAP_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 + +/** + * ECP_SUPPORT==1: Support ECP. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef ECP_SUPPORT +#define ECP_SUPPORT 0 +#endif + +/** + * DEMAND_SUPPORT==1: Support dial on demand. CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef DEMAND_SUPPORT +#define DEMAND_SUPPORT 0 +#endif + +/** + * LQR_SUPPORT==1: Support Link Quality Report. Do nothing except exchanging some LCP packets. + */ +#ifndef LQR_SUPPORT +#define LQR_SUPPORT 0 +#endif + +/** + * PPP_SERVER==1: Enable PPP server support (waiting for incoming PPP session). CURRENTLY NOT SUPPORTED! DO NOT SET! + */ +#ifndef PPP_SERVER +#define PPP_SERVER 0 +#endif + +/** + * VJ_SUPPORT==1: Support VJ header compression. + */ +#ifndef VJ_SUPPORT +#define VJ_SUPPORT 1 +#endif +#if !PPPOS_SUPPORT +#undef VJ_SUPPORT +#define VJ_SUPPORT 0 /* Only PPPoS may need VJ compression */ +#endif /* !PPPOS_SUPPORT */ + +/** + * PPP_MD5_RANDM==1: Use MD5 for better randomness. Automatically enabled if CHAP or L2TP AUTH support is enabled. + */ +#ifndef PPP_MD5_RANDM +#define PPP_MD5_RANDM 0 +#endif +#if CHAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT +#undef PPP_MD5_RANDM +#define PPP_MD5_RANDM 1 /* MD5 Random is required for CHAP and L2TP AUTH */ +#endif /* CHAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT */ + +/** + * PolarSSL library, used if necessary and not previously disabled + * + * + * lwIP contains some files fetched from the latest BSD release of + * the PolarSSL project for ciphers and encryption methods we need for lwIP + * PPP support. + * + * The PolarSSL files were cleaned to contain only the necessary struct + * fields and functions needed for lwIP. + * + * The PolarSSL API was not changed at all, so if you are already using + * PolarSSL you can choose to skip the compilation of the included PolarSSL + * library into lwIP: + * + * The following defines are available for flexibility: + * + * LWIP_INCLUDED_POLARSSL_MD4 ; Use lwIP internal PolarSSL for MD4 + * LWIP_INCLUDED_POLARSSL_MD5 ; Use lwIP internal PolarSSL for MD5 + * LWIP_INCLUDED_POLARSSL_SHA1 ; Use lwIP internal PolarSSL for SHA1 + * LWIP_INCLUDED_POLARSSL_DES ; Use lwIP internal PolarSSL for DES + * + * If set (=1), the default if required by another enabled PPP feature unless + * explicitly set to 0, using included lwIP PolarSSL. + * + * If clear (=0), using external PolarSSL. + * + * Undefined if not needed. + * + * Beware of the stack requirements which can be a lot larger if you are not + * using our cleaned PolarSSL library. + */ + +#if CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM +#ifndef LWIP_INCLUDED_POLARSSL_MD5 +#define LWIP_INCLUDED_POLARSSL_MD5 1 /* CHAP, EAP, L2TP AUTH and MD5 Random require MD5 support */ +#endif /* LWIP_INCLUDED_POLARSSL_MD5 */ +#endif /* CHAP_SUPPORT || EAP_SUPPORT || PPPOL2TP_AUTH_SUPPORT || PPP_MD5_RANDM */ + +#if MSCHAP_SUPPORT +#ifndef LWIP_INCLUDED_POLARSSL_MD4 +#define LWIP_INCLUDED_POLARSSL_MD4 1 /* MSCHAP require MD4 support */ +#endif /* LWIP_INCLUDED_POLARSSL_MD4 */ +#ifndef LWIP_INCLUDED_POLARSSL_SHA1 +#define LWIP_INCLUDED_POLARSSL_SHA1 1 /* MSCHAP require SHA1 support */ +#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */ +#ifndef LWIP_INCLUDED_POLARSSL_DES +#define LWIP_INCLUDED_POLARSSL_DES 1 /* MSCHAP require DES support */ +#endif /* LWIP_INCLUDED_POLARSSL_DES */ +#endif /* MSCHAP_SUPPORT */ + +/* + * 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_DEFTRANSMITS +#define UPAP_DEFTRANSMITS 10 /* Maximum number of auth-reqs to send */ +#endif + +#if PPP_SERVER +#ifndef UPAP_DEFREQTIME +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif +#endif /* PPP_SERVER */ + +#ifndef CHAP_DEFTIMEOUT +#define CHAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */ +#endif + +#ifndef CHAP_DEFTRANSMITS +#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */ +#endif + +#if PPP_SERVER +#ifndef CHAP_DEFREQTIME +#define CHAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif +#endif /* PPP_SERVER */ + +#ifndef EAP_DEFREQTIME +#define EAP_DEFREQTIME 6 /* Time to wait for peer request */ +#endif + +#ifndef EAP_DEFALLOWREQ +#define EAP_DEFALLOWREQ 10 /* max # times to accept requests */ +#endif + +#if PPP_SERVER +#ifndef EAP_DEFTIMEOUT +#define EAP_DEFTIMEOUT 6 /* Timeout (seconds) for rexmit */ +#endif + +#ifndef EAP_DEFTRANSMITS +#define EAP_DEFTRANSMITS 10 /* max # times to transmit */ +#endif +#endif /* PPP_SERVER */ + +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#ifndef LCP_DEFLOOPBACKFAIL +#define LCP_DEFLOOPBACKFAIL 10 +#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_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets. + */ +#ifndef CHECKSUM_GEN_ICMP +#define CHECKSUM_GEN_ICMP 1 +#endif + +/** + * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets. + */ +#ifndef CHECKSUM_GEN_ICMP6 +#define CHECKSUM_GEN_ICMP6 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 + +/** + * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets. + */ +#ifndef CHECKSUM_CHECK_ICMP +#define CHECKSUM_CHECK_ICMP 1 +#endif + +/** + * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets + */ +#ifndef CHECKSUM_CHECK_ICMP6 +#define CHECKSUM_CHECK_ICMP6 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 + +/* + --------------------------------------- + ---------- IPv6 options --------------- + --------------------------------------- +*/ +/** + * LWIP_IPV6==1: Enable IPv6 + */ +#ifndef LWIP_IPV6 +#define LWIP_IPV6 0 +#endif + +/** + * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif. + */ +#ifndef LWIP_IPV6_NUM_ADDRESSES +#define LWIP_IPV6_NUM_ADDRESSES 3 +#endif + +/** + * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs + */ +#ifndef LWIP_IPV6_FORWARD +#define LWIP_IPV6_FORWARD 0 +#endif + +/** + * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC) + */ +#ifndef LWIP_ICMP6 +#define LWIP_ICMP6 (LWIP_IPV6) +#endif + +/** + * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in + * ICMPv6 error messages. + */ +#ifndef LWIP_ICMP6_DATASIZE +#define LWIP_ICMP6_DATASIZE 8 +#endif + +/** + * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages + */ +#ifndef LWIP_ICMP6_HL +#define LWIP_ICMP6_HL 255 +#endif + +/** + * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol. + */ +#ifndef LWIP_IPV6_MLD +#define LWIP_IPV6_MLD (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined. + */ +#ifndef MEMP_NUM_MLD6_GROUP +#define MEMP_NUM_MLD6_GROUP 4 +#endif + +/** + * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big. + */ +#ifndef LWIP_IPV6_FRAG +#define LWIP_IPV6_FRAG 0 +#endif + +/** + * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented + */ +#ifndef LWIP_IPV6_REASS +#define LWIP_IPV6_REASS (LWIP_IPV6) +#endif + +/** + * LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address + * is being resolved. + */ +#ifndef LWIP_ND6_QUEUEING +#define LWIP_ND6_QUEUEING (LWIP_IPV6) +#endif + +/** + * MEMP_NUM_ND6_QUEUE: Max number of IPv6 packets to queue during MAC resolution. + */ +#ifndef MEMP_NUM_ND6_QUEUE +#define MEMP_NUM_ND6_QUEUE 20 +#endif + +/** + * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache + */ +#ifndef LWIP_ND6_NUM_NEIGHBORS +#define LWIP_ND6_NUM_NEIGHBORS 10 +#endif + +/** + * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache + */ +#ifndef LWIP_ND6_NUM_DESTINATIONS +#define LWIP_ND6_NUM_DESTINATIONS 10 +#endif + +/** + * LWIP_ND6_NUM_PREFIXES: number of entries in IPv6 on-link prefixes cache + */ +#ifndef LWIP_ND6_NUM_PREFIXES +#define LWIP_ND6_NUM_PREFIXES 5 +#endif + +/** + * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache + */ +#ifndef LWIP_ND6_NUM_ROUTERS +#define LWIP_ND6_NUM_ROUTERS 3 +#endif + +/** + * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send + * (neighbor solicit and router solicit) + */ +#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT +#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3 +#endif + +/** + * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages + * to send during neighbor reachability detection. + */ +#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT +#define LWIP_ND6_MAX_UNICAST_SOLICIT 3 +#endif + +/** + * Unused: See ND RFC (time in milliseconds). + */ +#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME +#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000 +#endif + +/** + * Unused: See ND RFC + */ +#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT +#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3 +#endif + +/** + * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds). + * May be updated by router advertisement messages. + */ +#ifndef LWIP_ND6_REACHABLE_TIME +#define LWIP_ND6_REACHABLE_TIME 30000 +#endif + +/** + * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages + */ +#ifndef LWIP_ND6_RETRANS_TIMER +#define LWIP_ND6_RETRANS_TIMER 1000 +#endif + +/** + * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation + * message is sent, during neighbor reachability detection. + */ +#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME +#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000 +#endif + +/** + * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update + * Reachable time and retransmission timers, and netif MTU. + */ +#ifndef LWIP_ND6_ALLOW_RA_UPDATES +#define LWIP_ND6_ALLOW_RA_UPDATES 1 +#endif + +/** + * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during + * network startup. + */ +#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT +#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#endif + +/** + * LWIP_ND6_TCP_REACHABILITY_HINTS==1: Allow TCP to provide Neighbor Discovery + * with reachability hints for connected destinations. This helps avoid sending + * unicast neighbor solicitation messages. + */ +#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS +#define LWIP_ND6_TCP_REACHABILITY_HINTS 1 +#endif + +/** + * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862. + */ +#ifndef LWIP_IPV6_AUTOCONFIG +#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6) +#endif + +/** + * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts. + */ +#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS +#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1 +#endif + +/** + * LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration. + */ +#ifndef LWIP_IPV6_DHCP6 +#define LWIP_IPV6_DHCP6 0 +#endif + +/* + --------------------------------------- + ---------- Hook options --------------- + --------------------------------------- +*/ + +/* Hooks are undefined by default, define them to a function if you need them. */ + +/** + * LWIP_HOOK_IP4_INPUT(pbuf, input_netif): + * - called from ip_input() (IPv4) + * - pbuf: received struct pbuf passed to ip_input() + * - input_netif: struct netif on which the packet has been received + * Return values: + * - 0: Hook has not consumed the packet, packet is processed as normal + * - != 0: Hook has consumed the packet. + * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook + * (i.e. free it when done). + */ + +/** + * LWIP_HOOK_IP4_ROUTE(dest): + * - called from ip_route() (IPv4) + * - dest: destination IPv4 address + * Returns the destination netif or NULL if no destination netif is found. In + * that case, ip_route() continues as normal. + */ + +/** + * LWIP_HOOK_ETHARP_GET_GW(netif, dest): + * - called from etharp_output() (IPv4) + * - netif: the netif used for sending + * - dest: the destination IPv4 address + * Returns the IPv4 address of the gateway to handle the specified destination + * IPv4 address. If NULL is returned, the netif's default gateway is used. + * The returned address MUST be reachable on the specified netif! + * This function is meant to implement advanced IPv4 routing together with + * LWIP_HOOK_IP4_ROUTE(). The actual routing/gateway table implementation is + * not part of lwIP but can e.g. be hidden in the netif's state argument. +*/ + +/** + * LWIP_HOOK_VLAN_CHECK(netif, eth_hdr, vlan_hdr): + * - called from ethernet_input() if VLAN support is enabled + * - netif: struct netif on which the packet has been received + * - eth_hdr: struct eth_hdr of the packet + * - vlan_hdr: struct eth_vlan_hdr of the packet + * Return values: + * - 0: Packet must be dropped. + * - != 0: Packet must be accepted. + */ + +/** + * LWIP_HOOK_VLAN_SET(netif, eth_hdr, vlan_hdr): + * - called from etharp_raw() and etharp_send_ip() if VLAN support is enabled + * - netif: struct netif that the packet will be sent through + * - eth_hdr: struct eth_hdr of the packet + * - vlan_hdr: struct eth_vlan_hdr of the packet + * Return values: + * - 0: Packet shall not contain VLAN header. + * - != 0: Packet shall contain VLAN header. + * Hook can be used to set prio_vid field of vlan_hdr. + */ + +/* + --------------------------------------- + ---------- 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 + +/** + * IP6_DEBUG: Enable debugging for IPv6. + */ +#ifndef IP6_DEBUG +#define IP6_DEBUG LWIP_DBG_OFF +#endif + +#endif /* LWIP_HDR_OPT_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pbuf.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pbuf.h new file mode 100644 index 0000000..6bd6b30 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pbuf.h @@ -0,0 +1,193 @@ +/* + * 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_HDR_PBUF_H +#define LWIP_HDR_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 */ +#ifndef LWIP_SUPPORT_CUSTOM_PBUF +#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) +#endif + +/* @todo: We need a mechanism to prevent wasting memory in every pbuf + (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */ + +#define PBUF_TRANSPORT_HLEN 20 +#if LWIP_IPV6 +#define PBUF_IP_HLEN 40 +#else +#define PBUF_IP_HLEN 20 +#endif + +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 */ +} 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 +/** indicates this pbuf was received as link-level broadcast */ +#define PBUF_FLAG_LLBCAST 0x08U +/** indicates this pbuf was received as link-level multicast */ +#define PBUF_FLAG_LLMCAST 0x10U +/** indicates this pbuf includes a TCP FIN flag */ +#define PBUF_FLAG_TCP_FIN 0x20U + +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; +}; + +#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 */ + +#if LWIP_TCP && TCP_QUEUE_OOSEQ +/** 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 NO_SYS && PBUF_POOL_FREE_OOSEQ +extern volatile u8_t pbuf_free_ooseq_pending; +void pbuf_free_ooseq(void); +/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ() + at regular intervals from main level to check if ooseq pbufs need to be + freed! */ +#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \ + /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \ + ooseq queued pbufs now */ \ + pbuf_free_ooseq(); }}while(0) +#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/ +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */ + +/* 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); +#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); +#endif /* LWIP_SUPPORT_CUSTOM_PBUF */ +void pbuf_realloc(struct pbuf *p, u16_t size); +u8_t pbuf_header(struct pbuf *p, s16_t header_size); +u8_t pbuf_header_force(struct pbuf *p, s16_t header_size); +void pbuf_ref(struct pbuf *p); +u8_t pbuf_free(struct pbuf *p); +u8_t pbuf_clen(struct pbuf *p); +void pbuf_cat(struct pbuf *head, struct pbuf *tail); +void pbuf_chain(struct pbuf *head, struct pbuf *tail); +struct pbuf *pbuf_dechain(struct pbuf *p); +err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from); +u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset); +err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); +err_t pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset); +struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer); +#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); +#endif /* LWIP_CHECKSUM_ON_COPY */ +#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE +void pbuf_split_64k(struct pbuf *p, struct pbuf **rest); +#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ + +u8_t pbuf_get_at(struct pbuf* p, u16_t offset); +void pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data); +u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n); +u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset); +u16_t pbuf_strstr(struct pbuf* p, const char* substr); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_PBUF_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pppapi.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pppapi.h new file mode 100644 index 0000000..dd24af5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/pppapi.h @@ -0,0 +1,151 @@ +/* + * 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_PPPAPI_H__ +#define __LWIP_PPPAPI_H__ + +#include "lwip/opt.h" + +#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/sys.h" +#include "netif/ppp/ppp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pppapi_msg_msg { +#if !LWIP_TCPIP_CORE_LOCKING + sys_sem_t sem; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + int err; + ppp_pcb *ppp; + union { + struct { + u8_t authtype; + const char *user; + const char *passwd; + } setauth; +#if PPP_NOTIFY_PHASE + struct { + ppp_notify_phase_cb_fn notify_phase_cb; + } setnotifyphasecb; +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT + struct { + sio_fd_t fd; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } serialcreate; +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT + struct { + struct netif *ethif; + const char *service_name; + const char *concentrator_name; + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } ethernetcreate; +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + struct { + struct netif *netif; + ip_addr_t *ipaddr; + u16_t port; +#if PPPOL2TP_AUTH_SUPPORT + u8_t *secret; + u8_t secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + ppp_link_status_cb_fn link_status_cb; + void *ctx_cb; + } l2tpcreate; +#endif /* PPPOL2TP_SUPPORT */ + struct { + u16_t holdoff; + } open; + struct { + int cmd; + void *arg; + } ioctl; +#if LWIP_NETIF_STATUS_CALLBACK + struct { + netif_status_callback_fn status_callback; + } netifstatuscallback; +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK + struct { + netif_status_callback_fn link_callback; + } netiflinkcallback; +#endif /* LWIP_NETIF_LINK_CALLBACK */ + } msg; +}; + +struct pppapi_msg { + void (* function)(struct pppapi_msg_msg *msg); + struct pppapi_msg_msg msg; +}; + +/* API for application */ +ppp_pcb *pppapi_new(void); +void pppapi_set_default(ppp_pcb *pcb); +void pppapi_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd); +#if PPP_NOTIFY_PHASE +void pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb); +#endif /* PPP_NOTIFY_PHASE */ +#if PPPOS_SUPPORT +int pppapi_over_serial_create(ppp_pcb *pcb, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT +int pppapi_over_ethernet_create(ppp_pcb *pcb, struct netif *ethif, const char *service_name, + const char *concentrator_name, ppp_link_status_cb_fn link_status_cb, + void *ctx_cb); +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +int pppapi_over_l2tp_create(ppp_pcb *pcb, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOL2TP_SUPPORT */ +int pppapi_open(ppp_pcb *pcb, u16_t holdoff); +int pppapi_close(ppp_pcb *pcb); +void pppapi_sighup(ppp_pcb *pcb); +int pppapi_delete(ppp_pcb *pcb); +int pppapi_ioctl(ppp_pcb *pcb, int cmd, void *arg); +#if LWIP_NETIF_STATUS_CALLBACK +void pppapi_set_netif_statuscallback(ppp_pcb *pcb, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK +void pppapi_set_netif_linkcallback(ppp_pcb *pcb, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_PPP_API */ + +#endif /* __LWIP_PPPAPI_H__ */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/raw.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/raw.h new file mode 100644 index 0000000..52964f5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/raw.h @@ -0,0 +1,136 @@ +/* + * 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_HDR_RAW_H +#define LWIP_HDR_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/lwip_ip.h" +#include "lwip/ip_addr.h" +#include "lwip/ip6_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); + +#if LWIP_IPV6 +/** Function prototype for raw pcb IPv6 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 IPv6 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_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6; +#else +#define RAW_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +struct raw_pcb { + /* Common members of all PCB types */ + IP_PCB; + + struct raw_pcb *next; + + u8_t protocol; + + /** receive callback function */ + union { + raw_recv_fn ip4; + RAW_PCB_RECV_IP6 + } recv; + /* user-supplied argument for the recv callback */ + void *recv_arg; +#if LWIP_IPV6 + /* fields for handling checksum computations as per RFC3542. */ + u16_t chksum_offset; + u8_t chksum_reqd; +#endif +}; + +/* The following functions is the application layer interface to the + RAW code. */ +struct raw_pcb * raw_new (u8_t proto); +void raw_remove (struct raw_pcb *pcb); +err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr); +err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr); + +void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg); +err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr); +err_t raw_send (struct raw_pcb *pcb, struct pbuf *p); + +#if LWIP_IPV6 +struct raw_pcb * raw_new_ip6 (u8_t proto); +#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr)) +#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr)) +#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg) +#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr)) +#endif /* LWIP_IPV6 */ + +/* The following functions are the lower layer interface to RAW. */ +u8_t raw_input (struct pbuf *p, struct netif *inp); +#define raw_init() /* Compatibility define, no init needed. */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_RAW */ + +#endif /* LWIP_HDR_RAW_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sio.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sio.h new file mode 100644 index 0000000..23d116b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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); +#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); +#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); +#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); +#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); +#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); +#endif + +#ifndef sio_read_abort +/** + * Aborts a blocking sio_read() call. + * + * @param fd serial device handle + */ +void sio_read_abort(sio_fd_t fd); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SIO_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp.h new file mode 100644 index 0000000..101bb66 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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_HDR_SNMP_H +#define LWIP_HDR_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_sysdescr(const u8_t* str, const u8_t* len); +void snmp_set_sysobjid(const struct snmp_obj_id *oid); +void snmp_get_sysobjid_ptr(const 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_sysdescr(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_HDR_SNMP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_asn1.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_asn1.h new file mode 100644 index 0000000..9ef9228 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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_HDR_SNMP_ASN1_H +#define LWIP_HDR_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_HDR_SNMP_ASN1_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_msg.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_msg.h new file mode 100644 index 0000000..3f65395 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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_HDR_SNMP_MSG_H +#define LWIP_HDR_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_HDR_SNMP_MSG_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_structs.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/snmp_structs.h new file mode 100644 index 0000000..b69160c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/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_HDR_SNMP_STRUCTS_H +#define LWIP_HDR_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 functions 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_HDR_SNMP_STRUCTS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sockets.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sockets.h new file mode 100644 index 0000000..97897c8 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sockets.h @@ -0,0 +1,482 @@ +/* + * 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_HDR_SOCKETS_H +#define LWIP_HDR_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/err.h" +#include "lwip/inet.h" +#include "lwip/inet6.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* If your port already typedef's sa_family_t, define SA_FAMILY_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED) +typedef u8_t sa_family_t; +#endif +/* If your port already typedef's in_port_t, define IN_PORT_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(in_port_t) && !defined(IN_PORT_T_DEFINED) +typedef u16_t in_port_t; +#endif + +/* members are in network byte order */ +struct sockaddr_in { + u8_t sin_len; + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; + +#if LWIP_IPV6 +struct sockaddr_in6 { + u8_t sin6_len; /* length of this structure */ + sa_family_t sin6_family; /* AF_INET6 */ + in_port_t sin6_port; /* Transport layer port # */ + u32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ +}; +#endif /* LWIP_IPV6 */ + +struct sockaddr { + u8_t sa_len; + sa_family_t sa_family; +#if LWIP_IPV6 + char sa_data[22]; +#else /* LWIP_IPV6 */ + char sa_data[14]; +#endif /* LWIP_IPV6 */ +}; + +struct sockaddr_storage { + u8_t s2_len; + sa_family_t ss_family; + char s2_data1[2]; + u32_t s2_data2[3]; +#if LWIP_IPV6 + u32_t s2_data3[2]; +#endif /* LWIP_IPV6 */ +}; + +/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef u32_t socklen_t; +#endif + +struct lwip_sock; + +#if !LWIP_TCPIP_CORE_LOCKING +/** Maximum optlen used by setsockopt/getsockopt */ +#define LWIP_SETGETSOCKOPT_MAXOPTLEN 16 + +/** 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 index for which to change options */ + int s; + /** 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 */ +#if LWIP_MPU_COMPATIBLE + u8_t optval[LWIP_SETGETSOCKOPT_MAXOPTLEN]; +#else + void* optval; +#endif + /** size of *optval */ + socklen_t optlen; + /** if an error occurs, it is temporarily stored here */ + err_t err; + /** semaphore to wake up the calling task */ + void* completed_sem; +}; +#endif /* !LWIP_TCPIP_CORE_LOCKING */ + +/* 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_REUSEADDR 0x0004 /* Allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */ + + +/* + * Additional options, not kept in so_options. + */ +#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */ +#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_DONTLINGER ((int)(~SO_LINGER)) +#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ +#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 +#if LWIP_IPV6 +#define AF_INET6 10 +#else /* LWIP_IPV6 */ +#define AF_INET6 AF_UNSPEC +#endif /* LWIP_IPV6 */ +#define PF_INET AF_INET +#define PF_INET6 AF_INET6 +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 +#if LWIP_IPV6 +#define IPPROTO_IPV6 41 +#define IPPROTO_ICMPV6 58 +#endif /* LWIP_IPV6 */ +#define IPPROTO_UDPLITE 136 +#define IPPROTO_RAW 255 + +/* 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_IPV6 +/* + * Options for level IPPROTO_IPV6 + */ +#define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ +#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ +#endif /* LWIP_IPV6 */ + +#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 */ + +#define lwip_socket_init() /* Compatibility define, no init needed. */ + +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) +#define fcntl(a,b,c) lwip_fcntl(a,b,c) +#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */ + +#if LWIP_IPV6 +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET6) ? ip6addr_ntoa_r((src),(dst),(size)) \ + : (((af) == AF_INET) ? ipaddr_ntoa_r((src),(dst),(size)) : NULL)) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET6) ? inet6_aton((src),(dst)) \ + : (((af) == AF_INET) ? inet_aton((src),(dst)) : 0)) +#else /* LWIP_IPV6 */ +#define inet_ntop(af,src,dst,size) \ + (((af) == AF_INET) ? ipaddr_ntoa_r((src),(dst),(size)) : NULL) +#define inet_pton(af,src,dst) \ + (((af) == AF_INET) ? inet_aton((src),(dst)) : 0) +#endif /* LWIP_IPV6 */ + +#endif /* LWIP_COMPAT_SOCKETS */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_SOCKET */ + +#endif /* LWIP_HDR_SOCKETS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/stats.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/stats.h new file mode 100644 index 0000000..6385318 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/stats.h @@ -0,0 +1,347 @@ +/* + * 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_HDR_STATS_H +#define LWIP_HDR_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 */ + STAT_COUNTER err; + mem_size_t avail; + mem_size_t used; + mem_size_t max; + 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 +#if IP6_STATS + struct stats_proto ip6; +#endif +#if ICMP6_STATS + struct stats_proto icmp6; +#endif +#if IP6_FRAG_STATS + struct stats_proto ip6_frag; +#endif +#if MLD6_STATS + struct stats_igmp mld6; +#endif +#if ND6_STATS + struct stats_proto nd6; +#endif +}; + +extern struct stats_ lwip_stats; + +void stats_init(void); + +#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, "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 + +#if IP6_STATS +#define IP6_STATS_INC(x) STATS_INC(x) +#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6") +#else +#define IP6_STATS_INC(x) +#define IP6_STATS_DISPLAY() +#endif + +#if ICMP6_STATS +#define ICMP6_STATS_INC(x) STATS_INC(x) +#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6") +#else +#define ICMP6_STATS_INC(x) +#define ICMP6_STATS_DISPLAY() +#endif + +#if IP6_FRAG_STATS +#define IP6_FRAG_STATS_INC(x) STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG") +#else +#define IP6_FRAG_STATS_INC(x) +#define IP6_FRAG_STATS_DISPLAY() +#endif + +#if MLD6_STATS +#define MLD6_STATS_INC(x) STATS_INC(x) +#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1") +#else +#define MLD6_STATS_INC(x) +#define MLD6_STATS_DISPLAY() +#endif + +#if ND6_STATS +#define ND6_STATS_INC(x) STATS_INC(x) +#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND") +#else +#define ND6_STATS_INC(x) +#define ND6_STATS_DISPLAY() +#endif + +/* Display of statistics */ +#if LWIP_STATS_DISPLAY +void stats_display(void); +void stats_display_proto(struct stats_proto *proto, const char *name); +void stats_display_igmp(struct stats_igmp *igmp, const char *name); +void stats_display_mem(struct stats_mem *mem, const char *name); +void stats_display_memp(struct stats_mem *mem, int index); +void stats_display_sys(struct stats_sys *sys); +#else /* LWIP_STATS_DISPLAY */ +#define stats_display() +#define stats_display_proto(proto, name) +#define stats_display_igmp(igmp, name) +#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_HDR_STATS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sys.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sys.h new file mode 100644 index 0000000..868bbf1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/sys.h @@ -0,0 +1,334 @@ +/* + * 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_HDR_SYS_H +#define LWIP_HDR_SYS_H + +#include "lwip/opt.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_sem_valid(s) 0 +#define sys_sem_set_invalid(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_mutex_valid(mu) 0 +#define sys_mutex_set_invalid(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_mbox_valid(m) +#define sys_mbox_set_invalid(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) */ + +/** 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 semaphore 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 (minimum) 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 (0 = wait forever) + * @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 timeout 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 + * @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 anything else. */ +void sys_init(void); + +#ifndef sys_jiffies +/** Ticks/jiffies since power up. */ +u32_t sys_jiffies(void); +#endif + +/** Returns the current time in milliseconds, + * may be the same as sys_jiffies or at least based on it. */ +u32_t sys_now(void); + +/* 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); +void sys_arch_unprotect(sys_prot_t pval); + +#else + +#define SYS_ARCH_DECL_PROTECT(lev) +#define SYS_ARCH_PROTECT(lev) +#define SYS_ARCH_UNPROTECT(lev) + +#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_HDR_SYS_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp.h new file mode 100644 index 0000000..4a9bb30 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp.h @@ -0,0 +1,421 @@ +/* + * 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_HDR_TCP_H +#define LWIP_HDR_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/lwip_ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct tcp_pcb; + +//Realtek add +extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */ +//Realtek add end + +/** 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); + +#if LWIP_WND_SCALE +#define RCV_WND_SCALE(pcb, wnd) (((wnd) >> (pcb)->rcv_scale)) +#define SND_WND_SCALE(pcb, wnd) (((wnd) << (pcb)->snd_scale)) +typedef u32_t tcpwnd_size_t; +typedef u16_t tcpflags_t; +#else +#define RCV_WND_SCALE(pcb, wnd) (wnd) +#define SND_WND_SCALE(pcb, wnd) (wnd) +typedef u16_t tcpwnd_size_t; +typedef u8_t tcpflags_t; +#endif + +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 aborts the 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 */ \ + void *callback_arg; \ + /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \ + DEF_ACCEPT_CALLBACK \ + enum tcp_state state; /* TCP state */ \ + u8_t prio; \ + /* 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; + + tcpflags_t flags; +#define TF_ACK_DELAY ((tcpflags_t)0x0001U) /* Delayed ACK. */ +#define TF_ACK_NOW ((tcpflags_t)0x0002U) /* Immediate ACK. */ +#define TF_INFR ((tcpflags_t)0x0004U) /* In fast recovery. */ +#define TF_TIMESTAMP ((tcpflags_t)0x0008U) /* Timestamp option enabled */ +#define TF_RXCLOSED ((tcpflags_t)0x0010U) /* rx closed by tcp_shutdown */ +#define TF_FIN ((tcpflags_t)0x0020U) /* Connection was closed locally (FIN segment enqueued). */ +#define TF_NODELAY ((tcpflags_t)0x0040U) /* Disable Nagle algorithm */ +#define TF_NAGLEMEMERR ((tcpflags_t)0x0080U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */ +#if LWIP_WND_SCALE +#define TF_WND_SCALE ((tcpflags_t)0x0100U) /* Window Scale option enabled */ +#endif + + /* the rest of the fields are in host byte order + as we have to do some math with them */ + + /* Timers */ + u8_t polltmr, pollinterval; + u8_t last_timer; + u32_t tmr; + + /* receiver variables */ + u32_t rcv_nxt; /* next seqno expected */ + tcpwnd_size_t rcv_wnd; /* receiver window available */ + tcpwnd_size_t rcv_ann_wnd; /* receiver window to announce */ + u32_t rcv_ann_right_edge; /* announced right edge of window */ + + /* 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 */ + u8_t dupacks; + u32_t lastack; /* Highest acknowledged seqno. */ + + /* congestion avoidance/control variables */ + tcpwnd_size_t cwnd; + tcpwnd_size_t ssthresh; + + /* sender variables */ + u32_t snd_nxt; /* next new seqno to be sent */ + 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. */ + tcpwnd_size_t snd_wnd; /* sender window */ + tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */ + + tcpwnd_size_t acked; + + tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */ +#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3) + u16_t snd_queuelen; /* Available buffer space for sending (in pbufs). */ + +#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 */ + u8_t persist_cnt; + /* Persist timer back-off */ + u8_t persist_backoff; + + /* KEEPALIVE counter */ + u8_t keep_cnt_sent; + +#if LWIP_WND_SCALE + u8_t snd_scale; + u8_t rcv_scale; +#endif +}; + +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_IPV6 + u8_t accept_any_ip_version; +#endif /* LWIP_IPV6 */ +}; + +#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); + +void tcp_arg (struct tcp_pcb *pcb, void *arg); +void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept); +void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv); +void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent); +void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval); +void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err); + +#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 &= (tcpflags_t)~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); +err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port, tcp_connected_fn connected); + +struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) + +void tcp_abort (struct tcp_pcb *pcb); +err_t tcp_close (struct tcp_pcb *pcb); +err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx); + +/* 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); + +void tcp_setprio (struct tcp_pcb *pcb, u8_t prio); + +#define TCP_PRIO_MIN 1 +#define TCP_PRIO_NORMAL 64 +#define TCP_PRIO_MAX 127 + +err_t tcp_output (struct tcp_pcb *pcb); + + +const char* tcp_debug_state_str(enum tcp_state s); + +#if LWIP_IPV6 +struct tcp_pcb * tcp_new_ip6 (void); +#define tcp_bind_ip6(pcb, ip6addr, port) \ + tcp_bind(pcb, ip6_2_ip(ip6addr), port) +#define tcp_connect_ip6(pcb, ip6addr, port, connected) \ + tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected) +struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog); +#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG) +#else /* LWIP_IPV6 */ +#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog) +#define tcp_listen_dual(pcb) tcp_listen(pcb) +#endif /* LWIP_IPV6 */ + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* LWIP_HDR_TCP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp_impl.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp_impl.h new file mode 100644 index 0000000..ac1a716 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcp_impl.h @@ -0,0 +1,538 @@ +/* + * 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_HDR_TCP_IMPL_H +#define LWIP_HDR_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/mem.h" +#include "lwip/pbuf.h" +#include "lwip/lwip_ip.h" +#include "lwip/icmp.h" +#include "lwip/err.h" +#include "lwip/ip6.h" +#include "lwip/ip6_addr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Functions for interfacing with TCP: */ + +/* Lower layer interface to TCP: */ +void tcp_init (void); /* Initialize this module. */ +void tcp_tmr (void); /* 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); +void tcp_fasttmr (void); + + +/* Only used by IP to pass a TCP segment to TCP: */ +void tcp_input (struct pbuf *p, struct netif *inp); +/* Used within the TCP code only: */ +struct tcp_pcb * tcp_alloc (u8_t prio); +void tcp_abandon (struct tcp_pcb *pcb, int reset); +err_t tcp_send_empty_ack(struct tcp_pcb *pcb); +void tcp_rexmit (struct tcp_pcb *pcb); +void tcp_rexmit_rto (struct tcp_pcb *pcb); +void tcp_rexmit_fast (struct tcp_pcb *pcb); +u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb); +err_t tcp_process_refused_data(struct tcp_pcb *pcb); + +/** + * 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))) || \ + ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \ + ) ? 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)((u32_t)(a) - (u32_t)(b)) < 0) +#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0) +#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0) +#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(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 250 /* 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 7200000UL /* Default KEEPALIVE timer in milliseconds */ +#endif + +#ifndef TCP_KEEPINTVL_DEFAULT +#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */ +#endif + +#ifndef TCP_KEEPCNT_DEFAULT +#define TCP_KEEPCNT_DEFAULT 9U /* 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_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS) + +#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) ? 1U : 0U)) + +/** 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 successfully 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 segments on a queue */ + struct pbuf *p; /* buffer containing data + TCP header */ + 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' */ +#define TF_SEG_OPTS_WND_SCALE (u8_t)0x08U /* Include WND SCALE option */ + struct tcp_hdr *tcphdr; /* the TCP header */ +}; + +#define LWIP_TCP_OPT_EOL 0 +#define LWIP_TCP_OPT_NOP 1 +#define LWIP_TCP_OPT_MSS 2 +#define LWIP_TCP_OPT_WS 3 +#define LWIP_TCP_OPT_TS 8 + +#define LWIP_TCP_OPT_LEN_MSS 4 +#if LWIP_TCP_TIMESTAMPS +#define LWIP_TCP_OPT_LEN_TS 10 +#define LWIP_TCP_OPT_LEN_TS_OUT 12 /* aligned for output (includes NOP padding) */ +#else +#define LWIP_TCP_OPT_LEN_TS_OUT 0 +#endif +#if LWIP_WND_SCALE +#define LWIP_TCP_OPT_LEN_WS 3 +#define LWIP_TCP_OPT_LEN_WS_OUT 4 /* aligned for output (includes NOP padding) */ +#else +#define LWIP_TCP_OPT_LEN_WS_OUT 0 +#endif + +#define LWIP_TCP_OPT_LENGTH(flags) \ + (flags & TF_SEG_OPTS_MSS ? LWIP_TCP_OPT_LEN_MSS : 0) + \ + (flags & TF_SEG_OPTS_TS ? LWIP_TCP_OPT_LEN_TS_OUT : 0) + \ + (flags & TF_SEG_OPTS_WND_SCALE ? LWIP_TCP_OPT_LEN_WS_OUT : 0) + +/** This returns a TCP header option for MSS in an u32_t */ +#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF)) + +#if LWIP_WND_SCALE +#define TCPWNDSIZE_F U32_F +#define TCPWND_MAX 0xFFFFFFFFU +#else /* LWIP_WND_SCALE */ +#define TCPWNDSIZE_F U16_F +#define TCPWND_MAX 0xFFFFU +#endif /* LWIP_WND_SCALE */ + +/* Global variables: */ +extern struct tcp_pcb *tcp_input_pcb; +extern u32_t tcp_ticks; +extern u8_t tcp_active_pcbs_changed; + +/* 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 */ + +#define TCP_REG_ACTIVE(npcb) \ + do { \ + TCP_REG(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_RMV_ACTIVE(npcb) \ + do { \ + TCP_RMV(&tcp_active_pcbs, npcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + +#define TCP_PCB_REMOVE_ACTIVE(pcb) \ + do { \ + tcp_pcb_remove(&tcp_active_pcbs, pcb); \ + tcp_active_pcbs_changed = 1; \ + } while (0) + + +/* Internal functions: */ +struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb); +void tcp_pcb_purge(struct tcp_pcb *pcb); +void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb); + +void tcp_segs_free(struct tcp_seg *seg); +void tcp_seg_free(struct tcp_seg *seg); +struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg); + +#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); +err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags); + +void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg); + +void tcp_rst_impl(u32_t seqno, u32_t ackno, + ipX_addr_t *local_ip, ipX_addr_t *remote_ip, + u16_t local_port, u16_t remote_port +#if LWIP_IPV6 + , u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \ + tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port) +#endif /* LWIP_IPV6 */ + +u32_t tcp_next_iss(void); + +void tcp_keepalive(struct tcp_pcb *pcb); +void tcp_zero_window_probe(struct tcp_pcb *pcb); + +#if TCP_CALCULATE_EFF_SEND_MSS +u16_t tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest +#if LWIP_IPV6 + , ipX_addr_t *src, u8_t isipv6 +#endif /* LWIP_IPV6 */ + ); +#if LWIP_IPV6 +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6) +#else /* LWIP_IPV6 */ +#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest) +#endif /* LWIP_IPV6 */ +#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); +#endif /* LWIP_CALLBACK_API */ + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void tcp_debug_print(struct tcp_hdr *tcphdr); +void tcp_debug_print_flags(u8_t flags); +void tcp_debug_print_state(enum tcp_state s); +void tcp_debug_print_pcbs(void); +s16_t tcp_pcbs_sane(void); +#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); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP */ + +#endif /* LWIP_HDR_TCP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcpip.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcpip.h new file mode 100644 index 0000000..fc6b421 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/tcpip.h @@ -0,0 +1,232 @@ +/* + * 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_HDR_TCPIP_H +#define LWIP_HDR_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/pppapi.h" +#include "lwip/pbuf.h" +#include "lwip/api.h" +#include "lwip/sys.h" +#include "lwip/lwip_timers.h" //Evan modified timers.h to 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) +#ifdef LWIP_DEBUG +#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */ +#else +#define TCIP_APIMSG_SET_ERR(m, e) +#endif +#if LWIP_NETCONN_SEM_PER_THREAD +#define TCPIP_APIMSG_SET_SEM(m) ((m)->msg.op_completed_sem = LWIP_NETCONN_THREAD_SEM_GET()) +#else +#define TCPIP_APIMSG_SET_SEM(m) +#endif +#define TCPIP_APIMSG_NOERR(m,f) do { \ + TCIP_APIMSG_SET_ERR(m, ERR_VAL); \ + TCPIP_APIMSG_SET_SEM(m); \ + LOCK_TCPIP_CORE(); \ + f(&((m)->msg)); \ + UNLOCK_TCPIP_CORE(); \ +} while(0) +#define TCPIP_APIMSG(m,f,e) do { \ + TCPIP_APIMSG_NOERR(m,f); \ + (e) = (m)->msg.err; \ +} while(0) +#define TCPIP_APIMSG_ACK(m) +#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m) +#define TCPIP_NETIFAPI_ACK(m) +#define TCPIP_PPPAPI(m) tcpip_pppapi_lock(m) +#define TCPIP_PPPAPI_ACK(m) +#else /* LWIP_TCPIP_CORE_LOCKING */ +#define LOCK_TCPIP_CORE() +#define UNLOCK_TCPIP_CORE() +#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0) +#define TCPIP_APIMSG_ACK(m) sys_sem_signal(LWIP_API_MSG_SEM(m)) +#define TCPIP_NETIFAPI(m) tcpip_netifapi(m) +#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem) +#define TCPIP_PPPAPI(m) tcpip_pppapi(m) +#define TCPIP_PPPAPI_ACK(m) sys_sem_signal(&m->sem) +#endif /* LWIP_TCPIP_CORE_LOCKING */ + + +#if LWIP_MPU_COMPATIBLE +#define API_VAR_REF(name) (*(name)) +#define API_VAR_DECLARE(type, name) type * name +#define API_VAR_ALLOC(type, pool, name) do { \ + name = (type *)memp_malloc(pool); \ + if (name == NULL) { \ + return ERR_MEM; \ + } \ + } while(0) +#define API_VAR_ALLOC_DONTFAIL(type, pool, name) do { \ + name = (type *)memp_malloc(pool); \ + LWIP_ASSERT("pool empty", name != NULL); \ + } while(0) +#define API_VAR_FREE(pool, name) memp_free(pool, name) +#define API_EXPR_REF(expr) &(expr) +#define API_EXPR_DEREF(expr) expr +#else /* LWIP_MPU_COMPATIBLE */ +#define API_VAR_REF(name) name +#define API_VAR_DECLARE(type, name) type name +#define API_VAR_ALLOC(type, pool, name) +#define API_VAR_ALLOC_DONTFAIL(type, pool, name) +#define API_VAR_FREE(pool, name) +#define API_EXPR_REF(expr) expr +#define API_EXPR_DEREF(expr) *(expr) +#endif /* LWIP_MPU_COMPATIBLE */ + + + +/** 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); + +/* Forward declarations */ +struct tcpip_callback_msg; + +void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg); + +#if LWIP_NETCONN || LWIP_SOCKET +err_t tcpip_apimsg(struct api_msg *apimsg); +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + +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 */ + +#if LWIP_PPP_API +err_t tcpip_pppapi(struct pppapi_msg *pppapimsg); +#if LWIP_TCPIP_CORE_LOCKING +err_t tcpip_pppapi_lock(struct pppapi_msg *pppapimsg); +#endif /* LWIP_TCPIP_CORE_LOCKING */ +#endif /* LWIP_PPP_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) + +struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx); +void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg); +err_t tcpip_trycallback(struct tcpip_callback_msg* msg); + +/* 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 || LWIP_SOCKET + TCPIP_MSG_API, +#endif /* LWIP_NETCONN || LWIP_SOCKET */ + TCPIP_MSG_INPKT, +#if LWIP_NETIF_API + TCPIP_MSG_NETIFAPI, +#endif /* LWIP_NETIF_API */ +#if LWIP_PPP_API + TCPIP_MSG_PPPAPI, +#endif /* LWIP_PPP_API */ +#if LWIP_TCPIP_TIMEOUT + TCPIP_MSG_TIMEOUT, + TCPIP_MSG_UNTIMEOUT, +#endif /* LWIP_TCPIP_TIMEOUT */ + TCPIP_MSG_CALLBACK, + TCPIP_MSG_CALLBACK_STATIC +}; + +struct tcpip_msg { + enum tcpip_msg_type type; + sys_sem_t *sem; + union { +#if LWIP_NETCONN || LWIP_SOCKET + struct api_msg *apimsg; +#endif /* LWIP_NETCONN || LWIP_SOCKET */ +#if LWIP_NETIF_API + struct netifapi_msg *netifapimsg; +#endif /* LWIP_NETIF_API */ +#if LWIP_PPP_API + struct pppapi_msg *pppapimsg; +#endif /* LWIP_PPP_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_HDR_TCPIP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/udp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/udp.h new file mode 100644 index 0000000..7593ca1 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/lwip/udp.h @@ -0,0 +1,221 @@ +/* + * 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_HDR_UDP_H +#define LWIP_HDR_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/lwip_ip.h" +#include "lwip/ip6_addr.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); + +#if LWIP_IPV6 +/** Function prototype for udp pcb IPv6 receive callback functions + * The callback is responsible for freeing the pbuf + * if it's not used any more. + * + * @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 IPv6 address from which the packet was received + * @param port the remote port from which the packet was received + */ +typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip6_addr_t *addr, u16_t port); +#endif /* LWIP_IPV6 */ + +#if LWIP_IPV6 +#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6; +#else +#define UDP_PCB_RECV_IP6 +#endif /* LWIP_IPV6 */ + +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 */ + union { + udp_recv_fn ip4; + UDP_PCB_RECV_IP6 + }recv; + /** user-supplied argument for the recv callback */ + void *recv_arg; +}; +/* udp_pcbs export for external 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); +void udp_remove (struct udp_pcb *pcb); +err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr, + u16_t port); +void udp_disconnect (struct udp_pcb *pcb); +void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, + void *recv_arg); +err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif); +err_t udp_sendto_if_src(struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port, + struct netif *netif, ip_addr_t *src_ip); +err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, + ip_addr_t *dst_ip, u16_t dst_port); +err_t udp_send (struct udp_pcb *pcb, struct pbuf *p); + +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +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); +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); +err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, + u8_t have_chksum, u16_t chksum); +err_t udp_sendto_if_src_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, ip_addr_t *src_ip); +#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ + +#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); + +void udp_init (void); + +#if LWIP_IPV6 +struct udp_pcb * udp_new_ip6(void); +#define udp_bind_ip6(pcb, ip6addr, port) \ + udp_bind(pcb, ip6_2_ip(ip6addr), port) +#define udp_connect_ip6(pcb, ip6addr, port) \ + udp_connect(pcb, ip6_2_ip(ip6addr), port) +#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \ + udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg) +#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \ + udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port) +#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \ + udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif) +#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP +#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \ + udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum) +#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \ + udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum) +#endif /*LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ +#endif /* LWIP_IPV6 */ + +#if UDP_DEBUG +void udp_debug_print(struct udp_hdr *udphdr); +#else +#define udp_debug_print(udphdr) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_UDP */ + +#endif /* LWIP_HDR_UDP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/etharp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/etharp.h new file mode 100644 index 0000000..39f148a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/etharp.h @@ -0,0 +1,229 @@ +/* + * 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 LWIP_HDR_NETIF_ETHARP_H +#define LWIP_HDR_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/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_FLD_8(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_FLD_8(u8_t padding[ETH_PAD_SIZE]); +#endif + PACK_STRUCT_FLD_S(struct eth_addr dest); + PACK_STRUCT_FLD_S(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 prio_vid); + PACK_STRUCT_FIELD(u16_t tpid); +} 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_FLD_8(u8_t hwlen); + PACK_STRUCT_FLD_8(u8_t protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FLD_S(struct eth_addr shwaddr); + PACK_STRUCT_FLD_S(struct ip_addr2 sipaddr); + PACK_STRUCT_FLD_S(struct eth_addr dhwaddr); + PACK_STRUCT_FLD_S(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_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR) +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) +#define SIZEOF_ETHARP_PACKET_TX (SIZEOF_ETHARP_PACKET + SIZEOF_VLAN_HDR) +#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ +#define SIZEOF_ETHARP_PACKET_TX SIZEOF_ETHARP_PACKET +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + +/** 1 seconds period */ +#define ARP_TMR_INTERVAL 1000 + +#define ETHTYPE_ARP 0x0806U +#define ETHTYPE_IP 0x0800U +#define ETHTYPE_VLAN 0x8100U +#define ETHTYPE_IPV6 0x86DDU +#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */ +#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */ + +/** 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(dst, src) SMEMCPY(dst, src, 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(dst, src) SMEMCPY(dst, src, 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, no init needed. */ +void etharp_tmr(void); +s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr, + struct eth_addr **eth_ret, ip_addr_t **ip_ret); +err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr); +err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q); +err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr); +/** 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); +err_t etharp_remove_static_entry(ip_addr_t *ipaddr); +#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); +#endif /* LWIP_AUTOIP */ + +#endif /* LWIP_ARP */ + +err_t ethernet_input(struct pbuf *p, struct netif *netif); + +#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0) + +extern const struct eth_addr ethbroadcast, ethzero; + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_ARP || LWIP_ETHERNET */ + +#endif /* LWIP_HDR_NETIF_ARP_H */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/loopif.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/loopif.h new file mode 100644 index 0000000..e69de29 diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ccp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ccp.h new file mode 100644 index 0000000..f106cb5 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ccp.h @@ -0,0 +1,57 @@ +/* + * ccp.h - Definitions for PPP Compression Control Protocol. + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ccp.h,v 1.12 2004/11/04 10:02:26 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +typedef struct ccp_options { + bool bsd_compress; /* do BSD Compress? */ + bool deflate; /* do Deflate? */ + bool predictor_1; /* do Predictor-1? */ + bool predictor_2; /* do Predictor-2? */ + bool deflate_correct; /* use correct code for deflate? */ + bool deflate_draft; /* use draft RFC code for deflate? */ + bool mppe; /* do MPPE? */ + u_short bsd_bits; /* # bits/code for BSD Compress */ + u_short deflate_size; /* lg(window size) for Deflate */ + short method; /* code for chosen compression method */ +} ccp_options; + +extern fsm ccp_fsm[]; +extern ccp_options ccp_wantoptions[]; +extern ccp_options ccp_gotoptions[]; +extern ccp_options ccp_allowoptions[]; +extern ccp_options ccp_hisoptions[]; + +extern const struct protent ccp_protent; + +#endif /* PPP_SUPPORT && CCP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-md5.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-md5.h new file mode 100644 index 0000000..a05a157 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-md5.h @@ -0,0 +1,36 @@ +/* + * chap-md5.h - New CHAP/MD5 implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +extern const struct chap_digest_type md5_digest; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-new.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-new.h new file mode 100644 index 0000000..5753fb6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap-new.h @@ -0,0 +1,193 @@ +/* + * chap-new.c - New CHAP implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef CHAP_H +#define CHAP_H + +#include "ppp.h" + +/* + * CHAP packets begin with a standard header with code, id, len (2 bytes). + */ +#define CHAP_HDRLEN 4 + +/* + * Values for the code field. + */ +#define CHAP_CHALLENGE 1 +#define CHAP_RESPONSE 2 +#define CHAP_SUCCESS 3 +#define CHAP_FAILURE 4 + +/* + * CHAP digest codes. + */ +#define CHAP_MD5 5 +#if MSCHAP_SUPPORT +#define CHAP_MICROSOFT 0x80 +#define CHAP_MICROSOFT_V2 0x81 +#endif /* MSCHAP_SUPPORT */ + +/* + * Semi-arbitrary limits on challenge and response fields. + */ +#define MAX_CHALLENGE_LEN 64 +#define MAX_RESPONSE_LEN 64 + +/* + * These limits apply to challenge and response packets we send. + * The +4 is the +1 that we actually need rounded up. + */ +#define CHAL_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_CHALLENGE_LEN + MAXNAMELEN) +#define RESP_MAX_PKTLEN (PPP_HDRLEN + CHAP_HDRLEN + 4 + MAX_RESPONSE_LEN + MAXNAMELEN) + +/* bitmask of supported algorithms */ +#if MSCHAP_SUPPORT +#define MDTYPE_MICROSOFT_V2 0x1 +#define MDTYPE_MICROSOFT 0x2 +#endif /* MSCHAP_SUPPORT */ +#define MDTYPE_MD5 0x4 +#define MDTYPE_NONE 0 + +#if MSCHAP_SUPPORT +/* Return the digest alg. ID for the most preferred digest type. */ +#define CHAP_DIGEST(mdtype) \ + ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \ + ((mdtype) & MDTYPE_MICROSOFT_V2)? CHAP_MICROSOFT_V2: \ + ((mdtype) & MDTYPE_MICROSOFT)? CHAP_MICROSOFT: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_DIGEST(mdtype) \ + ((mdtype) & MDTYPE_MD5)? CHAP_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* Return the bit flag (lsb set) for our most preferred digest type. */ +#define CHAP_MDTYPE(mdtype) ((mdtype) ^ ((mdtype) - 1)) & (mdtype) + +/* Return the bit flag for a given digest algorithm ID. */ +#if MSCHAP_SUPPORT +#define CHAP_MDTYPE_D(digest) \ + ((digest) == CHAP_MICROSOFT_V2)? MDTYPE_MICROSOFT_V2: \ + ((digest) == CHAP_MICROSOFT)? MDTYPE_MICROSOFT: \ + ((digest) == CHAP_MD5)? MDTYPE_MD5: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_MDTYPE_D(digest) \ + ((digest) == CHAP_MD5)? MDTYPE_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* Can we do the requested digest? */ +#if MSCHAP_SUPPORT +#define CHAP_CANDIGEST(mdtype, digest) \ + ((digest) == CHAP_MICROSOFT_V2)? (mdtype) & MDTYPE_MICROSOFT_V2: \ + ((digest) == CHAP_MICROSOFT)? (mdtype) & MDTYPE_MICROSOFT: \ + ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \ + 0 +#else /* !MSCHAP_SUPPORT */ +#define CHAP_CANDIGEST(mdtype, digest) \ + ((digest) == CHAP_MD5)? (mdtype) & MDTYPE_MD5: \ + 0 +#endif /* MSCHAP_SUPPORT */ + +/* + * The code for each digest type has to supply one of these. + */ +struct chap_digest_type { + int code; + +#if PPP_SERVER + /* + * Note: challenge and response arguments below are formatted as + * a length byte followed by the actual challenge/response data. + */ + void (*generate_challenge)(unsigned char *challenge); + int (*verify_response)(int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space); +#endif /* PPP_SERVER */ + void (*make_response)(unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + const unsigned char *priv); + int (*check_success)(unsigned char *pkt, int len, unsigned char *priv); + void (*handle_failure)(unsigned char *pkt, int len); +}; + +/* + * Each interface is described by chap structure. + */ +#if CHAP_SUPPORT +typedef struct chap_client_state { + u8_t flags; + const char *name; + const struct chap_digest_type *digest; + unsigned char priv[64]; /* private area for digest's use */ +} chap_client_state; + +#if PPP_SERVER +typedef struct chap_server_state { + u8_t flags; + int id; + const char *name; + const struct chap_digest_type *digest; + int challenge_xmits; + int challenge_pktlen; + unsigned char challenge[CHAL_MAX_PKTLEN]; + char message[256]; +} chap_server_state; +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPORT */ + +#if 0 /* UNUSED */ +/* Hook for a plugin to validate CHAP challenge */ +extern int (*chap_verify_hook)(char *name, char *ourname, int id, + const struct chap_digest_type *digest, + unsigned char *challenge, unsigned char *response, + char *message, int message_space); +#endif /* UNUSED */ + +#if PPP_SERVER +/* Called by authentication code to start authenticating the peer. */ +extern void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code); +#endif /* PPP_SERVER */ + +/* Called by auth. code to start authenticating us to the peer. */ +extern void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code); + +/* Represents the CHAP protocol to the main pppd code */ +extern const struct protent chap_protent; + +#endif /* CHAP_H */ +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap_ms.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap_ms.h new file mode 100644 index 0000000..a366e47 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/chap_ms.h @@ -0,0 +1,115 @@ +/* + * chap_ms.h - Challenge Handshake Authentication Protocol definitions. + * + * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: chap_ms.h,v 1.13 2004/11/15 22:13:26 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef __CHAPMS_INCLUDE__ + +#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */ +#define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */ + +#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */ +#define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */ +#define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */ + /* as ASCII */ + +/* Error codes for MS-CHAP failure messages. */ +#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646 +#define MS_CHAP_ERROR_ACCT_DISABLED 647 +#define MS_CHAP_ERROR_PASSWD_EXPIRED 648 +#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649 +#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691 +#define MS_CHAP_ERROR_CHANGING_PASSWORD 709 + +/* + * Offsets within the response field for MS-CHAP + */ +#define MS_CHAP_LANMANRESP 0 +#define MS_CHAP_LANMANRESP_LEN 24 +#define MS_CHAP_NTRESP 24 +#define MS_CHAP_NTRESP_LEN 24 +#define MS_CHAP_USENT 48 + +/* + * Offsets within the response field for MS-CHAP2 + */ +#define MS_CHAP2_PEER_CHALLENGE 0 +#define MS_CHAP2_PEER_CHAL_LEN 16 +#define MS_CHAP2_RESERVED_LEN 8 +#define MS_CHAP2_NTRESP 24 +#define MS_CHAP2_NTRESP_LEN 24 +#define MS_CHAP2_FLAGS 48 + +#ifdef MPPE +#include "mppe.h" /* MPPE_MAX_KEY_LEN */ +extern u_char mppe_send_key[MPPE_MAX_KEY_LEN]; +extern u_char mppe_recv_key[MPPE_MAX_KEY_LEN]; +extern int mppe_keys_set; + +/* These values are the RADIUS attribute values--see RFC 2548. */ +#define MPPE_ENC_POL_ENC_ALLOWED 1 +#define MPPE_ENC_POL_ENC_REQUIRED 2 +#define MPPE_ENC_TYPES_RC4_40 2 +#define MPPE_ENC_TYPES_RC4_128 4 + +/* used by plugins (using above values) */ +extern void set_mppe_enc_types(int, int); +#endif + +/* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */ +#define MS_CHAP2_AUTHENTICATEE 0 +#define MS_CHAP2_AUTHENTICATOR 1 + +void ChapMS (u_char *, char *, int, u_char *); +void ChapMS2 (u_char *, u_char *, char *, char *, int, + u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int); +#ifdef MPPE +void mppe_set_keys (u_char *, u_char[MD4_SIGNATURE_SIZE]); +void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], int IsServer); +#endif + +void ChallengeHash (u_char[16], u_char *, char *, u_char[8]); + +void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], u_char PeerChallenge[16], + u_char *rchallenge, char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]); + +extern const struct chap_digest_type chapms_digest; +extern const struct chap_digest_type chapms2_digest; + +#define __CHAPMS_INCLUDE__ +#endif /* __CHAPMS_INCLUDE__ */ + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eap.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eap.h new file mode 100644 index 0000000..cab6901 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eap.h @@ -0,0 +1,168 @@ +/* + * eap.h - Extensible Authentication Protocol for PPP (RFC 2284) + * + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Non-exclusive rights to redistribute, modify, translate, and use + * this software in source and binary forms, in whole or in part, is + * hereby granted, provided that the above copyright notice is + * duplicated in any source form, and that neither the name of the + * copyright holder nor the author is used to endorse or promote + * products derived from this software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Original version by James Carlson + * + * $Id: eap.h,v 1.2 2003/06/11 23:56:26 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_EAP_H +#define PPP_EAP_H + +#include "ppp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Packet header = Code, id, length. + */ +#define EAP_HEADERLEN 4 + + +/* EAP message codes. */ +#define EAP_REQUEST 1 +#define EAP_RESPONSE 2 +#define EAP_SUCCESS 3 +#define EAP_FAILURE 4 + +/* EAP types */ +#define EAPT_IDENTITY 1 +#define EAPT_NOTIFICATION 2 +#define EAPT_NAK 3 /* (response only) */ +#define EAPT_MD5CHAP 4 +#define EAPT_OTP 5 /* One-Time Password; RFC 1938 */ +#define EAPT_TOKEN 6 /* Generic Token Card */ +/* 7 and 8 are unassigned. */ +#define EAPT_RSA 9 /* RSA Public Key Authentication */ +#define EAPT_DSS 10 /* DSS Unilateral */ +#define EAPT_KEA 11 /* KEA */ +#define EAPT_KEA_VALIDATE 12 /* KEA-VALIDATE */ +#define EAPT_TLS 13 /* EAP-TLS */ +#define EAPT_DEFENDER 14 /* Defender Token (AXENT) */ +#define EAPT_W2K 15 /* Windows 2000 EAP */ +#define EAPT_ARCOT 16 /* Arcot Systems */ +#define EAPT_CISCOWIRELESS 17 /* Cisco Wireless */ +#define EAPT_NOKIACARD 18 /* Nokia IP smart card */ +#define EAPT_SRP 19 /* Secure Remote Password */ +/* 20 is deprecated */ + +/* EAP SRP-SHA1 Subtypes */ +#define EAPSRP_CHALLENGE 1 /* Request 1 - Challenge */ +#define EAPSRP_CKEY 1 /* Response 1 - Client Key */ +#define EAPSRP_SKEY 2 /* Request 2 - Server Key */ +#define EAPSRP_CVALIDATOR 2 /* Response 2 - Client Validator */ +#define EAPSRP_SVALIDATOR 3 /* Request 3 - Server Validator */ +#define EAPSRP_ACK 3 /* Response 3 - final ack */ +#define EAPSRP_LWRECHALLENGE 4 /* Req/resp 4 - Lightweight rechal */ + +#define SRPVAL_EBIT 0x00000001 /* Use shared key for ECP */ + +#define SRP_PSEUDO_ID "pseudo_" +#define SRP_PSEUDO_LEN 7 + +#define MD5_SIGNATURE_SIZE 16 +#define EAP_MIN_CHALLENGE_LENGTH 16 +#define EAP_MAX_CHALLENGE_LENGTH 24 + +#define EAP_STATES \ + "Initial", "Pending", "Closed", "Listen", "Identify", \ + "SRP1", "SRP2", "SRP3", "MD5Chall", "Open", "SRP4", "BadAuth" + +#define eap_client_active(pcb) ((pcb)->eap.es_client.ea_state == eapListen) +#if PPP_SERVER +#define eap_server_active(pcb) \ + ((pcb)->eap.es_server.ea_state >= eapIdentify && \ + (pcb)->eap.es_server.ea_state <= eapMD5Chall) +#endif /* PPP_SERVER */ + +/* + * Complete EAP state for one PPP session. + */ +enum eap_state_code { + eapInitial = 0, /* No EAP authentication yet requested */ + eapPending, /* Waiting for LCP (no timer) */ + eapClosed, /* Authentication not in use */ + eapListen, /* Client ready (and timer running) */ + eapIdentify, /* EAP Identify sent */ + eapSRP1, /* Sent EAP SRP-SHA1 Subtype 1 */ + eapSRP2, /* Sent EAP SRP-SHA1 Subtype 2 */ + eapSRP3, /* Sent EAP SRP-SHA1 Subtype 3 */ + eapMD5Chall, /* Sent MD5-Challenge */ + eapOpen, /* Completed authentication */ + eapSRP4, /* Sent EAP SRP-SHA1 Subtype 4 */ + eapBadAuth /* Failed authentication */ +}; + +struct eap_auth { + const char *ea_name; /* Our name */ + char *ea_peer; /* Peer's name */ + void *ea_session; /* Authentication library linkage */ + u_char *ea_skey; /* Shared encryption key */ + u_short ea_namelen; /* Length of our name */ + u_short ea_peerlen; /* Length of peer's name */ + enum eap_state_code ea_state; + u_char ea_id; /* Current id */ + u_char ea_requests; /* Number of Requests sent/received */ + u_char ea_responses; /* Number of Responses */ + u_char ea_type; /* One of EAPT_* */ + u32_t ea_keyflags; /* SRP shared key usage flags */ +}; + +#ifndef EAP_MAX_CHALLENGE_LENGTH +#define EAP_MAX_CHALLENGE_LENGTH 24 +#endif +typedef struct eap_state { + struct eap_auth es_client; /* Client (authenticatee) data */ +#if PPP_SERVER + struct eap_auth es_server; /* Server (authenticator) data */ +#endif /* PPP_SERVER */ + int es_savedtime; /* Saved timeout */ + int es_rechallenge; /* EAP rechallenge interval */ + int es_lwrechallenge; /* SRP lightweight rechallenge inter */ + u8_t es_usepseudo; /* Use SRP Pseudonym if offered one */ + int es_usedpseudo; /* Set if we already sent PN */ + int es_challen; /* Length of challenge string */ + u_char es_challenge[EAP_MAX_CHALLENGE_LENGTH]; +} eap_state; + +/* + * Timeouts. + */ +#if 0 /* moved to opt.h */ +#define EAP_DEFTIMEOUT 3 /* Timeout (seconds) for rexmit */ +#define EAP_DEFTRANSMITS 10 /* max # times to transmit */ +#define EAP_DEFREQTIME 20 /* Time to wait for peer request */ +#define EAP_DEFALLOWREQ 20 /* max # times to accept requests */ +#endif /* moved to opt.h */ + +void eap_authwithpeer(ppp_pcb *pcb, const char *localname); +void eap_authpeer(ppp_pcb *pcb, const char *localname); + +extern const struct protent eap_protent; + +#ifdef __cplusplus +} +#endif + +#endif /* PPP_EAP_H */ + +#endif /* PPP_SUPPORT && EAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ecp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ecp.h new file mode 100644 index 0000000..cba6678 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ecp.h @@ -0,0 +1,50 @@ +/* + * ecp.h - Definitions for PPP Encryption Control Protocol. + * + * Copyright (c) 2002 Google, Inc. + * 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ecp.h,v 1.2 2003/01/10 07:12:36 fcusack Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +typedef struct ecp_options { + bool required; /* Is ECP required? */ + unsigned enctype; /* Encryption type */ +} ecp_options; + +extern fsm ecp_fsm[]; +extern ecp_options ecp_wantoptions[]; +extern ecp_options ecp_gotoptions[]; +extern ecp_options ecp_allowoptions[]; +extern ecp_options ecp_hisoptions[]; + +extern const struct protent ecp_protent; + +#endif /* PPP_SUPPORT && ECP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eui64.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eui64.h new file mode 100644 index 0000000..dffb5e4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/eui64.h @@ -0,0 +1,94 @@ +/* + * eui64.h - EUI64 routines for IPv6CP. + * + * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: eui64.h,v 1.6 2002/12/04 23:03:32 paulus Exp $ +*/ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef __EUI64_H__ +#define __EUI64_H__ + +/* + * TODO: + * + * Maybe this should be done by processing struct in6_addr directly... + */ +typedef union +{ + u8_t e8[8]; + u16_t e16[4]; + u32_t e32[2]; +} eui64_t; + +#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0) +#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \ + ((e).e32[1] == (o).e32[1])) +#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0; + +#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t)) + +#define eui64_magic(e) do { \ + (e).e32[0] = magic(); \ + (e).e32[1] = magic(); \ + (e).e8[0] &= ~2; \ + } while (0) +#define eui64_magic_nz(x) do { \ + eui64_magic(x); \ + } while (eui64_iszero(x)) +#define eui64_magic_ne(x, y) do { \ + eui64_magic(x); \ + } while (eui64_equals(x, y)) + +#define eui64_get(ll, cp) do { \ + eui64_copy((*cp), (ll)); \ + (cp) += sizeof(eui64_t); \ + } while (0) + +#define eui64_put(ll, cp) do { \ + eui64_copy((ll), (*cp)); \ + (cp) += sizeof(eui64_t); \ + } while (0) + +#define eui64_set32(e, l) do { \ + (e).e32[0] = 0; \ + (e).e32[1] = htonl(l); \ + } while (0) +#define eui64_setlo32(e, l) eui64_set32(e, l) + +char *eui64_ntoa(eui64_t); /* Returns ascii representation of id */ + +#endif /* __EUI64_H__ */ +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/fsm.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/fsm.h new file mode 100644 index 0000000..fda2d6e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/fsm.h @@ -0,0 +1,175 @@ +/* + * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: fsm.h,v 1.10 2004/11/13 02:28:15 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef FSM_H +#define FSM_H + +#include "ppp.h" + +/* + * Packet header = Code, id, length. + */ +#define HEADERLEN 4 + + +/* + * CP (LCP, IPCP, etc.) codes. + */ +#define CONFREQ 1 /* Configuration Request */ +#define CONFACK 2 /* Configuration Ack */ +#define CONFNAK 3 /* Configuration Nak */ +#define CONFREJ 4 /* Configuration Reject */ +#define TERMREQ 5 /* Termination Request */ +#define TERMACK 6 /* Termination Ack */ +#define CODEREJ 7 /* Code Reject */ + + +/* + * Each FSM is described by an fsm structure and fsm callbacks. + */ +typedef struct fsm { + ppp_pcb *pcb; /* PPP Interface */ + const struct fsm_callbacks *callbacks; /* Callback routines */ + const char *term_reason; /* Reason for closing protocol */ + u8_t seen_ack; /* Have received valid Ack/Nak/Rej to Req */ + /* -- This is our only flag, we might use u_int :1 if we have more flags */ + u16_t protocol; /* Data Link Layer Protocol field value */ + u8_t state; /* State */ + u8_t flags; /* Contains option bits */ + u8_t id; /* Current id */ + u8_t reqid; /* Current request id */ + u8_t retransmits; /* Number of retransmissions left */ + u8_t nakloops; /* Number of nak loops since last ack */ + u8_t rnakloops; /* Number of naks received */ + u8_t maxnakloops; /* Maximum number of nak loops tolerated + (necessary because IPCP require a custom large max nak loops value) */ + u8_t term_reason_len; /* Length of term_reason */ +} fsm; + + +typedef struct fsm_callbacks { + void (*resetci) /* Reset our Configuration Information */ + (fsm *); + int (*cilen) /* Length of our Configuration Information */ + (fsm *); + void (*addci) /* Add our Configuration Information */ + (fsm *, u_char *, int *); + int (*ackci) /* ACK our Configuration Information */ + (fsm *, u_char *, int); + int (*nakci) /* NAK our Configuration Information */ + (fsm *, u_char *, int, int); + int (*rejci) /* Reject our Configuration Information */ + (fsm *, u_char *, int); + int (*reqci) /* Request peer's Configuration Information */ + (fsm *, u_char *, int *, int); + void (*up) /* Called when fsm reaches PPP_FSM_OPENED state */ + (fsm *); + void (*down) /* Called when fsm leaves PPP_FSM_OPENED state */ + (fsm *); + void (*starting) /* Called when we want the lower layer */ + (fsm *); + void (*finished) /* Called when we don't want the lower layer */ + (fsm *); + void (*protreject) /* Called when Protocol-Reject received */ + (int); + void (*retransmit) /* Retransmission is necessary */ + (fsm *); + int (*extcode) /* Called when unknown code received */ + (fsm *, int, int, u_char *, int); + const char *proto_name; /* String name for protocol (for messages) */ +} fsm_callbacks; + + +/* + * Link states. + */ +#define PPP_FSM_INITIAL 0 /* Down, hasn't been opened */ +#define PPP_FSM_STARTING 1 /* Down, been opened */ +#define PPP_FSM_CLOSED 2 /* Up, hasn't been opened */ +#define PPP_FSM_STOPPED 3 /* Open, waiting for down event */ +#define PPP_FSM_CLOSING 4 /* Terminating the connection, not open */ +#define PPP_FSM_STOPPING 5 /* Terminating, but open */ +#define PPP_FSM_REQSENT 6 /* We've sent a Config Request */ +#define PPP_FSM_ACKRCVD 7 /* We've received a Config Ack */ +#define PPP_FSM_ACKSENT 8 /* We've sent a Config Ack */ +#define PPP_FSM_OPENED 9 /* Connection available */ + + +/* + * Flags - indicate options controlling FSM operation + */ +#define OPT_PASSIVE 1 /* Don't die if we don't get a response */ +#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */ +#define OPT_SILENT 4 /* Wait for peer to speak first */ + + +/* + * Timeouts. + */ +#if 0 /* moved to opt.h */ +#define DEFTIMEOUT 3 /* Timeout time in seconds */ +#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */ +#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */ +#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */ +#endif /* moved to opt.h */ + + +/* + * Prototypes + */ +void fsm_init(fsm *f); +void fsm_lowerup(fsm *f); +void fsm_lowerdown(fsm *f); +void fsm_open(fsm *f); +void fsm_close(fsm *f, const char *reason); +void fsm_input(fsm *f, u_char *inpacket, int l); +void fsm_protreject(fsm *f); +void fsm_sdata(fsm *f, u_char code, u_char id, u_char *data, int datalen); + + +#endif /* FSM_H */ +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipcp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipcp.h new file mode 100644 index 0000000..b5f2334 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipcp.h @@ -0,0 +1,107 @@ +/* + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipcp.h,v 1.14 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef IPCP_H +#define IPCP_H + +/* + * Options. + */ +#define CI_ADDRS 1 /* IP Addresses */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ +#define CI_ADDR 3 + +#define CI_MS_DNS1 129 /* Primary DNS value */ +#define CI_MS_WINS1 130 /* Primary WINS value */ +#define CI_MS_DNS2 131 /* Secondary DNS value */ +#define CI_MS_WINS2 132 /* Secondary WINS value */ + +#define MAX_STATES 16 /* from slcompress.h */ + +#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */ +#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */ +#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */ + /* maxslot and slot number compression) */ + +#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/ +#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */ + /* compression option*/ + +typedef struct ipcp_options { + unsigned int neg_addr :1; /* Negotiate IP Address? */ + unsigned int old_addrs :1; /* Use old (IP-Addresses) option? */ + unsigned int req_addr :1; /* Ask peer to send IP address? */ +#if 0 /* UNUSED */ + unsigned int default_route :1; /* Assign default route through interface? */ + unsigned int replace_default_route :1; /* Replace default route through interface? */ +#endif /* UNUSED */ + unsigned int proxy_arp :1; /* Make proxy ARP entry for peer? */ + unsigned int neg_vj :1; /* Van Jacobson Compression? */ + unsigned int old_vj :1; /* use old (short) form of VJ option? */ + unsigned int accept_local :1; /* accept peer's value for ouraddr */ + unsigned int accept_remote :1; /* accept peer's value for hisaddr */ + unsigned int req_dns1 :1; /* Ask peer to send primary DNS address? */ + unsigned int req_dns2 :1; /* Ask peer to send secondary DNS address? */ + unsigned int cflag :1; + unsigned int :5; /* 3 bits of padding to round out to 16 bits */ + + u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */ + u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */ + u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */ + + u16_t vj_protocol; /* protocol value to use in VJ option */ + u8_t maxslotindex; /* values for RFC1332 VJ compression neg. */ +} ipcp_options; + +#if 0 /* UNUSED, already defined by lwIP */ +char *ip_ntoa (u32_t); +#endif /* UNUSED, already defined by lwIP */ + +extern const struct protent ipcp_protent; + +#endif /* IPCP_H */ +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipv6cp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipv6cp.h new file mode 100644 index 0000000..e6afddf --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ipv6cp.h @@ -0,0 +1,177 @@ +/* + * ipv6cp.h - PPP IPV6 Control Protocol. + * + * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* Original version, based on RFC2023 : + + Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt + Économique ayant pour membres BULL S.A. et l'INRIA). + + Ce logiciel informatique est disponible aux conditions + usuelles dans la recherche, c'est-à-dire qu'il peut + être utilisé, copié, modifié, distribué à l'unique + condition que ce texte soit conservé afin que + l'origine de ce logiciel soit reconnue. + + Le nom de l'Institut National de Recherche en Informatique + et en Automatique (INRIA), de l'IMAG, ou d'une personne morale + ou physique ayant participé à l'élaboration de ce logiciel ne peut + être utilisé sans son accord préalable explicite. + + Ce logiciel est fourni tel quel sans aucune garantie, + support ou responsabilité d'aucune sorte. + Ce logiciel est dérivé de sources d'origine + "University of California at Berkeley" et + "Digital Equipment Corporation" couvertes par des copyrights. + + L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG) + est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National + Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant + sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR). + + This work has been done in the context of GIE DYADE (joint R & D venture + between BULL S.A. and INRIA). + + This software is available with usual "research" terms + with the aim of retain credits of the software. + Permission to use, copy, modify and distribute this software for any + purpose and without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies, + and the name of INRIA, IMAG, or any contributor not be used in advertising + or publicity pertaining to this material without the prior explicit + permission. The software is provided "as is" without any + warranties, support or liabilities of any kind. + This software is derived from source code from + "University of California at Berkeley" and + "Digital Equipment Corporation" protected by copyrights. + + Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) + is a federation of seven research units funded by the CNRS, National + Polytechnic Institute of Grenoble and University Joseph Fourier. + The research unit in Software, Systems, Networks (LSR) is member of IMAG. +*/ + +/* + * Derived from : + * + * + * ipcp.h - IP Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipv6cp.h,v 1.7 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef IPV6CP_H +#define IPV6CP_H + +#include "eui64.h" + +/* + * Options. + */ +#define CI_IFACEID 1 /* Interface Identifier */ +#define CI_COMPRESSTYPE 2 /* Compression Type */ + +/* No compression types yet defined. + *#define IPV6CP_COMP 0x004f + */ +typedef struct ipv6cp_options { + unsigned int neg_ifaceid :1; /* Negotiate interface identifier? */ + unsigned int req_ifaceid :1; /* Ask peer to send interface identifier? */ + unsigned int accept_local :1; /* accept peer's value for iface id? */ + unsigned int opt_local :1; /* ourtoken set by option */ + unsigned int opt_remote :1; /* histoken set by option */ + unsigned int use_ip :1; /* use IP as interface identifier */ +#if 0 + unsigned int use_persistent :1; /* use uniquely persistent value for address */ +#endif + unsigned int neg_vj :1; /* Van Jacobson Compression? */ + unsigned int :1; /* 1 bit of padding to round out to 8 bits */ + u_short vj_protocol; /* protocol value to use in VJ option */ + eui64_t ourid, hisid; /* Interface identifiers */ +} ipv6cp_options; + +extern const struct protent ipv6cp_protent; + +#endif /* IPV6CP_H */ +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/lcp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/lcp.h new file mode 100644 index 0000000..d7fa7c4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/lcp.h @@ -0,0 +1,176 @@ +/* + * lcp.h - Link Control Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: lcp.h,v 1.20 2004/11/14 22:53:42 carlsonj Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef LCP_H +#define LCP_H + +#include "ppp.h" + +/* + * Options. + */ +#define CI_VENDOR 0 /* Vendor Specific */ +#define CI_MRU 1 /* Maximum Receive Unit */ +#define CI_ASYNCMAP 2 /* Async Control Character Map */ +#define CI_AUTHTYPE 3 /* Authentication Type */ +#define CI_QUALITY 4 /* Quality Protocol */ +#define CI_MAGICNUMBER 5 /* Magic Number */ +#define CI_PCOMPRESSION 7 /* Protocol Field Compression */ +#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */ +#define CI_FCSALTERN 9 /* FCS-Alternatives */ +#define CI_SDP 10 /* Self-Describing-Pad */ +#define CI_NUMBERED 11 /* Numbered-Mode */ +#define CI_CALLBACK 13 /* callback */ +#define CI_MRRU 17 /* max reconstructed receive unit; multilink */ +#define CI_SSNHF 18 /* short sequence numbers for multilink */ +#define CI_EPDISC 19 /* endpoint discriminator */ +#define CI_MPPLUS 22 /* Multi-Link-Plus-Procedure */ +#define CI_LDISC 23 /* Link-Discriminator */ +#define CI_LCPAUTH 24 /* LCP Authentication */ +#define CI_COBS 25 /* Consistent Overhead Byte Stuffing */ +#define CI_PREFELIS 26 /* Prefix Elision */ +#define CI_MPHDRFMT 27 /* MP Header Format */ +#define CI_I18N 28 /* Internationalization */ +#define CI_SDL 29 /* Simple Data Link */ + +/* + * LCP-specific packet types (code numbers). + */ +#define PROTREJ 8 /* Protocol Reject */ +#define ECHOREQ 9 /* Echo Request */ +#define ECHOREP 10 /* Echo Reply */ +#define DISCREQ 11 /* Discard Request */ +#define IDENTIF 12 /* Identification */ +#define TIMEREM 13 /* Time Remaining */ + +/* Value used as data for CI_CALLBACK option */ +#define CBCP_OPT 6 /* Use callback control protocol */ + +#define DEFMRU 1500 /* Try for this */ +#define MINMRU 128 /* No MRUs below this */ +#define MAXMRU 16384 /* Normally limit MRU to this */ + +/* An endpoint discriminator, used with multilink. */ +#define MAX_ENDP_LEN 20 /* maximum length of discriminator value */ +struct epdisc { + unsigned char class_; /* -- The word "class" is reserved in C++. */ + unsigned char length; + unsigned char value[MAX_ENDP_LEN]; +}; + +/* + * The state of options is described by an lcp_options structure. + */ +typedef struct lcp_options { + unsigned int passive :1; /* Don't die if we don't get a response */ + unsigned int silent :1; /* Wait for the other end to start first */ + unsigned int restart :1; /* Restart vs. exit after close */ + unsigned int neg_mru :1; /* Negotiate the MRU? */ + unsigned int neg_asyncmap :1; /* Negotiate the async map? */ +#if PAP_SUPPORT + unsigned int neg_upap :1; /* Ask for UPAP authentication? */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + unsigned int neg_chap :1; /* Ask for CHAP authentication? */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + unsigned int neg_eap :1; /* Ask for EAP authentication? */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* EAP_SUPPORT */ + unsigned int neg_magicnumber :1; /* Ask for magic number? */ + unsigned int neg_pcompression :1; /* HDLC Protocol Field Compression? */ + unsigned int neg_accompression :1; /* HDLC Address/Control Field Compression? */ +#if LQR_SUPPORT + unsigned int neg_lqr :1; /* Negotiate use of Link Quality Reports */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* LQR_SUPPORT */ + unsigned int neg_cbcp :1; /* Negotiate use of CBCP */ +#ifdef HAVE_MULTILINK + unsigned int neg_mrru :1; /* negotiate multilink MRRU */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* HAVE_MULTILINK */ + unsigned int neg_ssnhf :1; /* negotiate short sequence numbers */ + unsigned int neg_endpoint :1; /* negotiate endpoint discriminator */ + u16_t mru; /* Value of MRU */ +#ifdef HAVE_MULTILINK + u16_t mrru; /* Value of MRRU, and multilink enable */ +#endif /* MULTILINK */ +#if CHAP_SUPPORT + u8_t chap_mdtype; /* which MD types (hashing algorithm) */ +#endif /* CHAP_SUPPORT */ + u32_t asyncmap; /* Value of async map */ + u32_t magicnumber; + u8_t numloops; /* Number of loops during magic number neg. */ +#if LQR_SUPPORT + u32_t lqr_period; /* Reporting period for LQR 1/100ths second */ +#endif /* LQR_SUPPORT */ + struct epdisc endpoint; /* endpoint discriminator */ +} lcp_options; + +void lcp_open(ppp_pcb *pcb); +void lcp_close(ppp_pcb *pcb, const char *reason); +void lcp_lowerup(ppp_pcb *pcb); +void lcp_lowerdown(ppp_pcb *pcb); +void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len); /* send protocol reject */ + +extern const struct protent lcp_protent; + +#if 0 /* moved to opt.h */ +/* Default number of times we receive our magic number from the peer + before deciding the link is looped-back. */ +#define DEFLOOPBACKFAIL 10 +#endif /* moved to opt.h */ + +#endif /* LCP_H */ +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/magic.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/magic.h new file mode 100644 index 0000000..4661dea --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/magic.h @@ -0,0 +1,119 @@ +/* + * magic.h - PPP Magic Number definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: magic.h,v 1.5 2003/06/11 23:56:26 paulus Exp $ + */ +/***************************************************************************** +* randm.h - Random number generator header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-05-29 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef MAGIC_H +#define MAGIC_H + +/*********************** +*** PUBLIC FUNCTIONS *** +***********************/ + +/* + * Initialize the random number generator. + */ +void magic_init(void); + +/* + * Randomize our random seed value. To be called for truely random events + * such as user operations and network traffic. + */ +void magic_randomize(void); + +/* + * Return a new random number. + */ +u32_t magic(void); /* Returns the next magic number */ + +#if PPP_MD5_RANDM +/* + * Fill buffer with random bytes + * + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using magic_churnrand(). + * Thus it's important to make sure that the results of this are not + * published directly because one could predict the next result to at + * least some degree. Also, it's important to get a good seed before + * the first use. + */ +void random_bytes(unsigned char *buf, u32_t len); +#endif /* PPP_MD5_RANDM */ + +#endif /* MAGIC_H */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/des.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/des.h new file mode 100644 index 0000000..86417cd --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/des.h @@ -0,0 +1,92 @@ +/** + * \file des.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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 "lwip/opt.h" +#if LWIP_INCLUDED_POLARSSL_DES + +#ifndef LWIP_INCLUDED_POLARSSL_DES_H +#define LWIP_INCLUDED_POLARSSL_DES_H + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +/** + * \brief DES context structure + */ +typedef struct +{ + int mode; /*!< encrypt/decrypt */ + unsigned long sk[32]; /*!< DES subkeys */ +} +des_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief DES key schedule (56-bit, encryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + */ +void des_setkey_enc( des_context *ctx, unsigned char key[8] ); + +/** + * \brief DES key schedule (56-bit, decryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + */ +void des_setkey_dec( des_context *ctx, unsigned char key[8] ); + +/** + * \brief DES-ECB block encryption/decryption + * + * \param ctx DES context + * \param input 64-bit input block + * \param output 64-bit output block + */ +void des_crypt_ecb( des_context *ctx, + unsigned char input[8], + unsigned char output[8] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_DES_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_DES */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md4.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md4.h new file mode 100644 index 0000000..46ebf5c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md4.h @@ -0,0 +1,97 @@ +/** + * \file md4.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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 "lwip/opt.h" +#if LWIP_INCLUDED_POLARSSL_MD4 + +#ifndef LWIP_INCLUDED_POLARSSL_MD4_H +#define LWIP_INCLUDED_POLARSSL_MD4_H + +/** + * \brief MD4 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +md4_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD4 context setup + * + * \param ctx context to be initialized + */ +void md4_starts( md4_context *ctx ); + +/** + * \brief MD4 process buffer + * + * \param ctx MD4 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_update( md4_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief MD4 final digest + * + * \param ctx MD4 context + * \param output MD4 checksum result + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ); + +/** + * \brief Output = MD4( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD4 checksum result + */ +void md4( unsigned char *input, int ilen, unsigned char output[16] ); + + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_MD4_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_MD4 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md5.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md5.h new file mode 100644 index 0000000..3587b4c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/md5.h @@ -0,0 +1,96 @@ +/** + * \file md5.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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 "lwip/opt.h" +#if LWIP_INCLUDED_POLARSSL_MD5 + +#ifndef LWIP_INCLUDED_POLARSSL_MD5_H +#define LWIP_INCLUDED_POLARSSL_MD5_H + +/** + * \brief MD5 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +md5_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD5 context setup + * + * \param ctx context to be initialized + */ +void md5_starts( md5_context *ctx ); + +/** + * \brief MD5 process buffer + * + * \param ctx MD5 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md5_update( md5_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief MD5 final digest + * + * \param ctx MD5 context + * \param output MD5 checksum result + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ); + +/** + * \brief Output = MD5( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD5 checksum result + */ +void md5( unsigned char *input, int ilen, unsigned char output[16] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_MD5_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_MD5 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/sha1.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/sha1.h new file mode 100644 index 0000000..9d33163 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/polarssl/sha1.h @@ -0,0 +1,96 @@ +/** + * \file sha1.h + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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 "lwip/opt.h" +#if LWIP_INCLUDED_POLARSSL_SHA1 + +#ifndef LWIP_INCLUDED_POLARSSL_SHA1_H +#define LWIP_INCLUDED_POLARSSL_SHA1_H + +/** + * \brief SHA-1 context structure + */ +typedef struct +{ + unsigned long total[2]; /*!< number of bytes processed */ + unsigned long state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ +} +sha1_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts( sha1_context *ctx ); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_INCLUDED_POLARSSL_SHA1_H */ + +#endif /* LWIP_INCLUDED_POLARSSL_SHA1 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp.h new file mode 100644 index 0000000..ed60536 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp.h @@ -0,0 +1,644 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_H +#define PPP_H + +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/netif.h" +#include "lwip/sys.h" +#include "lwip/sio.h" +#include "lwip/timers.h" +#if PPP_IPV6_SUPPORT +#include "lwip/ip6_addr.h" +#endif /* PPP_IPV6_SUPPORT */ + +/* Disable non-working or rarely used PPP feature, so rarely that we don't want to bloat opt.h with them */ +#ifndef PPP_OPTIONS +#define PPP_OPTIONS 0 +#endif + +#ifndef PPP_NOTIFY +#define PPP_NOTIFY 0 +#endif + +#ifndef PPP_REMOTENAME +#define PPP_REMOTENAME 0 +#endif + +#ifndef PPP_IDLETIMELIMIT +#define PPP_IDLETIMELIMIT 0 +#endif + +#ifndef PPP_LCP_ADAPTIVE +#define PPP_LCP_ADAPTIVE 0 +#endif + +#ifndef PPP_MAXCONNECT +#define PPP_MAXCONNECT 0 +#endif + +#ifndef PPP_ALLOWED_ADDRS +#define PPP_ALLOWED_ADDRS 0 +#endif + +#ifndef PPP_PROTOCOLNAME +#define PPP_PROTOCOLNAME 0 +#endif + +#ifndef PPP_STATS_SUPPORT +#define PPP_STATS_SUPPORT 0 +#endif + + +/************************* +*** PUBLIC DEFINITIONS *** +*************************/ + +/* + * The basic PPP frame. + */ +#define PPP_HDRLEN 4 /* octets for standard ppp header */ +#define PPP_FCSLEN 2 /* octets for FCS */ + +/* + * Values for phase. + */ +#define PPP_PHASE_DEAD 0 +#define PPP_PHASE_INITIALIZE 1 +#define PPP_PHASE_SERIALCONN 2 +#define PPP_PHASE_DORMANT 3 +#define PPP_PHASE_ESTABLISH 4 +#define PPP_PHASE_AUTHENTICATE 5 +#define PPP_PHASE_CALLBACK 6 +#define PPP_PHASE_NETWORK 7 +#define PPP_PHASE_RUNNING 8 +#define PPP_PHASE_TERMINATE 9 +#define PPP_PHASE_DISCONNECT 10 +#define PPP_PHASE_HOLDOFF 11 +#define PPP_PHASE_MASTER 12 + +/* Error codes. */ +#define PPPERR_NONE 0 /* No error. */ +#define PPPERR_PARAM 1 /* Invalid parameter. */ +#define PPPERR_OPEN 2 /* Unable to open PPP session. */ +#define PPPERR_DEVICE 3 /* Invalid I/O device for PPP. */ +#define PPPERR_ALLOC 4 /* Unable to allocate resources. */ +#define PPPERR_USER 5 /* User interrupt. */ +#define PPPERR_CONNECT 6 /* Connection lost. */ +#define PPPERR_AUTHFAIL 7 /* Failed authentication challenge. */ +#define PPPERR_PROTOCOL 8 /* Failed to meet protocol. */ +#define PPPERR_PEERDEAD 9 /* Connection timeout */ +#define PPPERR_IDLETIMEOUT 10 /* Idle Timeout */ +#define PPPERR_CONNECTTIME 11 /* Max connect time reached */ +#define PPPERR_LOOPBACK 12 /* Loopback detected */ + +/* + * PPP IOCTL commands. + */ +/* + * Get the up status - 0 for down, non-zero for up. The argument must + * point to an int. + */ +#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */ +#define PPPCTLS_ERRCODE 101 /* Set the error code */ +#define PPPCTLG_ERRCODE 102 /* Get the error code */ +#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */ + +/************************ +*** PUBLIC DATA TYPES *** +************************/ + +/* + * Other headers require ppp_pcb definition for prototypes, but ppp_pcb + * require some structure definition from other headers as well, we are + * fixing the dependency loop here by declaring the ppp_pcb type then + * by including headers containing necessary struct definition for ppp_pcb + */ +typedef struct ppp_pcb_s ppp_pcb; + +/* Type definitions for BSD code. */ +#ifndef __u_char_defined +typedef unsigned long u_long; +typedef unsigned int u_int; +typedef unsigned short u_short; +typedef unsigned char u_char; +#endif + +#include "fsm.h" +#include "lcp.h" +#include "ipcp.h" +#if PPP_IPV6_SUPPORT +#include "ipv6cp.h" +#endif /* PPP_IPV6_SUPPORT */ +#if PAP_SUPPORT +#include "upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "eap.h" +#endif /* EAP_SUPPORT */ +#if VJ_SUPPORT +#include "vj.h" +#endif /* VJ_SUPPORT */ + +#if PPPOE_SUPPORT +#include "netif/ppp/pppoe.h" +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +#include "netif/ppp/pppol2tp.h" +#endif /* PPPOL2TP_SUPPORT */ + +/* + * PPP configuration. + */ +typedef struct ppp_settings_s { + +#if PPP_SERVER + unsigned int auth_required : 1; /* Peer is required to authenticate */ + unsigned int null_login : 1; /* Username of "" and a password of "" are acceptable */ +#else + unsigned int :2; /* 2 bits of padding */ +#endif /* PPP_SERVER */ +#if PPP_REMOTENAME + unsigned int explicit_remote : 1; /* remote_name specified with remotename opt */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PPP_REMOTENAME */ +#if PAP_SUPPORT + unsigned int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + unsigned int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* CHAP_SUPPORT */ +#if MSCHAP_SUPPORT + unsigned int refuse_mschap : 1; /* Don't wanna auth. ourselves with MS-CHAP */ + unsigned int refuse_mschap_v2 : 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ +#else + unsigned int :2; /* 2 bits of padding */ +#endif /* MSCHAP_SUPPORT */ +#if EAP_SUPPORT + unsigned int refuse_eap : 1; /* Don't wanna auth. ourselves with EAP */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* EAP_SUPPORT */ + unsigned int usepeerdns : 1; /* Ask peer for DNS adds */ + unsigned int persist : 1; /* Persist mode, always try to reopen the connection */ +#if PRINTPKT_SUPPORT + unsigned int hide_password : 1; /* Hide password in dumped packets */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PRINTPKT_SUPPORT */ + unsigned int noremoteip : 1; /* Let him have no IP address */ + unsigned int lax_recv : 1; /* accept control chars in asyncmap */ + unsigned int noendpoint : 1; /* don't send/accept endpoint discriminator */ +#if PPP_LCP_ADAPTIVE + unsigned int lcp_echo_adaptive : 1; /* request echo only if the link was idle */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif + unsigned int :1; /* 1 bit of padding to round out to 16 bits */ + + u16_t listen_time; /* time to listen first (ms), waiting for peer to send LCP packet */ + +#if PPP_IDLETIMELIMIT + u16_t idle_time_limit; /* Disconnect if idle for this many seconds */ +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT + u32_t maxconnect; /* Maximum connect time (seconds) */ +#endif /* PPP_MAXCONNECT */ + + /* auth data */ + const char *user; /* Username for PAP */ + const char *passwd; /* Password for PAP, secret for CHAP */ +#if PPP_SERVER + char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */ +#endif /* PPP_SERVER */ +#if PPP_REMOTENAME + char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */ +#endif /* PPP_REMOTENAME */ + +#if PAP_SUPPORT + u8_t pap_timeout_time; /* Timeout (seconds) for auth-req retrans. */ + u8_t pap_max_transmits; /* Number of auth-reqs sent */ +#if PPP_SERVER + u8_t pap_req_timeout; /* Time to wait for auth-req from peer */ +#endif /* PPP_SERVER */ +#endif /* PAP_SUPPPORT */ + +#if CHAP_SUPPORT + u8_t chap_timeout_time; /* Timeout (seconds) for retransmitting req */ + u8_t chap_max_transmits; /* max # times to send challenge */ +#if PPP_SERVER + u8_t chap_rechallenge_time; /* Time to wait for auth-req from peer */ +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPPORT */ + +#if EAP_SUPPORT + u8_t eap_req_time; /* Time to wait (for retransmit/fail) */ + u8_t eap_allow_req; /* Max Requests allowed */ +#if PPP_SERVER + u8_t eap_timeout_time; /* Time to wait (for retransmit/fail) */ + u8_t eap_max_transmits; /* Max Requests allowed */ +#endif /* PPP_SERVER */ +#endif /* EAP_SUPPORT */ + + u8_t fsm_timeout_time; /* Timeout time in seconds */ + u8_t fsm_max_conf_req_transmits; /* Maximum Configure-Request transmissions */ + u8_t fsm_max_term_transmits; /* Maximum Terminate-Request transmissions */ + u8_t fsm_max_nak_loops; /* Maximum number of nak loops tolerated */ + + u8_t lcp_loopbackfail; /* Number of times we receive our magic number from the peer + before deciding the link is looped-back. */ + u8_t lcp_echo_interval; /* Interval between LCP echo-requests */ + u8_t lcp_echo_fails; /* Tolerance to unanswered echo-requests */ + +} ppp_settings; + +struct ppp_addrs { + ip_addr_t our_ipaddr, his_ipaddr, netmask; + ip_addr_t dns1, dns2; +#if PPP_IPV6_SUPPORT + ip6_addr_t our6_ipaddr, his6_ipaddr; +#endif /* PPP_IPV6_SUPPORT */ +}; + +/* FIXME: find a way to move ppp_dev_states and ppp_pcb_rx_s to ppp_impl.h */ +#if PPPOS_SUPPORT +/* + * Extended asyncmap - allows any character to be escaped. + */ +typedef u_char ext_accm[32]; + +/* PPP packet parser states. Current state indicates operation yet to be + * completed. */ +typedef enum { + PDIDLE = 0, /* Idle state - waiting. */ + PDSTART, /* Process start flag. */ + PDADDRESS, /* Process address field. */ + PDCONTROL, /* Process control field. */ + PDPROTOCOL1, /* Process protocol field 1. */ + PDPROTOCOL2, /* Process protocol field 2. */ + PDDATA /* Process data byte. */ +} ppp_dev_states; + +/* + * PPP interface RX control block. + */ +typedef struct ppp_pcb_rx_s { + /** ppp descriptor */ + ppp_pcb *pcb; + /** the rx file descriptor */ + sio_fd_t fd; + + /* The input packet. */ + struct pbuf *in_head, *in_tail; + + u16_t in_protocol; /* The input protocol code. */ + u16_t in_fcs; /* Input Frame Check Sequence value. */ + ppp_dev_states in_state; /* The input process state. */ + char in_escaped; /* Escape next character. */ + ext_accm in_accm; /* Async-Ctl-Char-Map for input. */ +} ppp_pcb_rx; +#endif /* PPPOS_SUPPORT */ + +/* + * PPP interface control block. + */ +struct ppp_pcb_s { + /* -- below are data that will NOT be cleared between two sessions */ +#if PPP_DEBUG + u8_t num; /* Interface number - only useful for debugging */ +#endif /* PPP_DEBUG */ + ppp_settings settings; +#if PPPOS_SUPPORT + sio_fd_t fd; /* File device ID of port. */ +#endif /* PPPOS_SUPPORT */ +#if PPPOE_SUPPORT + struct pppoe_softc *pppoe_sc; +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + pppol2tp_pcb *l2tp_pcb; +#endif /* PPPOL2TP_SUPPORT */ + void (*link_status_cb)(ppp_pcb *pcb, int err_code, void *ctx); /* Status change callback */ +#if PPP_NOTIFY_PHASE + void (*notify_phase_cb)(ppp_pcb *pcb, u8_t phase, void *ctx); /* Notify phase callback */ +#endif /* PPP_NOTIFY_PHASE */ + void *ctx_cb; /* Callbacks optional pointer */ + struct netif netif; /* PPP interface */ + + /* -- below are data that will be cleared between two sessions */ + + /* + * phase must be the first member of cleared members, because it is used to know + * which part must not be cleared. + */ + u8_t phase; /* where the link is at */ + u8_t err_code; /* Code indicating why interface is down. */ + + /* flags */ + unsigned int if_up :1; /* True when the interface is up. */ +#if PPP_IPV6_SUPPORT + unsigned int if6_up :1; /* True when the IPv6 interface is up. */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PPP_IPV6_SUPPORT */ + unsigned int pcomp :1; /* Does peer accept protocol compression? */ + unsigned int accomp :1; /* Does peer accept addr/ctl compression? */ + unsigned int proxy_arp_set :1; /* Have created proxy arp entry */ + unsigned int ipcp_is_open :1; /* haven't called np_finished() */ + unsigned int ipcp_is_up :1; /* have called ipcp_up() */ +#if PPP_IPV6_SUPPORT + unsigned int ipv6cp_is_up :1; /* have called ip6cp_up() */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PPP_IPV6_SUPPORT */ + unsigned int ask_for_local :1; /* request our address from peer */ + unsigned int lcp_echo_timer_running :1; /* set if a timer is running */ +#if PPPOS_SUPPORT && VJ_SUPPORT + unsigned int vj_enabled :1; /* Flag indicating VJ compression enabled. */ +#else + unsigned int :1; /* 1 bit of padding */ +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + unsigned int :5; /* 5 bits of padding to round out to 16 bits */ + +#if PPPOS_SUPPORT +/* FIXME: there is probably one superfluous */ + ext_accm out_accm; /* Async-Ctl-Char-Map for output. */ + ext_accm xmit_accm; /* extended transmit ACCM */ + ppp_pcb_rx rx; +#if VJ_SUPPORT + struct vjcompress vj_comp; /* Van Jacobson compression header. */ +#endif /* VJ_SUPPORT */ +#endif /* PPPOS_SUPPORT */ + + u32_t last_xmit; /* Time of last transmission. */ + + struct ppp_addrs addrs; /* PPP addresses */ + + /* auth data */ +#if PPP_SERVER + char peer_authname[MAXNAMELEN + 1]; /* The name by which the peer authenticated itself to us. */ +#endif /* PPP_SERVER */ + u16_t auth_pending; /* Records which authentication operations haven't completed yet. */ + u16_t auth_done; /* Records which authentication operations have been completed. */ + u8_t num_np_open; /* Number of network protocols which we have opened. */ + u8_t num_np_up; /* Number of network protocols which have come up. */ + +#if PAP_SUPPORT + upap_state upap; /* PAP data */ +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + chap_client_state chap_client; /* CHAP client data */ +#if PPP_SERVER + chap_server_state chap_server; /* CHAP server data */ +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPORT */ + +#if EAP_SUPPORT + eap_state eap; /* EAP data */ +#endif /* EAP_SUPPORT */ + + fsm lcp_fsm; /* LCP fsm structure */ + lcp_options lcp_wantoptions; /* Options that we want to request */ + lcp_options lcp_gotoptions; /* Options that peer ack'd */ + lcp_options lcp_allowoptions; /* Options we allow peer to request */ + lcp_options lcp_hisoptions; /* Options that we ack'd */ + u8_t lcp_echos_pending; /* Number of outstanding echo msgs */ + u8_t lcp_echo_number; /* ID number of next echo frame */ + u16_t peer_mru; /* currently negotiated peer MRU */ + + fsm ipcp_fsm; /* IPCP fsm structure */ + ipcp_options ipcp_wantoptions; /* Options that we want to request */ + ipcp_options ipcp_gotoptions; /* Options that peer ack'd */ + ipcp_options ipcp_allowoptions; /* Options we allow peer to request */ + ipcp_options ipcp_hisoptions; /* Options that we ack'd */ + +#if PPP_IPV6_SUPPORT + fsm ipv6cp_fsm; /* IPV6CP fsm structure */ + ipv6cp_options ipv6cp_wantoptions; /* Options that we want to request */ + ipv6cp_options ipv6cp_gotoptions; /* Options that peer ack'd */ + ipv6cp_options ipv6cp_allowoptions; /* Options we allow peer to request */ + ipv6cp_options ipv6cp_hisoptions; /* Options that we ack'd */ +#endif /* PPP_IPV6_SUPPORT */ +}; + +/************************ + *** PUBLIC FUNCTIONS *** + ************************/ + +/* + * Create a new PPP session. + * + * This initializes the PPP control block but does not + * attempt to negotiate the LCP session. + * + * Return a new PPP connection control block pointer + * on success or a null pointer on failure. + */ +ppp_pcb *ppp_new(void); + +/* + * Set a PPP interface as the default network interface + * (used to output all packets for which no specific route is found). + */ +void ppp_set_default(ppp_pcb *pcb); + +/* + * Set auth helper, optional, you can either fill ppp_pcb->settings. + * + * Warning: Using PPPAUTHTYPE_ANY might have security consequences. + * RFC 1994 says: + * + * In practice, within or associated with each PPP server, there is a + * database which associates "user" names with authentication + * information ("secrets"). It is not anticipated that a particular + * named user would be authenticated by multiple methods. This would + * make the user vulnerable to attacks which negotiate the least secure + * method from among a set (such as PAP rather than CHAP). If the same + * secret was used, PAP would reveal the secret to be used later with + * CHAP. + * + * Instead, for each user name there should be an indication of exactly + * one method used to authenticate that user name. If a user needs to + * make use of different authentication methods under different + * circumstances, then distinct user names SHOULD be employed, each of + * which identifies exactly one authentication method. + * + */ +#define PPPAUTHTYPE_NONE 0x00 +#define PPPAUTHTYPE_PAP 0x01 +#define PPPAUTHTYPE_CHAP 0x02 +#define PPPAUTHTYPE_MSCHAP 0x04 +#define PPPAUTHTYPE_EAP 0x08 +#define PPPAUTHTYPE_ANY 0xff + +void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd); + +#if PPP_NOTIFY_PHASE +/* + * Set a PPP notify phase callback. + * + * This can be used for example to set a LED pattern depending on the + * current phase of the PPP session. + */ +typedef void (*ppp_notify_phase_cb_fn)(ppp_pcb *pcb, u8_t phase, void *ctx); +void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb); +#endif /* PPP_NOTIFY_PHASE */ + +/* Link status callback function prototype */ +typedef void (*ppp_link_status_cb_fn)(ppp_pcb *pcb, int err_code, void *ctx); + +#if PPPOS_SUPPORT +/* + * Create a new PPP connection using the given serial I/O device. + * + * If this port connects to a modem, the modem connection must be + * established before calling this. + * + * Return 0 on success, an error code on failure. + */ +int ppp_over_serial_create(ppp_pcb *pcb, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +/* + * Create a new PPP Over Ethernet (PPPoE) connection. + * + * Return 0 on success, an error code on failure. + */ +int ppp_over_ethernet_create(ppp_pcb *pcb, struct netif *ethif, const char *service_name, const char *concentrator_name, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT +/* + * Create a new PPP Over L2TP (PPPoL2TP) connection. + */ +int ppp_over_l2tp_create(ppp_pcb *pcb, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb); +#endif /* PPPOL2TP_SUPPORT */ + +/* + * Open a PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * Holdoff is the time to wait (in seconds) before initiating + * the connection. + */ +int ppp_open(ppp_pcb *pcb, u16_t holdoff); + +/* + * Initiate the end of a PPP connection. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int ppp_close(ppp_pcb *pcb); + +/* + * Indicate to the PPP stack that the line has disconnected. + */ +void ppp_sighup(ppp_pcb *pcb); + +/* + * Free the control block, clean everything except the PPP PCB itself + * and the netif, it allows you to change the underlying PPP protocol + * (eg. from PPPoE to PPPoS to switch from DSL to GPRS) without losing + * your PPP and netif handlers. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +int ppp_free(ppp_pcb *pcb); + +/* + * Release the control block. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +int ppp_delete(ppp_pcb *pcb); + +/* + * Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. + */ +int ppp_ioctl(ppp_pcb *pcb, int cmd, void *arg); + +#if PPPOS_SUPPORT +/* + * PPP over Serial: this is the input function to be called for received data. + */ +void pppos_input(ppp_pcb *pcb, u_char* data, int len); +#endif /* PPPOS_SUPPORT */ + +/* Get the PPP netif interface */ +#define ppp_netif(ppp) (&(ppp)->netif) + +/* Get the PPP addresses */ +#define ppp_addrs(ppp) (&(ppp)->addrs) + +#if LWIP_NETIF_STATUS_CALLBACK +/* Set an lwIP-style status-callback for the selected PPP device */ +void ppp_set_netif_statuscallback(ppp_pcb *pcb, netif_status_callback_fn status_callback); +#endif /* LWIP_NETIF_STATUS_CALLBACK */ +#if LWIP_NETIF_LINK_CALLBACK +/* Set an lwIP-style link-callback for the selected PPP device */ +void ppp_set_netif_linkcallback(ppp_pcb *pcb, netif_status_callback_fn link_callback); +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +#endif /* PPP_H */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp_impl.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp_impl.h new file mode 100644 index 0000000..9584871 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/ppp_impl.h @@ -0,0 +1,581 @@ +/***************************************************************************** +* ppp.h - Network Point to Point Protocol header file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original derived from BSD codes. +*****************************************************************************/ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_IMP_H_ +#define PPP_IMP_H_ + +#include /* formats */ +#include +#include +#include /* strtol() */ + +#include "lwip/netif.h" +#include "lwip/def.h" +#include "lwip/timers.h" + +#include "ppp.h" +#include "pppdebug.h" + +/* + * Memory used for control packets. + * + * PPP_CTRL_PBUF_MAX_SIZE is the amount of memory we allocate when we + * cannot figure out how much we are going to use before filling the buffer. + */ +#if PPP_USE_PBUF_RAM +#define PPP_CTRL_PBUF_TYPE PBUF_RAM +#define PPP_CTRL_PBUF_MAX_SIZE 512 +#else /* PPP_USE_PBUF_RAM */ +#define PPP_CTRL_PBUF_TYPE PBUF_POOL +#define PPP_CTRL_PBUF_MAX_SIZE PBUF_POOL_BUFSIZE +#endif /* PPP_USE_PBUF_RAM */ + +/* + * Limits. + */ +#define MAXWORDLEN 1024 /* max length of word in file (incl null) */ +#define MAXARGS 1 /* max # args to a command */ +#define MAXNAMELEN 256 /* max length of hostname or name for auth */ +#define MAXSECRETLEN 256 /* max length of password or secret */ + +/* + * The basic PPP frame. + */ +#define PPP_ADDRESS(p) (((u_char *)(p))[0]) +#define PPP_CONTROL(p) (((u_char *)(p))[1]) +#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3]) + +/* + * Significant octet values. + */ +#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */ +#define PPP_UI 0x03 /* Unnumbered Information */ +#define PPP_FLAG 0x7e /* Flag Sequence */ +#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */ +#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */ + +/* + * Protocol field values. + */ +#define PPP_IP 0x21 /* Internet Protocol */ +#if 0 /* UNUSED */ +#define PPP_AT 0x29 /* AppleTalk Protocol */ +#define PPP_IPX 0x2b /* IPX protocol */ +#endif /* UNUSED */ +#if VJ_SUPPORT +#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */ +#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */ +#endif /* VJ_SUPPORT */ +#if PPP_IPV6_SUPPORT +#define PPP_IPV6 0x57 /* Internet Protocol Version 6 */ +#endif /* PPP_IPV6_SUPPORT */ +#if CCP_SUPPORT +#define PPP_COMP 0xfd /* compressed packet */ +#endif /* CCP_SUPPORT */ +#define PPP_IPCP 0x8021 /* IP Control Protocol */ +#if 0 /* UNUSED */ +#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */ +#define PPP_IPXCP 0x802b /* IPX Control Protocol */ +#endif /* UNUSED */ +#if PPP_IPV6_SUPPORT +#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */ +#endif /* PPP_IPV6_SUPPORT */ +#if CCP_SUPPORT +#define PPP_CCP 0x80fd /* Compression Control Protocol */ +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT +#define PPP_ECP 0x8053 /* Encryption Control Protocol */ +#endif /* ECP_SUPPORT */ +#define PPP_LCP 0xc021 /* Link Control Protocol */ +#if PAP_SUPPORT +#define PPP_PAP 0xc023 /* Password Authentication Protocol */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT +#define PPP_LQR 0xc025 /* Link Quality Report protocol */ +#endif /* LQR_SUPPORT */ +#if CHAP_SUPPORT +#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */ +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT +#define PPP_CBCP 0xc029 /* Callback Control Protocol */ +#endif /* CBCP_SUPPORT */ +#if EAP_SUPPORT +#define PPP_EAP 0xc227 /* Extensible Authentication Protocol */ +#endif /* EAP_SUPPORT */ + +/* + * Values for FCS calculations. + */ +#define PPP_INITFCS 0xffff /* Initial FCS value */ +#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ +#if PPP_FCS_TABLE + #define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) +#else +u16_t ppp_get_fcs(u8_t byte); +#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_get_fcs(((fcs) ^ (c)) & 0xff)) +#endif + + +/* + * What to do with network protocol (NP) packets. + */ +enum NPmode { + NPMODE_PASS, /* pass the packet through */ + NPMODE_DROP, /* silently drop the packet */ + NPMODE_ERROR, /* return an error */ + NPMODE_QUEUE /* save it up for later. */ +}; + +/* + * Statistics. + */ +#if PPP_STATS_SUPPORT +struct pppstat { + unsigned int ppp_ibytes; /* bytes received */ + unsigned int ppp_ipackets; /* packets received */ + unsigned int ppp_ierrors; /* receive errors */ + unsigned int ppp_obytes; /* bytes sent */ + unsigned int ppp_opackets; /* packets sent */ + unsigned int ppp_oerrors; /* transmit errors */ +}; + +#if VJ_SUPPORT +struct vjstat { + unsigned int vjs_packets; /* outbound packets */ + unsigned int vjs_compressed; /* outbound compressed packets */ + unsigned int vjs_searches; /* searches for connection state */ + unsigned int vjs_misses; /* times couldn't find conn. state */ + unsigned int vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned int vjs_compressedin; /* inbound compressed packets */ + unsigned int vjs_errorin; /* inbound unknown type packets */ + unsigned int vjs_tossed; /* inbound packets tossed because of error */ +}; +#endif /* VJ_SUPPORT */ + +struct ppp_stats { + struct pppstat p; /* basic PPP statistics */ +#if VJ_SUPPORT + struct vjstat vj; /* VJ header compression statistics */ +#endif /* VJ_SUPPORT */ +}; + +#if CCP_SUPPORT +struct compstat { + unsigned int unc_bytes; /* total uncompressed bytes */ + unsigned int unc_packets; /* total uncompressed packets */ + unsigned int comp_bytes; /* compressed bytes */ + unsigned int comp_packets; /* compressed packets */ + unsigned int inc_bytes; /* incompressible bytes */ + unsigned int inc_packets; /* incompressible packets */ + unsigned int ratio; /* recent compression ratio << 8 */ +}; + +struct ppp_comp_stats { + struct compstat c; /* packet compression statistics */ + struct compstat d; /* packet decompression statistics */ +}; +#endif /* CCP_SUPPORT */ + +#endif /* PPP_STATS_SUPPORT */ + +#if PPP_IDLETIMELIMIT +/* + * The following structure records the time in seconds since + * the last NP packet was sent or received. + */ +struct ppp_idle { + time_t xmit_idle; /* time since last NP packet sent */ + time_t recv_idle; /* time since last NP packet received */ +}; +#endif /* PPP_IDLETIMELIMIT */ + +/* values for epdisc.class */ +#define EPD_NULL 0 /* null discriminator, no data */ +#define EPD_LOCAL 1 +#define EPD_IP 2 +#define EPD_MAC 3 +#define EPD_MAGIC 4 +#define EPD_PHONENUM 5 + +/* + * Global variables. + */ +#ifdef HAVE_MULTILINK +extern u8_t multilink; /* enable multilink operation */ +extern u8_t doing_multilink; +extern u8_t multilink_master; +extern u8_t bundle_eof; +extern u8_t bundle_terminating; +#endif + +#ifdef MAXOCTETS +extern unsigned int maxoctets; /* Maximum octetes per session (in bytes) */ +extern int maxoctets_dir; /* Direction : + 0 - in+out (default) + 1 - in + 2 - out + 3 - max(in,out) */ +extern int maxoctets_timeout; /* Timeout for check of octets limit */ +#define PPP_OCTETS_DIRECTION_SUM 0 +#define PPP_OCTETS_DIRECTION_IN 1 +#define PPP_OCTETS_DIRECTION_OUT 2 +#define PPP_OCTETS_DIRECTION_MAXOVERAL 3 +/* same as previos, but little different on RADIUS side */ +#define PPP_OCTETS_DIRECTION_MAXSESSION 4 +#endif + +/* + * The following struct gives the addresses of procedures to call + * for a particular protocol. + */ +struct protent { + u_short protocol; /* PPP protocol number */ + /* Initialization procedure */ + void (*init) (ppp_pcb *pcb); + /* Process a received packet */ + void (*input) (ppp_pcb *pcb, u_char *pkt, int len); + /* Process a received protocol-reject */ + void (*protrej) (ppp_pcb *pcb); + /* Lower layer has come up */ + void (*lowerup) (ppp_pcb *pcb); + /* Lower layer has gone down */ + void (*lowerdown) (ppp_pcb *pcb); + /* Open the protocol */ + void (*open) (ppp_pcb *pcb); + /* Close the protocol */ + void (*close) (ppp_pcb *pcb, const char *reason); +#if PRINTPKT_SUPPORT + /* Print a packet in readable form */ + int (*printpkt) (u_char *pkt, int len, + void (*printer) (void *, const char *, ...), + void *arg); +#endif /* PRINTPKT_SUPPORT */ + /* FIXME: data input is only used by CCP, which is not supported at this time, + * should we remove this entry and save some flash ? + */ + /* Process a received data packet */ + void (*datainput) (ppp_pcb *pcb, u_char *pkt, int len); + u8_t enabled_flag; /* 0 if protocol is disabled */ +#if PRINTPKT_SUPPORT + const char *name; /* Text name of protocol */ + const char *data_name; /* Text name of corresponding data protocol */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + option_t *options; /* List of command-line options */ + /* Check requested options, assign defaults */ + void (*check_options) (void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + /* Configure interface for demand-dial */ + int (*demand_conf) (int unit); + /* Say whether to bring up link for this pkt */ + int (*active_pkt) (u_char *pkt, int len); +#endif /* DEMAND_SUPPORT */ +}; + +/* Table of pointers to supported protocols */ +extern const struct protent* const protocols[]; + + +/* Values for auth_pending, auth_done */ +#if PAP_SUPPORT +#define PAP_WITHPEER 0x1 +#define PAP_PEER 0x2 +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#define CHAP_WITHPEER 0x4 +#define CHAP_PEER 0x8 +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#define EAP_WITHPEER 0x10 +#define EAP_PEER 0x20 +#endif /* EAP_SUPPORT */ + +/* Values for auth_done only */ +#if CHAP_SUPPORT +#define CHAP_MD5_WITHPEER 0x40 +#define CHAP_MD5_PEER 0x80 +#if MSCHAP_SUPPORT +#define CHAP_MS_SHIFT 8 /* LSB position for MS auths */ +#define CHAP_MS_WITHPEER 0x100 +#define CHAP_MS_PEER 0x200 +#define CHAP_MS2_WITHPEER 0x400 +#define CHAP_MS2_PEER 0x800 +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ + +/* Supported CHAP protocols */ +#if CHAP_SUPPORT +#include "chap-new.h" +#if MSCHAP_SUPPORT +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT | MDTYPE_MD5) +#else +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_MD5) +#endif +#else +#define CHAP_MDTYPE_SUPPORTED (MDTYPE_NONE) +#endif + +#if PPP_STATS_SUPPORT +/* + * PPP statistics structure + */ +struct pppd_stats { + unsigned int bytes_in; + unsigned int bytes_out; + unsigned int pkts_in; + unsigned int pkts_out; +}; +#endif /* PPP_STATS_SUPPORT */ + + +/* PPP flow functions + */ +/* initialize the PPP subsystem */ +int ppp_init(void); + +/* function called by pppoe.c */ +void ppp_input(ppp_pcb *pcb, struct pbuf *pb); + +/* function called by all PPP subsystems to send packets */ +int ppp_write(ppp_pcb *pcb, struct pbuf *p); + +/* functions called by auth.c link_terminated() */ +void ppp_link_down(ppp_pcb *pcb); +void ppp_link_terminated(ppp_pcb *pcb); + +/* merge a pbuf chain into one pbuf */ +struct pbuf * ppp_singlebuf(struct pbuf *p); + + +/* Functions called by various PPP subsystems to configure + * the PPP interface or change the PPP phase. + */ +void new_phase(ppp_pcb *pcb, int p); + +#if PPPOS_SUPPORT +void ppp_set_xaccm(ppp_pcb *pcb, ext_accm *accm); +#endif /* PPPOS_SUPPORT */ +int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp); +int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp); + +int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, u32_t net_mask); +int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr); + +#if PPP_IPV6_SUPPORT +int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64); +int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64); +#endif /* PPP_IPV6_SUPPORT */ + +int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2); +int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2); + +int sifup(ppp_pcb *pcb); +int sifdown (ppp_pcb *pcb); + +#if PPP_IPV6_SUPPORT +int sif6up(ppp_pcb *pcb); +int sif6down (ppp_pcb *pcb); +#endif /* PPP_IPV6_SUPPORT */ + +int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode); + +void netif_set_mtu(ppp_pcb *pcb, int mtu); +int netif_get_mtu(ppp_pcb *pcb); + +int sifproxyarp(ppp_pcb *pcb, u32_t his_adr); +int cifproxyarp(ppp_pcb *pcb, u32_t his_adr); + +int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid); + +#if PPP_IDLETIMELIMIT +int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip); +#endif /* PPP_IDLETIMELIMIT */ + +int get_loop_output(void); + +u32_t get_mask (u32_t addr); + + +/* Optional protocol names list, to make our messages a little more informative. */ +#if PPP_PROTOCOLNAME +const char * protocol_name(int proto); +#endif /* PPP_PROTOCOLNAME */ + + +/* Optional stats support, to get some statistics on the PPP interface */ +#if PPP_STATS_SUPPORT +void print_link_stats(void); /* Print stats, if available */ +void reset_link_stats(int u); /* Reset (init) stats when link goes up */ +void update_link_stats(int u); /* Get stats at link termination */ +#endif /* PPP_STATS_SUPPORT */ + + + +/* + * Inline versions of get/put char/short/long. + * Pointer is advanced; we assume that both arguments + * are lvalues and will already be in registers. + * cp MUST be u_char *. + */ +#define GETCHAR(c, cp) { \ + (c) = *(cp)++; \ +} +#define PUTCHAR(c, cp) { \ + *(cp)++ = (u_char) (c); \ +} + + +#define GETSHORT(s, cp) { \ + (s) = *(cp)++ << 8; \ + (s) |= *(cp)++; \ +} +#define PUTSHORT(s, cp) { \ + *(cp)++ = (u_char) ((s) >> 8); \ + *(cp)++ = (u_char) (s); \ +} + +#define GETLONG(l, cp) { \ + (l) = *(cp)++ << 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; (l) <<= 8; \ + (l) |= *(cp)++; \ +} +#define PUTLONG(l, cp) { \ + *(cp)++ = (u_char) ((l) >> 24); \ + *(cp)++ = (u_char) ((l) >> 16); \ + *(cp)++ = (u_char) ((l) >> 8); \ + *(cp)++ = (u_char) (l); \ +} + +#define INCPTR(n, cp) ((cp) += (n)) +#define DECPTR(n, cp) ((cp) -= (n)) + +/* + * System dependent definitions for user-level 4.3BSD UNIX implementation. + */ +#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0) +#define TIMEOUTMS(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t), (f), (a)); } while(0) +#define UNTIMEOUT(f, a) sys_untimeout((f), (a)) + +#define BZERO(s, n) memset(s, 0, n) +#define BCMP(s1, s2, l) memcmp(s1, s2, l) + +#define PRINTMSG(m, l) { ppp_info("Remote message: %0.*v", l, m); } + +/* + * MAKEHEADER - Add Header fields to a packet. + */ +#define MAKEHEADER(p, t) { \ + PUTCHAR(PPP_ALLSTATIONS, p); \ + PUTCHAR(PPP_UI, p); \ + PUTSHORT(t, p); } + +/* Procedures exported from auth.c */ +void link_required(ppp_pcb *pcb); /* we are starting to use the link */ +void link_terminated(ppp_pcb *pcb); /* we are finished with the link */ +void link_down(ppp_pcb *pcb); /* the LCP layer has left the Opened state */ +void upper_layers_down(ppp_pcb *pcb); /* take all NCPs down */ +void link_established(ppp_pcb *pcb); /* the link is up; authenticate now */ +void start_networks(ppp_pcb *pcb); /* start all the network control protos */ +void continue_networks(ppp_pcb *pcb); /* start network [ip, etc] control protos */ +#if PPP_SERVER +void auth_peer_fail(ppp_pcb *pcb, int protocol); + /* peer failed to authenticate itself */ +void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen); + /* peer successfully authenticated itself */ +#endif /* PPP_SERVER */ +void auth_withpeer_fail(ppp_pcb *pcb, int protocol); + /* we failed to authenticate ourselves */ +void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor); + /* we successfully authenticated ourselves */ +void np_up(ppp_pcb *pcb, int proto); /* a network protocol has come up */ +void np_down(ppp_pcb *pcb, int proto); /* a network protocol has gone down */ +void np_finished(ppp_pcb *pcb, int proto); /* a network protocol no longer needs link */ +void auth_reset(ppp_pcb *pcb); /* check what secrets we have */ +int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server); + /* get "secret" for chap */ + +/* Procedures exported from ipcp.c */ +/* int parse_dotted_ip (char *, u32_t *); */ + +/* Procedures exported from demand.c */ +#if DEMAND_SUPPORT +void demand_conf (void); /* config interface(s) for demand-dial */ +void demand_block (void); /* set all NPs to queue up packets */ +void demand_unblock (void); /* set all NPs to pass packets */ +void demand_discard (void); /* set all NPs to discard packets */ +void demand_rexmit (int, u32_t); /* retransmit saved frames for an NP*/ +int loop_chars (unsigned char *, int); /* process chars from loopback */ +int loop_frame (unsigned char *, int); /* should we bring link up? */ +#endif /* DEMAND_SUPPORT */ + +/* Procedures exported from multilink.c */ +#ifdef HAVE_MULTILINK +void mp_check_options (void); /* Check multilink-related options */ +int mp_join_bundle (void); /* join our link to an appropriate bundle */ +void mp_exit_bundle (void); /* have disconnected our link from bundle */ +void mp_bundle_terminated (void); +char *epdisc_to_str (struct epdisc *); /* string from endpoint discrim. */ +int str_to_epdisc (struct epdisc *, char *); /* endpt disc. from str */ +#else +#define mp_bundle_terminated() /* nothing */ +#define mp_exit_bundle() /* nothing */ +#define doing_multilink 0 +#define multilink_master 0 +#endif + +/* Procedures exported from utils.c. */ +void ppp_print_string(char *p, int len, void (*printer) (void *, const char *, ...), void *arg); /* Format a string for output */ +int ppp_slprintf(char *buf, int buflen, const char *fmt, ...); /* sprintf++ */ +int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args); /* vsprintf++ */ +size_t ppp_strlcpy(char *dest, const char *src, size_t len); /* safe strcpy */ +size_t ppp_strlcat(char *dest, const char *src, size_t len); /* safe strncpy */ +void ppp_dbglog(const char *fmt, ...); /* log a debug message */ +void ppp_info(const char *fmt, ...); /* log an informational message */ +void ppp_notice(const char *fmt, ...); /* log a notice-level message */ +void ppp_warn(const char *fmt, ...); /* log a warning message */ +void ppp_error(const char *fmt, ...); /* log an error message */ +void ppp_fatal(const char *fmt, ...); /* log an error message and die(1) */ +#if PRINTPKT_SUPPORT +void ppp_dump_packet(const char *tag, unsigned char *p, int len); + /* dump packet to debug log if interesting */ +#endif /* PRINTPKT_SUPPORT */ + + +#endif /* PPP_IMP_H_ */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppcrypt.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppcrypt.h new file mode 100644 index 0000000..ef2e87d --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppcrypt.h @@ -0,0 +1,43 @@ +/* + * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 + * + * Extracted from chap_ms.c by James Carlson. + * + * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPCRYPT_H +#define PPPCRYPT_H + +void pppcrypt_56_to_64_bit_key(u_char *key, u_char *des_key); + +#endif /* PPPCRYPT_H */ + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppdebug.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppdebug.h new file mode 100644 index 0000000..e35c8e0 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppdebug.h @@ -0,0 +1,80 @@ +/***************************************************************************** +* pppdebug.h - System debugging utilities. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1998 Global Election Systems Inc. +* portions Copyright (c) 2001 by Cognizant Pty Ltd. +* +* 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 (please don't use tabs!) +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-07-29 Guy Lancaster , Global Election Systems Inc. +* Original. +* +***************************************************************************** +*/ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPDEBUG_H +#define PPPDEBUG_H + +/* Trace levels. */ +#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE) +#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define LOG_INFO (PPP_DEBUG) +#define LOG_DETAIL (PPP_DEBUG) +#define LOG_DEBUG (PPP_DEBUG) + +#if PPP_DEBUG + +#define MAINDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define SYSDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define FSMDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define LCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define IPCPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define IPV6CPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define UPAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define CHAPDEBUG(a) LWIP_DEBUGF(LWIP_DBG_LEVEL_WARNING, a) +#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b) + +#else /* PPP_DEBUG */ + +#define MAINDEBUG(a) +#define SYSDEBUG(a) +#define FSMDEBUG(a) +#define LCPDEBUG(a) +#define IPCPDEBUG(a) +#define IPV6CPDEBUG(a) +#define UPAPDEBUG(a) +#define CHAPDEBUG(a) +#define PPPDEBUG(a, b) + +#endif /* PPP_DEBUG */ + +#endif /* PPPDEBUG_H */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppoe.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppoe.h new file mode 100644 index 0000000..eae032c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppoe.h @@ -0,0 +1,183 @@ +/***************************************************************************** +* pppoe.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. + */ +#include "lwip/opt.h" +#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPP_OE_H +#define PPP_OE_H + +#include "ppp.h" +#include "netif/etharp.h" + +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppoehdr { + PACK_STRUCT_FLD_8(u8_t vertype); + PACK_STRUCT_FLD_8(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 +/* passive */ +#define PPPOE_STATE_PADO_SENT 1 + +#define PPPOE_CB_STATE_UP 0 /* PPPoE link is UP */ +#define PPPOE_CB_STATE_DOWN 1 /* PPPoE link is DOWN - normal condition */ +#define PPPOE_CB_STATE_FAILED 2 /* Failed to setup PPPoE link */ + +#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 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 */ + ppp_pcb *pcb; /* PPP PCB */ + void (*sc_link_status_cb)(ppp_pcb *pcb, int up); + + struct eth_addr sc_dest; /* hardware address of concentrator */ + u16_t sc_session; /* PPPoE session id */ + u8_t sc_state; /* discovery phase or session connected */ + +#ifdef PPPOE_TODO + u8_t *sc_service_name; /* if != NULL: requested name of service */ + u8_t *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 */ + u8_t sc_ac_cookie_len; /* length of cookie data */ +#ifdef PPPOE_SERVER + u8_t *sc_hunique; /* content of host unique we must echo back */ + u8_t sc_hunique_len; /* length of host unique */ +#endif + u8_t sc_padi_retried; /* number of PADI retries already done */ + u8_t sc_padr_retried; /* number of PADR retries already done */ +}; + + +#define pppoe_init() /* compatibility define, no initialization needed */ + +err_t pppoe_create(struct netif *ethif, ppp_pcb *pcb, void (*link_status_cb)(ppp_pcb *pcb, int up), struct pppoe_softc **scptr); +err_t pppoe_destroy(struct pppoe_softc *sc); + +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); + +#endif /* PPP_OE_H */ + +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppol2tp.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppol2tp.h new file mode 100644 index 0000000..5b869ae --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/pppol2tp.h @@ -0,0 +1,217 @@ +/** + * @file + * Network Point to Point Protocol over Layer 2 Tunneling Protocol header file. + * + */ + +/* + * 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 PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef PPPOL2TP_H_ +#define PPPOL2TP_H_ + +#include "ppp.h" + +/* Timeout */ +#define PPPOL2TP_CONTROL_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOL2TP_SLOW_RETRY (60*1000) /* persistent retry interval */ + +#define PPPOL2TP_MAXSCCRQ 4 /* retry SCCRQ four times (quickly) */ +#define PPPOL2TP_MAXICRQ 4 /* retry IRCQ four times */ +#define PPPOL2TP_MAXICCN 4 /* retry ICCN four times */ + +/* L2TP header flags */ +#define PPPOL2TP_HEADERFLAG_CONTROL 0x8000 +#define PPPOL2TP_HEADERFLAG_LENGTH 0x4000 +#define PPPOL2TP_HEADERFLAG_SEQUENCE 0x0800 +#define PPPOL2TP_HEADERFLAG_OFFSET 0x0200 +#define PPPOL2TP_HEADERFLAG_PRIORITY 0x0100 +#define PPPOL2TP_HEADERFLAG_VERSION 0x0002 + +/* Mandatory bits for control: Control, Length, Sequence, Version 2 */ +#define PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY (PPPOL2TP_HEADERFLAG_CONTROL|PPPOL2TP_HEADERFLAG_LENGTH|PPPOL2TP_HEADERFLAG_SEQUENCE|PPPOL2TP_HEADERFLAG_VERSION) +/* Forbidden bits for control: Offset, Priority */ +#define PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN (PPPOL2TP_HEADERFLAG_OFFSET|PPPOL2TP_HEADERFLAG_PRIORITY) + +/* Mandatory bits for data: Version 2 */ +#define PPPOL2TP_HEADERFLAG_DATA_MANDATORY (PPPOL2TP_HEADERFLAG_VERSION) + +/* AVP (Attribute Value Pair) header */ +#define PPPOL2TP_AVPHEADERFLAG_MANDATORY 0x8000 +#define PPPOL2TP_AVPHEADERFLAG_HIDDEN 0x4000 +#define PPPOL2TP_AVPHEADERFLAG_LENGTHMASK 0x03ff + +/* -- AVP - Message type */ +#define PPPOL2TP_AVPTYPE_MESSAGE 0 /* Message type */ + +/* Control Connection Management */ +#define PPPOL2TP_MESSAGETYPE_SCCRQ 1 /* Start Control Connection Request */ +#define PPPOL2TP_MESSAGETYPE_SCCRP 2 /* Start Control Connection Reply */ +#define PPPOL2TP_MESSAGETYPE_SCCCN 3 /* Start Control Connection Connected */ +#define PPPOL2TP_MESSAGETYPE_STOPCCN 4 /* Stop Control Connection Notification */ +#define PPPOL2TP_MESSAGETYPE_HELLO 6 /* Hello */ +/* Call Management */ +#define PPPOL2TP_MESSAGETYPE_OCRQ 7 /* Outgoing Call Request */ +#define PPPOL2TP_MESSAGETYPE_OCRP 8 /* Outgoing Call Reply */ +#define PPPOL2TP_MESSAGETYPE_OCCN 9 /* Outgoing Call Connected */ +#define PPPOL2TP_MESSAGETYPE_ICRQ 10 /* Incoming Call Request */ +#define PPPOL2TP_MESSAGETYPE_ICRP 11 /* Incoming Call Reply */ +#define PPPOL2TP_MESSAGETYPE_ICCN 12 /* Incoming Call Connected */ +#define PPPOL2TP_MESSAGETYPE_CDN 14 /* Call Disconnect Notify */ +/* Error reporting */ +#define PPPOL2TP_MESSAGETYPE_WEN 15 /* WAN Error Notify */ +/* PPP Session Control */ +#define PPPOL2TP_MESSAGETYPE_SLI 16 /* Set Link Info */ + +/* -- AVP - Result code */ +#define PPPOL2TP_AVPTYPE_RESULTCODE 1 /* Result code */ +#define PPPOL2TP_RESULTCODE 1 /* General request to clear control connection */ + +/* -- AVP - Protocol version (!= L2TP Header version) */ +#define PPPOL2TP_AVPTYPE_VERSION 2 +#define PPPOL2TP_VERSION 0x0100 /* L2TP Protocol version 1, revision 0 */ + +/* -- AVP - Framing capabilities */ +#define PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES 3 /* Bearer capabilities */ +#define PPPOL2TP_FRAMINGCAPABILITIES 0x00000003 /* Async + Sync framing */ + +/* -- AVP - Bearer capabilities */ +#define PPPOL2TP_AVPTYPE_BEARERCAPABILITIES 4 /* Bearer capabilities */ +#define PPPOL2TP_BEARERCAPABILITIES 0x00000003 /* Analog + Digital Access */ + +/* -- AVP - Tie breaker */ +#define PPPOL2TP_AVPTYPE_TIEBREAKER 5 + +/* -- AVP - Host name */ +#define PPPOL2TP_AVPTYPE_HOSTNAME 7 /* Host name */ +#define PPPOL2TP_HOSTNAME "lwIP" /* FIXME: make it configurable */ + +/* -- AVP - Vendor name */ +#define PPPOL2TP_AVPTYPE_VENDORNAME 8 /* Vendor name */ +#define PPPOL2TP_VENDORNAME "lwIP" /* FIXME: make it configurable */ + +/* -- AVP - Assign tunnel ID */ +#define PPPOL2TP_AVPTYPE_TUNNELID 9 /* Assign Tunnel ID */ + +/* -- AVP - Receive window size */ +#define PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE 10 /* Receive window size */ +#define PPPOL2TP_RECEIVEWINDOWSIZE 8 /* FIXME: make it configurable */ + +/* -- AVP - Challenge */ +#define PPPOL2TP_AVPTYPE_CHALLENGE 11 /* Challenge */ + +/* -- AVP - Cause code */ +#define PPPOL2TP_AVPTYPE_CAUSECODE 12 /* Cause code*/ + +/* -- AVP - Challenge response */ +#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE 13 /* Challenge response */ +#define PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE 16 + +/* -- AVP - Assign session ID */ +#define PPPOL2TP_AVPTYPE_SESSIONID 14 /* Assign Session ID */ + +/* -- AVP - Call serial number */ +#define PPPOL2TP_AVPTYPE_CALLSERIALNUMBER 15 /* Call Serial Number */ + +/* -- AVP - Framing type */ +#define PPPOL2TP_AVPTYPE_FRAMINGTYPE 19 /* Framing Type */ +#define PPPOL2TP_FRAMINGTYPE 0x00000001 /* Sync framing */ + +/* -- AVP - TX Connect Speed */ +#define PPPOL2TP_AVPTYPE_TXCONNECTSPEED 24 /* TX Connect Speed */ +#define PPPOL2TP_TXCONNECTSPEED 100000000 /* Connect speed: 100 Mbits/s */ + +/* L2TP Session state */ +#define PPPOL2TP_STATE_INITIAL 0 +#define PPPOL2TP_STATE_SCCRQ_SENT 1 +#define PPPOL2TP_STATE_ICRQ_SENT 2 +#define PPPOL2TP_STATE_ICCN_SENT 3 +#define PPPOL2TP_STATE_DATA 4 + +#define PPPOL2TP_CB_STATE_UP 0 /* PPPoL2TP link is UP */ +#define PPPOL2TP_CB_STATE_DOWN 1 /* PPPo2TP link is DOWN - normal condition */ +#define PPPOL2TP_CB_STATE_FAILED 2 /* Failed to setup PPPo2TP link */ + +#define PPPOL2TP_OUTPUT_DATA_HEADER_LEN 6 /* Our data header len */ + +/* + * PPPoL2TP interface control block. + */ +typedef struct pppol2tp_pcb_s pppol2tp_pcb; +struct pppol2tp_pcb_s { + ppp_pcb *ppp; /* PPP PCB */ + u8_t phase; /* L2TP phase */ + void (*link_status_cb)(ppp_pcb *pcb, int status); + struct udp_pcb *udp; /* UDP L2TP Socket */ + struct netif *netif; /* Output interface, used as a default route */ + ip_addr_t remote_ip; /* LNS IP Address */ + u16_t remote_port; /* LNS port */ +#if PPPOL2TP_AUTH_SUPPORT + u8_t *secret; /* Secret string */ + u8_t secret_len; /* Secret string length */ + u8_t secret_rv[16]; /* Random vector */ + u8_t challenge_hash[16]; /* Challenge response */ + u8_t send_challenge; /* Boolean whether the next sent packet should contains a challenge response */ +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + u16_t tunnel_port; /* Tunnel port */ + u16_t our_ns; /* NS to peer */ + u16_t peer_nr; /* NR from peer */ + u16_t peer_ns; /* NS from peer */ + u16_t source_tunnel_id; /* Tunnel ID assigned by peer */ + u16_t remote_tunnel_id; /* Tunnel ID assigned to peer */ + u16_t source_session_id; /* Session ID assigned by peer */ + u16_t remote_session_id; /* Session ID assigned to peer */ + + u8_t sccrq_retried; /* number of SCCRQ retries already done */ + u8_t icrq_retried; /* number of ICRQ retries already done */ + u8_t iccn_retried; /* number of ICCN retries already done */ +}; + + +/* Create a new L2TP session. */ +err_t pppol2tp_create(ppp_pcb *ppp, void (*link_status_cb)(ppp_pcb *pcb, int status), pppol2tp_pcb **l2tpptr, + struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len); + +/* Destroy a L2TP control block */ +err_t pppol2tp_destroy(pppol2tp_pcb *l2tp); + +/* Be a LAC, connect to a LNS. */ +err_t pppol2tp_connect(pppol2tp_pcb *l2tp); + +/* Disconnect */ +void pppol2tp_disconnect(pppol2tp_pcb *l2tp); + +/* Data packet from PPP to L2TP */ +err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb); + +#endif /* PPPOL2TP_H_ */ +#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/upap.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/upap.h new file mode 100644 index 0000000..bb9309b --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/upap.h @@ -0,0 +1,123 @@ +/* + * upap.h - User/Password Authentication Protocol definitions. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: upap.h,v 1.8 2002/12/04 23:03:33 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef UPAP_H +#define UPAP_H + +#include "ppp.h" + +/* + * Packet header = Code, id, length. + */ +#define UPAP_HEADERLEN 4 + + +/* + * UPAP codes. + */ +#define UPAP_AUTHREQ 1 /* Authenticate-Request */ +#define UPAP_AUTHACK 2 /* Authenticate-Ack */ +#define UPAP_AUTHNAK 3 /* Authenticate-Nak */ + + +/* + * Client states. + */ +#define UPAPCS_INITIAL 0 /* Connection down */ +#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPCS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */ +#define UPAPCS_OPEN 4 /* We've received an Ack */ +#define UPAPCS_BADAUTH 5 /* We've received a Nak */ + +/* + * Server states. + */ +#define UPAPSS_INITIAL 0 /* Connection down */ +#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */ +#define UPAPSS_PENDING 2 /* Connection down, have requested auth */ +#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */ +#define UPAPSS_OPEN 4 /* We've sent an Ack */ +#define UPAPSS_BADAUTH 5 /* We've sent a Nak */ + + +/* + * Timeouts. + */ +#if 0 /* moved to opt.h */ +#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */ +#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */ +#endif /* moved to opt.h */ + +/* + * Each interface is described by upap structure. + */ +#if PAP_SUPPORT +typedef struct upap_state { + const char *us_user; /* User */ + u8_t us_userlen; /* User length */ + const char *us_passwd; /* Password */ + u8_t us_passwdlen; /* Password length */ + u8_t us_clientstate; /* Client state */ +#if PPP_SERVER + u8_t us_serverstate; /* Server state */ +#endif /* PPP_SERVER */ + u8_t us_id; /* Current id */ + u8_t us_transmits; /* Number of auth-reqs sent */ +} upap_state; +#endif /* PAP_SUPPORT */ + + +void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password); +#if PPP_SERVER +void upap_authpeer(ppp_pcb *pcb); +#endif /* PPP_SERVER */ + +extern const struct protent pap_protent; + +#endif /* UPAP_H */ +#endif /* PPP_SUPPORT && PAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/vj.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/vj.h new file mode 100644 index 0000000..3780667 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/ppp/vj.h @@ -0,0 +1,161 @@ +/* + * Definitions for tcp compression routines. + * + * $Id: vj.h,v 1.7 2010/02/22 17:52:09 goldsimon Exp $ + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#ifndef VJ_H +#define VJ_H + +#include "lwip/ip.h" +#include "lwip/tcp_impl.h" + +#define MAX_SLOTS 16 /* must be > 2 and < 256 */ +#define MAX_HDR 128 + +/* + * Compressed packet format: + * + * The first octet contains the packet type (top 3 bits), TCP + * 'push' bit, and flags that indicate which of the 4 TCP sequence + * numbers have changed (bottom 5 bits). The next octet is a + * conversation number that associates a saved IP/TCP header with + * the compressed packet. The next two octets are the TCP checksum + * from the original datagram. The next 0 to 15 octets are + * sequence number changes, one change per bit set in the header + * (there may be no changes and there are two special cases where + * the receiver implicitly knows what changed -- see below). + * + * There are 5 numbers which can change (they are always inserted + * in the following order): TCP urgent pointer, window, + * acknowlegement, sequence number and IP ID. (The urgent pointer + * is different from the others in that its value is sent, not the + * change in value.) Since typical use of SLIP links is biased + * toward small packets (see comments on MTU/MSS below), changes + * use a variable length coding with one octet for numbers in the + * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the + * range 256 - 65535 or 0. (If the change in sequence number or + * ack is more than 65535, an uncompressed packet is sent.) + */ + +/* + * Packet types (must not conflict with IP protocol version) + * + * The top nibble of the first octet is the packet type. There are + * three possible types: IP (not proto TCP or tcp with one of the + * control flags set); uncompressed TCP (a normal IP/TCP packet but + * with the 8-bit protocol field replaced by an 8-bit connection id -- + * this type of packet syncs the sender & receiver); and compressed + * TCP (described above). + * + * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and + * is logically part of the 4-bit "changes" field that follows. Top + * three bits are actual packet type. For backward compatibility + * and in the interest of conserving bits, numbers are chosen so the + * IP protocol version number (4) which normally appears in this nibble + * means "IP packet". + */ + +/* packet types */ +#define TYPE_IP 0x40 +#define TYPE_UNCOMPRESSED_TCP 0x70 +#define TYPE_COMPRESSED_TCP 0x80 +#define TYPE_ERROR 0x00 + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + + +/* + * "state" data for each active tcp conversation on the wire. This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct cstate { + struct cstate *cs_next; /* next most recently used state (xmit only) */ + u_short cs_hlen; /* size of hdr (receive only) */ + u_char cs_id; /* connection # associated with this state */ + u_char cs_filler; + union { + char csu_hdr[MAX_HDR]; + struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */ + } vjcs_u; +}; +#define cs_ip vjcs_u.csu_ip +#define cs_hdr vjcs_u.csu_hdr + + +struct vjstat { + unsigned long vjs_packets; /* outbound packets */ + unsigned long vjs_compressed; /* outbound compressed packets */ + unsigned long vjs_searches; /* searches for connection state */ + unsigned long vjs_misses; /* times couldn't find conn. state */ + unsigned long vjs_uncompressedin; /* inbound uncompressed packets */ + unsigned long vjs_compressedin; /* inbound compressed packets */ + unsigned long vjs_errorin; /* inbound unknown type packets */ + unsigned long vjs_tossed; /* inbound packets tossed because of error */ +}; + +/* + * all the state data for one serial line (we need one of these per line). + */ +struct vjcompress { + struct cstate *last_cs; /* most recently used tstate */ + u_char last_recv; /* last rcvd conn. id */ + u_char last_xmit; /* last sent conn. id */ + u_short flags; + u_char maxSlotIndex; + u_char compressSlot; /* Flag indicating OK to compress slot ID. */ +#if LINK_STATS + struct vjstat stats; +#endif + struct cstate tstate[MAX_SLOTS]; /* xmit connection states */ + struct cstate rstate[MAX_SLOTS]; /* receive connection states */ +}; + +/* flag values */ +#define VJF_TOSS 1U /* tossing rcvd frames because of input err */ + +extern void vj_compress_init (struct vjcompress *comp); +extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb); +extern void vj_uncompress_err (struct vjcompress *comp); +extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp); +extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp); + +#endif /* VJ_H */ + +#endif /* PPP_SUPPORT && VJ_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/slipif.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/slipif.h new file mode 100644 index 0000000..dc40d30 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/netif/slipif.h @@ -0,0 +1,81 @@ +/* + * 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 LWIP_HDR_NETIF_SLIPIF_H +#define LWIP_HDR_NETIF_SLIPIF_H + +#include "lwip/opt.h" +#include "lwip/netif.h" + +/** Set this to 1 to start a thread that blocks reading on the serial line + * (using sio_read()). + */ +#ifndef SLIP_USE_RX_THREAD +#define SLIP_USE_RX_THREAD !NO_SYS +#endif + +/** Set this to 1 to enable functions to pass in RX bytes from ISR context. + * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled + * packets on a queue, which is fed into lwIP from slipif_poll(). + * If disabled, slipif_poll() polls the serial line (using sio_tryread()). + */ +#ifndef SLIP_RX_FROM_ISR +#define SLIP_RX_FROM_ISR 0 +#endif + +/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets + * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available. + * If disabled, packets will be dropped if more than one packet is received. + */ +#ifndef SLIP_RX_QUEUE +#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +err_t slipif_init(struct netif * netif); +void slipif_poll(struct netif *netif); +#if SLIP_RX_FROM_ISR +void slipif_process_rxqueue(struct netif *netif); +void slipif_received_byte(struct netif *netif, u8_t data); +void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len); +#endif /* SLIP_RX_FROM_ISR */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_NETIF_SLIPIF_H */ + diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/netdb.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/netdb.h new file mode 100644 index 0000000..7134032 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/netdb.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/netdb.h. + */ + +/* + * 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/netdb.h" diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/sys/socket.h b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/sys/socket.h new file mode 100644 index 0000000..f7c7066 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/include/posix/sys/socket.h @@ -0,0 +1,33 @@ +/** + * @file + * This file is a posix wrapper for lwip/sockets.h. + */ + +/* + * 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/sockets.h" diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/FILES b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/FILES new file mode 100644 index 0000000..099dbf3 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/FILES @@ -0,0 +1,29 @@ +This directory contains generic network interface device drivers that +do not contain any hardware or architecture specific code. The files +are: + +etharp.c + Implements the ARP (Address Resolution Protocol) over + Ethernet. The code in this file should be used together with + Ethernet device drivers. Note that this module has been + largely made Ethernet independent so you should be able to + adapt this for other link layers (such as Firewire). + +ethernetif.c + An example of how an Ethernet device driver could look. This + file can be used as a "skeleton" for developing new Ethernet + network device drivers. It uses the etharp.c ARP code. + +loopif.c + A "loopback" network interface driver. It requires configuration + through the define LWIP_LOOPIF_MULTITHREADING (see opt.h). + +slipif.c + A generic implementation of the SLIP (Serial Line IP) + protocol. It requires a sio (serial I/O) module to work. + +ppp/ Point-to-Point Protocol stack + The PPP stack has been ported from ucip (http://ucip.sourceforge.net). + It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although + compared to that, it has some modifications for embedded systems and + the source code has been reordered a bit. \ No newline at end of file diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/etharp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/etharp.c new file mode 100644 index 0000000..b00cd80 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/etharp.c @@ -0,0 +1,1484 @@ +/** + * @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/lwip_ip.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/dhcp.h" +#include "lwip/autoip.h" +#include "netif/etharp.h" +#include "lwip/ip6.h" + +#if PPPOE_SUPPORT +#include "netif/ppp/pppoe.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}}; + +/** The 24-bit IANA multicast OUI is 01-00-5e: */ +#define LL_MULTICAST_ADDR_0 0x01 +#define LL_MULTICAST_ADDR_1 0x00 +#define LL_MULTICAST_ADDR_2 0x5e + +#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 = 1000, this is + * (60 * 20) seconds = 20 minutes. + */ +#define ARP_MAXAGE 1200 +/** Re-request a used ARP entry 1 minute before it would expire to prevent + * breaking a steadily used connection because the ARP entry timed out. */ +#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 60) + +/** the time an ARP entry stays pending after first request, + * for ARP_TMR_INTERVAL = 1000, this is + * 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 5 + +#define HWTYPE_ETHERNET 1 + +enum etharp_state { + ETHARP_STATE_EMPTY = 0, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + ETHARP_STATE_STABLE_REREQUESTING +#if ETHARP_SUPPORT_STATIC_ENTRIES + ,ETHARP_STATE_STATIC +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ +}; + +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 netif *netif; + struct eth_addr ethaddr; + u16_t ctime; + u8_t state; +}; + +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 +#if ETHARP_SUPPORT_STATIC_ENTRIES +#define ETHARP_FLAG_STATIC_ENTRY 4 +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + +#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 */ + + +/* 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 +etharp_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_free_entry: 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; +#ifdef LWIP_DEBUG + /* for debugging, clean out the complete entry */ + arp_table[i].ctime = 0; + arp_table[i].netif = NULL; + 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 + && (state != ETHARP_STATE_STATIC) +#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 */ + etharp_free_entry(i); + } + else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) { + /* Reset state to stable, so that the next transmitted packet will + re-send an ARP request. */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + /* still pending entry? (not expired) */ + else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* resend an ARP query here? */ + if (etharp_request(arp_table[i].netif, &arp_table[i].ipaddr) != ERR_OK) { + } + } + } + } +} + +/** + * 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 +etharp_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; + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u16_t age_queue = 0, age_pending = 0, age_stable = 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, ("etharp_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, ("etharp_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 (state < ETHARP_STATE_STATIC) +#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, ("etharp_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, ("etharp_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, ("etharp_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, ("etharp_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 etharp_free_entry) */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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, ("etharp_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); + etharp_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; + 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 +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; +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + struct eth_vlan_hdr *vlanhdr; +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + + LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!", + (netif->hwaddr_len == ETHARP_HWADDR_LEN)); +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + ethhdr->type = PP_HTONS(ETHTYPE_VLAN); + vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR); + vlanhdr->prio_vid = 0; + vlanhdr->tpid = PP_HTONS(ETHTYPE_IP); + if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) { + /* packet shall not contain VLAN header, so hide it and set correct ethertype */ + pbuf_header(p, -SIZEOF_VLAN_HDR); + ethhdr = (struct eth_hdr *)p->payload; +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + ethhdr->type = PP_HTONS(ETHTYPE_IP); +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + } +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + ETHADDR32_COPY(ðhdr->dest, dst); + ETHADDR16_COPY(ðhdr->src, src); + 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 Successfully 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 +etharp_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, ("etharp_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, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = etharp_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].state = ETHARP_STATE_STATIC; + } else +#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ + { + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + } + + /* record network interface */ + arp_table[i].netif = netif; + /* insert in SNMP ARP index tree */ + snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); + + LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_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 etharp_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 = etharp_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_STATIC) { + /* entry wasn't a static entry, cannot remove it */ + return ERR_ARG; + } + /* entry found, free it */ + etharp_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 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)) { + etharp_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 = etharp_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 +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 == PP_HTONS(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. */ + etharp_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 +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 */ + + 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 == PP_HTONS(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))) { + 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")\n", + hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen)); + 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 continuously 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 */ + etharp_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 possibly 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 */ + + /* return ARP reply */ + netif->linkoutput(netif, p); + /* 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); +} + +/** Just a small helper function that sends a pbuf to an ethernet address + * in the arp_table specified by the index 'arp_idx'. + */ +static err_t +etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) +{ + LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE", + arp_table[arp_idx].state >= ETHARP_STATE_STABLE); + /* if arp table entry is about to expire: re-request it, + but only if its state is ETHARP_STATE_STABLE to prevent flooding the + network with ARP requests if this address is used frequently. */ + if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) && + (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) { + if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { + arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING; + } + } + + return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), + &arp_table[arp_idx].ethaddr); +} + +/** + * 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; + struct eth_addr mcastaddr; + ip_addr_t *dst_addr = ipaddr; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_ASSERT("q != NULL", q != NULL); + LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL); + + /* make room for Ethernet header - should not fail */ +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + if (pbuf_header(q, sizeof(struct eth_hdr) + SIZEOF_VLAN_HDR) != 0) { +#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + /* 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; + } + + /* 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] = LL_MULTICAST_ADDR_0; + mcastaddr.addr[1] = LL_MULTICAST_ADDR_1; + mcastaddr.addr[2] = LL_MULTICAST_ADDR_2; + 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 { + s8_t i; + /* outside local network? if so, this can neither be a global broadcast nor + a subnet broadcast. */ + 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 + +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + SIZEOF_VLAN_HDR + +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + 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 */ + { +#ifdef LWIP_HOOK_ETHARP_GET_GW + /* For advanced routing, a single default gateway might not be enough, so get + the IP address of the gateway to handle the current destination address. */ + dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr); + if(dst_addr == NULL) +#endif /* LWIP_HOOK_ETHARP_GET_GW */ + { + /* interface has default gateway? */ + if (!ip_addr_isany(&netif->gw)) { + /* send to hardware address of default gateway IP address */ + dst_addr = &(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(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { + /* the per-pcb-cached entry is stable and the right one! */ + ETHARP_STATS_INC(etharp.cachehit); + return etharp_output_to_arp_index(netif, q, etharp_cached_entry); + } +#if LWIP_NETIF_HWADDRHINT + } + } +#endif /* LWIP_NETIF_HWADDRHINT */ + + /* find stable entry: do this here since this is a critical path for + throughput and etharp_find_entry() is kind of slow */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if ((arp_table[i].state >= ETHARP_STATE_STABLE) && + (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { + /* found an existing, stable entry */ + ETHARP_SET_HINT(netif, i); + return etharp_output_to_arp_index(netif, q, i); + } + } + /* no stable entry found, use the (slower) query function: + queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, dst_addr, 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; + int is_new_entry = 0; + 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 = etharp_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) { + is_new_entry = 1; + arp_table[i].state = ETHARP_STATE_PENDING; + /* record network interface for re-sending arp request in etharp_tmr */ + arp_table[i].netif = netif; + } + + /* { 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 new entry? or an implicit query request? */ + if (is_new_entry || (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) { + unsigned int qlen = 0; + 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; + qlen++; + while (r->next != NULL) { + r = r->next; + qlen++; + } + r->next = new_entry; + } else { + /* queue did not exist, first item in queue */ + arp_table[i].q = new_entry; + } +#if ARP_QUEUE_LEN + if (qlen >= ARP_QUEUE_LEN) { + struct etharp_q_entry *old; + old = arp_table[i].q; + arp_table[i].q = arp_table[i].q->next; + pbuf_free(old->p); + memp_free(MEMP_ARP_QUEUE, old); + } +#endif + 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 +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; +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + struct eth_vlan_hdr *vlanhdr; +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + struct etharp_hdr *hdr; +#if LWIP_AUTOIP + const u8_t * ethdst_hwaddr; +#endif /* LWIP_AUTOIP */ + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET_TX, 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_TX)); + + ethhdr = (struct eth_hdr *)p->payload; +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + vlanhdr = (struct eth_vlan_hdr*)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR); + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR); +#else /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + 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); + /* 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); + +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + ethhdr->type = PP_HTONS(ETHTYPE_VLAN); + vlanhdr->tpid = PP_HTONS(ETHTYPE_ARP); + vlanhdr->prio_vid = 0; + if (!LWIP_HOOK_VLAN_SET(netif, ethhdr, vlanhdr)) { + /* packet shall not contain VLAN header, so hide it and set correct ethertype */ + pbuf_header(p, -SIZEOF_VLAN_HDR); + ethhdr = (struct eth_hdr *)p->payload; +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + ethhdr->type = PP_HTONS(ETHTYPE_ARP); +#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) + } +#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */ + + /* 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); + + /* 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 received 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; +#if LWIP_ARP || ETHARP_SUPPORT_VLAN + s16_t ip_hdr_offset = SIZEOF_ETH_HDR; +#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */ + + if (p->len <= SIZEOF_ETH_HDR) { + /* a packet with only an ethernet header (or less) is not valid for us */ + 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 */ + ETHARP_STATS_INC(etharp.proterr); + ETHARP_STATS_INC(etharp.drop); + goto free_and_return; + } +#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */ +#ifdef LWIP_HOOK_VLAN_CHECK + if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK_FN) + if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) { +#elif defined(ETHARP_VLAN_CHECK) + if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) { +#endif + /* silently ignore this packet: not for our VLAN */ + pbuf_free(p); + return ERR_OK; + } +#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */ + 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*/ + + if (ethhdr->dest.addr[0] & 1) { + /* this might be a multicast or broadcast packet */ + if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) { + if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) && + (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) { + /* mark the pbuf as link-layer multicast */ + p->flags |= PBUF_FLAG_LLMCAST; + } + } else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) { + /* mark the pbuf as link-layer broadcast */ + p->flags |= PBUF_FLAG_LLBCAST; + } + } + + 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, (s16_t)-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 */ + +#if LWIP_IPV6 + case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */ + /* skip Ethernet header */ + if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) { + LWIP_ASSERT("Can't move over header in packet", 0); + goto free_and_return; + } else { + /* pass to IPv6 layer */ + ip6_input(p, netif); + } + break; +#endif /* LWIP_IPV6 */ + + 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/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ethernetif.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ethernetif.c new file mode 100644 index 0000000..b9276b6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ethernetif.c @@ -0,0 +1,322 @@ +/** + * @file + * Ethernet Interface Skeleton + * + */ + +/* + * 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 file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" + +#if 0 /* don't build, this is only a skeleton, see previous comment */ + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/ethip6.h" +#include "netif/etharp.h" +#include "netif/ppp/pppoe.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +/** + * Helper struct to hold private data used to operate your ethernet interface. + * Keeping the ethernet address of the MAC in this struct is not necessary + * as it is already kept in the struct netif. + * But this is only an example, anyway... + */ +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); + +/** + * In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = ETHARP_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; + + /* Do whatever else is needed to initialize interface. */ +} + +/** + * This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become available since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ + +static err_t +low_level_output(struct netif *netif, struct pbuf *p) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *q; + + initiate transfer(); + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.xmit); + + return ERR_OK; +} + +/** + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * +low_level_input(struct netif *netif) +{ + struct ethernetif *ethernetif = netif->state; + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + +#if ETH_PAD_SIZE + len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ +#endif + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + +#if ETH_PAD_SIZE + pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ +#endif + + /* We iterate over the pbuf chain until we have read the entire + * packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + * available data in the pbuf is given by the q->len + * variable. + * This does not necessarily have to be a memcpy, you can also preallocate + * pbufs for a DMA-enabled MAC and after receiving truncate it to the + * actually received size. In this case, ensure the tot_len member of the + * pbuf is the sum of the chained pbuf len members. + */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); + +#if ETH_PAD_SIZE + pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ +#endif + + LINK_STATS_INC(link.recv); + } else { + drop packet(); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + } + + return p; +} + +/** + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p; + + ethernetif = netif->state; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + /* points to packet payload, which starts with an Ethernet header */ + ethhdr = p->payload; + + switch (htons(ethhdr->type)) { + /* IP or ARP packet? */ + case ETHTYPE_IP: + case ETHTYPE_IPV6: + case ETHTYPE_ARP: +#if PPPOE_SUPPORT + /* PPPoE packet? */ + case ETHTYPE_PPPOEDISC: + case ETHTYPE_PPPOE: +#endif /* PPPOE_SUPPORT */ + /* full packet send to tcpip_thread to process */ + if (netif->input(p, netif)!=ERR_OK) + { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } + break; + + default: + pbuf_free(p); + p = NULL; + break; + } +} + +/** + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + if (ethernetif == NULL) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + /* + * Initialize the snmp variables and counters inside the struct netif. + * The last argument should be replaced with your link speed, in units + * of bits per second. + */ + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS); + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; +#if LWIP_IPV6 + netif->output_ip6 = ethip6_output; +#endif /* LWIP_IPV6 */ + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +#endif /* 0 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/PPPD_FOLLOWUP b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/PPPD_FOLLOWUP new file mode 100644 index 0000000..4a82915 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/PPPD_FOLLOWUP @@ -0,0 +1,382 @@ +The lwIP PPP support is based from pppd 2.4.5 (http://ppp.samba.org) with +huge changes to match code size and memory requirements for embedded devices. + +Anyway, pppd has a mature codebase for years and the average commit count +is getting low on their Git repositories, meaning that we can follow what +is happening on their side and merge what is relevant for lwIP. + +So, here is the pppd follow up, so that we don't get away too far from pppd. + + +== Patch fetched from from pppd Debian packages == + +This has nothing to do with pppd, but we merged some good patch from +Debian and this is a good place to be. + +- LCP adaptive echo, so that we don't send LCP echo request if we + are receiving data from peer, can be enabled by setting PPP_LCP_ADAPTIVE + to true. + +- IPCP no/replace default route option, were added in the early stage of + the ppp port, but it wasn't really helpful and was disabled when adding + the new API ppp_set_default() call, which gives the lwIP user control over + which one is the default interface, it was actually a requirement if you + are doing PPP over PPP (i.e. PPPoL2TP, VPN link, over PPPoE, ADSL link). + +- using rp-pppoe pppd exits with EXIT_OK after receiving a timeout waiting + for PADO due to no modem attached, bug reported to pppd bug tracker, fixed + in Debian but not in the latest (at the time when the port were started) + pppd release. + + +== Commits on pppd == + +2010-03-06 - Document +ipv6 and ipv6cp-accept-local + e7537958aee79b3f653c601e903cb31d78fb7dcc + +Don't care. + + +2010-03-06 - Install pppol2tp plugins with sane permissions + 406215672cfadc03017341fe03802d1c7294b903 + +Don't care. + + +2010-03-07 - pppd: Terminate correctly if lcp_lowerup delayed calling + fsm_lowerup + 3eb9e810cfa515543655659b72dde30c54fea0a5 + +Merged 2012-05-17. + + +2010-03-07 - rp_pppoe: Copy acName and pppd_pppoe_service after option parsing + cab58617fd9d328029fffabc788020264b4fa91f + +Don't care, is a patch for pppd/plugins/rp-pppoe/plugin.c which is not part +of the port. + + +2010-08-23 - set and reset options to control environment variables + for scripts. + 2b6310fd24dba8e0fca8999916a162f0a1842a84 + +We can't fork processes in embedded, therefore all the pppd process run +feature is disabled in the port, so we don't care about the new +"environment variables" pppd feature. + + +2010-08-23 - Nit: use _exit when exec fails and restrict values to 0-255 + per POSIX. + 2b4ea140432eeba5a007c0d4e6236bd0e0c12ba4 + +Again, we are not running as a heavy process, so all exit() or _exit() calls +were removed. + + +2010-08-23 - Fix quote handling in configuration files to be more like shell + quoting. + 3089132cdf5b58dbdfc2daf08ec5c08eb47f8aca + +We are not parsing config file, all the filesystem I/O stuff were disabled +in our port. + + +2010-08-24 - rp-pppoe: allow MTU to be increased up to 1500 + fd1dcdf758418f040da3ed801ab001b5e46854e7 + +Only concern changes on RP-PPPoE plugin, which we don't use. + + +2010-09-11 - chat: Allow TIMEOUT value to come from environment variable + ae80bf833e48a6202f44a935a68083ae52ad3824 + +See 2b6310fd24dba8e0fca8999916a162f0a1842a84. + + +2011-03-05 - pppdump: Fix printfs with insufficient arguments + 7b8db569642c83ba3283745034f2e2c95e459423 + +pppdump is a ppp tool outside pppd source tree. + + +2012-05-06 - pppd: Don't unconditionally disable VJ compression under Linux + d8a66adf98a0e525cf38031b42098d539da6eeb6 + +Patch for sys-linux.c, which we don't use. + + +2012-05-20 - Remove old version of Linux if_pppol2tp.h + c41092dd4c49267f232f6cba3d31c6c68bfdf68d + +Not in the port. + + +2012-05-20 - pppd: Make MSCHAP-v2 cope better with packet loss + 08ef47ca532294eb428238c831616748940e24a2 + +This is an interesting patch. However it consumes much more memory for +MSCHAP and I am not sure if the benefit worth it. The PPP client can +always start the authentication again if it failed for whatever reason. + + +2012-05-20 - scripts: Make poff ignore extra arguments to pppd + 18f515f32c9f5723a9c2c912601e04335106534b + +Again, we are not running scripts. + + +2012-05-20 - rp-pppoe plugin: Print leading zeros in MAC address + f5dda0cfc220c4b52e26144096d729e27b30f0f7 + +Again, we are not using the RP-PPPoE plugin. + + +2012-05-20 - pppd: Notify IPv6 up/down as we do for IPv4 + 845cda8fa18939cf56e60b073f63a7efa65336fc + +This is just a patch that adds plugins hooks for IPv6, the plugin interface +was disabled because we don't have .so plugins in embedded. + + +2012-05-20 - pppd: Enable IPV6 by default and fix some warnings + 0b6118239615e98959f7e0b4e746bdd197533248 + +Change on Makefile for IPv6, warnings were already cleared during port. + + +2012-05-20 - contrib: Fix pppgetpass.gtk compilation + 80a8e2ce257ca12cce723519a0f20ea1d663b14a + +Change on Makefile, don't care. + + +2012-05-20 - pppd: Don't crash if crypt() returns NULL + 04c4348108d847e034dd91066cc6843f60d71731 + +We are using the PolarSSL DES implementation that does not return NULL. + + +2012-05-20 - pppd: Eliminate some warnings + c44ae5e6a7338c96eb463881fe709b2dfaffe568 + +Again, we are handling compilation warnings on our own. + + +2012-05-20 - rp-pppoe plugin: Import some fixes from rp-pppoe-3.10 + 1817d83e51a411044e730ba89ebdb0480e1c8cd4 + +Once more, we are not using the RP-PPPoE plugin. + + +2013-01-23 - pppd: Clarify circumstances where DNS1/DNS2 environment variables are set + cf2f5c9538b9400ade23446a194729b0a4113b3a + +Documentation only. + + +2013-02-03 - ppp: ignore unrecognised radiusclient configuration directives + 7f736dde0da3c19855997d9e67370e351e15e923 + +Radius plugin, not in the port. + + +2013-02-03 - pppd: Take out unused %r conversion completely + 356d8d558d844412119aa18c8e5a113bc6459c7b + +Merged 2014-04-15. + + +2013-02-03 - pppd: Arrange to use logwtmp from libutil on Linux + 9617a7eb137f4fee62799a677a9ecf8d834db3f5 + +Patch for sys-linux.c, which we don't use. + + +2013-02-03 - pppdump: Eliminate some compiler warnings + 3e3acf1ba2b3046c072a42c19164788a9e419bd1 + +pppdump is a ppp tool outside pppd source tree. + + +2013-02-03 - chat: Correct spelling errors in the man page + 8dea1b969d266ccbf6f3a8c5474eb6dcd8838e3b + +Documentation only. + + +2013-02-03 - pppd: Fix spelling errors in man page + 9e05a25d76b3f83096c661678010320df673df6b + +Documentation only. + + +2013-02-03 - plugins/passprompt: Fix potential out-of-bounds array reference + 8edb889b753056a691a3e4b217a110a35f9fdedb + +Plugin patch, we do not have plugins. + + +2013-02-03 - chat: Fix *roff errors in the man page + a7c3489eeaf44e83ce592143c7c8a5b5c29f4c48 + +Documentation only. + + +2013-03-02 - pppd: Fix man page description of case when remote IP address isn't known + 224841f4799f4f1e2e71bc490c54448d66740f4f + +Documentation only. + + +2013-03-02 - pppd: Add master_detach option + 398ed2585640d198c53e736ee5bbd67f7ce8168e + +Option for multilink support, we do not support multilink and this option +is about detaching from the terminal, which is out of the embedded scope. + + +2013-03-11 - pppd: Default exit status to EXIT_CONNECT_FAILED during connection phase + 225361d64ae737afdc8cb57579a2f33525461bc9 + +Commented out in our port, and already fixed by a previously applied Debian patch. + + +2013-03-11 - pppstats: Fix undefined macro in man page + d16a3985eade5280b8e171f5dd0670a91cba0d39 + +Documentation only. + + +2013-05-11 - plugins/radius: Handle bindaddr keyword in radiusclient.conf + d883b2dbafeed3ebd9d7a56ab1469373bd001a3b + +Radius plugin, not in the port. + + +2013-06-09 - pppoatm: Remove explicit loading of pppoatm kernel module + 52cd43a84bea524033b918b603698104f221bbb7 + +PPPoATM plugin, not in the port. + + +2013-06-09 - pppd: Fix segfault in update_db_entry() + 37476164f15a45015310b9d4b197c2d7db1f7f8f + +We do not use the samba db. + + +2013-06-09 - chat: Fix some text that was intended to be literal + cd9683676618adcee8add2c3cfa3382341b5a1f6 + +Documentation only. + + +2013-06-09 - README.pppoe: Minor semantic fix + b5b8898af6fd3d44e873cfc66810ace5f1f47e17 + +Documentation only. + + +2013-06-10 - radius: Handle additional attributes + 2f581cd986a56f2ec4a95abad4f8297a1b10d7e2 + +Radius plugin, not in the port. + + +2013-06-10 - chat, pppd: Use \e instead of \\ in man pages + 8d6942415d22f6ca4377340ca26e345c3f5fa5db + +Documentation only. + + +2014-01-02 - pppd: Don't crash if NULL pointer passed to vslprintf for %q or %v + 906814431bddeb2061825fa1ebad1a967b6d87a9 + +Merged 2014-04-15. + + +2014-01-02 - pppd: Accept IPCP ConfAck packets containing MS-WINS options + a243f217f1c6ac1aa7793806bc88590d077f490a + +Merged 2014-04-15. + + +2014-01-02 - config: Update Solaris compiler options and enable CHAPMS and IPV6 + 99c46caaed01b7edba87962aa52b77fad61bfd7b + +Solaris port, don't care. + + +2014-01-02 - Update README and patchlevel for 2.4.6 release + 4043750fca36e7e0eb90d702e048ad1da4929418 + +Just release stuff. + + +2014-02-18 - pppd: Add option "stop-bits" to set number of serial port stop bits. + ad993a20ee485f0d0e2ac4105221641b200da6e2 + +Low level serial port, not in the port. + + +2014-03-09 - pppd: Separate IPv6 handling for sifup/sifdown + b04d2dc6df5c6b5650fea44250d58757ee3dac4a + +Reimplemented. + + +2014-03-09 - pppol2tp: Connect up/down events to notifiers and add IPv6 ones + fafbe50251efc7d6b4a8be652d085316e112b34f + +Not in the port. + + +2014-03-09 - pppd: Add declarations to eliminate compile warnings + 50967962addebe15c7a7e63116ff46a0441dc464 + +We are handling compilation warnings on our own + + +2014-03-09 - pppd: Eliminate some unnecessary ifdefs + de8da14d845ee6db9236ccfddabf1d8ebf045ddb + +We mostly did that previously. Anyway, merged 2014-12-24. + + +2014-08-01 - radius: Fix realms-config-file option + 880a81be7c8e0fe8567227bc17a1bff3ea035943 + +Radius plugin, not in the port. + + +2014-08-01 - pppd: Eliminate potential integer overflow in option parsing + 7658e8257183f062dc01f87969c140707c7e52cb + +pppd config file parser, not in the port. + + +2014-08-01 - pppd: Eliminate memory leak with multiple instances of a string option + b94b7fbbaa0589aa6ec5fdc733aeb9ff294d2656 + +pppd config file parser, not in the port. + + +2014-08-01 - pppd: Fix a stack variable overflow in MSCHAP-v2 + 36733a891fb56594fcee580f667b33a64b990981 + +This fixes a bug introduced in 08ef47ca ("pppd: Make MSCHAP-v2 cope better with packet loss"). + +We didn't merge 08ef47ca ;-) + + +2014-08-01 - winbind plugin: Add -DMPPE=1 to eliminate compiler warnings + 2b05e22c62095e97dd0a97e4b5588402c2185071 + +Linux plugin, not in the port. + + +2014-08-09 - Update README and patchlevel for 2.4.7 release + 6e8eaa7a78b31cdab2edf140a9c8afdb02ffaca5 + +Just release stuff. diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/auth.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/auth.c new file mode 100644 index 0000000..b98691e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/auth.c @@ -0,0 +1,2563 @@ +/* + * auth.c - PPP authentication and phase control. + * + * Copyright (c) 1993-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Derived from main.c, which is: + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_PATH_LASTLOG) && defined(__linux__) +#include +#endif + +#include +#include +#include + +#ifdef HAS_SHADOW +#include +#ifndef PW_PPP +#define PW_PPP PW_LOGIN +#endif +#endif + +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#if CCP_SUPPORT +#include "netif/ppp/ccp.h" +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT +#include "netif/ppp/ecp.h" +#endif /* ECP_SUPPORT */ +#include "netif/ppp/ipcp.h" +#if PAP_SUPPORT +#include "netif/ppp/upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "netif/ppp/eap.h" +#endif /* EAP_SUPPORT */ +#if CBCP_SUPPORT +#include "netif/ppp/cbcp.h" +#endif + +#if 0 /* UNUSED */ +#include "session.h" +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* Bits in scan_authfile return value */ +#define NONWILD_SERVER 1 +#define NONWILD_CLIENT 2 + +#define ISWILD(word) (word[0] == '*' && word[1] == 0) +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* List of addresses which the peer may use. */ +static struct permitted_ip *addresses[NUM_PPP]; + +/* Wordlist giving addresses which the peer may use + without authenticating itself. */ +static struct wordlist *noauth_addrs; + +/* Remote telephone number, if available */ +char remote_number[MAXNAMELEN]; + +/* Wordlist giving remote telephone numbers which may connect. */ +static struct wordlist *permitted_numbers; + +/* Extra options to apply, from the secrets file entry for the peer. */ +static struct wordlist *extra_options; +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* Set if we require authentication only because we have a default route. */ +static bool default_auth; + +/* Hook to enable a plugin to control the idle time limit */ +int (*idle_time_hook) (struct ppp_idle *) = NULL; + +/* Hook for a plugin to say whether we can possibly authenticate any peer */ +int (*pap_check_hook) (void) = NULL; + +/* Hook for a plugin to check the PAP user and password */ +int (*pap_auth_hook) (char *user, char *passwd, char **msgp, + struct wordlist **paddrs, + struct wordlist **popts) = NULL; + +/* Hook for a plugin to know about the PAP user logout */ +void (*pap_logout_hook) (void) = NULL; + +/* Hook for a plugin to get the PAP password for authenticating us */ +int (*pap_passwd_hook) (char *user, char *passwd) = NULL; + +/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */ +int (*chap_check_hook) (void) = NULL; + +/* Hook for a plugin to get the CHAP password for authenticating us */ +int (*chap_passwd_hook) (char *user, char *passwd) = NULL; + +/* Hook for a plugin to say whether it is OK if the peer + refuses to authenticate. */ +int (*null_auth_hook) (struct wordlist **paddrs, + struct wordlist **popts) = NULL; + +int (*allowed_address_hook) (u32_t addr) = NULL; +#endif /* UNUSED */ + +#ifdef HAVE_MULTILINK +/* Hook for plugin to hear when an interface joins a multilink bundle */ +void (*multilink_join_hook) (void) = NULL; +#endif + +#if PPP_NOTIFY +/* A notifier for when the peer has authenticated itself, + and we are proceeding to the network phase. */ +struct notifier *auth_up_notifier = NULL; + +/* A notifier for when the link goes down. */ +struct notifier *link_down_notifier = NULL; +#endif /* PPP_NOTIFY */ + +/* + * Option variables. + */ +#if 0 /* MOVED TO ppp_settings */ +bool uselogin = 0; /* Use /etc/passwd for checking PAP */ +bool session_mgmt = 0; /* Do session management (login records) */ +bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */ +bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */ +bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */ +bool refuse_eap = 0; /* Don't wanna auth. ourselves with EAP */ +#if MSCHAP_SUPPORT +bool refuse_mschap = 0; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 0; /* Don't wanna auth. ourselves with MS-CHAPv2 */ +#else /* MSCHAP_SUPPORT */ +bool refuse_mschap = 1; /* Don't wanna auth. ourselves with MS-CHAP */ +bool refuse_mschap_v2 = 1; /* Don't wanna auth. ourselves with MS-CHAPv2 */ +#endif /* MSCHAP_SUPPORT */ +bool usehostname = 0; /* Use hostname for our_name */ +bool auth_required = 0; /* Always require authentication from peer */ +bool allow_any_ip = 0; /* Allow peer to use any IP address */ +bool explicit_remote = 0; /* User specified explicit remote name */ +bool explicit_user = 0; /* Set if "user" option supplied */ +bool explicit_passwd = 0; /* Set if "password" option supplied */ +char remote_name[MAXNAMELEN]; /* Peer's name for authentication */ +static char *uafname; /* name of most recent +ua file */ + +extern char *crypt (const char *, const char *); +#endif /* UNUSED */ +/* Prototypes for procedures local to this file. */ + +static void network_phase(ppp_pcb *pcb); +#if PPP_IDLETIMELIMIT +static void check_idle(void *arg); +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT +static void connect_time_expired(void *arg); +#endif /* PPP_MAXCONNECT */ +#if 0 /* UNUSED */ +static int null_login (int); +/* static int get_pap_passwd (char *); */ +static int have_pap_secret (int *); +static int have_chap_secret (char *, char *, int, int *); +static int have_srp_secret (char *client, char *server, int need_ip, + int *lacks_ipp); +static int ip_addr_check (u32_t, struct permitted_ip *); +static int scan_authfile (FILE *, char *, char *, char *, + struct wordlist **, struct wordlist **, + char *, int); +static void free_wordlist (struct wordlist *); +static void set_allowed_addrs (int, struct wordlist *, struct wordlist *); +static int some_ip_ok (struct wordlist *); +static int setupapfile (char **); +static int privgroup (char **); +static int set_noauth_addr (char **); +static int set_permitted_number (char **); +static void check_access (FILE *, char *); +static int wordlist_count (struct wordlist *); +#endif /* UNUSED */ + +#ifdef MAXOCTETS +static void check_maxoctets (void *); +#endif + +#if PPP_OPTIONS +/* + * Authentication-related options. + */ +option_t auth_options[] = { + { "auth", o_bool, &auth_required, + "Require authentication from peer", OPT_PRIO | 1 }, + { "noauth", o_bool, &auth_required, + "Don't require peer to authenticate", OPT_PRIOSUB | OPT_PRIV, + &allow_any_ip }, + { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", + OPT_PRIOSUB | 1, &auth_required }, + { "+pap", o_bool, &lcp_wantoptions[0].neg_upap, + "Require PAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | 1, &auth_required }, + { "require-chap", o_bool, &auth_required, + "Require CHAP authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, + &lcp_wantoptions[0].chap_mdtype }, + { "+chap", o_bool, &auth_required, + "Require CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MD5, + &lcp_wantoptions[0].chap_mdtype }, +#if MSCHAP_SUPPORT + { "require-mschap", o_bool, &auth_required, + "Require MS-CHAP authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, + &lcp_wantoptions[0].chap_mdtype }, + { "+mschap", o_bool, &auth_required, + "Require MS-CHAP authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT, + &lcp_wantoptions[0].chap_mdtype }, + { "require-mschap-v2", o_bool, &auth_required, + "Require MS-CHAPv2 authentication from peer", + OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, + &lcp_wantoptions[0].chap_mdtype }, + { "+mschap-v2", o_bool, &auth_required, + "Require MS-CHAPv2 authentication from peer", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2OR | MDTYPE_MICROSOFT_V2, + &lcp_wantoptions[0].chap_mdtype }, +#endif /* MSCHAP_SUPPORT */ +#if 0 + { "refuse-pap", o_bool, &refuse_pap, + "Don't agree to auth to peer with PAP", 1 }, + { "-pap", o_bool, &refuse_pap, + "Don't allow PAP authentication with peer", OPT_ALIAS | 1 }, + { "refuse-chap", o_bool, &refuse_chap, + "Don't agree to auth to peer with CHAP", + OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, + { "-chap", o_bool, &refuse_chap, + "Don't allow CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MD5, + &lcp_allowoptions[0].chap_mdtype }, +#endif +#if MSCHAP_SUPPORT +#if 0 + { "refuse-mschap", o_bool, &refuse_mschap, + "Don't agree to auth to peer with MS-CHAP", + OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, + { "-mschap", o_bool, &refuse_mschap, + "Don't allow MS-CHAP authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT, + &lcp_allowoptions[0].chap_mdtype }, + { "refuse-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't agree to auth to peer with MS-CHAPv2", + OPT_A2CLRB | MDTYPE_MICROSOFT_V2, + &lcp_allowoptions[0].chap_mdtype }, + { "-mschap-v2", o_bool, &refuse_mschap_v2, + "Don't allow MS-CHAPv2 authentication with peer", + OPT_ALIAS | OPT_A2CLRB | MDTYPE_MICROSOFT_V2, + &lcp_allowoptions[0].chap_mdtype }, +#endif +#endif /* MSCHAP_SUPPORT*/ +#if EAP_SUPPORT + { "require-eap", o_bool, &lcp_wantoptions[0].neg_eap, + "Require EAP authentication from peer", OPT_PRIOSUB | 1, + &auth_required }, +#if 0 + { "refuse-eap", o_bool, &refuse_eap, + "Don't agree to authenticate to peer with EAP", 1 }, +#endif +#endif /* EAP_SUPPORT */ + { "name", o_string, our_name, + "Set local name for authentication", + OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXNAMELEN }, + + { "+ua", o_special, (void *)setupapfile, + "Get PAP user and password from file", + OPT_PRIO | OPT_A2STRVAL, &uafname }, + +#if 0 + { "user", o_string, user, + "Set name for auth with peer", OPT_PRIO | OPT_STATIC, + &explicit_user, MAXNAMELEN }, + + { "password", o_string, passwd, + "Password for authenticating us to the peer", + OPT_PRIO | OPT_STATIC | OPT_HIDE, + &explicit_passwd, MAXSECRETLEN }, +#endif + + { "usehostname", o_bool, &usehostname, + "Must use hostname for authentication", 1 }, + + { "remotename", o_string, remote_name, + "Set remote name for authentication", OPT_PRIO | OPT_STATIC, + &explicit_remote, MAXNAMELEN }, + + { "login", o_bool, &uselogin, + "Use system password database for PAP", OPT_A2COPY | 1 , + &session_mgmt }, + { "enable-session", o_bool, &session_mgmt, + "Enable session accounting for remote peers", OPT_PRIV | 1 }, + + { "papcrypt", o_bool, &cryptpap, + "PAP passwords are encrypted", 1 }, + + { "privgroup", o_special, (void *)privgroup, + "Allow group members to use privileged options", OPT_PRIV | OPT_A2LIST }, + + { "allow-ip", o_special, (void *)set_noauth_addr, + "Set IP address(es) which can be used without authentication", + OPT_PRIV | OPT_A2LIST }, + + { "remotenumber", o_string, remote_number, + "Set remote telephone number for authentication", OPT_PRIO | OPT_STATIC, + NULL, MAXNAMELEN }, + + { "allow-number", o_special, (void *)set_permitted_number, + "Set telephone number(s) which are allowed to connect", + OPT_PRIV | OPT_A2LIST }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +#if 0 /* UNUSED */ +/* + * setupapfile - specifies UPAP info for authenticating with peer. + */ +static int +setupapfile(argv) + char **argv; +{ + FILE *ufile; + int l; + uid_t euid; + char u[MAXNAMELEN], p[MAXSECRETLEN]; + char *fname; + + lcp_allowoptions[0].neg_upap = 1; + + /* open user info file */ + fname = strdup(*argv); + if (fname == NULL) + novm("+ua file name"); + euid = geteuid(); + if (seteuid(getuid()) == -1) { + option_error("unable to reset uid before opening %s: %m", fname); + return 0; + } + ufile = fopen(fname, "r"); + if (seteuid(euid) == -1) + fatal("unable to regain privileges: %m"); + if (ufile == NULL) { + option_error("unable to open user login data file %s", fname); + return 0; + } + check_access(ufile, fname); + uafname = fname; + + /* get username */ + if (fgets(u, MAXNAMELEN - 1, ufile) == NULL + || fgets(p, MAXSECRETLEN - 1, ufile) == NULL) { + fclose(ufile); + option_error("unable to read user login data file %s", fname); + return 0; + } + fclose(ufile); + + /* get rid of newlines */ + l = strlen(u); + if (l > 0 && u[l-1] == '\n') + u[l-1] = 0; + l = strlen(p); + if (l > 0 && p[l-1] == '\n') + p[l-1] = 0; + + if (override_value("user", option_priority, fname)) { + strlcpy(ppp_settings.user, u, sizeof(ppp_settings.user)); + explicit_user = 1; + } + if (override_value("passwd", option_priority, fname)) { + strlcpy(ppp_settings.passwd, p, sizeof(ppp_settings.passwd)); + explicit_passwd = 1; + } + + return (1); +} + +/* + * privgroup - allow members of the group to have privileged access. + */ +static int +privgroup(argv) + char **argv; +{ + struct group *g; + int i; + + g = getgrnam(*argv); + if (g == 0) { + option_error("group %s is unknown", *argv); + return 0; + } + for (i = 0; i < ngroups; ++i) { + if (groups[i] == g->gr_gid) { + privileged = 1; + break; + } + } + return 1; +} + + +/* + * set_noauth_addr - set address(es) that can be used without authentication. + * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets. + */ +static int +set_noauth_addr(argv) + char **argv; +{ + char *addr = *argv; + int l = strlen(addr) + 1; + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); + if (wp == NULL) + novm("allow-ip argument"); + wp->word = (char *) (wp + 1); + wp->next = noauth_addrs; + MEMCPY(wp->word, addr, l); + noauth_addrs = wp; + return 1; +} + + +/* + * set_permitted_number - set remote telephone number(s) that may connect. + */ +static int +set_permitted_number(argv) + char **argv; +{ + char *number = *argv; + int l = strlen(number) + 1; + struct wordlist *wp; + + wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l); + if (wp == NULL) + novm("allow-number argument"); + wp->word = (char *) (wp + 1); + wp->next = permitted_numbers; + MEMCPY(wp->word, number, l); + permitted_numbers = wp; + return 1; +} +#endif + +/* + * An Open on LCP has requested a change from Dead to Establish phase. + */ +void link_required(ppp_pcb *pcb) { + LWIP_UNUSED_ARG(pcb); +} + +#if 0 +/* + * Bring the link up to the point of being able to do ppp. + */ +void start_link(unit) + int unit; +{ + ppp_pcb *pcb = &ppp_pcb_list[unit]; + char *msg; + + status = EXIT_NEGOTIATION_FAILED; + new_phase(pcb, PPP_PHASE_SERIALCONN); + + hungup = 0; + devfd = the_channel->connect(); + msg = "Connect script failed"; + if (devfd < 0) + goto fail; + + /* set up the serial device as a ppp interface */ + /* + * N.B. we used to do tdb_writelock/tdb_writeunlock around this + * (from establish_ppp to set_ifunit). However, we won't be + * doing the set_ifunit in multilink mode, which is the only time + * we need the atomicity that the tdb_writelock/tdb_writeunlock + * gives us. Thus we don't need the tdb_writelock/tdb_writeunlock. + */ + fd_ppp = the_channel->establish_ppp(devfd); + msg = "ppp establishment failed"; + if (fd_ppp < 0) { + status = EXIT_FATAL_ERROR; + goto disconnect; + } + + if (!demand && ifunit >= 0) + set_ifunit(1); + + /* + * Start opening the connection and wait for + * incoming events (reply, timeout, etc.). + */ + if (ifunit >= 0) + ppp_notice("Connect: %s <--> %s", ifname, ppp_devnam); + else + ppp_notice("Starting negotiation on %s", ppp_devnam); + add_fd(fd_ppp); + + new_phase(pcb, PPP_PHASE_ESTABLISH); + + lcp_lowerup(pcb); + return; + + disconnect: + new_phase(pcb, PPP_PHASE_DISCONNECT); + if (the_channel->disconnect) + the_channel->disconnect(); + + fail: + new_phase(pcb, PPP_PHASE_DEAD); + if (the_channel->cleanup) + (*the_channel->cleanup)(); +} +#endif + +/* + * LCP has terminated the link; go to the Dead phase and take the + * physical layer down. + */ +void link_terminated(ppp_pcb *pcb) { + if (pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_MASTER) + return; + new_phase(pcb, PPP_PHASE_DISCONNECT); + +#if 0 /* UNUSED */ + if (pap_logout_hook) { + pap_logout_hook(); + } + session_end(devnam); +#endif /* UNUSED */ + + if (!doing_multilink) { + ppp_notice("Connection terminated."); +#if PPP_STATS_SUPPORT + print_link_stats(); +#endif /* PPP_STATS_SUPPORT */ + } else + ppp_notice("Link terminated."); + + lcp_lowerdown(pcb); + + new_phase(pcb, PPP_PHASE_DEAD); + ppp_link_terminated(pcb); +#if 0 + /* + * Delete pid files before disestablishing ppp. Otherwise it + * can happen that another pppd gets the same unit and then + * we delete its pid file. + */ + if (!doing_multilink && !demand) + remove_pidfiles(); + + /* + * If we may want to bring the link up again, transfer + * the ppp unit back to the loopback. Set the + * real serial device back to its normal mode of operation. + */ + if (fd_ppp >= 0) { + remove_fd(fd_ppp); + clean_check(); + the_channel->disestablish_ppp(devfd); + if (doing_multilink) + mp_exit_bundle(); + fd_ppp = -1; + } + if (!hungup) + lcp_lowerdown(pcb); + if (!doing_multilink && !demand) + script_unsetenv("IFNAME"); + + /* + * Run disconnector script, if requested. + * XXX we may not be able to do this if the line has hung up! + */ + if (devfd >= 0 && the_channel->disconnect) { + the_channel->disconnect(); + devfd = -1; + } + if (the_channel->cleanup) + (*the_channel->cleanup)(); + + if (doing_multilink && multilink_master) { + if (!bundle_terminating) + new_phase(pcb, PPP_PHASE_MASTER); + else + mp_bundle_terminated(); + } else + new_phase(pcb, PPP_PHASE_DEAD); +#endif +} + +/* + * LCP has gone down; it will either die or try to re-establish. + */ +void link_down(ppp_pcb *pcb) { +#if PPP_NOTIFY + notify(link_down_notifier, 0); +#endif /* PPP_NOTIFY */ + + if (!doing_multilink) { + upper_layers_down(pcb); + if (pcb->phase != PPP_PHASE_DEAD && pcb->phase != PPP_PHASE_MASTER) + new_phase(pcb, PPP_PHASE_ESTABLISH); + } + /* XXX if doing_multilink, should do something to stop + network-layer traffic on the link */ + + ppp_link_down(pcb); +} + +void upper_layers_down(ppp_pcb *pcb) { + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (!protp->enabled_flag) + continue; + if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) + (*protp->lowerdown)(pcb); + if (protp->protocol < 0xC000 && protp->close != NULL) + (*protp->close)(pcb, "LCP down"); + } + pcb->num_np_open = 0; + pcb->num_np_up = 0; +} + +/* + * The link is established. + * Proceed to the Dead, Authenticate or Network phase as appropriate. + */ +void link_established(ppp_pcb *pcb) { + int auth; +#if PPP_SERVER + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *go = &pcb->lcp_gotoptions; +#endif /* PPP_SERVER */ + lcp_options *ho = &pcb->lcp_hisoptions; + int i; + const struct protent *protp; +#if PPP_SERVER + int errcode; +#endif /* PPP_SERVER */ + + /* + * Tell higher-level protocols that LCP is up. + */ + if (!doing_multilink) { + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol != PPP_LCP && protp->enabled_flag + && protp->lowerup != NULL) + (*protp->lowerup)(pcb); + } + +#if PPP_SERVER +#if PPP_ALLOWED_ADDRS + if (!auth_required && noauth_addrs != NULL) + set_allowed_addrs(unit, NULL, NULL); +#endif /* PPP_ALLOWED_ADDRS */ + + if (pcb->settings.auth_required && !(0 +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + )) { + +#if PPP_ALLOWED_ADDRS + /* + * We wanted the peer to authenticate itself, and it refused: + * if we have some address(es) it can use without auth, fine, + * otherwise treat it as though it authenticated with PAP using + * a username of "" and a password of "". If that's not OK, + * boot it out. + */ + if (noauth_addrs != NULL) { + set_allowed_addrs(unit, NULL, NULL); + } else +#endif /* PPP_ALLOWED_ADDRS */ + if (!wo->neg_upap || !pcb->settings.null_login) { + ppp_warn("peer refused to authenticate: terminating link"); +#if 0 /* UNUSED */ + status = EXIT_PEER_AUTH_FAILED; +#endif /* UNUSED */ + errcode = PPPERR_AUTHFAIL; + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "peer refused to authenticate"); + return; + } + } +#endif /* PPP_SERVER */ + + new_phase(pcb, PPP_PHASE_AUTHENTICATE); + auth = 0; +#if PPP_SERVER +#if EAP_SUPPORT + if (go->neg_eap) { + eap_authpeer(pcb, pcb->settings.our_name); + auth |= EAP_PEER; + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (go->neg_chap) { + chap_auth_peer(pcb, pcb->settings.our_name, CHAP_DIGEST(go->chap_mdtype)); + auth |= CHAP_PEER; + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if (go->neg_upap) { + upap_authpeer(pcb); + auth |= PAP_PEER; + } else +#endif /* PAP_SUPPORT */ + {} +#endif /* PPP_SERVER */ + +#if EAP_SUPPORT + if (ho->neg_eap) { + eap_authwithpeer(pcb, pcb->settings.user); + auth |= EAP_WITHPEER; + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (ho->neg_chap) { + chap_auth_with_peer(pcb, pcb->settings.user, CHAP_DIGEST(ho->chap_mdtype)); + auth |= CHAP_WITHPEER; + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if (ho->neg_upap) { + upap_authwithpeer(pcb, pcb->settings.user, pcb->settings.passwd); + auth |= PAP_WITHPEER; + } else +#endif /* PAP_SUPPORT */ + {} + + pcb->auth_pending = auth; + pcb->auth_done = 0; + + if (!auth) + + network_phase(pcb); +} + +/* + * Proceed to the network phase. + */ +static void network_phase(ppp_pcb *pcb) { +#if CBCP_SUPPORT + ppp_pcb *pcb = &ppp_pcb_list[unit]; +#endif +#if 0 /* UNUSED */ + lcp_options *go = &lcp_gotoptions[unit]; +#endif /* UNUSED */ + +#if 0 /* UNUSED */ + /* Log calling number. */ + if (*remote_number) + ppp_notice("peer from calling number %q authorized", remote_number); +#endif /* UNUSED */ + +#if PPP_NOTIFY + /* + * If the peer had to authenticate, notify it now. + */ + if (0 +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + ) { + notify(auth_up_notifier, 0); + } +#endif /* PPP_NOTIFY */ + +#if CBCP_SUPPORT + /* + * If we negotiated callback, do it now. + */ + if (go->neg_cbcp) { + new_phase(pcb, PPP_PHASE_CALLBACK); + (*cbcp_protent.open)(pcb); + return; + } +#endif + +#if PPP_OPTIONS + /* + * Process extra options from the secrets file + */ + if (extra_options) { + options_from_list(extra_options, 1); + free_wordlist(extra_options); + extra_options = 0; + } +#endif /* PPP_OPTIONS */ + start_networks(pcb); +} + +void start_networks(ppp_pcb *pcb) { +#if CCP_SUPPORT || ECP_SUPPORT + int i; + const struct protent *protp; +#endif /* CCP_SUPPORT || ECP_SUPPORT */ +#if ECP_SUPPORT + int ecp_required; +#endif /* ECP_SUPPORT */ +#ifdef MPPE + int mppe_required; +#endif /* MPPE */ + + new_phase(pcb, PPP_PHASE_NETWORK); + +#ifdef HAVE_MULTILINK + if (multilink) { + if (mp_join_bundle()) { + if (multilink_join_hook) + (*multilink_join_hook)(); + if (updetach && !nodetach) + detach(); + return; + } + } +#endif /* HAVE_MULTILINK */ + +#ifdef PPP_FILTER + if (!demand) + set_filters(&pass_filter, &active_filter); +#endif +#if CCP_SUPPORT || ECP_SUPPORT + /* Start CCP and ECP */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if ( + (0 +#if ECP_SUPPORT + || protp->protocol == PPP_ECP +#endif /* ECP_SUPPORT */ +#if CCP_SUPPORT + || protp->protocol == PPP_CCP +#endif /* CCP_SUPPORT */ + ) + && protp->enabled_flag && protp->open != NULL) + (*protp->open)(pcb); +#endif /* CCP_SUPPORT || ECP_SUPPORT */ + + /* + * Bring up other network protocols iff encryption is not required. + */ +#if ECP_SUPPORT + ecp_required = ecp_gotoptions[unit].required; +#endif /* ECP_SUPPORT */ +#ifdef MPPE + mppe_required = ccp_gotoptions[unit].mppe; +#endif /* MPPE */ + + if (1 +#if ECP_SUPPORT + && !ecp_required +#endif /* ECP_SUPPORT */ +#ifdef MPPE + && !mppe_required +#endif /* MPPE */ + ) + continue_networks(pcb); +} + +void continue_networks(ppp_pcb *pcb) { + int i; + const struct protent *protp; + + /* + * Start the "real" network protocols. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol < 0xC000 +#if CCP_SUPPORT + && protp->protocol != PPP_CCP +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT + && protp->protocol != PPP_ECP +#endif /* ECP_SUPPORT */ + && protp->enabled_flag && protp->open != NULL) { + (*protp->open)(pcb); + ++pcb->num_np_open; + } + + if (pcb->num_np_open == 0) + /* nothing to do */ + lcp_close(pcb, "No network protocols running"); +} + +#if PPP_SERVER +/* + * The peer has failed to authenticate himself using `protocol'. + */ +void auth_peer_fail(ppp_pcb *pcb, int protocol) { + int errcode = PPPERR_AUTHFAIL; + LWIP_UNUSED_ARG(protocol); + /* + * Authentication failure: take the link down + */ +#if 0 /* UNUSED */ + status = EXIT_PEER_AUTH_FAILED; +#endif /* UNUSED */ + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "Authentication failed"); +} + +/* + * The peer has been successfully authenticated using `protocol'. + */ +void auth_peer_success(ppp_pcb *pcb, int protocol, int prot_flavor, const char *name, int namelen) { + int bit; + + switch (protocol) { +#if CHAP_SUPPORT + case PPP_CHAP: + bit = CHAP_PEER; + switch (prot_flavor) { + case CHAP_MD5: + bit |= CHAP_MD5_PEER; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + bit |= CHAP_MS_PEER; + break; + case CHAP_MICROSOFT_V2: + bit |= CHAP_MS2_PEER; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + break; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + case PPP_PAP: + bit = PAP_PEER; + break; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + bit = EAP_PEER; + break; +#endif /* EAP_SUPPORT */ + default: + ppp_warn("auth_peer_success: unknown protocol %x", protocol); + return; + } + + /* + * Save the authenticated name of the peer for later. + */ + /* FIXME: do we need that ? */ + if (namelen > (int)sizeof(pcb->peer_authname) - 1) + namelen = (int)sizeof(pcb->peer_authname) - 1; + MEMCPY(pcb->peer_authname, name, namelen); + pcb->peer_authname[namelen] = 0; +#if 0 /* UNUSED */ + script_setenv("PEERNAME", , 0); +#endif /* UNUSED */ + + /* Save the authentication method for later. */ + pcb->auth_done |= bit; + + /* + * If there is no more authentication still to be done, + * proceed to the network (or callback) phase. + */ + if ((pcb->auth_pending &= ~bit) == 0) + network_phase(pcb); +} +#endif /* PPP_SERVER */ + +/* + * We have failed to authenticate ourselves to the peer using `protocol'. + */ +void auth_withpeer_fail(ppp_pcb *pcb, int protocol) { + int errcode = PPPERR_AUTHFAIL; + LWIP_UNUSED_ARG(protocol); + /* + * We've failed to authenticate ourselves to our peer. + * + * Some servers keep sending CHAP challenges, but there + * is no point in persisting without any way to get updated + * authentication secrets. + * + * He'll probably take the link down, and there's not much + * we can do except wait for that. + */ + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "Failed to authenticate ourselves to peer"); +} + +/* + * We have successfully authenticated ourselves with the peer using `protocol'. + */ +void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor) { + int bit; + const char *prot = ""; + + switch (protocol) { +#if CHAP_SUPPORT + case PPP_CHAP: + bit = CHAP_WITHPEER; + prot = "CHAP"; + switch (prot_flavor) { + case CHAP_MD5: + bit |= CHAP_MD5_WITHPEER; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + bit |= CHAP_MS_WITHPEER; + break; + case CHAP_MICROSOFT_V2: + bit |= CHAP_MS2_WITHPEER; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + break; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + case PPP_PAP: + bit = PAP_WITHPEER; + prot = "PAP"; + break; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + bit = EAP_WITHPEER; + prot = "EAP"; + break; +#endif /* EAP_SUPPORT */ + default: + ppp_warn("auth_withpeer_success: unknown protocol %x", protocol); + bit = 0; + /* no break */ + } + + ppp_notice("%s authentication succeeded", prot); + + /* Save the authentication method for later. */ + pcb->auth_done |= bit; + + /* + * If there is no more authentication still being done, + * proceed to the network (or callback) phase. + */ + if ((pcb->auth_pending &= ~bit) == 0) + network_phase(pcb); +} + + +/* + * np_up - a network protocol has come up. + */ +void np_up(ppp_pcb *pcb, int proto) { +#if PPP_IDLETIMELIMIT + int tlim; +#endif /* PPP_IDLETIMELIMIT */ + LWIP_UNUSED_ARG(proto); + + if (pcb->num_np_up == 0) { + /* + * At this point we consider that the link has come up successfully. + */ + new_phase(pcb, PPP_PHASE_RUNNING); + +#if PPP_IDLETIMELIMIT +#if 0 /* UNUSED */ + if (idle_time_hook != 0) + tlim = (*idle_time_hook)(NULL); + else +#endif /* UNUSED */ + tlim = pcb->settings.idle_time_limit; + if (tlim > 0) + TIMEOUT(check_idle, (void*)pcb, tlim); +#endif /* PPP_IDLETIMELIMIT */ + +#if PPP_MAXCONNECT + /* + * Set a timeout to close the connection once the maximum + * connect time has expired. + */ + if (pcb->settings.maxconnect > 0) + TIMEOUT(connect_time_expired, (void*)pcb, pcb->settings.maxconnect); +#endif /* PPP_MAXCONNECT */ + +#ifdef MAXOCTETS + if (maxoctets > 0) + TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); +#endif + +#if 0 /* Unused */ + /* + * Detach now, if the updetach option was given. + */ + if (updetach && !nodetach) + detach(); +#endif /* Unused */ + } + ++pcb->num_np_up; +} + +/* + * np_down - a network protocol has gone down. + */ +void np_down(ppp_pcb *pcb, int proto) { + LWIP_UNUSED_ARG(proto); + if (--pcb->num_np_up == 0) { +#if PPP_IDLETIMELIMIT + UNTIMEOUT(check_idle, (void*)pcb); +#endif /* PPP_IDLETIMELIMIT */ +#if PPP_MAXCONNECT + UNTIMEOUT(connect_time_expired, NULL); +#endif /* PPP_MAXCONNECT */ +#ifdef MAXOCTETS + UNTIMEOUT(check_maxoctets, NULL); +#endif + new_phase(pcb, PPP_PHASE_NETWORK); + } +} + +/* + * np_finished - a network protocol has finished using the link. + */ +void np_finished(ppp_pcb *pcb, int proto) { + LWIP_UNUSED_ARG(proto); + if (--pcb->num_np_open <= 0) { + /* no further use for the link: shut up shop. */ + lcp_close(pcb, "No network protocols running"); + } +} + +#ifdef MAXOCTETS +static void +check_maxoctets(arg) + void *arg; +{ +#if PPP_STATS_SUPPORT + unsigned int used; + + update_link_stats(ifunit); + link_stats_valid=0; + + switch(maxoctets_dir) { + case PPP_OCTETS_DIRECTION_IN: + used = link_stats.bytes_in; + break; + case PPP_OCTETS_DIRECTION_OUT: + used = link_stats.bytes_out; + break; + case PPP_OCTETS_DIRECTION_MAXOVERAL: + case PPP_OCTETS_DIRECTION_MAXSESSION: + used = (link_stats.bytes_in > link_stats.bytes_out) ? link_stats.bytes_in : link_stats.bytes_out; + break; + default: + used = link_stats.bytes_in+link_stats.bytes_out; + break; + } + if (used > maxoctets) { + ppp_notice("Traffic limit reached. Limit: %u Used: %u", maxoctets, used); + status = EXIT_TRAFFIC_LIMIT; + lcp_close(pcb, "Traffic limit"); +#if 0 /* UNUSED */ + need_holdoff = 0; +#endif /* UNUSED */ + } else { + TIMEOUT(check_maxoctets, NULL, maxoctets_timeout); + } +#endif /* PPP_STATS_SUPPORT */ +} +#endif /* MAXOCTETS */ + +#if PPP_IDLETIMELIMIT +/* + * check_idle - check whether the link has been idle for long + * enough that we can shut it down. + */ +static void check_idle(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + struct ppp_idle idle; + time_t itime; + int tlim; + + if (!get_idle_time(pcb, &idle)) + return; +#if 0 /* UNUSED */ + if (idle_time_hook != 0) { + tlim = idle_time_hook(&idle); + } else { +#endif /* UNUSED */ + itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle); + tlim = pcb->settings.idle_time_limit - itime; +#if 0 /* UNUSED */ + } +#endif /* UNUSED */ + if (tlim <= 0) { + int errcode = PPPERR_IDLETIMEOUT; + /* link is idle: shut it down. */ + ppp_notice("Terminating connection due to lack of activity."); + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "Link inactive"); +#if 0 /* UNUSED */ + need_holdoff = 0; +#endif /* UNUSED */ + } else { + TIMEOUT(check_idle, (void*)pcb, tlim); + } +} +#endif /* PPP_IDLETIMELIMIT */ + +#if PPP_MAXCONNECT +/* + * connect_time_expired - log a message and close the connection. + */ +static void connect_time_expired(void *arg) { + int errcode = PPPERR_CONNECTTIME; + ppp_pcb *pcb = (ppp_pcb*)arg; + ppp_info("Connect time expired"); + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "Connect time expired"); /* Close connection */ +} +#endif /* PPP_MAXCONNECT */ + +#if PPP_OPTIONS +/* + * auth_check_options - called to check authentication options. + */ +void +auth_check_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + int can_auth; + int lacks_ip; + + /* Default our_name to hostname, and user to our_name */ + if (our_name[0] == 0 || usehostname) + strlcpy(our_name, hostname, sizeof(our_name)); + /* If a blank username was explicitly given as an option, trust + the user and don't use our_name */ + if (ppp_settings.user[0] == 0 && !explicit_user) + strlcpy(ppp_settings.user, our_name, sizeof(ppp_settings.user)); + + /* + * If we have a default route, require the peer to authenticate + * unless the noauth option was given or the real user is root. + */ + if (!auth_required && !allow_any_ip && have_route_to(0) && !privileged) { + auth_required = 1; + default_auth = 1; + } + +#if CHAP_SUPPORT + /* If we selected any CHAP flavors, we should probably negotiate it. :-) */ + if (wo->chap_mdtype) + wo->neg_chap = 1; +#endif /* CHAP_SUPPORT */ + + /* If authentication is required, ask peer for CHAP, PAP, or EAP. */ + if (auth_required) { + allow_any_ip = 0; + if (1 +#if CHAP_SUPPORT + && !wo->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + && !wo->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + && !wo->neg_eap +#endif /* EAP_SUPPORT */ + ) { +#if CHAP_SUPPORT + wo->neg_chap = CHAP_MDTYPE_SUPPORTED != MDTYPE_NONE; + wo->chap_mdtype = CHAP_MDTYPE_SUPPORTED; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + wo->neg_upap = 1; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + wo->neg_eap = 1; +#endif /* EAP_SUPPORT */ + } + } else { +#if CHAP_SUPPORT + wo->neg_chap = 0; + wo->chap_mdtype = MDTYPE_NONE; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + wo->neg_upap = 0; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + wo->neg_eap = 0; +#endif /* EAP_SUPPORT */ + } + + /* + * Check whether we have appropriate secrets to use + * to authenticate the peer. Note that EAP can authenticate by way + * of a CHAP-like exchanges as well as SRP. + */ + lacks_ip = 0; +#if PAP_SUPPORT + can_auth = wo->neg_upap && (uselogin || have_pap_secret(&lacks_ip)); +#else + can_auth = 0; +#endif /* PAP_SUPPORT */ + if (!can_auth && (0 +#if CHAP_SUPPORT + || wo->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || wo->neg_eap +#endif /* EAP_SUPPORT */ + )) { +#if CHAP_SUPPORT + can_auth = have_chap_secret((explicit_remote? remote_name: NULL), + our_name, 1, &lacks_ip); +#else + can_auth = 0; +#endif + } + if (!can_auth +#if EAP_SUPPORT + && wo->neg_eap +#endif /* EAP_SUPPORT */ + ) { + can_auth = have_srp_secret((explicit_remote? remote_name: NULL), + our_name, 1, &lacks_ip); + } + + if (auth_required && !can_auth && noauth_addrs == NULL) { + if (default_auth) { + option_error( +"By default the remote system is required to authenticate itself"); + option_error( +"(because this system has a default route to the internet)"); + } else if (explicit_remote) + option_error( +"The remote system (%s) is required to authenticate itself", + remote_name); + else + option_error( +"The remote system is required to authenticate itself"); + option_error( +"but I couldn't find any suitable secret (password) for it to use to do so."); + if (lacks_ip) + option_error( +"(None of the available passwords would let it use an IP address.)"); + + exit(1); + } + + /* + * Early check for remote number authorization. + */ + if (!auth_number()) { + ppp_warn("calling number %q is not authorized", remote_number); + exit(EXIT_CNID_AUTH_FAILED); + } +} +#endif /* PPP_OPTIONS */ + +/* + * auth_reset - called when LCP is starting negotiations to recheck + * authentication options, i.e. whether we have appropriate secrets + * to use for authenticating ourselves and/or the peer. + */ +void auth_reset(ppp_pcb *pcb) { + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + if(pcb->settings.passwd) { + +#if PAP_SUPPORT + ao->neg_upap = !pcb->settings.refuse_pap; +#endif /* PAP_SUPPORT */ + +#if EAP_SUPPORT + ao->neg_eap = !pcb->settings.refuse_eap; +#endif /* EAP_SUPPORT */ + +#if CHAP_SUPPORT + ao->chap_mdtype = MDTYPE_NONE; + if(!pcb->settings.refuse_chap) + ao->chap_mdtype |= MDTYPE_MD5; +#if MSCHAP_SUPPORT + if(!pcb->settings.refuse_mschap) + ao->chap_mdtype |= MDTYPE_MICROSOFT; + if(!pcb->settings.refuse_mschap_v2) + ao->chap_mdtype |= MDTYPE_MICROSOFT_V2; +#endif /* MSCHAP_SUPPORT */ + + ao->neg_chap = (ao->chap_mdtype != MDTYPE_NONE); +#endif /* CHAP_SUPPORT */ + + } else { +#if PAP_SUPPORT + ao->neg_upap = 0; +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + ao->neg_chap = 0; + ao->chap_mdtype = MDTYPE_NONE; +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + ao->neg_eap = 0; +#endif /* EAP_SUPPORT */ + } + +#if PAP_SUPPORT + PPPDEBUG(LOG_DEBUG, ("neg_upap: %d\n", ao->neg_upap) ); +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + PPPDEBUG(LOG_DEBUG, ("neg_chap: %d\n", ao->neg_chap) ); + PPPDEBUG(LOG_DEBUG, ("neg_chap_md5: %d\n", !!(ao->chap_mdtype&MDTYPE_MD5)) ); +#if MSCHAP_SUPPORT + PPPDEBUG(LOG_DEBUG, ("neg_chap_ms: %d\n", !!(ao->chap_mdtype&MDTYPE_MICROSOFT)) ); + PPPDEBUG(LOG_DEBUG, ("neg_chap_ms2: %d\n", !!(ao->chap_mdtype&MDTYPE_MICROSOFT_V2)) ); +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + PPPDEBUG(LOG_DEBUG, ("neg_eap: %d\n", ao->neg_eap) ); +#endif /* EAP_SUPPORT */ + +#if 0 /* OLD CODE */ + ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(NULL)); + + /* + ao->neg_chap = (!ppp_settings.refuse_chap || !refuse_mschap || !refuse_mschap_v2) + && (passwd[0] != 0 || + (hadchap = have_chap_secret(user, (explicit_remote? remote_name: + NULL), 0, NULL))); */ + /* + ao->neg_eap = !refuse_eap && ( + passwd[0] != 0 || + (hadchap == 1 || (hadchap == -1 && have_chap_secret(ppp_settings.user, + (explicit_remote? remote_name: NULL), 0, NULL))) || + have_srp_secret(ppp_settings.user, (explicit_remote? remote_name: NULL), 0, NULL)); */ +#endif /* OLD CODE */ + +#if PAP_SUPPORT + go->neg_upap = 0; +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + go->neg_chap = 0; + go->chap_mdtype = MDTYPE_NONE; +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + go->neg_eap = 0; +#endif /* EAP_SUPPORT */ + return; +#if 0 + /* FIXME: find what the below stuff do */ + int hadchap; + hadchap = -1; + + hadchap = -1; + if (go->neg_upap && !uselogin && !have_pap_secret(NULL)) + go->neg_upap = 0; + + if (go->neg_chap) { + if (!(hadchap = have_chap_secret((explicit_remote? remote_name: NULL), + our_name, 1, NULL))) + go->neg_chap = 0; + } + + if (go->neg_eap && + (hadchap == 0 || (hadchap == -1 && + !have_chap_secret((explicit_remote? remote_name: NULL), our_name, + 1, NULL))) && + !have_srp_secret((explicit_remote? remote_name: NULL), our_name, 1, + NULL)) + go->neg_eap = 0; +#endif +} + +#if 0 /* UNUSED */ +/* + * check_passwd - Check the user name and passwd against the PAP secrets + * file. If requested, also check against the system password database, + * and login the user if OK. + * + * returns: + * UPAP_AUTHNAK: Authentication failed. + * UPAP_AUTHACK: Authentication succeeded. + * In either case, msg points to an appropriate message. + */ +int +check_passwd(unit, auser, userlen, apasswd, passwdlen, msg) + int unit; + char *auser; + int userlen; + char *apasswd; + int passwdlen; + char **msg; +{ + return UPAP_AUTHNAK; + int ret; + char *filename; + FILE *f; + struct wordlist *addrs = NULL, *opts = NULL; + char passwd[256], user[256]; + char secret[MAXWORDLEN]; + static int attempts = 0; + + /* + * Make copies of apasswd and auser, then null-terminate them. + * If there are unprintable characters in the password, make + * them visible. + */ + slprintf(ppp_settings.passwd, sizeof(ppp_settings.passwd), "%.*v", passwdlen, apasswd); + slprintf(ppp_settings.user, sizeof(ppp_settings.user), "%.*v", userlen, auser); + *msg = ""; + + /* + * Check if a plugin wants to handle this. + */ + if (pap_auth_hook) { + ret = (*pap_auth_hook)(ppp_settings.user, ppp_settings.passwd, msg, &addrs, &opts); + if (ret >= 0) { + /* note: set_allowed_addrs() saves opts (but not addrs): + don't free it! */ + if (ret) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd)); + return ret? UPAP_AUTHACK: UPAP_AUTHNAK; + } + } + + /* + * Open the file of pap secrets and scan for a suitable secret + * for authenticating this user. + */ + filename = _PATH_UPAPFILE; + addrs = opts = NULL; + ret = UPAP_AUTHNAK; + f = fopen(filename, "r"); + if (f == NULL) { + ppp_error("Can't open PAP password file %s: %m", filename); + + } else { + check_access(f, filename); + if (scan_authfile(f, ppp_settings.user, our_name, secret, &addrs, &opts, filename, 0) < 0) { + ppp_warn("no PAP secret found for %s", user); + } else { + /* + * If the secret is "@login", it means to check + * the password against the login database. + */ + int login_secret = strcmp(secret, "@login") == 0; + ret = UPAP_AUTHACK; + if (uselogin || login_secret) { + /* login option or secret is @login */ + if (session_full(ppp_settings.user, ppp_settings.passwd, devnam, msg) == 0) { + ret = UPAP_AUTHNAK; + } + } else if (session_mgmt) { + if (session_check(ppp_settings.user, NULL, devnam, NULL) == 0) { + ppp_warn("Peer %q failed PAP Session verification", user); + ret = UPAP_AUTHNAK; + } + } + if (secret[0] != 0 && !login_secret) { + /* password given in pap-secrets - must match */ + if ((cryptpap || strcmp(ppp_settings.passwd, secret) != 0) + && strcmp(crypt(ppp_settings.passwd, secret), secret) != 0) + ret = UPAP_AUTHNAK; + } + } + fclose(f); + } + + if (ret == UPAP_AUTHNAK) { + if (**msg == 0) + *msg = "Login incorrect"; + /* + * XXX can we ever get here more than once?? + * Frustrate passwd stealer programs. + * Allow 10 tries, but start backing off after 3 (stolen from login). + * On 10'th, drop the connection. + */ + if (attempts++ >= 10) { + ppp_warn("%d LOGIN FAILURES ON %s, %s", attempts, devnam, user); + lcp_close(pcb, "login failed"); + } + if (attempts > 3) + sleep((u_int) (attempts - 3) * 5); + if (opts != NULL) + free_wordlist(opts); + + } else { + attempts = 0; /* Reset count */ + if (**msg == 0) + *msg = "Login ok"; + set_allowed_addrs(unit, addrs, opts); + } + + if (addrs != NULL) + free_wordlist(addrs); + BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd)); + BZERO(secret, sizeof(secret)); + + return ret; +} + +/* + * null_login - Check if a username of "" and a password of "" are + * acceptable, and iff so, set the list of acceptable IP addresses + * and return 1. + */ +static int +null_login(unit) + int unit; +{ + char *filename; + FILE *f; + int i, ret; + struct wordlist *addrs, *opts; + char secret[MAXWORDLEN]; + + /* + * Check if a plugin wants to handle this. + */ + ret = -1; + if (null_auth_hook) + ret = (*null_auth_hook)(&addrs, &opts); + + /* + * Open the file of pap secrets and scan for a suitable secret. + */ + if (ret <= 0) { + filename = _PATH_UPAPFILE; + addrs = NULL; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + + i = scan_authfile(f, "", our_name, secret, &addrs, &opts, filename, 0); + ret = i >= 0 && secret[0] == 0; + BZERO(secret, sizeof(secret)); + fclose(f); + } + + if (ret) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + + return ret; +} + +/* + * get_pap_passwd - get a password for authenticating ourselves with + * our peer using PAP. Returns 1 on success, 0 if no suitable password + * could be found. + * Assumes passwd points to MAXSECRETLEN bytes of space (if non-null). + */ +static int +get_pap_passwd(passwd) + char *passwd; +{ + char *filename; + FILE *f; + int ret; + char secret[MAXWORDLEN]; + + /* + * Check whether a plugin wants to supply this. + */ + if (pap_passwd_hook) { + ret = (*pap_passwd_hook)(ppp_settings,user, ppp_settings.passwd); + if (ret >= 0) + return ret; + } + + filename = _PATH_UPAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + check_access(f, filename); + ret = scan_authfile(f, user, + (remote_name[0]? remote_name: NULL), + secret, NULL, NULL, filename, 0); + fclose(f); + if (ret < 0) + return 0; + if (passwd != NULL) + strlcpy(passwd, secret, MAXSECRETLEN); + BZERO(secret, sizeof(secret)); + return 1; +} + +/* + * have_pap_secret - check whether we have a PAP file with any + * secrets that we could possibly use for authenticating the peer. + */ +static int +have_pap_secret(lacks_ipp) + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + /* let the plugin decide, if there is one */ + if (pap_check_hook) { + ret = (*pap_check_hook)(); + if (ret >= 0) + return ret; + } + + filename = _PATH_UPAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + ret = scan_authfile(f, (explicit_remote? remote_name: NULL), our_name, + NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} + +/* + * have_chap_secret - check whether we have a CHAP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_chap_secret(client, server, need_ip, lacks_ipp) + char *client; + char *server; + int need_ip; + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + if (chap_check_hook) { + ret = (*chap_check_hook)(); + if (ret >= 0) { + return ret; + } + } + + filename = _PATH_CHAPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + if (client != NULL && client[0] == 0) + client = NULL; + else if (server != NULL && server[0] == 0) + server = NULL; + + ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} + +/* + * have_srp_secret - check whether we have a SRP file with a + * secret that we could possibly use for authenticating `client' + * on `server'. Either can be the null string, meaning we don't + * know the identity yet. + */ +static int +have_srp_secret(client, server, need_ip, lacks_ipp) + char *client; + char *server; + int need_ip; + int *lacks_ipp; +{ + FILE *f; + int ret; + char *filename; + struct wordlist *addrs; + + filename = _PATH_SRPFILE; + f = fopen(filename, "r"); + if (f == NULL) + return 0; + + if (client != NULL && client[0] == 0) + client = NULL; + else if (server != NULL && server[0] == 0) + server = NULL; + + ret = scan_authfile(f, client, server, NULL, &addrs, NULL, filename, 0); + fclose(f); + if (ret >= 0 && need_ip && !some_ip_ok(addrs)) { + if (lacks_ipp != 0) + *lacks_ipp = 1; + ret = -1; + } + if (addrs != 0) + free_wordlist(addrs); + + return ret >= 0; +} +#endif /* UNUSED */ + +/* + * get_secret - open the CHAP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server) { + int len; + + LWIP_UNUSED_ARG(server); + LWIP_UNUSED_ARG(am_server); + + if(!client || !client[0] || !pcb->settings.user || !pcb->settings.passwd || strcmp(client, pcb->settings.user)) { + return 0; + } + + len = (int)strlen(pcb->settings.passwd); + if (len > MAXSECRETLEN) { + ppp_error("Secret for %s on %s is too long", client, server); + len = MAXSECRETLEN; + } + + MEMCPY(secret, pcb->settings.passwd, len); + *secret_len = len; + + return 1; + +/* FIXME: clean that */ +#if 0 + strlcpy(rname, ppp_settings.user, sizeof(rname)); + + +/* + strlcpy(rname, ppp_settings.user, sizeof(rname)); + strlcpy(secret, ppp_settings.passwd, sizeof(secret)); + secret_len = strlen(secret); +*/ + + FILE *f; + int ret, len; + char *filename; + struct wordlist *addrs, *opts; + char secbuf[MAXWORDLEN]; + struct wordlist *addrs; + addrs = NULL; + + if (!am_server && ppp_settings.passwd[0] != 0) { + strlcpy(secbuf, ppp_settings.passwd, sizeof(secbuf)); + } else if (!am_server && chap_passwd_hook) { + if ( (*chap_passwd_hook)(client, secbuf) < 0) { + ppp_error("Unable to obtain CHAP password for %s on %s from plugin", + client, server); + return 0; + } + } else { + filename = _PATH_CHAPFILE; + addrs = NULL; + secbuf[0] = 0; + + f = fopen(filename, "r"); + if (f == NULL) { + ppp_error("Can't open chap secret file %s: %m", filename); + return 0; + } + check_access(f, filename); + + ret = scan_authfile(f, client, server, secbuf, &addrs, &opts, filename, 0); + fclose(f); + if (ret < 0) + return 0; + + if (am_server) + set_allowed_addrs(unit, addrs, opts); + else if (opts != 0) + free_wordlist(opts); + if (addrs != 0) + free_wordlist(addrs); + } + + len = strlen(secbuf); + if (len > MAXSECRETLEN) { + ppp_error("Secret for %s on %s is too long", client, server); + len = MAXSECRETLEN; + } + MEMCPY(secret, secbuf, len); + BZERO(secbuf, sizeof(secbuf)); + *secret_len = len; + + return 1; +#endif +} + + +#if 0 /* UNUSED */ +/* + * get_srp_secret - open the SRP secret file and return the secret + * for authenticating the given client on the given server. + * (We could be either client or server). + */ +int +get_srp_secret(unit, client, server, secret, am_server) + int unit; + char *client; + char *server; + char *secret; + int am_server; +{ + FILE *fp; + int ret; + char *filename; + struct wordlist *addrs, *opts; + + if (!am_server && ppp_settings.passwd[0] != '\0') { + strlcpy(secret, ppp_settings.passwd, MAXWORDLEN); + } else { + filename = _PATH_SRPFILE; + addrs = NULL; + + fp = fopen(filename, "r"); + if (fp == NULL) { + ppp_error("Can't open srp secret file %s: %m", filename); + return 0; + } + check_access(fp, filename); + + secret[0] = '\0'; + ret = scan_authfile(fp, client, server, secret, &addrs, &opts, + filename, am_server); + fclose(fp); + if (ret < 0) + return 0; + + if (am_server) + set_allowed_addrs(unit, addrs, opts); + else if (opts != NULL) + free_wordlist(opts); + if (addrs != NULL) + free_wordlist(addrs); + } + + return 1; +} + +/* + * set_allowed_addrs() - set the list of allowed addresses. + * Also looks for `--' indicating options to apply for this peer + * and leaves the following words in extra_options. + */ +static void +set_allowed_addrs(unit, addrs, opts) + int unit; + struct wordlist *addrs; + struct wordlist *opts; +{ + int n; + struct wordlist *ap, **plink; + struct permitted_ip *ip; + char *ptr_word, *ptr_mask; + struct hostent *hp; + struct netent *np; + u32_t a, mask, ah, offset; + struct ipcp_options *wo = &ipcp_wantoptions[unit]; + u32_t suggested_ip = 0; + + if (addresses[unit] != NULL) + free(addresses[unit]); + addresses[unit] = NULL; + if (extra_options != NULL) + free_wordlist(extra_options); + extra_options = opts; + + /* + * Count the number of IP addresses given. + */ + n = wordlist_count(addrs) + wordlist_count(noauth_addrs); + if (n == 0) + return; + ip = (struct permitted_ip *) malloc((n + 1) * sizeof(struct permitted_ip)); + if (ip == 0) + return; + + /* temporarily append the noauth_addrs list to addrs */ + for (plink = &addrs; *plink != NULL; plink = &(*plink)->next) + ; + *plink = noauth_addrs; + + n = 0; + for (ap = addrs; ap != NULL; ap = ap->next) { + /* "-" means no addresses authorized, "*" means any address allowed */ + ptr_word = ap->word; + if (strcmp(ptr_word, "-") == 0) + break; + if (strcmp(ptr_word, "*") == 0) { + ip[n].permit = 1; + ip[n].base = ip[n].mask = 0; + ++n; + break; + } + + ip[n].permit = 1; + if (*ptr_word == '!') { + ip[n].permit = 0; + ++ptr_word; + } + + mask = ~ (u32_t) 0; + offset = 0; + ptr_mask = strchr (ptr_word, '/'); + if (ptr_mask != NULL) { + int bit_count; + char *endp; + + bit_count = (int) strtol (ptr_mask+1, &endp, 10); + if (bit_count <= 0 || bit_count > 32) { + ppp_warn("invalid address length %v in auth. address list", + ptr_mask+1); + continue; + } + bit_count = 32 - bit_count; /* # bits in host part */ + if (*endp == '+') { + offset = ifunit + 1; + ++endp; + } + if (*endp != 0) { + ppp_warn("invalid address length syntax: %v", ptr_mask+1); + continue; + } + *ptr_mask = '\0'; + mask <<= bit_count; + } + + hp = gethostbyname(ptr_word); + if (hp != NULL && hp->h_addrtype == AF_INET) { + a = *(u32_t *)hp->h_addr; + } else { + np = getnetbyname (ptr_word); + if (np != NULL && np->n_addrtype == AF_INET) { + a = htonl ((u32_t)np->n_net); + if (ptr_mask == NULL) { + /* calculate appropriate mask for net */ + ah = ntohl(a); + if (IN_CLASSA(ah)) + mask = IN_CLASSA_NET; + else if (IN_CLASSB(ah)) + mask = IN_CLASSB_NET; + else if (IN_CLASSC(ah)) + mask = IN_CLASSC_NET; + } + } else { + a = inet_addr (ptr_word); + } + } + + if (ptr_mask != NULL) + *ptr_mask = '/'; + + if (a == (u32_t)-1L) { + ppp_warn("unknown host %s in auth. address list", ap->word); + continue; + } + if (offset != 0) { + if (offset >= ~mask) { + ppp_warn("interface unit %d too large for subnet %v", + ifunit, ptr_word); + continue; + } + a = htonl((ntohl(a) & mask) + offset); + mask = ~(u32_t)0; + } + ip[n].mask = htonl(mask); + ip[n].base = a & ip[n].mask; + ++n; + if (~mask == 0 && suggested_ip == 0) + suggested_ip = a; + } + *plink = NULL; + + ip[n].permit = 0; /* make the last entry forbid all addresses */ + ip[n].base = 0; /* to terminate the list */ + ip[n].mask = 0; + + addresses[unit] = ip; + + /* + * If the address given for the peer isn't authorized, or if + * the user hasn't given one, AND there is an authorized address + * which is a single host, then use that if we find one. + */ + if (suggested_ip != 0 + && (wo->hisaddr == 0 || !auth_ip_addr(unit, wo->hisaddr))) { + wo->hisaddr = suggested_ip; + /* + * Do we insist on this address? No, if there are other + * addresses authorized than the suggested one. + */ + if (n > 1) + wo->accept_remote = 1; + } +} + +/* + * auth_ip_addr - check whether the peer is authorized to use + * a given IP address. Returns 1 if authorized, 0 otherwise. + */ +int +auth_ip_addr(unit, addr) + int unit; + u32_t addr; +{ + int ok; + + /* don't allow loopback or multicast address */ + if (bad_ip_adrs(addr)) + return 0; + + if (allowed_address_hook) { + ok = allowed_address_hook(addr); + if (ok >= 0) return ok; + } + + if (addresses[unit] != NULL) { + ok = ip_addr_check(addr, addresses[unit]); + if (ok >= 0) + return ok; + } + + if (auth_required) + return 0; /* no addresses authorized */ + return allow_any_ip || privileged || !have_route_to(addr); +} + +static int +ip_addr_check(addr, addrs) + u32_t addr; + struct permitted_ip *addrs; +{ + for (; ; ++addrs) + if ((addr & addrs->mask) == addrs->base) + return addrs->permit; +} + +/* + * bad_ip_adrs - return 1 if the IP address is one we don't want + * to use, such as an address in the loopback net or a multicast address. + * addr is in network byte order. + */ +int +bad_ip_adrs(addr) + u32_t addr; +{ + addr = ntohl(addr); + return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET + || IN_MULTICAST(addr) || IN_BADCLASS(addr); +} + +/* + * some_ip_ok - check a wordlist to see if it authorizes any + * IP address(es). + */ +static int +some_ip_ok(addrs) + struct wordlist *addrs; +{ + for (; addrs != 0; addrs = addrs->next) { + if (addrs->word[0] == '-') + break; + if (addrs->word[0] != '!') + return 1; /* some IP address is allowed */ + } + return 0; +} + +/* + * auth_number - check whether the remote number is allowed to connect. + * Returns 1 if authorized, 0 otherwise. + */ +int +auth_number() +{ + struct wordlist *wp = permitted_numbers; + int l; + + /* Allow all if no authorization list. */ + if (!wp) + return 1; + + /* Allow if we have a match in the authorization list. */ + while (wp) { + /* trailing '*' wildcard */ + l = strlen(wp->word); + if ((wp->word)[l - 1] == '*') + l--; + if (!strncasecmp(wp->word, remote_number, l)) + return 1; + wp = wp->next; + } + + return 0; +} + +/* + * check_access - complain if a secret file has too-liberal permissions. + */ +static void +check_access(f, filename) + FILE *f; + char *filename; +{ + struct stat sbuf; + + if (fstat(fileno(f), &sbuf) < 0) { + ppp_warn("cannot stat secret file %s: %m", filename); + } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) { + ppp_warn("Warning - secret file %s has world and/or group access", + filename); + } +} + +/* + * scan_authfile - Scan an authorization file for a secret suitable + * for authenticating `client' on `server'. The return value is -1 + * if no secret is found, otherwise >= 0. The return value has + * NONWILD_CLIENT set if the secret didn't have "*" for the client, and + * NONWILD_SERVER set if the secret didn't have "*" for the server. + * Any following words on the line up to a "--" (i.e. address authorization + * info) are placed in a wordlist and returned in *addrs. Any + * following words (extra options) are placed in a wordlist and + * returned in *opts. + * We assume secret is NULL or points to MAXWORDLEN bytes of space. + * Flags are non-zero if we need two colons in the secret in order to + * match. + */ +static int +scan_authfile(f, client, server, secret, addrs, opts, filename, flags) + FILE *f; + char *client; + char *server; + char *secret; + struct wordlist **addrs; + struct wordlist **opts; + char *filename; + int flags; +{ + int newline, xxx; + int got_flag, best_flag; + FILE *sf; + struct wordlist *ap, *addr_list, *alist, **app; + char word[MAXWORDLEN]; + char atfile[MAXWORDLEN]; + char lsecret[MAXWORDLEN]; + char *cp; + + if (addrs != NULL) + *addrs = NULL; + if (opts != NULL) + *opts = NULL; + addr_list = NULL; + if (!getword(f, word, &newline, filename)) + return -1; /* file is empty??? */ + newline = 1; + best_flag = -1; + for (;;) { + /* + * Skip until we find a word at the start of a line. + */ + while (!newline && getword(f, word, &newline, filename)) + ; + if (!newline) + break; /* got to end of file */ + + /* + * Got a client - check if it's a match or a wildcard. + */ + got_flag = 0; + if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) { + newline = 0; + continue; + } + if (!ISWILD(word)) + got_flag = NONWILD_CLIENT; + + /* + * Now get a server and check if it matches. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + if (!ISWILD(word)) { + if (server != NULL && strcmp(word, server) != 0) + continue; + got_flag |= NONWILD_SERVER; + } + + /* + * Got some sort of a match - see if it's better than what + * we have already. + */ + if (got_flag <= best_flag) + continue; + + /* + * Get the secret. + */ + if (!getword(f, word, &newline, filename)) + break; + if (newline) + continue; + + /* + * SRP-SHA1 authenticator should never be reading secrets from + * a file. (Authenticatee may, though.) + */ + if (flags && ((cp = strchr(word, ':')) == NULL || + strchr(cp + 1, ':') == NULL)) + continue; + + if (secret != NULL) { + /* + * Special syntax: @/pathname means read secret from file. + */ + if (word[0] == '@' && word[1] == '/') { + strlcpy(atfile, word+1, sizeof(atfile)); + if ((sf = fopen(atfile, "r")) == NULL) { + ppp_warn("can't open indirect secret file %s", atfile); + continue; + } + check_access(sf, atfile); + if (!getword(sf, word, &xxx, atfile)) { + ppp_warn("no secret in indirect secret file %s", atfile); + fclose(sf); + continue; + } + fclose(sf); + } + strlcpy(lsecret, word, sizeof(lsecret)); + } + + /* + * Now read address authorization info and make a wordlist. + */ + app = &alist; + for (;;) { + if (!getword(f, word, &newline, filename) || newline) + break; + ap = (struct wordlist *) + malloc(sizeof(struct wordlist) + strlen(word) + 1); + if (ap == NULL) + novm("authorized addresses"); + ap->word = (char *) (ap + 1); + strcpy(ap->word, word); + *app = ap; + app = &ap->next; + } + *app = NULL; + + /* + * This is the best so far; remember it. + */ + best_flag = got_flag; + if (addr_list) + free_wordlist(addr_list); + addr_list = alist; + if (secret != NULL) + strlcpy(secret, lsecret, MAXWORDLEN); + + if (!newline) + break; + } + + /* scan for a -- word indicating the start of options */ + for (app = &addr_list; (ap = *app) != NULL; app = &ap->next) + if (strcmp(ap->word, "--") == 0) + break; + /* ap = start of options */ + if (ap != NULL) { + ap = ap->next; /* first option */ + free(*app); /* free the "--" word */ + *app = NULL; /* terminate addr list */ + } + if (opts != NULL) + *opts = ap; + else if (ap != NULL) + free_wordlist(ap); + if (addrs != NULL) + *addrs = addr_list; + else if (addr_list != NULL) + free_wordlist(addr_list); + + return best_flag; +} + +/* + * wordlist_count - return the number of items in a wordlist + */ +static int +wordlist_count(wp) + struct wordlist *wp; +{ + int n; + + for (n = 0; wp != NULL; wp = wp->next) + ++n; + return n; +} + +/* + * free_wordlist - release memory allocated for a wordlist. + */ +static void +free_wordlist(wp) + struct wordlist *wp; +{ + struct wordlist *next; + + while (wp != NULL) { + next = wp->next; + free(wp); + wp = next; + } +} +#endif /* UNUSED */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ccp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ccp.c new file mode 100644 index 0000000..0af3548 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ccp.c @@ -0,0 +1,1694 @@ +/* + * ccp.c - PPP Compression Control Protocol. + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CCP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ccp.h" +#include + +#ifdef MPPE +#include "netif/ppp/chap_ms.h" /* mppe_xxxx_key, mppe_keys_set */ +#include "netif/ppp/lcp.h" /* lcp_close(), lcp_fsm */ +#endif + +/* + * Unfortunately there is a bug in zlib which means that using a + * size of 8 (window size = 256) for Deflate compression will cause + * buffer overruns and kernel crashes in the deflate module. + * Until this is fixed we only accept sizes in the range 9 .. 15. + * Thanks to James Carlson for pointing this out. + */ +#define DEFLATE_MIN_WORKS 9 + +/* + * Command-line options. + */ +static int setbsdcomp (char **); +static int setdeflate (char **); +static char bsd_value[8]; +static char deflate_value[8]; + +/* + * Option variables. + */ +#ifdef MPPE +bool refuse_mppe_stateful = 1; /* Allow stateful mode? */ +#endif + +#if PPP_OPTIONS +static option_t ccp_option_list[] = { + { "noccp", o_bool, &ccp_protent.enabled_flag, + "Disable CCP negotiation" }, + { "-ccp", o_bool, &ccp_protent.enabled_flag, + "Disable CCP negotiation", OPT_ALIAS }, + + { "bsdcomp", o_special, (void *)setbsdcomp, + "Request BSD-Compress packet compression", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bsd_value }, + { "nobsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, + "don't allow BSD-Compress", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].bsd_compress }, + { "-bsdcomp", o_bool, &ccp_wantoptions[0].bsd_compress, + "don't allow BSD-Compress", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].bsd_compress }, + + { "deflate", o_special, (void *)setdeflate, + "request Deflate compression", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, deflate_value }, + { "nodeflate", o_bool, &ccp_wantoptions[0].deflate, + "don't allow Deflate compression", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].deflate }, + { "-deflate", o_bool, &ccp_wantoptions[0].deflate, + "don't allow Deflate compression", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].deflate }, + + { "nodeflatedraft", o_bool, &ccp_wantoptions[0].deflate_draft, + "don't use draft deflate #", OPT_A2COPY, + &ccp_allowoptions[0].deflate_draft }, + + { "predictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "request Predictor-1", OPT_PRIO | 1 }, + { "nopredictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "don't allow Predictor-1", OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].predictor_1 }, + { "-predictor1", o_bool, &ccp_wantoptions[0].predictor_1, + "don't allow Predictor-1", OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, + &ccp_allowoptions[0].predictor_1 }, + +#ifdef MPPE + /* MPPE options are symmetrical ... we only set wantoptions here */ + { "require-mppe", o_bool, &ccp_wantoptions[0].mppe, + "require MPPE encryption", + OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, + { "+mppe", o_bool, &ccp_wantoptions[0].mppe, + "require MPPE encryption", + OPT_ALIAS | OPT_PRIO | MPPE_OPT_40 | MPPE_OPT_128 }, + { "nomppe", o_bool, &ccp_wantoptions[0].mppe, + "don't allow MPPE encryption", OPT_PRIO }, + { "-mppe", o_bool, &ccp_wantoptions[0].mppe, + "don't allow MPPE encryption", OPT_ALIAS | OPT_PRIO }, + + /* We use ccp_allowoptions[0].mppe as a junk var ... it is reset later */ + { "require-mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + { "+mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 40-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + { "nomppe-40", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 40-bit encryption", + OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, &ccp_wantoptions[0].mppe }, + { "-mppe-40", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 40-bit encryption", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_40, + &ccp_wantoptions[0].mppe }, + + { "require-mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 128-bit encryption", OPT_PRIO | OPT_A2OR | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + { "+mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "require MPPE 128-bit encryption", + OPT_ALIAS | OPT_PRIO | OPT_A2OR | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + { "nomppe-128", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 128-bit encryption", + OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, &ccp_wantoptions[0].mppe }, + { "-mppe-128", o_bool, &ccp_allowoptions[0].mppe, + "don't allow MPPE 128-bit encryption", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLRB | MPPE_OPT_128, + &ccp_wantoptions[0].mppe }, + + /* strange one; we always request stateless, but will we allow stateful? */ + { "mppe-stateful", o_bool, &refuse_mppe_stateful, + "allow MPPE stateful mode", OPT_PRIO }, + { "nomppe-stateful", o_bool, &refuse_mppe_stateful, + "disallow MPPE stateful mode", OPT_PRIO | 1 }, +#endif /* MPPE */ + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ccp_init (int unit); +static void ccp_open (int unit); +static void ccp_close (int unit, const char *); +static void ccp_lowerup (int unit); +static void ccp_lowerdown (int); +static void ccp_input (int unit, u_char *pkt, int len); +static void ccp_protrej (int unit); +#if PRINTPKT_SUPPORT +static int ccp_printpkt (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); +#endif /* PRINTPKT_SUPPORT */ +static void ccp_datainput (int unit, u_char *pkt, int len); + +const struct protent ccp_protent = { + PPP_CCP, + ccp_init, + ccp_input, + ccp_protrej, + ccp_lowerup, + ccp_lowerdown, + ccp_open, + ccp_close, +#if PRINTPKT_SUPPORT + ccp_printpkt, +#endif /* PRINTPKT_SUPPORT */ + ccp_datainput, + 1, +#if PRINTPKT_SUPPORT + "CCP", + "Compressed", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ccp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +fsm ccp_fsm[NUM_PPP]; +ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */ +ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ +ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */ +ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */ + +/* + * Callbacks for fsm code. + */ +static void ccp_resetci (fsm *); +static int ccp_cilen (fsm *); +static void ccp_addci (fsm *, u_char *, int *); +static int ccp_ackci (fsm *, u_char *, int); +static int ccp_nakci (fsm *, u_char *, int, int); +static int ccp_rejci (fsm *, u_char *, int); +static int ccp_reqci (fsm *, u_char *, int *, int); +static void ccp_up (fsm *); +static void ccp_down (fsm *); +static int ccp_extcode (fsm *, int, int, u_char *, int); +static void ccp_rack_timeout (void *); +static char *method_name (ccp_options *, ccp_options *); + +static const fsm_callbacks ccp_callbacks = { + ccp_resetci, + ccp_cilen, + ccp_addci, + ccp_ackci, + ccp_nakci, + ccp_rejci, + ccp_reqci, + ccp_up, + ccp_down, + NULL, + NULL, + NULL, + NULL, + ccp_extcode, + "CCP" +}; + +/* + * Do we want / did we get any compression? + */ +#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \ + || (opt).predictor_1 || (opt).predictor_2 \ + || (opt).mppe) + +/* + * Local state (mainly for handling reset-reqs and reset-acks). + */ +static int ccp_localstate[NUM_PPP]; +#define RACK_PENDING 1 /* waiting for reset-ack */ +#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */ + +#define RACKTIMEOUT 1 /* second */ + +static int all_rejected[NUM_PPP]; /* we rejected all peer's options */ + +/* + * Option parsing + */ +static int +setbsdcomp(argv) + char **argv; +{ + int rbits, abits; + char *str, *endp; + + str = *argv; + abits = rbits = strtol(str, &endp, 0); + if (endp != str && *endp == ',') { + str = endp + 1; + abits = strtol(str, &endp, 0); + } + if (*endp != 0 || endp == str) { + option_error("invalid parameter '%s' for bsdcomp option", *argv); + return 0; + } + if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS)) + || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) { + option_error("bsdcomp option values must be 0 or %d .. %d", + BSD_MIN_BITS, BSD_MAX_BITS); + return 0; + } + if (rbits > 0) { + ccp_wantoptions[0].bsd_compress = 1; + ccp_wantoptions[0].bsd_bits = rbits; + } else + ccp_wantoptions[0].bsd_compress = 0; + if (abits > 0) { + ccp_allowoptions[0].bsd_compress = 1; + ccp_allowoptions[0].bsd_bits = abits; + } else + ccp_allowoptions[0].bsd_compress = 0; + slprintf(bsd_value, sizeof(bsd_value), + rbits == abits? "%d": "%d,%d", rbits, abits); + + return 1; +} + +static int +setdeflate(argv) + char **argv; +{ + int rbits, abits; + char *str, *endp; + + str = *argv; + abits = rbits = strtol(str, &endp, 0); + if (endp != str && *endp == ',') { + str = endp + 1; + abits = strtol(str, &endp, 0); + } + if (*endp != 0 || endp == str) { + option_error("invalid parameter '%s' for deflate option", *argv); + return 0; + } + if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE)) + || (abits != 0 && (abits < DEFLATE_MIN_SIZE + || abits > DEFLATE_MAX_SIZE))) { + option_error("deflate option values must be 0 or %d .. %d", + DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE); + return 0; + } + if (rbits == DEFLATE_MIN_SIZE || abits == DEFLATE_MIN_SIZE) { + if (rbits == DEFLATE_MIN_SIZE) + rbits = DEFLATE_MIN_WORKS; + if (abits == DEFLATE_MIN_SIZE) + abits = DEFLATE_MIN_WORKS; + warn("deflate option value of %d changed to %d to avoid zlib bug", + DEFLATE_MIN_SIZE, DEFLATE_MIN_WORKS); + } + if (rbits > 0) { + ccp_wantoptions[0].deflate = 1; + ccp_wantoptions[0].deflate_size = rbits; + } else + ccp_wantoptions[0].deflate = 0; + if (abits > 0) { + ccp_allowoptions[0].deflate = 1; + ccp_allowoptions[0].deflate_size = abits; + } else + ccp_allowoptions[0].deflate = 0; + slprintf(deflate_value, sizeof(deflate_value), + rbits == abits? "%d": "%d,%d", rbits, abits); + + return 1; +} + +/* + * ccp_init - initialize CCP. + */ +static void +ccp_init(unit) + int unit; +{ + fsm *f = &ccp_fsm[unit]; + + f->unit = unit; + f->protocol = PPP_CCP; + f->callbacks = &ccp_callbacks; + fsm_init(f); + + memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options)); + memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options)); + + ccp_wantoptions[0].deflate = 1; + ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE; + ccp_wantoptions[0].deflate_correct = 1; + ccp_wantoptions[0].deflate_draft = 1; + ccp_allowoptions[0].deflate = 1; + ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE; + ccp_allowoptions[0].deflate_correct = 1; + ccp_allowoptions[0].deflate_draft = 1; + + ccp_wantoptions[0].bsd_compress = 1; + ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS; + ccp_allowoptions[0].bsd_compress = 1; + ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS; + + ccp_allowoptions[0].predictor_1 = 1; +} + +/* + * ccp_open - CCP is allowed to come up. + */ +static void +ccp_open(unit) + int unit; +{ + fsm *f = &ccp_fsm[unit]; + + if (f->state != OPENED) + ccp_flags_set(unit, 1, 0); + + /* + * Find out which compressors the kernel supports before + * deciding whether to open in silent mode. + */ + ccp_resetci(f); + if (!ANY_COMPRESS(ccp_gotoptions[unit])) + f->flags |= OPT_SILENT; + + fsm_open(f); +} + +/* + * ccp_close - Terminate CCP. + */ +static void +ccp_close(unit, reason) + int unit; + const char *reason; +{ + ccp_flags_set(unit, 0, 0); + fsm_close(&ccp_fsm[unit], reason); +} + +/* + * ccp_lowerup - we may now transmit CCP packets. + */ +static void +ccp_lowerup(unit) + int unit; +{ + fsm_lowerup(&ccp_fsm[unit]); +} + +/* + * ccp_lowerdown - we may not transmit CCP packets. + */ +static void +ccp_lowerdown(unit) + int unit; +{ + fsm_lowerdown(&ccp_fsm[unit]); +} + +/* + * ccp_input - process a received CCP packet. + */ +static void +ccp_input(unit, p, len) + int unit; + u_char *p; + int len; +{ + fsm *f = &ccp_fsm[unit]; + int oldstate; + + /* + * Check for a terminate-request so we can print a message. + */ + oldstate = f->state; + fsm_input(f, p, len); + if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED) { + notice("Compression disabled by peer."); +#ifdef MPPE + if (ccp_gotoptions[unit].mppe) { + error("MPPE disabled, closing LCP"); + lcp_close(unit, "MPPE disabled by peer"); + } +#endif + } + + /* + * If we get a terminate-ack and we're not asking for compression, + * close CCP. + */ + if (oldstate == REQSENT && p[0] == TERMACK + && !ANY_COMPRESS(ccp_gotoptions[unit])) + ccp_close(unit, "No compression negotiated"); +} + +/* + * Handle a CCP-specific code. + */ +static int +ccp_extcode(f, code, id, p, len) + fsm *f; + int code, id; + u_char *p; + int len; +{ + switch (code) { + case CCP_RESETREQ: + if (f->state != OPENED) + break; + /* send a reset-ack, which the transmitter will see and + reset its compression state. */ + fsm_sdata(f, CCP_RESETACK, id, NULL, 0); + break; + + case CCP_RESETACK: + if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) { + ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT); + UNTIMEOUT(ccp_rack_timeout, f); + } + break; + + default: + return 0; + } + + return 1; +} + +/* + * ccp_protrej - peer doesn't talk CCP. + */ +static void +ccp_protrej(unit) + int unit; +{ + ccp_flags_set(unit, 0, 0); + fsm_lowerdown(&ccp_fsm[unit]); + +#ifdef MPPE + if (ccp_gotoptions[unit].mppe) { + error("MPPE required but peer negotiation failed"); + lcp_close(unit, "MPPE required but peer negotiation failed"); + } +#endif + +} + +/* + * ccp_resetci - initialize at start of negotiation. + */ +static void +ccp_resetci(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char opt_buf[CCP_MAX_OPTION_LENGTH]; + + *go = ccp_wantoptions[f->unit]; + all_rejected[f->unit] = 0; + +#ifdef MPPE + if (go->mppe) { + ccp_options *ao = &ccp_allowoptions[f->unit]; + int auth_mschap_bits = auth_done[f->unit]; + int numbits; + + /* + * Start with a basic sanity check: mschap[v2] auth must be in + * exactly one direction. RFC 3079 says that the keys are + * 'derived from the credentials of the peer that initiated the call', + * however the PPP protocol doesn't have such a concept, and pppd + * cannot get this info externally. Instead we do the best we can. + * NB: If MPPE is required, all other compression opts are invalid. + * So, we return right away if we can't do it. + */ + + /* Leave only the mschap auth bits set */ + auth_mschap_bits &= (CHAP_MS_WITHPEER | CHAP_MS_PEER | + CHAP_MS2_WITHPEER | CHAP_MS2_PEER); + /* Count the mschap auths */ + auth_mschap_bits >>= CHAP_MS_SHIFT; + numbits = 0; + do { + numbits += auth_mschap_bits & 1; + auth_mschap_bits >>= 1; + } while (auth_mschap_bits); + if (numbits > 1) { + error("MPPE required, but auth done in both directions."); + lcp_close(f->unit, "MPPE required but not available"); + return; + } + if (!numbits) { + error("MPPE required, but MS-CHAP[v2] auth not performed."); + lcp_close(f->unit, "MPPE required but not available"); + return; + } + + /* A plugin (eg radius) may not have obtained key material. */ + if (!mppe_keys_set) { + error("MPPE required, but keys are not available. " + "Possible plugin problem?"); + lcp_close(f->unit, "MPPE required but not available"); + return; + } + + /* LM auth not supported for MPPE */ + if (auth_done[f->unit] & (CHAP_MS_WITHPEER | CHAP_MS_PEER)) { + /* This might be noise */ + if (go->mppe & MPPE_OPT_40) { + notice("Disabling 40-bit MPPE; MS-CHAP LM not supported"); + go->mppe &= ~MPPE_OPT_40; + ccp_wantoptions[f->unit].mppe &= ~MPPE_OPT_40; + } + } + + /* Last check: can we actually negotiate something? */ + if (!(go->mppe & (MPPE_OPT_40 | MPPE_OPT_128))) { + /* Could be misconfig, could be 40-bit disabled above. */ + error("MPPE required, but both 40-bit and 128-bit disabled."); + lcp_close(f->unit, "MPPE required but not available"); + return; + } + + /* sync options */ + ao->mppe = go->mppe; + /* MPPE is not compatible with other compression types */ + ao->bsd_compress = go->bsd_compress = 0; + ao->predictor_1 = go->predictor_1 = 0; + ao->predictor_2 = go->predictor_2 = 0; + ao->deflate = go->deflate = 0; + } +#endif /* MPPE */ + + /* + * Check whether the kernel knows about the various + * compression methods we might request. + */ +#ifdef MPPE + if (go->mppe) { + opt_buf[0] = CI_MPPE; + opt_buf[1] = CILEN_MPPE; + MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); + /* Key material unimportant here. */ + if (ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0) <= 0) { + error("MPPE required, but kernel has no support."); + lcp_close(f->unit, "MPPE required but not available"); + } + } +#endif + if (go->bsd_compress) { + opt_buf[0] = CI_BSD_COMPRESS; + opt_buf[1] = CILEN_BSD_COMPRESS; + opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS); + if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0) + go->bsd_compress = 0; + } + if (go->deflate) { + if (go->deflate_correct) { + opt_buf[0] = CI_DEFLATE; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS); + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) + go->deflate_correct = 0; + } + if (go->deflate_draft) { + opt_buf[0] = CI_DEFLATE_DRAFT; + opt_buf[1] = CILEN_DEFLATE; + opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_WORKS); + opt_buf[3] = DEFLATE_CHK_SEQUENCE; + if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0) + go->deflate_draft = 0; + } + if (!go->deflate_correct && !go->deflate_draft) + go->deflate = 0; + } + if (go->predictor_1) { + opt_buf[0] = CI_PREDICTOR_1; + opt_buf[1] = CILEN_PREDICTOR_1; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0) + go->predictor_1 = 0; + } + if (go->predictor_2) { + opt_buf[0] = CI_PREDICTOR_2; + opt_buf[1] = CILEN_PREDICTOR_2; + if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0) + go->predictor_2 = 0; + } +} + +/* + * ccp_cilen - Return total length of our configuration info. + */ +static int +ccp_cilen(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + + return (go->bsd_compress? CILEN_BSD_COMPRESS: 0) + + (go->deflate? CILEN_DEFLATE: 0) + + (go->predictor_1? CILEN_PREDICTOR_1: 0) + + (go->predictor_2? CILEN_PREDICTOR_2: 0) + + (go->mppe? CILEN_MPPE: 0); +} + +/* + * ccp_addci - put our requests in a packet. + */ +static void +ccp_addci(f, p, lenp) + fsm *f; + u_char *p; + int *lenp; +{ + int res; + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + + /* + * Add the compression types that we can receive, in decreasing + * preference order. Get the kernel to allocate the first one + * in case it gets Acked. + */ +#ifdef MPPE + if (go->mppe) { + u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN]; + + p[0] = opt_buf[0] = CI_MPPE; + p[1] = opt_buf[1] = CILEN_MPPE; + MPPE_OPTS_TO_CI(go->mppe, &p[2]); + MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); + MEMCPY(&opt_buf[CILEN_MPPE], mppe_recv_key, MPPE_MAX_KEY_LEN); + res = ccp_test(f->unit, opt_buf, CILEN_MPPE + MPPE_MAX_KEY_LEN, 0); + if (res > 0) + p += CILEN_MPPE; + else + /* This shouldn't happen, we've already tested it! */ + lcp_close(f->unit, "MPPE required but not available in kernel"); + } +#endif + if (go->deflate) { + p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + if (p != p0) { + p += CILEN_DEFLATE; + } else { + for (;;) { + if (go->deflate_size < DEFLATE_MIN_WORKS) { + go->deflate = 0; + break; + } + res = ccp_test(f->unit, p, CILEN_DEFLATE, 0); + if (res > 0) { + p += CILEN_DEFLATE; + break; + } else if (res < 0) { + go->deflate = 0; + break; + } + --go->deflate_size; + p[2] = DEFLATE_MAKE_OPT(go->deflate_size); + } + } + if (p != p0 && go->deflate_correct && go->deflate_draft) { + p[0] = CI_DEFLATE_DRAFT; + p[1] = CILEN_DEFLATE; + p[2] = p[2 - CILEN_DEFLATE]; + p[3] = DEFLATE_CHK_SEQUENCE; + p += CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + p[0] = CI_BSD_COMPRESS; + p[1] = CILEN_BSD_COMPRESS; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + if (p != p0) { + p += CILEN_BSD_COMPRESS; /* not the first option */ + } else { + for (;;) { + if (go->bsd_bits < BSD_MIN_BITS) { + go->bsd_compress = 0; + break; + } + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0); + if (res > 0) { + p += CILEN_BSD_COMPRESS; + break; + } else if (res < 0) { + go->bsd_compress = 0; + break; + } + --go->bsd_bits; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits); + } + } + } + /* XXX Should Predictor 2 be preferable to Predictor 1? */ + if (go->predictor_1) { + p[0] = CI_PREDICTOR_1; + p[1] = CILEN_PREDICTOR_1; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) { + go->predictor_1 = 0; + } else { + p += CILEN_PREDICTOR_1; + } + } + if (go->predictor_2) { + p[0] = CI_PREDICTOR_2; + p[1] = CILEN_PREDICTOR_2; + if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) { + go->predictor_2 = 0; + } else { + p += CILEN_PREDICTOR_2; + } + } + + go->method = (p > p0)? p0[0]: -1; + + *lenp = p - p0; +} + +/* + * ccp_ackci - process a received configure-ack, and return + * 1 iff the packet was OK. + */ +static int +ccp_ackci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + u_char *p0 = p; + +#ifdef MPPE + if (go->mppe) { + u_char opt_buf[CILEN_MPPE]; + + opt_buf[0] = CI_MPPE; + opt_buf[1] = CILEN_MPPE; + MPPE_OPTS_TO_CI(go->mppe, &opt_buf[2]); + if (len < CILEN_MPPE || memcmp(opt_buf, p, CILEN_MPPE)) + return 0; + p += CILEN_MPPE; + len -= CILEN_MPPE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + } +#endif + if (go->deflate) { + if (len < CILEN_DEFLATE + || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + /* XXX Cope with first/fast ack */ + if (len == 0) + return 1; + if (go->deflate_correct && go->deflate_draft) { + if (len < CILEN_DEFLATE + || p[0] != CI_DEFLATE_DRAFT + || p[1] != CILEN_DEFLATE + || p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } + if (go->bsd_compress) { + if (len < CILEN_BSD_COMPRESS + || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS + || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_1) { + if (len < CILEN_PREDICTOR_1 + || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1) + return 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + if (go->predictor_2) { + if (len < CILEN_PREDICTOR_2 + || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2) + return 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + /* XXX Cope with first/fast ack */ + if (p == p0 && len == 0) + return 1; + } + + if (len != 0) + return 0; + return 1; +} + +/* + * ccp_nakci - process received configure-nak. + * Returns 1 iff the nak was OK. + */ +static int +ccp_nakci(f, p, len, treat_as_reject) + fsm *f; + u_char *p; + int len; + int treat_as_reject; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options no; /* options we've seen already */ + ccp_options try; /* options to ask for next time */ + + memset(&no, 0, sizeof(no)); + try = *go; + +#ifdef MPPE + if (go->mppe && len >= CILEN_MPPE + && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { + no.mppe = 1; + /* + * Peer wants us to use a different strength or other setting. + * Fail if we aren't willing to use his suggestion. + */ + MPPE_CI_TO_OPTS(&p[2], try.mppe); + if ((try.mppe & MPPE_OPT_STATEFUL) && refuse_mppe_stateful) { + error("Refusing MPPE stateful mode offered by peer"); + try.mppe = 0; + } else if (((go->mppe | MPPE_OPT_STATEFUL) & try.mppe) != try.mppe) { + /* Peer must have set options we didn't request (suggest) */ + try.mppe = 0; + } + + if (!try.mppe) { + error("MPPE required but peer negotiation failed"); + lcp_close(f->unit, "MPPE required but peer negotiation failed"); + } + } +#endif /* MPPE */ + if (go->deflate && len >= CILEN_DEFLATE + && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT) + && p[1] == CILEN_DEFLATE) { + no.deflate = 1; + /* + * Peer wants us to use a different code size or something. + * Stop asking for Deflate if we don't understand his suggestion. + */ + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_WORKS + || p[3] != DEFLATE_CHK_SEQUENCE) + try.deflate = 0; + else if (DEFLATE_SIZE(p[2]) < go->deflate_size) + try.deflate_size = DEFLATE_SIZE(p[2]); + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + if (go->deflate_correct && go->deflate_draft + && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT + && p[1] == CILEN_DEFLATE) { + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + } + + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + no.bsd_compress = 1; + /* + * Peer wants us to use a different number of bits + * or a different version. + */ + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION) + try.bsd_compress = 0; + else if (BSD_NBITS(p[2]) < go->bsd_bits) + try.bsd_bits = BSD_NBITS(p[2]); + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } + + /* + * Predictor-1 and 2 have no options, so they can't be Naked. + * + * There may be remaining options but we ignore them. + */ + + if (f->state != OPENED) + *go = try; + return 1; +} + +/* + * ccp_rejci - reject some of our suggested compression methods. + */ +static int +ccp_rejci(f, p, len) + fsm *f; + u_char *p; + int len; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options try; /* options to request next time */ + + try = *go; + + /* + * Cope with empty configure-rejects by ceasing to send + * configure-requests. + */ + if (len == 0 && all_rejected[f->unit]) + return -1; + +#ifdef MPPE + if (go->mppe && len >= CILEN_MPPE + && p[0] == CI_MPPE && p[1] == CILEN_MPPE) { + error("MPPE required but peer refused"); + lcp_close(f->unit, "MPPE required but peer refused"); + p += CILEN_MPPE; + len -= CILEN_MPPE; + } +#endif + if (go->deflate_correct && len >= CILEN_DEFLATE + && p[0] == CI_DEFLATE && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try.deflate_correct = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (go->deflate_draft && len >= CILEN_DEFLATE + && p[0] == CI_DEFLATE_DRAFT && p[1] == CILEN_DEFLATE) { + if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size) + || p[3] != DEFLATE_CHK_SEQUENCE) + return 0; /* Rej is bad */ + try.deflate_draft = 0; + p += CILEN_DEFLATE; + len -= CILEN_DEFLATE; + } + if (!try.deflate_correct && !try.deflate_draft) + try.deflate = 0; + if (go->bsd_compress && len >= CILEN_BSD_COMPRESS + && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) { + if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits)) + return 0; + try.bsd_compress = 0; + p += CILEN_BSD_COMPRESS; + len -= CILEN_BSD_COMPRESS; + } + if (go->predictor_1 && len >= CILEN_PREDICTOR_1 + && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) { + try.predictor_1 = 0; + p += CILEN_PREDICTOR_1; + len -= CILEN_PREDICTOR_1; + } + if (go->predictor_2 && len >= CILEN_PREDICTOR_2 + && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) { + try.predictor_2 = 0; + p += CILEN_PREDICTOR_2; + len -= CILEN_PREDICTOR_2; + } + + if (len != 0) + return 0; + + if (f->state != OPENED) + *go = try; + + return 1; +} + +/* + * ccp_reqci - processed a received configure-request. + * Returns CONFACK, CONFNAK or CONFREJ and the packet modified + * appropriately. + */ +static int +ccp_reqci(f, p, lenp, dont_nak) + fsm *f; + u_char *p; + int *lenp; + int dont_nak; +{ + ppp_pcb *pcb = &ppp_pcb_list[f->unit]; + int ret, newret, res; + u_char *p0, *retp; + int len, clen, type, nb; + ccp_options *ho = &ccp_hisoptions[f->unit]; + ccp_options *ao = &ccp_allowoptions[f->unit]; +#ifdef MPPE + bool rej_for_ci_mppe = 1; /* Are we rejecting based on a bad/missing */ + /* CI_MPPE, or due to other options? */ +#endif + + ret = CONFACK; + retp = p0 = p; + len = *lenp; + + memset(ho, 0, sizeof(ccp_options)); + ho->method = (len > 0)? p[0]: -1; + + while (len > 0) { + newret = CONFACK; + if (len < 2 || p[1] < 2 || p[1] > len) { + /* length is bad */ + clen = len; + newret = CONFREJ; + + } else { + type = p[0]; + clen = p[1]; + + switch (type) { +#ifdef MPPE + case CI_MPPE: + if (!ao->mppe || clen != CILEN_MPPE) { + newret = CONFREJ; + break; + } + MPPE_CI_TO_OPTS(&p[2], ho->mppe); + + /* Nak if anything unsupported or unknown are set. */ + if (ho->mppe & MPPE_OPT_UNSUPPORTED) { + newret = CONFNAK; + ho->mppe &= ~MPPE_OPT_UNSUPPORTED; + } + if (ho->mppe & MPPE_OPT_UNKNOWN) { + newret = CONFNAK; + ho->mppe &= ~MPPE_OPT_UNKNOWN; + } + + /* Check state opt */ + if (ho->mppe & MPPE_OPT_STATEFUL) { + /* + * We can Nak and request stateless, but it's a + * lot easier to just assume the peer will request + * it if he can do it; stateful mode is bad over + * the Internet -- which is where we expect MPPE. + */ + if (refuse_mppe_stateful) { + error("Refusing MPPE stateful mode offered by peer"); + newret = CONFREJ; + break; + } + } + + /* Find out which of {S,L} are set. */ + if ((ho->mppe & MPPE_OPT_128) + && (ho->mppe & MPPE_OPT_40)) { + /* Both are set, negotiate the strongest. */ + newret = CONFNAK; + if (ao->mppe & MPPE_OPT_128) + ho->mppe &= ~MPPE_OPT_40; + else if (ao->mppe & MPPE_OPT_40) + ho->mppe &= ~MPPE_OPT_128; + else { + newret = CONFREJ; + break; + } + } else if (ho->mppe & MPPE_OPT_128) { + if (!(ao->mppe & MPPE_OPT_128)) { + newret = CONFREJ; + break; + } + } else if (ho->mppe & MPPE_OPT_40) { + if (!(ao->mppe & MPPE_OPT_40)) { + newret = CONFREJ; + break; + } + } else { + /* Neither are set. */ + /* We cannot accept this. */ + newret = CONFNAK; + /* Give the peer our idea of what can be used, + so it can choose and confirm */ + ho->mppe = ao->mppe; + } + + /* rebuild the opts */ + MPPE_OPTS_TO_CI(ho->mppe, &p[2]); + if (newret == CONFACK) { + u_char opt_buf[CILEN_MPPE + MPPE_MAX_KEY_LEN]; + int mtu; + + MEMCPY(opt_buf, p, CILEN_MPPE); + MEMCPY(&opt_buf[CILEN_MPPE], mppe_send_key, + MPPE_MAX_KEY_LEN); + if (ccp_test(f->unit, opt_buf, + CILEN_MPPE + MPPE_MAX_KEY_LEN, 1) <= 0) { + /* This shouldn't happen, we've already tested it! */ + error("MPPE required, but kernel has no support."); + lcp_close(f->unit, "MPPE required but not available"); + newret = CONFREJ; + break; + } + /* + * We need to decrease the interface MTU by MPPE_PAD + * because MPPE frames **grow**. The kernel [must] + * allocate MPPE_PAD extra bytes in xmit buffers. + */ + mtu = netif_get_mtu(pcb); + if (mtu) + netif_set_mtu(pcb, mtu - MPPE_PAD); + else + newret = CONFREJ; + } + + /* + * We have accepted MPPE or are willing to negotiate + * MPPE parameters. A CONFREJ is due to subsequent + * (non-MPPE) processing. + */ + rej_for_ci_mppe = 0; + break; +#endif /* MPPE */ + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (!ao->deflate || clen != CILEN_DEFLATE + || (!ao->deflate_correct && type == CI_DEFLATE) + || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) { + newret = CONFREJ; + break; + } + + ho->deflate = 1; + ho->deflate_size = nb = DEFLATE_SIZE(p[2]); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL + || p[3] != DEFLATE_CHK_SEQUENCE + || nb > ao->deflate_size || nb < DEFLATE_MIN_WORKS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = DEFLATE_MAKE_OPT(ao->deflate_size); + p[3] = DEFLATE_CHK_SEQUENCE; + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do Deflate with the window + * size they want. If the window is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(f->unit, p, CILEN_DEFLATE, 1); + if (res > 0) + break; /* it's OK now */ + if (res < 0 || nb == DEFLATE_MIN_WORKS || dont_nak) { + newret = CONFREJ; + p[2] = DEFLATE_MAKE_OPT(ho->deflate_size); + break; + } + newret = CONFNAK; + --nb; + p[2] = DEFLATE_MAKE_OPT(nb); + } + } + break; + + case CI_BSD_COMPRESS: + if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) { + newret = CONFREJ; + break; + } + + ho->bsd_compress = 1; + ho->bsd_bits = nb = BSD_NBITS(p[2]); + if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION + || nb > ao->bsd_bits || nb < BSD_MIN_BITS) { + newret = CONFNAK; + if (!dont_nak) { + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits); + /* fall through to test this #bits below */ + } else + break; + } + + /* + * Check whether we can do BSD-Compress with the code + * size they want. If the code size is too big, reduce + * it until the kernel can cope and nak with that. + * We only check this for the first option. + */ + if (p == p0) { + for (;;) { + res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1); + if (res > 0) + break; + if (res < 0 || nb == BSD_MIN_BITS || dont_nak) { + newret = CONFREJ; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, + ho->bsd_bits); + break; + } + newret = CONFNAK; + --nb; + p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb); + } + } + break; + + case CI_PREDICTOR_1: + if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) { + newret = CONFREJ; + break; + } + + ho->predictor_1 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) { + newret = CONFREJ; + } + break; + + case CI_PREDICTOR_2: + if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) { + newret = CONFREJ; + break; + } + + ho->predictor_2 = 1; + if (p == p0 + && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) { + newret = CONFREJ; + } + break; + + default: + newret = CONFREJ; + } + } + + if (newret == CONFNAK && dont_nak) + newret = CONFREJ; + if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) { + /* we're returning this option */ + if (newret == CONFREJ && ret == CONFNAK) + retp = p0; + ret = newret; + if (p != retp) + MEMCPY(retp, p, clen); + retp += clen; + } + + p += clen; + len -= clen; + } + + if (ret != CONFACK) { + if (ret == CONFREJ && *lenp == retp - p0) + all_rejected[f->unit] = 1; + else + *lenp = retp - p0; + } +#ifdef MPPE + if (ret == CONFREJ && ao->mppe && rej_for_ci_mppe) { + error("MPPE required but peer negotiation failed"); + lcp_close(f->unit, "MPPE required but peer negotiation failed"); + } +#endif + return ret; +} + +/* + * Make a string name for a compression method (or 2). + */ +static char * +method_name(opt, opt2) + ccp_options *opt, *opt2; +{ + static char result[64]; + + if (!ANY_COMPRESS(*opt)) + return "(none)"; + switch (opt->method) { +#ifdef MPPE + case CI_MPPE: + { + char *p = result; + char *q = result + sizeof(result); /* 1 past result */ + + slprintf(p, q - p, "MPPE "); + p += 5; + if (opt->mppe & MPPE_OPT_128) { + slprintf(p, q - p, "128-bit "); + p += 8; + } + if (opt->mppe & MPPE_OPT_40) { + slprintf(p, q - p, "40-bit "); + p += 7; + } + if (opt->mppe & MPPE_OPT_STATEFUL) + slprintf(p, q - p, "stateful"); + else + slprintf(p, q - p, "stateless"); + + break; + } +#endif + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (opt2 != NULL && opt2->deflate_size != opt->deflate_size) + slprintf(result, sizeof(result), "Deflate%s (%d/%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size, opt2->deflate_size); + else + slprintf(result, sizeof(result), "Deflate%s (%d)", + (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""), + opt->deflate_size); + break; + case CI_BSD_COMPRESS: + if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits) + slprintf(result, sizeof(result), "BSD-Compress (%d/%d)", + opt->bsd_bits, opt2->bsd_bits); + else + slprintf(result, sizeof(result), "BSD-Compress (%d)", + opt->bsd_bits); + break; + case CI_PREDICTOR_1: + return "Predictor 1"; + case CI_PREDICTOR_2: + return "Predictor 2"; + default: + slprintf(result, sizeof(result), "Method %d", opt->method); + } + return result; +} + +/* + * CCP has come up - inform the kernel driver and log a message. + */ +static void +ccp_up(f) + fsm *f; +{ + ccp_options *go = &ccp_gotoptions[f->unit]; + ccp_options *ho = &ccp_hisoptions[f->unit]; + char method1[64]; + + ccp_flags_set(f->unit, 1, 1); + if (ANY_COMPRESS(*go)) { + if (ANY_COMPRESS(*ho)) { + if (go->method == ho->method) { + notice("%s compression enabled", method_name(go, ho)); + } else { + strlcpy(method1, method_name(go, NULL), sizeof(method1)); + notice("%s / %s compression enabled", + method1, method_name(ho, NULL)); + } + } else + notice("%s receive compression enabled", method_name(go, NULL)); + } else if (ANY_COMPRESS(*ho)) + notice("%s transmit compression enabled", method_name(ho, NULL)); +#ifdef MPPE + if (go->mppe) { + BZERO(mppe_recv_key, MPPE_MAX_KEY_LEN); + BZERO(mppe_send_key, MPPE_MAX_KEY_LEN); + continue_networks(f->unit); /* Bring up IP et al */ + } +#endif +} + +/* + * CCP has gone down - inform the kernel driver. + */ +static void +ccp_down(f) + fsm *f; +{ + if (ccp_localstate[f->unit] & RACK_PENDING) + UNTIMEOUT(ccp_rack_timeout, f); + ccp_localstate[f->unit] = 0; + ccp_flags_set(f->unit, 1, 0); +#ifdef MPPE + if (ccp_gotoptions[f->unit].mppe) { + ccp_gotoptions[f->unit].mppe = 0; + if (lcp_fsm[f->unit].state == OPENED) { + /* If LCP is not already going down, make sure it does. */ + error("MPPE disabled"); + lcp_close(f->unit, "MPPE disabled"); + } + } +#endif +} + +#if PRINTPKT_SUPPORT +/* + * Print the contents of a CCP packet. + */ +static char *ccp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", + NULL, NULL, NULL, NULL, NULL, NULL, + "ResetReq", "ResetAck", +}; + +static int +ccp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) (void *, char *, ...); + void *arg; +{ + u_char *p0, *optend; + int code, id, len; + int optlen; + + p0 = p; + if (plen < HEADERLEN) + return 0; + code = p[0]; + id = p[1]; + len = (p[2] << 8) + p[3]; + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *) + && ccp_codenames[code-1] != NULL) + printer(arg, " %s", ccp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + p += HEADERLEN; + + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print list of possible compression methods */ + while (len >= 2) { + code = p[0]; + optlen = p[1]; + if (optlen < 2 || optlen > len) + break; + printer(arg, " <"); + len -= optlen; + optend = p + optlen; + switch (code) { +#ifdef MPPE + case CI_MPPE: + if (optlen >= CILEN_MPPE) { + u_char mppe_opts; + + MPPE_CI_TO_OPTS(&p[2], mppe_opts); + printer(arg, "mppe %s %s %s %s %s %s%s", + (p[2] & MPPE_H_BIT)? "+H": "-H", + (p[5] & MPPE_M_BIT)? "+M": "-M", + (p[5] & MPPE_S_BIT)? "+S": "-S", + (p[5] & MPPE_L_BIT)? "+L": "-L", + (p[5] & MPPE_D_BIT)? "+D": "-D", + (p[5] & MPPE_C_BIT)? "+C": "-C", + (mppe_opts & MPPE_OPT_UNKNOWN)? " +U": ""); + if (mppe_opts & MPPE_OPT_UNKNOWN) + printer(arg, " (%.2x %.2x %.2x %.2x)", + p[2], p[3], p[4], p[5]); + p += CILEN_MPPE; + } + break; +#endif + case CI_DEFLATE: + case CI_DEFLATE_DRAFT: + if (optlen >= CILEN_DEFLATE) { + printer(arg, "deflate%s %d", + (code == CI_DEFLATE_DRAFT? "(old#)": ""), + DEFLATE_SIZE(p[2])); + if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL) + printer(arg, " method %d", DEFLATE_METHOD(p[2])); + if (p[3] != DEFLATE_CHK_SEQUENCE) + printer(arg, " check %d", p[3]); + p += CILEN_DEFLATE; + } + break; + case CI_BSD_COMPRESS: + if (optlen >= CILEN_BSD_COMPRESS) { + printer(arg, "bsd v%d %d", BSD_VERSION(p[2]), + BSD_NBITS(p[2])); + p += CILEN_BSD_COMPRESS; + } + break; + case CI_PREDICTOR_1: + if (optlen >= CILEN_PREDICTOR_1) { + printer(arg, "predictor 1"); + p += CILEN_PREDICTOR_1; + } + break; + case CI_PREDICTOR_2: + if (optlen >= CILEN_PREDICTOR_2) { + printer(arg, "predictor 2"); + p += CILEN_PREDICTOR_2; + } + break; + } + while (p < optend) + printer(arg, " %.2x", *p++); + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + print_string((char *)p, len, printer, arg); + p += len; + len = 0; + } + break; + } + + /* dump out the rest of the packet in hex */ + while (--len >= 0) + printer(arg, " %.2x", *p++); + + return p - p0; +} +#endif /* PRINTPKT_SUPPORT */ + +/* + * We have received a packet that the decompressor failed to + * decompress. Here we would expect to issue a reset-request, but + * Motorola has a patent on resetting the compressor as a result of + * detecting an error in the decompressed data after decompression. + * (See US patent 5,130,993; international patent publication number + * WO 91/10289; Australian patent 73296/91.) + * + * So we ask the kernel whether the error was detected after + * decompression; if it was, we take CCP down, thus disabling + * compression :-(, otherwise we issue the reset-request. + */ +static void +ccp_datainput(unit, pkt, len) + int unit; + u_char *pkt; + int len; +{ + fsm *f; + + f = &ccp_fsm[unit]; + if (f->state == OPENED) { + if (ccp_fatal_error(unit)) { + /* + * Disable compression by taking CCP down. + */ + error("Lost compression sync: disabling compression"); + ccp_close(unit, "Lost compression sync"); +#ifdef MPPE + /* + * If we were doing MPPE, we must also take the link down. + */ + if (ccp_gotoptions[unit].mppe) { + error("Too many MPPE errors, closing LCP"); + lcp_close(unit, "Too many MPPE errors"); + } +#endif + } else { + /* + * Send a reset-request to reset the peer's compressor. + * We don't do that if we are still waiting for an + * acknowledgement to a previous reset-request. + */ + if (!(ccp_localstate[f->unit] & RACK_PENDING)) { + fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] |= RACK_PENDING; + } else + ccp_localstate[f->unit] |= RREQ_REPEAT; + } + } +} + +/* + * Timeout waiting for reset-ack. + */ +static void +ccp_rack_timeout(arg) + void *arg; +{ + fsm *f = arg; + + if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) { + fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0); + TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT); + ccp_localstate[f->unit] &= ~RREQ_REPEAT; + } else + ccp_localstate[f->unit] &= ~RACK_PENDING; +} + +#endif /* PPP_SUPPORT && CCP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-md5.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-md5.c new file mode 100644 index 0000000..70b879a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-md5.c @@ -0,0 +1,124 @@ +/* + * chap-md5.c - New CHAP/MD5 implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap-md5.h" +#include "netif/ppp/magic.h" + +#if LWIP_INCLUDED_POLARSSL_MD5 +#include "netif/ppp/polarssl/md5.h" +#else +#include "polarssl/md5.h" +#endif + +#define MD5_HASH_SIZE 16 +#define MD5_MIN_CHALLENGE 16 +#define MD5_MAX_CHALLENGE 24 + +#if PPP_SERVER +static void chap_md5_generate_challenge(unsigned char *cp) { + int clen; + + clen = (int)(drand48() * (MD5_MAX_CHALLENGE - MD5_MIN_CHALLENGE)) + + MD5_MIN_CHALLENGE; + *cp++ = clen; + random_bytes(cp, clen); +} + +static int chap_md5_verify_response(int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + md5_context ctx; + unsigned char idbyte = id; + unsigned char hash[MD5_HASH_SIZE]; + int challenge_len, response_len; + LWIP_UNUSED_ARG(name); + + challenge_len = *challenge++; + response_len = *response++; + if (response_len == MD5_HASH_SIZE) { + /* Generate hash of ID, secret, challenge */ + md5_starts(&ctx); + md5_update(&ctx, &idbyte, 1); + md5_update(&ctx, (unsigned char*)secret, secret_len); + md5_update(&ctx, (unsigned char*)challenge, challenge_len); + md5_finish(&ctx, hash); + + /* Test if our hash matches the peer's response */ + if (memcmp(hash, response, MD5_HASH_SIZE) == 0) { + ppp_slprintf(message, message_space, "Access granted"); + return 1; + } + } + ppp_slprintf(message, message_space, "Access denied"); + return 0; +} +#endif /* PPP_SERVER */ + +static void chap_md5_make_response(unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + const unsigned char *private_) { + md5_context ctx; + unsigned char idbyte = id; + int challenge_len = *challenge++; + LWIP_UNUSED_ARG(our_name); + LWIP_UNUSED_ARG(private_); + + md5_starts(&ctx); + md5_update(&ctx, &idbyte, 1); + md5_update(&ctx, (u_char *)secret, secret_len); + md5_update(&ctx, (unsigned char *)challenge, challenge_len); + md5_finish(&ctx, &response[1]); + response[0] = MD5_HASH_SIZE; +} + +const struct chap_digest_type md5_digest = { + CHAP_MD5, /* code */ +#if PPP_SERVER + chap_md5_generate_challenge, + chap_md5_verify_response, +#endif /* PPP_SERVER */ + chap_md5_make_response, + NULL, /* check_success */ + NULL, /* handle_failure */ +}; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-new.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-new.c new file mode 100644 index 0000000..8432dac --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap-new.c @@ -0,0 +1,670 @@ +/* + * chap-new.c - New CHAP implementation. + * + * Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#if 0 /* UNUSED */ +#include "session.h" +#endif /* UNUSED */ + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap-md5.h" +#if MSCHAP_SUPPORT +#include "netif/ppp/chap_ms.h" +#endif + +/* Hook for a plugin to validate CHAP challenge */ +int (*chap_verify_hook)(const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) = NULL; + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t chap_option_list[] = { + { "chap-restart", o_int, &chap_timeout_time, + "Set timeout for CHAP", OPT_PRIO }, + { "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits, + "Set max #xmits for challenge", OPT_PRIO }, + { "chap-interval", o_int, &pcb->settings.chap_rechallenge_time, + "Set interval for rechallenge", OPT_PRIO }, + { NULL } +}; +#endif /* PPP_OPTIONS */ + + +/* Values for flags in chap_client_state and chap_server_state */ +#define LOWERUP 1 +#define AUTH_STARTED 2 +#define AUTH_DONE 4 +#define AUTH_FAILED 8 +#define TIMEOUT_PENDING 0x10 +#define CHALLENGE_VALID 0x20 + +/* + * Prototypes. + */ +static void chap_init(ppp_pcb *pcb); +static void chap_lowerup(ppp_pcb *pcb); +static void chap_lowerdown(ppp_pcb *pcb); +#if PPP_SERVER +static void chap_timeout(void *arg); +static void chap_generate_challenge(ppp_pcb *pcb); +static void chap_handle_response(ppp_pcb *pcb, int code, + unsigned char *pkt, int len); +static int chap_verify_response(const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space); +#endif /* PPP_SERVER */ +static void chap_respond(ppp_pcb *pcb, int id, + unsigned char *pkt, int len); +static void chap_handle_status(ppp_pcb *pcb, int code, int id, + unsigned char *pkt, int len); +static void chap_protrej(ppp_pcb *pcb); +static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen); +#if PRINTPKT_SUPPORT +static int chap_print_pkt(unsigned char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +/* List of digest types that we know about */ +static const struct chap_digest_type* const chap_digests[] = { + &md5_digest, +#if MSCHAP_SUPPORT + &chapms_digest, + &chapms2_digest, +#endif /* MSCHAP_SUPPORT */ + NULL +}; + +/* + * chap_init - reset to initial state. + */ +static void chap_init(ppp_pcb *pcb) { + + memset(&pcb->chap_client, 0, sizeof(chap_client_state)); +#if PPP_SERVER + memset(&pcb->chap_server, 0, sizeof(chap_server_state)); +#endif /* PPP_SERVER */ +} + +/* + * chap_lowerup - we can start doing stuff now. + */ +static void chap_lowerup(ppp_pcb *pcb) { + + pcb->chap_client.flags |= LOWERUP; +#if PPP_SERVER + pcb->chap_server.flags |= LOWERUP; + if (pcb->chap_server.flags & AUTH_STARTED) + chap_timeout(pcb); +#endif /* PPP_SERVER */ +} + +static void chap_lowerdown(ppp_pcb *pcb) { + + pcb->chap_client.flags = 0; +#if PPP_SERVER + if (pcb->chap_server.flags & TIMEOUT_PENDING) + UNTIMEOUT(chap_timeout, pcb); + pcb->chap_server.flags = 0; +#endif /* PPP_SERVER */ +} + +#if PPP_SERVER +/* + * chap_auth_peer - Start authenticating the peer. + * If the lower layer is already up, we start sending challenges, + * otherwise we wait for the lower layer to come up. + */ +void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) { + struct chap_server_state *ss = &pcb->chap_server; + const struct chap_digest_type *dp; + int i; + + if (pcb->chap_server.flags & AUTH_STARTED) { + ppp_error("CHAP: peer authentication already started!"); + return; + } + for (i = 0; (dp = chap_digests[i]) != NULL; ++i) + if (dp->code == digest_code) + break; + if (dp == NULL) + ppp_fatal("CHAP digest 0x%x requested but not available", + digest_code); + + pcb->chap_server.digest = dp; + pcb->chap_server.name = our_name; + /* Start with a random ID value */ + pcb->chap_server.id = (unsigned char)(drand48() * 256); + pcb->chap_server.flags |= AUTH_STARTED; + if (pcb->chap_server.flags & LOWERUP) + chap_timeout(ss); +} +#endif /* PPP_SERVER */ + +/* + * chap_auth_with_peer - Prepare to authenticate ourselves to the peer. + * There isn't much to do until we receive a challenge. + */ +void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) { + const struct chap_digest_type *dp; + int i; + + if(NULL == our_name) + return; + + if (pcb->chap_client.flags & AUTH_STARTED) { + ppp_error("CHAP: authentication with peer already started!"); + return; + } + for (i = 0; (dp = chap_digests[i]) != NULL; ++i) + if (dp->code == digest_code) + break; + + if (dp == NULL) + ppp_fatal("CHAP digest 0x%x requested but not available", + digest_code); + + pcb->chap_client.digest = dp; + pcb->chap_client.name = our_name; + pcb->chap_client.flags |= AUTH_STARTED; +} + +#if PPP_SERVER +/* + * chap_timeout - It's time to send another challenge to the peer. + * This could be either a retransmission of a previous challenge, + * or a new challenge to start re-authentication. + */ +static void chap_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + struct pbuf *p; + + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) { + pcb->chap_server.challenge_xmits = 0; + chap_generate_challenge(pcb); + pcb->chap_server.flags |= CHALLENGE_VALID; + } else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) { + pcb->chap_server.flags &= ~CHALLENGE_VALID; + pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED; + auth_peer_fail(pcb, PPP_CHAP); + return; + } + + p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen); + ppp_write(pcb, p); + ++pcb->chap_server.challenge_xmits; + pcb->chap_server.flags |= TIMEOUT_PENDING; + TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time); +} + +/* + * chap_generate_challenge - generate a challenge string and format + * the challenge packet in pcb->chap_server.challenge_pkt. + */ +static void chap_generate_challenge(ppp_pcb *pcb) { + int clen = 1, nlen, len; + unsigned char *p; + + p = pcb->chap_server.challenge; + MAKEHEADER(p, PPP_CHAP); + p += CHAP_HDRLEN; + pcb->chap_server.digest->generate_challenge(p); + clen = *p; + nlen = strlen(pcb->chap_server.name); + memcpy(p + 1 + clen, pcb->chap_server.name, nlen); + + len = CHAP_HDRLEN + 1 + clen + nlen; + pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len; + + p = pcb->chap_server.challenge + PPP_HDRLEN; + p[0] = CHAP_CHALLENGE; + p[1] = ++pcb->chap_server.id; + p[2] = len >> 8; + p[3] = len; +} + +/* + * chap_handle_response - check the response to our challenge. + */ +static void chap_handle_response(ppp_pcb *pcb, int id, + unsigned char *pkt, int len) { + int response_len, ok, mlen; + const unsigned char *response; + unsigned char *outp; + struct pbuf *p; + const char *name = NULL; /* initialized to shut gcc up */ + int (*verifier)(const char *, const char *, int, const struct chap_digest_type *, + const unsigned char *, const unsigned char *, char *, int); + char rname[MAXNAMELEN+1]; + + if ((pcb->chap_server.flags & LOWERUP) == 0) + return; + if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2) + return; + if (pcb->chap_server.flags & CHALLENGE_VALID) { + response = pkt; + GETCHAR(response_len, pkt); + len -= response_len + 1; /* length of name */ + name = (char *)pkt + response_len; + if (len < 0) + return; + + if (pcb->chap_server.flags & TIMEOUT_PENDING) { + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + UNTIMEOUT(chap_timeout, pcb); + } +#if PPP_REMOTENAME + if (pcb->settings.explicit_remote) { + name = pcb->remote_name; + } else +#endif /* PPP_REMOTENAME */ + { + /* Null terminate and clean remote name. */ + ppp_slprintf(rname, sizeof(rname), "%.*v", len, name); + name = rname; + } + + if (chap_verify_hook) + verifier = chap_verify_hook; + else + verifier = chap_verify_response; + ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest, + pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN, + response, pcb->chap_server.message, sizeof(pcb->chap_server.message)); +#if 0 /* UNUSED */ + if (!ok || !auth_number()) { +#endif /* UNUSED */ + if (!ok) { + pcb->chap_server.flags |= AUTH_FAILED; + ppp_warn("Peer %q failed CHAP authentication", name); + } + } else if ((pcb->chap_server.flags & AUTH_DONE) == 0) + return; + + /* send the response */ + mlen = strlen(pcb->chap_server.message); + len = CHAP_HDRLEN + mlen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (unsigned char *)p->payload; + MAKEHEADER(outp, PPP_CHAP); + + outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS; + outp[1] = id; + outp[2] = len >> 8; + outp[3] = len; + if (mlen > 0) + memcpy(outp + CHAP_HDRLEN, pcb->chap_server.message, mlen); + ppp_write(pcb, p); + + if (pcb->chap_server.flags & CHALLENGE_VALID) { + pcb->chap_server.flags &= ~CHALLENGE_VALID; + if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) { + +#if 0 /* UNUSED */ + /* + * Auth is OK, so now we need to check session restrictions + * to ensure everything is OK, but only if we used a + * plugin, and only if we're configured to check. This + * allows us to do PAM checks on PPP servers that + * authenticate against ActiveDirectory, and use AD for + * account info (like when using Winbind integrated with + * PAM). + */ + if (session_mgmt && + session_check(name, NULL, devnam, NULL) == 0) { + pcb->chap_server.flags |= AUTH_FAILED; + ppp_warn("Peer %q failed CHAP Session verification", name); + } +#endif /* UNUSED */ + + } + if (pcb->chap_server.flags & AUTH_FAILED) { + auth_peer_fail(pcb, PPP_CHAP); + } else { + if ((pcb->chap_server.flags & AUTH_DONE) == 0) + auth_peer_success(pcb, PPP_CHAP, + pcb->chap_server.digest->code, + name, strlen(name)); + if (pcb->settings.chap_rechallenge_time) { + pcb->chap_server.flags |= TIMEOUT_PENDING; + TIMEOUT(chap_timeout, pcb, + pcb->settings.chap_rechallenge_time); + } + } + pcb->chap_server.flags |= AUTH_DONE; + } +} + +/* + * chap_verify_response - check whether the peer's response matches + * what we think it should be. Returns 1 if it does (authentication + * succeeded), or 0 if it doesn't. + */ +static int chap_verify_response(const char *name, const char *ourname, int id, + const struct chap_digest_type *digest, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + int ok; + unsigned char secret[MAXSECRETLEN]; + int secret_len; + +/* FIXME: we need a way to check peer secret */ +#if 0 + /* Get the secret that the peer is supposed to know */ + if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) { + ppp_error("No CHAP secret found for authenticating %q", name); + return 0; + } +#else + /* only here to clean compiler warnings */ + LWIP_UNUSED_ARG(ourname); + secret_len = 0; +#endif /* 0 */ + ok = digest->verify_response(id, name, secret, secret_len, challenge, + response, message, message_space); + memset(secret, 0, sizeof(secret)); + + return ok; +} +#endif /* PPP_SERVER */ + +/* + * chap_respond - Generate and send a response to a challenge. + */ +static void chap_respond(ppp_pcb *pcb, int id, + unsigned char *pkt, int len) { + int clen, nlen; + int secret_len; + struct pbuf *p; + u_char *outp; + char rname[MAXNAMELEN+1]; + char secret[MAXSECRETLEN+1]; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED)) + return; /* not ready */ + if (len < 2 || len < pkt[0] + 1) + return; /* too short */ + clen = pkt[0]; + nlen = len - (clen + 1); + + /* Null terminate and clean remote name. */ + ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1); + +#if PPP_REMOTENAME + /* Microsoft doesn't send their name back in the PPP packet */ + if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0)) + strlcpy(rname, pcb->settings.remote_name, sizeof(rname)); +#endif /* PPP_REMOTENAME */ + + /* get secret for authenticating ourselves with the specified host */ + if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) { + secret_len = 0; /* assume null secret if can't find one */ + ppp_warn("No CHAP secret found for authenticating us to %q", rname); + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_CHAP); + outp += CHAP_HDRLEN; + + pcb->chap_client.digest->make_response(outp, id, pcb->chap_client.name, pkt, + secret, secret_len, pcb->chap_client.priv); + memset(secret, 0, secret_len); + + clen = *outp; + nlen = strlen(pcb->chap_client.name); + memcpy(outp + clen + 1, pcb->chap_client.name, nlen); + + outp = (u_char*)p->payload + PPP_HDRLEN; + len = CHAP_HDRLEN + clen + 1 + nlen; + outp[0] = CHAP_RESPONSE; + outp[1] = id; + outp[2] = len >> 8; + outp[3] = len; + + pbuf_realloc(p, PPP_HDRLEN + len); + ppp_write(pcb, p); +} + +static void chap_handle_status(ppp_pcb *pcb, int code, int id, + unsigned char *pkt, int len) { + const char *msg = NULL; + LWIP_UNUSED_ARG(id); + + if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP)) + != (AUTH_STARTED|LOWERUP)) + return; + pcb->chap_client.flags |= AUTH_DONE; + + if (code == CHAP_SUCCESS) { + /* used for MS-CHAP v2 mutual auth, yuck */ + if (pcb->chap_client.digest->check_success != NULL) { + if (!(*pcb->chap_client.digest->check_success)(pkt, len, pcb->chap_client.priv)) + code = CHAP_FAILURE; + } else + msg = "CHAP authentication succeeded"; + } else { + if (pcb->chap_client.digest->handle_failure != NULL) + (*pcb->chap_client.digest->handle_failure)(pkt, len); + else + msg = "CHAP authentication failed"; + } + if (msg) { + if (len > 0) + ppp_info("%s: %.*v", msg, len, pkt); + else + ppp_info("%s", msg); + } + if (code == CHAP_SUCCESS) + auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code); + else { + pcb->chap_client.flags |= AUTH_FAILED; + ppp_error("CHAP authentication failed"); + auth_withpeer_fail(pcb, PPP_CHAP); + } +} + +static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) { + unsigned char code, id; + int len; + + if (pktlen < CHAP_HDRLEN) + return; + GETCHAR(code, pkt); + GETCHAR(id, pkt); + GETSHORT(len, pkt); + if (len < CHAP_HDRLEN || len > pktlen) + return; + len -= CHAP_HDRLEN; + + switch (code) { + case CHAP_CHALLENGE: + chap_respond(pcb, id, pkt, len); + break; +#if PPP_SERVER + case CHAP_RESPONSE: + chap_handle_response(pcb, id, pkt, len); + break; +#endif /* PPP_SERVER */ + case CHAP_FAILURE: + case CHAP_SUCCESS: + chap_handle_status(pcb, code, id, pkt, len); + break; + default: + break; + } +} + +static void chap_protrej(ppp_pcb *pcb) { + +#if PPP_SERVER + if (pcb->chap_server.flags & TIMEOUT_PENDING) { + pcb->chap_server.flags &= ~TIMEOUT_PENDING; + UNTIMEOUT(chap_timeout, pcb); + } + if (pcb->chap_server.flags & AUTH_STARTED) { + pcb->chap_server.flags = 0; + auth_peer_fail(pcb, PPP_CHAP); + } +#endif /* PPP_SERVER */ + if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) { + pcb->chap_client.flags &= ~AUTH_STARTED; + ppp_error("CHAP authentication failed due to protocol-reject"); + auth_withpeer_fail(pcb, PPP_CHAP); + } +} + +#if PRINTPKT_SUPPORT +/* + * chap_print_pkt - print the contents of a CHAP packet. + */ +static const char *chap_code_names[] = { + "Challenge", "Response", "Success", "Failure" +}; + +static int chap_print_pkt(unsigned char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len; + int clen, nlen; + unsigned char x; + + if (plen < CHAP_HDRLEN) + return 0; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < CHAP_HDRLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)sizeof(chap_code_names) / (int)sizeof(char *)) + printer(arg, " %s", chap_code_names[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= CHAP_HDRLEN; + switch (code) { + case CHAP_CHALLENGE: + case CHAP_RESPONSE: + if (len < 1) + break; + clen = p[0]; + if (len < clen + 1) + break; + ++p; + nlen = len - clen - 1; + printer(arg, " <"); + for (; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, "%.2x", x); + } + printer(arg, ">, name = "); + ppp_print_string((char *)p, nlen, printer, arg); + break; + case CHAP_FAILURE: + case CHAP_SUCCESS: + printer(arg, " "); + ppp_print_string((char *)p, len, printer, arg); + break; + default: + for (clen = len; clen > 0; --clen) { + GETCHAR(x, p); + printer(arg, " %.2x", x); + } + /* no break */ + } + + return len + CHAP_HDRLEN; +} +#endif /* PRINTPKT_SUPPORT */ + +const struct protent chap_protent = { + PPP_CHAP, + chap_init, + chap_input, + chap_protrej, + chap_lowerup, + chap_lowerdown, + NULL, /* open */ + NULL, /* close */ +#if PRINTPKT_SUPPORT + chap_print_pkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, /* datainput */ + 1, /* enabled_flag */ +#if PRINTPKT_SUPPORT + "CHAP", /* name */ + NULL, /* data_name */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + chap_option_list, + NULL, /* check_options */ +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +#endif /* PPP_SUPPORT && CHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap_ms.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap_ms.c new file mode 100644 index 0000000..e0b67d4 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/chap_ms.c @@ -0,0 +1,913 @@ +/* + * chap_ms.c - Microsoft MS-CHAP compatible implementation. + * + * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997 + * + * Implemented LANManager type password response to MS-CHAP challenges. + * Now pppd provides both NT style and LANMan style blocks, and the + * prefered is set by option "ms-lanman". Default is to use NT. + * The hash text (StdText) was taken from Win95 RASAPI32.DLL. + * + * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80 + */ + +/* + * Modifications by Frank Cusack, frank@google.com, March 2002. + * + * Implemented MS-CHAPv2 functionality, heavily based on sample + * implementation in RFC 2759. Implemented MPPE functionality, + * heavily based on sample implementation in RFC 3079. + * + * Copyright (c) 2002 Google, Inc. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/chap-new.h" +#include "netif/ppp/chap_ms.h" +#include "netif/ppp/pppcrypt.h" +#include "netif/ppp/magic.h" + +#if LWIP_INCLUDED_POLARSSL_MD4 +#include "netif/ppp/polarssl/md4.h" +#else +#include "polarssl/md4.h" +#endif + +#if LWIP_INCLUDED_POLARSSL_SHA1 +#include "netif/ppp/polarssl/sha1.h" +#else +#include "polarssl/sha1.h" +#endif + +#if LWIP_INCLUDED_POLARSSL_DES +#include "netif/ppp/polarssl/des.h" +#else +#include "polarssl/des.h" +#endif + +#define SHA1_SIGNATURE_SIZE 20 + +static void ascii2unicode (char[], int, u_char[]); +static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]); +static void ChallengeResponse (u_char *, u_char *, u_char[24]); +static void ChapMS_NT (u_char *, char *, int, u_char[24]); +static void ChapMS2_NT (u_char *, u_char[16], char *, char *, int, + u_char[24]); +static void GenerateAuthenticatorResponsePlain + (char*, int, u_char[24], u_char[16], u_char *, + char *, u_char[41]); +#ifdef MSLANMAN +static void ChapMS_LANMan (u_char *, char *, int, u_char *); +#endif + +#ifdef MPPE +static void Set_Start_Key (u_char *, char *, int); +static void SetMasterKeys (char *, int, u_char[24], int); +#endif + +#ifdef MSLANMAN +bool ms_lanman = 0; /* Use LanMan password instead of NT */ + /* Has meaning only with MS-CHAP challenges */ +#endif + +#ifdef MPPE +u_char mppe_send_key[MPPE_MAX_KEY_LEN]; +u_char mppe_recv_key[MPPE_MAX_KEY_LEN]; +int mppe_keys_set = 0; /* Have the MPPE keys been set? */ + +#ifdef DEBUGMPPEKEY +/* For MPPE debug */ +/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */ +static char *mschap_challenge = NULL; +/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */ +static char *mschap2_peer_challenge = NULL; +#endif + +#include "netif/ppp/fsm.h" /* Need to poke MPPE options */ +#include "netif/ppp/ccp.h" +#include +#endif + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t chapms_option_list[] = { +#ifdef MSLANMAN + { "ms-lanman", o_bool, &ms_lanman, + "Use LanMan passwd when using MS-CHAP", 1 }, +#endif +#ifdef DEBUGMPPEKEY + { "mschap-challenge", o_string, &mschap_challenge, + "specify CHAP challenge" }, + { "mschap2-peer-challenge", o_string, &mschap2_peer_challenge, + "specify CHAP peer challenge" }, +#endif + { NULL } +}; +#endif /* PPP_OPTIONS */ + +#if PPP_SERVER +/* + * chapms_generate_challenge - generate a challenge for MS-CHAP. + * For MS-CHAP the challenge length is fixed at 8 bytes. + * The length goes in challenge[0] and the actual challenge starts + * at challenge[1]. + */ +static void chapms_generate_challenge(unsigned char *challenge) { + *challenge++ = 8; +#ifdef DEBUGMPPEKEY + if (mschap_challenge && strlen(mschap_challenge) == 8) + memcpy(challenge, mschap_challenge, 8); + else +#endif + random_bytes(challenge, 8); +} + +static void chapms2_generate_challenge(unsigned char *challenge) { + *challenge++ = 16; +#ifdef DEBUGMPPEKEY + if (mschap_challenge && strlen(mschap_challenge) == 16) + memcpy(challenge, mschap_challenge, 16); + else +#endif + random_bytes(challenge, 16); +} + +static int chapms_verify_response(int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + unsigned char md[MS_CHAP_RESPONSE_LEN]; + int diff; + int challenge_len, response_len; + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(name); + + challenge_len = *challenge++; /* skip length, is 8 */ + response_len = *response++; + if (response_len != MS_CHAP_RESPONSE_LEN) + goto bad; + +#ifndef MSLANMAN + if (!response[MS_CHAP_USENT]) { + /* Should really propagate this into the error packet. */ + ppp_notice("Peer request for LANMAN auth not supported"); + goto bad; + } +#endif + + /* Generate the expected response. */ + ChapMS((u_char *)challenge, (char *)secret, secret_len, md); + +#ifdef MSLANMAN + /* Determine which part of response to verify against */ + if (!response[MS_CHAP_USENT]) + diff = memcmp(&response[MS_CHAP_LANMANRESP], + &md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN); + else +#endif + diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP], + MS_CHAP_NTRESP_LEN); + + if (diff == 0) { + ppp_slprintf(message, message_space, "Access granted"); + return 1; + } + + bad: + /* See comments below for MS-CHAP V2 */ + ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0", + challenge_len, challenge); + return 0; +} + +static int chapms2_verify_response(int id, const char *name, + const unsigned char *secret, int secret_len, + const unsigned char *challenge, const unsigned char *response, + char *message, int message_space) { + unsigned char md[MS_CHAP2_RESPONSE_LEN]; + char saresponse[MS_AUTH_RESPONSE_LENGTH+1]; + int challenge_len, response_len; + LWIP_UNUSED_ARG(id); + + challenge_len = *challenge++; /* skip length, is 16 */ + response_len = *response++; + if (response_len != MS_CHAP2_RESPONSE_LEN) + goto bad; /* not even the right length */ + + /* Generate the expected response and our mutual auth. */ + ChapMS2((u_char*)challenge, (u_char*)&response[MS_CHAP2_PEER_CHALLENGE], (char*)name, + (char *)secret, secret_len, md, + (unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR); + + /* compare MDs and send the appropriate status */ + /* + * Per RFC 2759, success message must be formatted as + * "S= M=" + * where + * is the Authenticator Response (mutual auth) + * is a text message + * + * However, some versions of Windows (win98 tested) do not know + * about the M= part (required per RFC 2759) and flag + * it as an error (reported incorrectly as an encryption error + * to the user). Since the RFC requires it, and it can be + * useful information, we supply it if the peer is a conforming + * system. Luckily (?), win98 sets the Flags field to 0x04 + * (contrary to RFC requirements) so we can use that to + * distinguish between conforming and non-conforming systems. + * + * Special thanks to Alex Swiridov for + * help debugging this. + */ + if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP], + MS_CHAP2_NTRESP_LEN) == 0) { + if (response[MS_CHAP2_FLAGS]) + ppp_slprintf(message, message_space, "S=%s", saresponse); + else + ppp_slprintf(message, message_space, "S=%s M=%s", + saresponse, "Access granted"); + return 1; + } + + bad: + /* + * Failure message must be formatted as + * "E=e R=r C=c V=v M=m" + * where + * e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE) + * r = retry (we use 1, ok to retry) + * c = challenge to use for next response, we reuse previous + * v = Change Password version supported, we use 0 + * m = text message + * + * The M=m part is only for MS-CHAPv2. Neither win2k nor + * win98 (others untested) display the message to the user anyway. + * They also both ignore the E=e code. + * + * Note that it's safe to reuse the same challenge as we don't + * actually accept another response based on the error message + * (and no clients try to resend a response anyway). + * + * Basically, this whole bit is useless code, even the small + * implementation here is only because of overspecification. + */ + ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s", + challenge_len, challenge, "Access denied"); + return 0; +} +#endif /* PPP_SERVER */ + +static void chapms_make_response(unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + const unsigned char *private_) { + LWIP_UNUSED_ARG(id); + LWIP_UNUSED_ARG(our_name); + LWIP_UNUSED_ARG(private_); + challenge++; /* skip length, should be 8 */ + *response++ = MS_CHAP_RESPONSE_LEN; + ChapMS((u_char*)challenge, (char*)secret, secret_len, response); +} + +static void chapms2_make_response(unsigned char *response, int id, const char *our_name, + const unsigned char *challenge, const char *secret, int secret_len, + const unsigned char *private_) { + LWIP_UNUSED_ARG(id); + challenge++; /* skip length, should be 16 */ + *response++ = MS_CHAP2_RESPONSE_LEN; + ChapMS2((u_char*)challenge, +#ifdef DEBUGMPPEKEY + mschap2_peer_challenge, +#else + NULL, +#endif + (char*)our_name, (char*)secret, secret_len, response, (u_char*)private_, + MS_CHAP2_AUTHENTICATEE); +} + +static int chapms2_check_success(unsigned char *msg, int len, unsigned char *private_) { + if ((len < MS_AUTH_RESPONSE_LENGTH + 2) || + strncmp((char *)msg, "S=", 2) != 0) { + /* Packet does not start with "S=" */ + ppp_error("MS-CHAPv2 Success packet is badly formed."); + return 0; + } + msg += 2; + len -= 2; + if (len < MS_AUTH_RESPONSE_LENGTH + || memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) { + /* Authenticator Response did not match expected. */ + ppp_error("MS-CHAPv2 mutual authentication failed."); + return 0; + } + /* Authenticator Response matches. */ + msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */ + len -= MS_AUTH_RESPONSE_LENGTH; + if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) { + msg += 3; /* Eat the delimiter */ + } else if (len) { + /* Packet has extra text which does not begin " M=" */ + ppp_error("MS-CHAPv2 Success packet is badly formed."); + return 0; + } + return 1; +} + +static void chapms_handle_failure(unsigned char *inp, int len) { + int err; + const char *p; + char msg[64]; + + /* We want a null-terminated string for strxxx(). */ + len = LWIP_MIN(len, 63); + MEMCPY(msg, inp, len); + msg[len] = 0; + p = msg; + + /* + * Deal with MS-CHAP formatted failure messages; just print the + * M= part (if any). For MS-CHAP we're not really supposed + * to use M=, but it shouldn't hurt. See + * chapms[2]_verify_response. + */ + if (!strncmp(p, "E=", 2)) + err = strtol(p+2, NULL, 10); /* Remember the error code. */ + else + goto print_msg; /* Message is badly formatted. */ + + if (len && ((p = strstr(p, " M=")) != NULL)) { + /* M= field found. */ + p += 3; + } else { + /* No M=; use the error code. */ + switch (err) { + case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS: + p = "E=646 Restricted logon hours"; + break; + + case MS_CHAP_ERROR_ACCT_DISABLED: + p = "E=647 Account disabled"; + break; + + case MS_CHAP_ERROR_PASSWD_EXPIRED: + p = "E=648 Password expired"; + break; + + case MS_CHAP_ERROR_NO_DIALIN_PERMISSION: + p = "E=649 No dialin permission"; + break; + + case MS_CHAP_ERROR_AUTHENTICATION_FAILURE: + p = "E=691 Authentication failure"; + break; + + case MS_CHAP_ERROR_CHANGING_PASSWORD: + /* Should never see this, we don't support Change Password. */ + p = "E=709 Error changing password"; + break; + + default: + ppp_error("Unknown MS-CHAP authentication failure: %.*v", + len, inp); + return; + } + } +print_msg: + if (p != NULL) + ppp_error("MS-CHAP authentication failed: %v", p); +} + +static void ChallengeResponse(u_char *challenge, + u_char PasswordHash[MD4_SIGNATURE_SIZE], + u_char response[24]) { + u_char ZPasswordHash[21]; + des_context des; + u_char des_key[8]; + + BZERO(ZPasswordHash, sizeof(ZPasswordHash)); + MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE); + +#if 0 + dbglog("ChallengeResponse - ZPasswordHash %.*B", + sizeof(ZPasswordHash), ZPasswordHash); +#endif + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key); + des_setkey_enc(&des, des_key); + des_crypt_ecb(&des, challenge, response +0); + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key); + des_setkey_enc(&des, des_key); + des_crypt_ecb(&des, challenge, response +8); + + pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key); + des_setkey_enc(&des, des_key); + des_crypt_ecb(&des, challenge, response +16); + +#if 0 + dbglog("ChallengeResponse - response %.24B", response); +#endif +} + +void ChallengeHash(u_char PeerChallenge[16], u_char *rchallenge, + char *username, u_char Challenge[8]) { + sha1_context sha1Context; + u_char sha1Hash[SHA1_SIGNATURE_SIZE]; + char *user; + + /* remove domain from "domain\username" */ + if ((user = strrchr(username, '\\')) != NULL) + ++user; + else + user = username; + + sha1_starts(&sha1Context); + sha1_update(&sha1Context, PeerChallenge, 16); + sha1_update(&sha1Context, rchallenge, 16); + sha1_update(&sha1Context, (unsigned char *)user, strlen(user)); + sha1_finish(&sha1Context, sha1Hash); + + MEMCPY(Challenge, sha1Hash, 8); +} + +/* + * Convert the ASCII version of the password to Unicode. + * This implicitly supports 8-bit ISO8859/1 characters. + * This gives us the little-endian representation, which + * is assumed by all M$ CHAP RFCs. (Unicode byte ordering + * is machine-dependent.) + */ +static void ascii2unicode(char ascii[], int ascii_len, u_char unicode[]) { + int i; + + BZERO(unicode, ascii_len * 2); + for (i = 0; i < ascii_len; i++) + unicode[i * 2] = (u_char) ascii[i]; +} + +static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) { + md4_context md4Context; + + md4_starts(&md4Context); + md4_update(&md4Context, secret, secret_len); + md4_finish(&md4Context, hash); +} + +static void ChapMS_NT(u_char *rchallenge, char *secret, int secret_len, + u_char NTResponse[24]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + ChallengeResponse(rchallenge, PasswordHash, NTResponse); +} + +static void ChapMS2_NT(u_char *rchallenge, u_char PeerChallenge[16], char *username, + char *secret, int secret_len, u_char NTResponse[24]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char Challenge[8]; + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + /* Hash the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + + ChallengeResponse(Challenge, PasswordHash, NTResponse); +} + +#ifdef MSLANMAN +static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */ + +static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len, + unsigned char *response) { + int i; + u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */ + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + des_context des; + u_char des_key[8]; + + /* LANMan password is case insensitive */ + BZERO(UcasePassword, sizeof(UcasePassword)); + for (i = 0; i < secret_len; i++) + UcasePassword[i] = (u_char)toupper(secret[i]); + + pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key); + des_setkey_enc(&des, des_key); + des_crypt_ecb(&des, StdText, PasswordHash +0); + + pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key); + des_setkey_enc(&des, des_key); + des_crypt_ecb(&des, StdText, PasswordHash +8); + + ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]); +} +#endif + + +void GenerateAuthenticatorResponse(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], u_char PeerChallenge[16], + u_char *rchallenge, char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { + /* + * "Magic" constants used in response generation, from RFC 2759. + */ + u_char Magic1[39] = /* "Magic server to client signing constant" */ + { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, + 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, + 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; + u_char Magic2[41] = /* "Pad to make it do more than one iteration" */ + { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, + 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, + 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, + 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, + 0x6E }; + + int i; + sha1_context sha1Context; + u_char Digest[SHA1_SIGNATURE_SIZE]; + u_char Challenge[8]; + + sha1_starts(&sha1Context); + sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + sha1_update(&sha1Context, NTResponse, 24); + sha1_update(&sha1Context, Magic1, sizeof(Magic1)); + sha1_finish(&sha1Context, Digest); + + ChallengeHash(PeerChallenge, rchallenge, username, Challenge); + + sha1_starts(&sha1Context); + sha1_update(&sha1Context, Digest, sizeof(Digest)); + sha1_update(&sha1Context, Challenge, sizeof(Challenge)); + sha1_update(&sha1Context, Magic2, sizeof(Magic2)); + sha1_finish(&sha1Context, Digest); + + /* Convert to ASCII hex string. */ + for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++) + sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]); +} + + +static void GenerateAuthenticatorResponsePlain + (char *secret, int secret_len, + u_char NTResponse[24], u_char PeerChallenge[16], + u_char *rchallenge, char *username, + u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), + PasswordHashHash); + + GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge, + rchallenge, username, authResponse); +} + + +#ifdef MPPE +/* + * Set mppe_xxxx_key from the NTPasswordHashHash. + * RFC 2548 (RADIUS support) requires us to export this function (ugh). + */ +void mppe_set_keys(u_char *rchallenge, u_char PasswordHashHash[MD4_SIGNATURE_SIZE]) { + sha1_context sha1Context; + u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + + sha1_starts(&sha1Context); + sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + sha1_update(&sha1Context, rchallenge, 8); + sha1_finish(&sha1Context, Digest); + + /* Same key in both directions. */ + MEMCPY(mppe_send_key, Digest, sizeof(mppe_send_key)); + MEMCPY(mppe_recv_key, Digest, sizeof(mppe_recv_key)); + + mppe_keys_set = 1; +} + +/* + * Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079) + */ +static void Set_Start_Key(u_char *rchallenge, char *secret, int secret_len) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); + + mppe_set_keys(rchallenge, PasswordHashHash); +} + +/* + * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) + * + * This helper function used in the Winbind module, which gets the + * NTHashHash from the server. + */ +void mppe_set_keys2(u_char PasswordHashHash[MD4_SIGNATURE_SIZE], + u_char NTResponse[24], int IsServer) { + sha1_context sha1Context; + u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */ + + u_char SHApad1[40] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + u_char SHApad2[40] = + { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 }; + + /* "This is the MPPE Master Key" */ + u_char Magic1[27] = + { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 }; + /* "On the client side, this is the send key; " + "on the server side, it is the receive key." */ + u_char Magic2[84] = + { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e }; + /* "On the client side, this is the receive key; " + "on the server side, it is the send key." */ + u_char Magic3[84] = + { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e }; + u_char *s; + + sha1_starts(&sha1Context); + sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE); + sha1_update(&sha1Context, NTResponse, 24); + sha1_update(&sha1Context, Magic1, sizeof(Magic1)); + sha1_finish(&sha1Context, MasterKey); + + /* + * generate send key + */ + if (IsServer) + s = Magic3; + else + s = Magic2; + sha1_starts(&sha1Context); + sha1_update(&sha1Context, MasterKey, 16); + sha1_update(&sha1Context, SHApad1, sizeof(SHApad1)); + sha1_update(&sha1Context, s, 84); + sha1_update(&sha1Context, SHApad2, sizeof(SHApad2)); + sha1_finish(&sha1Context, Digest); + + MEMCPY(mppe_send_key, Digest, sizeof(mppe_send_key)); + + /* + * generate recv key + */ + if (IsServer) + s = Magic2; + else + s = Magic3; + sha1_starts(&sha1Context); + sha1_update(&sha1Context, MasterKey, 16); + sha1_update(&sha1Context, SHApad1, sizeof(SHApad1)); + sha1_update(&sha1Context, s, 84); + sha1_update(&sha1Context, SHApad2, sizeof(SHApad2)); + sha1_finish(&sha1Context, Digest); + + MEMCPY(mppe_recv_key, Digest, sizeof(mppe_recv_key)); + + mppe_keys_set = 1; +} + +/* + * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079) + */ +static void SetMasterKeys(char *secret, int secret_len, u_char NTResponse[24], int IsServer) { + u_char unicodePassword[MAX_NT_PASSWORD * 2]; + u_char PasswordHash[MD4_SIGNATURE_SIZE]; + u_char PasswordHashHash[MD4_SIGNATURE_SIZE]; + /* Hash (x2) the Unicode version of the secret (== password). */ + ascii2unicode(secret, secret_len, unicodePassword); + NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash); + NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash); + mppe_set_keys2(PasswordHashHash, NTResponse, IsServer); +} + +#endif /* MPPE */ + + +void ChapMS(u_char *rchallenge, char *secret, int secret_len, + unsigned char *response) { + BZERO(response, MS_CHAP_RESPONSE_LEN); + + ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]); + +#ifdef MSLANMAN + ChapMS_LANMan(rchallenge, secret, secret_len, + &response[MS_CHAP_LANMANRESP]); + + /* preferred method is set by option */ + response[MS_CHAP_USENT] = !ms_lanman; +#else + response[MS_CHAP_USENT] = 1; +#endif + +#ifdef MPPE + Set_Start_Key(rchallenge, secret, secret_len); +#endif +} + + +/* + * If PeerChallenge is NULL, one is generated and the PeerChallenge + * field of response is filled in. Call this way when generating a response. + * If PeerChallenge is supplied, it is copied into the PeerChallenge field. + * Call this way when verifying a response (or debugging). + * Do not call with PeerChallenge = response. + * + * The PeerChallenge field of response is then used for calculation of the + * Authenticator Response. + */ +void ChapMS2(u_char *rchallenge, u_char *PeerChallenge, + char *user, char *secret, int secret_len, unsigned char *response, + u_char authResponse[], int authenticator) { + /* ARGSUSED */ + u_char *p = &response[MS_CHAP2_PEER_CHALLENGE]; + int i; + LWIP_UNUSED_ARG(authenticator); + + BZERO(response, MS_CHAP2_RESPONSE_LEN); + + /* Generate the Peer-Challenge if requested, or copy it if supplied. */ + if (!PeerChallenge) + for (i = 0; i < MS_CHAP2_PEER_CHAL_LEN; i++) + *p++ = (u_char) (drand48() * 0xff); + else + MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge, + MS_CHAP2_PEER_CHAL_LEN); + + /* Generate the NT-Response */ + ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user, + secret, secret_len, &response[MS_CHAP2_NTRESP]); + + /* Generate the Authenticator Response. */ + GenerateAuthenticatorResponsePlain(secret, secret_len, + &response[MS_CHAP2_NTRESP], + &response[MS_CHAP2_PEER_CHALLENGE], + rchallenge, user, authResponse); + +#ifdef MPPE + SetMasterKeys(secret, secret_len, + &response[MS_CHAP2_NTRESP], authenticator); +#endif +} + +#ifdef MPPE +/* + * Set MPPE options from plugins. + */ +void set_mppe_enc_types(int policy, int types) { + /* Early exit for unknown policies. */ + if (policy != MPPE_ENC_POL_ENC_ALLOWED || + policy != MPPE_ENC_POL_ENC_REQUIRED) + return; + + /* Don't modify MPPE if it's optional and wasn't already configured. */ + if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe) + return; + + /* + * Disable undesirable encryption types. Note that we don't ENABLE + * any encryption types, to avoid overriding manual configuration. + */ + switch(types) { + case MPPE_ENC_TYPES_RC4_40: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */ + break; + case MPPE_ENC_TYPES_RC4_128: + ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */ + break; + default: + break; + } +} +#endif /* MPPE */ + +const struct chap_digest_type chapms_digest = { + CHAP_MICROSOFT, /* code */ +#if PPP_SERVER + chapms_generate_challenge, + chapms_verify_response, +#endif /* PPP_SERVER */ + chapms_make_response, + NULL, /* check_success */ + chapms_handle_failure, +}; + +const struct chap_digest_type chapms2_digest = { + CHAP_MICROSOFT_V2, /* code */ +#if PPP_SERVER + chapms2_generate_challenge, + chapms2_verify_response, +#endif /* PPP_SERVER */ + chapms2_make_response, + chapms2_check_success, + chapms_handle_failure, +}; + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/demand.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/demand.c new file mode 100644 index 0000000..f31a124 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/demand.c @@ -0,0 +1,467 @@ +/* + * demand.c - Support routines for demand-dialling. + * + * Copyright (c) 1996-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && DEMAND_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef PPP_FILTER +#include +#endif + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/lcp.h" + +char *frame; +int framelen; +int framemax; +int escape_flag; +int flush_flag; +int fcs; + +struct packet { + int length; + struct packet *next; + unsigned char data[1]; +}; + +struct packet *pend_q; +struct packet *pend_qtail; + +static int active_packet (unsigned char *, int); + +/* + * demand_conf - configure the interface for doing dial-on-demand. + */ +void +demand_conf() +{ + int i; + const struct protent *protp; + +/* framemax = lcp_allowoptions[0].mru; + if (framemax < PPP_MRU) */ + framemax = PPP_MRU; + framemax += PPP_HDRLEN + PPP_FCSLEN; + frame = malloc(framemax); + if (frame == NULL) + novm("demand frame"); + framelen = 0; + pend_q = NULL; + escape_flag = 0; + flush_flag = 0; + fcs = PPP_INITFCS; + + netif_set_mtu(pcb, LWIP_MIN(lcp_allowoptions[0].mru, PPP_MRU)); + if (ppp_send_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0 + || ppp_recv_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0) + fatal("Couldn't set up demand-dialled PPP interface: %m"); + +#ifdef PPP_FILTER + set_filters(&pass_filter, &active_filter); +#endif + + /* + * Call the demand_conf procedure for each protocol that's got one. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + ((*protp->demand_conf)(pcb)); +/* FIXME: find a way to die() here */ +#if 0 + if (!((*protp->demand_conf)(pcb))) + die(1); +#endif +} + + +/* + * demand_block - set each network protocol to block further packets. + */ +void +demand_block() +{ + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_QUEUE); + get_loop_output(); +} + +/* + * demand_discard - set each network protocol to discard packets + * with an error. + */ +void +demand_discard() +{ + struct packet *pkt, *nextpkt; + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_ERROR); + get_loop_output(); + + /* discard all saved packets */ + for (pkt = pend_q; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + free(pkt); + } + pend_q = NULL; + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; +} + +/* + * demand_unblock - set each enabled network protocol to pass packets. + */ +void +demand_unblock() +{ + int i; + const struct protent *protp; + + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->enabled_flag && protp->demand_conf != NULL) + sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_PASS); +} + +/* + * FCS lookup table as calculated by genfcstab. + */ +static u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +/* + * loop_chars - process characters received from the loopback. + * Calls loop_frame when a complete frame has been accumulated. + * Return value is 1 if we need to bring up the link, 0 otherwise. + */ +int +loop_chars(p, n) + unsigned char *p; + int n; +{ + int c, rv; + + rv = 0; + +/* check for synchronous connection... */ + + if ( (p[0] == 0xFF) && (p[1] == 0x03) ) { + rv = loop_frame(p,n); + return rv; + } + + for (; n > 0; --n) { + c = *p++; + if (c == PPP_FLAG) { + if (!escape_flag && !flush_flag + && framelen > 2 && fcs == PPP_GOODFCS) { + framelen -= 2; + if (loop_frame((unsigned char *)frame, framelen)) + rv = 1; + } + framelen = 0; + flush_flag = 0; + escape_flag = 0; + fcs = PPP_INITFCS; + continue; + } + if (flush_flag) + continue; + if (escape_flag) { + c ^= PPP_TRANS; + escape_flag = 0; + } else if (c == PPP_ESCAPE) { + escape_flag = 1; + continue; + } + if (framelen >= framemax) { + flush_flag = 1; + continue; + } + frame[framelen++] = c; + fcs = PPP_FCS(fcs, c); + } + return rv; +} + +/* + * loop_frame - given a frame obtained from the loopback, + * decide whether to bring up the link or not, and, if we want + * to transmit this frame later, put it on the pending queue. + * Return value is 1 if we need to bring up the link, 0 otherwise. + * We assume that the kernel driver has already applied the + * pass_filter, so we won't get packets it rejected. + * We apply the active_filter to see if we want this packet to + * bring up the link. + */ +int +loop_frame(frame, len) + unsigned char *frame; + int len; +{ + struct packet *pkt; + + /* dbglog("from loop: %P", frame, len); */ + if (len < PPP_HDRLEN) + return 0; + if ((PPP_PROTOCOL(frame) & 0x8000) != 0) + return 0; /* shouldn't get any of these anyway */ + if (!active_packet(frame, len)) + return 0; + + pkt = (struct packet *) malloc(sizeof(struct packet) + len); + if (pkt != NULL) { + pkt->length = len; + pkt->next = NULL; + memcpy(pkt->data, frame, len); + if (pend_q == NULL) + pend_q = pkt; + else + pend_qtail->next = pkt; + pend_qtail = pkt; + } + return 1; +} + +/* + * demand_rexmit - Resend all those frames which we got via the + * loopback, now that the real serial link is up. + */ +void +demand_rexmit(proto, newip) + int proto; + u32_t newip; +{ + struct packet *pkt, *prev, *nextpkt; + unsigned short checksum; + unsigned short pkt_checksum = 0; + unsigned iphdr; + struct timeval tv; + char cv = 0; + char ipstr[16]; + + prev = NULL; + pkt = pend_q; + pend_q = NULL; + tv.tv_sec = 1; + tv.tv_usec = 0; + select(0,NULL,NULL,NULL,&tv); /* Sleep for 1 Seconds */ + for (; pkt != NULL; pkt = nextpkt) { + nextpkt = pkt->next; + if (PPP_PROTOCOL(pkt->data) == proto) { + if ( (proto == PPP_IP) && newip ) { + /* Get old checksum */ + + iphdr = (pkt->data[4] & 15) << 2; + checksum = *((unsigned short *) (pkt->data+14)); + if (checksum == 0xFFFF) { + checksum = 0; + } + + + if (pkt->data[13] == 17) { + pkt_checksum = *((unsigned short *) (pkt->data+10+iphdr)); + if (pkt_checksum) { + cv = 1; + if (pkt_checksum == 0xFFFF) { + pkt_checksum = 0; + } + } + else { + cv = 0; + } + } + + if (pkt->data[13] == 6) { + pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr)); + cv = 1; + if (pkt_checksum == 0xFFFF) { + pkt_checksum = 0; + } + } + + /* Delete old Source-IP-Address */ + checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + /* Change Source-IP-Address */ + * ((u32_t *) (pkt->data + 16)) = newip; + + /* Add new Source-IP-Address */ + checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF; + pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF; + + /* Write new checksum */ + if (!checksum) { + checksum = 0xFFFF; + } + *((unsigned short *) (pkt->data+14)) = checksum; + if (pkt->data[13] == 6) { + *((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum; + } + if (cv && (pkt->data[13] == 17) ) { + *((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum; + } + + /* Log Packet */ + strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16)))); + if (pkt->data[13] == 1) { + syslog(LOG_INFO,"Open ICMP %s -> %s\n", + ipstr, + inet_ntoa(*( (struct in_addr *) (pkt->data+20)))); + } else { + syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n", + pkt->data[13] == 6 ? "TCP" : "UDP", + ipstr, + ntohs(*( (short *) (pkt->data+iphdr+4))), + inet_ntoa(*( (struct in_addr *) (pkt->data+20))), + ntohs(*( (short *) (pkt->data+iphdr+6)))); + } + } + output(pcb, pkt->data, pkt->length); + free(pkt); + } else { + if (prev == NULL) + pend_q = pkt; + else + prev->next = pkt; + prev = pkt; + } + } + pend_qtail = prev; + if (prev != NULL) + prev->next = NULL; +} + +/* + * Scan a packet to decide whether it is an "active" packet, + * that is, whether it is worth bringing up the link for. + */ +static int +active_packet(p, len) + unsigned char *p; + int len; +{ + int proto, i; + const struct protent *protp; + + if (len < PPP_HDRLEN) + return 0; + proto = PPP_PROTOCOL(p); +#ifdef PPP_FILTER + p[0] = 1; /* outbound packet indicator */ + if ((pass_filter.bf_len != 0 + && bpf_filter(pass_filter.bf_insns, p, len, len) == 0) + || (active_filter.bf_len != 0 + && bpf_filter(active_filter.bf_insns, p, len, len) == 0)) { + p[0] = 0xff; + return 0; + } + p[0] = 0xff; +#endif + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) { + if (!protp->enabled_flag) + return 0; + if (protp->active_pkt == NULL) + return 1; + return (*protp->active_pkt)(p, len); + } + } + return 0; /* not a supported protocol !!?? */ +} + +#endif /* PPP_SUPPORT && DEMAND_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eap.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eap.c new file mode 100644 index 0000000..5016eac --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eap.c @@ -0,0 +1,2455 @@ +/* + * eap.c - Extensible Authentication Protocol for PPP (RFC 2284) + * + * Copyright (c) 2001 by Sun Microsystems, Inc. + * All rights reserved. + * + * Non-exclusive rights to redistribute, modify, translate, and use + * this software in source and binary forms, in whole or in part, is + * hereby granted, provided that the above copyright notice is + * duplicated in any source form, and that neither the name of the + * copyright holder nor the author is used to endorse or promote + * products derived from this software. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Original version by James Carlson + * + * This implementation of EAP supports MD5-Challenge and SRP-SHA1 + * authentication styles. Note that support of MD5-Challenge is a + * requirement of RFC 2284, and that it's essentially just a + * reimplementation of regular RFC 1994 CHAP using EAP messages. + * + * As an authenticator ("server"), there are multiple phases for each + * style. In the first phase of each style, the unauthenticated peer + * name is queried using the EAP Identity request type. If the + * "remotename" option is used, then this phase is skipped, because + * the peer's name is presumed to be known. + * + * For MD5-Challenge, there are two phases, and the second phase + * consists of sending the challenge itself and handling the + * associated response. + * + * For SRP-SHA1, there are four phases. The second sends 's', 'N', + * and 'g'. The reply contains 'A'. The third sends 'B', and the + * reply contains 'M1'. The forth sends the 'M2' value. + * + * As an authenticatee ("client"), there's just a single phase -- + * responding to the queries generated by the peer. EAP is an + * authenticator-driven protocol. + * + * Based on draft-ietf-pppext-eap-srp-03.txt. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && EAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" + +#if LWIP_INCLUDED_POLARSSL_MD5 +#include "netif/ppp/polarssl/md5.h" +#else +#include "polarssl/md5.h" +#endif + +#include "netif/ppp/eap.h" + +#ifdef USE_SRP +#include +#include +#include +#include "netif/ppp/pppcrypt.h" +#endif /* USE_SRP */ + +#ifndef SHA_DIGESTSIZE +#define SHA_DIGESTSIZE 20 +#endif + +#ifdef USE_SRP +static char *pn_secret = NULL; /* Pseudonym generating secret */ +#endif + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t eap_option_list[] = { + { "eap-restart", o_int, &eap_states[0].es_server.ea_timeout, + "Set retransmit timeout for EAP Requests (server)" }, + { "eap-max-sreq", o_int, &eap_states[0].es_server.ea_maxrequests, + "Set max number of EAP Requests sent (server)" }, + { "eap-timeout", o_int, &eap_states[0].es_client.ea_timeout, + "Set time limit for peer EAP authentication" }, + { "eap-max-rreq", o_int, &eap_states[0].es_client.ea_maxrequests, + "Set max number of EAP Requests allows (client)" }, + { "eap-interval", o_int, &eap_states[0].es_rechallenge, + "Set interval for EAP rechallenge" }, +#ifdef USE_SRP + { "srp-interval", o_int, &eap_states[0].es_lwrechallenge, + "Set interval for SRP lightweight rechallenge" }, + { "srp-pn-secret", o_string, &pn_secret, + "Long term pseudonym generation secret" }, + { "srp-use-pseudonym", o_bool, &eap_states[0].es_usepseudo, + "Use pseudonym if offered one by server", 1 }, +#endif + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points. + */ +static void eap_init(ppp_pcb *pcb); +static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen); +static void eap_protrej(ppp_pcb *pcb); +static void eap_lowerup(ppp_pcb *pcb); +static void eap_lowerdown(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int eap_printpkt(u_char *inp, int inlen, + void (*)(void *arg, const char *fmt, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent eap_protent = { + PPP_EAP, /* protocol number */ + eap_init, /* initialization procedure */ + eap_input, /* process a received packet */ + eap_protrej, /* process a received protocol-reject */ + eap_lowerup, /* lower layer has gone up */ + eap_lowerdown, /* lower layer has gone down */ + NULL, /* open the protocol */ + NULL, /* close the protocol */ +#if PRINTPKT_SUPPORT + eap_printpkt, /* print a packet in readable form */ +#endif /* PRINTPKT_SUPPORT */ + NULL, /* process a received data packet */ + 1, /* protocol enabled */ +#if PRINTPKT_SUPPORT + "EAP", /* text name of protocol */ + NULL, /* text name of corresponding data protocol */ +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + eap_option_list, /* list of command-line options */ + NULL, /* check requested options; assign defaults */ +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, /* configure interface for demand-dial */ + NULL /* say whether to bring up link for this pkt */ +#endif /* DEMAND_SUPPORT */ +}; + +#ifdef USE_SRP +/* + * A well-known 2048 bit modulus. + */ +static const u_char wkmodulus[] = { + 0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B, + 0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F, + 0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07, + 0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50, + 0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED, + 0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D, + 0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D, + 0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50, + 0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0, + 0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3, + 0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8, + 0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8, + 0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA, + 0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74, + 0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7, + 0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B, + 0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16, + 0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81, + 0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A, + 0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48, + 0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D, + 0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA, + 0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78, + 0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6, + 0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29, + 0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8, + 0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82, + 0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6, + 0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4, + 0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75, + 0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2, + 0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73 +}; +#endif + +#if PPP_SERVER +/* Local forward declarations. */ +static void eap_server_timeout(void *arg); +#endif /* PPP_SERVER */ + +/* + * Convert EAP state code to printable string for debug. + */ +static const char * eap_state_name(enum eap_state_code esc) +{ + static const char *state_names[] = { EAP_STATES }; + + return (state_names[(int)esc]); +} + +/* + * eap_init - Initialize state for an EAP user. This is currently + * called once by main() during start-up. + */ +static void eap_init(ppp_pcb *pcb) { + + BZERO(&pcb->eap, sizeof(eap_state)); +#if PPP_SERVER + pcb->eap.es_server.ea_id = (u_char)(drand48() * 0x100); /* FIXME: use magic.c random function */ +#endif /* PPP_SERVER */ +} + +/* + * eap_client_timeout - Give up waiting for the peer to send any + * Request messages. + */ +static void eap_client_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (!eap_client_active(pcb)) + return; + + ppp_error("EAP: timeout waiting for Request from peer"); + auth_withpeer_fail(pcb, PPP_EAP); + pcb->eap.es_client.ea_state = eapBadAuth; +} + +/* + * eap_authwithpeer - Authenticate to our peer (behave as client). + * + * Start client state and wait for requests. This is called only + * after eap_lowerup. + */ +void eap_authwithpeer(ppp_pcb *pcb, const char *localname) { + + if(NULL == localname) + return; + + /* Save the peer name we're given */ + pcb->eap.es_client.ea_name = localname; + pcb->eap.es_client.ea_namelen = strlen(localname); + + pcb->eap.es_client.ea_state = eapListen; + + /* + * Start a timer so that if the other end just goes + * silent, we don't sit here waiting forever. + */ + if (pcb->settings.eap_req_time > 0) + TIMEOUT(eap_client_timeout, pcb, + pcb->settings.eap_req_time); +} + +#if PPP_SERVER +/* + * Format a standard EAP Failure message and send it to the peer. + * (Server operation) + */ +static void eap_send_failure(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_FAILURE, outp); + pcb->eap.es_server.ea_id++; + PUTCHAR(pcb->eap.es_server.ea_id, outp); + PUTSHORT(EAP_HEADERLEN, outp); + + ppp_write(pcb, p); + + pcb->eap.es_server.ea_state = eapBadAuth; + auth_peer_fail(pcb, PPP_EAP); +} + +/* + * Format a standard EAP Success message and send it to the peer. + * (Server operation) + */ +static void eap_send_success(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + EAP_HEADERLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_SUCCESS, outp); + pcb->eap.es_server.ea_id++; + PUTCHAR(pcb->eap.es_server.ea_id, outp); + PUTSHORT(EAP_HEADERLEN, outp); + + ppp_write(pcb, p); + + auth_peer_success(pcb, PPP_EAP, 0, + pcb->eap.es_server.ea_peer, pcb->eap.es_server.ea_peerlen); +} +#endif /* PPP_SERVER */ + +#ifdef USE_SRP +/* + * Set DES key according to pseudonym-generating secret and current + * date. + */ +static bool +pncrypt_setkey(int timeoffs) +{ + struct tm *tp; + char tbuf[9]; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + time_t reftime; + + if (pn_secret == NULL) + return (0); + reftime = time(NULL) + timeoffs; + tp = localtime(&reftime); + SHA1Init(&ctxt); + SHA1Update(&ctxt, pn_secret, strlen(pn_secret)); + strftime(tbuf, sizeof (tbuf), "%Y%m%d", tp); + SHA1Update(&ctxt, tbuf, strlen(tbuf)); + SHA1Final(dig, &ctxt); + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + return (DesSetkey(dig)); +} + +static char base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +struct b64state { + u32_t bs_bits; + int bs_offs; +}; + +static int +b64enc(bs, inp, inlen, outp) +struct b64state *bs; +u_char *inp; +int inlen; +u_char *outp; +{ + int outlen = 0; + + while (inlen > 0) { + bs->bs_bits = (bs->bs_bits << 8) | *inp++; + inlen--; + bs->bs_offs += 8; + if (bs->bs_offs >= 24) { + *outp++ = base64[(bs->bs_bits >> 18) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 12) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 6) & 0x3F]; + *outp++ = base64[bs->bs_bits & 0x3F]; + outlen += 4; + bs->bs_offs = 0; + bs->bs_bits = 0; + } + } + return (outlen); +} + +static int +b64flush(bs, outp) +struct b64state *bs; +u_char *outp; +{ + int outlen = 0; + + if (bs->bs_offs == 8) { + *outp++ = base64[(bs->bs_bits >> 2) & 0x3F]; + *outp++ = base64[(bs->bs_bits << 4) & 0x3F]; + outlen = 2; + } else if (bs->bs_offs == 16) { + *outp++ = base64[(bs->bs_bits >> 10) & 0x3F]; + *outp++ = base64[(bs->bs_bits >> 4) & 0x3F]; + *outp++ = base64[(bs->bs_bits << 2) & 0x3F]; + outlen = 3; + } + bs->bs_offs = 0; + bs->bs_bits = 0; + return (outlen); +} + +static int +b64dec(bs, inp, inlen, outp) +struct b64state *bs; +u_char *inp; +int inlen; +u_char *outp; +{ + int outlen = 0; + char *cp; + + while (inlen > 0) { + if ((cp = strchr(base64, *inp++)) == NULL) + break; + bs->bs_bits = (bs->bs_bits << 6) | (cp - base64); + inlen--; + bs->bs_offs += 6; + if (bs->bs_offs >= 8) { + *outp++ = bs->bs_bits >> (bs->bs_offs - 8); + outlen++; + bs->bs_offs -= 8; + } + } + return (outlen); +} +#endif /* USE_SRP */ + +#if PPP_SERVER +/* + * Assume that current waiting server state is complete and figure + * next state to use based on available authentication data. 'status' + * indicates if there was an error in handling the last query. It is + * 0 for success and non-zero for failure. + */ +static void eap_figure_next_state(ppp_pcb *pcb, int status) { +#ifdef USE_SRP + unsigned char secbuf[MAXWORDLEN], clear[8], *sp, *dp; + struct t_pw tpw; + struct t_confent *tce, mytce; + char *cp, *cp2; + struct t_server *ts; + int id, i, plen, toffs; + u_char vals[2]; + struct b64state bs; +#endif /* USE_SRP */ + + pcb->settings.eap_timeout_time = pcb->eap.es_savedtime; + switch (pcb->eap.es_server.ea_state) { + case eapBadAuth: + return; + + case eapIdentify: +#ifdef USE_SRP + /* Discard any previous session. */ + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0) { + pcb->eap.es_server.ea_state = eapBadAuth; + break; + } +#ifdef USE_SRP + /* If we've got a pseudonym, try to decode to real name. */ + if (pcb->eap.es_server.ea_peerlen > SRP_PSEUDO_LEN && + strncmp(pcb->eap.es_server.ea_peer, SRP_PSEUDO_ID, + SRP_PSEUDO_LEN) == 0 && + (pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN) * 3 / 4 < + sizeof (secbuf)) { + BZERO(&bs, sizeof (bs)); + plen = b64dec(&bs, + pcb->eap.es_server.ea_peer + SRP_PSEUDO_LEN, + pcb->eap.es_server.ea_peerlen - SRP_PSEUDO_LEN, + secbuf); + toffs = 0; + for (i = 0; i < 5; i++) { + pncrypt_setkey(toffs); + toffs -= 86400; + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + if (!DesDecrypt(secbuf, clear)) { + ppp_dbglog("no DES here; cannot decode " + "pseudonym"); + return; + } + id = *(unsigned char *)clear; + if (id + 1 <= plen && id + 9 > plen) + break; + } + if (plen % 8 == 0 && i < 5) { + /* + * Note that this is always shorter than the + * original stored string, so there's no need + * to realloc. + */ + if ((i = plen = *(unsigned char *)clear) > 7) + i = 7; + pcb->eap.es_server.ea_peerlen = plen; + dp = (unsigned char *)pcb->eap.es_server.ea_peer; + MEMCPY(dp, clear + 1, i); + plen -= i; + dp += i; + sp = secbuf + 8; + while (plen > 0) { + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesDecrypt(sp, dp); + sp += 8; + dp += 8; + plen -= 8; + } + pcb->eap.es_server.ea_peer[ + pcb->eap.es_server.ea_peerlen] = '\0'; + ppp_dbglog("decoded pseudonym to \"%.*q\"", + pcb->eap.es_server.ea_peerlen, + pcb->eap.es_server.ea_peer); + } else { + ppp_dbglog("failed to decode real name"); + /* Stay in eapIdentfy state; requery */ + break; + } + } + /* Look up user in secrets database. */ + if (get_srp_secret(pcb->eap.es_unit, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_name, (char *)secbuf, 1) != 0) { + /* Set up default in case SRP entry is bad */ + pcb->eap.es_server.ea_state = eapMD5Chall; + /* Get t_confent based on index in srp-secrets */ + id = strtol((char *)secbuf, &cp, 10); + if (*cp++ != ':' || id < 0) + break; + if (id == 0) { + mytce.index = 0; + mytce.modulus.data = (u_char *)wkmodulus; + mytce.modulus.len = sizeof (wkmodulus); + mytce.generator.data = (u_char *)"\002"; + mytce.generator.len = 1; + tce = &mytce; + } else if ((tce = gettcid(id)) != NULL) { + /* + * Client will have to verify this modulus/ + * generator combination, and that will take + * a while. Lengthen the timeout here. + */ + if (pcb->settings.eap_timeout_time > 0 && + pcb->settings.eap_timeout_time < 30) + pcb->settings.eap_timeout_time = 30; + } else { + break; + } + if ((cp2 = strchr(cp, ':')) == NULL) + break; + *cp2++ = '\0'; + tpw.pebuf.name = pcb->eap.es_server.ea_peer; + tpw.pebuf.password.len = t_fromb64((char *)tpw.pwbuf, + cp); + tpw.pebuf.password.data = tpw.pwbuf; + tpw.pebuf.salt.len = t_fromb64((char *)tpw.saltbuf, + cp2); + tpw.pebuf.salt.data = tpw.saltbuf; + if ((ts = t_serveropenraw(&tpw.pebuf, tce)) == NULL) + break; + pcb->eap.es_server.ea_session = (void *)ts; + pcb->eap.es_server.ea_state = eapSRP1; + vals[0] = pcb->eap.es_server.ea_id + 1; + vals[1] = EAPT_SRP; + t_serveraddexdata(ts, vals, 2); + /* Generate B; must call before t_servergetkey() */ + t_servergenexp(ts); + break; + } +#endif /* USE_SRP */ + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + + case eapSRP1: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status == 1) { + pcb->eap.es_server.ea_state = eapMD5Chall; + } else if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapSRP2; + } + break; + + case eapSRP2: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapSRP3; + } + break; + + case eapSRP3: + case eapSRP4: +#ifdef USE_SRP + ts = (struct t_server *)pcb->eap.es_server.ea_session; + if (ts != NULL && status != 0) { + t_serverclose(ts); + pcb->eap.es_server.ea_session = NULL; + pcb->eap.es_server.ea_skey = NULL; + } +#endif /* USE_SRP */ + if (status != 0 || pcb->eap.es_server.ea_session == NULL) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapOpen; + } + break; + + case eapMD5Chall: + if (status != 0) { + pcb->eap.es_server.ea_state = eapBadAuth; + } else { + pcb->eap.es_server.ea_state = eapOpen; + } + break; + + default: + pcb->eap.es_server.ea_state = eapBadAuth; + break; + } + if (pcb->eap.es_server.ea_state == eapBadAuth) + eap_send_failure(pcb); +} + +/* + * Format an EAP Request message and send it to the peer. Message + * type depends on current state. (Server operation) + */ +static void eap_send_request(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + u_char *lenloc; + u_char *ptr; + int outlen; + int challen; + const char *str; +#ifdef USE_SRP + struct t_server *ts; + u_char clear[8], cipher[8], dig[SHA_DIGESTSIZE], *optr, *cp; + int i, j; + struct b64state b64; + SHA1_CTX ctxt; +#endif /* USE_SRP */ + + /* Handle both initial auth and restart */ + if (pcb->eap.es_server.ea_state < eapIdentify && + pcb->eap.es_server.ea_state != eapInitial) { + pcb->eap.es_server.ea_state = eapIdentify; +#if PPP_REMOTENAME + if (pcb->settings.explicit_remote) { + /* + * If we already know the peer's + * unauthenticated name, then there's no + * reason to ask. Go to next state instead. + */ + pcb->eap.es_server.ea_peer = pcb->remote_name; + pcb->eap.es_server.ea_peerlen = strlen(pcb->remote_name); + eap_figure_next_state(pcb, 0); + } +#endif /* PPP_REMOTENAME */ + } + + if (pcb->settings.eap_max_transmits > 0 && + pcb->eap.es_server.ea_requests >= pcb->settings.eap_max_transmits) { + if (pcb->eap.es_server.ea_responses > 0) + ppp_error("EAP: too many Requests sent"); + else + ppp_error("EAP: no response to Requests"); + eap_send_failure(pcb); + return; + } + + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_REQUEST, outp); + PUTCHAR(pcb->eap.es_server.ea_id, outp); + lenloc = outp; + INCPTR(2, outp); + + switch (pcb->eap.es_server.ea_state) { + case eapIdentify: + PUTCHAR(EAPT_IDENTITY, outp); + str = "Name"; + challen = strlen(str); + MEMCPY(outp, str, challen); + INCPTR(challen, outp); + break; + + case eapMD5Chall: + PUTCHAR(EAPT_MD5CHAP, outp); + /* + * pick a random challenge length between + * EAP_MIN_CHALLENGE_LENGTH and EAP_MAX_CHALLENGE_LENGTH + */ + challen = (drand48() * + (EAP_MAX_CHALLENGE_LENGTH - EAP_MIN_CHALLENGE_LENGTH)) + + EAP_MIN_CHALLENGE_LENGTH; + PUTCHAR(challen, outp); + pcb->eap.es_challen = challen; + ptr = pcb->eap.es_challenge; + while (--challen >= 0) + *ptr++ = (u_char) (drand48() * 0x100); + MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen); + INCPTR(pcb->eap.es_challen, outp); + MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen); + INCPTR(pcb->eap.es_server.ea_namelen, outp); + break; + +#ifdef USE_SRP + case eapSRP1: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_CHALLENGE, outp); + + PUTCHAR(pcb->eap.es_server.ea_namelen, outp); + MEMCPY(outp, pcb->eap.es_server.ea_name, pcb->eap.es_server.ea_namelen); + INCPTR(pcb->eap.es_server.ea_namelen, outp); + + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + PUTCHAR(ts->s.len, outp); + MEMCPY(outp, ts->s.data, ts->s.len); + INCPTR(ts->s.len, outp); + + if (ts->g.len == 1 && ts->g.data[0] == 2) { + PUTCHAR(0, outp); + } else { + PUTCHAR(ts->g.len, outp); + MEMCPY(outp, ts->g.data, ts->g.len); + INCPTR(ts->g.len, outp); + } + + if (ts->n.len != sizeof (wkmodulus) || + BCMP(ts->n.data, wkmodulus, sizeof (wkmodulus)) != 0) { + MEMCPY(outp, ts->n.data, ts->n.len); + INCPTR(ts->n.len, outp); + } + break; + + case eapSRP2: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_SKEY, outp); + + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + MEMCPY(outp, ts->B.data, ts->B.len); + INCPTR(ts->B.len, outp); + break; + + case eapSRP3: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_SVALIDATOR, outp); + PUTLONG(SRPVAL_EBIT, outp); + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + MEMCPY(outp, t_serverresponse(ts), SHA_DIGESTSIZE); + INCPTR(SHA_DIGESTSIZE, outp); + + if (pncrypt_setkey(0)) { + /* Generate pseudonym */ + optr = outp; + cp = (unsigned char *)pcb->eap.es_server.ea_peer; + if ((j = i = pcb->eap.es_server.ea_peerlen) > 7) + j = 7; + clear[0] = i; + MEMCPY(clear + 1, cp, j); + i -= j; + cp += j; + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + if (!DesEncrypt(clear, cipher)) { + ppp_dbglog("no DES here; not generating pseudonym"); + break; + } + BZERO(&b64, sizeof (b64)); + outp++; /* space for pseudonym length */ + outp += b64enc(&b64, cipher, 8, outp); + while (i >= 8) { + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesEncrypt(cp, cipher); + outp += b64enc(&b64, cipher, 8, outp); + cp += 8; + i -= 8; + } + if (i > 0) { + MEMCPY(clear, cp, i); + cp += i; + while (i < 8) { + *cp++ = drand48() * 0x100; + i++; + } + /* FIXME: if we want to do SRP, we need to find a way to pass the PolarSSL des_context instead of using static memory */ + (void) DesEncrypt(clear, cipher); + outp += b64enc(&b64, cipher, 8, outp); + } + outp += b64flush(&b64, outp); + + /* Set length and pad out to next 20 octet boundary */ + i = outp - optr - 1; + *optr = i; + i %= SHA_DIGESTSIZE; + if (i != 0) { + while (i < SHA_DIGESTSIZE) { + *outp++ = drand48() * 0x100; + i++; + } + } + + /* Obscure the pseudonym with SHA1 hash */ + SHA1Init(&ctxt); + SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_peerlen); + while (optr < outp) { + SHA1Final(dig, &ctxt); + cp = dig; + while (cp < dig + SHA_DIGESTSIZE) + *optr++ ^= *cp++; + SHA1Init(&ctxt); + SHA1Update(&ctxt, &pcb->eap.es_server.ea_id, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, optr - SHA_DIGESTSIZE, + SHA_DIGESTSIZE); + } + } + break; + + case eapSRP4: + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_LWRECHALLENGE, outp); + challen = EAP_MIN_CHALLENGE_LENGTH + + ((EAP_MAX_CHALLENGE_LENGTH - EAP_MIN_CHALLENGE_LENGTH) * drand48()); + pcb->eap.es_challen = challen; + ptr = pcb->eap.es_challenge; + while (--challen >= 0) + *ptr++ = drand48() * 0x100; + MEMCPY(outp, pcb->eap.es_challenge, pcb->eap.es_challen); + INCPTR(pcb->eap.es_challen, outp); + break; +#endif /* USE_SRP */ + + default: + return; + } + + outlen = (outp - (unsigned char*)p->payload) - PPP_HDRLEN; + PUTSHORT(outlen, lenloc); + + pbuf_realloc(p, outlen + PPP_HDRLEN); + ppp_write(pcb, p); + + pcb->eap.es_server.ea_requests++; + + if (pcb->settings.eap_timeout_time > 0) + TIMEOUT(eap_server_timeout, pcb, pcb->settings.eap_timeout_time); +} + +/* + * eap_authpeer - Authenticate our peer (behave as server). + * + * Start server state and send first request. This is called only + * after eap_lowerup. + */ +void eap_authpeer(ppp_pcb *pcb, const char *localname) { + + /* Save the name we're given. */ + pcb->eap.es_server.ea_name = localname; + pcb->eap.es_server.ea_namelen = strlen(localname); + + pcb->eap.es_savedtime = pcb->settings.eap_timeout_time; + + /* Lower layer up yet? */ + if (pcb->eap.es_server.ea_state == eapInitial || + pcb->eap.es_server.ea_state == eapPending) { + pcb->eap.es_server.ea_state = eapPending; + return; + } + + pcb->eap.es_server.ea_state = eapPending; + + /* ID number not updated here intentionally; hashed into M1 */ + eap_send_request(pcb); +} + +/* + * eap_server_timeout - Retransmission timer for sending Requests + * expired. + */ +static void eap_server_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (!eap_server_active(pcb)) + return; + + /* EAP ID number must not change on timeout. */ + eap_send_request(pcb); +} + +/* + * When it's time to send rechallenge the peer, this timeout is + * called. Once the rechallenge is successful, the response handler + * will restart the timer. If it fails, then the link is dropped. + */ +static void eap_rechallenge(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->eap.es_server.ea_state != eapOpen && + pcb->eap.es_server.ea_state != eapSRP4) + return; + + pcb->eap.es_server.ea_requests = 0; + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); +} + +static void srp_lwrechallenge(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->eap.es_server.ea_state != eapOpen || + pcb->eap.es_server.ea_type != EAPT_SRP) + return; + + pcb->eap.es_server.ea_requests = 0; + pcb->eap.es_server.ea_state = eapSRP4; + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); +} +#endif /* PPP_SERVER */ + +/* + * eap_lowerup - The lower layer is now up. + * + * This is called before either eap_authpeer or eap_authwithpeer. See + * link_established() in auth.c. All that's necessary here is to + * return to closed state so that those two routines will do the right + * thing. + */ +static void eap_lowerup(ppp_pcb *pcb) { + + /* Discard any (possibly authenticated) peer name. */ +#if PPP_SERVER + if (pcb->eap.es_server.ea_peer != NULL +#if PPP_REMOTENAME + && pcb->eap.es_server.ea_peer != pcb->remote_name +#endif /* PPP_REMOTENAME */ + ) + free(pcb->eap.es_server.ea_peer); + pcb->eap.es_server.ea_peer = NULL; +#endif /* PPP_SERVER */ + if (pcb->eap.es_client.ea_peer != NULL) + free(pcb->eap.es_client.ea_peer); + pcb->eap.es_client.ea_peer = NULL; + + pcb->eap.es_client.ea_state = eapClosed; +#if PPP_SERVER + pcb->eap.es_server.ea_state = eapClosed; +#endif /* PPP_SERVER */ +} + +/* + * eap_lowerdown - The lower layer is now down. + * + * Cancel all timeouts and return to initial state. + */ +static void eap_lowerdown(ppp_pcb *pcb) { + + if (eap_client_active(pcb) && pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } +#if PPP_SERVER + if (eap_server_active(pcb)) { + if (pcb->settings.eap_timeout_time > 0) { + UNTIMEOUT(eap_server_timeout, pcb); + } + } else { + if ((pcb->eap.es_server.ea_state == eapOpen || + pcb->eap.es_server.ea_state == eapSRP4) && + pcb->eap.es_rechallenge > 0) { + UNTIMEOUT(eap_rechallenge, (void *)pcb); + } + if (pcb->eap.es_server.ea_state == eapOpen && + pcb->eap.es_lwrechallenge > 0) { + UNTIMEOUT(srp_lwrechallenge, (void *)pcb); + } + } + + pcb->eap.es_client.ea_state = pcb->eap.es_server.ea_state = eapInitial; + pcb->eap.es_client.ea_requests = pcb->eap.es_server.ea_requests = 0; +#endif /* PPP_SERVER */ +} + +/* + * eap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. If it does, it represents authentication + * failure. + */ +static void eap_protrej(ppp_pcb *pcb) { + + if (eap_client_active(pcb)) { + ppp_error("EAP authentication failed due to Protocol-Reject"); + auth_withpeer_fail(pcb, PPP_EAP); + } +#if PPP_SERVER + if (eap_server_active(pcb)) { + ppp_error("EAP authentication of peer failed on Protocol-Reject"); + auth_peer_fail(pcb, PPP_EAP); + } +#endif /* PPP_SERVER */ + eap_lowerdown(pcb); +} + +/* + * Format and send a regular EAP Response message. + */ +static void eap_send_response(ppp_pcb *pcb, u_char id, u_char typenum, u_char *str, int lenstr) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + sizeof (u_char) + lenstr; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(typenum, outp); + if (lenstr > 0) { + MEMCPY(outp, str, lenstr); + } + + ppp_write(pcb, p); +} + +/* + * Format and send an MD5-Challenge EAP Response message. + */ +static void eap_chap_response(ppp_pcb *pcb, u_char id, u_char *hash, const char *name, int namelen) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + MD5_SIGNATURE_SIZE + + namelen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_MD5CHAP, outp); + PUTCHAR(MD5_SIGNATURE_SIZE, outp); + MEMCPY(outp, hash, MD5_SIGNATURE_SIZE); + INCPTR(MD5_SIGNATURE_SIZE, outp); + if (namelen > 0) { + MEMCPY(outp, name, namelen); + } + + ppp_write(pcb, p); +} + +#ifdef USE_SRP +/* + * Format and send a SRP EAP Response message. + */ +static void +eap_srp_response(esp, id, subtypenum, str, lenstr) +eap_state *esp; +u_char id; +u_char subtypenum; +u_char *str; +int lenstr; +{ + ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit]; + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + lenstr; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(subtypenum, outp); + if (lenstr > 0) { + MEMCPY(outp, str, lenstr); + } + + ppp_write(pcb, p); +} + +/* + * Format and send a SRP EAP Client Validator Response message. + */ +static void +eap_srpval_response(esp, id, flags, str) +eap_state *esp; +u_char id; +u32_t flags; +u_char *str; +{ + ppp_pcb *pcb = &ppp_pcb_list[pcb->eap.es_unit]; + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char) + sizeof (u32_t) + + SHA_DIGESTSIZE; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_SRP, outp); + PUTCHAR(EAPSRP_CVALIDATOR, outp); + PUTLONG(flags, outp); + MEMCPY(outp, str, SHA_DIGESTSIZE); + + ppp_write(pcb, p); +} +#endif /* USE_SRP */ + +static void eap_send_nak(ppp_pcb *pcb, u_char id, u_char type) { + struct pbuf *p; + u_char *outp; + int msglen; + + msglen = EAP_HEADERLEN + 2 * sizeof (u_char); + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN + msglen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + + MAKEHEADER(outp, PPP_EAP); + + PUTCHAR(EAP_RESPONSE, outp); + PUTCHAR(id, outp); + pcb->eap.es_client.ea_id = id; + PUTSHORT(msglen, outp); + PUTCHAR(EAPT_NAK, outp); + PUTCHAR(type, outp); + + ppp_write(pcb, p); +} + +#ifdef USE_SRP +static char * +name_of_pn_file() +{ + char *user, *path, *file; + struct passwd *pw; + size_t pl; + static bool pnlogged = 0; + + pw = getpwuid(getuid()); + if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0) { + errno = EINVAL; + return (NULL); + } + file = _PATH_PSEUDONYM; + pl = strlen(user) + strlen(file) + 2; + path = malloc(pl); + if (path == NULL) + return (NULL); + (void) slprintf(path, pl, "%s/%s", user, file); + if (!pnlogged) { + ppp_dbglog("pseudonym file: %s", path); + pnlogged = 1; + } + return (path); +} + +static int +open_pn_file(modebits) +mode_t modebits; +{ + char *path; + int fd, err; + + if ((path = name_of_pn_file()) == NULL) + return (-1); + fd = open(path, modebits, S_IRUSR | S_IWUSR); + err = errno; + free(path); + errno = err; + return (fd); +} + +static void +remove_pn_file() +{ + char *path; + + if ((path = name_of_pn_file()) != NULL) { + (void) unlink(path); + (void) free(path); + } +} + +static void +write_pseudonym(esp, inp, len, id) +eap_state *esp; +u_char *inp; +int len, id; +{ + u_char val; + u_char *datp, *digp; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + int dsize, fd, olen = len; + + /* + * Do the decoding by working backwards. This eliminates the need + * to save the decoded output in a separate buffer. + */ + val = id; + while (len > 0) { + if ((dsize = len % SHA_DIGESTSIZE) == 0) + dsize = SHA_DIGESTSIZE; + len -= dsize; + datp = inp + len; + SHA1Init(&ctxt); + SHA1Update(&ctxt, &val, 1); + SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, SESSION_KEY_LEN); + if (len > 0) { + SHA1Update(&ctxt, datp, SHA_DIGESTSIZE); + } else { + SHA1Update(&ctxt, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + } + SHA1Final(dig, &ctxt); + for (digp = dig; digp < dig + SHA_DIGESTSIZE; digp++) + *datp++ ^= *digp; + } + + /* Now check that the result is sane */ + if (olen <= 0 || *inp + 1 > olen) { + ppp_dbglog("EAP: decoded pseudonym is unusable <%.*B>", olen, inp); + return; + } + + /* Save it away */ + fd = open_pn_file(O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + ppp_dbglog("EAP: error saving pseudonym: %m"); + return; + } + len = write(fd, inp + 1, *inp); + if (close(fd) != -1 && len == *inp) { + ppp_dbglog("EAP: saved pseudonym"); + pcb->eap.es_usedpseudo = 0; + } else { + ppp_dbglog("EAP: failed to save pseudonym"); + remove_pn_file(); + } +} +#endif /* USE_SRP */ + +/* + * eap_request - Receive EAP Request message (client mode). + */ +static void eap_request(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char typenum; + u_char vallen; + int secret_len; + char secret[MAXWORDLEN]; + char rhostname[256]; + md5_context mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; +#ifdef USE_SRP + struct t_client *tc; + struct t_num sval, gval, Nval, *Ap, Bval; + u_char vals[2]; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; + int fd; +#endif /* USE_SRP */ + + /* + * Note: we update es_client.ea_id *only if* a Response + * message is being generated. Otherwise, we leave it the + * same for duplicate detection purposes. + */ + + pcb->eap.es_client.ea_requests++; + if (pcb->settings.eap_allow_req != 0 && + pcb->eap.es_client.ea_requests > pcb->settings.eap_allow_req) { + ppp_info("EAP: received too many Request messages"); + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + auth_withpeer_fail(pcb, PPP_EAP); + return; + } + + if (len <= 0) { + ppp_error("EAP: empty Request message discarded"); + return; + } + + GETCHAR(typenum, inp); + len--; + + switch (typenum) { + case EAPT_IDENTITY: + if (len > 0) + ppp_info("EAP: Identity prompt \"%.*q\"", len, inp); +#ifdef USE_SRP + if (pcb->eap.es_usepseudo && + (pcb->eap.es_usedpseudo == 0 || + (pcb->eap.es_usedpseudo == 1 && + id == pcb->eap.es_client.ea_id))) { + pcb->eap.es_usedpseudo = 1; + /* Try to get a pseudonym */ + if ((fd = open_pn_file(O_RDONLY)) >= 0) { + strcpy(rhostname, SRP_PSEUDO_ID); + len = read(fd, rhostname + SRP_PSEUDO_LEN, + sizeof (rhostname) - SRP_PSEUDO_LEN); + /* XXX NAI unsupported */ + if (len > 0) { + eap_send_response(pcb, id, typenum, + rhostname, len + SRP_PSEUDO_LEN); + } + (void) close(fd); + if (len > 0) + break; + } + } + /* Stop using pseudonym now. */ + if (pcb->eap.es_usepseudo && pcb->eap.es_usedpseudo != 2) { + remove_pn_file(); + pcb->eap.es_usedpseudo = 2; + } +#endif /* USE_SRP */ + eap_send_response(pcb, id, typenum, (u_char*)pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + break; + + case EAPT_NOTIFICATION: + if (len > 0) + ppp_info("EAP: Notification \"%.*q\"", len, inp); + eap_send_response(pcb, id, typenum, NULL, 0); + break; + + case EAPT_NAK: + /* + * Avoid the temptation to send Response Nak in reply + * to Request Nak here. It can only lead to trouble. + */ + ppp_warn("EAP: unexpected Nak in Request; ignored"); + /* Return because we're waiting for something real. */ + return; + + case EAPT_MD5CHAP: + if (len < 1) { + ppp_error("EAP: received MD5-Challenge with no data"); + /* Bogus request; wait for something real. */ + return; + } + GETCHAR(vallen, inp); + len--; + if (vallen < 8 || vallen > len) { + ppp_error("EAP: MD5-Challenge with bad length %d (8..%d)", + vallen, len); + /* Try something better. */ + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + + /* Not so likely to happen. */ + if (vallen >= len + sizeof (rhostname)) { + ppp_dbglog("EAP: trimming really long peer name down"); + MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + MEMCPY(rhostname, inp + vallen, len - vallen); + rhostname[len - vallen] = '\0'; + } + +#if PPP_REMOTENAME + /* In case the remote doesn't give us his name. */ + if (pcb->settings.explicit_remote || + (pcb->settings.remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, pcb->settings.remote_name, sizeof (rhostname)); +#endif /* PPP_REMOTENAME */ + + /* + * Get the secret for authenticating ourselves with + * the specified host. + */ + if (!get_secret(pcb, pcb->eap.es_client.ea_name, + rhostname, secret, &secret_len, 0)) { + ppp_dbglog("EAP: no MD5 secret for auth to %q", rhostname); + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + md5_starts(&mdContext); + typenum = id; + md5_update(&mdContext, &typenum, 1); + md5_update(&mdContext, (u_char *)secret, secret_len); + BZERO(secret, sizeof (secret)); + md5_update(&mdContext, inp, vallen); + md5_finish(&mdContext, hash); + eap_chap_response(pcb, id, hash, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + break; + +#ifdef USE_SRP + case EAPT_SRP: + if (len < 1) { + ppp_error("EAP: received empty SRP Request"); + /* Bogus request; wait for something real. */ + return; + } + + /* Get subtype */ + GETCHAR(vallen, inp); + len--; + switch (vallen) { + case EAPSRP_CHALLENGE: + tc = NULL; + if (pcb->eap.es_client.ea_session != NULL) { + tc = (struct t_client *)pcb->eap.es_client. + ea_session; + /* + * If this is a new challenge, then start + * over with a new client session context. + * Otherwise, just resend last response. + */ + if (id != pcb->eap.es_client.ea_id) { + t_clientclose(tc); + pcb->eap.es_client.ea_session = NULL; + tc = NULL; + } + } + /* No session key just yet */ + pcb->eap.es_client.ea_skey = NULL; + if (tc == NULL) { + GETCHAR(vallen, inp); + len--; + if (vallen >= len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (name)"); + /* Ignore badly-formed messages */ + return; + } + MEMCPY(rhostname, inp, vallen); + rhostname[vallen] = '\0'; + INCPTR(vallen, inp); + len -= vallen; + + /* + * In case the remote doesn't give us his name, + * use configured name. + */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == 0)) { + strlcpy(rhostname, remote_name, + sizeof (rhostname)); + } + + if (pcb->eap.es_client.ea_peer != NULL) + free(pcb->eap.es_client.ea_peer); + pcb->eap.es_client.ea_peer = strdup(rhostname); + pcb->eap.es_client.ea_peerlen = strlen(rhostname); + + GETCHAR(vallen, inp); + len--; + if (vallen >= len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (s)"); + /* Ignore badly-formed messages */ + return; + } + sval.data = inp; + sval.len = vallen; + INCPTR(vallen, inp); + len -= vallen; + + GETCHAR(vallen, inp); + len--; + if (vallen > len) { + ppp_error("EAP: badly-formed SRP Challenge" + " (g)"); + /* Ignore badly-formed messages */ + return; + } + /* If no generator present, then use value 2 */ + if (vallen == 0) { + gval.data = (u_char *)"\002"; + gval.len = 1; + } else { + gval.data = inp; + gval.len = vallen; + } + INCPTR(vallen, inp); + len -= vallen; + + /* + * If no modulus present, then use well-known + * value. + */ + if (len == 0) { + Nval.data = (u_char *)wkmodulus; + Nval.len = sizeof (wkmodulus); + } else { + Nval.data = inp; + Nval.len = len; + } + tc = t_clientopen(pcb->eap.es_client.ea_name, + &Nval, &gval, &sval); + if (tc == NULL) { + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + pcb->eap.es_client.ea_session = (void *)tc; + + /* Add Challenge ID & type to verifier */ + vals[0] = id; + vals[1] = EAPT_SRP; + t_clientaddexdata(tc, vals, 2); + } + Ap = t_clientgenexp(tc); + eap_srp_response(esp, id, EAPSRP_CKEY, Ap->data, + Ap->len); + break; + + case EAPSRP_SKEY: + tc = (struct t_client *)pcb->eap.es_client.ea_session; + if (tc == NULL) { + ppp_warn("EAP: peer sent Subtype 2 without 1"); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + if (pcb->eap.es_client.ea_skey != NULL) { + /* + * ID number should not change here. Warn + * if it does (but otherwise ignore). + */ + if (id != pcb->eap.es_client.ea_id) { + ppp_warn("EAP: ID changed from %d to %d " + "in SRP Subtype 2 rexmit", + pcb->eap.es_client.ea_id, id); + } + } else { + if (get_srp_secret(pcb->eap.es_unit, + pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_peer, secret, 0) == 0) { + /* + * Can't work with this peer because + * the secret is missing. Just give + * up. + */ + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + Bval.data = inp; + Bval.len = len; + t_clientpasswd(tc, secret); + BZERO(secret, sizeof (secret)); + pcb->eap.es_client.ea_skey = + t_clientgetkey(tc, &Bval); + if (pcb->eap.es_client.ea_skey == NULL) { + /* Server is rogue; stop now */ + ppp_error("EAP: SRP server is rogue"); + goto client_failure; + } + } + eap_srpval_response(esp, id, SRPVAL_EBIT, + t_clientresponse(tc)); + break; + + case EAPSRP_SVALIDATOR: + tc = (struct t_client *)pcb->eap.es_client.ea_session; + if (tc == NULL || pcb->eap.es_client.ea_skey == NULL) { + ppp_warn("EAP: peer sent Subtype 3 without 1/2"); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + /* + * If we're already open, then this ought to be a + * duplicate. Otherwise, check that the server is + * who we think it is. + */ + if (pcb->eap.es_client.ea_state == eapOpen) { + if (id != pcb->eap.es_client.ea_id) { + ppp_warn("EAP: ID changed from %d to %d " + "in SRP Subtype 3 rexmit", + pcb->eap.es_client.ea_id, id); + } + } else { + len -= sizeof (u32_t) + SHA_DIGESTSIZE; + if (len < 0 || t_clientverify(tc, inp + + sizeof (u32_t)) != 0) { + ppp_error("EAP: SRP server verification " + "failed"); + goto client_failure; + } + GETLONG(pcb->eap.es_client.ea_keyflags, inp); + /* Save pseudonym if user wants it. */ + if (len > 0 && pcb->eap.es_usepseudo) { + INCPTR(SHA_DIGESTSIZE, inp); + write_pseudonym(esp, inp, len, id); + } + } + /* + * We've verified our peer. We're now mostly done, + * except for waiting on the regular EAP Success + * message. + */ + eap_srp_response(esp, id, EAPSRP_ACK, NULL, 0); + break; + + case EAPSRP_LWRECHALLENGE: + if (len < 4) { + ppp_warn("EAP: malformed Lightweight rechallenge"); + return; + } + SHA1Init(&ctxt); + vals[0] = id; + SHA1Update(&ctxt, vals, 1); + SHA1Update(&ctxt, pcb->eap.es_client.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, inp, len); + SHA1Update(&ctxt, pcb->eap.es_client.ea_name, + pcb->eap.es_client.ea_namelen); + SHA1Final(dig, &ctxt); + eap_srp_response(esp, id, EAPSRP_LWRECHALLENGE, dig, + SHA_DIGESTSIZE); + break; + + default: + ppp_error("EAP: unknown SRP Subtype %d", vallen); + eap_send_nak(pcb, id, EAPT_MD5CHAP); + break; + } + break; +#endif /* USE_SRP */ + + default: + ppp_info("EAP: unknown authentication type %d; Naking", typenum); + eap_send_nak(pcb, id, EAPT_SRP); + break; + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + TIMEOUT(eap_client_timeout, pcb, + pcb->settings.eap_req_time); + } + return; + +#ifdef USE_SRP +client_failure: + pcb->eap.es_client.ea_state = eapBadAuth; + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, (void *)esp); + } + pcb->eap.es_client.ea_session = NULL; + t_clientclose(tc); + auth_withpeer_fail(pcb, PPP_EAP); +#endif /* USE_SRP */ +} + +#if PPP_SERVER +/* FIXME: remove malloc() and free() */ +/* + * eap_response - Receive EAP Response message (server mode). + */ +static void eap_response(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char typenum; + u_char vallen; + int secret_len; + char secret[MAXSECRETLEN]; + char rhostname[256]; + md5_context mdContext; + u_char hash[MD5_SIGNATURE_SIZE]; +#ifdef USE_SRP + struct t_server *ts; + struct t_num A; + SHA1_CTX ctxt; + u_char dig[SHA_DIGESTSIZE]; +#endif /* USE_SRP */ + + if (pcb->eap.es_server.ea_id != id) { + ppp_dbglog("EAP: discarding Response %d; expected ID %d", id, + pcb->eap.es_server.ea_id); + return; + } + + pcb->eap.es_server.ea_responses++; + + if (len <= 0) { + ppp_error("EAP: empty Response message discarded"); + return; + } + + GETCHAR(typenum, inp); + len--; + + switch (typenum) { + case EAPT_IDENTITY: + if (pcb->eap.es_server.ea_state != eapIdentify) { + ppp_dbglog("EAP discarding unwanted Identify \"%.q\"", len, + inp); + break; + } + ppp_info("EAP: unauthenticated peer name \"%.*q\"", len, inp); + if (pcb->eap.es_server.ea_peer != NULL +#if PPP_REMOTENAME + && pcb->eap.es_server.ea_peer != pcb->remote_name +#endif /* PPP_REMOTENAME */ + ) + free(pcb->eap.es_server.ea_peer); + pcb->eap.es_server.ea_peer = (char*)malloc(len + 1); + if (pcb->eap.es_server.ea_peer == NULL) { + pcb->eap.es_server.ea_peerlen = 0; + eap_figure_next_state(pcb, 1); + break; + } + MEMCPY(pcb->eap.es_server.ea_peer, inp, len); + pcb->eap.es_server.ea_peer[len] = '\0'; + pcb->eap.es_server.ea_peerlen = len; + eap_figure_next_state(pcb, 0); + break; + + case EAPT_NOTIFICATION: + ppp_dbglog("EAP unexpected Notification; response discarded"); + break; + + case EAPT_NAK: + if (len < 1) { + ppp_info("EAP: Nak Response with no suggested protocol"); + eap_figure_next_state(pcb, 1); + break; + } + + GETCHAR(vallen, inp); + len--; + + if ( +#if PPP_REMOTENAME + !pcb->explicit_remote && +#endif /* PPP_REMOTENAME */ + pcb->eap.es_server.ea_state == eapIdentify){ + /* Peer cannot Nak Identify Request */ + eap_figure_next_state(pcb, 1); + break; + } + + switch (vallen) { + case EAPT_SRP: + /* Run through SRP validator selection again. */ + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + break; + + case EAPT_MD5CHAP: + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + + default: + ppp_dbglog("EAP: peer requesting unknown Type %d", vallen); + switch (pcb->eap.es_server.ea_state) { + case eapSRP1: + case eapSRP2: + case eapSRP3: + pcb->eap.es_server.ea_state = eapMD5Chall; + break; + case eapMD5Chall: + case eapSRP4: + pcb->eap.es_server.ea_state = eapIdentify; + eap_figure_next_state(pcb, 0); + break; + default: + break; + } + break; + } + break; + + case EAPT_MD5CHAP: + if (pcb->eap.es_server.ea_state != eapMD5Chall) { + ppp_error("EAP: unexpected MD5-Response"); + eap_figure_next_state(pcb, 1); + break; + } + if (len < 1) { + ppp_error("EAP: received MD5-Response with no data"); + eap_figure_next_state(pcb, 1); + break; + } + GETCHAR(vallen, inp); + len--; + if (vallen != 16 || vallen > len) { + ppp_error("EAP: MD5-Response with bad length %d", vallen); + eap_figure_next_state(pcb, 1); + break; + } + + /* Not so likely to happen. */ + if (vallen >= len + sizeof (rhostname)) { + ppp_dbglog("EAP: trimming really long peer name down"); + MEMCPY(rhostname, inp + vallen, sizeof (rhostname) - 1); + rhostname[sizeof (rhostname) - 1] = '\0'; + } else { + MEMCPY(rhostname, inp + vallen, len - vallen); + rhostname[len - vallen] = '\0'; + } + +#if PPP_REMOTENAME + /* In case the remote doesn't give us his name. */ + if (explicit_remote || + (remote_name[0] != '\0' && vallen == len)) + strlcpy(rhostname, remote_name, sizeof (rhostname)); +#endif /* PPP_REMOTENAME */ + + /* + * Get the secret for authenticating the specified + * host. + */ + if (!get_secret(pcb, rhostname, + pcb->eap.es_server.ea_name, secret, &secret_len, 1)) { + ppp_dbglog("EAP: no MD5 secret for auth of %q", rhostname); + eap_send_failure(pcb); + break; + } + md5_starts(&mdContext); + md5_update(&mdContext, &pcb->eap.es_server.ea_id, 1); + md5_update(&mdContext, (u_char *)secret, secret_len); + BZERO(secret, sizeof (secret)); + md5_update(&mdContext, pcb->eap.es_challenge, pcb->eap.es_challen); + md5_finish(&mdContext, hash); + if (BCMP(hash, inp, MD5_SIGNATURE_SIZE) != 0) { + eap_send_failure(pcb); + break; + } + pcb->eap.es_server.ea_type = EAPT_MD5CHAP; + eap_send_success(pcb); + eap_figure_next_state(pcb, 0); + if (pcb->eap.es_rechallenge != 0) + TIMEOUT(eap_rechallenge, pcb, pcb->eap.es_rechallenge); + break; + +#ifdef USE_SRP + case EAPT_SRP: + if (len < 1) { + ppp_error("EAP: empty SRP Response"); + eap_figure_next_state(pcb, 1); + break; + } + GETCHAR(typenum, inp); + len--; + switch (typenum) { + case EAPSRP_CKEY: + if (pcb->eap.es_server.ea_state != eapSRP1) { + ppp_error("EAP: unexpected SRP Subtype 1 Response"); + eap_figure_next_state(pcb, 1); + break; + } + A.data = inp; + A.len = len; + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + pcb->eap.es_server.ea_skey = t_servergetkey(ts, &A); + if (pcb->eap.es_server.ea_skey == NULL) { + /* Client's A value is bogus; terminate now */ + ppp_error("EAP: bogus A value from client"); + eap_send_failure(pcb); + } else { + eap_figure_next_state(pcb, 0); + } + break; + + case EAPSRP_CVALIDATOR: + if (pcb->eap.es_server.ea_state != eapSRP2) { + ppp_error("EAP: unexpected SRP Subtype 2 Response"); + eap_figure_next_state(pcb, 1); + break; + } + if (len < sizeof (u32_t) + SHA_DIGESTSIZE) { + ppp_error("EAP: M1 length %d < %d", len, + sizeof (u32_t) + SHA_DIGESTSIZE); + eap_figure_next_state(pcb, 1); + break; + } + GETLONG(pcb->eap.es_server.ea_keyflags, inp); + ts = (struct t_server *)pcb->eap.es_server.ea_session; + assert(ts != NULL); + if (t_serververify(ts, inp)) { + ppp_info("EAP: unable to validate client identity"); + eap_send_failure(pcb); + break; + } + eap_figure_next_state(pcb, 0); + break; + + case EAPSRP_ACK: + if (pcb->eap.es_server.ea_state != eapSRP3) { + ppp_error("EAP: unexpected SRP Subtype 3 Response"); + eap_send_failure(esp); + break; + } + pcb->eap.es_server.ea_type = EAPT_SRP; + eap_send_success(pcb, esp); + eap_figure_next_state(pcb, 0); + if (pcb->eap.es_rechallenge != 0) + TIMEOUT(eap_rechallenge, pcb, + pcb->eap.es_rechallenge); + if (pcb->eap.es_lwrechallenge != 0) + TIMEOUT(srp_lwrechallenge, pcb, + pcb->eap.es_lwrechallenge); + break; + + case EAPSRP_LWRECHALLENGE: + if (pcb->eap.es_server.ea_state != eapSRP4) { + ppp_info("EAP: unexpected SRP Subtype 4 Response"); + return; + } + if (len != SHA_DIGESTSIZE) { + ppp_error("EAP: bad Lightweight rechallenge " + "response"); + return; + } + SHA1Init(&ctxt); + vallen = id; + SHA1Update(&ctxt, &vallen, 1); + SHA1Update(&ctxt, pcb->eap.es_server.ea_skey, + SESSION_KEY_LEN); + SHA1Update(&ctxt, pcb->eap.es_challenge, pcb->eap.es_challen); + SHA1Update(&ctxt, pcb->eap.es_server.ea_peer, + pcb->eap.es_server.ea_peerlen); + SHA1Final(dig, &ctxt); + if (BCMP(dig, inp, SHA_DIGESTSIZE) != 0) { + ppp_error("EAP: failed Lightweight rechallenge"); + eap_send_failure(pcb); + break; + } + pcb->eap.es_server.ea_state = eapOpen; + if (pcb->eap.es_lwrechallenge != 0) + TIMEOUT(srp_lwrechallenge, esp, + pcb->eap.es_lwrechallenge); + break; + } + break; +#endif /* USE_SRP */ + + default: + /* This can't happen. */ + ppp_error("EAP: unknown Response type %d; ignored", typenum); + return; + } + + if (pcb->settings.eap_timeout_time > 0) { + UNTIMEOUT(eap_server_timeout, pcb); + } + + if (pcb->eap.es_server.ea_state != eapBadAuth && + pcb->eap.es_server.ea_state != eapOpen) { + pcb->eap.es_server.ea_id++; + eap_send_request(pcb); + } +} +#endif /* PPP_SERVER */ + +/* + * eap_success - Receive EAP Success message (client mode). + */ +static void eap_success(ppp_pcb *pcb, u_char *inp, int id, int len) { + LWIP_UNUSED_ARG(id); + + if (pcb->eap.es_client.ea_state != eapOpen && !eap_client_active(pcb)) { + ppp_dbglog("EAP unexpected success message in state %s (%d)", + eap_state_name(pcb->eap.es_client.ea_state), + pcb->eap.es_client.ea_state); + return; + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + + if (len > 0) { + /* This is odd. The spec doesn't allow for this. */ + PRINTMSG(inp, len); + } + + pcb->eap.es_client.ea_state = eapOpen; + auth_withpeer_success(pcb, PPP_EAP, 0); +} + +/* + * eap_failure - Receive EAP Failure message (client mode). + */ +static void eap_failure(ppp_pcb *pcb, u_char *inp, int id, int len) { + LWIP_UNUSED_ARG(id); + + if (!eap_client_active(pcb)) { + ppp_dbglog("EAP unexpected failure message in state %s (%d)", + eap_state_name(pcb->eap.es_client.ea_state), + pcb->eap.es_client.ea_state); + } + + if (pcb->settings.eap_req_time > 0) { + UNTIMEOUT(eap_client_timeout, pcb); + } + + if (len > 0) { + /* This is odd. The spec doesn't allow for this. */ + PRINTMSG(inp, len); + } + + pcb->eap.es_client.ea_state = eapBadAuth; + + ppp_error("EAP: peer reports authentication failure"); + auth_withpeer_fail(pcb, PPP_EAP); +} + +/* + * eap_input - Handle received EAP message. + */ +static void eap_input(ppp_pcb *pcb, u_char *inp, int inlen) { + u_char code, id; + int len; + + /* + * Parse header (code, id and length). If packet too short, + * drop it. + */ + if (inlen < EAP_HEADERLEN) { + ppp_error("EAP: packet too short: %d < %d", inlen, EAP_HEADERLEN); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < EAP_HEADERLEN || len > inlen) { + ppp_error("EAP: packet has illegal length field %d (%d..%d)", len, + EAP_HEADERLEN, inlen); + return; + } + len -= EAP_HEADERLEN; + + /* Dispatch based on message code */ + switch (code) { + case EAP_REQUEST: + eap_request(pcb, inp, id, len); + break; + +#if PPP_SERVER + case EAP_RESPONSE: + eap_response(pcb, inp, id, len); + break; +#endif /* PPP_SERVER */ + + case EAP_SUCCESS: + eap_success(pcb, inp, id, len); + break; + + case EAP_FAILURE: + eap_failure(pcb, inp, id, len); + break; + + default: /* XXX Need code reject */ + /* Note: it's not legal to send EAP Nak here. */ + ppp_warn("EAP: unknown code %d received", code); + break; + } +} + +#if PRINTPKT_SUPPORT +/* + * eap_printpkt - print the contents of an EAP packet. + */ +static const char *eap_codenames[] = { + "Request", "Response", "Success", "Failure" +}; + +static const char *eap_typenames[] = { + "Identity", "Notification", "Nak", "MD5-Challenge", + "OTP", "Generic-Token", NULL, NULL, + "RSA", "DSS", "KEA", "KEA-Validate", + "TLS", "Defender", "Windows 2000", "Arcot", + "Cisco", "Nokia", "SRP" +}; + +static int eap_printpkt(u_char *inp, int inlen, void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, rtype, vallen; + u_char *pstart; + u32_t uval; + + if (inlen < EAP_HEADERLEN) + return (0); + pstart = inp; + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < EAP_HEADERLEN || len > inlen) + return (0); + + if (code >= 1 && code <= (int)sizeof(eap_codenames) / (int)sizeof(char *)) + printer(arg, " %s", eap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= EAP_HEADERLEN; + switch (code) { + case EAP_REQUEST: + if (len < 1) { + printer(arg, " "); + break; + } + GETCHAR(rtype, inp); + len--; + if (rtype >= 1 && + rtype <= (int)sizeof (eap_typenames) / (int)sizeof (char *)) + printer(arg, " %s", eap_typenames[rtype-1]); + else + printer(arg, " type=0x%x", rtype); + switch (rtype) { + case EAPT_IDENTITY: + case EAPT_NOTIFICATION: + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_MD5CHAP: + if (len <= 0) + break; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_SRP: + if (len < 3) + goto truncated; + GETCHAR(vallen, inp); + len--; + printer(arg, "-%d", vallen); + switch (vallen) { + case EAPSRP_CHALLENGE: + GETCHAR(vallen, inp); + len--; + if (vallen >= len) + goto truncated; + if (vallen > 0) { + printer(arg, " "); + } else { + printer(arg, " "); + } + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen >= len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + if (vallen == 0) { + printer(arg, " "); + } else { + printer(arg, " ", vallen, inp); + } + INCPTR(vallen, inp); + len -= vallen; + if (len == 0) { + printer(arg, " "); + } else { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_SKEY: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_SVALIDATOR: + if (len < (int)sizeof (u32_t)) + break; + GETLONG(uval, inp); + len -= sizeof (u32_t); + if (uval & SRPVAL_EBIT) { + printer(arg, " E"); + uval &= ~SRPVAL_EBIT; + } + if (uval != 0) { + printer(arg, " f<%X>", uval); + } + if ((vallen = len) > SHA_DIGESTSIZE) + vallen = SHA_DIGESTSIZE; + printer(arg, " ", len, inp, + len < SHA_DIGESTSIZE ? "?" : ""); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + default: + break; + } + break; + default: + break; + } + break; + + case EAP_RESPONSE: + if (len < 1) + break; + GETCHAR(rtype, inp); + len--; + if (rtype >= 1 && + rtype <= (int)sizeof (eap_typenames) / (int)sizeof (char *)) + printer(arg, " %s", eap_typenames[rtype-1]); + else + printer(arg, " type=0x%x", rtype); + switch (rtype) { + case EAPT_IDENTITY: + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } + break; + + case EAPT_NAK: + if (len <= 0) { + printer(arg, " "); + break; + } + GETCHAR(rtype, inp); + len--; + printer(arg, " = 1 && + rtype < (int)sizeof (eap_typenames) / (int)sizeof (char *)) + printer(arg, " (%s)", eap_typenames[rtype-1]); + printer(arg, ">"); + break; + + case EAPT_MD5CHAP: + if (len <= 0) { + printer(arg, " "); + break; + } + GETCHAR(vallen, inp); + len--; + if (vallen > len) + goto truncated; + printer(arg, " ", vallen, inp); + INCPTR(vallen, inp); + len -= vallen; + if (len > 0) { + printer(arg, " "); + INCPTR(len, inp); + len = 0; + } else { + printer(arg, " "); + } + break; + + case EAPT_SRP: + if (len < 1) + goto truncated; + GETCHAR(vallen, inp); + len--; + printer(arg, "-%d", vallen); + switch (vallen) { + case EAPSRP_CKEY: + printer(arg, " ", len, inp); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_CVALIDATOR: + if (len < (int)sizeof (u32_t)) + break; + GETLONG(uval, inp); + len -= sizeof (u32_t); + if (uval & SRPVAL_EBIT) { + printer(arg, " E"); + uval &= ~SRPVAL_EBIT; + } + if (uval != 0) { + printer(arg, " f<%X>", uval); + } + printer(arg, " ", len, inp, + len == SHA_DIGESTSIZE ? "" : "?"); + INCPTR(len, inp); + len = 0; + break; + + case EAPSRP_ACK: + break; + + case EAPSRP_LWRECHALLENGE: + printer(arg, " ", len, inp, + len == SHA_DIGESTSIZE ? "" : "?"); + if ((vallen = len) > SHA_DIGESTSIZE) + vallen = SHA_DIGESTSIZE; + INCPTR(vallen, inp); + len -= vallen; + break; + default: + break; + } + break; + default: + break; + } + break; + + case EAP_SUCCESS: /* No payload expected for these! */ + case EAP_FAILURE: + default: + break; + + truncated: + printer(arg, " "); + break; + } + + if (len > 8) + printer(arg, "%8B...", inp); + else if (len > 0) + printer(arg, "%.*B", len, inp); + INCPTR(len, inp); + + return (inp - pstart); +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && EAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ecp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ecp.c new file mode 100644 index 0000000..728fb5f --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ecp.c @@ -0,0 +1,188 @@ +/* + * ecp.c - PPP Encryption Control Protocol. + * + * Copyright (c) 2002 Google, Inc. + * 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Derived from ccp.c, which is: + * + * Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ecp.h" + +#if PPP_OPTIONS +static option_t ecp_option_list[] = { + { "noecp", o_bool, &ecp_protent.enabled_flag, + "Disable ECP negotiation" }, + { "-ecp", o_bool, &ecp_protent.enabled_flag, + "Disable ECP negotiation", OPT_ALIAS }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ecp_init (int unit); +/* +static void ecp_open (int unit); +static void ecp_close (int unit, char *); +static void ecp_lowerup (int unit); +static void ecp_lowerdown (int); +static void ecp_input (int unit, u_char *pkt, int len); +static void ecp_protrej (int unit); +*/ +#if PRINTPKT_SUPPORT +static int ecp_printpkt (u_char *pkt, int len, + void (*printer) (void *, char *, ...), + void *arg); +#endif /* PRINTPKT_SUPPORT */ +/* +static void ecp_datainput (int unit, u_char *pkt, int len); +*/ + +const struct protent ecp_protent = { + PPP_ECP, + ecp_init, + NULL, /* ecp_input, */ + NULL, /* ecp_protrej, */ + NULL, /* ecp_lowerup, */ + NULL, /* ecp_lowerdown, */ + NULL, /* ecp_open, */ + NULL, /* ecp_close, */ +#if PRINTPKT_SUPPORT + ecp_printpkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, /* ecp_datainput, */ + 0, +#if PRINTPKT_SUPPORT + "ECP", + "Encrypted", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ecp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +fsm ecp_fsm[NUM_PPP]; +ecp_options ecp_wantoptions[NUM_PPP]; /* what to request the peer to use */ +ecp_options ecp_gotoptions[NUM_PPP]; /* what the peer agreed to do */ +ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */ +ecp_options ecp_hisoptions[NUM_PPP]; /* what we agreed to do */ + +static const fsm_callbacks ecp_callbacks = { + NULL, /* ecp_resetci, */ + NULL, /* ecp_cilen, */ + NULL, /* ecp_addci, */ + NULL, /* ecp_ackci, */ + NULL, /* ecp_nakci, */ + NULL, /* ecp_rejci, */ + NULL, /* ecp_reqci, */ + NULL, /* ecp_up, */ + NULL, /* ecp_down, */ + NULL, + NULL, + NULL, + NULL, + NULL, /* ecp_extcode, */ + "ECP" +}; + +/* + * ecp_init - initialize ECP. + */ +static void +ecp_init(unit) + int unit; +{ + fsm *f = &ecp_fsm[unit]; + + f->unit = unit; + f->protocol = PPP_ECP; + f->callbacks = &ecp_callbacks; + fsm_init(f); + + memset(&ecp_wantoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_gotoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options)); + memset(&ecp_hisoptions[unit], 0, sizeof(ecp_options)); + +} + + +#if PRINTPKT_SUPPORT +static int +ecp_printpkt(p, plen, printer, arg) + u_char *p; + int plen; + void (*printer) (void *, char *, ...); + void *arg; +{ + return 0; +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && ECP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eui64.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eui64.c new file mode 100644 index 0000000..e23a34e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/eui64.c @@ -0,0 +1,56 @@ +/* + * eui64.c - EUI64 routines for IPv6CP. + * + * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $ + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/eui64.h" + +/* + * eui64_ntoa - Make an ascii representation of an interface identifier + */ +char *eui64_ntoa(eui64_t e) { + static char buf[32]; + + snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x", + e.e8[0], e.e8[1], e.e8[2], e.e8[3], + e.e8[4], e.e8[5], e.e8[6], e.e8[7]); + return buf; +} + +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/fsm.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/fsm.c new file mode 100644 index 0000000..71f2c95 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/fsm.c @@ -0,0 +1,798 @@ +/* + * fsm.c - {Link, IP} Control Protocol Finite State Machine. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * TODO: + * Randomize fsm id on link/init. + * Deal with variable outgoing MTU. + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" + +static void fsm_timeout (void *); +static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len); +static void fsm_rconfack(fsm *f, int id, u_char *inp, int len); +static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len); +static void fsm_rtermreq(fsm *f, int id, u_char *p, int len); +static void fsm_rtermack(fsm *f); +static void fsm_rcoderej(fsm *f, u_char *inp, int len); +static void fsm_sconfreq(fsm *f, int retransmit); + +#define PROTO_NAME(f) ((f)->callbacks->proto_name) + +/* + * fsm_init - Initialize fsm. + * + * Initialize fsm state. + */ +void fsm_init(fsm *f) { + ppp_pcb *pcb = f->pcb; + f->state = PPP_FSM_INITIAL; + f->flags = 0; + f->id = 0; /* XXX Start with random id? */ + f->maxnakloops = pcb->settings.fsm_max_nak_loops; + f->term_reason_len = 0; +} + + +/* + * fsm_lowerup - The lower layer is up. + */ +void fsm_lowerup(fsm *f) { + switch( f->state ){ + case PPP_FSM_INITIAL: + f->state = PPP_FSM_CLOSED; + break; + + case PPP_FSM_STARTING: + if( f->flags & OPT_SILENT ) + f->state = PPP_FSM_STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + } + break; + + default: + FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_lowerdown - The lower layer is down. + * + * Cancel all timeouts and inform upper layers. + */ +void fsm_lowerdown(fsm *f) { + switch( f->state ){ + case PPP_FSM_CLOSED: + f->state = PPP_FSM_INITIAL; + break; + + case PPP_FSM_STOPPED: + f->state = PPP_FSM_STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case PPP_FSM_CLOSING: + f->state = PPP_FSM_INITIAL; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case PPP_FSM_STOPPING: + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + f->state = PPP_FSM_STARTING; + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + break; + + case PPP_FSM_OPENED: + if( f->callbacks->down ) + (*f->callbacks->down)(f); + f->state = PPP_FSM_STARTING; + break; + + default: + FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_open - Link is allowed to come up. + */ +void fsm_open(fsm *f) { + switch( f->state ){ + case PPP_FSM_INITIAL: + f->state = PPP_FSM_STARTING; + if( f->callbacks->starting ) + (*f->callbacks->starting)(f); + break; + + case PPP_FSM_CLOSED: + if( f->flags & OPT_SILENT ) + f->state = PPP_FSM_STOPPED; + else { + /* Send an initial configure-request */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + } + break; + + case PPP_FSM_CLOSING: + f->state = PPP_FSM_STOPPING; + /* fall through */ + /* no break */ + case PPP_FSM_STOPPED: + case PPP_FSM_OPENED: + if( f->flags & OPT_RESTART ){ + fsm_lowerdown(f); + fsm_lowerup(f); + } + break; + default: + break; + } +} + +/* + * terminate_layer - Start process of shutting down the FSM + * + * Cancel any timeout running, notify upper layers we're done, and + * send a terminate-request message as configured. + */ +static void terminate_layer(fsm *f, int nextstate) { + ppp_pcb *pcb = f->pcb; + + if( f->state != PPP_FSM_OPENED ) + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + else if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers we're down */ + + /* Init restart counter and send Terminate-Request */ + f->retransmits = pcb->settings.fsm_max_term_transmits; + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + + if (f->retransmits == 0) { + /* + * User asked for no terminate requests at all; just close it. + * We've already fired off one Terminate-Request just to be nice + * to the peer, but we're not going to wait for a reply. + */ + f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + return; + } + + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + --f->retransmits; + + f->state = nextstate; +} + +/* + * fsm_close - Start closing connection. + * + * Cancel timeouts and either initiate close or possibly go directly to + * the PPP_FSM_CLOSED state. + */ +void fsm_close(fsm *f, const char *reason) { + f->term_reason = reason; + f->term_reason_len = (reason == NULL? 0: LWIP_MIN(strlen(reason), 0xFF) ); + switch( f->state ){ + case PPP_FSM_STARTING: + f->state = PPP_FSM_INITIAL; + break; + case PPP_FSM_STOPPED: + f->state = PPP_FSM_CLOSED; + break; + case PPP_FSM_STOPPING: + f->state = PPP_FSM_CLOSING; + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + case PPP_FSM_OPENED: + terminate_layer(f, PPP_FSM_CLOSING); + break; + default: + break; + } +} + + +/* + * fsm_timeout - Timeout expired. + */ +static void fsm_timeout(void *arg) { + fsm *f = (fsm *) arg; + ppp_pcb *pcb = f->pcb; + + switch (f->state) { + case PPP_FSM_CLOSING: + case PPP_FSM_STOPPING: + if( f->retransmits <= 0 ){ + /* + * We've waited for an ack long enough. Peer probably heard us. + */ + f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + } else { + /* Send Terminate-Request */ + fsm_sdata(f, TERMREQ, f->reqid = ++f->id, + (u_char *) f->term_reason, f->term_reason_len); + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + --f->retransmits; + } + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + if (f->retransmits <= 0) { + ppp_warn("%s: timeout sending Config-Requests\n", PROTO_NAME(f)); + f->state = PPP_FSM_STOPPED; + if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) + (*f->callbacks->finished)(f); + + } else { + /* Retransmit the configure-request */ + if (f->callbacks->retransmit) + (*f->callbacks->retransmit)(f); + fsm_sconfreq(f, 1); /* Re-send Configure-Request */ + if( f->state == PPP_FSM_ACKRCVD ) + f->state = PPP_FSM_REQSENT; + } + break; + + default: + FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_input - Input packet. + */ +void fsm_input(fsm *f, u_char *inpacket, int l) { + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < HEADERLEN) { + FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol)); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < HEADERLEN) { + FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol)); + return; + } + if (len > l) { + FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol)); + return; + } + len -= HEADERLEN; /* subtract header length */ + + if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){ + FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.", + f->protocol, f->state)); + return; + } + + /* + * Action depends on code. + */ + switch (code) { + case CONFREQ: + fsm_rconfreq(f, id, inp, len); + break; + + case CONFACK: + fsm_rconfack(f, id, inp, len); + break; + + case CONFNAK: + case CONFREJ: + fsm_rconfnakrej(f, code, id, inp, len); + break; + + case TERMREQ: + fsm_rtermreq(f, id, inp, len); + break; + + case TERMACK: + fsm_rtermack(f); + break; + + case CODEREJ: + fsm_rcoderej(f, inp, len); + break; + + default: + if( !f->callbacks->extcode + || !(*f->callbacks->extcode)(f, code, id, inp, len) ) + fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN); + break; + } +} + + +/* + * fsm_rconfreq - Receive Configure-Request. + */ +static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) { + int code, reject_if_disagree; + + switch( f->state ){ + case PPP_FSM_CLOSED: + /* Go away, we're closed */ + fsm_sdata(f, TERMACK, id, NULL, 0); + return; + case PPP_FSM_CLOSING: + case PPP_FSM_STOPPING: + return; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if( f->callbacks->down ) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_STOPPED: + /* Negotiation started by our peer */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } + + /* + * Pass the requested configuration options + * to protocol-specific code for checking. + */ + if (f->callbacks->reqci){ /* Check CI */ + reject_if_disagree = (f->nakloops >= f->maxnakloops); + code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree); + } else if (len) + code = CONFREJ; /* Reject all CI */ + else + code = CONFACK; + + /* send the Ack, Nak or Rej to the peer */ + fsm_sdata(f, code, id, inp, len); + + if (code == CONFACK) { + if (f->state == PPP_FSM_ACKRCVD) { + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = PPP_FSM_OPENED; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + } else + f->state = PPP_FSM_ACKSENT; + f->nakloops = 0; + + } else { + /* we sent CONFACK or CONFREJ */ + if (f->state != PPP_FSM_ACKRCVD) + f->state = PPP_FSM_REQSENT; + if( code == CONFNAK ) + ++f->nakloops; + } +} + + +/* + * fsm_rconfack - Receive Configure-Ack. + */ +static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): + (len == 0)) ){ + /* Ack is bad - ignore it */ + ppp_error("Received bad configure-ack: %P", inp, len); + return; + } + f->seen_ack = 1; + f->rnakloops = 0; + + switch (f->state) { + case PPP_FSM_CLOSED: + case PPP_FSM_STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case PPP_FSM_REQSENT: + f->state = PPP_FSM_ACKRCVD; + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + break; + + case PPP_FSM_ACKRCVD: + /* Huh? an extra valid Ack? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + f->state = PPP_FSM_OPENED; + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + if (f->callbacks->up) + (*f->callbacks->up)(f); /* Inform upper layers */ + break; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + */ +static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) { + int ret; + int treat_as_reject; + + if (id != f->reqid || f->seen_ack) /* Expected id? */ + return; /* Nope, toss... */ + + if (code == CONFNAK) { + ++f->rnakloops; + treat_as_reject = (f->rnakloops >= f->maxnakloops); + if (f->callbacks->nakci == NULL + || !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) { + ppp_error("Received bad configure-nak: %P", inp, len); + return; + } + } else { + f->rnakloops = 0; + if (f->callbacks->rejci == NULL + || !(ret = f->callbacks->rejci(f, inp, len))) { + ppp_error("Received bad configure-rej: %P", inp, len); + return; + } + } + + f->seen_ack = 1; + + switch (f->state) { + case PPP_FSM_CLOSED: + case PPP_FSM_STOPPED: + fsm_sdata(f, TERMACK, id, NULL, 0); + break; + + case PPP_FSM_REQSENT: + case PPP_FSM_ACKSENT: + /* They didn't agree to what we wanted - try another request */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + if (ret < 0) + f->state = PPP_FSM_STOPPED; /* kludge for stopping CCP */ + else + fsm_sconfreq(f, 0); /* Send Configure-Request */ + break; + + case PPP_FSM_ACKRCVD: + /* Got a Nak/reject when we had already had an Ack?? oh well... */ + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_OPENED: + /* Go down and restart negotiation */ + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); /* Send initial Configure-Request */ + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rtermreq - Receive Terminate-Req. + */ +static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + + switch (f->state) { + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + f->state = PPP_FSM_REQSENT; /* Start over but keep trying */ + break; + + case PPP_FSM_OPENED: + if (len > 0) { + ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p); + } else + ppp_info("%s terminated by peer", PROTO_NAME(f)); + f->retransmits = 0; + f->state = PPP_FSM_STOPPING; + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); + break; + default: + break; + } + + fsm_sdata(f, TERMACK, id, NULL, 0); +} + + +/* + * fsm_rtermack - Receive Terminate-Ack. + */ +static void fsm_rtermack(fsm *f) { + switch (f->state) { + case PPP_FSM_CLOSING: + UNTIMEOUT(fsm_timeout, f); + f->state = PPP_FSM_CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + case PPP_FSM_STOPPING: + UNTIMEOUT(fsm_timeout, f); + f->state = PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_ACKRCVD: + f->state = PPP_FSM_REQSENT; + break; + + case PPP_FSM_OPENED: + if (f->callbacks->down) + (*f->callbacks->down)(f); /* Inform upper layers */ + fsm_sconfreq(f, 0); + f->state = PPP_FSM_REQSENT; + break; + default: + break; + } +} + + +/* + * fsm_rcoderej - Receive an Code-Reject. + */ +static void fsm_rcoderej(fsm *f, u_char *inp, int len) { + u_char code, id; + + if (len < HEADERLEN) { + FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id); + + if( f->state == PPP_FSM_ACKRCVD ) + f->state = PPP_FSM_REQSENT; +} + + +/* + * fsm_protreject - Peer doesn't speak this protocol. + * + * Treat this as a catastrophic error (RXJ-). + */ +void fsm_protreject(fsm *f) { + switch( f->state ){ + case PPP_FSM_CLOSING: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + /* no break */ + case PPP_FSM_CLOSED: + f->state = PPP_FSM_CLOSED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_STOPPING: + case PPP_FSM_REQSENT: + case PPP_FSM_ACKRCVD: + case PPP_FSM_ACKSENT: + UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */ + /* fall through */ + /* no break */ + case PPP_FSM_STOPPED: + f->state = PPP_FSM_STOPPED; + if( f->callbacks->finished ) + (*f->callbacks->finished)(f); + break; + + case PPP_FSM_OPENED: + terminate_layer(f, PPP_FSM_STOPPING); + break; + + default: + FSMDEBUG(("%s: Protocol-reject event in state %d!", + PROTO_NAME(f), f->state)); + /* no break */ + } +} + + +/* + * fsm_sconfreq - Send a Configure-Request. + */ +static void fsm_sconfreq(fsm *f, int retransmit) { + ppp_pcb *pcb = f->pcb; + struct pbuf *p; + u_char *outp; + int cilen; + + if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){ + /* Not currently negotiating - reset options */ + if( f->callbacks->resetci ) + (*f->callbacks->resetci)(f); + f->nakloops = 0; + f->rnakloops = 0; + } + + if( !retransmit ){ + /* New request - reset retransmission counter, use new ID */ + f->retransmits = pcb->settings.fsm_max_conf_req_transmits; + f->reqid = ++f->id; + } + + f->seen_ack = 0; + + /* + * Make up the request packet + */ + if( f->callbacks->cilen && f->callbacks->addci ){ + cilen = (*f->callbacks->cilen)(f); + if( cilen > pcb->peer_mru - HEADERLEN ) + cilen = pcb->peer_mru - HEADERLEN; + } else + cilen = 0; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + /* send the request to our peer */ + outp = (u_char*)p->payload; + MAKEHEADER(outp, f->protocol); + PUTCHAR(CONFREQ, outp); + PUTCHAR(f->reqid, outp); + PUTSHORT(cilen + HEADERLEN, outp); + if (cilen != 0) { + (*f->callbacks->addci)(f, outp, &cilen); + } + + ppp_write(pcb, p); + + /* start the retransmit timer */ + --f->retransmits; + TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time); +} + + +/* + * fsm_sdata - Send some data. + * + * Used for all packets sent to our peer by this module. + */ +void fsm_sdata(fsm *f, u_char code, u_char id, u_char *data, int datalen) { + ppp_pcb *pcb = f->pcb; + struct pbuf *p; + u_char *outp; + int outlen; + + /* Adjust length to be smaller than MTU */ + if (datalen > pcb->peer_mru - HEADERLEN) + datalen = pcb->peer_mru - HEADERLEN; + outlen = datalen + HEADERLEN; + + p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; +/* if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) -- was only for fsm_sconfreq() */ + MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen); + MAKEHEADER(outp, f->protocol); + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + ppp_write(pcb, p); +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipcp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipcp.c new file mode 100644 index 0000000..970e69e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipcp.c @@ -0,0 +1,2274 @@ +/* + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * TODO: + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" + +#if 0 /* UNUSED */ +/* global vars */ +u32_t netmask = 0; /* IP netmask to set on interface */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */ +#endif /* UNUSED */ + +#if 0 /* moved to ppp_settings */ +bool noremoteip = 0; /* Let him have no IP address */ +#endif /* moved to ppp_setting */ + +#if 0 /* UNUSED */ +/* Hook for a plugin to know when IP protocol has come up */ +void (*ip_up_hook) (void) = NULL; + +/* Hook for a plugin to know when IP protocol has come down */ +void (*ip_down_hook) (void) = NULL; + +/* Hook for a plugin to choose the remote IP address */ +void (*ip_choose_hook) (u32_t *) = NULL; +#endif /* UNUSED */ + +#if PPP_NOTIFY +/* Notifiers for when IPCP goes up and down */ +struct notifier *ip_up_notifier = NULL; +struct notifier *ip_down_notifier = NULL; +#endif /* PPP_NOTIFY */ + +/* local vars */ +#if 0 /* moved to ppp_pcb */ +static int default_route_set[NUM_PPP]; /* Have set up a default route */ +static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */ +static int ipcp_is_up; /* have called np_up() */ +static int ipcp_is_open; /* haven't called np_finished() */ +static bool ask_for_local; /* request our address from peer */ +#endif /* moved to ppp_pcb */ +#if 0 /* UNUSED */ +static char vj_value[8]; /* string form of vj option value */ +static char netmask_str[20]; /* string form of netmask value */ +#endif /* UNUSED */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipcp_resetci(fsm *f); /* Reset our CI */ +static int ipcp_cilen(fsm *f); /* Return length of our CI */ +static void ipcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */ +static int ipcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject);/* Peer nak'd our CI */ +static int ipcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */ +static void ipcp_up(fsm *f); /* We're UP */ +static void ipcp_down(fsm *f); /* We're DOWN */ +static void ipcp_finished(fsm *f); /* Don't need lower layer */ + +static const fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */ + ipcp_resetci, /* Reset our Configuration Information */ + ipcp_cilen, /* Length of our Configuration Information */ + ipcp_addci, /* Add our Configuration Information */ + ipcp_ackci, /* ACK our Configuration Information */ + ipcp_nakci, /* NAK our Configuration Information */ + ipcp_rejci, /* Reject our Configuration Information */ + ipcp_reqci, /* Request peer's Configuration Information */ + ipcp_up, /* Called when fsm reaches OPENED state */ + ipcp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPCP" /* String name of protocol */ +}; + +/* + * Command-line options. + */ +#if PPP_OPTIONS +static int setvjslots (char **); +static int setdnsaddr (char **); +static int setwinsaddr (char **); +static int setnetmask (char **); +int setipaddr (char *, char **, int); + +static void printipaddr (option_t *, void (*)(void *, char *,...),void *); + +static option_t ipcp_option_list[] = { + { "noip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP" }, + { "-ip", o_bool, &ipcp_protent.enabled_flag, + "Disable IP and IPCP", OPT_ALIAS }, + + { "novj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_A2CLR, &ipcp_allowoptions[0].neg_vj }, + { "-vj", o_bool, &ipcp_wantoptions[0].neg_vj, + "Disable VJ compression", OPT_ALIAS | OPT_A2CLR, + &ipcp_allowoptions[0].neg_vj }, + + { "novjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_A2CLR, + &ipcp_allowoptions[0].cflag }, + { "-vjccomp", o_bool, &ipcp_wantoptions[0].cflag, + "Disable VJ connection-ID compression", OPT_ALIAS | OPT_A2CLR, + &ipcp_allowoptions[0].cflag }, + + { "vj-max-slots", o_special, (void *)setvjslots, + "Set maximum VJ header slots", + OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, vj_value }, + + { "ipcp-accept-local", o_bool, &ipcp_wantoptions[0].accept_local, + "Accept peer's address for us", 1 }, + { "ipcp-accept-remote", o_bool, &ipcp_wantoptions[0].accept_remote, + "Accept peer's address for it", 1 }, + + { "ipparam", o_string, &ipparam, + "Set ip script parameter", OPT_PRIO }, + + { "noipdefault", o_bool, &disable_defaultip, + "Don't use name for default IP adrs", 1 }, + + { "ms-dns", 1, (void *)setdnsaddr, + "DNS address for the peer's use" }, + { "ms-wins", 1, (void *)setwinsaddr, + "Nameserver for SMB over TCP/IP for peer" }, + + { "ipcp-restart", o_int, &ipcp_fsm[0].timeouttime, + "Set timeout for IPCP", OPT_PRIO }, + { "ipcp-max-terminate", o_int, &ipcp_fsm[0].maxtermtransmits, + "Set max #xmits for term-reqs", OPT_PRIO }, + { "ipcp-max-configure", o_int, &ipcp_fsm[0].maxconfreqtransmits, + "Set max #xmits for conf-reqs", OPT_PRIO }, + { "ipcp-max-failure", o_int, &ipcp_fsm[0].maxnakloops, + "Set max #conf-naks for IPCP", OPT_PRIO }, + + { "defaultroute", o_bool, &ipcp_wantoptions[0].default_route, + "Add default route", OPT_ENABLE|1, &ipcp_allowoptions[0].default_route }, + { "nodefaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_A2CLR, + &ipcp_wantoptions[0].default_route }, + { "-defaultroute", o_bool, &ipcp_allowoptions[0].default_route, + "disable defaultroute option", OPT_ALIAS | OPT_A2CLR, + &ipcp_wantoptions[0].default_route }, + + { "replacedefaultroute", o_bool, + &ipcp_wantoptions[0].replace_default_route, + "Replace default route", 1 + }, + { "noreplacedefaultroute", o_bool, + &ipcp_allowoptions[0].replace_default_route, + "Never replace default route", OPT_A2COPY, + &ipcp_wantoptions[0].replace_default_route }, + { "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp, + "Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp }, + { "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_A2CLR, + &ipcp_wantoptions[0].proxy_arp }, + { "-proxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp, + "disable proxyarp option", OPT_ALIAS | OPT_A2CLR, + &ipcp_wantoptions[0].proxy_arp }, + + { "usepeerdns", o_bool, &usepeerdns, + "Ask peer for DNS address(es)", 1 }, + + { "netmask", o_special, (void *)setnetmask, + "set netmask", OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, netmask_str }, + + { "ipcp-no-addresses", o_bool, &ipcp_wantoptions[0].old_addrs, + "Disable old-style IP-Addresses usage", OPT_A2CLR, + &ipcp_allowoptions[0].old_addrs }, + { "ipcp-no-address", o_bool, &ipcp_wantoptions[0].neg_addr, + "Disable IP-Address usage", OPT_A2CLR, + &ipcp_allowoptions[0].neg_addr }, + + { "noremoteip", o_bool, &noremoteip, + "Allow peer to have no IP address", 1 }, + + { "nosendip", o_bool, &ipcp_wantoptions[0].neg_addr, + "Don't send our IP address to peer", OPT_A2CLR, + &ipcp_wantoptions[0].old_addrs}, + + { "IP addresses", o_wild, (void *) &setipaddr, + "set local and remote IP addresses", + OPT_NOARG | OPT_A2PRINTER, (void *) &printipaddr }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ipcp_init(ppp_pcb *pcb); +static void ipcp_open(ppp_pcb *pcb); +static void ipcp_close(ppp_pcb *pcb, const char *reason); +static void ipcp_lowerup(ppp_pcb *pcb); +static void ipcp_lowerdown(ppp_pcb *pcb); +static void ipcp_input(ppp_pcb *pcb, u_char *p, int len); +static void ipcp_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int ipcp_printpkt(u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS +static void ip_check_options (void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT +static int ip_demand_conf (int); +static int ip_active_pkt (u_char *, int); +#endif /* DEMAND_SUPPORT */ +#if 0 /* UNUSED */ +static void create_resolv (u32_t, u32_t); +#endif /* UNUSED */ + +const struct protent ipcp_protent = { + PPP_IPCP, + ipcp_init, + ipcp_input, + ipcp_protrej, + ipcp_lowerup, + ipcp_lowerdown, + ipcp_open, + ipcp_close, +#if PRINTPKT_SUPPORT + ipcp_printpkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, + 1, +#if PRINTPKT_SUPPORT + "IPCP", + "IP", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ipcp_option_list, + ip_check_options, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + ip_demand_conf, + ip_active_pkt +#endif /* DEMAND_SUPPORT */ +}; + +static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute); + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */ +#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */ +#define CILEN_ADDR 6 /* new-style single address option */ +#define CILEN_ADDRS 10 /* old-style dual address option */ + + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED, already defined by lwIP */ +/* + * Make a string representation of a network IP address. + */ +char * +ip_ntoa(ipaddr) +u32_t ipaddr; +{ + static char b[64]; + + slprintf(b, sizeof(b), "%I", ipaddr); + return b; +} +#endif /* UNUSED, already defined by lwIP */ + +/* + * Option parsing. + */ +#if PPP_OPTIONS +/* + * setvjslots - set maximum number of connection slots for VJ compression + */ +static int +setvjslots(argv) + char **argv; +{ + int value; + +/* FIXME: found what int_option() did */ +#if PPP_OPTIONS + if (!int_option(*argv, &value)) + return 0; +#endif /* PPP_OPTIONS */ + + if (value < 2 || value > 16) { + option_error("vj-max-slots value must be between 2 and 16"); + return 0; + } + ipcp_wantoptions [0].maxslotindex = + ipcp_allowoptions[0].maxslotindex = value - 1; + slprintf(vj_value, sizeof(vj_value), "%d", value); + return 1; +} + +/* + * setdnsaddr - set the dns address(es) + */ +static int +setdnsaddr(argv) + char **argv; +{ + u32_t dns; + struct hostent *hp; + + dns = inet_addr(*argv); + if (dns == (u32_t) -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-dns option", + *argv); + return 0; + } + dns = *(u32_t *)hp->h_addr; + } + + /* We take the last 2 values given, the 2nd-last as the primary + and the last as the secondary. If only one is given it + becomes both primary and secondary. */ + if (ipcp_allowoptions[0].dnsaddr[1] == 0) + ipcp_allowoptions[0].dnsaddr[0] = dns; + else + ipcp_allowoptions[0].dnsaddr[0] = ipcp_allowoptions[0].dnsaddr[1]; + + /* always set the secondary address value. */ + ipcp_allowoptions[0].dnsaddr[1] = dns; + + return (1); +} + +/* + * setwinsaddr - set the wins address(es) + * This is primrarly used with the Samba package under UNIX or for pointing + * the caller to the existing WINS server on a Windows NT platform. + */ +static int +setwinsaddr(argv) + char **argv; +{ + u32_t wins; + struct hostent *hp; + + wins = inet_addr(*argv); + if (wins == (u32_t) -1) { + if ((hp = gethostbyname(*argv)) == NULL) { + option_error("invalid address parameter '%s' for ms-wins option", + *argv); + return 0; + } + wins = *(u32_t *)hp->h_addr; + } + + /* We take the last 2 values given, the 2nd-last as the primary + and the last as the secondary. If only one is given it + becomes both primary and secondary. */ + if (ipcp_allowoptions[0].winsaddr[1] == 0) + ipcp_allowoptions[0].winsaddr[0] = wins; + else + ipcp_allowoptions[0].winsaddr[0] = ipcp_allowoptions[0].winsaddr[1]; + + /* always set the secondary address value. */ + ipcp_allowoptions[0].winsaddr[1] = wins; + + return (1); +} + +/* + * setipaddr - Set the IP address + * If doit is 0, the call is to check whether this option is + * potentially an IP address specification. + * Not static so that plugins can call it to set the addresses + */ +int +setipaddr(arg, argv, doit) + char *arg; + char **argv; + int doit; +{ + struct hostent *hp; + char *colon; + u32_t local, remote; + ipcp_options *wo = &ipcp_wantoptions[0]; + static int prio_local = 0, prio_remote = 0; + + /* + * IP address pair separated by ":". + */ + if ((colon = strchr(arg, ':')) == NULL) + return 0; + if (!doit) + return 1; + + /* + * If colon first character, then no local addr. + */ + if (colon != arg && option_priority >= prio_local) { + *colon = '\0'; + if ((local = inet_addr(arg)) == (u32_t) -1) { + if ((hp = gethostbyname(arg)) == NULL) { + option_error("unknown host: %s", arg); + return 0; + } + local = *(u32_t *)hp->h_addr; + } + if (bad_ip_adrs(local)) { + option_error("bad local IP address %s", ip_ntoa(local)); + return 0; + } + if (local != 0) + wo->ouraddr = local; + *colon = ':'; + prio_local = option_priority; + } + + /* + * If colon last character, then no remote addr. + */ + if (*++colon != '\0' && option_priority >= prio_remote) { + if ((remote = inet_addr(colon)) == (u32_t) -1) { + if ((hp = gethostbyname(colon)) == NULL) { + option_error("unknown host: %s", colon); + return 0; + } + remote = *(u32_t *)hp->h_addr; + if (remote_name[0] == 0) + strlcpy(remote_name, colon, sizeof(remote_name)); + } + if (bad_ip_adrs(remote)) { + option_error("bad remote IP address %s", ip_ntoa(remote)); + return 0; + } + if (remote != 0) + wo->hisaddr = remote; + prio_remote = option_priority; + } + + return 1; +} + +static void +printipaddr(opt, printer, arg) + option_t *opt; + void (*printer) (void *, char *, ...); + void *arg; +{ + ipcp_options *wo = &ipcp_wantoptions[0]; + + if (wo->ouraddr != 0) + printer(arg, "%I", wo->ouraddr); + printer(arg, ":"); + if (wo->hisaddr != 0) + printer(arg, "%I", wo->hisaddr); +} + +/* + * setnetmask - set the netmask to be used on the interface. + */ +static int +setnetmask(argv) + char **argv; +{ + u32_t mask; + int n; + char *p; + + /* + * Unfortunately, if we use inet_addr, we can't tell whether + * a result of all 1s is an error or a valid 255.255.255.255. + */ + p = *argv; + n = parse_dotted_ip(p, &mask); + + mask = htonl(mask); + + if (n == 0 || p[n] != 0 || (netmask & ~mask) != 0) { + option_error("invalid netmask value '%s'", *argv); + return 0; + } + + netmask = mask; + slprintf(netmask_str, sizeof(netmask_str), "%I", mask); + + return (1); +} + +int +parse_dotted_ip(p, vp) + char *p; + u32_t *vp; +{ + int n; + u32_t v, b; + char *endp, *p0 = p; + + v = 0; + for (n = 3;; --n) { + b = strtoul(p, &endp, 0); + if (endp == p) + return 0; + if (b > 255) { + if (n < 3) + return 0; + /* accept e.g. 0xffffff00 */ + *vp = b; + return endp - p0; + } + v |= b << (n * 8); + p = endp; + if (n == 0) + break; + if (*p != '.') + return 0; + ++p; + } + *vp = v; + return p - p0; +} +#endif /* PPP_OPTIONS */ + +/* + * ipcp_init - Initialize IPCP. + */ +static void ipcp_init(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_IPCP; + f->callbacks = &ipcp_callbacks; + fsm_init(f); + + /* + * Some 3G modems use repeated IPCP NAKs as a way of stalling + * until they can contact a server on the network, so we increase + * the default number of NAKs we accept before we start treating + * them as rejects. + */ + f->maxnakloops = 100; + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->neg_addr = wo->old_addrs = 1; + wo->neg_vj = 1; + wo->vj_protocol = IPCP_VJ_COMP; + wo->maxslotindex = MAX_STATES - 1; /* really max index */ + wo->cflag = 1; + +#if 0 /* UNUSED */ + /* wanting default route by default */ + wo->default_route = 1; +#endif /* UNUSED */ + + /* max slots and slot-id compression are currently hardwired in */ + /* ppp_if.c to 16 and 1, this needs to be changed (among other */ + /* things) gmc */ + + ao->neg_addr = ao->old_addrs = 1; + ao->neg_vj = 1; + ao->maxslotindex = MAX_STATES - 1; + ao->cflag = 1; + +#if 0 /* UNUSED */ + /* + * XXX These control whether the user may use the proxyarp + * and defaultroute options. + */ + ao->proxy_arp = 1; + ao->default_route = 1; +#endif /* UNUSED */ +} + + +/* + * ipcp_open - IPCP is allowed to come up. + */ +static void ipcp_open(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_open(f); + pcb->ipcp_is_open = 1; +} + + +/* + * ipcp_close - Take IPCP down. + */ +static void ipcp_close(ppp_pcb *pcb, const char *reason) { + fsm *f = &pcb->ipcp_fsm; + fsm_close(f, reason); +} + + +/* + * ipcp_lowerup - The lower layer is up. + */ +static void ipcp_lowerup(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerup(f); +} + + +/* + * ipcp_lowerdown - The lower layer is down. + */ +static void ipcp_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerdown(f); +} + + +/* + * ipcp_input - Input IPCP packet. + */ +static void ipcp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->ipcp_fsm; + fsm_input(f, p, len); +} + + +/* + * ipcp_protrej - A Protocol-Reject was received for IPCP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void ipcp_protrej(ppp_pcb *pcb) { + fsm *f = &pcb->ipcp_fsm; + fsm_lowerdown(f); +} + + +/* + * ipcp_resetci - Reset our CI. + * Called by fsm_sconfreq, Send Configure Request. + */ +static void ipcp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + + wo->req_addr = (wo->neg_addr || wo->old_addrs) && + (ao->neg_addr || ao->old_addrs); + if (wo->ouraddr == 0) + wo->accept_local = 1; + if (wo->hisaddr == 0) + wo->accept_remote = 1; + wo->req_dns1 = pcb->settings.usepeerdns; /* Request DNS addresses from the peer */ + wo->req_dns2 = pcb->settings.usepeerdns; + *go = *wo; + if (!pcb->ask_for_local) + go->ouraddr = 0; +#if 0 /* UNUSED */ + if (ip_choose_hook) { + ip_choose_hook(&wo->hisaddr); + if (wo->hisaddr) { + wo->accept_remote = 0; + } + } +#endif /* UNUSED */ + BZERO(&pcb->ipcp_hisoptions, sizeof(ipcp_options)); +} + + +/* + * ipcp_cilen - Return length of our CI. + * Called by fsm_sconfreq, Send Configure Request. + */ +static int ipcp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *ho = &pcb->ipcp_hisoptions; + +#define LENCIADDRS(neg) (neg ? CILEN_ADDRS : 0) +#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0) +#define LENCIADDR(neg) (neg ? CILEN_ADDR : 0) +#define LENCIDNS(neg) LENCIADDR(neg) +#define LENCIWINS(neg) LENCIADDR(neg) + + /* + * First see if we want to change our options to the old + * forms because we have received old forms from the peer. + */ + if (go->neg_addr && go->old_addrs && !ho->neg_addr && ho->old_addrs) + go->neg_addr = 0; + if (wo->neg_vj && !go->neg_vj && !go->old_vj) { + /* try an older style of VJ negotiation */ + /* use the old style only if the peer did */ + if (ho->neg_vj && ho->old_vj) { + go->neg_vj = 1; + go->old_vj = 1; + go->vj_protocol = ho->vj_protocol; + } + } + + return (LENCIADDRS(!go->neg_addr && go->old_addrs) + + LENCIVJ(go->neg_vj, go->old_vj) + + LENCIADDR(go->neg_addr) + + LENCIDNS(go->req_dns1) + + LENCIDNS(go->req_dns2) + + LENCIWINS(go->winsaddr[0]) + + LENCIWINS(go->winsaddr[1])) ; +} + + +/* + * ipcp_addci - Add our desired CIs to a packet. + * Called by fsm_sconfreq, Send Configure Request. + */ +static void ipcp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + int len = *lenp; + +#define ADDCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + if (len >= CILEN_ADDRS) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDRS, ucp); \ + l = ntohl(val1); \ + PUTLONG(l, ucp); \ + l = ntohl(val2); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDRS; \ + } else \ + go->old_addrs = 0; \ + } + +#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + if (!old) { \ + PUTCHAR(maxslotindex, ucp); \ + PUTCHAR(cflag, ucp); \ + } \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIADDR(opt, neg, val) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(val); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + neg = 0; \ + } + +#define ADDCIDNS(opt, neg, addr) \ + if (neg) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + neg = 0; \ + } + +#define ADDCIWINS(opt, addr) \ + if (addr) { \ + if (len >= CILEN_ADDR) { \ + u32_t l; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_ADDR, ucp); \ + l = ntohl(addr); \ + PUTLONG(l, ucp); \ + len -= CILEN_ADDR; \ + } else \ + addr = 0; \ + } + + ADDCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr, + go->hisaddr); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ADDCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); + + ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + ADDCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + ADDCIWINS(CI_MS_WINS2, go->winsaddr[1]); + + *lenp -= len; +} + + +/* + * ipcp_ackci - Ack our CIs. + * Called by fsm_rconfack, Receive Configure ACK. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int ipcp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_short cilen, citype, cishort; + u32_t cilong; + u_char cimaxslotindex, cicflag; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIADDRS(opt, neg, val1, val2) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDRS) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDRS || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val1 != cilong) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val2 != cilong) \ + goto bad; \ + } + +#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \ + if (neg) { \ + int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslotindex) \ + goto bad; \ + GETCHAR(cicflag, p); \ + if (cicflag != cflag) \ + goto bad; \ + } \ + } + +#define ACKCIADDR(opt, neg, val) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || \ + citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (val != cilong) \ + goto bad; \ + } + +#define ACKCIDNS(opt, neg, addr) \ + if (neg) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) \ + goto bad; \ + } + +#define ACKCIWINS(opt, addr) \ + if (addr) { \ + u32_t l; \ + if ((len -= CILEN_ADDR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_ADDR || citype != opt) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + if (addr != cilong) \ + goto bad; \ + } + + ACKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, go->ouraddr, + go->hisaddr); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + ACKCIADDR(CI_ADDR, go->neg_addr, go->ouraddr); + + ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]); + + ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]); + + ACKCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + ACKCIWINS(CI_MS_WINS2, go->winsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPCPDEBUG(("ipcp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPCP is in the OPENED state. + * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int ipcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_char cimaxslotindex, cicflag; + u_char citype, cilen, *next; + u_short cishort; + u32_t ciaddr1, ciaddr2, l, cidnsaddr; + ipcp_options no; /* options we've seen Naks for */ + ipcp_options try_; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIADDRS(opt, neg, code) \ + if ((neg) && \ + (cilen = p[1]) == CILEN_ADDRS && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + GETLONG(l, p); \ + ciaddr2 = htonl(l); \ + no.old_addrs = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIADDR(opt, neg, code) \ + if (go->neg && \ + (cilen = p[1]) == CILEN_ADDR && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + ciaddr1 = htonl(l); \ + no.neg = 1; \ + code \ + } + +#define NAKCIDNS(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cidnsaddr = htonl(l); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} address, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, + if (treat_as_reject) { + try_.old_addrs = 0; + } else { + if (go->accept_local && ciaddr1) { + /* take his idea of our address */ + try_.ouraddr = ciaddr1; + } + if (go->accept_remote && ciaddr2) { + /* take his idea of his address */ + try_.hisaddr = ciaddr2; + } + } + ); + + /* + * Accept the peer's value of maxslotindex provided that it + * is less than what we asked for. Turn off slot-ID compression + * if the peer wants. Send old-style compress-type option if + * the peer wants. + */ + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + if (treat_as_reject) { + try_.neg_vj = 0; + } else if (cilen == CILEN_VJ) { + GETCHAR(cimaxslotindex, p); + GETCHAR(cicflag, p); + if (cishort == IPCP_VJ_COMP) { + try_.old_vj = 0; + if (cimaxslotindex < go->maxslotindex) + try_.maxslotindex = cimaxslotindex; + if (!cicflag) + try_.cflag = 0; + } else { + try_.neg_vj = 0; + } + } else { + if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) { + try_.old_vj = 1; + try_.vj_protocol = cishort; + } else { + try_.neg_vj = 0; + } + } + ); + + NAKCIADDR(CI_ADDR, neg_addr, + if (treat_as_reject) { + try_.neg_addr = 0; + try_.old_addrs = 0; + } else if (go->accept_local && ciaddr1) { + /* take his idea of our address */ + try_.ouraddr = ciaddr1; + } + ); + + NAKCIDNS(CI_MS_DNS1, req_dns1, + if (treat_as_reject) { + try_.req_dns1 = 0; + } else { + try_.dnsaddr[0] = cidnsaddr; + } + ); + + NAKCIDNS(CI_MS_DNS2, req_dns2, + if (treat_as_reject) { + try_.req_dns2 = 0; + } else { + try_.dnsaddr[1] = cidnsaddr; + } + ); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about IP addresses, we comply. + * If they want us to ask for compression, we refuse. + * If they want us to ask for ms-dns, we do that, since some + * peers get huffy if we don't. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_ADDRS: + if ((!go->neg_addr && go->old_addrs) || no.old_addrs + || cilen != CILEN_ADDRS) + goto bad; + try_.neg_addr = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try_.ouraddr = ciaddr1; + GETLONG(l, p); + ciaddr2 = htonl(l); + if (ciaddr2 && go->accept_remote) + try_.hisaddr = ciaddr2; + no.old_addrs = 1; + break; + case CI_ADDR: + if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) + goto bad; + try_.old_addrs = 0; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1 && go->accept_local) + try_.ouraddr = ciaddr1; + if (try_.ouraddr != 0) + try_.neg_addr = 1; + no.neg_addr = 1; + break; + case CI_MS_DNS1: + if (go->req_dns1 || no.req_dns1 || cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + try_.dnsaddr[0] = htonl(l); + try_.req_dns1 = 1; + no.req_dns1 = 1; + break; + case CI_MS_DNS2: + if (go->req_dns2 || no.req_dns2 || cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + try_.dnsaddr[1] = htonl(l); + try_.req_dns2 = 1; + no.req_dns2 = 1; + break; + case CI_MS_WINS1: + case CI_MS_WINS2: + if (cilen != CILEN_ADDR) + goto bad; + GETLONG(l, p); + ciaddr1 = htonl(l); + if (ciaddr1) + try_.winsaddr[citype == CI_MS_WINS2] = ciaddr1; + break; + default: + break; + } + p = next; + } + + /* + * OK, the Nak is good. Now we can update state. + * If there are any remaining options, we ignore them. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + + return 1; + +bad: + IPCPDEBUG(("ipcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipcp_rejci - Reject some of our CIs. + * Callback from fsm_rconfnakrej. + */ +static int ipcp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipcp_options *go = &pcb->ipcp_gotoptions; + u_char cimaxslotindex, ciflag, cilen; + u_short cishort; + u32_t cilong; + ipcp_options try_; /* options to request next time */ + + try_ = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIADDRS(opt, neg, val1, val2) \ + if ((neg) && \ + (cilen = p[1]) == CILEN_ADDRS && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val1) \ + goto bad; \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val2) \ + goto bad; \ + try_.old_addrs = 0; \ + } + +#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \ + if (go->neg && \ + p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + if (!old) { \ + GETCHAR(cimaxslotindex, p); \ + if (cimaxslotindex != maxslot) \ + goto bad; \ + GETCHAR(ciflag, p); \ + if (ciflag != cflag) \ + goto bad; \ + } \ + try_.neg = 0; \ + } + +#define REJCIADDR(opt, neg, val) \ + if (go->neg && \ + (cilen = p[1]) == CILEN_ADDR && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } + +#define REJCIDNS(opt, neg, dnsaddr) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != dnsaddr) \ + goto bad; \ + try_.neg = 0; \ + } + +#define REJCIWINS(opt, addr) \ + if (addr && \ + ((cilen = p[1]) == CILEN_ADDR) && \ + len >= cilen && \ + p[0] == opt) { \ + u32_t l; \ + len -= cilen; \ + INCPTR(2, p); \ + GETLONG(l, p); \ + cilong = htonl(l); \ + /* Check rejected value. */ \ + if (cilong != addr) \ + goto bad; \ + try_.winsaddr[opt == CI_MS_WINS2] = 0; \ + } + + REJCIADDRS(CI_ADDRS, !go->neg_addr && go->old_addrs, + go->ouraddr, go->hisaddr); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj, + go->maxslotindex, go->cflag); + + REJCIADDR(CI_ADDR, neg_addr, go->ouraddr); + + REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]); + + REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]); + + REJCIWINS(CI_MS_WINS1, go->winsaddr[0]); + + REJCIWINS(CI_MS_WINS2, go->winsaddr[1]); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + IPCPDEBUG(("ipcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipcp_reqci - Check the peer's requested CIs and send appropriate response. + * Callback from fsm_rconfreq, Receive Configure Request + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * len = Length of requested CIs + */ +static int ipcp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + ipcp_options *wo = &pcb->ipcp_wantoptions; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *ao = &pcb->ipcp_allowoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + u32_t tl, ciaddr1, ciaddr2;/* Parsed address values */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + u_char maxslotindex, cflag; + int d; + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPCPDEBUG(("ipcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_ADDRS: + if (!ao->old_addrs || ho->neg_addr || + cilen != CILEN_ADDRS) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * If neither we nor he knows his address, reject the option. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + /* + * If he doesn't know our address, or if we both have our address + * but disagree about it, then NAK it with our idea. + */ + GETLONG(tl, p); /* Parse desination address (ours) */ + ciaddr2 = htonl(tl); + if (ciaddr2 != wo->ouraddr) { + if (ciaddr2 == 0 || !wo->accept_local) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->ouraddr); + PUTLONG(tl, p); + } + } else { + wo->ouraddr = ciaddr2; /* accept peer's idea */ + } + } + + ho->old_addrs = 1; + ho->hisaddr = ciaddr1; + ho->ouraddr = ciaddr2; + break; + + case CI_ADDR: + if (!ao->neg_addr || ho->old_addrs || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no address, or if we both have his address but + * disagree about it, then NAK it with our idea. + * In particular, if we don't know his address, but he does, + * then accept it. + */ + GETLONG(tl, p); /* Parse source address (his) */ + ciaddr1 = htonl(tl); + if (ciaddr1 != wo->hisaddr + && (ciaddr1 == 0 || !wo->accept_remote)) { + orc = CONFNAK; + if (!reject_if_disagree) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, p); + } + } else if (ciaddr1 == 0 && wo->hisaddr == 0) { + /* + * Don't ACK an address of 0.0.0.0 - reject it instead. + */ + orc = CONFREJ; + wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */ + break; + } + + ho->neg_addr = 1; + ho->hisaddr = ciaddr1; + break; + + case CI_MS_DNS1: + case CI_MS_DNS2: + /* Microsoft primary or secondary DNS request */ + d = citype == CI_MS_DNS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->dnsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->dnsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->dnsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_MS_WINS1: + case CI_MS_WINS2: + /* Microsoft primary or secondary WINS request */ + d = citype == CI_MS_WINS2; + + /* If we do not have a DNS address then we cannot send it */ + if (ao->winsaddr[d] == 0 || + cilen != CILEN_ADDR) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETLONG(tl, p); + if (htonl(tl) != ao->winsaddr[d]) { + DECPTR(sizeof(u32_t), p); + tl = ntohl(ao->winsaddr[d]); + PUTLONG(tl, p); + orc = CONFNAK; + } + break; + + case CI_COMPRESSTYPE: + if (!ao->neg_vj || + (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + if (!(cishort == IPCP_VJ_COMP || + (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + if (cilen == CILEN_VJ) { + GETCHAR(maxslotindex, p); + if (maxslotindex > ao->maxslotindex) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(ao->maxslotindex, p); + } + } + GETCHAR(cflag, p); + if (cflag && !ao->cflag) { + orc = CONFNAK; + if (!reject_if_disagree){ + DECPTR(1, p); + PUTCHAR(wo->cflag, p); + } + } + ho->maxslotindex = maxslotindex; + ho->cflag = cflag; + } else { + ho->old_vj = 1; + ho->maxslotindex = MAX_STATES - 1; + ho->cflag = 1; + } + break; + + default: + orc = CONFREJ; + break; + } +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + MEMCPY(ucp, cip, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their address, and they didn't send their address, then we + * send a NAK with a CI_ADDR option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_addr && !ho->old_addrs && + wo->req_addr && !reject_if_disagree && !pcb->settings.noremoteip) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_addr = 0; /* don't ask again */ + } + PUTCHAR(CI_ADDR, ucp); + PUTCHAR(CILEN_ADDR, ucp); + tl = ntohl(wo->hisaddr); + PUTLONG(tl, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +#if 0 /* UNUSED */ +/* + * ip_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void +ip_check_options() +{ + struct hostent *hp; + u32_t local; + ipcp_options *wo = &ipcp_wantoptions[0]; + + /* + * Default our local IP address based on our hostname. + * If local IP address already given, don't bother. + */ + if (wo->ouraddr == 0 && !disable_defaultip) { + /* + * Look up our hostname (possibly with domain name appended) + * and take the first IP address as our local IP address. + * If there isn't an IP address for our hostname, too bad. + */ + wo->accept_local = 1; /* don't insist on this default value */ + if ((hp = gethostbyname(hostname)) != NULL) { + local = *(u32_t *)hp->h_addr; + if (local != 0 && !bad_ip_adrs(local)) + wo->ouraddr = local; + } + } + ask_for_local = wo->ouraddr != 0 || !disable_defaultip; +} +#endif /* UNUSED */ + +#if DEMAND_SUPPORT +/* + * ip_demand_conf - configure the interface as though + * IPCP were up, for use with dial-on-demand. + */ +static int +ip_demand_conf(u) + int u; +{ + ppp_pcb *pcb = &ppp_pcb_list[u]; + ipcp_options *wo = &ipcp_wantoptions[u]; + + if (wo->hisaddr == 0 && !pcb->settings.noremoteip) { + /* make up an arbitrary address for the peer */ + wo->hisaddr = htonl(0x0a707070 + ifunit); + wo->accept_remote = 1; + } + if (wo->ouraddr == 0) { + /* make up an arbitrary address for us */ + wo->ouraddr = htonl(0x0a404040 + ifunit); + wo->accept_local = 1; + ask_for_local = 0; /* don't tell the peer this address */ + } + if (!sifaddr(pcb, wo->ouraddr, wo->hisaddr, get_mask(wo->ouraddr))) + return 0; + if (!sifup(pcb)) + return 0; + if (!sifnpmode(pcb, PPP_IP, NPMODE_QUEUE)) + return 0; +#if 0 /* UNUSED */ + if (wo->default_route) + if (sifdefaultroute(pcb, wo->ouraddr, wo->hisaddr, + wo->replace_default_route)) + default_route_set[u] = 1; +#endif /* UNUSED */ + if (wo->proxy_arp) + if (sifproxyarp(pcb, wo->hisaddr)) + proxy_arp_set[u] = 1; + + ppp_notice("local IP address %I", wo->ouraddr); + if (wo->hisaddr) + ppp_notice("remote IP address %I", wo->hisaddr); + + return 1; +} +#endif /* DEMAND_SUPPORT */ + +/* + * ipcp_up - IPCP has come UP. + * + * Configure the IP network interface appropriately and bring it up. + */ +static void ipcp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + u32_t mask; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + ipcp_options *wo = &pcb->ipcp_wantoptions; + + IPCPDEBUG(("ipcp: up")); + + /* + * We must have a non-zero IP address for both ends of the link. + */ + if (!ho->neg_addr && !ho->old_addrs) + ho->hisaddr = wo->hisaddr; + + if (!(go->neg_addr || go->old_addrs) && (wo->neg_addr || wo->old_addrs) + && wo->ouraddr != 0) { + ppp_error("Peer refused to agree to our IP address"); + ipcp_close(f->pcb, "Refused our IP address"); + return; + } + if (go->ouraddr == 0) { + ppp_error("Could not determine local IP address"); + ipcp_close(f->pcb, "Could not determine local IP address"); + return; + } + if (ho->hisaddr == 0 && !pcb->settings.noremoteip) { + ho->hisaddr = htonl(0x0a404040); + ppp_warn("Could not determine remote IP address: defaulting to %I", + ho->hisaddr); + } +#if 0 /* UNUSED */ + script_setenv("IPLOCAL", ip_ntoa(go->ouraddr), 0); + if (ho->hisaddr != 0) + script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr), 1); +#endif /* UNUSED */ + + if (!go->req_dns1) + go->dnsaddr[0] = 0; + if (!go->req_dns2) + go->dnsaddr[1] = 0; +#if 0 /* UNUSED */ + if (go->dnsaddr[0]) + script_setenv("DNS1", ip_ntoa(go->dnsaddr[0]), 0); + if (go->dnsaddr[1]) + script_setenv("DNS2", ip_ntoa(go->dnsaddr[1]), 0); +#endif /* UNUSED */ + if (pcb->settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) { + sdns(pcb, go->dnsaddr[0], go->dnsaddr[1]); +#if 0 /* UNUSED */ + script_setenv("USEPEERDNS", "1", 0); + create_resolv(go->dnsaddr[0], go->dnsaddr[1]); +#endif /* UNUSED */ + } + +/* FIXME: check why it fails, just to know */ +#if 0 /* Unused */ + /* + * Check that the peer is allowed to use the IP address it wants. + */ + if (ho->hisaddr != 0 && !auth_ip_addr(f->unit, ho->hisaddr)) { + ppp_error("Peer is not authorized to use remote address %I", ho->hisaddr); + ipcp_close(f->unit, "Unauthorized remote IP address"); + return; + } +#endif /* Unused */ + + /* set tcp compression */ + sifvjcomp(pcb, ho->neg_vj, ho->cflag, ho->maxslotindex); + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IP packets. + */ + if (demand) { + if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) { + ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr, + wo->replace_default_route); + if (go->ouraddr != wo->ouraddr) { + ppp_warn("Local IP address changed to %I", go->ouraddr); + script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0); + wo->ouraddr = go->ouraddr; + } else + script_unsetenv("OLDIPLOCAL"); + if (ho->hisaddr != wo->hisaddr && wo->hisaddr != 0) { + ppp_warn("Remote IP address changed to %I", ho->hisaddr); + script_setenv("OLDIPREMOTE", ip_ntoa(wo->hisaddr), 0); + wo->hisaddr = ho->hisaddr; + } else + script_unsetenv("OLDIPREMOTE"); + + /* Set the interface to the new addresses */ + mask = get_mask(go->ouraddr); + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->unit, "Interface configuration failed"); + return; + } + + /* assign a default route through the interface if required */ + if (ipcp_wantoptions[f->unit].default_route) + if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr, + wo->replace_default_route)) + default_route_set[f->unit] = 1; + + /* Make a proxy ARP entry if requested. */ + if (ho->hisaddr != 0 && ipcp_wantoptions[f->unit].proxy_arp) + if (sifproxyarp(pcb, ho->hisaddr)) + proxy_arp_set[f->unit] = 1; + + } + demand_rexmit(PPP_IP,go->ouraddr); + sifnpmode(pcb, PPP_IP, NPMODE_PASS); + + } else +#endif /* DEMAND_SUPPORT */ + { + /* + * Set IP addresses and (if specified) netmask. + */ + mask = get_mask(go->ouraddr); + +#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->pcb, "Interface configuration failed"); + return; + } +#endif + + /* bring the interface up for IP */ + if (!sifup(pcb)) { +#if PPP_DEBUG + ppp_warn("Interface failed to come up"); +#endif /* PPP_DEBUG */ + ipcp_close(f->pcb, "Interface configuration failed"); + return; + } + +#if (defined(SVR4) && (defined(SNI) || defined(__USLC__))) + if (!sifaddr(pcb, go->ouraddr, ho->hisaddr, mask)) { +#if PPP_DEBUG + ppp_warn("Interface configuration failed"); +#endif /* PPP_DEBUG */ + ipcp_close(f->unit, "Interface configuration failed"); + return; + } +#endif + sifnpmode(pcb, PPP_IP, NPMODE_PASS); + +#if 0 /* UNUSED */ + /* assign a default route through the interface if required */ + if (wo->default_route) + if (sifdefaultroute(pcb, go->ouraddr, ho->hisaddr, + wo->replace_default_route)) + pcb->default_route_set = 1; +#endif /* UNUSED */ + + /* Make a proxy ARP entry if requested. */ + if (ho->hisaddr != 0 && wo->proxy_arp) + if (sifproxyarp(pcb, ho->hisaddr)) + pcb->proxy_arp_set = 1; + + wo->ouraddr = go->ouraddr; + + ppp_notice("local IP address %I", go->ouraddr); + if (ho->hisaddr != 0) + ppp_notice("remote IP address %I", ho->hisaddr); + if (go->dnsaddr[0]) + ppp_notice("primary DNS address %I", go->dnsaddr[0]); + if (go->dnsaddr[1]) + ppp_notice("secondary DNS address %I", go->dnsaddr[1]); + } + +#if PPP_STATS_SUPPORT + reset_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ + + np_up(pcb, PPP_IP); + pcb->ipcp_is_up = 1; + +#if PPP_NOTIFY + notify(ip_up_notifier, 0); +#endif /* PPP_NOTIFY */ +#if 0 /* UNUSED */ + if (ip_up_hook) + ip_up_hook(); +#endif /* UNUSED */ +} + + +/* + * ipcp_down - IPCP has gone DOWN. + * + * Take the IP network interface down, clear its addresses + * and delete routes through it. + */ +static void ipcp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipcp_options *ho = &pcb->ipcp_hisoptions; + ipcp_options *go = &pcb->ipcp_gotoptions; + + IPCPDEBUG(("ipcp: down")); +#if PPP_STATS_SUPPORT + /* XXX a bit IPv4-centric here, we only need to get the stats + * before the interface is marked down. */ + /* XXX more correct: we must get the stats before running the notifiers, + * at least for the radius plugin */ + update_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ +#if PPP_NOTIFY + notify(ip_down_notifier, 0); +#endif /* PPP_NOTIFY */ +#if 0 /* UNUSED */ + if (ip_down_hook) + ip_down_hook(); +#endif /* UNUSED */ + if (pcb->ipcp_is_up) { + pcb->ipcp_is_up = 0; + np_down(pcb, PPP_IP); + } + sifvjcomp(pcb, 0, 0, 0); + +#if PPP_STATS_SUPPORT + print_link_stats(); /* _after_ running the notifiers and ip_down_hook(), + * because print_link_stats() sets link_stats_valid + * to 0 (zero) */ +#endif /* PPP_STATS_SUPPORT */ + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(pcb, PPP_IP, NPMODE_QUEUE); + } else +#endif /* DEMAND_SUPPORT */ + { + sifnpmode(pcb, PPP_IP, NPMODE_DROP); + sifdown(pcb); + ipcp_clear_addrs(pcb, go->ouraddr, + ho->hisaddr, 0); + cdns(pcb, go->dnsaddr[0], go->dnsaddr[1]); + } +} + + +/* + * ipcp_clear_addrs() - clear the interface addresses, routes, + * proxy arp entries, etc. + */ +static void ipcp_clear_addrs(ppp_pcb *pcb, u32_t ouraddr, u32_t hisaddr, u8_t replacedefaultroute) { + LWIP_UNUSED_ARG(replacedefaultroute); + + if (pcb->proxy_arp_set) { + cifproxyarp(pcb, hisaddr); + pcb->proxy_arp_set = 0; + } +#if 0 /* UNUSED */ + /* If replacedefaultroute, sifdefaultroute will be called soon + * with replacedefaultroute set and that will overwrite the current + * default route. This is the case only when doing demand, otherwise + * during demand, this cifdefaultroute would restore the old default + * route which is not what we want in this case. In the non-demand + * case, we'll delete the default route and restore the old if there + * is one saved by an sifdefaultroute with replacedefaultroute. + */ + if (!replacedefaultroute && pcb->default_route_set) { + cifdefaultroute(pcb, ouraddr, hisaddr); + pcb->default_route_set = 0; + } +#endif /* UNUSED */ + cifaddr(pcb, ouraddr, hisaddr); +} + + +/* + * ipcp_finished - possibly shut down the lower layers. + */ +static void ipcp_finished(fsm *f) { + ppp_pcb *pcb = f->pcb; + if (pcb->ipcp_is_open) { + pcb->ipcp_is_open = 0; + np_finished(pcb, PPP_IP); + } +} + + +#if 0 /* UNUSED */ +/* + * create_resolv - create the replacement resolv.conf file + */ +static void +create_resolv(peerdns1, peerdns2) + u32_t peerdns1, peerdns2; +{ + +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ipcp_printpkt - print the contents of an IPCP packet. + */ +static const char *ipcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int ipcp_printpkt(u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)sizeof(ipcp_codenames) / (int)sizeof(char *)) + printer(arg, " %s", ipcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_ADDRS: + if (olen == CILEN_ADDRS) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addrs %I", htonl(cilong)); + GETLONG(cilong, p); + printer(arg, " %I", htonl(cilong)); + } + break; + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + switch (cishort) { + case IPCP_VJ_COMP: + printer(arg, "VJ"); + break; + case IPCP_VJ_COMP_OLD: + printer(arg, "old-VJ"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_ADDR: + if (olen == CILEN_ADDR) { + p += 2; + GETLONG(cilong, p); + printer(arg, "addr %I", htonl(cilong)); + } + break; + case CI_MS_DNS1: + case CI_MS_DNS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-dns%d %I", (code == CI_MS_DNS1? 1: 2), + htonl(cilong)); + break; + case CI_MS_WINS1: + case CI_MS_WINS2: + p += 2; + GETLONG(cilong, p); + printer(arg, "ms-wins %I", htonl(cilong)); + break; + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string((char *)p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#if DEMAND_SUPPORT +/* + * ip_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP_HDRLEN 20 /* bytes */ +#define IP_OFFMASK 0x1fff +#ifndef IPPROTO_TCP +#define IPPROTO_TCP 6 +#endif +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define net_short(x) (((x)[0] << 8) + (x)[1]) +#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF) +#define get_ipoff(x) net_short((unsigned char *)(x) + 6) +#define get_ipproto(x) (((unsigned char *)(x))[9]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int +ip_active_pkt(pkt, len) + u_char *pkt; + int len; +{ + u_char *tcp; + int hlen; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP_HDRLEN) + return 0; + if ((get_ipoff(pkt) & IP_OFFMASK) != 0) + return 0; + if (get_ipproto(pkt) != IPPROTO_TCP) + return 1; + hlen = get_iphl(pkt) * 4; + if (len < hlen + TCP_HDRLEN) + return 0; + tcp = pkt + hlen; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) + return 0; + return 1; +} +#endif /* DEMAND_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipv6cp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipv6cp.c new file mode 100644 index 0000000..5ff6183 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ipv6cp.c @@ -0,0 +1,1498 @@ +/* + * ipv6cp.c - PPP IPV6 Control Protocol. + * + * Copyright (c) 1999 Tommi Komulainen. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Tommi Komulainen + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* Original version, based on RFC2023 : + + Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE, + Alain.Durand@imag.fr, IMAG, + Jean-Luc.Richier@imag.fr, IMAG-LSR. + + Ce travail a �t� fait au sein du GIE DYADE (Groupement d'Int�r�t + �conomique ayant pour membres BULL S.A. et l'INRIA). + + Ce logiciel informatique est disponible aux conditions + usuelles dans la recherche, c'est-�-dire qu'il peut + �tre utilis�, copi�, modifi�, distribu� � l'unique + condition que ce texte soit conserv� afin que + l'origine de ce logiciel soit reconnue. + + Le nom de l'Institut National de Recherche en Informatique + et en Automatique (INRIA), de l'IMAG, ou d'une personne morale + ou physique ayant particip� � l'�laboration de ce logiciel ne peut + �tre utilis� sans son accord pr�alable explicite. + + Ce logiciel est fourni tel quel sans aucune garantie, + support ou responsabilit� d'aucune sorte. + Ce logiciel est d�riv� de sources d'origine + "University of California at Berkeley" et + "Digital Equipment Corporation" couvertes par des copyrights. + + L'Institut d'Informatique et de Math�matiques Appliqu�es de Grenoble (IMAG) + est une f�d�ration d'unit�s mixtes de recherche du CNRS, de l'Institut National + Polytechnique de Grenoble et de l'Universit� Joseph Fourier regroupant + sept laboratoires dont le laboratoire Logiciels, Syst�mes, R�seaux (LSR). + + This work has been done in the context of GIE DYADE (joint R & D venture + between BULL S.A. and INRIA). + + This software is available with usual "research" terms + with the aim of retain credits of the software. + Permission to use, copy, modify and distribute this software for any + purpose and without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies, + and the name of INRIA, IMAG, or any contributor not be used in advertising + or publicity pertaining to this material without the prior explicit + permission. The software is provided "as is" without any + warranties, support or liabilities of any kind. + This software is derived from source code from + "University of California at Berkeley" and + "Digital Equipment Corporation" protected by copyrights. + + Grenoble's Institute of Computer Science and Applied Mathematics (IMAG) + is a federation of seven research units funded by the CNRS, National + Polytechnic Institute of Grenoble and University Joseph Fourier. + The research unit in Software, Systems, Networks (LSR) is member of IMAG. +*/ + +/* + * Derived from : + * + * + * ipcp.c - PPP IP Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $Id: ipv6cp.c,v 1.21 2005/08/25 23:59:34 paulus Exp $ + */ + +/* + * TODO: + * + * Proxy Neighbour Discovery. + * + * Better defines for selecting the ordering of + * interface up / set address. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/fsm.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/ipv6cp.h" +#include "netif/ppp/magic.h" + +/* global vars */ +#if 0 /* UNUSED */ +int no_ifaceid_neg = 0; +#endif /* UNUSED */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void ipv6cp_resetci(fsm *f); /* Reset our CI */ +static int ipv6cp_cilen(fsm *f); /* Return length of our CI */ +static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI */ +static int ipv6cp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */ +static int ipv6cp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree); /* Rcv CI */ +static void ipv6cp_up(fsm *f); /* We're UP */ +static void ipv6cp_down(fsm *f); /* We're DOWN */ +static void ipv6cp_finished(fsm *f); /* Don't need lower layer */ + +static const fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */ + ipv6cp_resetci, /* Reset our Configuration Information */ + ipv6cp_cilen, /* Length of our Configuration Information */ + ipv6cp_addci, /* Add our Configuration Information */ + ipv6cp_ackci, /* ACK our Configuration Information */ + ipv6cp_nakci, /* NAK our Configuration Information */ + ipv6cp_rejci, /* Reject our Configuration Information */ + ipv6cp_reqci, /* Request peer's Configuration Information */ + ipv6cp_up, /* Called when fsm reaches OPENED state */ + ipv6cp_down, /* Called when fsm leaves OPENED state */ + NULL, /* Called when we want the lower layer up */ + ipv6cp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + NULL, /* Called to handle protocol-specific codes */ + "IPV6CP" /* String name of protocol */ +}; + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static int setifaceid(char **arg)); +static void printifaceid(option_t *, + void (*)(void *, char *, ...), void *)); + +static option_t ipv6cp_option_list[] = { + { "ipv6", o_special, (void *)setifaceid, + "Set interface identifiers for IPV6", + OPT_A2PRINTER, (void *)printifaceid }, + + { "+ipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Enable IPv6 and IPv6CP", OPT_PRIO | 1 }, + { "noipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB }, + { "-ipv6", o_bool, &ipv6cp_protent.enabled_flag, + "Disable IPv6 and IPv6CP", OPT_PRIOSUB | OPT_ALIAS }, + + { "ipv6cp-accept-local", o_bool, &ipv6cp_allowoptions[0].accept_local, + "Accept peer's interface identifier for us", 1 }, + + { "ipv6cp-use-ipaddr", o_bool, &ipv6cp_allowoptions[0].use_ip, + "Use (default) IPv4 address as interface identifier", 1 }, + + { "ipv6cp-use-persistent", o_bool, &ipv6cp_wantoptions[0].use_persistent, + "Use uniquely-available persistent value for link local address", 1 }, + + { "ipv6cp-restart", o_int, &ipv6cp_fsm[0].timeouttime, + "Set timeout for IPv6CP", OPT_PRIO }, + { "ipv6cp-max-terminate", o_int, &ipv6cp_fsm[0].maxtermtransmits, + "Set max #xmits for term-reqs", OPT_PRIO }, + { "ipv6cp-max-configure", o_int, &ipv6cp_fsm[0].maxconfreqtransmits, + "Set max #xmits for conf-reqs", OPT_PRIO }, + { "ipv6cp-max-failure", o_int, &ipv6cp_fsm[0].maxnakloops, + "Set max #conf-naks for IPv6CP", OPT_PRIO }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points from main code. + */ +static void ipv6cp_init(ppp_pcb *pcb); +static void ipv6cp_open(ppp_pcb *pcb); +static void ipv6cp_close(ppp_pcb *pcb, const char *reason); +static void ipv6cp_lowerup(ppp_pcb *pcb); +static void ipv6cp_lowerdown(ppp_pcb *pcb); +static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len); +static void ipv6cp_protrej(ppp_pcb *pcb); +#if PPP_OPTIONS +static void ipv6_check_options(void); +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT +static int ipv6_demand_conf(int u); +#endif /* DEMAND_SUPPORT */ +#if PRINTPKT_SUPPORT +static int ipv6cp_printpkt(u_char *p, int plen, + void (*printer)(void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ +#if DEMAND_SUPPORT +static int ipv6_active_pkt(u_char *pkt, int len); +#endif /* DEMAND_SUPPORT */ + +const struct protent ipv6cp_protent = { + PPP_IPV6CP, + ipv6cp_init, + ipv6cp_input, + ipv6cp_protrej, + ipv6cp_lowerup, + ipv6cp_lowerdown, + ipv6cp_open, + ipv6cp_close, +#if PRINTPKT_SUPPORT + ipv6cp_printpkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, + 1, +#if PRINTPKT_SUPPORT + "IPV6CP", + "IPV6", +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + ipv6cp_option_list, + ipv6_check_options, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + ipv6_demand_conf, + ipv6_active_pkt +#endif /* DEMAND_SUPPORT */ +}; + +static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid); +#if 0 /* UNUSED */ +static void ipv6cp_script(char *)); +static void ipv6cp_script_done(void *)); +#endif /* UNUSED */ + +/* + * Lengths of configuration options. + */ +#define CILEN_VOID 2 +#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */ +#define CILEN_IFACEID 10 /* RFC2472, interface identifier */ + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if 0 /* UNUSED */ +/* + * This state variable is used to ensure that we don't + * run an ipcp-up/down script while one is already running. + */ +static enum script_state { + s_down, + s_up, +} ipv6cp_script_state; +static pid_t ipv6cp_script_pid; +#endif /* UNUSED */ + +static char *llv6_ntoa(eui64_t ifaceid); + +#if PPP_OPTIONS +/* + * setifaceid - set the interface identifiers manually + */ +static int +setifaceid(argv) + char **argv; +{ + char *comma, *arg, c; + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + struct in6_addr addr; + static int prio_local, prio_remote; + +#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \ + (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) ) + + arg = *argv; + if ((comma = strchr(arg, ',')) == NULL) + comma = arg + strlen(arg); + + /* + * If comma first character, then no local identifier + */ + if (comma != arg) { + c = *comma; + *comma = '\0'; + + if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) { + option_error("Illegal interface identifier (local): %s", arg); + return 0; + } + + if (option_priority >= prio_local) { + eui64_copy(addr.s6_addr32[2], wo->ourid); + wo->opt_local = 1; + prio_local = option_priority; + } + *comma = c; + } + + /* + * If comma last character, the no remote identifier + */ + if (*comma != 0 && *++comma != '\0') { + if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) { + option_error("Illegal interface identifier (remote): %s", comma); + return 0; + } + if (option_priority >= prio_remote) { + eui64_copy(addr.s6_addr32[2], wo->hisid); + wo->opt_remote = 1; + prio_remote = option_priority; + } + } + + if (override_value("+ipv6", option_priority, option_source)) + ipv6cp_protent.enabled_flag = 1; + return 1; +} + +static void +printifaceid(opt, printer, arg) + option_t *opt; + void (*printer)(void *, char *, ...)); + void *arg; +{ + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + + if (wo->opt_local) + printer(arg, "%s", llv6_ntoa(wo->ourid)); + printer(arg, ","); + if (wo->opt_remote) + printer(arg, "%s", llv6_ntoa(wo->hisid)); +} +#endif /* PPP_OPTIONS */ + +/* + * Make a string representation of a network address. + */ +static char * +llv6_ntoa(eui64_t ifaceid) +{ + static char b[64]; + + sprintf(b, "fe80::%s", eui64_ntoa(ifaceid)); + return b; +} + + +/* + * ipv6cp_init - Initialize IPV6CP. + */ +static void ipv6cp_init(ppp_pcb *pcb) { + fsm *f = &pcb->ipv6cp_fsm; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_IPV6CP; + f->callbacks = &ipv6cp_callbacks; + fsm_init(f); + + memset(wo, 0, sizeof(*wo)); + memset(ao, 0, sizeof(*ao)); + + wo->accept_local = 1; + wo->neg_ifaceid = 1; + ao->neg_ifaceid = 1; + +#ifdef IPV6CP_COMP + wo->neg_vj = 1; + ao->neg_vj = 1; + wo->vj_protocol = IPV6CP_COMP; +#endif + +} + + +/* + * ipv6cp_open - IPV6CP is allowed to come up. + */ +static void ipv6cp_open(ppp_pcb *pcb) { + fsm_open(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_close - Take IPV6CP down. + */ +static void ipv6cp_close(ppp_pcb *pcb, const char *reason) { + fsm_close(&pcb->ipv6cp_fsm, reason); +} + + +/* + * ipv6cp_lowerup - The lower layer is up. + */ +static void ipv6cp_lowerup(ppp_pcb *pcb) { + fsm_lowerup(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_lowerdown - The lower layer is down. + */ +static void ipv6cp_lowerdown(ppp_pcb *pcb) { + fsm_lowerdown(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_input - Input IPV6CP packet. + */ +static void ipv6cp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm_input(&pcb->ipv6cp_fsm, p, len); +} + + +/* + * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP. + * + * Pretend the lower layer went down, so we shut up. + */ +static void ipv6cp_protrej(ppp_pcb *pcb) { + fsm_lowerdown(&pcb->ipv6cp_fsm); +} + + +/* + * ipv6cp_resetci - Reset our CI. + */ +static void ipv6cp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + + wo->req_ifaceid = wo->neg_ifaceid && ao->neg_ifaceid; + + if (!wo->opt_local) { + eui64_magic_nz(wo->ourid); + } + + *go = *wo; + eui64_zero(go->hisid); /* last proposed interface identifier */ +} + + +/* + * ipv6cp_cilen - Return length of our CI. + */ +static int ipv6cp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + +#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0) +#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0) + + return (LENCIIFACEID(go->neg_ifaceid) + + LENCIVJ(go->neg_vj)); +} + + +/* + * ipv6cp_addci - Add our desired CIs to a packet. + */ +static void ipv6cp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + int len = *lenp; + +#define ADDCIVJ(opt, neg, val) \ + if (neg) { \ + int vjlen = CILEN_COMPRESS; \ + if (len >= vjlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(vjlen, ucp); \ + PUTSHORT(val, ucp); \ + len -= vjlen; \ + } else \ + neg = 0; \ + } + +#define ADDCIIFACEID(opt, neg, val1) \ + if (neg) { \ + int idlen = CILEN_IFACEID; \ + if (len >= idlen) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(idlen, ucp); \ + eui64_put(val1, ucp); \ + len -= idlen; \ + } else \ + neg = 0; \ + } + + ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); + + ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); + + *lenp -= len; +} + + +/* + * ipv6cp_ackci - Ack our CIs. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int ipv6cp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_short cilen, citype, cishort; + eui64_t ifaceid; + + /* + * CIs must be in exactly the same order that we sent... + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ + +#define ACKCIVJ(opt, neg, val) \ + if (neg) { \ + int vjlen = CILEN_COMPRESS; \ + if ((len -= vjlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != vjlen || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } + +#define ACKCIIFACEID(opt, neg, val1) \ + if (neg) { \ + int idlen = CILEN_IFACEID; \ + if ((len -= idlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != idlen || \ + citype != opt) \ + goto bad; \ + eui64_get(ifaceid, p); \ + if (! eui64_equals(val1, ifaceid)) \ + goto bad; \ + } + + ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid); + + ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); + +bad: + IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!")); + return (0); +} + +/* + * ipv6cp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if IPV6CP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int ipv6cp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char citype, cilen, *next; + u_short cishort; + eui64_t ifaceid; + ipv6cp_options no; /* options we've seen Naks for */ + ipv6cp_options try_; /* options to request next time */ + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIIFACEID(opt, neg, code) \ + if (go->neg && \ + len >= (cilen = CILEN_IFACEID) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + eui64_get(ifaceid, p); \ + no.neg = 1; \ + code \ + } + +#define NAKCIVJ(opt, neg, code) \ + if (go->neg && \ + ((cilen = p[1]) == CILEN_COMPRESS) && \ + len >= cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } + + /* + * Accept the peer's idea of {our,his} interface identifier, if different + * from our idea, only if the accept_{local,remote} flag is set. + */ + NAKCIIFACEID(CI_IFACEID, neg_ifaceid, + if (treat_as_reject) { + try_.neg_ifaceid = 0; + } else if (go->accept_local) { + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->hisid)) /* bad luck */ + eui64_magic(ifaceid); + try_.ourid = ifaceid; + IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid))); + } + ); + +#ifdef IPV6CP_COMP + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + { + if (cishort == IPV6CP_COMP && !treat_as_reject) { + try_.vj_protocol = cishort; + } else { + try_.neg_vj = 0; + } + } + ); +#else + NAKCIVJ(CI_COMPRESSTYPE, neg_vj, + { + try_.neg_vj = 0; + } + ); +#endif + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If they want to negotiate about interface identifier, we comply. + * If they want us to ask for compression, we refuse. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if ( cilen < CILEN_VOID || (len -= cilen) < 0 ) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_COMPRESSTYPE: + if (go->neg_vj || no.neg_vj || + (cilen != CILEN_COMPRESS)) + goto bad; + no.neg_vj = 1; + break; + case CI_IFACEID: + if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID) + goto bad; + try_.neg_ifaceid = 1; + eui64_get(ifaceid, p); + if (go->accept_local) { + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->hisid)) /* bad luck */ + eui64_magic(ifaceid); + try_.ourid = ifaceid; + } + no.neg_ifaceid = 1; + break; + default: + break; + } + p = next; + } + + /* If there is still anything left, this packet is bad. */ + if (len != 0) + goto bad; + + /* + * OK, the Nak is good. Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + + return 1; + +bad: + IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!")); + return 0; +} + + +/* + * ipv6cp_rejci - Reject some of our CIs. + */ +static int ipv6cp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char cilen; + u_short cishort; + eui64_t ifaceid; + ipv6cp_options try_; /* options to request next time */ + + try_ = *go; + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIIFACEID(opt, neg, val1) \ + if (go->neg && \ + len >= (cilen = CILEN_IFACEID) && \ + p[1] == cilen && \ + p[0] == opt) { \ + len -= cilen; \ + INCPTR(2, p); \ + eui64_get(ifaceid, p); \ + /* Check rejected value. */ \ + if (! eui64_equals(ifaceid, val1)) \ + goto bad; \ + try_.neg = 0; \ + } + +#define REJCIVJ(opt, neg, val) \ + if (go->neg && \ + p[1] == CILEN_COMPRESS && \ + len >= p[1] && \ + p[0] == opt) { \ + len -= p[1]; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try_.neg = 0; \ + } + + REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid); + + REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!")); + return 0; +} + + +/* + * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * len = Length of requested CIs + * + */ +static int ipv6cp_reqci(fsm *f, u_char *inp, int *len, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + ipv6cp_options *ao = &pcb->ipv6cp_allowoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + u_short cilen, citype; /* Parsed len, type */ + u_short cishort; /* Parsed short value */ + eui64_t ifaceid; /* Parsed interface identifier */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *ucp = inp; /* Pointer to current output char */ + int l = *len; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_IFACEID: + IPV6CPDEBUG(("ipv6cp: received interface identifier ")); + + if (!ao->neg_ifaceid || + cilen != CILEN_IFACEID) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + + /* + * If he has no interface identifier, or if we both have same + * identifier then NAK it with new idea. + * In particular, if we don't know his identifier, but he does, + * then accept it. + */ + eui64_get(ifaceid, p); + IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid))); + if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) { + orc = CONFREJ; /* Reject CI */ + break; + } + if (!eui64_iszero(wo->hisid) && + !eui64_equals(ifaceid, wo->hisid) && + eui64_iszero(go->hisid)) { + + orc = CONFNAK; + ifaceid = wo->hisid; + go->hisid = ifaceid; + DECPTR(sizeof(ifaceid), p); + eui64_put(ifaceid, p); + } else + if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) { + orc = CONFNAK; + if (eui64_iszero(go->hisid)) /* first time, try option */ + ifaceid = wo->hisid; + while (eui64_iszero(ifaceid) || + eui64_equals(ifaceid, go->ourid)) /* bad luck */ + eui64_magic(ifaceid); + go->hisid = ifaceid; + DECPTR(sizeof(ifaceid), p); + eui64_put(ifaceid, p); + } + + ho->neg_ifaceid = 1; + ho->hisid = ifaceid; + break; + + case CI_COMPRESSTYPE: + IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE ")); + if (!ao->neg_vj || + (cilen != CILEN_COMPRESS)) { + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + IPV6CPDEBUG(("(%d)", cishort)); + +#ifdef IPV6CP_COMP + if (!(cishort == IPV6CP_COMP)) { + orc = CONFREJ; + break; + } + + ho->neg_vj = 1; + ho->vj_protocol = cishort; + break; +#else + orc = CONFREJ; + break; +#endif + + default: + orc = CONFREJ; + break; + } + +endswitch: + IPV6CPDEBUG((" (%s)\n", CODENAME(orc))); + + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree) /* Getting fed up with sending NAKs? */ + orc = CONFREJ; /* Get tough if so */ + else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + if (rc == CONFACK) { /* Ack'd all prior CIs? */ + rc = CONFNAK; /* Not anymore... */ + ucp = inp; /* Backup */ + } + } + } + + if (orc == CONFREJ && /* Reject this CI */ + rc != CONFREJ) { /* but no prior ones? */ + rc = CONFREJ; + ucp = inp; /* Backup */ + } + + /* Need to move CI? */ + if (ucp != cip) + MEMCPY(ucp, cip, cilen); /* Move it */ + + /* Update output pointer */ + INCPTR(cilen, ucp); + } + + /* + * If we aren't rejecting this packet, and we want to negotiate + * their identifier and they didn't send their identifier, then we + * send a NAK with a CI_IFACEID option appended. We assume the + * input buffer is long enough that we can append the extra + * option safely. + */ + if (rc != CONFREJ && !ho->neg_ifaceid && + wo->req_ifaceid && !reject_if_disagree) { + if (rc == CONFACK) { + rc = CONFNAK; + ucp = inp; /* reset pointer */ + wo->req_ifaceid = 0; /* don't ask again */ + } + PUTCHAR(CI_IFACEID, ucp); + PUTCHAR(CILEN_IFACEID, ucp); + eui64_put(wo->hisid, ucp); + } + + *len = ucp - inp; /* Compute output length */ + IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc))); + return (rc); /* Return final code */ +} + +#if PPP_OPTIONS +/* + * ipv6_check_options - check that any IP-related options are OK, + * and assign appropriate defaults. + */ +static void ipv6_check_options() { + ipv6cp_options *wo = &ipv6cp_wantoptions[0]; + + if (!ipv6cp_protent.enabled_flag) + return; + + /* + * Persistent link-local id is only used when user has not explicitly + * configure/hard-code the id + */ + if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) { + + /* + * On systems where there are no Ethernet interfaces used, there + * may be other ways to obtain a persistent id. Right now, it + * will fall back to using magic [see eui64_magic] below when + * an EUI-48 from MAC address can't be obtained. Other possibilities + * include obtaining EEPROM serial numbers, or some other unique + * yet persistent number. On Sparc platforms, this is possible, + * but too bad there's no standards yet for x86 machines. + */ + if (ether_to_eui64(&wo->ourid)) { + wo->opt_local = 1; + } + } + + if (!wo->opt_local) { /* init interface identifier */ + if (wo->use_ip && eui64_iszero(wo->ourid)) { + eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr)); + if (!eui64_iszero(wo->ourid)) + wo->opt_local = 1; + } + + while (eui64_iszero(wo->ourid)) + eui64_magic(wo->ourid); + } + + if (!wo->opt_remote) { + if (wo->use_ip && eui64_iszero(wo->hisid)) { + eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr)); + if (!eui64_iszero(wo->hisid)) + wo->opt_remote = 1; + } + } + + if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) { + option_error("local/remote LL address required for demand-dialling\n"); + exit(1); + } +} +#endif /* PPP_OPTIONS */ + +#if DEMAND_SUPPORT +/* + * ipv6_demand_conf - configure the interface as though + * IPV6CP were up, for use with dial-on-demand. + */ +static int ipv6_demand_conf(int u) { + ipv6cp_options *wo = &ipv6cp_wantoptions[u]; + + if (!sif6up(u)) + return 0; + + if (!sif6addr(u, wo->ourid, wo->hisid)) + return 0; + + if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE)) + return 0; + + ppp_notice("ipv6_demand_conf"); + ppp_notice("local LL address %s", llv6_ntoa(wo->ourid)); + ppp_notice("remote LL address %s", llv6_ntoa(wo->hisid)); + + return 1; +} +#endif /* DEMAND_SUPPORT */ + + +/* + * ipv6cp_up - IPV6CP has come UP. + * + * Configure the IPv6 network interface appropriately and bring it up. + */ +static void ipv6cp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *wo = &pcb->ipv6cp_wantoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + + IPV6CPDEBUG(("ipv6cp: up")); + + /* + * We must have a non-zero LL address for both ends of the link. + */ + if (!ho->neg_ifaceid) + ho->hisid = wo->hisid; + +#if 0 /* UNUSED */ + if(!no_ifaceid_neg) { +#endif /* UNUSED */ + if (eui64_iszero(ho->hisid)) { + ppp_error("Could not determine remote LL address"); + ipv6cp_close(f->pcb, "Could not determine remote LL address"); + return; + } + if (eui64_iszero(go->ourid)) { + ppp_error("Could not determine local LL address"); + ipv6cp_close(f->pcb, "Could not determine local LL address"); + return; + } + if (eui64_equals(go->ourid, ho->hisid)) { + ppp_error("local and remote LL addresses are equal"); + ipv6cp_close(f->pcb, "local and remote LL addresses are equal"); + return; + } +#if 0 /* UNUSED */ + } +#endif /* UNUSED */ +#if 0 /* UNUSED */ + script_setenv("LLLOCAL", llv6_ntoa(go->ourid), 0); + script_setenv("LLREMOTE", llv6_ntoa(ho->hisid), 0); +#endif /* UNUSED */ + +#ifdef IPV6CP_COMP + /* set tcp compression */ + sif6comp(f->unit, ho->neg_vj); +#endif + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, the interface is already + * configured, so we put out any saved-up packets, then set the + * interface to pass IPv6 packets. + */ + if (demand) { + if (! eui64_equals(go->ourid, wo->ourid) || + ! eui64_equals(ho->hisid, wo->hisid)) { + if (! eui64_equals(go->ourid, wo->ourid)) + warn("Local LL address changed to %s", + llv6_ntoa(go->ourid)); + if (! eui64_equals(ho->hisid, wo->hisid)) + warn("Remote LL address changed to %s", + llv6_ntoa(ho->hisid)); + ipv6cp_clear_addrs(f->pcb, go->ourid, ho->hisid); + + /* Set the interface to the new addresses */ + if (!sif6addr(f->pcb, go->ourid, ho->hisid)) { + if (debug) + warn("sif6addr failed"); + ipv6cp_close(f->unit, "Interface configuration failed"); + return; + } + + } + demand_rexmit(PPP_IPV6); + sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS); + + } else +#endif /* DEMAND_SUPPORT */ + { + /* + * Set LL addresses + */ + if (!sif6addr(f->pcb, go->ourid, ho->hisid)) { + PPPDEBUG(LOG_DEBUG, ("sif6addr failed")); + ipv6cp_close(f->pcb, "Interface configuration failed"); + return; + } + + /* bring the interface up for IPv6 */ + if (!sif6up(f->pcb)) { + PPPDEBUG(LOG_DEBUG, ("sif6up failed (IPV6)")); + ipv6cp_close(f->pcb, "Interface configuration failed"); + return; + } + sifnpmode(f->pcb, PPP_IPV6, NPMODE_PASS); + + ppp_notice("local LL address %s", llv6_ntoa(go->ourid)); + ppp_notice("remote LL address %s", llv6_ntoa(ho->hisid)); + } + + np_up(f->pcb, PPP_IPV6); + pcb->ipv6cp_is_up = 1; + +#if 0 /* UNUSED */ + /* + * Execute the ipv6-up script, like this: + * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL + */ + if (ipv6cp_script_state == s_down && ipv6cp_script_pid == 0) { + ipv6cp_script_state = s_up; + ipv6cp_script(_PATH_IPV6UP); + } +#endif /* UNUSED */ +} + + +/* + * ipv6cp_down - IPV6CP has gone DOWN. + * + * Take the IPv6 network interface down, clear its addresses + * and delete routes through it. + */ +static void ipv6cp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + ipv6cp_options *go = &pcb->ipv6cp_gotoptions; + ipv6cp_options *ho = &pcb->ipv6cp_hisoptions; + + IPV6CPDEBUG(("ipv6cp: down")); +#if PPP_STATS_SUPPORT + update_link_stats(f->unit); +#endif /* PPP_STATS_SUPPORT */ + if (pcb->ipv6cp_is_up) { + pcb->ipv6cp_is_up = 0; + np_down(f->pcb, PPP_IPV6); + } +#ifdef IPV6CP_COMP + sif6comp(f->unit, 0); +#endif + +#if DEMAND_SUPPORT + /* + * If we are doing dial-on-demand, set the interface + * to queue up outgoing packets (for now). + */ + if (demand) { + sifnpmode(f->pcb, PPP_IPV6, NPMODE_QUEUE); + } else +#endif /* DEMAND_SUPPORT */ + { + sifnpmode(f->pcb, PPP_IPV6, NPMODE_DROP); + ipv6cp_clear_addrs(f->pcb, + go->ourid, + ho->hisid); + sif6down(f->pcb); + } + +#if 0 /* UNUSED */ + /* Execute the ipv6-down script */ + if (ipv6cp_script_state == s_up && ipv6cp_script_pid == 0) { + ipv6cp_script_state = s_down; + ipv6cp_script(_PATH_IPV6DOWN); + } +#endif /* UNUSED */ +} + + +/* + * ipv6cp_clear_addrs() - clear the interface addresses, routes, + * proxy neighbour discovery entries, etc. + */ +static void ipv6cp_clear_addrs(ppp_pcb *pcb, eui64_t ourid, eui64_t hisid) { + cif6addr(pcb, ourid, hisid); +} + + +/* + * ipv6cp_finished - possibly shut down the lower layers. + */ +static void ipv6cp_finished(fsm *f) { + np_finished(f->pcb, PPP_IPV6); +} + + +#if 0 /* UNUSED */ +/* + * ipv6cp_script_done - called when the ipv6-up or ipv6-down script + * has finished. + */ +static void +ipv6cp_script_done(arg) + void *arg; +{ + ipv6cp_script_pid = 0; + switch (ipv6cp_script_state) { + case s_up: + if (ipv6cp_fsm[0].state != PPP_FSM_OPENED) { + ipv6cp_script_state = s_down; + ipv6cp_script(_PATH_IPV6DOWN); + } + break; + case s_down: + if (ipv6cp_fsm[0].state == PPP_FSM_OPENED) { + ipv6cp_script_state = s_up; + ipv6cp_script(_PATH_IPV6UP); + } + break; + } +} + + +/* + * ipv6cp_script - Execute a script with arguments + * interface-name tty-name speed local-LL remote-LL. + */ +static void +ipv6cp_script(script) + char *script; +{ + char strspeed[32], strlocal[32], strremote[32]; + char *argv[8]; + + sprintf(strspeed, "%d", baud_rate); + strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid)); + strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid)); + + argv[0] = script; + argv[1] = ifname; + argv[2] = devnam; + argv[3] = strspeed; + argv[4] = strlocal; + argv[5] = strremote; + argv[6] = ipparam; + argv[7] = NULL; + + ipv6cp_script_pid = run_program(script, argv, 0, ipv6cp_script_done, + NULL, 0); +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ipv6cp_printpkt - print the contents of an IPV6CP packet. + */ +static const char *ipv6cp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej" +}; + +static int ipv6cp_printpkt(u_char *p, int plen, + void (*printer)(void *, const char *, ...), void *arg) { + int code, id, len, olen; + u_char *pstart, *optend; + u_short cishort; + eui64_t ifaceid; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)sizeof(ipv6cp_codenames) / (int)sizeof(char *)) + printer(arg, " %s", ipv6cp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_COMPRESSTYPE: + if (olen >= CILEN_COMPRESS) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "compress "); + printer(arg, "0x%x", cishort); + } + break; + case CI_IFACEID: + if (olen == CILEN_IFACEID) { + p += 2; + eui64_get(ifaceid, p); + printer(arg, "addr %s", llv6_ntoa(ifaceid)); + } + break; + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string((char *)p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#if DEMAND_SUPPORT +/* + * ipv6_active_pkt - see if this IP packet is worth bringing the link up for. + * We don't bring the link up for IP fragments or for TCP FIN packets + * with no data. + */ +#define IP6_HDRLEN 40 /* bytes */ +#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */ +#define TCP_HDRLEN 20 +#define TH_FIN 0x01 + +/* + * We use these macros because the IP header may be at an odd address, + * and some compilers might use word loads to get th_off or ip_hl. + */ + +#define get_ip6nh(x) (((unsigned char *)(x))[6]) +#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4) +#define get_tcpflags(x) (((unsigned char *)(x))[13]) + +static int ipv6_active_pkt(u_char *pkt, int len) { + u_char *tcp; + + len -= PPP_HDRLEN; + pkt += PPP_HDRLEN; + if (len < IP6_HDRLEN) + return 0; + if (get_ip6nh(pkt) == IP6_NHDR_FRAG) + return 0; + if (get_ip6nh(pkt) != IPPROTO_TCP) + return 1; + if (len < IP6_HDRLEN + TCP_HDRLEN) + return 0; + tcp = pkt + IP6_HDRLEN; + if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4) + return 0; + return 1; +} +#endif /* DEMAND_SUPPORT */ + +#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/lcp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/lcp.c new file mode 100644 index 0000000..5aa99da --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/lcp.c @@ -0,0 +1,2732 @@ +/* + * lcp.c - PPP Link Control Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * TODO: + */ + +#if 0 /* UNUSED */ +#include +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#include "netif/ppp/magic.h" + +/* + * When the link comes up we want to be able to wait for a short while, + * or until seeing some input from the peer, before starting to send + * configure-requests. We do this by delaying the fsm_lowerup call. + */ +/* steal a bit in fsm flags word */ +#define DELAYED_UP 0x80 + +static void lcp_delayed_up(void *arg); + +/* + * LCP-related command-line options. + */ +#if 0 /* UNUSED */ +int lcp_echo_interval = 0; /* Interval between LCP echo-requests */ +int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +/* options */ +static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */ +static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */ +#endif /* UNUSED */ + +#if 0 /* UNUSED */ +#if PPP_LCP_ADAPTIVE +bool lcp_echo_adaptive = 0; /* request echo only if the link was idle */ +#endif +bool lax_recv = 0; /* accept control chars in asyncmap */ +bool noendpoint = 0; /* don't send/accept endpoint discriminator */ +#endif /* UNUSED */ + +#if PPP_OPTIONS +static int noopt (char **); +#endif /* PPP_OPTIONS */ + +#ifdef HAVE_MULTILINK +static int setendpoint (char **); +static void printendpoint (option_t *, void (*)(void *, char *, ...), + void *); +#endif /* HAVE_MULTILINK */ + +#if PPP_OPTIONS +static option_t lcp_option_list[] = { + /* LCP options */ + { "-all", o_special_noarg, (void *)noopt, + "Don't request/allow any LCP options" }, + + { "noaccomp", o_bool, &lcp_wantoptions[0].neg_accompression, + "Disable address/control compression", + OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, + { "-ac", o_bool, &lcp_wantoptions[0].neg_accompression, + "Disable address/control compression", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_accompression }, + + { "asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, + "Set asyncmap (for received packets)", + OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, + { "-as", o_uint32, &lcp_wantoptions[0].asyncmap, + "Set asyncmap (for received packets)", + OPT_ALIAS | OPT_OR, &lcp_wantoptions[0].neg_asyncmap }, + { "default-asyncmap", o_uint32, &lcp_wantoptions[0].asyncmap, + "Disable asyncmap negotiation", + OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, + &lcp_allowoptions[0].neg_asyncmap }, + { "-am", o_uint32, &lcp_wantoptions[0].asyncmap, + "Disable asyncmap negotiation", + OPT_ALIAS | OPT_OR | OPT_NOARG | OPT_VAL(~0U) | OPT_A2CLR, + &lcp_allowoptions[0].neg_asyncmap }, + + { "nomagic", o_bool, &lcp_wantoptions[0].neg_magicnumber, + "Disable magic number negotiation (looped-back line detection)", + OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, + { "-mn", o_bool, &lcp_wantoptions[0].neg_magicnumber, + "Disable magic number negotiation (looped-back line detection)", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_magicnumber }, + + { "mru", o_int, &lcp_wantoptions[0].mru, + "Set MRU (maximum received packet size) for negotiation", + OPT_PRIO, &lcp_wantoptions[0].neg_mru }, + { "default-mru", o_bool, &lcp_wantoptions[0].neg_mru, + "Disable MRU negotiation (use default 1500)", + OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, + { "-mru", o_bool, &lcp_wantoptions[0].neg_mru, + "Disable MRU negotiation (use default 1500)", + OPT_ALIAS | OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_mru }, + + { "mtu", o_int, &lcp_allowoptions[0].mru, + "Set our MTU", OPT_LIMITS, NULL, MAXMRU, MINMRU }, + + { "nopcomp", o_bool, &lcp_wantoptions[0].neg_pcompression, + "Disable protocol field compression", + OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, + { "-pc", o_bool, &lcp_wantoptions[0].neg_pcompression, + "Disable protocol field compression", + OPT_ALIAS | OPT_A2CLR, &lcp_allowoptions[0].neg_pcompression }, + + { "passive", o_bool, &lcp_wantoptions[0].passive, + "Set passive mode", 1 }, + { "-p", o_bool, &lcp_wantoptions[0].passive, + "Set passive mode", OPT_ALIAS | 1 }, + + { "silent", o_bool, &lcp_wantoptions[0].silent, + "Set silent mode", 1 }, + + { "lcp-echo-failure", o_int, &lcp_echo_fails, + "Set number of consecutive echo failures to indicate link failure", + OPT_PRIO }, + { "lcp-echo-interval", o_int, &lcp_echo_interval, + "Set time in seconds between LCP echo requests", OPT_PRIO }, +#if PPP_LCP_ADAPTIVE + { "lcp-echo-adaptive", o_bool, &lcp_echo_adaptive, + "Suppress LCP echo requests if traffic was received", 1 }, +#endif + { "lcp-restart", o_int, &lcp_fsm[0].timeouttime, + "Set time in seconds between LCP retransmissions", OPT_PRIO }, + { "lcp-max-terminate", o_int, &lcp_fsm[0].maxtermtransmits, + "Set maximum number of LCP terminate-request transmissions", OPT_PRIO }, + { "lcp-max-configure", o_int, &lcp_fsm[0].maxconfreqtransmits, + "Set maximum number of LCP configure-request transmissions", OPT_PRIO }, + { "lcp-max-failure", o_int, &lcp_fsm[0].maxnakloops, + "Set limit on number of LCP configure-naks", OPT_PRIO }, + + { "receive-all", o_bool, &lax_recv, + "Accept all received control characters", 1 }, + +#ifdef HAVE_MULTILINK + { "mrru", o_int, &lcp_wantoptions[0].mrru, + "Maximum received packet size for multilink bundle", + OPT_PRIO, &lcp_wantoptions[0].neg_mrru }, + + { "mpshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, + "Use short sequence numbers in multilink headers", + OPT_PRIO | 1, &lcp_allowoptions[0].neg_ssnhf }, + { "nompshortseq", o_bool, &lcp_wantoptions[0].neg_ssnhf, + "Don't use short sequence numbers in multilink headers", + OPT_PRIOSUB | OPT_A2CLR, &lcp_allowoptions[0].neg_ssnhf }, + + { "endpoint", o_special, (void *) setendpoint, + "Endpoint discriminator for multilink", + OPT_PRIO | OPT_A2PRINTER, (void *) printendpoint }, +#endif /* HAVE_MULTILINK */ + + { "noendpoint", o_bool, &noendpoint, + "Don't send or accept multilink endpoint discriminator", 1 }, + + {NULL} +}; +#endif /* PPP_OPTIONS */ + +/* + * Callbacks for fsm code. (CI = Configuration Information) + */ +static void lcp_resetci(fsm *f); /* Reset our CI */ +static int lcp_cilen(fsm *f); /* Return length of our CI */ +static void lcp_addci(fsm *f, u_char *ucp, int *lenp); /* Add our CI to pkt */ +static int lcp_ackci(fsm *f, u_char *p, int len); /* Peer ack'd our CI */ +static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject); /* Peer nak'd our CI */ +static int lcp_rejci(fsm *f, u_char *p, int len); /* Peer rej'd our CI */ +static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree); /* Rcv peer CI */ +static void lcp_up(fsm *f); /* We're UP */ +static void lcp_down(fsm *f); /* We're DOWN */ +static void lcp_starting (fsm *); /* We need lower layer up */ +static void lcp_finished (fsm *); /* We need lower layer down */ +static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len); +static void lcp_rprotrej(fsm *f, u_char *inp, int len); + +/* + * routines to send LCP echos to peer + */ + +static void lcp_echo_lowerup(ppp_pcb *pcb); +static void lcp_echo_lowerdown(ppp_pcb *pcb); +static void LcpEchoTimeout(void *arg); +static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len); +static void LcpSendEchoRequest(fsm *f); +static void LcpLinkFailure(fsm *f); +static void LcpEchoCheck(fsm *f); + +static const fsm_callbacks lcp_callbacks = { /* LCP callback routines */ + lcp_resetci, /* Reset our Configuration Information */ + lcp_cilen, /* Length of our Configuration Information */ + lcp_addci, /* Add our Configuration Information */ + lcp_ackci, /* ACK our Configuration Information */ + lcp_nakci, /* NAK our Configuration Information */ + lcp_rejci, /* Reject our Configuration Information */ + lcp_reqci, /* Request peer's Configuration Information */ + lcp_up, /* Called when fsm reaches OPENED state */ + lcp_down, /* Called when fsm leaves OPENED state */ + lcp_starting, /* Called when we want the lower layer up */ + lcp_finished, /* Called when we want the lower layer down */ + NULL, /* Called when Protocol-Reject received */ + NULL, /* Retransmission is necessary */ + lcp_extcode, /* Called to handle LCP-specific codes */ + "LCP" /* String name of protocol */ +}; + +/* + * Protocol entry points. + * Some of these are called directly. + */ + +static void lcp_init(ppp_pcb *pcb); +static void lcp_input(ppp_pcb *pcb, u_char *p, int len); +static void lcp_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int lcp_printpkt(u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent lcp_protent = { + PPP_LCP, + lcp_init, + lcp_input, + lcp_protrej, + lcp_lowerup, + lcp_lowerdown, + lcp_open, + lcp_close, +#if PRINTPKT_SUPPORT + lcp_printpkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, + 1, +#if PRINTPKT_SUPPORT + "LCP", + NULL, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + lcp_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +/* + * Length of each type of configuration option (in octets) + */ +#define CILEN_VOID 2 +#define CILEN_CHAR 3 +#define CILEN_SHORT 4 /* CILEN_VOID + 2 */ +#if CHAP_SUPPORT +#define CILEN_CHAP 5 /* CILEN_VOID + 2 + 1 */ +#endif /* CHAP_SUPPORT */ +#define CILEN_LONG 6 /* CILEN_VOID + 4 */ +#if LQR_SUPPORT +#define CILEN_LQR 8 /* CILEN_VOID + 2 + 4 */ +#endif /* LQR_SUPPORT */ +#define CILEN_CBCP 3 + +#define CODENAME(x) ((x) == CONFACK ? "ACK" : \ + (x) == CONFNAK ? "NAK" : "REJ") + +#if PPP_OPTIONS +/* + * noopt - Disable all options (why?). + */ +static int +noopt(argv) + char **argv; +{ + BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options)); + BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options)); + + return (1); +} +#endif /* PPP_OPTIONS */ + +#ifdef HAVE_MULTILINK +static int +setendpoint(argv) + char **argv; +{ + if (str_to_epdisc(&lcp_wantoptions[0].endpoint, *argv)) { + lcp_wantoptions[0].neg_endpoint = 1; + return 1; + } + option_error("Can't parse '%s' as an endpoint discriminator", *argv); + return 0; +} + +static void +printendpoint(opt, printer, arg) + option_t *opt; + void (*printer) (void *, char *, ...); + void *arg; +{ + printer(arg, "%s", epdisc_to_str(&lcp_wantoptions[0].endpoint)); +} +#endif /* HAVE_MULTILINK */ + +/* + * lcp_init - Initialize LCP. + */ +static void lcp_init(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + f->pcb = pcb; + f->protocol = PPP_LCP; + f->callbacks = &lcp_callbacks; + + fsm_init(f); + + BZERO(wo, sizeof(*wo)); + wo->neg_mru = 1; + wo->mru = DEFMRU; + wo->neg_asyncmap = 1; + wo->neg_magicnumber = 1; + wo->neg_pcompression = 1; + wo->neg_accompression = 1; + + BZERO(ao, sizeof(*ao)); + ao->neg_mru = 1; + ao->mru = MAXMRU; + ao->neg_asyncmap = 1; +#if CHAP_SUPPORT + ao->neg_chap = 1; + ao->chap_mdtype = CHAP_MDTYPE_SUPPORTED; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + ao->neg_upap = 1; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + ao->neg_eap = 1; +#endif /* EAP_SUPPORT */ + ao->neg_magicnumber = 1; + ao->neg_pcompression = 1; + ao->neg_accompression = 1; + ao->neg_endpoint = 1; + +#if PPPOS_SUPPORT + /* + * Set transmit escape for the flag and escape characters plus anything + * set for the allowable options. + */ + memset(pcb->xmit_accm, 0, sizeof(ext_accm)); + pcb->xmit_accm[15] = 0x60; + pcb->xmit_accm[0] = (u_char)((ao->asyncmap & 0xFF)); + pcb->xmit_accm[1] = (u_char)((ao->asyncmap >> 8) & 0xFF); + pcb->xmit_accm[2] = (u_char)((ao->asyncmap >> 16) & 0xFF); + pcb->xmit_accm[3] = (u_char)((ao->asyncmap >> 24) & 0xFF); + LCPDEBUG(("lcp_init: xmit_accm=%X %X %X %X\n", + pcb->xmit_accm[0], + pcb->xmit_accm[1], + pcb->xmit_accm[2], + pcb->xmit_accm[3])); +#endif /* PPPOS_SUPPORT */ +} + + +/* + * lcp_open - LCP is allowed to come up. + */ +void lcp_open(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + lcp_options *wo = &pcb->lcp_wantoptions; + + f->flags &= ~(OPT_PASSIVE | OPT_SILENT); + if (wo->passive) + f->flags |= OPT_PASSIVE; + if (wo->silent) + f->flags |= OPT_SILENT; + fsm_open(f); +} + + +/* + * lcp_close - Take LCP down. + */ +void lcp_close(ppp_pcb *pcb, const char *reason) { + fsm *f = &pcb->lcp_fsm; + int oldstate; + + if (pcb->phase != PPP_PHASE_DEAD && pcb->phase != PPP_PHASE_MASTER) + new_phase(pcb, PPP_PHASE_TERMINATE); + + if (f->flags & DELAYED_UP) { + UNTIMEOUT(lcp_delayed_up, f); + f->state = PPP_FSM_STOPPED; + } + oldstate = f->state; + + fsm_close(f, reason); + if (oldstate == PPP_FSM_STOPPED && (f->flags & (OPT_PASSIVE|OPT_SILENT|DELAYED_UP))) { + /* + * This action is not strictly according to the FSM in RFC1548, + * but it does mean that the program terminates if you do a + * lcp_close() when a connection hasn't been established + * because we are in passive/silent mode or because we have + * delayed the fsm_lowerup() call and it hasn't happened yet. + */ + f->flags &= ~DELAYED_UP; + lcp_finished(f); + } +} + + +/* + * lcp_lowerup - The lower layer is up. + */ +void lcp_lowerup(ppp_pcb *pcb) { + lcp_options *wo = &pcb->lcp_wantoptions; +#if PPPOS_SUPPORT + lcp_options *ao = &pcb->lcp_allowoptions; +#endif /* PPPOS_SUPPORT */ + fsm *f = &pcb->lcp_fsm; + /* + * Don't use A/C or protocol compression on transmission, + * but accept A/C and protocol compressed packets + * if we are going to ask for A/C and protocol compression. + */ +#if PPPOS_SUPPORT + ppp_set_xaccm(pcb, &pcb->xmit_accm); +#endif /* PPPOS_SUPPORT */ + if (ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0) < 0 + || ppp_recv_config(pcb, PPP_MRU, (pcb->settings.lax_recv? 0: 0xffffffff), + wo->neg_pcompression, wo->neg_accompression) < 0) + return; + pcb->peer_mru = PPP_MRU; + +#if PPPOS_SUPPORT + ao->asyncmap = (u_long)pcb->xmit_accm[0] + | ((u_long)pcb->xmit_accm[1] << 8) + | ((u_long)pcb->xmit_accm[2] << 16) + | ((u_long)pcb->xmit_accm[3] << 24); + LCPDEBUG(("lcp_lowerup: asyncmap=%X %X %X %X\n", + pcb->xmit_accm[3], + pcb->xmit_accm[2], + pcb->xmit_accm[1], + pcb->xmit_accm[0])); +#endif /* PPPOS_SUPPORT */ + + if (pcb->settings.listen_time != 0) { + f->flags |= DELAYED_UP; + TIMEOUTMS(lcp_delayed_up, f, pcb->settings.listen_time); + } else + fsm_lowerup(f); +} + + +/* + * lcp_lowerdown - The lower layer is down. + */ +void lcp_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + UNTIMEOUT(lcp_delayed_up, f); + } else + fsm_lowerdown(f); +} + + +/* + * lcp_delayed_up - Bring the lower layer up now. + */ +static void lcp_delayed_up(void *arg) { + fsm *f = (fsm*)arg; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + fsm_lowerup(f); + } +} + + +/* + * lcp_input - Input LCP packet. + */ +static void lcp_input(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->lcp_fsm; + + if (f->flags & DELAYED_UP) { + f->flags &= ~DELAYED_UP; + UNTIMEOUT(lcp_delayed_up, f); + fsm_lowerup(f); + } + fsm_input(f, p, len); +} + +/* + * lcp_extcode - Handle a LCP-specific code. + */ +static int lcp_extcode(fsm *f, int code, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char *magp; + + switch( code ){ + case PROTREJ: + lcp_rprotrej(f, inp, len); + break; + + case ECHOREQ: + if (f->state != PPP_FSM_OPENED) + break; + magp = inp; + PUTLONG(go->magicnumber, magp); + fsm_sdata(f, ECHOREP, id, inp, len); + break; + + case ECHOREP: + lcp_received_echo_reply(f, id, inp, len); + break; + + case DISCREQ: + case IDENTIF: + case TIMEREM: + break; + + default: + return 0; + } + return 1; +} + + +/* + * lcp_rprotrej - Receive an Protocol-Reject. + * + * Figure out which protocol is rejected and inform it. + */ +static void lcp_rprotrej(fsm *f, u_char *inp, int len) { + int i; + const struct protent *protp; + u_short prot; +#if PPP_PROTOCOLNAME + const char *pname; +#endif /* PPP_PROTOCOLNAME */ + + if (len < 2) { + LCPDEBUG(("lcp_rprotrej: Rcvd short Protocol-Reject packet!")); + return; + } + + GETSHORT(prot, inp); + + /* + * Protocol-Reject packets received in any state other than the LCP + * OPENED state SHOULD be silently discarded. + */ + if( f->state != PPP_FSM_OPENED ){ + LCPDEBUG(("Protocol-Reject discarded: LCP in state %d", f->state)); + return; + } + +#if PPP_PROTOCOLNAME + pname = protocol_name(prot); +#endif /* PPP_PROTOCOLNAME */ + + /* + * Upcall the proper Protocol-Reject routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (protp->protocol == prot && protp->enabled_flag) { +#if PPP_PROTOCOLNAME + if (pname != NULL) + ppp_dbglog("Protocol-Reject for '%s' (0x%x) received", pname, + prot); + else +#endif /* PPP_PROTOCOLNAME */ + ppp_dbglog("Protocol-Reject for 0x%x received", prot); + (*protp->protrej)(f->pcb); + return; + } + +#if PPP_PROTOCOLNAME + if (pname != NULL) + ppp_warn("Protocol-Reject for unsupported protocol '%s' (0x%x)", pname, + prot); + else +#endif /* #if PPP_PROTOCOLNAME */ + ppp_warn("Protocol-Reject for unsupported protocol 0x%x", prot); +} + + +/* + * lcp_protrej - A Protocol-Reject was received. + */ +/*ARGSUSED*/ +static void lcp_protrej(ppp_pcb *pcb) { + /* + * Can't reject LCP! + */ + ppp_error("Received Protocol-Reject for LCP!"); + fsm_protreject(&pcb->lcp_fsm); +} + + +/* + * lcp_sprotrej - Send a Protocol-Reject for some protocol. + */ +void lcp_sprotrej(ppp_pcb *pcb, u_char *p, int len) { + fsm *f = &pcb->lcp_fsm; + /* + * Send back the protocol and the information field of the + * rejected packet. We only get here if LCP is in the OPENED state. + */ +#if 0 + p += 2; + len -= 2; +#endif + + fsm_sdata(f, PROTREJ, ++f->id, + p, len); +} + + +/* + * lcp_resetci - Reset our CI. + */ +static void lcp_resetci(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + wo->magicnumber = magic(); + wo->numloops = 0; + *go = *wo; +#ifdef HAVE_MULTILINK + if (!multilink) { + go->neg_mrru = 0; +#endif /* HAVE_MULTILINK */ + go->neg_ssnhf = 0; + go->neg_endpoint = 0; +#ifdef HAVE_MULTILINK + } +#endif /* HAVE_MULTILINK */ + if (pcb->settings.noendpoint) + ao->neg_endpoint = 0; + pcb->peer_mru = PPP_MRU; + auth_reset(pcb); +} + + +/* + * lcp_cilen - Return length of our CI. + */ +static int lcp_cilen(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + +#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0) +#if CHAP_SUPPORT +#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0) +#endif /* CHAP_SUPPORT */ +#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0) +#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0) +#if LQR_SUPPORT +#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0) +#endif /* LQR_SUPPORT */ +#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0) + /* + * NB: we only ask for one of CHAP, UPAP, or EAP, even if we will + * accept more than one. We prefer EAP first, then CHAP, then + * PAP. + */ + return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) + + LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + +#if EAP_SUPPORT + LENCISHORT(go->neg_eap) + +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + LENCICHAP(!go->neg_eap && go->neg_chap) + +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + LENCICHAP(go->neg_chap) + +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + LENCISHORT(!go->neg_eap && !go->neg_chap && go->neg_upap) + +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + LENCISHORT(!go->neg_eap && go->neg_upap) + +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + LENCISHORT(!go->neg_chap && go->neg_upap) + +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + LENCISHORT(go->neg_upap) + +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + LENCILQR(go->neg_lqr) + +#endif /* LQR_SUPPORT */ + LENCICBCP(go->neg_cbcp) + + LENCILONG(go->neg_magicnumber) + + LENCIVOID(go->neg_pcompression) + + LENCIVOID(go->neg_accompression) + +#ifdef HAVE_MULTILINK + LENCISHORT(go->neg_mrru) + +#endif /* HAVE_MULTILINK */ + LENCIVOID(go->neg_ssnhf) + + (go->neg_endpoint? CILEN_CHAR + go->endpoint.length: 0)); +} + + +/* + * lcp_addci - Add our desired CIs to a packet. + */ +static void lcp_addci(fsm *f, u_char *ucp, int *lenp) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char *start_ucp = ucp; + +#define ADDCIVOID(opt, neg) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_VOID, ucp); \ + } +#define ADDCISHORT(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_SHORT, ucp); \ + PUTSHORT(val, ucp); \ + } +#if CHAP_SUPPORT +#define ADDCICHAP(opt, neg, val) \ + if (neg) { \ + PUTCHAR((opt), ucp); \ + PUTCHAR(CILEN_CHAP, ucp); \ + PUTSHORT(PPP_CHAP, ucp); \ + PUTCHAR((CHAP_DIGEST(val)), ucp); \ + } +#endif /* CHAP_SUPPORT */ +#define ADDCILONG(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LONG, ucp); \ + PUTLONG(val, ucp); \ + } +#if LQR_SUPPORT +#define ADDCILQR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_LQR, ucp); \ + PUTSHORT(PPP_LQR, ucp); \ + PUTLONG(val, ucp); \ + } +#endif /* LQR_SUPPORT */ +#define ADDCICHAR(opt, neg, val) \ + if (neg) { \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR, ucp); \ + PUTCHAR(val, ucp); \ + } +#define ADDCIENDP(opt, neg, class, val, len) \ + if (neg) { \ + int i; \ + PUTCHAR(opt, ucp); \ + PUTCHAR(CILEN_CHAR + len, ucp); \ + PUTCHAR(class, ucp); \ + for (i = 0; i < len; ++i) \ + PUTCHAR(val[i], ucp); \ + } + + ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); +#if EAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + ADDCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + ADDCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + ADDCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression); +#ifdef HAVE_MULTILINK + ADDCISHORT(CI_MRRU, go->neg_mrru, go->mrru); +#endif + ADDCIVOID(CI_SSNHF, go->neg_ssnhf); + ADDCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + if (ucp - start_ucp != *lenp) { + /* this should never happen, because peer_mtu should be 1500 */ + ppp_error("Bug in lcp_addci: wrong length"); + } +} + + +/* + * lcp_ackci - Ack our CIs. + * This should not modify any state if the Ack is bad. + * + * Returns: + * 0 - Ack was bad. + * 1 - Ack was good. + */ +static int lcp_ackci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char cilen, citype, cichar; + u_short cishort; + u32_t cilong; + + /* + * CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define ACKCIVOID(opt, neg) \ + if (neg) { \ + if ((len -= CILEN_VOID) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_VOID || \ + citype != opt) \ + goto bad; \ + } +#define ACKCISHORT(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_SHORT) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_SHORT || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != val) \ + goto bad; \ + } +#define ACKCICHAR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != val) \ + goto bad; \ + } +#if CHAP_SUPPORT +#define ACKCICHAP(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_CHAP) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAP || \ + citype != (opt)) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_CHAP) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != (CHAP_DIGEST(val))) \ + goto bad; \ + } +#endif /* CHAP_SUPPORT */ +#define ACKCILONG(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LONG) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LONG || \ + citype != opt) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#if LQR_SUPPORT +#define ACKCILQR(opt, neg, val) \ + if (neg) { \ + if ((len -= CILEN_LQR) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_LQR || \ + citype != opt) \ + goto bad; \ + GETSHORT(cishort, p); \ + if (cishort != PPP_LQR) \ + goto bad; \ + GETLONG(cilong, p); \ + if (cilong != val) \ + goto bad; \ + } +#endif /* LQR_SUPPORT */ +#define ACKCIENDP(opt, neg, class, val, vlen) \ + if (neg) { \ + int i; \ + if ((len -= CILEN_CHAR + vlen) < 0) \ + goto bad; \ + GETCHAR(citype, p); \ + GETCHAR(cilen, p); \ + if (cilen != CILEN_CHAR + vlen || \ + citype != opt) \ + goto bad; \ + GETCHAR(cichar, p); \ + if (cichar != class) \ + goto bad; \ + for (i = 0; i < vlen; ++i) { \ + GETCHAR(cichar, p); \ + if (cichar != val[i]) \ + goto bad; \ + } \ + } + + ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru); + ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF, + go->asyncmap); +#if EAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, go->neg_eap, PPP_EAP); +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT + ACKCICHAP(CI_AUTHTYPE, !go->neg_eap && go->neg_chap, go->chap_mdtype); +#endif /* EAP_SUPPORT */ +#if !EAP_SUPPORT + ACKCICHAP(CI_AUTHTYPE, go->neg_chap, go->chap_mdtype); +#endif /* !EAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT /* cannot be improved, embedding a directive within macro arguments is not portable */ +#if EAP_SUPPORT && CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && CHAP_SUPPORT */ +#if EAP_SUPPORT && !CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_eap && go->neg_upap, PPP_PAP); +#endif /* EAP_SUPPORT && !CHAP_SUPPORT */ +#if !EAP_SUPPORT && CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && CHAP_SUPPORT */ +#if !EAP_SUPPORT && !CHAP_SUPPORT + ACKCISHORT(CI_AUTHTYPE, go->neg_upap, PPP_PAP); +#endif /* !EAP_SUPPORT && !CHAP_SUPPORT */ +#endif /* PAP_SUPPORT */ +#if LQR_SUPPORT + ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT); + ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber); + ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression); + ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression); +#ifdef HAVE_MULTILINK + ACKCISHORT(CI_MRRU, go->neg_mrru, go->mrru); +#endif /* HAVE_MULTILINK */ + ACKCIVOID(CI_SSNHF, go->neg_ssnhf); + ACKCIENDP(CI_EPDISC, go->neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + return (1); +bad: + LCPDEBUG(("lcp_acki: received bad Ack!")); + return (0); +} + + +/* + * lcp_nakci - Peer has sent a NAK for some of our CIs. + * This should not modify any state if the Nak is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Nak was bad. + * 1 - Nak was good. + */ +static int lcp_nakci(fsm *f, u_char *p, int len, int treat_as_reject) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *wo = &pcb->lcp_wantoptions; + u_char citype, cichar, *next; + u_short cishort; + u32_t cilong; + lcp_options no; /* options we've seen Naks for */ + lcp_options try_; /* options to request next time */ + int looped_back = 0; + int cilen; + + BZERO(&no, sizeof(no)); + try_ = *go; + + /* + * Any Nak'd CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define NAKCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + no.neg = 1; \ + try_.neg = 0; \ + } +#if CHAP_SUPPORT +#define NAKCICHAP(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#endif /* CHAP_SUPPORT */ +#define NAKCICHAR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[1] == CILEN_CHAR && \ + p[0] == opt) { \ + len -= CILEN_CHAR; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + no.neg = 1; \ + code \ + } +#define NAKCISHORT(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + no.neg = 1; \ + code \ + } +#define NAKCILONG(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#if LQR_SUPPORT +#define NAKCILQR(opt, neg, code) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + no.neg = 1; \ + code \ + } +#endif /* LQR_SUPPORT */ +#define NAKCIENDP(opt, neg) \ + if (go->neg && \ + len >= CILEN_CHAR && \ + p[0] == opt && \ + p[1] >= CILEN_CHAR && \ + p[1] <= len) { \ + len -= p[1]; \ + INCPTR(p[1], p); \ + no.neg = 1; \ + try_.neg = 0; \ + } + + /* + * NOTE! There must be no assignments to individual fields of *go in + * the code below. Any such assignment is a BUG! + */ + /* + * We don't care if they want to send us smaller packets than + * we want. Therefore, accept any MRU less than what we asked for, + * but then ignore the new value when setting the MRU in the kernel. + * If they send us a bigger MRU than what we asked, accept it, up to + * the limit of the default MRU we'd get if we didn't negotiate. + */ + if (go->neg_mru && go->mru != DEFMRU) { + NAKCISHORT(CI_MRU, neg_mru, + if (cishort <= wo->mru || cishort <= DEFMRU) + try_.mru = cishort; + ); + } + + /* + * Add any characters they want to our (receive-side) asyncmap. + */ + if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) { + NAKCILONG(CI_ASYNCMAP, neg_asyncmap, + try_.asyncmap = go->asyncmap | cilong; + ); + } + + /* + * If they've nak'd our authentication-protocol, check whether + * they are proposing a different protocol, or a different + * hash algorithm for CHAP. + */ + if ((0 +#if CHAP_SUPPORT + || go->neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap +#endif /* EAP_SUPPORT */ + ) + && len >= CILEN_SHORT + && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) { + cilen = p[1]; + len -= cilen; +#if CHAP_SUPPORT + no.neg_chap = go->neg_chap; +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + no.neg_upap = go->neg_upap; +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + no.neg_eap = go->neg_eap; +#endif /* EAP_SUPPORT */ + INCPTR(2, p); + GETSHORT(cishort, p); + +#if PAP_SUPPORT + if (cishort == PPP_PAP && cilen == CILEN_SHORT) { +#if EAP_SUPPORT + /* If we were asking for EAP, then we need to stop that. */ + if (go->neg_eap) + try_.neg_eap = 0; + else +#endif /* EAP_SUPPORT */ + +#if CHAP_SUPPORT + /* If we were asking for CHAP, then we need to stop that. */ + if (go->neg_chap) + try_.neg_chap = 0; + else +#endif /* CHAP_SUPPORT */ + + /* + * If we weren't asking for CHAP or EAP, then we were asking for + * PAP, in which case this Nak is bad. + */ + goto bad; + } else +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + if (cishort == PPP_CHAP && cilen == CILEN_CHAP) { + GETCHAR(cichar, p); +#if EAP_SUPPORT + /* Stop asking for EAP, if we were. */ + if (go->neg_eap) { + try_.neg_eap = 0; + /* Try to set up to use their suggestion, if possible */ + if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) + try_.chap_mdtype = CHAP_MDTYPE_D(cichar); + } else +#endif /* EAP_SUPPORT */ + if (go->neg_chap) { + /* + * We were asking for our preferred algorithm, they must + * want something different. + */ + if (cichar != CHAP_DIGEST(go->chap_mdtype)) { + if (CHAP_CANDIGEST(go->chap_mdtype, cichar)) { + /* Use their suggestion if we support it ... */ + try_.chap_mdtype = CHAP_MDTYPE_D(cichar); + } else { + /* ... otherwise, try our next-preferred algorithm. */ + try_.chap_mdtype &= ~(CHAP_MDTYPE(try_.chap_mdtype)); + if (try_.chap_mdtype == MDTYPE_NONE) /* out of algos */ + try_.neg_chap = 0; + } + } else { + /* + * Whoops, they Nak'd our algorithm of choice + * but then suggested it back to us. + */ + goto bad; + } + } else { + /* + * Stop asking for PAP if we were asking for it. + */ +#if PAP_SUPPORT + try_.neg_upap = 0; +#endif /* PAP_SUPPORT */ + } + + } else +#endif /* CHAP_SUPPORT */ + { + +#if EAP_SUPPORT + /* + * If we were asking for EAP, and they're Conf-Naking EAP, + * well, that's just strange. Nobody should do that. + */ + if (cishort == PPP_EAP && cilen == CILEN_SHORT && go->neg_eap) + ppp_dbglog("Unexpected Conf-Nak for EAP"); + + /* + * We don't recognize what they're suggesting. + * Stop asking for what we were asking for. + */ + if (go->neg_eap) + try_.neg_eap = 0; + else +#endif /* EAP_SUPPORT */ + +#if CHAP_SUPPORT + if (go->neg_chap) + try_.neg_chap = 0; + else +#endif /* CHAP_SUPPORT */ + +#if PAP_SUPPORT + if(1) + try_.neg_upap = 0; + else +#endif /* PAP_SUPPORT */ + {} + + p += cilen - CILEN_SHORT; + } + } + +#if LQR_SUPPORT + /* + * If they can't cope with our link quality protocol, we'll have + * to stop asking for LQR. We haven't got any other protocol. + * If they Nak the reporting period, take their value XXX ? + */ + NAKCILQR(CI_QUALITY, neg_lqr, + if (cishort != PPP_LQR) + try_.neg_lqr = 0; + else + try_.lqr_period = cilong; + ); +#endif /* LQR_SUPPORT */ + + /* + * Only implementing CBCP...not the rest of the callback options + */ + NAKCICHAR(CI_CALLBACK, neg_cbcp, + try_.neg_cbcp = 0; + (void)cichar; /* if CHAP support is not compiled, cichar is set but not used, which makes some compilers complaining */ + ); + + /* + * Check for a looped-back line. + */ + NAKCILONG(CI_MAGICNUMBER, neg_magicnumber, + try_.magicnumber = magic(); + looped_back = 1; + ); + + /* + * Peer shouldn't send Nak for protocol compression or + * address/control compression requests; they should send + * a Reject instead. If they send a Nak, treat it as a Reject. + */ + NAKCIVOID(CI_PCOMPRESSION, neg_pcompression); + NAKCIVOID(CI_ACCOMPRESSION, neg_accompression); + +#ifdef HAVE_MULTILINK + /* + * Nak for MRRU option - accept their value if it is smaller + * than the one we want. + */ + if (go->neg_mrru) { + NAKCISHORT(CI_MRRU, neg_mrru, + if (treat_as_reject) + try_.neg_mrru = 0; + else if (cishort <= wo->mrru) + try_.mrru = cishort; + ); + } +#else /* HAVE_MULTILINK */ + LWIP_UNUSED_ARG(treat_as_reject); +#endif /* HAVE_MULTILINK */ + + /* + * Nak for short sequence numbers shouldn't be sent, treat it + * like a reject. + */ + NAKCIVOID(CI_SSNHF, neg_ssnhf); + + /* + * Nak of the endpoint discriminator option is not permitted, + * treat it like a reject. + */ + NAKCIENDP(CI_EPDISC, neg_endpoint); + + /* + * There may be remaining CIs, if the peer is requesting negotiation + * on an option that we didn't include in our request packet. + * If we see an option that we requested, or one we've already seen + * in this packet, then this packet is bad. + * If we wanted to respond by starting to negotiate on the requested + * option(s), we could, but we don't, because except for the + * authentication type and quality protocol, if we are not negotiating + * an option, it is because we were told not to. + * For the authentication type, the Nak from the peer means + * `let me authenticate myself with you' which is a bit pointless. + * For the quality protocol, the Nak means `ask me to send you quality + * reports', but if we didn't ask for them, we don't want them. + * An option we don't recognize represents the peer asking to + * negotiate some option we don't support, so ignore it. + */ + while (len >= CILEN_VOID) { + GETCHAR(citype, p); + GETCHAR(cilen, p); + if (cilen < CILEN_VOID || (len -= cilen) < 0) + goto bad; + next = p + cilen - 2; + + switch (citype) { + case CI_MRU: + if ((go->neg_mru && go->mru != DEFMRU) + || no.neg_mru || cilen != CILEN_SHORT) + goto bad; + GETSHORT(cishort, p); + if (cishort < DEFMRU) { + try_.neg_mru = 1; + try_.mru = cishort; + } + break; + case CI_ASYNCMAP: + if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) + || no.neg_asyncmap || cilen != CILEN_LONG) + goto bad; + break; + case CI_AUTHTYPE: + if (0 +#if CHAP_SUPPORT + || go->neg_chap || no.neg_chap +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + || go->neg_upap || no.neg_upap +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + || go->neg_eap || no.neg_eap +#endif /* EAP_SUPPORT */ + ) + goto bad; + break; + case CI_MAGICNUMBER: + if (go->neg_magicnumber || no.neg_magicnumber || + cilen != CILEN_LONG) + goto bad; + break; + case CI_PCOMPRESSION: + if (go->neg_pcompression || no.neg_pcompression + || cilen != CILEN_VOID) + goto bad; + break; + case CI_ACCOMPRESSION: + if (go->neg_accompression || no.neg_accompression + || cilen != CILEN_VOID) + goto bad; + break; +#if LQR_SUPPORT + case CI_QUALITY: + if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) + goto bad; + break; +#endif /* LQR_SUPPORT */ +#ifdef HAVE_MULTILINK + case CI_MRRU: + if (go->neg_mrru || no.neg_mrru || cilen != CILEN_SHORT) + goto bad; + break; +#endif /* HAVE_MULTILINK */ + case CI_SSNHF: + if (go->neg_ssnhf || no.neg_ssnhf || cilen != CILEN_VOID) + goto bad; + try_.neg_ssnhf = 1; + break; + case CI_EPDISC: + if (go->neg_endpoint || no.neg_endpoint || cilen < CILEN_CHAR) + goto bad; + break; + default: + break; + } + p = next; + } + + /* + * OK, the Nak is good. Now we can update state. + * If there are any options left we ignore them. + */ + if (f->state != PPP_FSM_OPENED) { + if (looped_back) { + if (++try_.numloops >= pcb->settings.lcp_loopbackfail) { + int errcode = PPPERR_LOOPBACK; + ppp_notice("Serial line is looped back."); + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(f->pcb, "Loopback detected"); + } + } else + try_.numloops = 0; + *go = try_; + } + + return 1; + +bad: + LCPDEBUG(("lcp_nakci: received bad Nak!")); + return 0; +} + + +/* + * lcp_rejci - Peer has Rejected some of our CIs. + * This should not modify any state if the Reject is bad + * or if LCP is in the OPENED state. + * + * Returns: + * 0 - Reject was bad. + * 1 - Reject was good. + */ +static int lcp_rejci(fsm *f, u_char *p, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u_char cichar; + u_short cishort; + u32_t cilong; + lcp_options try_; /* options to request next time */ + + try_ = *go; + + /* + * Any Rejected CIs must be in exactly the same order that we sent. + * Check packet length and CI length at each step. + * If we find any deviations, then this packet is bad. + */ +#define REJCIVOID(opt, neg) \ + if (go->neg && \ + len >= CILEN_VOID && \ + p[1] == CILEN_VOID && \ + p[0] == opt) { \ + len -= CILEN_VOID; \ + INCPTR(CILEN_VOID, p); \ + try_.neg = 0; \ + } +#define REJCISHORT(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_SHORT && \ + p[1] == CILEN_SHORT && \ + p[0] == opt) { \ + len -= CILEN_SHORT; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + /* Check rejected value. */ \ + if (cishort != val) \ + goto bad; \ + try_.neg = 0; \ + } + +#if CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_eap = try_.neg_upap = 0; \ + } +#endif /* CHAP_SUPPORT && EAP_SUPPORT && PAP_SUPPORT */ + +#if CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_upap = 0; \ + } +#endif /* CHAP_SUPPORT && !EAP_SUPPORT && PAP_SUPPORT */ + +#if CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + try_.neg_eap = 0; \ + } +#endif /* CHAP_SUPPORT && EAP_SUPPORT && !PAP_SUPPORT */ + +#if CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT +#define REJCICHAP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CHAP && \ + p[1] == CILEN_CHAP && \ + p[0] == opt) { \ + len -= CILEN_CHAP; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if ((cishort != PPP_CHAP) || (cichar != (CHAP_DIGEST(val)))) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* CHAP_SUPPORT && !EAP_SUPPORT && !PAP_SUPPORT */ + +#define REJCILONG(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LONG && \ + p[1] == CILEN_LONG && \ + p[0] == opt) { \ + len -= CILEN_LONG; \ + INCPTR(2, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } +#if LQR_SUPPORT +#define REJCILQR(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_LQR && \ + p[1] == CILEN_LQR && \ + p[0] == opt) { \ + len -= CILEN_LQR; \ + INCPTR(2, p); \ + GETSHORT(cishort, p); \ + GETLONG(cilong, p); \ + /* Check rejected value. */ \ + if (cishort != PPP_LQR || cilong != val) \ + goto bad; \ + try_.neg = 0; \ + } +#endif /* LQR_SUPPORT */ +#define REJCICBCP(opt, neg, val) \ + if (go->neg && \ + len >= CILEN_CBCP && \ + p[1] == CILEN_CBCP && \ + p[0] == opt) { \ + len -= CILEN_CBCP; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + /* Check rejected value. */ \ + if (cichar != val) \ + goto bad; \ + try_.neg = 0; \ + } +#define REJCIENDP(opt, neg, class, val, vlen) \ + if (go->neg && \ + len >= CILEN_CHAR + vlen && \ + p[0] == opt && \ + p[1] == CILEN_CHAR + vlen) { \ + int i; \ + len -= CILEN_CHAR + vlen; \ + INCPTR(2, p); \ + GETCHAR(cichar, p); \ + if (cichar != class) \ + goto bad; \ + for (i = 0; i < vlen; ++i) { \ + GETCHAR(cichar, p); \ + if (cichar != val[i]) \ + goto bad; \ + } \ + try_.neg = 0; \ + } + + REJCISHORT(CI_MRU, neg_mru, go->mru); + REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap); +#if EAP_SUPPORT + REJCISHORT(CI_AUTHTYPE, neg_eap, PPP_EAP); + if (!go->neg_eap) { +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + REJCICHAP(CI_AUTHTYPE, neg_chap, go->chap_mdtype); + if (!go->neg_chap) { +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP); +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + } +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + } +#endif /* EAP_SUPPORT */ +#if LQR_SUPPORT + REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period); +#endif /* LQR_SUPPORT */ + REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT); + REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber); + REJCIVOID(CI_PCOMPRESSION, neg_pcompression); + REJCIVOID(CI_ACCOMPRESSION, neg_accompression); +#ifdef HAVE_MULTILINK + REJCISHORT(CI_MRRU, neg_mrru, go->mrru); +#endif /* HAVE_MULTILINK */ + REJCIVOID(CI_SSNHF, neg_ssnhf); + REJCIENDP(CI_EPDISC, neg_endpoint, go->endpoint.class_, + go->endpoint.value, go->endpoint.length); + + /* + * If there are any remaining CIs, then this packet is bad. + */ + if (len != 0) + goto bad; + /* + * Now we can update state. + */ + if (f->state != PPP_FSM_OPENED) + *go = try_; + return 1; + +bad: + LCPDEBUG(("lcp_rejci: received bad Reject!")); + return 0; +} + + +/* + * lcp_reqci - Check the peer's requested CIs and send appropriate response. + * + * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified + * appropriately. If reject_if_disagree is non-zero, doesn't return + * CONFNAK; returns CONFREJ if it can't return CONFACK. + * + * inp = Requested CIs + * lenp = Length of requested CIs + */ +static int lcp_reqci(fsm *f, u_char *inp, int *lenp, int reject_if_disagree) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ho = &pcb->lcp_hisoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + u_char *cip, *next; /* Pointer to current and next CIs */ + int cilen, citype, cichar; /* Parsed len, type, char value */ + u_short cishort; /* Parsed short value */ + u32_t cilong; /* Parse long value */ + int rc = CONFACK; /* Final packet return code */ + int orc; /* Individual option return code */ + u_char *p; /* Pointer to next char to parse */ + u_char *rejp; /* Pointer to next char in reject frame */ + struct pbuf *nakp; /* Nak buffer */ + u_char *nakoutp; /* Pointer to next char in Nak frame */ + int l = *lenp; /* Length left */ + + /* + * Reset all his options. + */ + BZERO(ho, sizeof(*ho)); + + /* + * Process all his options. + */ + next = inp; + nakp = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_CTRL_PBUF_MAX_SIZE), PPP_CTRL_PBUF_TYPE); + if(NULL == nakp) + return 0; + if(nakp->tot_len != nakp->len) { + pbuf_free(nakp); + return 0; + } + + nakoutp = (u_char*)nakp->payload; + rejp = inp; + while (l) { + orc = CONFACK; /* Assume success */ + cip = p = next; /* Remember begining of CI */ + if (l < 2 || /* Not enough data for CI header or */ + p[1] < 2 || /* CI length too small or */ + p[1] > l) { /* CI length too big? */ + LCPDEBUG(("lcp_reqci: bad CI length!")); + orc = CONFREJ; /* Reject bad CI */ + cilen = l; /* Reject till end of packet */ + l = 0; /* Don't loop again */ + citype = 0; + goto endswitch; + } + GETCHAR(citype, p); /* Parse CI type */ + GETCHAR(cilen, p); /* Parse CI length */ + l -= cilen; /* Adjust remaining length */ + next += cilen; /* Step to next CI */ + + switch (citype) { /* Check CI type */ + case CI_MRU: + if (!ao->neg_mru || /* Allow option? */ + cilen != CILEN_SHORT) { /* Check CI length */ + orc = CONFREJ; /* Reject CI */ + break; + } + GETSHORT(cishort, p); /* Parse MRU */ + + /* + * He must be able to receive at least our minimum. + * No need to check a maximum. If he sends a large number, + * we'll just ignore it. + */ + if (cishort < MINMRU) { + orc = CONFNAK; /* Nak CI */ + PUTCHAR(CI_MRU, nakoutp); + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(MINMRU, nakoutp); /* Give him a hint */ + break; + } + ho->neg_mru = 1; /* Remember he sent MRU */ + ho->mru = cishort; /* And remember value */ + break; + + case CI_ASYNCMAP: + if (!ao->neg_asyncmap || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * Asyncmap must have set at least the bits + * which are set in lcp_allowoptions[unit].asyncmap. + */ + if ((ao->asyncmap & ~cilong) != 0) { + orc = CONFNAK; + PUTCHAR(CI_ASYNCMAP, nakoutp); + PUTCHAR(CILEN_LONG, nakoutp); + PUTLONG(ao->asyncmap | cilong, nakoutp); + break; + } + ho->neg_asyncmap = 1; + ho->asyncmap = cilong; + break; + + case CI_AUTHTYPE: + if (cilen < CILEN_SHORT || + !(0 +#if PAP_SUPPORT + || ao->neg_upap +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || ao->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || ao->neg_eap +#endif /* EAP_SUPPORT */ + )) { + /* + * Reject the option if we're not willing to authenticate. + */ + ppp_dbglog("No auth is possible"); + orc = CONFREJ; + break; + } + GETSHORT(cishort, p); + + /* + * Authtype must be PAP, CHAP, or EAP. + * + * Note: if more than one of ao->neg_upap, ao->neg_chap, and + * ao->neg_eap are set, and the peer sends a Configure-Request + * with two or more authenticate-protocol requests, then we will + * reject the second request. + * Whether we end up doing CHAP, UPAP, or EAP depends then on + * the ordering of the CIs in the peer's Configure-Request. + */ + +#if PAP_SUPPORT + if (cishort == PPP_PAP) { + /* we've already accepted CHAP or EAP */ + if (0 +#if CHAP_SUPPORT + || ho->neg_chap +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || ho->neg_eap +#endif /* EAP_SUPPORT */ + || cilen != CILEN_SHORT) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE PAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_upap) { /* we don't want to do PAP */ + orc = CONFNAK; /* NAK it and suggest CHAP or EAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_EAP, nakoutp); + } else { +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + } +#endif /* EAP_SUPPORT */ + break; + } + ho->neg_upap = 1; + break; + } +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + if (cishort == PPP_CHAP) { + /* we've already accepted PAP or EAP */ + if ( +#if PAP_SUPPORT + ho->neg_upap || +#endif /* PAP_SUPPORT */ +#if EAP_SUPPORT + ho->neg_eap || +#endif /* EAP_SUPPORT */ + cilen != CILEN_CHAP) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE CHAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_chap) { /* we don't want to do CHAP */ + orc = CONFNAK; /* NAK it and suggest EAP or PAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); + PUTCHAR(CILEN_SHORT, nakoutp); +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTSHORT(PPP_EAP, nakoutp); + } else +#endif /* EAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTSHORT(PPP_PAP, nakoutp); + } + else +#endif /* PAP_SUPPORT */ + {} + break; + } + GETCHAR(cichar, p); /* get digest type */ + if (!(CHAP_CANDIGEST(ao->chap_mdtype, cichar))) { + /* + * We can't/won't do the requested type, + * suggest something else. + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakoutp); + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + break; + } + ho->chap_mdtype = CHAP_MDTYPE_D(cichar); /* save md type */ + ho->neg_chap = 1; + break; + } +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + if (cishort == PPP_EAP) { + /* we've already accepted CHAP or PAP */ + if ( +#if CHAP_SUPPORT + ho->neg_chap || +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + ho->neg_upap || +#endif /* PAP_SUPPORT */ + cilen != CILEN_SHORT) { + LCPDEBUG(("lcp_reqci: rcvd AUTHTYPE EAP, rejecting...")); + orc = CONFREJ; + break; + } + if (!ao->neg_eap) { /* we don't want to do EAP */ + orc = CONFNAK; /* NAK it and suggest CHAP or PAP */ + PUTCHAR(CI_AUTHTYPE, nakoutp); +#if CHAP_SUPPORT + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_PAP, nakoutp); + } else +#endif /* PAP_SUPPORT */ + {} + break; + } + ho->neg_eap = 1; + break; + } +#endif /* EAP_SUPPORT */ + + /* + * We don't recognize the protocol they're asking for. + * Nak it with something we're willing to do. + * (At this point we know ao->neg_upap || ao->neg_chap || + * ao->neg_eap.) + */ + orc = CONFNAK; + PUTCHAR(CI_AUTHTYPE, nakoutp); + +#if EAP_SUPPORT + if (ao->neg_eap) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_EAP, nakoutp); + } else +#endif /* EAP_SUPPORT */ +#if CHAP_SUPPORT + if (ao->neg_chap) { + PUTCHAR(CILEN_CHAP, nakoutp); + PUTSHORT(PPP_CHAP, nakoutp); + PUTCHAR(CHAP_DIGEST(ao->chap_mdtype), nakoutp); + } else +#endif /* CHAP_SUPPORT */ +#if PAP_SUPPORT + if(1) { + PUTCHAR(CILEN_SHORT, nakoutp); + PUTSHORT(PPP_PAP, nakoutp); + } else +#endif /* PAP_SUPPORT */ + {} + break; + +#if LQR_SUPPORT + case CI_QUALITY: + if (!ao->neg_lqr || + cilen != CILEN_LQR) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + GETLONG(cilong, p); + + /* + * Check the protocol and the reporting period. + * XXX When should we Nak this, and what with? + */ + if (cishort != PPP_LQR) { + orc = CONFNAK; + PUTCHAR(CI_QUALITY, nakoutp); + PUTCHAR(CILEN_LQR, nakoutp); + PUTSHORT(PPP_LQR, nakoutp); + PUTLONG(ao->lqr_period, nakoutp); + break; + } + break; +#endif /* LQR_SUPPORT */ + + case CI_MAGICNUMBER: + if (!(ao->neg_magicnumber || go->neg_magicnumber) || + cilen != CILEN_LONG) { + orc = CONFREJ; + break; + } + GETLONG(cilong, p); + + /* + * He must have a different magic number. + */ + if (go->neg_magicnumber && + cilong == go->magicnumber) { + cilong = magic(); /* Don't put magic() inside macro! */ + orc = CONFNAK; + PUTCHAR(CI_MAGICNUMBER, nakoutp); + PUTCHAR(CILEN_LONG, nakoutp); + PUTLONG(cilong, nakoutp); + break; + } + ho->neg_magicnumber = 1; + ho->magicnumber = cilong; + break; + + + case CI_PCOMPRESSION: + if (!ao->neg_pcompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_pcompression = 1; + break; + + case CI_ACCOMPRESSION: + if (!ao->neg_accompression || + cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_accompression = 1; + break; + +#ifdef HAVE_MULTILINK + case CI_MRRU: + if (!ao->neg_mrru + || !multilink + || cilen != CILEN_SHORT) { + orc = CONFREJ; + break; + } + + GETSHORT(cishort, p); + /* possibly should insist on a minimum/maximum MRRU here */ + ho->neg_mrru = 1; + ho->mrru = cishort; + break; +#endif /* HAVE_MULTILINK */ + + case CI_SSNHF: + if (!ao->neg_ssnhf +#ifdef HAVE_MULTILINK + || !multilink +#endif /* HAVE_MULTILINK */ + || cilen != CILEN_VOID) { + orc = CONFREJ; + break; + } + ho->neg_ssnhf = 1; + break; + + case CI_EPDISC: + if (!ao->neg_endpoint || + cilen < CILEN_CHAR || + cilen > CILEN_CHAR + MAX_ENDP_LEN) { + orc = CONFREJ; + break; + } + GETCHAR(cichar, p); + cilen -= CILEN_CHAR; + ho->neg_endpoint = 1; + ho->endpoint.class_ = cichar; + ho->endpoint.length = cilen; + MEMCPY(ho->endpoint.value, p, cilen); + INCPTR(cilen, p); + break; + + default: + LCPDEBUG(("lcp_reqci: rcvd unknown option %d", citype)); + orc = CONFREJ; + break; + } + +endswitch: + if (orc == CONFACK && /* Good CI */ + rc != CONFACK) /* but prior CI wasnt? */ + continue; /* Don't send this one */ + + if (orc == CONFNAK) { /* Nak this CI? */ + if (reject_if_disagree /* Getting fed up with sending NAKs? */ + && citype != CI_MAGICNUMBER) { + orc = CONFREJ; /* Get tough if so */ + } else { + if (rc == CONFREJ) /* Rejecting prior CI? */ + continue; /* Don't send this one */ + rc = CONFNAK; + } + } + if (orc == CONFREJ) { /* Reject this CI */ + rc = CONFREJ; + if (cip != rejp) /* Need to move rejected CI? */ + MEMCPY(rejp, cip, cilen); /* Move it */ + INCPTR(cilen, rejp); /* Update output pointer */ + } + } + + /* + * If we wanted to send additional NAKs (for unsent CIs), the + * code would go here. The extra NAKs would go at *nakoutp. + * At present there are no cases where we want to ask the + * peer to negotiate an option. + */ + + switch (rc) { + case CONFACK: + *lenp = next - inp; + break; + case CONFNAK: + /* + * Copy the Nak'd options from the nak buffer to the caller's buffer. + */ + *lenp = nakoutp - (u_char*)nakp->payload; + MEMCPY(inp, nakp->payload, *lenp); + break; + case CONFREJ: + *lenp = rejp - inp; + break; + default: + break; + } + + pbuf_free(nakp); + LCPDEBUG(("lcp_reqci: returning CONF%s.", CODENAME(rc))); + return (rc); /* Return final code */ +} + + +/* + * lcp_up - LCP has come UP. + */ +static void lcp_up(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ho = &pcb->lcp_hisoptions; + lcp_options *go = &pcb->lcp_gotoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + int mtu, mru; + + if (!go->neg_magicnumber) + go->magicnumber = 0; + if (!ho->neg_magicnumber) + ho->magicnumber = 0; + + /* + * Set our MTU to the smaller of the MTU we wanted and + * the MRU our peer wanted. If we negotiated an MRU, + * set our MRU to the larger of value we wanted and + * the value we got in the negotiation. + * Note on the MTU: the link MTU can be the MRU the peer wanted, + * the interface MTU is set to the lowest of that, the + * MTU we want to use, and our link MRU. + */ + mtu = ho->neg_mru? ho->mru: PPP_MRU; + mru = go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU; +#ifdef HAVE_MULTILINK + if (!(multilink && go->neg_mrru && ho->neg_mrru)) +#endif /* HAVE_MULTILINK */ + netif_set_mtu(pcb, LWIP_MIN(LWIP_MIN(mtu, mru), ao->mru)); + ppp_send_config(pcb, mtu, + (ho->neg_asyncmap? ho->asyncmap: 0xffffffff), + ho->neg_pcompression, ho->neg_accompression); + ppp_recv_config(pcb, mru, + (pcb->settings.lax_recv? 0: go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + + if (ho->neg_mru) + pcb->peer_mru = ho->mru; + + lcp_echo_lowerup(f->pcb); /* Enable echo messages */ + + link_established(pcb); +} + + +/* + * lcp_down - LCP has gone DOWN. + * + * Alert other protocols. + */ +static void lcp_down(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + + lcp_echo_lowerdown(f->pcb); + + link_down(pcb); + + ppp_send_config(pcb, PPP_MRU, 0xffffffff, 0, 0); + ppp_recv_config(pcb, PPP_MRU, + (go->neg_asyncmap? go->asyncmap: 0xffffffff), + go->neg_pcompression, go->neg_accompression); + pcb->peer_mru = PPP_MRU; +} + + +/* + * lcp_starting - LCP needs the lower layer up. + */ +static void lcp_starting(fsm *f) { + ppp_pcb *pcb = f->pcb; + link_required(pcb); +} + + +/* + * lcp_finished - LCP has finished with the lower layer. + */ +static void lcp_finished(fsm *f) { + ppp_pcb *pcb = f->pcb; + link_terminated(pcb); +} + + +#if PRINTPKT_SUPPORT +/* + * lcp_printpkt - print the contents of an LCP packet. + */ +static const char *lcp_codenames[] = { + "ConfReq", "ConfAck", "ConfNak", "ConfRej", + "TermReq", "TermAck", "CodeRej", "ProtRej", + "EchoReq", "EchoRep", "DiscReq", "Ident", + "TimeRem" +}; + +static int lcp_printpkt(u_char *p, int plen, + void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len, olen, i; + u_char *pstart, *optend; + u_short cishort; + u32_t cilong; + + if (plen < HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)sizeof(lcp_codenames) / (int)sizeof(char *)) + printer(arg, " %s", lcp_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= HEADERLEN; + switch (code) { + case CONFREQ: + case CONFACK: + case CONFNAK: + case CONFREJ: + /* print option list */ + while (len >= 2) { + GETCHAR(code, p); + GETCHAR(olen, p); + p -= 2; + if (olen < 2 || olen > len) { + break; + } + printer(arg, " <"); + len -= olen; + optend = p + olen; + switch (code) { + case CI_MRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mru %d", cishort); + } + break; + case CI_ASYNCMAP: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "asyncmap 0x%x", cilong); + } + break; + case CI_AUTHTYPE: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "auth "); + GETSHORT(cishort, p); + switch (cishort) { +#if PAP_SUPPORT + case PPP_PAP: + printer(arg, "pap"); + break; +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + case PPP_CHAP: + printer(arg, "chap"); + if (p < optend) { + switch (*p) { + case CHAP_MD5: + printer(arg, " MD5"); + ++p; + break; +#if MSCHAP_SUPPORT + case CHAP_MICROSOFT: + printer(arg, " MS"); + ++p; + break; + + case CHAP_MICROSOFT_V2: + printer(arg, " MS-v2"); + ++p; + break; +#endif /* MSCHAP_SUPPORT */ + default: + break; + } + } + break; +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + case PPP_EAP: + printer(arg, "eap"); + break; +#endif /* EAP_SUPPORT */ + default: + printer(arg, "0x%x", cishort); + } + } + break; +#if LQR_SUPPORT + case CI_QUALITY: + if (olen >= CILEN_SHORT) { + p += 2; + printer(arg, "quality "); + GETSHORT(cishort, p); + switch (cishort) { + case PPP_LQR: + printer(arg, "lqr"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; +#endif /* LQR_SUPPORT */ + case CI_CALLBACK: + if (olen >= CILEN_CHAR) { + p += 2; + printer(arg, "callback "); + GETCHAR(cishort, p); + switch (cishort) { + case CBCP_OPT: + printer(arg, "CBCP"); + break; + default: + printer(arg, "0x%x", cishort); + } + } + break; + case CI_MAGICNUMBER: + if (olen == CILEN_LONG) { + p += 2; + GETLONG(cilong, p); + printer(arg, "magic 0x%x", cilong); + } + break; + case CI_PCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "pcomp"); + } + break; + case CI_ACCOMPRESSION: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "accomp"); + } + break; + case CI_MRRU: + if (olen == CILEN_SHORT) { + p += 2; + GETSHORT(cishort, p); + printer(arg, "mrru %d", cishort); + } + break; + case CI_SSNHF: + if (olen == CILEN_VOID) { + p += 2; + printer(arg, "ssnhf"); + } + break; + case CI_EPDISC: +#ifdef HAVE_MULTILINK + if (olen >= CILEN_CHAR) { + struct epdisc epd; + p += 2; + GETCHAR(epd.class, p); + epd.length = olen - CILEN_CHAR; + if (epd.length > MAX_ENDP_LEN) + epd.length = MAX_ENDP_LEN; + if (epd.length > 0) { + MEMCPY(epd.value, p, epd.length); + p += epd.length; + } + printer(arg, "endpoint [%s]", epdisc_to_str(&epd)); + } +#else + printer(arg, "endpoint"); +#endif + break; + default: + break; + } + while (p < optend) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + printer(arg, ">"); + } + break; + + case TERMACK: + case TERMREQ: + if (len > 0 && *p >= ' ' && *p < 0x7f) { + printer(arg, " "); + ppp_print_string((char *)p, len, printer, arg); + p += len; + len = 0; + } + break; + + case ECHOREQ: + case ECHOREP: + case DISCREQ: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + len -= 4; + } + break; + + case IDENTIF: + case TIMEREM: + if (len >= 4) { + GETLONG(cilong, p); + printer(arg, " magic=0x%x", cilong); + len -= 4; + } + if (code == TIMEREM) { + if (len < 4) + break; + GETLONG(cilong, p); + printer(arg, " seconds=%u", cilong); + len -= 4; + } + if (len > 0) { + printer(arg, " "); + ppp_print_string((char *)p, len, printer, arg); + p += len; + len = 0; + } + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (i = 0; i < len && i < 32; ++i) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + if (i < len) { + printer(arg, " ..."); + p += len - i; + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +/* + * Time to shut down the link because there is nothing out there. + */ + +static void LcpLinkFailure(fsm *f) { + ppp_pcb *pcb = f->pcb; + if (f->state == PPP_FSM_OPENED) { + int errcode = PPPERR_PEERDEAD; + ppp_info("No response to %d echo-requests", pcb->lcp_echos_pending); + ppp_notice("Serial link appears to be disconnected."); + ppp_ioctl(pcb, PPPCTLS_ERRCODE, &errcode); + lcp_close(pcb, "Peer not responding"); + } +} + +/* + * Timer expired for the LCP echo requests from this process. + */ + +static void LcpEchoCheck(fsm *f) { + ppp_pcb *pcb = f->pcb; + + LcpSendEchoRequest (f); + if (f->state != PPP_FSM_OPENED) + return; + + /* + * Start the timer for the next interval. + */ + if (pcb->lcp_echo_timer_running) + ppp_warn("assertion lcp_echo_timer_running==0 failed"); + TIMEOUT (LcpEchoTimeout, f, pcb->settings.lcp_echo_interval); + pcb->lcp_echo_timer_running = 1; +} + +/* + * LcpEchoTimeout - Timer expired on the LCP echo + */ + +static void LcpEchoTimeout(void *arg) { + fsm *f = (fsm*)arg; + ppp_pcb *pcb = f->pcb; + if (pcb->lcp_echo_timer_running != 0) { + pcb->lcp_echo_timer_running = 0; + LcpEchoCheck ((fsm *) arg); + } +} + +/* + * LcpEchoReply - LCP has received a reply to the echo + */ + +static void lcp_received_echo_reply(fsm *f, int id, u_char *inp, int len) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u32_t magic; + LWIP_UNUSED_ARG(id); + + /* Check the magic number - don't count replies from ourselves. */ + if (len < 4) { + ppp_dbglog("lcp: received short Echo-Reply, length %d", len); + return; + } + GETLONG(magic, inp); + if (go->neg_magicnumber + && magic == go->magicnumber) { + ppp_warn("appear to have received our own echo-reply!"); + return; + } + + /* Reset the number of outstanding echo frames */ + pcb->lcp_echos_pending = 0; +} + +/* + * LcpSendEchoRequest - Send an echo request frame to the peer + */ + +static void LcpSendEchoRequest(fsm *f) { + ppp_pcb *pcb = f->pcb; + lcp_options *go = &pcb->lcp_gotoptions; + u32_t lcp_magic; + u_char pkt[4], *pktp; + + /* + * Detect the failure of the peer at this point. + */ + if (pcb->settings.lcp_echo_fails != 0) { + if (pcb->lcp_echos_pending >= pcb->settings.lcp_echo_fails) { + LcpLinkFailure(f); + pcb->lcp_echos_pending = 0; + } + } + +#if PPP_LCP_ADAPTIVE + /* + * If adaptive echos have been enabled, only send the echo request if + * no traffic was received since the last one. + */ + if (pcb->settings.lcp_echo_adaptive) { + static unsigned int last_pkts_in = 0; + +#if PPP_STATS_SUPPORT + update_link_stats(f->unit); + link_stats_valid = 0; +#endif /* PPP_STATS_SUPPORT */ + + if (link_stats.pkts_in != last_pkts_in) { + last_pkts_in = link_stats.pkts_in; + return; + } + } +#endif + + /* + * Make and send the echo request frame. + */ + if (f->state == PPP_FSM_OPENED) { + lcp_magic = go->magicnumber; + pktp = pkt; + PUTLONG(lcp_magic, pktp); + fsm_sdata(f, ECHOREQ, pcb->lcp_echo_number++, pkt, pktp - pkt); + ++pcb->lcp_echos_pending; + } +} + +/* + * lcp_echo_lowerup - Start the timer for the LCP frame + */ + +static void lcp_echo_lowerup(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + /* Clear the parameters for generating echo frames */ + pcb->lcp_echos_pending = 0; + pcb->lcp_echo_number = 0; + pcb->lcp_echo_timer_running = 0; + + /* If a timeout interval is specified then start the timer */ + if (pcb->settings.lcp_echo_interval != 0) + LcpEchoCheck (f); +} + +/* + * lcp_echo_lowerdown - Stop the timer for the LCP frame + */ + +static void lcp_echo_lowerdown(ppp_pcb *pcb) { + fsm *f = &pcb->lcp_fsm; + + if (pcb->lcp_echo_timer_running != 0) { + UNTIMEOUT (LcpEchoTimeout, f); + pcb->lcp_echo_timer_running = 0; + } +} + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/magic.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/magic.c new file mode 100644 index 0000000..a6a5549 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/magic.c @@ -0,0 +1,267 @@ +/* + * magic.c - PPP Magic Number routines. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/***************************************************************************** +* randm.c - Random number generator program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* Copyright (c) 1998 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 98-06-03 Guy Lancaster , Global Election Systems Inc. +* Extracted from avos. +*****************************************************************************/ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/magic.h" + +#if PPP_MD5_RANDM /* Using MD5 for better randomness if enabled */ + +#if LWIP_INCLUDED_POLARSSL_MD5 +#include "netif/ppp/polarssl/md5.h" +#else +#include "polarssl/md5.h" +#endif + +#define MAGIC_RANDPOOLSIZE 16 /* Bytes stored in the pool of randomness. */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static char magic_randpool[MAGIC_RANDPOOLSIZE]; /* Pool of randomness. */ +static long magic_randcount = 0; /* Pseudo-random incrementer */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* + * Churn the randomness pool on a random event. Call this early and often + * on random and semi-random system events to build randomness in time for + * usage. For randomly timed events, pass a null pointer and a zero length + * and this will use the system timer and other sources to add randomness. + * If new random data is available, pass a pointer to that and it will be + * included. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + */ +static void magic_churnrand(char *rand_data, u32_t rand_len) { + md5_context md5; + + /* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: %u@%P\n", rand_len, rand_data)); */ + md5_starts(&md5); + md5_update(&md5, (u_char *)magic_randpool, sizeof(magic_randpool)); + if (rand_data) { + md5_update(&md5, (u_char *)rand_data, rand_len); + } else { + struct { + /* INCLUDE fields for any system sources of randomness */ + u32_t jiffies; + } sys_data; + sys_data.jiffies = sys_jiffies(); + /* Load sys_data fields here. */ + md5_update(&md5, (u_char *)&sys_data, sizeof(sys_data)); + } + md5_finish(&md5, (u_char *)magic_randpool); +/* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: -> 0\n")); */ +} + +/* + * Initialize the random number generator. + */ +void magic_init(void) { + magic_churnrand(NULL, 0); +} + +/* + * Randomize our random seed value. + */ +void magic_randomize(void) { + magic_churnrand(NULL, 0); +} + +/* + * random_bytes - Fill a buffer with random bytes. + * + * Use the random pool to generate random data. This degrades to pseudo + * random when used faster than randomness is supplied using magic_churnrand(). + * Note: It's important that there be sufficient randomness in magic_randpool + * before this is called for otherwise the range of the result may be + * narrow enough to make a search feasible. + * + * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427 + * + * XXX Why does he not just call magic_churnrand() for each block? Probably + * so that you don't ever publish the seed which could possibly help + * predict future values. + * XXX Why don't we preserve md5 between blocks and just update it with + * magic_randcount each time? Probably there is a weakness but I wish that + * it was documented. + */ +void random_bytes(unsigned char *buf, u32_t buf_len) { + md5_context md5; + u_char tmp[16]; + u32_t n; + + while (buf_len > 0) { + n = LWIP_MIN(buf_len, MAGIC_RANDPOOLSIZE); + md5_starts(&md5); + md5_update(&md5, (u_char *)magic_randpool, sizeof(magic_randpool)); + md5_update(&md5, (u_char *)&magic_randcount, sizeof(magic_randcount)); + md5_finish(&md5, tmp); + magic_randcount++; + MEMCPY(buf, tmp, n); + buf += n; + buf_len -= n; + } +} + +/* + * Return a new random number. + */ +u32_t magic(void) { + u32_t new_rand; + + random_bytes((unsigned char *)&new_rand, sizeof(new_rand)); + + return new_rand; +} + +#else /* PPP_MD5_RANDM */ + +/*****************************/ +/*** LOCAL DATA STRUCTURES ***/ +/*****************************/ +static int magic_randomized = 0; /* Set when truely randomized. */ +static u32_t magic_randomseed = 0; /* Seed used for random number generation. */ + + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* + * Initialize the random number generator. + * + * Here we attempt to compute a random number seed but even if + * it isn't random, we'll randomize it later. + * + * The current method uses the fields from the real time clock, + * the idle process counter, the millisecond counter, and the + * hardware timer tick counter. When this is invoked + * in startup(), then the idle counter and timer values may + * repeat after each boot and the real time clock may not be + * operational. Thus we call it again on the first random + * event. + */ +void magic_init() { + magic_randomseed += sys_jiffies(); + + /* Initialize the Borland random number generator. */ + srand((unsigned)magic_randomseed); +} + +/* + * magic_init - Initialize the magic number generator. + * + * Randomize our random seed value. Here we use the fact that + * this function is called at *truely random* times by the polling + * and network functions. Here we only get 16 bits of new random + * value but we use the previous value to randomize the other 16 + * bits. + */ +void magic_randomize(void) { + static u32_t last_jiffies; + + if (!magic_randomized) { + magic_randomized = !0; + magic_init(); + /* The initialization function also updates the seed. */ + } else { + /* magic_randomseed += (magic_randomseed << 16) + TM1; */ + magic_randomseed += (sys_jiffies() - last_jiffies); /* XXX */ + } + last_jiffies = sys_jiffies(); +} + +/* + * Return a new random number. + * + * Here we use the Borland rand() function to supply a pseudo random + * number which we make truely random by combining it with our own + * seed which is randomized by truely random events. + * Thus the numbers will be truely random unless there have been no + * operator or network events in which case it will be pseudo random + * seeded by the real time clock. + */ +u32_t magic() { + return ((((u32_t)rand() << 16) + rand()) + magic_randomseed); +} + +#endif /* PPP_MD5_RANDM */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/multilink.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/multilink.c new file mode 100644 index 0000000..3bea516 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/multilink.c @@ -0,0 +1,609 @@ +/* + * multilink.c - support routines for multilink. + * + * Copyright (c) 2000-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */ + +/* Multilink support + * + * Multilink uses Samba TDB (Trivial Database Library), which + * we cannot port, because it needs a filesystem. + * + * We have to choose between doing a memory-shared TDB-clone, + * or dropping multilink support at all. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/tdb.h" + +bool endpoint_specified; /* user gave explicit endpoint discriminator */ +char *bundle_id; /* identifier for our bundle */ +char *blinks_id; /* key for the list of links */ +bool doing_multilink; /* multilink was enabled and agreed to */ +bool multilink_master; /* we own the multilink bundle */ + +extern TDB_CONTEXT *pppdb; +extern char db_key[]; + +static void make_bundle_links (int append); +static void remove_bundle_link (void); +static void iterate_bundle_links (void (*func) (char *)); + +static int get_default_epdisc (struct epdisc *); +static int parse_num (char *str, const char *key, int *valp); +static int owns_unit (TDB_DATA pid, int unit); + +#define set_ip_epdisc(ep, addr) do { \ + ep->length = 4; \ + ep->value[0] = addr >> 24; \ + ep->value[1] = addr >> 16; \ + ep->value[2] = addr >> 8; \ + ep->value[3] = addr; \ +} while (0) + +#define LOCAL_IP_ADDR(addr) \ + (((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \ + || ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \ + || ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */ + +#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH) + +void +mp_check_options() +{ + lcp_options *wo = &lcp_wantoptions[0]; + lcp_options *ao = &lcp_allowoptions[0]; + + doing_multilink = 0; + if (!multilink) + return; + /* if we're doing multilink, we have to negotiate MRRU */ + if (!wo->neg_mrru) { + /* mrru not specified, default to mru */ + wo->mrru = wo->mru; + wo->neg_mrru = 1; + } + ao->mrru = ao->mru; + ao->neg_mrru = 1; + + if (!wo->neg_endpoint && !noendpoint) { + /* get a default endpoint value */ + wo->neg_endpoint = get_default_epdisc(&wo->endpoint); + } +} + +/* + * Make a new bundle or join us to an existing bundle + * if we are doing multilink. + */ +int +mp_join_bundle() +{ + lcp_options *go = &lcp_gotoptions[0]; + lcp_options *ho = &lcp_hisoptions[0]; + lcp_options *ao = &lcp_allowoptions[0]; + int unit, pppd_pid; + int l, mtu; + char *p; + TDB_DATA key, pid, rec; + + if (doing_multilink) { + /* have previously joined a bundle */ + if (!go->neg_mrru || !ho->neg_mrru) { + notice("oops, didn't get multilink on renegotiation"); + lcp_close(pcb, "multilink required"); + return 0; + } + /* XXX should check the peer_authname and ho->endpoint + are the same as previously */ + return 0; + } + + if (!go->neg_mrru || !ho->neg_mrru) { + /* not doing multilink */ + if (go->neg_mrru) + notice("oops, multilink negotiated only for receive"); + mtu = ho->neg_mru? ho->mru: PPP_MRU; + if (mtu > ao->mru) + mtu = ao->mru; + if (demand) { + /* already have a bundle */ + cfg_bundle(0, 0, 0, 0); + netif_set_mtu(pcb, mtu); + return 0; + } + make_new_bundle(0, 0, 0, 0); + set_ifunit(1); + netif_set_mtu(pcb, mtu); + return 0; + } + + doing_multilink = 1; + + /* + * Find the appropriate bundle or join a new one. + * First we make up a name for the bundle. + * The length estimate is worst-case assuming every + * character has to be quoted. + */ + l = 4 * strlen(peer_authname) + 10; + if (ho->neg_endpoint) + l += 3 * ho->endpoint.length + 8; + if (bundle_name) + l += 3 * strlen(bundle_name) + 2; + bundle_id = malloc(l); + if (bundle_id == 0) + novm("bundle identifier"); + + p = bundle_id; + p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname); + if (ho->neg_endpoint || bundle_name) + *p++ = '/'; + if (ho->neg_endpoint) + p += slprintf(p, bundle_id+l-p, "%s", + epdisc_to_str(&ho->endpoint)); + if (bundle_name) + p += slprintf(p, bundle_id+l-p, "/%v", bundle_name); + + /* Make the key for the list of links belonging to the bundle */ + l = p - bundle_id; + blinks_id = malloc(l + 7); + if (blinks_id == NULL) + novm("bundle links key"); + slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7); + + /* + * For demand mode, we only need to configure the bundle + * and attach the link. + */ + mtu = LWIP_MIN(ho->mrru, ao->mru); + if (demand) { + cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); + netif_set_mtu(pcb, mtu); + script_setenv("BUNDLE", bundle_id + 7, 1); + return 0; + } + + /* + * Check if the bundle ID is already in the database. + */ + unit = -1; + lock_db(); + key.dptr = bundle_id; + key.dsize = p - bundle_id; + pid = tdb_fetch(pppdb, key); + if (pid.dptr != NULL) { + /* bundle ID exists, see if the pppd record exists */ + rec = tdb_fetch(pppdb, pid); + if (rec.dptr != NULL && rec.dsize > 0) { + /* make sure the string is null-terminated */ + rec.dptr[rec.dsize-1] = 0; + /* parse the interface number */ + parse_num(rec.dptr, "IFNAME=ppp", &unit); + /* check the pid value */ + if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid) + || !process_exists(pppd_pid) + || !owns_unit(pid, unit)) + unit = -1; + free(rec.dptr); + } + free(pid.dptr); + } + + if (unit >= 0) { + /* attach to existing unit */ + if (bundle_attach(unit)) { + set_ifunit(0); + script_setenv("BUNDLE", bundle_id + 7, 0); + make_bundle_links(1); + unlock_db(); + info("Link attached to %s", ifname); + return 1; + } + /* attach failed because bundle doesn't exist */ + } + + /* we have to make a new bundle */ + make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf); + set_ifunit(1); + netif_set_mtu(pcb, mtu); + script_setenv("BUNDLE", bundle_id + 7, 1); + make_bundle_links(pcb); + unlock_db(); + info("New bundle %s created", ifname); + multilink_master = 1; + return 0; +} + +void mp_exit_bundle() +{ + lock_db(); + remove_bundle_link(); + unlock_db(); +} + +static void sendhup(char *str) +{ + int pid; + + if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) { + if (debug) + dbglog("sending SIGHUP to process %d", pid); + kill(pid, SIGHUP); + } +} + +void mp_bundle_terminated() +{ + TDB_DATA key; + + bundle_terminating = 1; + upper_layers_down(pcb); + notice("Connection terminated."); +#if PPP_STATS_SUPPORT + print_link_stats(); +#endif /* PPP_STATS_SUPPORT */ + if (!demand) { + remove_pidfiles(); + script_unsetenv("IFNAME"); + } + + lock_db(); + destroy_bundle(); + iterate_bundle_links(sendhup); + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + tdb_delete(pppdb, key); + unlock_db(); + + new_phase(PPP_PHASE_DEAD); + + doing_multilink = 0; + multilink_master = 0; +} + +static void make_bundle_links(int append) +{ + TDB_DATA key, rec; + char *p; + char entry[32]; + int l; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + slprintf(entry, sizeof(entry), "%s;", db_key); + p = entry; + if (append) { + rec = tdb_fetch(pppdb, key); + if (rec.dptr != NULL && rec.dsize > 0) { + rec.dptr[rec.dsize-1] = 0; + if (strstr(rec.dptr, db_key) != NULL) { + /* already in there? strange */ + warn("link entry already exists in tdb"); + return; + } + l = rec.dsize + strlen(entry); + p = malloc(l); + if (p == NULL) + novm("bundle link list"); + slprintf(p, l, "%s%s", rec.dptr, entry); + } else { + warn("bundle link list not found"); + } + if (rec.dptr != NULL) + free(rec.dptr); + } + rec.dptr = p; + rec.dsize = strlen(p) + 1; + if (tdb_store(pppdb, key, rec, TDB_REPLACE)) + error("couldn't %s bundle link list", + append? "update": "create"); + if (p != entry) + free(p); +} + +static void remove_bundle_link() +{ + TDB_DATA key, rec; + char entry[32]; + char *p, *q; + int l; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + slprintf(entry, sizeof(entry), "%s;", db_key); + + rec = tdb_fetch(pppdb, key); + if (rec.dptr == NULL || rec.dsize <= 0) { + if (rec.dptr != NULL) + free(rec.dptr); + return; + } + rec.dptr[rec.dsize-1] = 0; + p = strstr(rec.dptr, entry); + if (p != NULL) { + q = p + strlen(entry); + l = strlen(q) + 1; + memmove(p, q, l); + rec.dsize = p - rec.dptr + l; + if (tdb_store(pppdb, key, rec, TDB_REPLACE)) + error("couldn't update bundle link list (removal)"); + } + free(rec.dptr); +} + +static void iterate_bundle_links(void (*func)(char *)) +{ + TDB_DATA key, rec, pp; + char *p, *q; + + key.dptr = blinks_id; + key.dsize = strlen(blinks_id); + rec = tdb_fetch(pppdb, key); + if (rec.dptr == NULL || rec.dsize <= 0) { + error("bundle link list not found (iterating list)"); + if (rec.dptr != NULL) + free(rec.dptr); + return; + } + p = rec.dptr; + p[rec.dsize-1] = 0; + while ((q = strchr(p, ';')) != NULL) { + *q = 0; + key.dptr = p; + key.dsize = q - p; + pp = tdb_fetch(pppdb, key); + if (pp.dptr != NULL && pp.dsize > 0) { + pp.dptr[pp.dsize-1] = 0; + func(pp.dptr); + } + if (pp.dptr != NULL) + free(pp.dptr); + p = q + 1; + } + free(rec.dptr); +} + +static int +parse_num(str, key, valp) + char *str; + const char *key; + int *valp; +{ + char *p, *endp; + int i; + + p = strstr(str, key); + if (p != 0) { + p += strlen(key); + i = strtol(p, &endp, 10); + if (endp != p && (*endp == 0 || *endp == ';')) { + *valp = i; + return 1; + } + } + return 0; +} + +/* + * Check whether the pppd identified by `key' still owns ppp unit `unit'. + */ +static int +owns_unit(key, unit) + TDB_DATA key; + int unit; +{ + char ifkey[32]; + TDB_DATA kd, vd; + int ret = 0; + + slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit); + kd.dptr = ifkey; + kd.dsize = strlen(ifkey); + vd = tdb_fetch(pppdb, kd); + if (vd.dptr != NULL) { + ret = vd.dsize == key.dsize + && memcmp(vd.dptr, key.dptr, vd.dsize) == 0; + free(vd.dptr); + } + return ret; +} + +static int +get_default_epdisc(ep) + struct epdisc *ep; +{ + char *p; + struct hostent *hp; + u32_t addr; + + /* First try for an ethernet MAC address */ + p = get_first_ethernet(); + if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) { + ep->class = EPD_MAC; + ep->length = 6; + return 1; + } + + /* see if our hostname corresponds to a reasonable IP address */ + hp = gethostbyname(hostname); + if (hp != NULL) { + addr = *(u32_t *)hp->h_addr; + if (!bad_ip_adrs(addr)) { + addr = ntohl(addr); + if (!LOCAL_IP_ADDR(addr)) { + ep->class = EPD_IP; + set_ip_epdisc(ep, addr); + return 1; + } + } + } + + return 0; +} + +/* + * epdisc_to_str - make a printable string from an endpoint discriminator. + */ + +static char *endp_class_names[] = { + "null", "local", "IP", "MAC", "magic", "phone" +}; + +char * +epdisc_to_str(ep) + struct epdisc *ep; +{ + static char str[MAX_ENDP_LEN*3+8]; + u_char *p = ep->value; + int i, mask = 0; + char *q, c, c2; + + if (ep->class == EPD_NULL && ep->length == 0) + return "null"; + if (ep->class == EPD_IP && ep->length == 4) { + u32_t addr; + + GETLONG(addr, p); + slprintf(str, sizeof(str), "IP:%I", htonl(addr)); + return str; + } + + c = ':'; + c2 = '.'; + if (ep->class == EPD_MAC && ep->length == 6) + c2 = ':'; + else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0) + mask = 3; + q = str; + if (ep->class <= EPD_PHONENUM) + q += slprintf(q, sizeof(str)-1, "%s", + endp_class_names[ep->class]); + else + q += slprintf(q, sizeof(str)-1, "%d", ep->class); + c = ':'; + for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) { + if ((i & mask) == 0) { + *q++ = c; + c = c2; + } + q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]); + } + return str; +} + +static int hexc_val(int c) +{ + if (c >= 'a') + return c - 'a' + 10; + if (c >= 'A') + return c - 'A' + 10; + return c - '0'; +} + +int +str_to_epdisc(ep, str) + struct epdisc *ep; + char *str; +{ + int i, l; + char *p, *endp; + + for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) { + int sl = strlen(endp_class_names[i]); + if (strncasecmp(str, endp_class_names[i], sl) == 0) { + str += sl; + break; + } + } + if (i > EPD_PHONENUM) { + /* not a class name, try a decimal class number */ + i = strtol(str, &endp, 10); + if (endp == str) + return 0; /* can't parse class number */ + str = endp; + } + ep->class = i; + if (*str == 0) { + ep->length = 0; + return 1; + } + if (*str != ':' && *str != '.') + return 0; + ++str; + + if (i == EPD_IP) { + u32_t addr; + i = parse_dotted_ip(str, &addr); + if (i == 0 || str[i] != 0) + return 0; + set_ip_epdisc(ep, addr); + return 1; + } + if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) { + ep->length = 6; + return 1; + } + + p = str; + for (l = 0; l < MAX_ENDP_LEN; ++l) { + if (*str == 0) + break; + if (p <= str) + for (p = str; isxdigit(*p); ++p) + ; + i = p - str; + if (i == 0) + return 0; + ep->value[l] = hexc_val(*str++); + if ((i & 1) == 0) + ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++); + if (*str == ':' || *str == '.') + ++str; + } + if (*str != 0 || (ep->class == EPD_MAC && l != 6)) + return 0; + ep->length = l; + return 1; +} + +#endif /* PPP_SUPPORT && HAVE_MULTILINK */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/README b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/README new file mode 100644 index 0000000..71f78fa --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/README @@ -0,0 +1,34 @@ +About PolarSSL files into lwIP PPP support +------------------------------------------ + +This folder contains some files fetched from the latest BSD release of +the PolarSSL project for ciphers and encryption methods we need for lwIP +PPP support. + +The PolarSSL files were cleaned to contain only the necessary struct +fields and functions needed for lwIP. + + +The PolarSSL API was not changed at all, so if you are already using +PolarSSL you can choose to skip the compilation of the included PolarSSL +library into lwIP: + +The following defines are available for flexibility: + +LWIP_INCLUDED_POLARSSL_MD4 ; Use lwIP internal PolarSSL for MD4 +LWIP_INCLUDED_POLARSSL_MD5 ; Use lwIP internal PolarSSL for MD5 +LWIP_INCLUDED_POLARSSL_SHA1 ; Use lwIP internal PolarSSL for SHA1 +LWIP_INCLUDED_POLARSSL_DES ; Use lwIP internal PolarSSL for DES + +If set (=1), the default if required by another enabled PPP feature unless +explicitly set to 0, using included lwIP PolarSSL. + +If clear (=0), using external PolarSSL. + +Undefined if not needed. + +Beware of the stack requirements which can be a lot larger if you are not +using our cleaned PolarSSL library. + + +PolarSSL project website: http://polarssl.org/ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/des.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/des.c new file mode 100644 index 0000000..38f3a74 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/des.c @@ -0,0 +1,422 @@ +/* + * FIPS-46-3 compliant Triple-DES implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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. + */ +/* + * DES, on which TDES is based, was originally designed by Horst Feistel + * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). + * + * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES + +#include "netif/ppp/polarssl/des.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * Expanded DES S-boxes + */ +static const unsigned long SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const unsigned long SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const unsigned long SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const unsigned long SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const unsigned long SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const unsigned long SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const unsigned long SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const unsigned long SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* + * PC1: left and right halves bit-swap + */ +static const unsigned long LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const unsigned long RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* + * Initial Permutation macro + */ +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* + * Final Permutation macro + */ +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* + * DES round macro + */ +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +#define SWAP(a,b) { unsigned long t = a; a = b; b = t; t = 0; } + +static void des_setkey( unsigned long SK[32], unsigned char key[8] ) +{ + int i; + unsigned long X, Y, T; + + GET_ULONG_BE( X, key, 0 ); + GET_ULONG_BE( Y, key, 4 ); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* + * DES key schedule (56-bit, encryption) + */ +void des_setkey_enc( des_context *ctx, unsigned char key[8] ) +{ + des_setkey( ctx->sk, key ); +} + +/* + * DES key schedule (56-bit, decryption) + */ +void des_setkey_dec( des_context *ctx, unsigned char key[8] ) +{ + int i; + + des_setkey( ctx->sk, key ); + + for( i = 0; i < 16; i += 2 ) + { + SWAP( ctx->sk[i ], ctx->sk[30 - i] ); + SWAP( ctx->sk[i + 1], ctx->sk[31 - i] ); + } +} + +/* + * DES-ECB block encryption/decryption + */ +void des_crypt_ecb( des_context *ctx, + unsigned char input[8], + unsigned char output[8] ) +{ + int i; + unsigned long X, Y, T, *SK; + + SK = ctx->sk; + + GET_ULONG_BE( X, input, 0 ); + GET_ULONG_BE( Y, input, 4 ); + + DES_IP( X, Y ); + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( Y, X ); + DES_ROUND( X, Y ); + } + + DES_FP( Y, X ); + + PUT_ULONG_BE( Y, output, 0 ); + PUT_ULONG_BE( X, output, 4 ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_DES */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md4.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md4.c new file mode 100644 index 0000000..8de2eb9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md4.c @@ -0,0 +1,279 @@ +/* + * RFC 1186/1320 compliant MD4 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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. + */ +/* + * The MD4 algorithm was designed by Ron Rivest in 1990. + * + * http://www.ietf.org/rfc/rfc1186.txt + * http://www.ietf.org/rfc/rfc1320.txt + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 + +#include "netif/ppp/polarssl/md4.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD4 context setup + */ +void md4_starts( md4_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md4_process( md4_context *ctx, unsigned char data[64] ) +{ + unsigned long X[16], A, B, C, D; + + GET_ULONG_LE( X[ 0], data, 0 ); + GET_ULONG_LE( X[ 1], data, 4 ); + GET_ULONG_LE( X[ 2], data, 8 ); + GET_ULONG_LE( X[ 3], data, 12 ); + GET_ULONG_LE( X[ 4], data, 16 ); + GET_ULONG_LE( X[ 5], data, 20 ); + GET_ULONG_LE( X[ 6], data, 24 ); + GET_ULONG_LE( X[ 7], data, 28 ); + GET_ULONG_LE( X[ 8], data, 32 ); + GET_ULONG_LE( X[ 9], data, 36 ); + GET_ULONG_LE( X[10], data, 40 ); + GET_ULONG_LE( X[11], data, 44 ); + GET_ULONG_LE( X[12], data, 48 ); + GET_ULONG_LE( X[13], data, 52 ); + GET_ULONG_LE( X[14], data, 56 ); + GET_ULONG_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x, y, z) ((x & y) | ((~x) & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 1], 7 ); + P( C, D, A, B, X[ 2], 11 ); + P( B, C, D, A, X[ 3], 19 ); + P( A, B, C, D, X[ 4], 3 ); + P( D, A, B, C, X[ 5], 7 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[ 7], 19 ); + P( A, B, C, D, X[ 8], 3 ); + P( D, A, B, C, X[ 9], 7 ); + P( C, D, A, B, X[10], 11 ); + P( B, C, D, A, X[11], 19 ); + P( A, B, C, D, X[12], 3 ); + P( D, A, B, C, X[13], 7 ); + P( C, D, A, B, X[14], 11 ); + P( B, C, D, A, X[15], 19 ); + +#undef P +#undef F + +#define F(x,y,z) ((x & y) | (x & z) | (y & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 4], 5 ); + P( C, D, A, B, X[ 8], 9 ); + P( B, C, D, A, X[12], 13 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 5], 5 ); + P( C, D, A, B, X[ 9], 9 ); + P( B, C, D, A, X[13], 13 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[ 6], 5 ); + P( C, D, A, B, X[10], 9 ); + P( B, C, D, A, X[14], 13 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[ 7], 5 ); + P( C, D, A, B, X[11], 9 ); + P( B, C, D, A, X[15], 13 ); + +#undef P +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 8], 9 ); + P( C, D, A, B, X[ 4], 11 ); + P( B, C, D, A, X[12], 15 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[10], 9 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[14], 15 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 9], 9 ); + P( C, D, A, B, X[ 5], 11 ); + P( B, C, D, A, X[13], 15 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[11], 9 ); + P( C, D, A, B, X[ 7], 11 ); + P( B, C, D, A, X[15], 15 ); + +#undef F +#undef P + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD4 process buffer + */ +void md4_update( md4_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, fill ); + md4_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md4_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char md4_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 +}; + +/* + * MD4 final digest + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_LE( low, msglen, 0 ); + PUT_ULONG_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md4_update( ctx, (unsigned char *) md4_padding, padn ); + md4_update( ctx, msglen, 8 ); + + PUT_ULONG_LE( ctx->state[0], output, 0 ); + PUT_ULONG_LE( ctx->state[1], output, 4 ); + PUT_ULONG_LE( ctx->state[2], output, 8 ); + PUT_ULONG_LE( ctx->state[3], output, 12 ); +} + +/* + * output = MD4( input buffer ) + */ +void md4( unsigned char *input, int ilen, unsigned char output[16] ) +{ + md4_context ctx; + + md4_starts( &ctx ); + md4_update( &ctx, input, ilen ); + md4_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD4 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md5.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md5.c new file mode 100644 index 0000000..50aa911 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/md5.c @@ -0,0 +1,298 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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. + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 + +#include "netif/ppp/polarssl/md5.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_ULONG_LE +#define GET_ULONG_LE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] ) \ + | ( (unsigned long) (b)[(i) + 1] << 8 ) \ + | ( (unsigned long) (b)[(i) + 2] << 16 ) \ + | ( (unsigned long) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_ULONG_LE +#define PUT_ULONG_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* + * MD5 context setup + */ +void md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +static void md5_process( md5_context *ctx, unsigned char data[64] ) +{ + unsigned long X[16], A, B, C, D; + + GET_ULONG_LE( X[ 0], data, 0 ); + GET_ULONG_LE( X[ 1], data, 4 ); + GET_ULONG_LE( X[ 2], data, 8 ); + GET_ULONG_LE( X[ 3], data, 12 ); + GET_ULONG_LE( X[ 4], data, 16 ); + GET_ULONG_LE( X[ 5], data, 20 ); + GET_ULONG_LE( X[ 6], data, 24 ); + GET_ULONG_LE( X[ 7], data, 28 ); + GET_ULONG_LE( X[ 8], data, 32 ); + GET_ULONG_LE( X[ 9], data, 36 ); + GET_ULONG_LE( X[10], data, 40 ); + GET_ULONG_LE( X[11], data, 44 ); + GET_ULONG_LE( X[12], data, 48 ); + GET_ULONG_LE( X[13], data, 52 ); + GET_ULONG_LE( X[14], data, 56 ); + GET_ULONG_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD5 process buffer + */ +void md5_update( md5_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, fill ); + md5_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md5_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char md5_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 +}; + +/* + * MD5 final digest + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_LE( low, msglen, 0 ); + PUT_ULONG_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, (unsigned char *) md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_ULONG_LE( ctx->state[0], output, 0 ); + PUT_ULONG_LE( ctx->state[1], output, 4 ); + PUT_ULONG_LE( ctx->state[2], output, 8 ); + PUT_ULONG_LE( ctx->state[3], output, 12 ); +} + +/* + * output = MD5( input buffer ) + */ +void md5( unsigned char *input, int ilen, unsigned char output[16] ) +{ + md5_context ctx; + + md5_starts( &ctx ); + md5_update( &ctx, input, ilen ); + md5_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_MD5 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/sha1.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/sha1.c new file mode 100644 index 0000000..78fe46e --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/polarssl/sha1.c @@ -0,0 +1,333 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Based on XySSL: Copyright (C) 2006-2008 Christophe Devine + * + * Copyright (C) 2009 Paul Bakker + * + * 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 names of PolarSSL or XySSL 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. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 + +#include "netif/ppp/polarssl/sha1.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_ULONG_BE +#define GET_ULONG_BE(n,b,i) \ +{ \ + (n) = ( (unsigned long) (b)[(i) ] << 24 ) \ + | ( (unsigned long) (b)[(i) + 1] << 16 ) \ + | ( (unsigned long) (b)[(i) + 2] << 8 ) \ + | ( (unsigned long) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_ULONG_BE +#define PUT_ULONG_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * SHA-1 context setup + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +static void sha1_process( sha1_context *ctx, unsigned char data[64] ) +{ + unsigned long temp, W[16], A, B, C, D, E; + + GET_ULONG_BE( W[ 0], data, 0 ); + GET_ULONG_BE( W[ 1], data, 4 ); + GET_ULONG_BE( W[ 2], data, 8 ); + GET_ULONG_BE( W[ 3], data, 12 ); + GET_ULONG_BE( W[ 4], data, 16 ); + GET_ULONG_BE( W[ 5], data, 20 ); + GET_ULONG_BE( W[ 6], data, 24 ); + GET_ULONG_BE( W[ 7], data, 28 ); + GET_ULONG_BE( W[ 8], data, 32 ); + GET_ULONG_BE( W[ 9], data, 36 ); + GET_ULONG_BE( W[10], data, 40 ); + GET_ULONG_BE( W[11], data, 44 ); + GET_ULONG_BE( W[12], data, 48 ); + GET_ULONG_BE( W[13], data, 52 ); + GET_ULONG_BE( W[14], data, 56 ); + GET_ULONG_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ) +{ + int fill; + unsigned long left; + + if( ilen <= 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (unsigned long) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + MEMCPY( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char sha1_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 +}; + +/* + * SHA-1 final digest + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned long last, padn; + unsigned long high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_ULONG_BE( high, msglen, 0 ); + PUT_ULONG_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, (unsigned char *) sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_ULONG_BE( ctx->state[0], output, 0 ); + PUT_ULONG_BE( ctx->state[1], output, 4 ); + PUT_ULONG_BE( ctx->state[2], output, 8 ); + PUT_ULONG_BE( ctx->state[3], output, 12 ); + PUT_ULONG_BE( ctx->state[4], output, 16 ); +} + +/* + * output = SHA-1( input buffer ) + */ +void sha1( unsigned char *input, int ilen, unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); +} + +#endif /* PPP_SUPPORT && LWIP_INCLUDED_POLARSSL_SHA1 */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ppp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ppp.c new file mode 100644 index 0000000..bc85396 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/ppp.c @@ -0,0 +1,2606 @@ +/***************************************************************************** +* ppp.c - Network Point to Point Protocol program file. +* +* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. +* portions Copyright (c) 1997 by Global Election Systems 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 +* +* 03-01-01 Marc Boucher +* Ported to lwIP. +* 97-11-05 Guy Lancaster , Global Election Systems Inc. +* Original. +*****************************************************************************/ + +/* + * ppp_defs.h - PPP definitions. + * + * if_pppvar.h - private structures and declarations for PPP. + * + * Copyright (c) 1994 The Australian National University. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation is hereby granted, provided that the above copyright + * notice appears in all copies. This software is provided without any + * warranty, express or implied. The Australian National University + * makes no representations about the suitability of this software for + * any purpose. + * + * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY + * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO + * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, + * OR MODIFICATIONS. + */ + +/* + * if_ppp.h - Point-to-Point Protocol definitions. + * + * Copyright (c) 1989 Carnegie Mellon University. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by Carnegie Mellon University. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/sys.h" +#include "lwip/tcpip.h" +#include "lwip/api.h" +#include "lwip/snmp.h" +#include "lwip/sio.h" +#include "lwip/sys.h" +#include "lwip/ip.h" /* for ip_input() */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" +#include "netif/ppp/ipcp.h" +#include "netif/ppp/magic.h" + +#if PAP_SUPPORT +#include "netif/ppp/upap.h" +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT +#include "netif/ppp/chap-new.h" +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT +#include "netif/ppp/eap.h" +#endif /* EAP_SUPPORT */ +#if CCP_SUPPORT +#include "netif/ppp/ccp.h" +#endif /* EAP_SUPPORT */ +#if ECP_SUPPORT +#include "netif/ppp/ecp.h" +#endif /* EAP_SUPPORT */ +#if VJ_SUPPORT +#include "netif/ppp/vj.h" +#endif /* VJ_SUPPORT */ +#if PPP_IPV6_SUPPORT +#include "netif/ppp/ipv6cp.h" +#endif /* PPP_IPV6_SUPPORT */ + +#if PPPOE_SUPPORT +#include "netif/ppp/pppoe.h" +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT +#include "netif/ppp/pppol2tp.h" +#endif /* PPPOL2TP_SUPPORT */ + +/* Global variables */ + +#if PPP_DEBUG +u8_t ppp_num; /* PPP Interface counter, used for debugging messages */ +#endif /* PPP_DEBUG */ + +/*************************/ +/*** LOCAL DEFINITIONS ***/ +/*************************/ + +/* FIXME: add stats per PPP session */ +#if PPP_STATS_SUPPORT +static struct timeval start_time; /* Time when link was started. */ +static struct pppd_stats old_link_stats; +struct pppd_stats link_stats; +unsigned link_connect_time; +int link_stats_valid; +#endif /* PPP_STATS_SUPPORT */ + +/* + * PPP Data Link Layer "protocol" table. + * One entry per supported protocol. + * The last entry must be NULL. + */ +const struct protent* const protocols[] = { + &lcp_protent, +#if PAP_SUPPORT + &pap_protent, +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + &chap_protent, +#endif /* CHAP_SUPPORT */ +#if CBCP_SUPPORT + &cbcp_protent, +#endif + &ipcp_protent, +#if PPP_IPV6_SUPPORT + &ipv6cp_protent, +#endif +#if CCP_SUPPORT + &ccp_protent, +#endif /* CCP_SUPPORT */ +#if ECP_SUPPORT + &ecp_protent, +#endif /* ECP_SUPPORT */ +#ifdef AT_CHANGE + &atcp_protent, +#endif +#if EAP_SUPPORT + &eap_protent, +#endif /* EAP_SUPPORT */ + NULL +}; + +/* Prototypes for procedures local to this file. */ +static void ppp_clear(ppp_pcb *pcb); +static void ppp_do_open(void *arg); +static void ppp_start(ppp_pcb *pcb); /** Initiate LCP open request */ +static void ppp_stop(ppp_pcb *pcb); +static void ppp_hup(ppp_pcb *pcb); + +static err_t ppp_netif_init_cb(struct netif *netif); +static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr); +#if PPP_IPV6_SUPPORT +static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, ip6_addr_t *ipaddr); +#endif /* PPP_IPV6_SUPPORT */ +static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u_short protocol); + +#if PPPOS_SUPPORT +#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & ppp_accm_mask[c & 0x07]) +static void ppp_over_serial_open(ppp_pcb *pcb); +static err_t ppp_netif_output_over_serial(ppp_pcb *pcb, struct pbuf *pb, u_short protocol); +static int ppp_write_over_serial(ppp_pcb *pcb, struct pbuf *p); +static void ppp_drop(ppp_pcb_rx *pcrx); +#if PPP_INPROC_MULTITHREADED +static void pppos_input_callback(void *arg); +#endif /* PPP_INPROC_MULTITHREADED */ +static void ppp_free_current_input_packet(ppp_pcb_rx *pcrx); +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void ppp_over_ethernet_open(ppp_pcb *pcb); +static err_t ppp_netif_output_over_ethernet(ppp_pcb *pcb, struct pbuf *p, u_short protocol); +static int ppp_write_over_ethernet(ppp_pcb *pcb, struct pbuf *p); +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT +static void ppp_over_l2tp_open(ppp_pcb *pcb); +static err_t ppp_netif_output_over_l2tp(ppp_pcb *pcb, struct pbuf *p, u_short protocol); +static int ppp_write_over_l2tp(ppp_pcb *pcb, struct pbuf *p); +#endif /* PPPOL2TP_SUPPORT */ + +/***********************************/ +/*** PUBLIC FUNCTION DEFINITIONS ***/ +/***********************************/ + +/* Initialize the PPP subsystem. */ +int ppp_init(void) { + + /* + * Initialize magic number generator now so that protocols may + * use magic numbers in initialization. + */ + magic_init(); + + return 0; +} + +/* Create a new PPP session. */ +ppp_pcb *ppp_new(void) { + ppp_pcb *pcb; + + pcb = (ppp_pcb*)memp_malloc(MEMP_PPP_PCB); + if (pcb == NULL) { + return NULL; + } + + memset(pcb, 0, sizeof(ppp_pcb)); +#if PPP_DEBUG + pcb->num = ppp_num++; +#endif /* PPP_DEBUG */ + + /* default configuration */ + pcb->settings.usepeerdns = 1; + +#if PAP_SUPPORT + pcb->settings.pap_timeout_time = UPAP_DEFTIMEOUT; + pcb->settings.pap_max_transmits = UPAP_DEFTRANSMITS; +#if PPP_SERVER + pcb->settings.pap_req_timeout = UPAP_DEFREQTIME; +#endif /* PPP_SERVER */ +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + pcb->settings.chap_timeout_time = CHAP_DEFTIMEOUT; + pcb->settings.chap_max_transmits = CHAP_DEFTRANSMITS; +#if PPP_SERVER + pcb->settings.chap_rechallenge_time = CHAP_DEFREQTIME; +#endif /* PPP_SERVER */ +#endif /* CHAP_SUPPPORT */ + +#if EAP_SUPPORT + pcb->settings.eap_req_time = EAP_DEFREQTIME; + pcb->settings.eap_allow_req = EAP_DEFALLOWREQ; +#if PPP_SERVER + pcb->settings.eap_timeout_time = EAP_DEFTIMEOUT; + pcb->settings.eap_max_transmits = EAP_DEFTRANSMITS; +#endif /* PPP_SERVER */ +#endif /* EAP_SUPPORT */ + + pcb->settings.lcp_loopbackfail = LCP_DEFLOOPBACKFAIL; + pcb->settings.lcp_echo_interval = LCP_ECHOINTERVAL; + pcb->settings.lcp_echo_fails = LCP_MAXECHOFAILS; + + pcb->settings.fsm_timeout_time = FSM_DEFTIMEOUT; + pcb->settings.fsm_max_conf_req_transmits = FSM_DEFMAXCONFREQS; + pcb->settings.fsm_max_term_transmits = FSM_DEFMAXTERMREQS; + pcb->settings.fsm_max_nak_loops = FSM_DEFMAXNAKLOOPS; + + if (!netif_add(&pcb->netif, &pcb->addrs.our_ipaddr, &pcb->addrs.netmask, + &pcb->addrs.his_ipaddr, (void *)pcb, ppp_netif_init_cb, NULL)) { + memp_free(MEMP_PPP_PCB, pcb); + PPPDEBUG(LOG_ERR, ("ppp_new[%d]: netif_add failed\n", pcb->num)); + return NULL; + } + + new_phase(pcb, PPP_PHASE_DEAD); + return pcb; +} + +void ppp_set_default(ppp_pcb *pcb) { + netif_set_default(&pcb->netif); +} + +void ppp_set_auth(ppp_pcb *pcb, u8_t authtype, const char *user, const char *passwd) { + +#if PAP_SUPPORT + if (authtype & PPPAUTHTYPE_PAP) { + pcb->settings.refuse_pap = 0; + } else { + pcb->settings.refuse_pap = 1; + } +#endif /* PAP_SUPPORT */ + +#if CHAP_SUPPORT + if (authtype & PPPAUTHTYPE_CHAP) { + pcb->settings.refuse_chap = 0; + } else { + pcb->settings.refuse_chap = 1; + } +#if MSCHAP_SUPPORT + if (authtype & PPPAUTHTYPE_MSCHAP) { + pcb->settings.refuse_mschap = 0; + pcb->settings.refuse_mschap_v2 = 0; + } else { + pcb->settings.refuse_mschap = 1; + pcb->settings.refuse_mschap_v2 = 1; + } +#endif /* MSCHAP_SUPPORT */ +#endif /* CHAP_SUPPORT */ + +#if EAP_SUPPORT + if (authtype & PPPAUTHTYPE_EAP) { + pcb->settings.refuse_eap = 0; + } else { + pcb->settings.refuse_eap = 1; + } +#endif /* EAP_SUPPORT */ + + if (user) { + pcb->settings.user = user; + } + + if (passwd) { + pcb->settings.passwd = passwd; + } +} + +#if PPP_NOTIFY_PHASE +void ppp_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb) { + pcb->notify_phase_cb = notify_phase_cb; + notify_phase_cb(pcb, pcb->phase, pcb->ctx_cb); +} +#endif /* PPP_NOTIFY_PHASE */ + +#if PPPOS_SUPPORT +int ppp_over_serial_create(ppp_pcb *pcb, sio_fd_t fd, ppp_link_status_cb_fn link_status_cb, void *ctx_cb) { + + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + if (link_status_cb == NULL) { + return PPPERR_PARAM; + } + + pcb->fd = fd; + pcb->link_status_cb = link_status_cb; + pcb->ctx_cb = ctx_cb; + return PPPERR_NONE; +} + +/* + * ppp_set_xaccm - set the extended transmit ACCM for the interface. + */ +void ppp_set_xaccm(ppp_pcb *pcb, ext_accm *accm) { + SMEMCPY(pcb->out_accm, accm, sizeof(ext_accm)); + PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: out_accm=%X %X %X %X\n", + pcb->num, + pcb->out_accm[0], + pcb->out_accm[1], + pcb->out_accm[2], + pcb->out_accm[3])); +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static void ppp_over_ethernet_link_status_cb(ppp_pcb *pcb, int state); + +int ppp_over_ethernet_create(ppp_pcb *pcb, struct netif *ethif, const char *service_name, const char *concentrator_name, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) { + + LWIP_UNUSED_ARG(service_name); + LWIP_UNUSED_ARG(concentrator_name); + + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + if (link_status_cb == NULL) { + return PPPERR_PARAM; + } + + pcb->link_status_cb = link_status_cb; + pcb->ctx_cb = ctx_cb; + + if (pppoe_create(ethif, pcb, ppp_over_ethernet_link_status_cb, &pcb->pppoe_sc) != ERR_OK) { + return PPPERR_OPEN; + } + + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT +static void ppp_over_l2tp_link_status_cb(ppp_pcb *pcb, int state); + +int ppp_over_l2tp_create(ppp_pcb *pcb, struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len, + ppp_link_status_cb_fn link_status_cb, void *ctx_cb) { + + /* PPP is single-threaded: without a callback, + * there is no way to know when the link is up. */ + if (link_status_cb == NULL) { + return PPPERR_PARAM; + } + + pcb->link_status_cb = link_status_cb; + pcb->ctx_cb = ctx_cb; + + if (pppol2tp_create(pcb, ppp_over_l2tp_link_status_cb, &pcb->l2tp_pcb, netif, ipaddr, port, secret, secret_len) != ERR_OK) { + return PPPERR_OPEN; + } + + return PPPERR_NONE; +} +#endif /* PPPOL2TP_SUPPORT */ + +/* + * Open a PPP connection. + * + * This can only be called if PPP is in the dead phase. + * + * Holdoff is the time to wait (in seconds) before initiating + * the connection. + */ +int ppp_open(ppp_pcb *pcb, u16_t holdoff) { + if (pcb->phase != PPP_PHASE_DEAD) { + return PPPERR_PARAM; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_open() called, holdoff=%d\n", holdoff)); + + if (holdoff == 0) { + ppp_do_open(pcb); + return PPPERR_NONE; + } + + new_phase(pcb, PPP_PHASE_HOLDOFF); + sys_timeout((u32_t)(holdoff*1000), ppp_do_open, pcb); + return PPPERR_NONE; +} + +/* + * Initiate the end of a PPP connection. + * Any outstanding packets in the queues are dropped. + * Return 0 on success, an error code on failure. + */ +int +ppp_close(ppp_pcb *pcb) +{ + int st = 0; + + pcb->err_code = PPPERR_USER; + + /* dead phase, nothing to do, call the status callback to be consistent */ + if (pcb->phase == PPP_PHASE_DEAD) { + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return PPPERR_NONE; + } + + /* holdoff phase, cancel the reconnection and call the status callback */ + if (pcb->phase == PPP_PHASE_HOLDOFF) { + sys_untimeout(ppp_do_open, pcb); + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return PPPERR_NONE; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_close() called\n")); + + /* Disconnect */ +#if PPPOE_SUPPORT + if (pcb->pppoe_sc) { + PPPDEBUG(LOG_DEBUG, ("ppp_close: unit %d kill_link -> ppp_stop\n", pcb->num)); + /* This will leave us at PPP_PHASE_DEAD. */ + ppp_stop(pcb); + } else +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + if (pcb->l2tp_pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_close: unit %d kill_link -> ppp_stop\n", pcb->num)); + /* This will leave us at PPP_PHASE_DEAD. */ + ppp_stop(pcb); + } else +#endif /* PPPOL2TP_SUPPORT */ + { +#if PPPOS_SUPPORT + PPPDEBUG(LOG_DEBUG, ("ppp_close: unit %d kill_link -> ppp_stop\n", pcb->num)); + /* This will leave us at PPP_PHASE_DEAD. */ + ppp_stop(pcb); +#endif /* PPPOS_SUPPORT */ + } + + return st; +} + +/* This function is called when carrier is lost on the PPP channel. */ +void +ppp_sighup(ppp_pcb *pcb) +{ + PPPDEBUG(LOG_DEBUG, ("ppp_sighup: unit %d sig_hup -> ppp_hup\n", pcb->num)); + ppp_hup(pcb); +} + +/* + * Free the control block, clean everything except the PPP PCB itself + * and the netif, it allows you to change the underlying PPP protocol + * (eg. from PPPoE to PPPoS to switch from DSL to GPRS) without losing + * your PPP and netif handlers. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +int ppp_free(ppp_pcb *pcb) { + if (pcb->phase != PPP_PHASE_DEAD) { + return PPPERR_PARAM; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_free: unit %d\n", pcb->num)); + +#if PPPOE_SUPPORT + if (pcb->pppoe_sc) { + pppoe_destroy(pcb->pppoe_sc); + pcb->pppoe_sc = NULL; + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT + if (pcb->l2tp_pcb) { + pppol2tp_destroy(pcb->l2tp_pcb); + pcb->l2tp_pcb = NULL; + } +#endif /* PPPOL2TP_SUPPORT */ + +#if PPPOS_SUPPORT + /* input pbuf left ? */ + ppp_free_current_input_packet(&pcb->rx); +#endif /* PPPOS_SUPPORT */ + + return 0; +} + +/* + * Release the control block. + * + * This can only be called if PPP is in the dead phase. + * + * You must use ppp_close() before if you wish to terminate + * an established PPP session. + * + * Return 0 on success, an error code on failure. + */ +int ppp_delete(ppp_pcb *pcb) { + int err; + + if (pcb->phase != PPP_PHASE_DEAD) { + return PPPERR_PARAM; + } + + PPPDEBUG(LOG_DEBUG, ("ppp_delete: unit %d\n", pcb->num)); + + netif_remove(&pcb->netif); + if( (err = ppp_free(pcb)) != PPPERR_NONE) { + return err; + } + + memp_free(MEMP_PPP_PCB, pcb); + return 0; +} + + + + +/************************************/ +/*** PRIVATE FUNCTION DEFINITIONS ***/ +/************************************/ + +/* Set a PPP PCB to its initial state */ +static void ppp_clear(ppp_pcb *pcb) { + const struct protent *protp; + int i; + + LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF); + +#if PPP_STATS_SUPPORT + link_stats_valid = 0; +#endif /* PPP_STATS_SUPPORT */ + + memset(&pcb->phase, 0, sizeof(ppp_pcb) - ( (char*)&((ppp_pcb*)0)->phase - (char*)0 ) ); + IP4_ADDR(&pcb->addrs.netmask, 255,255,255,255); + + /* + * Initialize each protocol. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + (*protp->init)(pcb); + } + + new_phase(pcb, PPP_PHASE_INITIALIZE); +} + +static void ppp_do_open(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF); + +#if PPPOE_SUPPORT + if (pcb->pppoe_sc) { + ppp_over_ethernet_open(pcb); + return; + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT + if (pcb->l2tp_pcb) { + ppp_over_l2tp_open(pcb); + return; + } +#endif /* PPPOL2TP_SUPPORT */ + +#if PPPOS_SUPPORT + ppp_over_serial_open(pcb); +#endif /* PPPOS_SUPPORT */ +} + +/** Initiate LCP open request */ +static void ppp_start(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_start: unit %d\n", pcb->num)); + lcp_open(pcb); /* Start protocol */ + lcp_lowerup(pcb); + PPPDEBUG(LOG_DEBUG, ("ppp_start: finished\n")); +} + +/** LCP close request */ +static void ppp_stop(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_stop: unit %d\n", pcb->num)); + lcp_close(pcb, "User request"); +} + +/** Called when carrier/link is lost */ +static void ppp_hup(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_hup: unit %d\n", pcb->num)); + lcp_lowerdown(pcb); + link_terminated(pcb); +} + +/* + * Pass the processed input packet to the appropriate handler. + * This function and all handlers run in the context of the tcpip_thread + */ +void ppp_input(ppp_pcb *pcb, struct pbuf *pb) { + u16_t protocol; + + protocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1]; + +#if PRINTPKT_SUPPORT + ppp_dump_packet("rcvd", (unsigned char *)pb->payload, pb->len); +#endif /* PRINTPKT_SUPPORT */ + + if(pbuf_header(pb, -(s16_t)sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + LINK_STATS_INC(link.recv); + snmp_inc_ifinucastpkts(&pcb->netif); + snmp_add_ifinoctets(&pcb->netif, pb->tot_len); + + /* + * Toss all non-LCP packets unless LCP is OPEN. + */ + if (protocol != PPP_LCP && pcb->lcp_fsm.state != PPP_FSM_OPENED) { + ppp_dbglog("Discarded non-LCP packet when LCP not open"); + goto drop; + } + + /* + * Until we get past the authentication phase, toss all packets + * except LCP, LQR and authentication packets. + */ + if (pcb->phase <= PPP_PHASE_AUTHENTICATE + && !(protocol == PPP_LCP +#if LQR_SUPPORT + || protocol == PPP_LQR +#endif /* LQR_SUPPORT */ +#if PAP_SUPPORT + || protocol == PPP_PAP +#endif /* PAP_SUPPORT */ +#if CHAP_SUPPORT + || protocol == PPP_CHAP +#endif /* CHAP_SUPPORT */ +#if EAP_SUPPORT + || protocol == PPP_EAP +#endif /* EAP_SUPPORT */ + )) { + ppp_dbglog("discarding proto 0x%x in phase %d", + protocol, pcb->phase); + goto drop; + } + + /* FIXME: should we write protent to do that ? */ + + switch(protocol) { + +#if VJ_SUPPORT + case PPP_VJC_COMP: /* VJ compressed TCP */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_comp in pbuf len=%d\n", pcb->num, pb->len)); + /* + * Clip off the VJ header and prepend the rebuilt TCP/IP header and + * pass the result to IP. + */ + if (vj_uncompress_tcp(&pb, &pcb->vj_comp) >= 0) { + ip_input(pb, &pcb->netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ compressed\n", pcb->num)); + break; + + case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: vj_un in pbuf len=%d\n", pcb->num, pb->len)); + /* + * Process the TCP/IP header for VJ header compression and then pass + * the packet to IP. + */ + if (vj_uncompress_uncomp(pb, &pcb->vj_comp) >= 0) { + ip_input(pb, &pcb->netif); + return; + } + /* Something's wrong so drop it. */ + PPPDEBUG(LOG_WARNING, ("ppp_input[%d]: Dropping VJ uncompressed\n", pcb->num)); + break; +#endif /* VJ_SUPPORT */ + + case PPP_IP: /* Internet Protocol */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip in pbuf len=%d\n", pcb->num, pb->len)); + ip_input(pb, &pcb->netif); + return; + +#if PPP_IPV6_SUPPORT + case PPP_IPV6: /* Internet Protocol Version 6 */ + PPPDEBUG(LOG_INFO, ("ppp_input[%d]: ip6 in pbuf len=%d\n", pcb->num, pb->len)); + ip6_input(pb, &pcb->netif); + return; +#endif /* PPP_IPV6_SUPPORT */ + + default: { + + int i; + const struct protent *protp; + /* + * Upcall the proper protocol input routine. + */ + for (i = 0; (protp = protocols[i]) != NULL; ++i) { + if (protp->protocol == protocol && protp->enabled_flag) { + pb = ppp_singlebuf(pb); + (*protp->input)(pcb, (u_char*)pb->payload, pb->len); + goto out; + } +#if 0 /* UNUSED + * + * This is actually a (hacked?) way for the PPP kernel implementation to pass a + * data packet to the PPP daemon. The PPP daemon normally only do signaling + * (LCP, PAP, CHAP, IPCP, ...) and does not handle any data packet at all. + * + * This is only used by CCP, which we cannot support until we have a CCP data + * implementation. + */ + if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag + && protp->datainput != NULL) { + (*protp->datainput)(pcb, pb->payload, pb->len); + goto out; + } +#endif /* UNUSED */ + } + +#if PPP_DEBUG +#if PPP_PROTOCOLNAME + const char *pname = protocol_name(protocol); + if (pname != NULL) { + ppp_warn("Unsupported protocol '%s' (0x%x) received", pname, protocol); + } else +#endif /* PPP_PROTOCOLNAME */ + ppp_warn("Unsupported protocol 0x%x received", protocol); +#endif /* PPP_DEBUG */ + if (pbuf_header(pb, (s16_t)sizeof(protocol))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + lcp_sprotrej(pcb, (u_char*)pb->payload, pb->len); + } + break; + } + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pcb->netif); + +out: + pbuf_free(pb); + magic_randomize(); + return; +} + +#if PPPOS_SUPPORT +#if PPP_FCS_TABLE +/* + * FCS lookup table as calculated by genfcstab. + */ +static const u_short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; +#else /* PPP_FCS_TABLE */ +/* The HDLC polynomial: X**0 + X**5 + X**12 + X**16 (0x8408) */ +#define PPP_FCS_POLYNOMIAL 0x8408 +u16_t ppp_get_fcs(u8_t byte) { + unsigned int octet; + int bit; + octet = byte; + for (bit = 8; bit-- > 0; ) { + octet = (octet & 0x01) ? ((octet >> 1) ^ PPP_FCS_POLYNOMIAL) : (octet >> 1); + } + return octet & 0xffff; +} +#endif /* PPP_FCS_TABLE */ + +/* PPP's Asynchronous-Control-Character-Map. The mask array is used + * to select the specific bit for a character. */ +static u_char ppp_accm_mask[] = { + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80 +}; +#endif /* PPPOS_SUPPORT */ + +/* + * ppp_netif_init_cb - netif init callback + */ +static err_t ppp_netif_init_cb(struct netif *netif) { + netif->name[0] = 'p'; + netif->name[1] = 'p'; + netif->output = ppp_netif_output_ip4; +#if PPP_IPV6_SUPPORT + netif->output_ip6 = ppp_netif_output_ip6; +#endif /* PPP_IPV6_SUPPORT */ + netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP; +#if LWIP_NETIF_HOSTNAME + /* @todo: Initialize interface hostname */ + /* netif_set_hostname(netif, "lwip"); */ +#endif /* LWIP_NETIF_HOSTNAME */ + return ERR_OK; +} + + +/**********************************/ +/*** LOCAL FUNCTION DEFINITIONS ***/ +/**********************************/ + +#if PPPOS_SUPPORT +static void ppp_over_serial_open(ppp_pcb *pcb) { + + /* input pbuf left over from last session? */ + ppp_free_current_input_packet(&pcb->rx); + + ppp_clear(pcb); + + pcb->rx.pcb = pcb; + pcb->rx.fd = pcb->fd; + +#if VJ_SUPPORT + vj_compress_init(&pcb->vj_comp); +#endif /* VJ_SUPPORT */ + + /* + * Default the in and out accm so that escape and flag characters + * are always escaped. + */ + pcb->rx.in_accm[15] = 0x60; /* no need to protect since RX is not running */ + pcb->out_accm[15] = 0x60; + + /* + * Start the connection and handle incoming events (packet or timeout). + */ + PPPDEBUG(LOG_INFO, ("ppp_over_serial_open: unit %d: connecting\n", pcb->num)); + ppp_start(pcb); +} + +static void +pppos_put(ppp_pcb *pcb, struct pbuf *nb) +{ + struct pbuf *b; + int c; + + for(b = nb; b != NULL; b = b->next) { + c = sio_write(pcb->fd, (u8_t*)b->payload, b->len); + if(c != b->len) { + PPPDEBUG(LOG_WARNING, + ("PPP pppos_put: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pcb->fd, b->len, c, c)); + LINK_STATS_INC(link.err); + pcb->last_xmit = 0; /* prepend PPP_FLAG to next packet */ + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(nb); + return; + } + } + + snmp_add_ifoutoctets(&pcb->netif, nb->tot_len); + snmp_inc_ifoutucastpkts(&pcb->netif); + pbuf_free(nb); + LINK_STATS_INC(link.xmit); +} + +/* + * ppp_append - append given character to end of given pbuf. If out_accm + * is not NULL and the character needs to be escaped, do so. + * If pbuf is full, append another. + * Return the current pbuf. + */ +static struct pbuf * +ppp_append(u_char c, struct pbuf *nb, ext_accm *out_accm) +{ + struct pbuf *tb = nb; + + /* Make sure there is room for the character and an escape code. + * Sure we don't quite fill the buffer if the character doesn't + * get escaped but is one character worth complicating this? */ + /* Note: We assume no packet header. */ + if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) { + tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (tb) { + nb->next = tb; + } else { + LINK_STATS_INC(link.memerr); + } + nb = tb; + } + + if (nb) { + if (out_accm && ESCAPE_P(*out_accm, c)) { + *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE; + *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS; + } else { + *((u_char*)nb->payload + nb->len++) = c; + } + } + + return tb; +} +#endif /* PPPOS_SUPPORT */ + + +/* Send a IPv4 packet on the given connection. + */ +static err_t ppp_netif_output_ip4(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); + return ppp_netif_output(netif, pb, PPP_IP); +} + +#if PPP_IPV6_SUPPORT +/* Send a IPv6 packet on the given connection. + */ +static err_t ppp_netif_output_ip6(struct netif *netif, struct pbuf *pb, ip6_addr_t *ipaddr) { + LWIP_UNUSED_ARG(ipaddr); + return ppp_netif_output(netif, pb, PPP_IPV6); +} +#endif /* PPP_IPV6_SUPPORT */ + +/* Send a packet on the given connection. + * + * This is the low level function that send the PPP packet, + * only for IPv4 and IPv6 packets coming from lwIP. + */ +static err_t ppp_netif_output(struct netif *netif, struct pbuf *pb, u_short protocol) { + ppp_pcb *pcb = (ppp_pcb*)netif->state; + + /* Validate parameters. */ + /* We let any protocol value go through - it can't hurt us + * and the peer will just drop it if it's not accepting it. */ + if (!pcb || !pb) { + PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad params prot=%d pb=%p\n", + pcb->num, PPP_IP, (void*)pb)); + LINK_STATS_INC(link.opterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_ARG; + } + + /* Check that the link is up. */ + if (!pcb->if_up) { + PPPDEBUG(LOG_ERR, ("ppp_netif_output[%d]: link not up\n", pcb->num)); + LINK_STATS_INC(link.rterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(netif); + return ERR_RTE; + } + +#if PPPOE_SUPPORT + if(pcb->pppoe_sc) { + return ppp_netif_output_over_ethernet(pcb, pb, protocol); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT + if(pcb->l2tp_pcb) { + return ppp_netif_output_over_l2tp(pcb, pb, protocol); + } +#endif /* PPPOL2TP_SUPPORT */ + +#if PPPOS_SUPPORT + return ppp_netif_output_over_serial(pcb, pb, protocol); +#endif /* PPPOS_SUPPORT */ + +#if !PPPOS_SUPPORT + return ERR_OK; +#endif +} + +#if PPPOS_SUPPORT +static err_t ppp_netif_output_over_serial(ppp_pcb *pcb, struct pbuf *pb, u_short protocol) { + u_int fcs_out = PPP_INITFCS; + struct pbuf *head = NULL, *tail = NULL, *p; + u_char c; + + /* Grab an output buffer. */ + head = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (head == NULL) { + PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: first alloc fail\n", pcb->num)); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(&pcb->netif); + return ERR_MEM; + } + +#if VJ_SUPPORT + /* + * Attempt Van Jacobson header compression if VJ is configured and + * this is an IP packet. + */ + if (protocol == PPP_IP && pcb->vj_enabled) { + switch (vj_compress_tcp(&pcb->vj_comp, pb)) { + case TYPE_IP: + /* No change... + protocol = PPP_IP_PROTOCOL; */ + break; + case TYPE_COMPRESSED_TCP: + protocol = PPP_VJC_COMP; + break; + case TYPE_UNCOMPRESSED_TCP: + protocol = PPP_VJC_UNCOMP; + break; + default: + PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad IP packet\n", pcb->num)); + LINK_STATS_INC(link.proterr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(head); + return ERR_VAL; + } + } +#endif /* VJ_SUPPORT */ + + tail = head; + + /* Build the PPP header. */ + if ((sys_jiffies() - pcb->last_xmit) >= PPP_MAXIDLEFLAG) { + tail = ppp_append(PPP_FLAG, tail, NULL); + } + + pcb->last_xmit = sys_jiffies(); + if (!pcb->accomp) { + fcs_out = PPP_FCS(fcs_out, PPP_ALLSTATIONS); + tail = ppp_append(PPP_ALLSTATIONS, tail, &pcb->out_accm); + fcs_out = PPP_FCS(fcs_out, PPP_UI); + tail = ppp_append(PPP_UI, tail, &pcb->out_accm); + } + if (!pcb->pcomp || protocol > 0xFF) { + c = (protocol >> 8) & 0xFF; + fcs_out = PPP_FCS(fcs_out, c); + tail = ppp_append(c, tail, &pcb->out_accm); + } + c = protocol & 0xFF; + fcs_out = PPP_FCS(fcs_out, c); + tail = ppp_append(c, tail, &pcb->out_accm); + + /* Load packet. */ + for(p = pb; p; p = p->next) { + int n; + u_char *sPtr; + + sPtr = (u_char*)p->payload; + n = p->len; + while (n-- > 0) { + c = *sPtr++; + + /* Update FCS before checking for special characters. */ + fcs_out = PPP_FCS(fcs_out, c); + + /* Copy to output buffer escaping special characters. */ + tail = ppp_append(c, tail, &pcb->out_accm); + } + } + + /* Add FCS and trailing flag. */ + c = ~fcs_out & 0xFF; + tail = ppp_append(c, tail, &pcb->out_accm); + c = (~fcs_out >> 8) & 0xFF; + tail = ppp_append(c, tail, &pcb->out_accm); + tail = ppp_append(PPP_FLAG, tail, NULL); + + /* If we failed to complete the packet, throw it away. */ + if (!tail) { + PPPDEBUG(LOG_WARNING, + ("ppp_netif_output[%d]: Alloc err - dropping proto=%d\n", + pcb->num, protocol)); + pbuf_free(head); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.drop); + snmp_inc_ifoutdiscards(&pcb->netif); + return ERR_MEM; + } + + /* Send it. */ + PPPDEBUG(LOG_INFO, ("ppp_netif_output[%d]: proto=0x%"X16_F"\n", pcb->num, protocol)); + + pppos_put(pcb, head); + return ERR_OK; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static err_t ppp_netif_output_over_ethernet(ppp_pcb *pcb, struct pbuf *p, u_short protocol) { + struct pbuf *pb; + int i=0; +#if LWIP_SNMP + u16_t tot_len; +#endif /* LWIP_SNMP */ + err_t err; + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_LINK, PPPOE_HEADERLEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOE_HEADERLEN); + + pcb->last_xmit = sys_jiffies(); + + if (!pcb->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); +#if LWIP_SNMP + tot_len = pb->tot_len; +#endif /* LWIP_SNMP */ + + if( (err = pppoe_xmit(pcb->pppoe_sc, pb)) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pcb->netif); + return err; + } + + snmp_add_ifoutoctets(&pcb->netif, tot_len); + snmp_inc_ifoutucastpkts(&pcb->netif); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOE_SUPPORT */ + + +#if PPPOL2TP_SUPPORT +static err_t ppp_netif_output_over_l2tp(ppp_pcb *pcb, struct pbuf *p, u_short protocol) { + struct pbuf *pb; + int i=0; +#if LWIP_SNMP + u16_t tot_len; +#endif /* LWIP_SNMP */ + err_t err; + + /* @todo: try to use pbuf_header() here! */ + pb = pbuf_alloc(PBUF_TRANSPORT, PPPOL2TP_OUTPUT_DATA_HEADER_LEN + sizeof(protocol), PBUF_RAM); + if(!pb) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + return ERR_MEM; + } + + pbuf_header(pb, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN); + + pcb->last_xmit = sys_jiffies(); + + if (!pcb->pcomp || protocol > 0xFF) { + *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF; + } + *((u_char*)pb->payload + i) = protocol & 0xFF; + + pbuf_chain(pb, p); +#if LWIP_SNMP + tot_len = pb->tot_len; +#endif /* LWIP_SNMP */ + + if( (err = pppol2tp_xmit(pcb->l2tp_pcb, pb)) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pcb->netif); + return err; + } + + snmp_add_ifoutoctets(&pcb->netif, tot_len); + snmp_inc_ifoutucastpkts(&pcb->netif); + LINK_STATS_INC(link.xmit); + return ERR_OK; +} +#endif /* PPPOL2TP_SUPPORT */ + + +/* Get and set parameters for the given connection. + * Return 0 on success, an error code on failure. */ +int +ppp_ioctl(ppp_pcb *pcb, int cmd, void *arg) +{ + if(NULL == pcb) + return PPPERR_PARAM; + + switch(cmd) { + case PPPCTLG_UPSTATUS: /* Get the PPP up status. */ + if (arg) { + *(int *)arg = (int)(pcb->if_up); + return PPPERR_NONE; + } + return PPPERR_PARAM; + break; + + case PPPCTLS_ERRCODE: /* Set the PPP error code. */ + if (arg) { + pcb->err_code = (u8_t)(*(int *)arg); + return PPPERR_NONE; + } + return PPPERR_PARAM; + break; + + case PPPCTLG_ERRCODE: /* Get the PPP error code. */ + if (arg) { + *(int *)arg = (int)(pcb->err_code); + return PPPERR_NONE; + } + return PPPERR_PARAM; + break; + +#if PPPOS_SUPPORT + case PPPCTLG_FD: /* Get the fd associated with the ppp */ + if (arg) { + *(sio_fd_t *)arg = pcb->fd; + return PPPERR_NONE; + } + return PPPERR_PARAM; + break; +#endif /* PPPOS_SUPPORT */ + + default: + break; + } + + return PPPERR_PARAM; +} + +/* + * Write a pbuf to a ppp link, only used from PPP functions + * to send PPP packets. + * + * IPv4 and IPv6 packets from lwIP are sent, respectively, + * with ppp_netif_output_ip4() and ppp_netif_output_ip6() + * functions (which are callbacks of the netif PPP interface). + * + * RETURN: >= 0 Number of characters written + * -1 Failed to write to device + */ +int ppp_write(ppp_pcb *pcb, struct pbuf *p) { +#if PRINTPKT_SUPPORT + ppp_dump_packet("sent", (unsigned char *)p->payload+2, p->len-2); +#endif /* PRINTPKT_SUPPORT */ + +#if PPPOE_SUPPORT + if(pcb->pppoe_sc) { + return ppp_write_over_ethernet(pcb, p); + } +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT + if(pcb->l2tp_pcb) { + return ppp_write_over_l2tp(pcb, p); + } +#endif /* PPPOL2TP_SUPPORT */ + +#if PPPOS_SUPPORT + return ppp_write_over_serial(pcb, p); +#endif /* PPPOS_SUPPORT */ + +#if !PPPOS_SUPPORT + pbuf_free(p); + return PPPERR_NONE; +#endif +} + +#if PPPOS_SUPPORT +static int ppp_write_over_serial(ppp_pcb *pcb, struct pbuf *p) { + u_char *s = (u_char*)p->payload; + int n = p->len; + u_char c; + u_int fcs_out; + struct pbuf *head, *tail; + + head = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL); + if (head == NULL) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(p); + return PPPERR_ALLOC; + } + + tail = head; + + /* If the link has been idle, we'll send a fresh flag character to + * flush any noise. */ + if ((sys_jiffies() - pcb->last_xmit) >= PPP_MAXIDLEFLAG) { + tail = ppp_append(PPP_FLAG, tail, NULL); + } + pcb->last_xmit = sys_jiffies(); + + fcs_out = PPP_INITFCS; + /* Load output buffer. */ + while (n-- > 0) { + c = *s++; + + /* Update FCS before checking for special characters. */ + fcs_out = PPP_FCS(fcs_out, c); + + /* Copy to output buffer escaping special characters. */ + tail = ppp_append(c, tail, &pcb->out_accm); + } + + /* Add FCS and trailing flag. */ + c = ~fcs_out & 0xFF; + tail = ppp_append(c, tail, &pcb->out_accm); + c = (~fcs_out >> 8) & 0xFF; + tail = ppp_append(c, tail, &pcb->out_accm); + tail = ppp_append(PPP_FLAG, tail, NULL); + + /* If we failed to complete the packet, throw it away. + * Otherwise send it. */ + if (!tail) { + PPPDEBUG(LOG_WARNING, + ("ppp_write[%d]: Alloc err - dropping pbuf len=%d\n", pcb->num, head->len)); + /*"ppp_write[%d]: Alloc err - dropping %d:%.*H", pd, head->len, LWIP_MIN(head->len * 2, 40), head->payload)); */ + pbuf_free(head); + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(p); + return PPPERR_ALLOC; + } + + PPPDEBUG(LOG_INFO, ("ppp_write[%d]: len=%d\n", pcb->num, head->len)); + /* "ppp_write[%d]: %d:%.*H", pd, head->len, LWIP_MIN(head->len * 2, 40), head->payload)); */ + pppos_put(pcb, head); + pbuf_free(p); + return PPPERR_NONE; +} +#endif /* PPPOS_SUPPORT */ + +#if PPPOE_SUPPORT +static int ppp_write_over_ethernet(ppp_pcb *pcb, struct pbuf *p) { + struct pbuf *ph; /* Ethernet + PPPoE header */ +#if LWIP_SNMP + u16_t tot_len; +#endif /* LWIP_SNMP */ + + /* skip address & flags */ + pbuf_header(p, -(s16_t)2); + + ph = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM); + if(!ph) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(p); + return PPPERR_ALLOC; + } + + pbuf_header(ph, -(s16_t)PPPOE_HEADERLEN); /* hide PPPoE header */ + pbuf_cat(ph, p); +#if LWIP_SNMP + tot_len = ph->tot_len; +#endif /* LWIP_SNMP */ + + pcb->last_xmit = sys_jiffies(); + + if(pppoe_xmit(pcb->pppoe_sc, ph) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pcb->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pcb->netif, (u16_t)tot_len); + snmp_inc_ifoutucastpkts(&pcb->netif); + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT +static int ppp_write_over_l2tp(ppp_pcb *pcb, struct pbuf *p) { + struct pbuf *ph; /* UDP + L2TP header */ +#if LWIP_SNMP + u16_t tot_len; +#endif /* LWIP_SNMP */ + + ph = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(PPPOL2TP_OUTPUT_DATA_HEADER_LEN), PBUF_RAM); + if(!ph) { + LINK_STATS_INC(link.memerr); + LINK_STATS_INC(link.proterr); + snmp_inc_ifoutdiscards(&pcb->netif); + pbuf_free(p); + return PPPERR_ALLOC; + } + + pbuf_header(ph, -(s16_t)PPPOL2TP_OUTPUT_DATA_HEADER_LEN); /* hide L2TP header */ + pbuf_cat(ph, p); +#if LWIP_SNMP + tot_len = ph->tot_len; +#endif /* LWIP_SNMP */ + + pcb->last_xmit = sys_jiffies(); + + if(pppol2tp_xmit(pcb->l2tp_pcb, ph) != ERR_OK) { + LINK_STATS_INC(link.err); + snmp_inc_ifoutdiscards(&pcb->netif); + return PPPERR_DEVICE; + } + + snmp_add_ifoutoctets(&pcb->netif, (u16_t)tot_len); + snmp_inc_ifoutucastpkts(&pcb->netif); + LINK_STATS_INC(link.xmit); + return PPPERR_NONE; +} +#endif /* PPPOL2TP_SUPPORT */ + +#if PPPOS_SUPPORT +/* + * Drop the input packet. + */ +static void +ppp_free_current_input_packet(ppp_pcb_rx *pcrx) +{ + if (pcrx->in_head != NULL) { + if (pcrx->in_tail && (pcrx->in_tail != pcrx->in_head)) { + pbuf_free(pcrx->in_tail); + } + pbuf_free(pcrx->in_head); + pcrx->in_head = NULL; + } + pcrx->in_tail = NULL; +} + +/* + * Drop the input packet and increase error counters. + */ +static void +ppp_drop(ppp_pcb_rx *pcrx) +{ +#if LWIP_SNMP || VJ_SUPPORT + ppp_pcb *pcb = (ppp_pcb*)pcrx->pcb; +#endif /* LWIP_SNMP || VJ_SUPPORT */ + if (pcrx->in_head != NULL) { +#if 0 + PPPDEBUG(LOG_INFO, ("ppp_drop: %d:%.*H\n", pcrx->in_head->len, min(60, pcrx->in_head->len * 2), pcrx->in_head->payload)); +#endif + PPPDEBUG(LOG_INFO, ("ppp_drop: pbuf len=%d, addr %p\n", pcrx->in_head->len, (void*)pcrx->in_head)); + } + ppp_free_current_input_packet(pcrx); +#if VJ_SUPPORT + vj_uncompress_err(&pcb->vj_comp); +#endif /* VJ_SUPPORT */ + + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pcb->netif); +} + +/** PPPoS input helper struct, must be packed since it is stored + * to pbuf->payload, which might be unaligned. */ +#if PPP_INPROC_MULTITHREADED +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/bpstruct.h" +#endif +PACK_STRUCT_BEGIN +struct pppos_input_header { + PACK_STRUCT_FIELD(ppp_pcb *pcb); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END +#ifdef PACK_STRUCT_USE_INCLUDES +# include "arch/epstruct.h" +#endif +#endif /* PPP_INPROC_MULTITHREADED */ + +/** Pass received raw characters to PPPoS to be decoded. This function is + * thread-safe and can be called from a dedicated RX-thread or from a main-loop. + * + * @param pcb PPP descriptor index, returned by ppp_new() + * @param data received data + * @param len length of received data + */ +void +pppos_input(ppp_pcb *pcb, u_char *s, int l) +{ + ppp_pcb_rx *pcrx = &pcb->rx; + struct pbuf *next_pbuf; + u_char cur_char; + u_char escaped; + SYS_ARCH_DECL_PROTECT(lev); + + PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", pcb->num, l)); + while (l-- > 0) { + cur_char = *s++; + + SYS_ARCH_PROTECT(lev); + escaped = ESCAPE_P(pcrx->in_accm, cur_char); + SYS_ARCH_UNPROTECT(lev); + /* Handle special characters. */ + if (escaped) { + /* Check for escape sequences. */ + /* XXX Note that this does not handle an escaped 0x5d character which + * would appear as an escape character. Since this is an ASCII ']' + * and there is no reason that I know of to escape it, I won't complicate + * the code to handle this case. GLL */ + if (cur_char == PPP_ESCAPE) { + pcrx->in_escaped = 1; + /* Check for the flag character. */ + } else if (cur_char == PPP_FLAG) { + /* If this is just an extra flag character, ignore it. */ + if (pcrx->in_state <= PDADDRESS) { + /* ignore it */; + /* If we haven't received the packet header, drop what has come in. */ + } else if (pcrx->in_state < PDDATA) { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Dropping incomplete packet %d\n", + pcb->num, pcrx->in_state)); + LINK_STATS_INC(link.lenerr); + ppp_drop(pcrx); + /* If the fcs is invalid, drop the packet. */ + } else if (pcrx->in_fcs != PPP_GOODFCS) { + PPPDEBUG(LOG_INFO, + ("pppos_input[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n", + pcb->num, pcrx->in_fcs, pcrx->in_protocol)); + /* Note: If you get lots of these, check for UART frame errors or try different baud rate */ + LINK_STATS_INC(link.chkerr); + ppp_drop(pcrx); + /* Otherwise it's a good packet so pass it on. */ + } else { + struct pbuf *inp; + /* Trim off the checksum. */ + if(pcrx->in_tail->len > 2) { + pcrx->in_tail->len -= 2; + + pcrx->in_tail->tot_len = pcrx->in_tail->len; + if (pcrx->in_tail != pcrx->in_head) { + pbuf_cat(pcrx->in_head, pcrx->in_tail); + } + } else { + pcrx->in_tail->tot_len = pcrx->in_tail->len; + if (pcrx->in_tail != pcrx->in_head) { + pbuf_cat(pcrx->in_head, pcrx->in_tail); + } + + pbuf_realloc(pcrx->in_head, pcrx->in_head->tot_len - 2); + } + + /* Dispatch the packet thereby consuming it. */ + inp = pcrx->in_head; + /* Packet consumed, release our references. */ + pcrx->in_head = NULL; + pcrx->in_tail = NULL; +#if IP_FORWARD || LWIP_IPV6_FORWARD + /* hide the room for Ethernet forwarding header */ + pbuf_header(inp, -(s16_t)PBUF_LINK_HLEN); +#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */ +#if PPP_INPROC_MULTITHREADED + if(tcpip_callback_with_block(pppos_input_callback, inp, 0) != ERR_OK) { + PPPDEBUG(LOG_ERR, ("pppos_input[%d]: tcpip_callback() failed, dropping packet\n", pcb->num)); + pbuf_free(inp); + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pcb->netif); + } +#else /* PPP_INPROC_MULTITHREADED */ + ppp_input(pcb, inp); +#endif /* PPP_INPROC_MULTITHREADED */ + } + + /* Prepare for a new packet. */ + pcrx->in_fcs = PPP_INITFCS; + pcrx->in_state = PDADDRESS; + pcrx->in_escaped = 0; + /* Other characters are usually control characters that may have + * been inserted by the physical layer so here we just drop them. */ + } else { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Dropping ACCM char <%d>\n", pcb->num, cur_char)); + } + /* Process other characters. */ + } else { + /* Unencode escaped characters. */ + if (pcrx->in_escaped) { + pcrx->in_escaped = 0; + cur_char ^= PPP_TRANS; + } + + /* Process character relative to current state. */ + switch(pcrx->in_state) { + case PDIDLE: /* Idle state - waiting. */ + /* Drop the character if it's not 0xff + * we would have processed a flag character above. */ + if (cur_char != PPP_ALLSTATIONS) { + break; + } + /* no break */ + /* Fall through */ + + case PDSTART: /* Process start flag. */ + /* Prepare for a new packet. */ + pcrx->in_fcs = PPP_INITFCS; + /* no break */ + /* Fall through */ + + case PDADDRESS: /* Process address field. */ + if (cur_char == PPP_ALLSTATIONS) { + pcrx->in_state = PDCONTROL; + break; + } + /* no break */ + + /* Else assume compressed address and control fields so + * fall through to get the protocol... */ + case PDCONTROL: /* Process control field. */ + /* If we don't get a valid control code, restart. */ + if (cur_char == PPP_UI) { + pcrx->in_state = PDPROTOCOL1; + break; + } + /* no break */ + +#if 0 + else { + PPPDEBUG(LOG_WARNING, + ("pppos_input[%d]: Invalid control <%d>\n", pcb->num, cur_char)); + pcrx->in_state = PDSTART; + } +#endif + case PDPROTOCOL1: /* Process protocol field 1. */ + /* If the lower bit is set, this is the end of the protocol + * field. */ + if (cur_char & 1) { + pcrx->in_protocol = cur_char; + pcrx->in_state = PDDATA; + } else { + pcrx->in_protocol = (u_int)cur_char << 8; + pcrx->in_state = PDPROTOCOL2; + } + break; + case PDPROTOCOL2: /* Process protocol field 2. */ + pcrx->in_protocol |= cur_char; + pcrx->in_state = PDDATA; + break; + case PDDATA: /* Process data byte. */ + /* Make space to receive processed data. */ + if (pcrx->in_tail == NULL || pcrx->in_tail->len == PBUF_POOL_BUFSIZE) { + u16_t pbuf_alloc_len; + if (pcrx->in_tail != NULL) { + pcrx->in_tail->tot_len = pcrx->in_tail->len; + if (pcrx->in_tail != pcrx->in_head) { + pbuf_cat(pcrx->in_head, pcrx->in_tail); + /* give up the in_tail reference now */ + pcrx->in_tail = NULL; + } + } + /* If we haven't started a packet, we need a packet header. */ + pbuf_alloc_len = 0; +#if IP_FORWARD || LWIP_IPV6_FORWARD + /* If IP forwarding is enabled we are reserving PBUF_LINK_HLEN bytes so + * the packet is being allocated with enough header space to be + * forwarded (to Ethernet for example). + */ + if (pcrx->in_head == NULL) { + pbuf_alloc_len = PBUF_LINK_HLEN; + } +#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */ + next_pbuf = pbuf_alloc(PBUF_RAW, pbuf_alloc_len, PBUF_POOL); + if (next_pbuf == NULL) { + /* No free buffers. Drop the input packet and let the + * higher layers deal with it. Continue processing + * the received pbuf chain in case a new packet starts. */ + PPPDEBUG(LOG_ERR, ("pppos_input[%d]: NO FREE PBUFS!\n", pcb->num)); + LINK_STATS_INC(link.memerr); + ppp_drop(pcrx); + pcrx->in_state = PDSTART; /* Wait for flag sequence. */ + break; + } + if (pcrx->in_head == NULL) { + u8_t *payload = ((u8_t*)next_pbuf->payload) + pbuf_alloc_len; +#if PPP_INPROC_MULTITHREADED + ((struct pppos_input_header*)payload)->pcb = pcb; + payload += sizeof(struct pppos_input_header); + next_pbuf->len += sizeof(struct pppos_input_header); +#endif /* PPP_INPROC_MULTITHREADED */ + next_pbuf->len += sizeof(pcrx->in_protocol); + *(payload++) = pcrx->in_protocol >> 8; + *(payload) = pcrx->in_protocol & 0xFF; + pcrx->in_head = next_pbuf; + } + pcrx->in_tail = next_pbuf; + } + /* Load character into buffer. */ + ((u_char*)pcrx->in_tail->payload)[pcrx->in_tail->len++] = cur_char; + break; + default: + break; + } + + /* update the frame check sequence number. */ + pcrx->in_fcs = PPP_FCS(pcrx->in_fcs, cur_char); + } + } /* while (l-- > 0), all bytes processed */ + + magic_randomize(); +} + +#if PPP_INPROC_MULTITHREADED +/* PPPoS input callback using one input pointer + */ +static void pppos_input_callback(void *arg) { + struct pbuf *pb = (struct pbuf*)arg; + ppp_pcb *pcb; + + pcb = ((struct pppos_input_header*)pb->payload)->pcb; + if(pbuf_header(pb, -(s16_t)sizeof(struct pppos_input_header))) { + LWIP_ASSERT("pbuf_header failed\n", 0); + goto drop; + } + + /* Dispatch the packet thereby consuming it. */ + ppp_input(pcb, pb); + return; + +drop: + LINK_STATS_INC(link.drop); + snmp_inc_ifindiscards(&pcb->netif); + pbuf_free(pb); +} +#endif /* PPP_INPROC_MULTITHREADED */ +#endif /* PPPOS_SUPPORT */ + +/* merge a pbuf chain into one pbuf */ +struct pbuf * ppp_singlebuf(struct pbuf *p) { + struct pbuf *q, *b; + u_char *pl; + + if(p->tot_len == p->len) { + return p; + } + + q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if(!q) { + PPPDEBUG(LOG_ERR, + ("ppp_singlebuf: unable to alloc new buf (%d)\n", p->tot_len)); + return p; /* live dangerously */ + } + + for(b = p, pl = (u_char*)q->payload; b != NULL; b = b->next) { + MEMCPY(pl, b->payload, b->len); + pl += b->len; + } + + pbuf_free(p); + + return q; +} + +#if PPPOE_SUPPORT +static void ppp_over_ethernet_link_status_cb(ppp_pcb *pcb, int state) { + int pppoe_err_code = PPPERR_NONE; + + switch(state) { + + /* PPPoE link is established, starting PPP negotiation */ + case PPPOE_CB_STATE_UP: + PPPDEBUG(LOG_INFO, ("ppp_over_ethernet_link_status_cb: unit %d: UP, connecting\n", pcb->num)); + ppp_start(pcb); + return; + + /* PPPoE link normally down (i.e. asked to do so) */ + case PPPOE_CB_STATE_DOWN: + PPPDEBUG(LOG_INFO, ("ppp_over_ethernet_link_status_cb: unit %d: DOWN, disconnected\n", pcb->num)); + pppoe_err_code = PPPERR_CONNECT; + break; + + /* PPPoE link failed to setup (i.e. PADI/PADO timeout) */ + case PPPOE_CB_STATE_FAILED: + PPPDEBUG(LOG_INFO, ("ppp_over_ethernet_link_status_cb: unit %d: FAILED, aborting\n", pcb->num)); + pppoe_err_code = PPPERR_OPEN; + new_phase(pcb, PPP_PHASE_DEAD); + break; + + default: + break; + } + + pcb->link_status_cb(pcb, pcb->err_code ? pcb->err_code : pppoe_err_code, pcb->ctx_cb); +} + +static void ppp_over_ethernet_open(ppp_pcb *pcb) { + + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + ppp_clear(pcb); + + wo->mru = pcb->pppoe_sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */ + wo->neg_asyncmap = 0; + wo->neg_pcompression = 0; + wo->neg_accompression = 0; + + ao->mru = pcb->pppoe_sc->sc_ethif->mtu-PPPOE_HEADERLEN-2; /* two byte PPP protocol discriminator, then IP data */ + ao->neg_asyncmap = 0; + ao->neg_pcompression = 0; + ao->neg_accompression = 0; + + pppoe_connect(pcb->pppoe_sc); +} +#endif /* PPPOE_SUPPORT */ + +#if PPPOL2TP_SUPPORT +static void ppp_over_l2tp_link_status_cb(ppp_pcb *pcb, int state) { + int pppol2tp_err_code = PPPERR_NONE; + + switch(state) { + + /* PPPoL2TP link is established, starting PPP negotiation */ + case PPPOL2TP_CB_STATE_UP: + PPPDEBUG(LOG_INFO, ("ppp_over_l2tp_link_status_cb: unit %d: UP, connecting\n", pcb->num)); + ppp_start(pcb); + return; + + /* PPPoL2TP link normally down (i.e. asked to do so) */ + case PPPOL2TP_CB_STATE_DOWN: + PPPDEBUG(LOG_INFO, ("ppp_over_l2tp_link_status_cb: unit %d: DOWN, disconnected\n", pcb->num)); + pppol2tp_err_code = PPPERR_CONNECT; + break; + + /* PPPoL2TP link failed to setup (i.e. L2TP timeout) */ + case PPPOL2TP_CB_STATE_FAILED: + PPPDEBUG(LOG_INFO, ("ppp_over_l2tp_link_status_cb: unit %d: FAILED, aborting\n", pcb->num)); + pppol2tp_err_code = PPPERR_OPEN; + new_phase(pcb, PPP_PHASE_DEAD); + break; + + default: + break; + } + + pcb->link_status_cb(pcb, pcb->err_code ? pcb->err_code : pppol2tp_err_code, pcb->ctx_cb); +} + +static void ppp_over_l2tp_open(ppp_pcb *pcb) { + + lcp_options *wo = &pcb->lcp_wantoptions; + lcp_options *ao = &pcb->lcp_allowoptions; + + ppp_clear(pcb); + + wo->mru = 1500; /* FIXME: MTU depends if we support IP fragmentation or not */ + wo->neg_asyncmap = 0; + wo->neg_pcompression = 0; + wo->neg_accompression = 0; + + ao->mru = 1500; /* FIXME: MTU depends if we support IP fragmentation or not */ + ao->neg_asyncmap = 0; + ao->neg_pcompression = 0; + ao->neg_accompression = 0; + + pppol2tp_connect(pcb->l2tp_pcb); +} +#endif /* PPPOL2TP_SUPPORT */ + +void ppp_link_down(ppp_pcb *pcb) { + LWIP_UNUSED_ARG(pcb); /* necessary if PPPDEBUG is defined to an empty function */ + PPPDEBUG(LOG_DEBUG, ("ppp_link_down: unit %d\n", pcb->num)); +} + +void ppp_link_terminated(ppp_pcb *pcb) { + PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated: unit %d\n", pcb->num)); + +#if PPPOE_SUPPORT + if (pcb->pppoe_sc) { + pppoe_disconnect(pcb->pppoe_sc); + } else +#endif /* PPPOE_SUPPORT */ +#if PPPOL2TP_SUPPORT + if (pcb->l2tp_pcb) { + pppol2tp_disconnect(pcb->l2tp_pcb); + } else +#endif /* PPPOL2TP_SUPPORT */ + { +#if PPPOS_SUPPORT + /* We cannot call ppp_free_current_input_packet() here because + * rx thread might still call pppos_input() + */ + PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated: unit %d: err_code=%d\n", pcb->num, pcb->err_code)); + pcb->link_status_cb(pcb, pcb->err_code ? pcb->err_code : PPPERR_PROTOCOL, pcb->ctx_cb); +#endif /* PPPOS_SUPPORT */ + } + PPPDEBUG(LOG_DEBUG, ("ppp_link_terminated: finished.\n")); +} + +#if LWIP_NETIF_STATUS_CALLBACK +/** Set the status callback of a PPP's netif + * + * @param pcb The PPP descriptor returned by ppp_new() + * @param status_callback pointer to the status callback function + * + * @see netif_set_status_callback + */ +void +ppp_set_netif_statuscallback(ppp_pcb *pcb, netif_status_callback_fn status_callback) +{ + netif_set_status_callback(&pcb->netif, status_callback); +} +#endif /* LWIP_NETIF_STATUS_CALLBACK */ + +#if LWIP_NETIF_LINK_CALLBACK +/** Set the link callback of a PPP's netif + * + * @param pcb The PPP descriptor returned by ppp_new() + * @param link_callback pointer to the link callback function + * + * @see netif_set_link_callback + */ +void +ppp_set_netif_linkcallback(ppp_pcb *pcb, netif_status_callback_fn link_callback) +{ + netif_set_link_callback(&pcb->netif, link_callback); +} +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +/************************************************************************ + * Functions called by various PPP subsystems to configure + * the PPP interface or change the PPP phase. + */ + +/* + * new_phase - signal the start of a new phase of pppd's operation. + */ +void new_phase(ppp_pcb *pcb, int p) { + pcb->phase = p; + PPPDEBUG(LOG_DEBUG, ("ppp phase changed: unit %d: phase=%d\n", pcb->num, pcb->phase)); +#if PPP_NOTIFY_PHASE + if(NULL != pcb->notify_phase_cb) { + pcb->notify_phase_cb(pcb, p, pcb->ctx_cb); + } +#endif /* PPP_NOTIFY_PHASE */ +} + +/* + * ppp_send_config - configure the transmit-side characteristics of + * the ppp interface. + */ +int ppp_send_config(ppp_pcb *pcb, int mtu, u32_t accm, int pcomp, int accomp) { +#if PPPOS_SUPPORT + int i; +#endif /* PPPOS_SUPPORT */ + LWIP_UNUSED_ARG(mtu); + + /* pcb->mtu = mtu; -- set correctly with netif_set_mtu */ + pcb->pcomp = pcomp; + pcb->accomp = accomp; + +#if PPPOS_SUPPORT + /* Load the ACCM bits for the 32 control codes. */ + for (i = 0; i < 32/8; i++) { + pcb->out_accm[i] = (u_char)((accm >> (8 * i)) & 0xFF); + } +#else + LWIP_UNUSED_ARG(accm); +#endif /* PPPOS_SUPPORT */ + +#if PPPOS_SUPPORT + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: out_accm=%X %X %X %X\n", + pcb->num, + pcb->out_accm[0], pcb->out_accm[1], pcb->out_accm[2], pcb->out_accm[3])); +#else + PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]\n", pcb->num) ); +#endif /* PPPOS_SUPPORT */ + return 0; +} + +/* + * ppp_recv_config - configure the receive-side characteristics of + * the ppp interface. + */ +int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp) { +#if PPPOS_SUPPORT + int i; + SYS_ARCH_DECL_PROTECT(lev); +#endif /* PPPOS_SUPPORT */ + + LWIP_UNUSED_ARG(accomp); + LWIP_UNUSED_ARG(pcomp); + LWIP_UNUSED_ARG(mru); + + /* Load the ACCM bits for the 32 control codes. */ +#if PPPOS_SUPPORT + SYS_ARCH_PROTECT(lev); + for (i = 0; i < 32 / 8; i++) { + /* @todo: does this work? ext_accm has been modified from pppd! */ + pcb->rx.in_accm[i] = (u_char)(accm >> (i * 8)); + } + SYS_ARCH_UNPROTECT(lev); +#else + LWIP_UNUSED_ARG(accm); +#endif /* PPPOS_SUPPORT */ + +#if PPPOS_SUPPORT + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: in_accm=%X %X %X %X\n", + pcb->num, + pcb->rx.in_accm[0], pcb->rx.in_accm[1], pcb->rx.in_accm[2], pcb->rx.in_accm[3])); +#else + PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]\n", pcb->num) ); +#endif /* PPPOS_SUPPORT */ + return 0; +} + + +/* + * sifaddr - Config the interface IP addresses and netmask. + */ +int sifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr, + u32_t net_mask) { + + SMEMCPY(&pcb->addrs.our_ipaddr, &our_adr, sizeof(our_adr)); + SMEMCPY(&pcb->addrs.his_ipaddr, &his_adr, sizeof(his_adr)); + SMEMCPY(&pcb->addrs.netmask, &net_mask, sizeof(net_mask)); + return 1; +} + + +/******************************************************************** + * + * cifaddr - Clear the interface IP addresses, and delete routes + * through the interface if possible. + */ +int cifaddr(ppp_pcb *pcb, u32_t our_adr, u32_t his_adr) { + + LWIP_UNUSED_ARG(our_adr); + LWIP_UNUSED_ARG(his_adr); + + IP4_ADDR(&pcb->addrs.our_ipaddr, 0,0,0,0); + IP4_ADDR(&pcb->addrs.his_ipaddr, 0,0,0,0); + IP4_ADDR(&pcb->addrs.netmask, 255,255,255,255); + return 1; +} + + +#if PPP_IPV6_SUPPORT +#define IN6_LLADDR_FROM_EUI64(ip6, eui64) do { \ + memset(&ip6.addr, 0, sizeof(ip6_addr_t)); \ + ip6.addr[0] = PP_HTONL(0xfe800000); \ + eui64_copy(eui64, ip6.addr[2]); \ + } while (0) + +/******************************************************************** + * + * sif6addr - Config the interface with an IPv6 link-local address + */ +int sif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) { + + IN6_LLADDR_FROM_EUI64(pcb->addrs.our6_ipaddr, our_eui64); + IN6_LLADDR_FROM_EUI64(pcb->addrs.his6_ipaddr, his_eui64); + return 1; +} + +/******************************************************************** + * + * cif6addr - Remove IPv6 address from interface + */ +int cif6addr(ppp_pcb *pcb, eui64_t our_eui64, eui64_t his_eui64) { + + LWIP_UNUSED_ARG(our_eui64); + LWIP_UNUSED_ARG(his_eui64); + + IP6_ADDR(&pcb->addrs.our6_ipaddr, 0, 0,0,0,0); + IP6_ADDR(&pcb->addrs.his6_ipaddr, 0, 0,0,0,0); + return 1; +} +#endif /* PPP_IPV6_SUPPORT */ + + +/* + * sdns - Config the DNS servers + */ +int sdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) { + + SMEMCPY(&pcb->addrs.dns1, &ns1, sizeof(ns1)); + SMEMCPY(&pcb->addrs.dns2, &ns2, sizeof(ns2)); + return 1; +} + + +/******************************************************************** + * + * cdns - Clear the DNS servers + */ +int cdns(ppp_pcb *pcb, u32_t ns1, u32_t ns2) { + + LWIP_UNUSED_ARG(ns1); + LWIP_UNUSED_ARG(ns2); + + IP4_ADDR(&pcb->addrs.dns1, 0,0,0,0); + IP4_ADDR(&pcb->addrs.dns2, 0,0,0,0); + return 1; +} + + +/* + * sifup - Config the interface up and enable IP packets to pass. + */ +int sifup(ppp_pcb *pcb) { + + netif_set_addr(&pcb->netif, &pcb->addrs.our_ipaddr, &pcb->addrs.netmask, + &pcb->addrs.his_ipaddr); + + netif_set_up(&pcb->netif); + pcb->if_up = 1; + pcb->err_code = PPPERR_NONE; + + PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: err_code=%d\n", pcb->num, pcb->err_code)); + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return 1; +} + +/******************************************************************** + * + * sifdown - Disable the indicated protocol and config the interface + * down if there are no remaining protocols. + */ +int sifdown(ppp_pcb *pcb) { + + if(!pcb->if_up) + return 1; + + pcb->if_up = 0; + + if (1 +#if PPP_IPV6_SUPPORT + /* set the interface down if IPv6 is down as well */ + && !pcb->if6_up +#endif /* PPP_IPV6_SUPPORT */ + ) { + /* make sure the netif status callback is called */ + netif_set_down(&pcb->netif); + } + PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: err_code=%d\n", pcb->num, pcb->err_code)); + return 1; +} + +#if PPP_IPV6_SUPPORT +/* + * sif6up - Config the interface up and enable IPv6 packets to pass. + */ +int sif6up(ppp_pcb *pcb) { + + ip6_addr_copy(pcb->netif.ip6_addr[0], pcb->addrs.our6_ipaddr); + netif_ip6_addr_set_state(&pcb->netif, 0, IP6_ADDR_PREFERRED); + + netif_set_up(&pcb->netif); + pcb->if6_up = 1; + pcb->err_code = PPPERR_NONE; + + PPPDEBUG(LOG_DEBUG, ("sif6up: unit %d: err_code=%d\n", pcb->num, pcb->err_code)); + pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb); + return 1; +} + +/******************************************************************** + * + * sif6down - Disable the indicated protocol and config the interface + * down if there are no remaining protocols. + */ +int sif6down(ppp_pcb *pcb) { + + if(!pcb->if6_up) + return 1; + + pcb->if6_up = 0; + /* set the interface down if IPv4 is down as well */ + if (!pcb->if_up) { + /* make sure the netif status callback is called */ + netif_set_down(&pcb->netif); + } + PPPDEBUG(LOG_DEBUG, ("sif6down: unit %d: err_code=%d\n", pcb->num, pcb->err_code)); + return 1; +} +#endif /* PPP_IPV6_SUPPORT */ + +/* + * sifnpmode - Set the mode for handling packets for a given NP. + */ +int sifnpmode(ppp_pcb *pcb, int proto, enum NPmode mode) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(proto); + LWIP_UNUSED_ARG(mode); + return 0; +} + +/* + * netif_set_mtu - set the MTU on the PPP network interface. + */ +void netif_set_mtu(ppp_pcb *pcb, int mtu) { + + pcb->netif.mtu = mtu; +} + +/* + * netif_get_mtu - get PPP interface MTU + */ +int netif_get_mtu(ppp_pcb *pcb) { + + return pcb->netif.mtu; +} + +/******************************************************************** + * + * sifproxyarp - Make a proxy ARP entry for the peer. + */ + +int sifproxyarp(ppp_pcb *pcb, u32_t his_adr) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(his_adr); + /* FIXME: do we really need that in IPCP ? */ + return 0; +} + +/******************************************************************** + * + * cifproxyarp - Delete the proxy ARP entry for the peer. + */ + +int cifproxyarp(ppp_pcb *pcb, u32_t his_adr) { + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(his_adr); + /* FIXME: do we really need that in IPCP ? */ + return 0; +} + +/******************************************************************** + * + * sifvjcomp - config tcp header compression + */ +int sifvjcomp(ppp_pcb *pcb, int vjcomp, int cidcomp, int maxcid) { + +#if PPPOS_SUPPORT && VJ_SUPPORT + pcb->vj_enabled = vjcomp; + pcb->vj_comp.compressSlot = cidcomp; + pcb->vj_comp.maxSlotIndex = maxcid; + PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n", + vjcomp, cidcomp, maxcid)); +#else /* PPPOS_SUPPORT && VJ_SUPPORT */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(vjcomp); + LWIP_UNUSED_ARG(cidcomp); + LWIP_UNUSED_ARG(maxcid); +#endif /* PPPOS_SUPPORT && VJ_SUPPORT */ + + return 0; +} + +#if PPP_IDLETIMELIMIT +/******************************************************************** + * + * get_idle_time - return how long the link has been idle. + */ +int get_idle_time(ppp_pcb *pcb, struct ppp_idle *ip) { + /* FIXME: add idle time support and make it optional */ + LWIP_UNUSED_ARG(pcb); + LWIP_UNUSED_ARG(ip); + return 1; +} +#endif /* PPP_IDLETIMELIMIT */ + +/******************************************************************** + * + * get_loop_output - get outgoing packets from the ppp device, + * and detect when we want to bring the real link up. + * Return value is 1 if we need to bring up the link, 0 otherwise. + */ +int get_loop_output(void) { + /* FIXME: necessary for "demand", do we really need to support on-demand ? */ + return 0; +} + +/******************************************************************** + * + * Return user specified netmask, modified by any mask we might determine + * for address `addr' (in network byte order). + * Here we scan through the system's list of interfaces, looking for + * any non-point-to-point interfaces which might appear to be on the same + * network as `addr'. If we find any, we OR in their netmask to the + * user-specified netmask. + */ +u32_t get_mask(u32_t addr) { +#if 0 + u32_t mask, nmask; + + addr = htonl(addr); + if (IP_CLASSA(addr)) { /* determine network mask for address class */ + nmask = IP_CLASSA_NET; + } else if (IP_CLASSB(addr)) { + nmask = IP_CLASSB_NET; + } else { + nmask = IP_CLASSC_NET; + } + + /* class D nets are disallowed by bad_ip_adrs */ + mask = PP_HTONL(0xffffff00UL) | htonl(nmask); + + /* XXX + * Scan through the system's network interfaces. + * Get each netmask and OR them into our mask. + */ + /* return mask; */ + return mask; +#endif + LWIP_UNUSED_ARG(addr); + return 0xFFFFFFFF; +} + + +#if PPP_PROTOCOLNAME +/* List of protocol names, to make our messages a little more informative. */ +struct protocol_list { + u_short proto; + const char *name; +} protocol_list[] = { + { 0x21, "IP" }, + { 0x23, "OSI Network Layer" }, + { 0x25, "Xerox NS IDP" }, + { 0x27, "DECnet Phase IV" }, + { 0x29, "Appletalk" }, + { 0x2b, "Novell IPX" }, + { 0x2d, "VJ compressed TCP/IP" }, + { 0x2f, "VJ uncompressed TCP/IP" }, + { 0x31, "Bridging PDU" }, + { 0x33, "Stream Protocol ST-II" }, + { 0x35, "Banyan Vines" }, + { 0x39, "AppleTalk EDDP" }, + { 0x3b, "AppleTalk SmartBuffered" }, + { 0x3d, "Multi-Link" }, + { 0x3f, "NETBIOS Framing" }, + { 0x41, "Cisco Systems" }, + { 0x43, "Ascom Timeplex" }, + { 0x45, "Fujitsu Link Backup and Load Balancing (LBLB)" }, + { 0x47, "DCA Remote Lan" }, + { 0x49, "Serial Data Transport Protocol (PPP-SDTP)" }, + { 0x4b, "SNA over 802.2" }, + { 0x4d, "SNA" }, + { 0x4f, "IP6 Header Compression" }, + { 0x51, "KNX Bridging Data" }, + { 0x53, "Encryption" }, + { 0x55, "Individual Link Encryption" }, + { 0x57, "IPv6" }, + { 0x59, "PPP Muxing" }, + { 0x5b, "Vendor-Specific Network Protocol" }, + { 0x61, "RTP IPHC Full Header" }, + { 0x63, "RTP IPHC Compressed TCP" }, + { 0x65, "RTP IPHC Compressed non-TCP" }, + { 0x67, "RTP IPHC Compressed UDP 8" }, + { 0x69, "RTP IPHC Compressed RTP 8" }, + { 0x6f, "Stampede Bridging" }, + { 0x73, "MP+" }, + { 0xc1, "NTCITS IPI" }, + { 0xfb, "single-link compression" }, + { 0xfd, "Compressed Datagram" }, + { 0x0201, "802.1d Hello Packets" }, + { 0x0203, "IBM Source Routing BPDU" }, + { 0x0205, "DEC LANBridge100 Spanning Tree" }, + { 0x0207, "Cisco Discovery Protocol" }, + { 0x0209, "Netcs Twin Routing" }, + { 0x020b, "STP - Scheduled Transfer Protocol" }, + { 0x020d, "EDP - Extreme Discovery Protocol" }, + { 0x0211, "Optical Supervisory Channel Protocol" }, + { 0x0213, "Optical Supervisory Channel Protocol" }, + { 0x0231, "Luxcom" }, + { 0x0233, "Sigma Network Systems" }, + { 0x0235, "Apple Client Server Protocol" }, + { 0x0281, "MPLS Unicast" }, + { 0x0283, "MPLS Multicast" }, + { 0x0285, "IEEE p1284.4 standard - data packets" }, + { 0x0287, "ETSI TETRA Network Protocol Type 1" }, + { 0x0289, "Multichannel Flow Treatment Protocol" }, + { 0x2063, "RTP IPHC Compressed TCP No Delta" }, + { 0x2065, "RTP IPHC Context State" }, + { 0x2067, "RTP IPHC Compressed UDP 16" }, + { 0x2069, "RTP IPHC Compressed RTP 16" }, + { 0x4001, "Cray Communications Control Protocol" }, + { 0x4003, "CDPD Mobile Network Registration Protocol" }, + { 0x4005, "Expand accelerator protocol" }, + { 0x4007, "ODSICP NCP" }, + { 0x4009, "DOCSIS DLL" }, + { 0x400B, "Cetacean Network Detection Protocol" }, + { 0x4021, "Stacker LZS" }, + { 0x4023, "RefTek Protocol" }, + { 0x4025, "Fibre Channel" }, + { 0x4027, "EMIT Protocols" }, + { 0x405b, "Vendor-Specific Protocol (VSP)" }, + { 0x8021, "Internet Protocol Control Protocol" }, + { 0x8023, "OSI Network Layer Control Protocol" }, + { 0x8025, "Xerox NS IDP Control Protocol" }, + { 0x8027, "DECnet Phase IV Control Protocol" }, + { 0x8029, "Appletalk Control Protocol" }, + { 0x802b, "Novell IPX Control Protocol" }, + { 0x8031, "Bridging NCP" }, + { 0x8033, "Stream Protocol Control Protocol" }, + { 0x8035, "Banyan Vines Control Protocol" }, + { 0x803d, "Multi-Link Control Protocol" }, + { 0x803f, "NETBIOS Framing Control Protocol" }, + { 0x8041, "Cisco Systems Control Protocol" }, + { 0x8043, "Ascom Timeplex" }, + { 0x8045, "Fujitsu LBLB Control Protocol" }, + { 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" }, + { 0x8049, "Serial Data Control Protocol (PPP-SDCP)" }, + { 0x804b, "SNA over 802.2 Control Protocol" }, + { 0x804d, "SNA Control Protocol" }, + { 0x804f, "IP6 Header Compression Control Protocol" }, + { 0x8051, "KNX Bridging Control Protocol" }, + { 0x8053, "Encryption Control Protocol" }, + { 0x8055, "Individual Link Encryption Control Protocol" }, + { 0x8057, "IPv6 Control Protocol" }, + { 0x8059, "PPP Muxing Control Protocol" }, + { 0x805b, "Vendor-Specific Network Control Protocol (VSNCP)" }, + { 0x806f, "Stampede Bridging Control Protocol" }, + { 0x8073, "MP+ Control Protocol" }, + { 0x80c1, "NTCITS IPI Control Protocol" }, + { 0x80fb, "Single Link Compression Control Protocol" }, + { 0x80fd, "Compression Control Protocol" }, + { 0x8207, "Cisco Discovery Protocol Control" }, + { 0x8209, "Netcs Twin Routing" }, + { 0x820b, "STP - Control Protocol" }, + { 0x820d, "EDPCP - Extreme Discovery Protocol Ctrl Prtcl" }, + { 0x8235, "Apple Client Server Protocol Control" }, + { 0x8281, "MPLSCP" }, + { 0x8285, "IEEE p1284.4 standard - Protocol Control" }, + { 0x8287, "ETSI TETRA TNP1 Control Protocol" }, + { 0x8289, "Multichannel Flow Treatment Protocol" }, + { 0xc021, "Link Control Protocol" }, + { 0xc023, "Password Authentication Protocol" }, + { 0xc025, "Link Quality Report" }, + { 0xc027, "Shiva Password Authentication Protocol" }, + { 0xc029, "CallBack Control Protocol (CBCP)" }, + { 0xc02b, "BACP Bandwidth Allocation Control Protocol" }, + { 0xc02d, "BAP" }, + { 0xc05b, "Vendor-Specific Authentication Protocol (VSAP)" }, + { 0xc081, "Container Control Protocol" }, + { 0xc223, "Challenge Handshake Authentication Protocol" }, + { 0xc225, "RSA Authentication Protocol" }, + { 0xc227, "Extensible Authentication Protocol" }, + { 0xc229, "Mitsubishi Security Info Exch Ptcl (SIEP)" }, + { 0xc26f, "Stampede Bridging Authorization Protocol" }, + { 0xc281, "Proprietary Authentication Protocol" }, + { 0xc283, "Proprietary Authentication Protocol" }, + { 0xc481, "Proprietary Node ID Authentication Protocol" }, + { 0, NULL }, +}; + +/* + * protocol_name - find a name for a PPP protocol. + */ +const char * protocol_name(int proto) { + struct protocol_list *lp; + + for (lp = protocol_list; lp->proto != 0; ++lp) + if (proto == lp->proto) + return lp->name; + return NULL; +} +#endif /* PPP_PROTOCOLNAME */ + +#if PPP_STATS_SUPPORT + +/* ---- Note on PPP Stats support ---- + * + * The one willing link stats support should add the get_ppp_stats() + * to fetch statistics from lwIP. + */ + +/* + * reset_link_stats - "reset" stats when link goes up. + */ +void reset_link_stats(int u) { + if (!get_ppp_stats(u, &old_link_stats)) + return; + gettimeofday(&start_time, NULL); +} + +/* + * update_link_stats - get stats at link termination. + */ +void update_link_stats(int u) { + + struct timeval now; + char numbuf[32]; + + if (!get_ppp_stats(u, &link_stats) + || gettimeofday(&now, NULL) < 0) + return; + link_connect_time = now.tv_sec - start_time.tv_sec; + link_stats_valid = 1; + + link_stats.bytes_in -= old_link_stats.bytes_in; + link_stats.bytes_out -= old_link_stats.bytes_out; + link_stats.pkts_in -= old_link_stats.pkts_in; + link_stats.pkts_out -= old_link_stats.pkts_out; +} + +void print_link_stats() { + /* + * Print connect time and statistics. + */ + if (link_stats_valid) { + int t = (link_connect_time + 5) / 6; /* 1/10ths of minutes */ + info("Connect time %d.%d minutes.", t/10, t%10); + info("Sent %u bytes, received %u bytes.", + link_stats.bytes_out, link_stats.bytes_in); + link_stats_valid = 0; + } +} +#endif /* PPP_STATS_SUPPORT */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppcrypt.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppcrypt.c new file mode 100644 index 0000000..097f702 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppcrypt.c @@ -0,0 +1,66 @@ +/* + * pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1 + * + * Extracted from chap_ms.c by James Carlson. + * + * Copyright (c) 1995 Eric Rosenquist. 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(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not necessary */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/pppcrypt.h" + + +static u_char pppcrypt_get_7bits(u_char *input, int startBit) { + unsigned int word; + + word = (unsigned)input[startBit / 8] << 8; + word |= (unsigned)input[startBit / 8 + 1]; + + word >>= 15 - (startBit % 8 + 7); + + return word & 0xFE; +} + +/* IN 56 bit DES key missing parity bits + * OUT 64 bit DES key with parity bits added + */ +void pppcrypt_56_to_64_bit_key(u_char *key, u_char * des_key) { + des_key[0] = pppcrypt_get_7bits(key, 0); + des_key[1] = pppcrypt_get_7bits(key, 7); + des_key[2] = pppcrypt_get_7bits(key, 14); + des_key[3] = pppcrypt_get_7bits(key, 21); + des_key[4] = pppcrypt_get_7bits(key, 28); + des_key[5] = pppcrypt_get_7bits(key, 35); + des_key[6] = pppcrypt_get_7bits(key, 42); + des_key[7] = pppcrypt_get_7bits(key, 49); +} + +#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppoe.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppoe.c new file mode 100644 index 0000000..2aa819c --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppoe.c @@ -0,0 +1,1113 @@ +/***************************************************************************** +* pppoe.c - 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. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "lwip/timers.h" +#include "lwip/memp.h" +#include "lwip/stats.h" + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppoe.h" + +/* Add a 16 bit unsigned value to a buffer pointed to by PTR */ +#define PPPOE_ADD_16(PTR, VAL) \ + *(PTR)++ = (u8_t)((VAL) / 256); \ + *(PTR)++ = (u8_t)((VAL) % 256) + +/* Add a complete PPPoE header to the buffer pointed to by PTR */ +#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \ + *(PTR)++ = PPPOE_VERTYPE; \ + *(PTR)++ = (CODE); \ + PPPOE_ADD_16(PTR, SESS); \ + PPPOE_ADD_16(PTR, LEN) + +#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */ +#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */ +#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */ +#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */ + +#ifdef PPPOE_SERVER +#error "PPPOE_SERVER is not yet supported under lwIP!" +/* from if_spppsubr.c */ +#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ +#endif + +/* FIXME: we should probably remove that, this is only used for debug purposes */ +#ifndef PPPOE_ERRORSTRING_LEN +#define PPPOE_ERRORSTRING_LEN 64 +#endif +static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN]; + + +/* management routines */ +static void pppoe_abort_connect(struct pppoe_softc *); +static void pppoe_clear_softc(struct pppoe_softc *, const char *); + +/* internal timeout handling */ +static void pppoe_timeout(void *); + +/* sending actual protocol controll packets */ +static err_t pppoe_send_padi(struct pppoe_softc *); +static err_t pppoe_send_padr(struct pppoe_softc *); +#ifdef PPPOE_SERVER +static err_t pppoe_send_pado(struct pppoe_softc *); +static err_t pppoe_send_pads(struct pppoe_softc *); +#endif +static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *); + +/* internal helper functions */ +static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif); +static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif); + +/** linked list of created pppoe interfaces */ +static struct pppoe_softc *pppoe_softc_list; + +err_t +pppoe_create(struct netif *ethif, ppp_pcb *pcb, void (*link_status_cb)(ppp_pcb *pcb, int up), struct pppoe_softc **scptr) +{ + struct pppoe_softc *sc; + + sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF); + if (sc == NULL) { + *scptr = NULL; + return ERR_MEM; + } + memset(sc, 0, sizeof(struct pppoe_softc)); + + /* changed to real address later */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + + sc->pcb = pcb; + sc->sc_link_status_cb = link_status_cb; + sc->sc_ethif = ethif; + + /* put the new interface at the head of the list */ + sc->next = pppoe_softc_list; + pppoe_softc_list = sc; + + *scptr = sc; + + return ERR_OK; +} + +err_t +pppoe_destroy(struct pppoe_softc *sc) +{ + struct pppoe_softc *cur, *prev = NULL; + + /* find previous linked list entry */ + for (cur = pppoe_softc_list; cur != NULL; prev = cur, cur = cur->next) { + if (sc == cur) { + break; + } + } + + if (cur != sc) { + return ERR_IF; + } + + sys_untimeout(pppoe_timeout, sc); + if (prev == NULL) { + /* remove sc from the head of the list */ + pppoe_softc_list = sc->next; + } else { + /* remove sc from the list */ + prev->next = sc->next; + } + +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name) { + mem_free(sc->sc_concentrator_name); + } + if (sc->sc_service_name) { + mem_free(sc->sc_service_name); + } +#endif /* PPPOE_TODO */ + memp_free(MEMP_PPPOE_IF, sc); + + return ERR_OK; +} + +/* + * Find the interface handling the specified session. + * Note: O(number of sessions open), this is a client-side only, mean + * and lean implementation, so number of open sessions typically should + * be 1. + */ +static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif) { + struct pppoe_softc *sc; + + if (session == 0) { + return NULL; + } + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc->sc_state == PPPOE_STATE_SESSION + && sc->sc_session == session + && sc->sc_ethif == rcvif) { + return sc; + } + } + return NULL; +} + +/* Check host unique token passed and return appropriate softc pointer, + * or NULL if token is bogus. */ +static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) { + struct pppoe_softc *sc, *t; + + if (pppoe_softc_list == NULL) { + return NULL; + } + + if (len != sizeof sc) { + return NULL; + } + MEMCPY(&t, token, len); + + for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) { + if (sc == t) { + break; + } + } + + if (sc == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n")); + return NULL; + } + + /* should be safe to access *sc now */ + if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state)); + return NULL; + } + if (sc->sc_ethif != rcvif) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": wrong interface, not accepting host unique\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + return NULL; + } + return sc; +} + +static void +pppoe_linkstatus_up(struct pppoe_softc *sc) +{ + sc->sc_link_status_cb(sc->pcb, PPPOE_CB_STATE_UP); +} + +/* analyze and handle a single received packet while not in session state */ +void +pppoe_disc_input(struct netif *netif, struct pbuf *pb) +{ + u16_t tag, len; + u16_t session, plen; + struct pppoe_softc *sc; + const char *err_msg; + char devname[6]; + u8_t *ac_cookie; + u16_t ac_cookie_len; +#ifdef PPPOE_SERVER + u8_t *hunique; + size_t hunique_len; +#endif + struct pppoehdr *ph; + struct pppoetag pt; + int off, err; + struct eth_hdr *ethhdr; + + /* don't do anything if there is not a single PPPoE instance */ + if (pppoe_softc_list == NULL) { + pbuf_free(pb); + return; + } + + pb = ppp_singlebuf(pb); + + strcpy(devname, "pppoe"); /* as long as we don't know which instance */ + err_msg = NULL; + if (pb->len < sizeof(*ethhdr)) { + goto done; + } + ethhdr = (struct eth_hdr *)pb->payload; + off = sizeof(*ethhdr); + + ac_cookie = NULL; + ac_cookie_len = 0; +#ifdef PPPOE_SERVER + hunique = NULL; + hunique_len = 0; +#endif + session = 0; + if (pb->len - off < (u16_t)PPPOE_HEADERLEN) { + PPPDEBUG(LOG_DEBUG, ("pppoe: packet too short: %d\n", pb->len)); + goto done; + } + + ph = (struct pppoehdr *) (ethhdr + 1); + if (ph->vertype != PPPOE_VERTYPE) { + PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype)); + goto done; + } + session = ntohs(ph->session); + plen = ntohs(ph->plen); + off += sizeof(*ph); + + if (plen + off > pb->len) { + PPPDEBUG(LOG_DEBUG, ("pppoe: packet content does not fit: data available = %d, packet size = %u\n", + pb->len - off, plen)); + goto done; + } + if(pb->tot_len == pb->len) { + pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */ + } + tag = 0; + len = 0; + sc = NULL; + while (off + sizeof(pt) <= pb->len) { + MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt)); + tag = ntohs(pt.tag); + len = ntohs(pt.len); + if (off + sizeof(pt) + len > pb->len) { + PPPDEBUG(LOG_DEBUG, ("pppoe: tag 0x%x len 0x%x is too long\n", tag, len)); + goto done; + } + switch (tag) { + case PPPOE_TAG_EOL: + goto breakbreak; + case PPPOE_TAG_SNAME: + break; /* ignored */ + case PPPOE_TAG_ACNAME: + break; /* ignored */ + case PPPOE_TAG_HUNIQUE: + if (sc != NULL) { + break; + } +#ifdef PPPOE_SERVER + hunique = (u8_t*)pb->payload + off + sizeof(pt); + hunique_len = len; +#endif + sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif); + if (sc != NULL) { + devname[0] = sc->sc_ethif->name[0]; + devname[1] = sc->sc_ethif->name[1]; + devname[2] = sc->sc_ethif->num; + devname[3] = '\0'; + } + break; + case PPPOE_TAG_ACCOOKIE: + if (ac_cookie == NULL) { + ac_cookie = (u8_t*)pb->payload + off + sizeof(pt); + ac_cookie_len = len; + } + break; + case PPPOE_TAG_SNAME_ERR: + err_msg = "SERVICE NAME ERROR"; + break; + case PPPOE_TAG_ACSYS_ERR: + err_msg = "AC SYSTEM ERROR"; + break; + case PPPOE_TAG_GENERIC_ERR: + err_msg = "GENERIC ERROR"; + break; + default: + break; + } + if (NULL != err_msg) { + if (len) { + u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1); + strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len); + pppoe_error_tmp[error_len] = '\0'; + PPPDEBUG(LOG_DEBUG, ("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp)); + } else { + PPPDEBUG(LOG_DEBUG, ("%s: %s\n", devname, err_msg)); + } + } + off += sizeof(pt) + len; + } + +breakbreak:; + switch (ph->code) { + case PPPOE_CODE_PADI: +#ifdef PPPOE_SERVER + /* + * got service name, concentrator name, and/or host unique. + * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP. + */ + if (LIST_EMPTY(&pppoe_softc_list)) { + goto done; + } + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + continue; + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + continue; + } + if (sc->sc_state == PPPOE_STATE_INITIAL) { + break; + } + } + if (sc == NULL) { + /* PPPDEBUG(LOG_DEBUG, ("pppoe: free passive interface is not found\n")); */ + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); + sc->sc_state = PPPOE_STATE_PADO_SENT; + pppoe_send_pado(sc); + break; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADR: +#ifdef PPPOE_SERVER + /* + * get sc from ac_cookie if IFF_PASSIVE + */ + if (ac_cookie == NULL) { + /* be quiet if there is not a single pppoe instance */ + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but not includes ac_cookie\n")); + goto done; + } + sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif); + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (!LIST_EMPTY(&pppoe_softc_list)) { + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n")); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + goto done; + } + if (hunique) { + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + } + sc->sc_hunique = mem_malloc(hunique_len); + if (sc->sc_hunique == NULL) { + goto done; + } + sc->sc_hunique_len = hunique_len; + MEMCPY(sc->sc_hunique, hunique, hunique_len); + } + pppoe_send_pads(sc); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; +#else + /* ignore, we are no access concentrator */ + goto done; +#endif /* PPPOE_SERVER */ + case PPPOE_CODE_PADO: + if (sc == NULL) { + /* be quiet if there is not a single pppoe instance */ + if (pppoe_softc_list != NULL) { + PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n")); + } + goto done; + } + if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + goto done; + } + if (ac_cookie) { + sc->sc_ac_cookie_len = ac_cookie_len; + MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len); + } + MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr)); + sys_untimeout(pppoe_timeout, sc); + sc->sc_padr_retried = 0; + sc->sc_state = PPPOE_STATE_PADR_SENT; + if ((err = pppoe_send_padr(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + case PPPOE_CODE_PADS: + if (sc == NULL) { + goto done; + } + sc->sc_session = session; + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session)); + sc->sc_state = PPPOE_STATE_SESSION; + pppoe_linkstatus_up(sc); /* notify upper layers */ + break; + case PPPOE_CODE_PADT: + if (sc == NULL) { + goto done; + } + pppoe_clear_softc(sc, "received PADT"); + break; + default: + if(sc) { + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + (u16_t)ph->code, session)); + } else { + PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session)); + } + break; + } + +done: + pbuf_free(pb); + return; +} + +void +pppoe_data_input(struct netif *netif, struct pbuf *pb) +{ + u16_t session, plen; + struct pppoe_softc *sc; + struct pppoehdr *ph; +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + u8_t shost[ETHER_ADDR_LEN]; +#endif + +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost)); +#endif + if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + pb = ppp_singlebuf (pb); + + if (pb->len <= PPPOE_HEADERLEN) { + PPPDEBUG(LOG_DEBUG, ("pppoe (data): dropping too short packet: %d bytes\n", pb->len)); + goto drop; + } + + if (pb->len < sizeof(*ph)) { + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n")); + goto drop; + } + ph = (struct pppoehdr *)pb->payload; + + if (ph->vertype != PPPOE_VERTYPE) { + PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype)); + goto drop; + } + if (ph->code != 0) { + goto drop; + } + + session = ntohs(ph->session); + sc = pppoe_find_softc_by_session(session, netif); + if (sc == NULL) { +#ifdef PPPOE_TERM_UNKNOWN_SESSIONS + PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session)); + pppoe_send_padt(netif, session, shost); +#endif + goto drop; + } + + plen = ntohs(ph->plen); + + if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n")); + LINK_STATS_INC(link.lenerr); + goto drop; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, + pb->len, plen)); + + if (pb->len < plen) { + goto drop; + } + + /* Dispatch the packet thereby consuming it. */ + ppp_input(sc->pcb, pb); + return; + +drop: + pbuf_free(pb); +} + +static err_t +pppoe_output(struct pppoe_softc *sc, struct pbuf *pb) +{ + struct eth_hdr *ethhdr; + u16_t etype; + err_t res; + + if (!sc->sc_ethif) { + pbuf_free(pb); + return ERR_IF; + } + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(pb, (u16_t)(sizeof(struct eth_hdr))) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_output: could not allocate room for Ethernet header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + ethhdr = (struct eth_hdr *)pb->payload; + etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC; + ethhdr->type = htons(etype); + MEMCPY(ðhdr->dest.addr, &sc->sc_dest.addr, sizeof(ethhdr->dest.addr)); + MEMCPY(ðhdr->src.addr, &sc->sc_ethif->hwaddr, sizeof(ethhdr->src.addr)); + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype, + sc->sc_state, sc->sc_session, + sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5], + pb->tot_len)); + + res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb); + + pbuf_free(pb); + + return res; +} + +static err_t +pppoe_send_padi(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + int len; +#ifdef PPPOE_TODO + int l1 = 0, l2 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state >PPPOE_STATE_PADI_SENT) { + PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state)); + } + + /* calculate length of frame (excluding ethernet header + pppoe header) */ + len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + l1 = (int)strlen(sc->sc_service_name); + len += l1; + } + if (sc->sc_concentrator_name != NULL) { + l2 = (int)strlen(sc->sc_concentrator_name); + len += 2 + 2 + l2; + } +#endif /* PPPOE_TODO */ + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } +#ifdef PPPOE_TODO + if (sc->sc_concentrator_name != NULL) { + PPPOE_ADD_16(p, PPPOE_TAG_ACNAME); + PPPOE_ADD_16(p, l2); + MEMCPY(p, sc->sc_concentrator_name, l2); + p += l2; + } +#endif /* PPPOE_TODO */ + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + /* send pkt */ + return pppoe_output(sc, pb); +} + +static void +pppoe_timeout(void *arg) +{ + u32_t retry_wait; + int err; + struct pppoe_softc *sc = (struct pppoe_softc*)arg; + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + switch (sc->sc_state) { + case PPPOE_STATE_PADI_SENT: + /* + * We have two basic ways of retrying: + * - Quick retry mode: try a few times in short sequence + * - Slow retry mode: we already had a connection successfully + * established and will try infinitely (without user + * intervention) + * We only enter slow retry mode if IFF_LINK1 (aka autodial) + * is not set. + */ + if (sc->sc_padi_retried < 0xff) { + sc->sc_padi_retried++; + } + if (!sc->pcb->settings.persist && sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { +#if 0 + if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { + /* slow retry mode */ + retry_wait = PPPOE_SLOW_RETRY; + } else +#endif + { + pppoe_abort_connect(sc); + return; + } + } + /* initialize for quick retry mode */ + retry_wait = LWIP_MIN(PPPOE_DISC_TIMEOUT * sc->sc_padi_retried, PPPOE_SLOW_RETRY); + if ((err = pppoe_send_padi(sc)) != 0) { + sc->sc_padi_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(retry_wait, pppoe_timeout, sc); + break; + + case PPPOE_STATE_PADR_SENT: + sc->sc_padr_retried++; + if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_state = PPPOE_STATE_PADI_SENT; + sc->sc_padr_retried = 0; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); + return; + } + if ((err = pppoe_send_padr(sc)) != 0) { + sc->sc_padr_retried--; + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + break; + default: + return; /* all done, work in peace */ + } +} + +/* Start a connection (i.e. initiate discovery phase) */ +int +pppoe_connect(struct pppoe_softc *sc) +{ + int err; + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + return EBUSY; + } + + /* stop any timer */ + sys_untimeout(pppoe_timeout, sc); + sc->sc_session = 0; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; +#ifdef PPPOE_SERVER + /* wait PADI if IFF_PASSIVE */ + if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + return 0; + } +#endif + /* save state, in case we fail to send PADI */ + sc->sc_state = PPPOE_STATE_PADI_SENT; + if ((err = pppoe_send_padi(sc)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err)); + } + sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); + return err; +} + +/* disconnect */ +void +pppoe_disconnect(struct pppoe_softc *sc) +{ + if (sc->sc_state < PPPOE_STATE_SESSION) { + return; + } + + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest); + + /* cleanup softc */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; +#ifdef PPPOE_SERVER + if (sc->sc_hunique) { + mem_free(sc->sc_hunique); + sc->sc_hunique = NULL; + } + sc->sc_hunique_len = 0; +#endif + sc->sc_session = 0; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; + + sc->sc_link_status_cb(sc->pcb, PPPOE_CB_STATE_DOWN); /* notify upper layers */ + return; +} + +/* Connection attempt aborted */ +static void +pppoe_abort_connect(struct pppoe_softc *sc) +{ + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + + /* clear connection state */ + sc->sc_state = PPPOE_STATE_INITIAL; + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; + + sc->sc_link_status_cb(sc->pcb, PPPOE_CB_STATE_FAILED); /* notify upper layers */ +} + +/* Send a PADR packet */ +static err_t +pppoe_send_padr(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; +#ifdef PPPOE_TODO + size_t l1 = 0; /* XXX: gcc */ +#endif /* PPPOE_TODO */ + + if (sc->sc_state != PPPOE_STATE_PADR_SENT) { + return ERR_CONN; + } + + len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */ +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } +#endif /* PPPOE_TODO */ + if (sc->sc_ac_cookie_len > 0) { + len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */ + } + LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff", + sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff); + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); +#ifdef PPPOE_TODO + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else +#endif /* PPPOE_TODO */ + { + PPPOE_ADD_16(p, 0); + } + if (sc->sc_ac_cookie_len > 0) { + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sc->sc_ac_cookie_len); + MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len); + p += sc->sc_ac_cookie_len; + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof sc); + + return pppoe_output(sc, pb); +} + +/* send a PADT packet */ +static err_t +pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest) +{ + struct pbuf *pb; + struct eth_hdr *ethhdr; + err_t res; + u8_t *p; + + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + pbuf_header(pb, sizeof(struct eth_hdr)); + ethhdr = (struct eth_hdr *)pb->payload; + ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC); + MEMCPY(ðhdr->dest.addr, dest, sizeof(ethhdr->dest.addr)); + MEMCPY(ðhdr->src.addr, &outgoing_if->hwaddr, sizeof(ethhdr->src.addr)); + + p = (u8_t*)(ethhdr + 1); + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0); + + res = outgoing_if->linkoutput(outgoing_if, pb); + + pbuf_free(pb); + + return res; +} + +#ifdef PPPOE_SERVER +static err_t +pppoe_send_pado(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len; + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + /* calc length */ + len = 0; + /* include ac_cookie */ + len += 2 + 2 + sizeof(sc); + /* include hunique */ + len += 2 + 2 + sc->sc_hunique_len; + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len); + PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE); + PPPOE_ADD_16(p, sizeof(sc)); + MEMCPY(p, &sc, sizeof(sc)); + p += sizeof(sc); + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} + +static err_t +pppoe_send_pads(struct pppoe_softc *sc) +{ + struct pbuf *pb; + u8_t *p; + size_t len, l1 = 0; /* XXX: gcc */ + + if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + return ERR_CONN; + } + + sc->sc_session = mono_time.tv_sec % 0xff + 1; + /* calc length */ + len = 0; + /* include hunique */ + len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/ + if (sc->sc_service_name != NULL) { /* service name tag maybe empty */ + l1 = strlen(sc->sc_service_name); + len += l1; + } + pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HEADERLEN + len), PBUF_RAM); + if (!pb) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len); + PPPOE_ADD_16(p, PPPOE_TAG_SNAME); + if (sc->sc_service_name != NULL) { + PPPOE_ADD_16(p, l1); + MEMCPY(p, sc->sc_service_name, l1); + p += l1; + } else { + PPPOE_ADD_16(p, 0); + } + PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE); + PPPOE_ADD_16(p, sc->sc_hunique_len); + MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len); + return pppoe_output(sc, pb); +} +#endif + +err_t +pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb) +{ + u8_t *p; + size_t len; + + /* are we ready to process data yet? */ + if (sc->sc_state < PPPOE_STATE_SESSION) { + /*sppp_flush(&sc->sc_sppp.pp_if);*/ + pbuf_free(pb); + return ERR_CONN; + } + + len = pb->tot_len; + + /* make room for PPPoE header - should not fail */ + if (pbuf_header(pb, (u16_t)(PPPOE_HEADERLEN)) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for PPPoE header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload; + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + return pppoe_output(sc, pb); +} + +#if 0 /*def PFIL_HOOKS*/ +static int +pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir) +{ + struct pppoe_softc *sc; + int s; + + if (mp != (struct pbuf **)PFIL_IFNET_DETACH) { + return 0; + } + + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc->sc_ethif != ifp) { + continue; + } + if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { + sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); + PPPDEBUG(LOG_DEBUG, ("%c%c%"U16_F": ethernet interface detached, going down\n", + sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num)); + } + sc->sc_ethif = NULL; + pppoe_clear_softc(sc, "ethernet interface detached"); + } + + return 0; +} +#endif + +static void +pppoe_clear_softc(struct pppoe_softc *sc, const char *message) +{ + LWIP_UNUSED_ARG(message); + + /* stop timer */ + sys_untimeout(pppoe_timeout, sc); + PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message)); + /* fix our state */ + sc->sc_state = PPPOE_STATE_INITIAL; + + /* notify upper layers */ + sc->sc_link_status_cb(sc->pcb, PPPOE_CB_STATE_DOWN); + + /* clean up softc */ + MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest)); + sc->sc_ac_cookie_len = 0; + sc->sc_session = 0; + sc->sc_padi_retried = 0; + sc->sc_padr_retried = 0; +} +#endif /* PPP_SUPPORT && PPPOE_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppol2tp.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppol2tp.c new file mode 100644 index 0000000..b526410 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/pppol2tp.c @@ -0,0 +1,1016 @@ +/** + * @file + * Network Point to Point Protocol over Layer 2 Tunneling Protocol program file. + * + */ + +/* + * 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. + * + */ + +/* + * L2TP Support status: + * + * Supported: + * - L2TPv2 (PPP over L2TP, a.k.a. UDP tunnels) + * - LAC + * + * Not supported: + * - LNS (require PPP server support) + * - L2TPv3 ethernet pseudowires + * - L2TPv3 VLAN pseudowire + * - L2TPv3 PPP pseudowires + * - L2TPv3 IP encapsulation + * - L2TPv3 IP pseudowire + * - L2TP tunnel switching - http://tools.ietf.org/html/draft-ietf-l2tpext-tunnel-switching-08 + * - Multiple tunnels per UDP socket, as well as multiple sessions per tunnel + * - Hidden AVPs + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PPPOL2TP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "lwip/err.h" +#include "lwip/memp.h" +#include "lwip/netif.h" +#include "lwip/udp.h" + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppol2tp.h" + +#include "netif/ppp/magic.h" + +#if PPPOL2TP_AUTH_SUPPORT +#if LWIP_INCLUDED_POLARSSL_MD5 +#include "netif/ppp/polarssl/md5.h" +#else +#include "polarssl/md5.h" +#endif +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + /* Prototypes for procedures local to this file. */ +static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, struct ip_addr *addr, u16_t port, + struct pbuf *p, u16_t len, u16_t tunnel_id, u16_t session_id, u16_t ns, u16_t nr); +static void pppol2tp_timeout(void *arg); +static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp); +static void pppol2tp_clear(pppol2tp_pcb *l2tp); +static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp); +static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns); +static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns); + + +/* Create a new L2TP session. */ +err_t pppol2tp_create(ppp_pcb *ppp, void (*link_status_cb)(ppp_pcb *pcb, int status), pppol2tp_pcb **l2tpptr, + struct netif *netif, ip_addr_t *ipaddr, u16_t port, + u8_t *secret, u8_t secret_len) { + pppol2tp_pcb *l2tp; + struct udp_pcb *udp; + + l2tp = (pppol2tp_pcb *)memp_malloc(MEMP_PPPOL2TP_PCB); + if (l2tp == NULL) { + *l2tpptr = NULL; + return ERR_MEM; + } + + udp = udp_new(); + if (udp == NULL) { + memp_free(MEMP_PPPOL2TP_PCB, l2tp); + *l2tpptr = NULL; + return ERR_MEM; + } + udp_recv(udp, pppol2tp_input, l2tp); + + memset(l2tp, 0, sizeof(pppol2tp_pcb)); + l2tp->phase = PPPOL2TP_STATE_INITIAL; + l2tp->ppp = ppp; + l2tp->udp = udp; + l2tp->link_status_cb = link_status_cb; + l2tp->netif = netif; + ip_addr_set(&l2tp->remote_ip, ipaddr); + l2tp->remote_port = port; + #if PPPOL2TP_AUTH_SUPPORT + l2tp->secret = secret; + l2tp->secret_len = secret_len; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + *l2tpptr = l2tp; + return ERR_OK; +} + +/* Destroy a L2TP control block */ +err_t pppol2tp_destroy(pppol2tp_pcb *l2tp) { + + sys_untimeout(pppol2tp_timeout, l2tp); + if (l2tp->udp != NULL) { + udp_remove(l2tp->udp); + } + memp_free(MEMP_PPPOL2TP_PCB, l2tp); + return ERR_OK; +} + +/* Be a LAC, connect to a LNS. */ +err_t pppol2tp_connect(pppol2tp_pcb *l2tp) { + err_t err; + + if (l2tp->phase != PPPOL2TP_STATE_INITIAL) { + return ERR_VAL; + } + + pppol2tp_clear(l2tp); + + /* Listen to a random source port, we need to do that instead of using udp_connect() + * because the L2TP LNS might answer with its own random source port (!= 1701) + */ + udp_bind(l2tp->udp, IP_ADDR_ANY, 0); + +#if PPPOL2TP_AUTH_SUPPORT + /* Generate random vector */ + if (l2tp->secret != NULL) { + random_bytes(l2tp->secret_rv, sizeof(l2tp->secret_rv)); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + do { + l2tp->remote_tunnel_id = magic(); + } while(l2tp->remote_tunnel_id == 0); + /* save state, in case we fail to send SCCRQ */ + l2tp->sccrq_retried = 0; + l2tp->phase = PPPOL2TP_STATE_SCCRQ_SENT; + if ((err = pppol2tp_send_sccrq(l2tp)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + return err; +} + +/* Disconnect */ +void pppol2tp_disconnect(pppol2tp_pcb *l2tp) { + + if (l2tp->phase < PPPOL2TP_STATE_DATA) { + return; + } + + l2tp->our_ns++; + pppol2tp_send_stopccn(l2tp, l2tp->our_ns); + + pppol2tp_clear(l2tp); + l2tp->link_status_cb(l2tp->ppp, PPPOL2TP_CB_STATE_DOWN); /* notify upper layers */ +} + +/* UDP Callback for incoming L2TP frames */ +static void pppol2tp_input(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg; + u16_t hflags, hlen, len=0, tunnel_id=0, session_id=0, ns=0, nr=0, offset=0; + u8_t *inp; + LWIP_UNUSED_ARG(pcb); + + if (l2tp->phase < PPPOL2TP_STATE_SCCRQ_SENT) { + goto free_and_return; + } + + /* printf("-----------\nL2TP INPUT, %d\n", p->len); */ + p = ppp_singlebuf(p); + + /* L2TP header */ + if (p->len < sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id) ) { + goto packet_too_short; + } + + inp = (u8_t*)p->payload; + GETSHORT(hflags, inp); + + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) { + /* check mandatory flags for a control packet */ + if ( (hflags & PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY) != PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for control packet not set\n")); + goto free_and_return; + } + /* check forbidden flags for a control packet */ + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL_FORBIDDEN) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: forbidden header flags for control packet found\n")); + goto free_and_return; + } + } else { + /* check mandatory flags for a data packet */ + if ( (hflags & PPPOL2TP_HEADERFLAG_DATA_MANDATORY) != PPPOL2TP_HEADERFLAG_DATA_MANDATORY) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: mandatory header flags for data packet not set\n")); + goto free_and_return; + } + } + + /* Expected header size */ + hlen = sizeof(hflags) + sizeof(tunnel_id) + sizeof(session_id); + if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) { + hlen += sizeof(len); + } + if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) { + hlen += sizeof(ns) + sizeof(nr); + } + if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) { + hlen += sizeof(offset); + } + if (p->len < hlen) { + goto packet_too_short; + } + + if (hflags & PPPOL2TP_HEADERFLAG_LENGTH) { + GETSHORT(len, inp); + if (p->len < len || len < hlen) { + goto packet_too_short; + } + } + GETSHORT(tunnel_id, inp); + GETSHORT(session_id, inp); + if (hflags & PPPOL2TP_HEADERFLAG_SEQUENCE) { + GETSHORT(ns, inp); + GETSHORT(nr, inp); + } + if (hflags & PPPOL2TP_HEADERFLAG_OFFSET) { + GETSHORT(offset, inp) + if (offset > 4096) { /* don't be fooled with large offset which might overflow hlen */ + PPPDEBUG(LOG_DEBUG, ("pppol2tp: strange packet received, offset=%d\n", offset)); + goto free_and_return; + } + hlen += offset; + if (p->len < hlen) { + goto packet_too_short; + } + INCPTR(offset, inp); + } + + /* printf("HLEN = %d\n", hlen); */ + + /* skip L2TP header */ + if (pbuf_header(p, -hlen) != 0) { + goto free_and_return; + } + + /* printf("LEN=%d, TUNNEL_ID=%d, SESSION_ID=%d, NS=%d, NR=%d, OFFSET=%d\n", len, tunnel_id, session_id, ns, nr, offset); */ + PPPDEBUG(LOG_DEBUG, ("pppol2tp: input packet, len=%"U16_F", tunnel=%"U16_F", session=%"U16_F", ns=%"U16_F", nr=%"U16_F"\n", + len, tunnel_id, session_id, ns, nr)); + + /* Control packet */ + if (hflags & PPPOL2TP_HEADERFLAG_CONTROL) { + pppol2tp_dispatch_control_packet(l2tp, addr, port, p, len, tunnel_id, session_id, ns, nr); + goto free_and_return; + } + + /* Data packet */ + if(l2tp->phase != PPPOL2TP_STATE_DATA) { + goto free_and_return; + } + if(tunnel_id != l2tp->remote_tunnel_id) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: tunnel ID mismatch, assigned=%d, received=%d\n", l2tp->remote_tunnel_id, tunnel_id)); + goto free_and_return; + } + if(session_id != l2tp->remote_session_id) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: session ID mismatch, assigned=%d, received=%d\n", l2tp->remote_session_id, session_id)); + goto free_and_return; + } + /* + * skip address & flags if necessary + * + * RFC 2661 does not specify whether the PPP frame in the L2TP payload should + * have a HDLC header or not. We handle both cases for compatibility. + */ + GETSHORT(hflags, inp); + if (hflags == 0xff03) { + pbuf_header(p, -(s16_t)2); + } + /* Dispatch the packet thereby consuming it. */ + ppp_input(l2tp->ppp, p); + return; + +packet_too_short: + PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len)); +free_and_return: + pbuf_free(p); +} + +/* L2TP Control packet entry point */ +static void pppol2tp_dispatch_control_packet(pppol2tp_pcb *l2tp, struct ip_addr *addr, u16_t port, + struct pbuf *p, u16_t len, u16_t tunnel_id, u16_t session_id, u16_t ns, u16_t nr) { + u8_t *inp; + u16_t avplen, avpflags, vendorid, attributetype, messagetype=0; + err_t err; +#if PPPOL2TP_AUTH_SUPPORT + md5_context md5_ctx; + u8_t md5_hash[16]; + u8_t challenge_id = 0; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + LWIP_UNUSED_ARG(addr); + LWIP_UNUSED_ARG(len); + LWIP_UNUSED_ARG(tunnel_id); + LWIP_UNUSED_ARG(session_id); + + l2tp->peer_nr = nr; + l2tp->peer_ns = ns; + /* printf("L2TP CTRL INPUT, ns=%d, nr=%d, len=%d\n", ns, nr, p->len); */ + + /* Handle the special case of the ICCN acknowledge */ + if (l2tp->phase == PPPOL2TP_STATE_ICCN_SENT && l2tp->peer_nr > l2tp->our_ns) { + l2tp->phase = PPPOL2TP_STATE_DATA; + } + + /* ZLB packets */ + if (p->len == 0) { + return; + } + + inp = (u8_t*)p->payload; + /* Decode AVPs */ + while (p->len > 0) { + if (p->len < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype) ) { + goto packet_too_short; + } + GETSHORT(avpflags, inp); + avplen = avpflags & PPPOL2TP_AVPHEADERFLAG_LENGTHMASK; + /* printf("AVPLEN = %d\n", avplen); */ + if (p->len < avplen || avplen < sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype)) { + goto packet_too_short; + } + GETSHORT(vendorid, inp); + GETSHORT(attributetype, inp); + avplen -= sizeof(avpflags) + sizeof(vendorid) + sizeof(attributetype); + + /* Message type must be the first AVP */ + if (messagetype == 0) { + if (attributetype != 0 || vendorid != 0 || avplen != sizeof(messagetype) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: message type must be the first AVP\n")); + return; + } + GETSHORT(messagetype, inp); + /* printf("Message type = %d\n", messagetype); */ + switch(messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + /* Only accept SCCRP packet if we sent a SCCRQ */ + if (l2tp->phase != PPPOL2TP_STATE_SCCRQ_SENT) { + goto send_zlb; + } + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + /* Only accept ICRP packet if we sent a IRCQ */ + if (l2tp->phase != PPPOL2TP_STATE_ICRQ_SENT) { + goto send_zlb; + } + break; + /* Stop Control Connection Notification */ + case PPPOL2TP_MESSAGETYPE_STOPCCN: + pppol2tp_send_zlb(l2tp, l2tp->our_ns); /* Ack the StopCCN before we switch to down state */ + if (l2tp->phase < PPPOL2TP_STATE_DATA) { + pppol2tp_abort_connect(l2tp); + } else if (l2tp->phase == PPPOL2TP_STATE_DATA) { + /* Don't disconnect here, we let the LCP Echo/Reply find the fact + * that PPP session is down. Asking the PPP stack to end the session + * require strict checking about the PPP phase to prevent endless + * disconnection loops. + */ + } + return; + default: + break; + } + goto nextavp; + } + + /* Skip proprietary L2TP extensions */ + if (vendorid != 0) { + goto skipavp; + } + + switch (messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + switch (attributetype) { + case PPPOL2TP_AVPTYPE_TUNNELID: + if (avplen != sizeof(l2tp->source_tunnel_id) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign tunnel ID length check failed\n")); + return; + } + GETSHORT(l2tp->source_tunnel_id, inp); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned tunnel ID %"U16_F"\n", l2tp->source_tunnel_id)); + goto nextavp; +#if PPPOL2TP_AUTH_SUPPORT + case PPPOL2TP_AVPTYPE_CHALLENGE: + if (avplen == 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Challenge length check failed\n")); + return; + } + if (l2tp->secret == NULL) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge from peer and no secret key available\n")); + pppol2tp_abort_connect(l2tp); + return; + } + /* Generate hash of ID, secret, challenge */ + md5_starts(&md5_ctx); + challenge_id = PPPOL2TP_MESSAGETYPE_SCCCN; + md5_update(&md5_ctx, &challenge_id, 1); + md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len); + md5_update(&md5_ctx, inp, avplen); + md5_finish(&md5_ctx, l2tp->challenge_hash); + l2tp->send_challenge = 1; + goto skipavp; + case PPPOL2TP_AVPTYPE_CHALLENGERESPONSE: + if (avplen != PPPOL2TP_AVPTYPE_CHALLENGERESPONSE_SIZE) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Challenge Response length check failed\n")); + return; + } + /* Generate hash of ID, secret, challenge */ + md5_starts(&md5_ctx); + challenge_id = PPPOL2TP_MESSAGETYPE_SCCRP; + md5_update(&md5_ctx, &challenge_id, 1); + md5_update(&md5_ctx, l2tp->secret, l2tp->secret_len); + md5_update(&md5_ctx, l2tp->secret_rv, sizeof(l2tp->secret_rv)); + md5_finish(&md5_ctx, md5_hash); + if ( memcmp(inp, md5_hash, sizeof(md5_hash)) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Received challenge response from peer and secret key do not match\n")); + pppol2tp_abort_connect(l2tp); + return; + } + goto skipavp; +#endif /* PPPOL2TP_AUTH_SUPPORT */ + default: + break; + } + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + switch (attributetype) { + case PPPOL2TP_AVPTYPE_SESSIONID: + if (avplen != sizeof(l2tp->source_session_id) ) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: AVP Assign session ID length check failed\n")); + return; + } + GETSHORT(l2tp->source_session_id, inp); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: Assigned session ID %"U16_F"\n", l2tp->source_session_id)); + goto nextavp; + default: + break; + } + break; + default: + break; + } + +skipavp: + INCPTR(avplen, inp); +nextavp: + /* printf("AVP Found, vendor=%d, attribute=%d, len=%d\n", vendorid, attributetype, avplen); */ + /* next AVP */ + if (pbuf_header(p, -avplen - sizeof(avpflags) - sizeof(vendorid) - sizeof(attributetype) ) != 0) { + return; + } + } + + switch(messagetype) { + /* Start Control Connection Reply */ + case PPPOL2TP_MESSAGETYPE_SCCRP: + do { + l2tp->remote_session_id = magic(); + } while(l2tp->remote_session_id == 0); + l2tp->tunnel_port = port; /* LNS server might have chosen its own local port */ + l2tp->icrq_retried = 0; + l2tp->phase = PPPOL2TP_STATE_ICRQ_SENT; + l2tp->our_ns++; + if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err)); + } + l2tp->our_ns++; + if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err)); + } + sys_untimeout(pppol2tp_timeout, l2tp); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + /* Incoming Call Reply */ + case PPPOL2TP_MESSAGETYPE_ICRP: + l2tp->iccn_retried = 0; + l2tp->phase = PPPOL2TP_STATE_ICCN_SENT; + l2tp->our_ns++; + l2tp->link_status_cb(l2tp->ppp, PPPOL2TP_CB_STATE_UP); /* notify upper layers */ + if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err)); + } + sys_untimeout(pppol2tp_timeout, l2tp); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + /* Unhandled packet, send ZLB ACK */ + default: + goto send_zlb; + } + return; + +send_zlb: + pppol2tp_send_zlb(l2tp, l2tp->our_ns); + return; +packet_too_short: + PPPDEBUG(LOG_DEBUG, ("pppol2tp: packet too short: %d\n", p->len)); +} + +/* L2TP Timeout handler */ +static void pppol2tp_timeout(void *arg) { + pppol2tp_pcb *l2tp = (pppol2tp_pcb*)arg; + err_t err; + u32_t retry_wait; + + PPPDEBUG(LOG_DEBUG, ("pppol2tp: timeout\n")); + + switch (l2tp->phase) { + case PPPOL2TP_STATE_SCCRQ_SENT: + /* backoff wait */ + if (l2tp->sccrq_retried < 0xff) { + l2tp->sccrq_retried++; + } + if (!l2tp->ppp->settings.persist && l2tp->sccrq_retried >= PPPOL2TP_MAXSCCRQ) { + pppol2tp_abort_connect(l2tp); + return; + } + retry_wait = LWIP_MIN(PPPOL2TP_CONTROL_TIMEOUT * l2tp->sccrq_retried, PPPOL2TP_SLOW_RETRY); + PPPDEBUG(LOG_DEBUG, ("pppol2tp: sccrq_retried=%d\n", l2tp->sccrq_retried)); + if ((err = pppol2tp_send_sccrq(l2tp)) != 0) { + l2tp->sccrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCRQ, error=%d\n", err)); + } + sys_timeout(retry_wait, pppol2tp_timeout, l2tp); + break; + + case PPPOL2TP_STATE_ICRQ_SENT: + l2tp->icrq_retried++; + if (l2tp->icrq_retried >= PPPOL2TP_MAXICRQ) { + pppol2tp_abort_connect(l2tp); + return; + } + PPPDEBUG(LOG_DEBUG, ("pppol2tp: icrq_retried=%d\n", l2tp->icrq_retried)); + if (l2tp->peer_nr <= l2tp->our_ns -1) { /* the SCCCN was not acknowledged */ + if ((err = pppol2tp_send_scccn(l2tp, l2tp->our_ns -1)) != 0) { + l2tp->icrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send SCCCN, error=%d\n", err)); + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + } + } + if ((err = pppol2tp_send_icrq(l2tp, l2tp->our_ns)) != 0) { + l2tp->icrq_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICRQ, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + + case PPPOL2TP_STATE_ICCN_SENT: + l2tp->iccn_retried++; + if (l2tp->iccn_retried >= PPPOL2TP_MAXICCN) { + pppol2tp_abort_connect(l2tp); + return; + } + PPPDEBUG(LOG_DEBUG, ("pppol2tp: iccn_retried=%d\n", l2tp->iccn_retried)); + if ((err = pppol2tp_send_iccn(l2tp, l2tp->our_ns)) != 0) { + l2tp->iccn_retried--; + PPPDEBUG(LOG_DEBUG, ("pppol2tp: failed to send ICCN, error=%d\n", err)); + } + sys_timeout(PPPOL2TP_CONTROL_TIMEOUT, pppol2tp_timeout, l2tp); + break; + + default: + return; /* all done, work in peace */ + } +} + +/* Connection attempt aborted */ +static void pppol2tp_abort_connect(pppol2tp_pcb *l2tp) { + PPPDEBUG(LOG_DEBUG, ("pppol2tp: could not establish connection\n")); + pppol2tp_clear(l2tp); + l2tp->link_status_cb(l2tp->ppp, PPPOL2TP_CB_STATE_FAILED); /* notify upper layers */ +} + +/* Reset L2TP control block to its initial state */ +static void pppol2tp_clear(pppol2tp_pcb *l2tp) { + /* stop any timer */ + sys_untimeout(pppol2tp_timeout, l2tp); + l2tp->phase = PPPOL2TP_STATE_INITIAL; + l2tp->tunnel_port = l2tp->remote_port; + l2tp->our_ns = 0; + l2tp->peer_nr = 0; + l2tp->peer_ns = 0; + l2tp->source_tunnel_id = 0; + l2tp->remote_tunnel_id = 0; + l2tp->source_session_id = 0; + l2tp->remote_session_id = 0; + /* l2tp->*_retried are cleared when used */ +} + +/* Initiate a new tunnel */ +static err_t pppol2tp_send_sccrq(pppol2tp_pcb *l2tp) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +8 +10 +10 +6+sizeof(PPPOL2TP_HOSTNAME)-1 +6+sizeof(PPPOL2TP_VENDORNAME)-1 +8 +8; +#if PPPOL2TP_AUTH_SUPPORT + if (l2tp->secret != NULL) { + len += 6 + sizeof(l2tp->secret_rv); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(0, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(0, p); /* NS Sequence number - to peer */ + PUTSHORT(0, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCRQ, p); /* Attribute value: Message type: SCCRQ */ + + /* AVP - L2TP Version */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_VERSION, p); /* Attribute type: Version */ + PUTSHORT(PPPOL2TP_VERSION, p); /* Attribute value: L2TP Version */ + + /* AVP - Framing capabilities */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGCAPABILITIES, p); /* Attribute type: Framing capabilities */ + PUTLONG(PPPOL2TP_FRAMINGCAPABILITIES, p); /* Attribute value: Framing capabilities */ + + /* AVP - Bearer capabilities */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_BEARERCAPABILITIES, p); /* Attribute type: Bearer capabilities */ + PUTLONG(PPPOL2TP_BEARERCAPABILITIES, p); /* Attribute value: Bearer capabilities */ + + /* AVP - Host name */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6+sizeof(PPPOL2TP_HOSTNAME)-1, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_HOSTNAME, p); /* Attribute type: Hostname */ + MEMCPY(p, PPPOL2TP_HOSTNAME, sizeof(PPPOL2TP_HOSTNAME)-1); /* Attribute value: Hostname */ + INCPTR(sizeof(PPPOL2TP_HOSTNAME)-1, p); + + /* AVP - Vendor name */ + PUTSHORT(6+sizeof(PPPOL2TP_VENDORNAME)-1, p); /* len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_VENDORNAME, p); /* Attribute type: Vendor name */ + MEMCPY(p, PPPOL2TP_VENDORNAME, sizeof(PPPOL2TP_VENDORNAME)-1); /* Attribute value: Vendor name */ + INCPTR(sizeof(PPPOL2TP_VENDORNAME)-1, p); + + /* AVP - Assign tunnel ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */ + PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */ + + /* AVP - Receive window size */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_RECEIVEWINDOWSIZE, p); /* Attribute type: Receive window size */ + PUTSHORT(PPPOL2TP_RECEIVEWINDOWSIZE, p); /* Attribute value: Receive window size */ + +#if PPPOL2TP_AUTH_SUPPORT + /* AVP - Challenge */ + if (l2tp->secret != NULL) { + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->secret_rv), p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGE, p); /* Attribute type: Challenge */ + MEMCPY(p, l2tp->secret_rv, sizeof(l2tp->secret_rv)); /* Attribute value: Random vector */ + INCPTR(sizeof(l2tp->secret_rv), p); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +/* Complete tunnel establishment */ +static err_t pppol2tp_send_scccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8; +#if PPPOL2TP_AUTH_SUPPORT + if (l2tp->send_challenge) { + len += 6 + sizeof(l2tp->challenge_hash); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_SCCCN, p); /* Attribute value: Message type: SCCCN */ + +#if PPPOL2TP_AUTH_SUPPORT + /* AVP - Challenge response */ + if (l2tp->send_challenge) { + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 6 + sizeof(l2tp->challenge_hash), p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CHALLENGERESPONSE, p); /* Attribute type: Challenge response */ + MEMCPY(p, l2tp->challenge_hash, sizeof(l2tp->challenge_hash)); /* Attribute value: Computed challenge */ + INCPTR(sizeof(l2tp->challenge_hash), p); + } +#endif /* PPPOL2TP_AUTH_SUPPORT */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +/* Initiate a new session */ +static err_t pppol2tp_send_icrq(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + u32_t serialnumber; + + /* calculate UDP packet length */ + len = 12 +8 +8 +10; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_ICRQ, p); /* Attribute value: Message type: ICRQ */ + + /* AVP - Assign session ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_SESSIONID, p); /* Attribute type: Session ID */ + PUTSHORT(l2tp->remote_session_id, p); /* Attribute value: Session ID */ + + /* AVP - Call Serial Number */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_CALLSERIALNUMBER, p); /* Attribute type: Serial number */ + serialnumber = magic(); + PUTLONG(serialnumber, p); /* Attribute value: Serial number */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +/* Complete tunnel establishment */ +static err_t pppol2tp_send_iccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +10 +10; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(l2tp->source_session_id, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_ICCN, p); /* Attribute value: Message type: ICCN */ + + /* AVP - Framing type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_FRAMINGTYPE, p); /* Attribute type: Framing type */ + PUTLONG(PPPOL2TP_FRAMINGTYPE, p); /* Attribute value: Framing type */ + + /* AVP - TX Connect speed */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 10, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TXCONNECTSPEED, p); /* Attribute type: TX Connect speed */ + PUTLONG(PPPOL2TP_TXCONNECTSPEED, p); /* Attribute value: TX Connect speed */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +/* Send a ZLB ACK packet */ +static err_t pppol2tp_send_zlb(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +/* Send a StopCCN packet */ +static err_t pppol2tp_send_stopccn(pppol2tp_pcb *l2tp, u16_t ns) { + struct pbuf *pb; + u8_t *p; + u16_t len; + + /* calculate UDP packet length */ + len = 12 +8 +8 +8; + + /* allocate a buffer */ + pb = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (pb == NULL) { + return ERR_MEM; + } + LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len); + + p = (u8_t*)pb->payload; + /* fill in pkt */ + /* L2TP control header */ + PUTSHORT(PPPOL2TP_HEADERFLAG_CONTROL_MANDATORY, p); + PUTSHORT(len, p); /* Length */ + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(0, p); /* Session Id */ + PUTSHORT(ns, p); /* NS Sequence number - to peer */ + PUTSHORT(l2tp->peer_ns+1, p); /* NR Sequence number - expected for peer */ + + /* AVP - Message type */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_MESSAGE, p); /* Attribute type: Message Type */ + PUTSHORT(PPPOL2TP_MESSAGETYPE_STOPCCN, p); /* Attribute value: Message type: StopCCN */ + + /* AVP - Assign tunnel ID */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_TUNNELID, p); /* Attribute type: Tunnel ID */ + PUTSHORT(l2tp->remote_tunnel_id, p); /* Attribute value: Tunnel ID */ + + /* AVP - Result code */ + PUTSHORT(PPPOL2TP_AVPHEADERFLAG_MANDATORY + 8, p); /* Mandatory flag + len field */ + PUTSHORT(0, p); /* Vendor ID */ + PUTSHORT(PPPOL2TP_AVPTYPE_RESULTCODE, p); /* Attribute type: Result code */ + PUTSHORT(PPPOL2TP_RESULTCODE, p); /* Attribute value: Result code */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +err_t pppol2tp_xmit(pppol2tp_pcb *l2tp, struct pbuf *pb) { + u8_t *p; + + /* are we ready to process data yet? */ + if (l2tp->phase < PPPOL2TP_STATE_DATA) { + pbuf_free(pb); + return ERR_CONN; + } + + /* make room for L2TP header - should not fail */ + if (pbuf_header(pb, PPPOL2TP_OUTPUT_DATA_HEADER_LEN) != 0) { + /* bail out */ + PPPDEBUG(LOG_ERR, ("pppol2tp: pppol2tp_pcb: could not allocate room for L2TP header\n")); + LINK_STATS_INC(link.lenerr); + pbuf_free(pb); + return ERR_BUF; + } + + p = (u8_t*)pb->payload; + PUTSHORT(PPPOL2TP_HEADERFLAG_DATA_MANDATORY, p); + PUTSHORT(l2tp->source_tunnel_id, p); /* Tunnel Id */ + PUTSHORT(l2tp->source_session_id, p); /* Session Id */ + + if(l2tp->netif) { + udp_sendto_if(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port, l2tp->netif); + } else { + udp_sendto(l2tp->udp, pb, &l2tp->remote_ip, l2tp->remote_port); + } + pbuf_free(pb); + return ERR_OK; +} + +#endif /* PPP_SUPPORT && PPPOL2TP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/upap.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/upap.c new file mode 100644 index 0000000..14f80b6 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/upap.c @@ -0,0 +1,682 @@ +/* + * upap.c - User/Password Authentication Protocol. + * + * Copyright (c) 1984-2000 Carnegie Mellon University. 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 "Carnegie Mellon University" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For permission or any legal + * details, please contact + * Office of Technology Transfer + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, PA 15213-3890 + * (412) 268-4387, fax: (412) 268-7395 + * tech-transfer@andrew.cmu.edu + * + * 4. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Computing Services + * at Carnegie Mellon University (http://www.cmu.edu/computing/)." + * + * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +/* + * TODO: + */ + +#if 0 /* UNUSED */ +#include +#include +#endif /* UNUSED */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/upap.h" + +#if PPP_OPTIONS +/* + * Command-line options. + */ +static option_t pap_option_list[] = { + { "hide-password", o_bool, &hide_password, + "Don't output passwords to log", OPT_PRIO | 1 }, + { "show-password", o_bool, &hide_password, + "Show password string in debug log messages", OPT_PRIOSUB | 0 }, + + { "pap-restart", o_int, &upap[0].us_timeouttime, + "Set retransmit timeout for PAP", OPT_PRIO }, + { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, + "Set max number of transmissions for auth-reqs", OPT_PRIO }, + { "pap-timeout", o_int, &upap[0].us_reqtimeout, + "Set time limit for peer PAP authentication", OPT_PRIO }, + + { NULL } +}; +#endif /* PPP_OPTIONS */ + +/* + * Protocol entry points. + */ +static void upap_init(ppp_pcb *pcb); +static void upap_lowerup(ppp_pcb *pcb); +static void upap_lowerdown(ppp_pcb *pcb); +static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l); +static void upap_protrej(ppp_pcb *pcb); +#if PRINTPKT_SUPPORT +static int upap_printpkt(u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); +#endif /* PRINTPKT_SUPPORT */ + +const struct protent pap_protent = { + PPP_PAP, + upap_init, + upap_input, + upap_protrej, + upap_lowerup, + upap_lowerdown, + NULL, + NULL, +#if PRINTPKT_SUPPORT + upap_printpkt, +#endif /* PRINTPKT_SUPPORT */ + NULL, + 1, +#if PRINTPKT_SUPPORT + "PAP", + NULL, +#endif /* PRINTPKT_SUPPORT */ +#if PPP_OPTIONS + pap_option_list, + NULL, +#endif /* PPP_OPTIONS */ +#if DEMAND_SUPPORT + NULL, + NULL +#endif /* DEMAND_SUPPORT */ +}; + +static void upap_timeout(void *arg); +#if PPP_SERVER +static void upap_reqtimeout(void *arg); +static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len); +#endif /* PPP_SERVER */ +static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len); +static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len); +static void upap_sauthreq(ppp_pcb *pcb); +#if PPP_SERVER +static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen); +#endif /* PPP_SERVER */ + + +/* + * upap_init - Initialize a UPAP unit. + */ +static void upap_init(ppp_pcb *pcb) { + pcb->upap.us_user = NULL; + pcb->upap.us_userlen = 0; + pcb->upap.us_passwd = NULL; + pcb->upap.us_passwdlen = 0; + pcb->upap.us_clientstate = UPAPCS_INITIAL; +#if PPP_SERVER + pcb->upap.us_serverstate = UPAPSS_INITIAL; +#endif /* PPP_SERVER */ + pcb->upap.us_id = 0; +} + + +/* + * upap_authwithpeer - Authenticate us with our peer (start client). + * + * Set new state and send authenticate's. + */ +void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) { + + if(!user || !password) + return; + + /* Save the username and password we're given */ + pcb->upap.us_user = user; + pcb->upap.us_userlen = LWIP_MIN(strlen(user), 0xff); + pcb->upap.us_passwd = password; + pcb->upap.us_passwdlen = LWIP_MIN(strlen(password), 0xff); + pcb->upap.us_transmits = 0; + + /* Lower layer up yet? */ + if (pcb->upap.us_clientstate == UPAPCS_INITIAL || + pcb->upap.us_clientstate == UPAPCS_PENDING) { + pcb->upap.us_clientstate = UPAPCS_PENDING; + return; + } + + upap_sauthreq(pcb); /* Start protocol */ +} + +#if PPP_SERVER +/* + * upap_authpeer - Authenticate our peer (start server). + * + * Set new state. + */ +void upap_authpeer(ppp_pcb *pcb) { + + /* Lower layer up yet? */ + if (pcb->upap.us_serverstate == UPAPSS_INITIAL || + pcb->upap.us_serverstate == UPAPSS_PENDING) { + pcb->upap.us_serverstate = UPAPSS_PENDING; + return; + } + + pcb->upap.us_serverstate = UPAPSS_LISTEN; + if (pcb->settings.pap_req_timeout > 0) + TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); +} +#endif /* PPP_SERVER */ + +/* + * upap_timeout - Retransmission timer for sending auth-reqs expired. + */ +static void upap_timeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) + return; + + if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) { + /* give up in disgust */ + ppp_error("No response to PAP authenticate-requests"); + pcb->upap.us_clientstate = UPAPCS_BADAUTH; + auth_withpeer_fail(pcb, PPP_PAP); + return; + } + + upap_sauthreq(pcb); /* Send Authenticate-Request */ +} + + +#if PPP_SERVER +/* + * upap_reqtimeout - Give up waiting for the peer to send an auth-req. + */ +static void upap_reqtimeout(void *arg) { + ppp_pcb *pcb = (ppp_pcb*)arg; + + if (pcb->upap.us_serverstate != UPAPSS_LISTEN) + return; /* huh?? */ + + auth_peer_fail(pcb, PPP_PAP); + pcb->upap.us_serverstate = UPAPSS_BADAUTH; +} +#endif /* PPP_SERVER */ + + +/* + * upap_lowerup - The lower layer is up. + * + * Start authenticating if pending. + */ +static void upap_lowerup(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_INITIAL) + pcb->upap.us_clientstate = UPAPCS_CLOSED; + else if (pcb->upap.us_clientstate == UPAPCS_PENDING) { + upap_sauthreq(pcb); /* send an auth-request */ + } + +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_INITIAL) + pcb->upap.us_serverstate = UPAPSS_CLOSED; + else if (pcb->upap.us_serverstate == UPAPSS_PENDING) { + pcb->upap.us_serverstate = UPAPSS_LISTEN; + if (pcb->settings.pap_req_timeout > 0) + TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); + } +#endif /* PPP_SERVER */ +} + + +/* + * upap_lowerdown - The lower layer is down. + * + * Cancel all timeouts. + */ +static void upap_lowerdown(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ + UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */ +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0) + UNTIMEOUT(upap_reqtimeout, pcb); +#endif /* PPP_SERVER */ + + pcb->upap.us_clientstate = UPAPCS_INITIAL; +#if PPP_SERVER + pcb->upap.us_serverstate = UPAPSS_INITIAL; +#endif /* PPP_SERVER */ +} + + +/* + * upap_protrej - Peer doesn't speak this protocol. + * + * This shouldn't happen. In any case, pretend lower layer went down. + */ +static void upap_protrej(ppp_pcb *pcb) { + + if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) { + ppp_error("PAP authentication failed due to protocol-reject"); + auth_withpeer_fail(pcb, PPP_PAP); + } +#if PPP_SERVER + if (pcb->upap.us_serverstate == UPAPSS_LISTEN) { + ppp_error("PAP authentication of peer failed (protocol-reject)"); + auth_peer_fail(pcb, PPP_PAP); + } +#endif /* PPP_SERVER */ + upap_lowerdown(pcb); +} + + +/* + * upap_input - Input UPAP packet. + */ +static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) { + u_char *inp; + u_char code, id; + int len; + + /* + * Parse header (code, id and length). + * If packet too short, drop it. + */ + inp = inpacket; + if (l < UPAP_HEADERLEN) { + UPAPDEBUG(("pap_input: rcvd short header.")); + return; + } + GETCHAR(code, inp); + GETCHAR(id, inp); + GETSHORT(len, inp); + if (len < UPAP_HEADERLEN) { + UPAPDEBUG(("pap_input: rcvd illegal length.")); + return; + } + if (len > l) { + UPAPDEBUG(("pap_input: rcvd short packet.")); + return; + } + len -= UPAP_HEADERLEN; + + /* + * Action depends on code. + */ + switch (code) { + case UPAP_AUTHREQ: +#if PPP_SERVER + upap_rauthreq(pcb, inp, id, len); +#endif /* PPP_SERVER */ + break; + + case UPAP_AUTHACK: + upap_rauthack(pcb, inp, id, len); + break; + + case UPAP_AUTHNAK: + upap_rauthnak(pcb, inp, id, len); + break; + + default: /* XXX Need code reject */ + break; + } +} + +#if PPP_SERVER +/* + * upap_rauth - Receive Authenticate. + */ +static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char ruserlen, rpasswdlen; + char *ruser; +#if 0 + char *rpasswd; +#endif + char rhostname[256]; + int retcode; + const char *msg; + int msglen; + + if (pcb->upap.us_serverstate < UPAPSS_LISTEN) + return; + + /* + * If we receive a duplicate authenticate-request, we are + * supposed to return the same status as for the first request. + */ + if (pcb->upap.us_serverstate == UPAPSS_OPEN) { + upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ + return; + } + if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) { + upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ + return; + } + + /* + * Parse user/passwd. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + GETCHAR(ruserlen, inp); + len -= sizeof (u_char) + ruserlen + sizeof (u_char); + if (len < 0) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + ruser = (char *) inp; + INCPTR(ruserlen, inp); + GETCHAR(rpasswdlen, inp); + if (len < rpasswdlen) { + UPAPDEBUG(("pap_rauth: rcvd short packet.")); + return; + } + + /* FIXME: we need a way to check peer secret */ +#if 0 + rpasswd = (char *) inp; + + /* + * Check the username and password given. + */ + retcode = check_passwd(pcb->upap.us_unit, ruser, ruserlen, rpasswd, + rpasswdlen, &msg); + BZERO(rpasswd, rpasswdlen); + + /* + * Check remote number authorization. A plugin may have filled in + * the remote number or added an allowed number, and rather than + * return an authenticate failure, is leaving it for us to verify. + */ + if (retcode == UPAP_AUTHACK) { + if (!auth_number()) { + /* We do not want to leak info about the pap result. */ + retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */ + warn("calling number %q is not authorized", remote_number); + } + } + + msglen = strlen(msg); + if (msglen > 255) + msglen = 255; +#else + /* only here to clean compiler warnings */ + retcode = UPAP_AUTHNAK; + msg = NULL; + msglen = 0; +#endif /* 0 */ + + upap_sresp(pcb, retcode, id, msg, msglen); + + /* Null terminate and clean remote name. */ + ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser); + + if (retcode == UPAP_AUTHACK) { + pcb->upap.us_serverstate = UPAPSS_OPEN; + ppp_notice("PAP peer authentication succeeded for %q", rhostname); + auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen); + } else { + pcb->upap.us_serverstate = UPAPSS_BADAUTH; + ppp_warn("PAP peer authentication failed for %q", rhostname); + auth_peer_fail(pcb, PPP_PAP); + } + + if (pcb->settings.pap_req_timeout > 0) + UNTIMEOUT(upap_reqtimeout, pcb); +} +#endif /* PPP_SERVER */ + +/* + * upap_rauthack - Receive Authenticate-Ack. + */ +static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char msglen; + char *msg; + LWIP_UNUSED_ARG(id); + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauthack: ignoring missing msg-length.")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(("pap_rauthack: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + pcb->upap.us_clientstate = UPAPCS_OPEN; + + auth_withpeer_success(pcb, PPP_PAP, 0); +} + + +/* + * upap_rauthnak - Receive Authenticate-Nak. + */ +static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) { + u_char msglen; + char *msg; + LWIP_UNUSED_ARG(id); + + if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ + return; + + /* + * Parse message. + */ + if (len < 1) { + UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length.")); + } else { + GETCHAR(msglen, inp); + if (msglen > 0) { + len -= sizeof (u_char); + if (len < msglen) { + UPAPDEBUG(("pap_rauthnak: rcvd short packet.")); + return; + } + msg = (char *) inp; + PRINTMSG(msg, msglen); + } + } + + pcb->upap.us_clientstate = UPAPCS_BADAUTH; + + ppp_error("PAP authentication failed"); + auth_withpeer_fail(pcb, PPP_PAP); +} + + +/* + * upap_sauthreq - Send an Authenticate-Request. + */ +static void upap_sauthreq(ppp_pcb *pcb) { + struct pbuf *p; + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + + pcb->upap.us_userlen + pcb->upap.us_passwdlen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(UPAP_AUTHREQ, outp); + PUTCHAR(++pcb->upap.us_id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(pcb->upap.us_userlen, outp); + MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen); + INCPTR(pcb->upap.us_userlen, outp); + PUTCHAR(pcb->upap.us_passwdlen, outp); + MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen); + + ppp_write(pcb, p); + + TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time); + ++pcb->upap.us_transmits; + pcb->upap.us_clientstate = UPAPCS_AUTHREQ; +} + +#if PPP_SERVER +/* + * upap_sresp - Send a response (ack or nak). + */ +static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) { + struct pbuf *p; + u_char *outp; + int outlen; + + outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; + p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE); + if(NULL == p) + return; + if(p->tot_len != p->len) { + pbuf_free(p); + return; + } + + outp = (u_char*)p->payload; + MAKEHEADER(outp, PPP_PAP); + + PUTCHAR(code, outp); + PUTCHAR(id, outp); + PUTSHORT(outlen, outp); + PUTCHAR(msglen, outp); + MEMCPY(outp, msg, msglen); + + ppp_write(pcb, p); +} +#endif /* PPP_SERVER */ + +#if PRINTPKT_SUPPORT +/* + * upap_printpkt - print the contents of a PAP packet. + */ +static const char *upap_codenames[] = { + "AuthReq", "AuthAck", "AuthNak" +}; + +static int upap_printpkt(u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { + int code, id, len; + int mlen, ulen, wlen; + char *user, *pwd, *msg; + u_char *pstart; + + if (plen < UPAP_HEADERLEN) + return 0; + pstart = p; + GETCHAR(code, p); + GETCHAR(id, p); + GETSHORT(len, p); + if (len < UPAP_HEADERLEN || len > plen) + return 0; + + if (code >= 1 && code <= (int)sizeof(upap_codenames) / (int)sizeof(char *)) + printer(arg, " %s", upap_codenames[code-1]); + else + printer(arg, " code=0x%x", code); + printer(arg, " id=0x%x", id); + len -= UPAP_HEADERLEN; + switch (code) { + case UPAP_AUTHREQ: + if (len < 1) + break; + ulen = p[0]; + if (len < ulen + 2) + break; + wlen = p[ulen + 1]; + if (len < ulen + wlen + 2) + break; + user = (char *) (p + 1); + pwd = (char *) (p + ulen + 2); + p += ulen + wlen + 2; + len -= ulen + wlen + 2; + printer(arg, " user="); + ppp_print_string(user, ulen, printer, arg); + printer(arg, " password="); +/* FIXME: require ppp_pcb struct as printpkt() argument */ +#if 0 + if (!pcb->settings.hide_password) +#endif + ppp_print_string(pwd, wlen, printer, arg); +#if 0 + else + printer(arg, ""); +#endif + break; + case UPAP_AUTHACK: + case UPAP_AUTHNAK: + if (len < 1) + break; + mlen = p[0]; + if (len < mlen + 1) + break; + msg = (char *) (p + 1); + p += mlen + 1; + len -= mlen + 1; + printer(arg, " "); + ppp_print_string(msg, mlen, printer, arg); + break; + default: + break; + } + + /* print the rest of the bytes in the packet */ + for (; len > 0; --len) { + GETCHAR(code, p); + printer(arg, " %.2x", code); + } + + return p - pstart; +} +#endif /* PRINTPKT_SUPPORT */ + +#endif /* PPP_SUPPORT && PAP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/utils.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/utils.c new file mode 100644 index 0000000..ecd305a --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/utils.c @@ -0,0 +1,964 @@ +/* + * utils.c - various utility functions used in pppd. + * + * Copyright (c) 1999-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 3. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Paul Mackerras + * ". + * + * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#if 0 /* UNUSED */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SVR4 +#include +#endif +#endif /* UNUSED */ + +#include /* isdigit() */ + +#include "netif/ppp/ppp_impl.h" + +#include "netif/ppp/fsm.h" +#include "netif/ppp/lcp.h" + +#if defined(SUNOS4) +extern char *strerror(); +#endif + +static void ppp_logit(int level, const char *fmt, va_list args); +static void ppp_log_write(int level, char *buf); +#if PRINTPKT_SUPPORT +static void ppp_vslp_printer(void *arg, const char *fmt, ...); +static void ppp_format_packet(u_char *p, int len, + void (*printer) (void *, const char *, ...), void *arg); + +struct buffer_info { + char *ptr; + int len; +}; +#endif /* PRINTPKT_SUPPORT */ + +/* + * ppp_strlcpy - like strcpy/strncpy, doesn't overflow destination buffer, + * always leaves destination null-terminated (for len > 0). + */ +size_t ppp_strlcpy(char *dest, const char *src, size_t len) { + size_t ret = strlen(src); + + if (len != 0) { + if (ret < len) + strcpy(dest, src); + else { + strncpy(dest, src, len - 1); + dest[len-1] = 0; + } + } + return ret; +} + +/* + * ppp_strlcat - like strcat/strncat, doesn't overflow destination buffer, + * always leaves destination null-terminated (for len > 0). + */ +size_t ppp_strlcat(char *dest, const char *src, size_t len) { + size_t dlen = strlen(dest); + + return dlen + ppp_strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0)); +} + + +/* + * ppp_slprintf - format a message into a buffer. Like sprintf except we + * also specify the length of the output buffer, and we handle + * %m (error message), %v (visible string), + * %q (quoted string), %t (current time) and %I (IP address) formats. + * Doesn't do floating-point formats. + * Returns the number of chars put into buf. + */ +int ppp_slprintf(char *buf, int buflen, const char *fmt, ...) { + va_list args; + int n; + + va_start(args, fmt); + n = ppp_vslprintf(buf, buflen, fmt, args); + va_end(args); + return n; +} + +/* + * ppp_vslprintf - like ppp_slprintf, takes a va_list instead of a list of args. + */ +#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) + +int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) { + int c, i, n; + int width, prec, fillch; + int base, len, neg, quoted; + unsigned long val = 0; + const char *f; + char *str, *buf0; + unsigned char *p; + char num[32]; +#if 0 /* need port */ + time_t t; +#endif /* need port */ + u32_t ip; + static char hexchars[] = "0123456789abcdef"; +#if PRINTPKT_SUPPORT + struct buffer_info bufinfo; +#endif /* PRINTPKT_SUPPORT */ + + buf0 = buf; + --buflen; + while (buflen > 0) { + for (f = fmt; *f != '%' && *f != 0; ++f) + ; + if (f > fmt) { + len = f - fmt; + if (len > buflen) + len = buflen; + memcpy(buf, fmt, len); + buf += len; + buflen -= len; + fmt = f; + } + if (*fmt == 0) + break; + c = *++fmt; + width = 0; + prec = -1; + fillch = ' '; + if (c == '0') { + fillch = '0'; + c = *++fmt; + } + if (c == '*') { + width = va_arg(args, int); + c = *++fmt; + } else { + while (isdigit(c)) { + width = width * 10 + c - '0'; + c = *++fmt; + } + } + if (c == '.') { + c = *++fmt; + if (c == '*') { + prec = va_arg(args, int); + c = *++fmt; + } else { + prec = 0; + while (isdigit(c)) { + prec = prec * 10 + c - '0'; + c = *++fmt; + } + } + } + str = 0; + base = 0; + neg = 0; + ++fmt; + switch (c) { + case 'l': + c = *fmt++; + switch (c) { + case 'd': + val = va_arg(args, long); + if ((long)val < 0) { + neg = 1; + val = (unsigned long)-val; + } + base = 10; + break; + case 'u': + val = va_arg(args, unsigned long); + base = 10; + break; + default: + OUTCHAR('%'); + OUTCHAR('l'); + --fmt; /* so %lz outputs %lz etc. */ + continue; + } + break; + case 'd': + i = va_arg(args, int); + if (i < 0) { + neg = 1; + val = -i; + } else + val = i; + base = 10; + break; + case 'u': + val = va_arg(args, unsigned int); + base = 10; + break; + case 'o': + val = va_arg(args, unsigned int); + base = 8; + break; + case 'x': + case 'X': + val = va_arg(args, unsigned int); + base = 16; + break; + case 'p': + val = (unsigned long) va_arg(args, void *); + base = 16; + neg = 2; + break; + case 's': + str = va_arg(args, char *); + break; + case 'c': + num[0] = va_arg(args, int); + num[1] = 0; + str = num; + break; +#if 0 /* do we always have strerror() in embedded ? */ + case 'm': + str = strerror(errno); + break; +#endif /* do we always have strerror() in embedded ? */ + case 'I': + ip = va_arg(args, u32_t); + ip = ntohl(ip); + ppp_slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff, + (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); + str = num; + break; +#if 0 /* need port */ + case 't': + time(&t); + str = ctime(&t); + str += 4; /* chop off the day name */ + str[15] = 0; /* chop off year and newline */ + break; +#endif /* need port */ + case 'v': /* "visible" string */ + case 'q': /* quoted string */ + quoted = c == 'q'; + p = va_arg(args, unsigned char *); + if (p == NULL) + p = (unsigned char *)""; + if (fillch == '0' && prec >= 0) { + n = prec; + } else { + n = strlen((char *)p); + if (prec >= 0 && n > prec) + n = prec; + } + while (n > 0 && buflen > 0) { + c = *p++; + --n; + if (!quoted && c >= 0x80) { + OUTCHAR('M'); + OUTCHAR('-'); + c -= 0x80; + } + if (quoted && (c == '"' || c == '\\')) + OUTCHAR('\\'); + if (c < 0x20 || (0x7f <= c && c < 0xa0)) { + if (quoted) { + OUTCHAR('\\'); + switch (c) { + case '\t': OUTCHAR('t'); break; + case '\n': OUTCHAR('n'); break; + case '\b': OUTCHAR('b'); break; + case '\f': OUTCHAR('f'); break; + default: + OUTCHAR('x'); + OUTCHAR(hexchars[c >> 4]); + OUTCHAR(hexchars[c & 0xf]); + } + } else { + if (c == '\t') + OUTCHAR(c); + else { + OUTCHAR('^'); + OUTCHAR(c ^ 0x40); + } + } + } else + OUTCHAR(c); + } + continue; +#if PRINTPKT_SUPPORT + case 'P': /* print PPP packet */ + bufinfo.ptr = buf; + bufinfo.len = buflen + 1; + p = va_arg(args, unsigned char *); + n = va_arg(args, int); + ppp_format_packet(p, n, ppp_vslp_printer, &bufinfo); + buf = bufinfo.ptr; + buflen = bufinfo.len - 1; + continue; +#endif /* PRINTPKT_SUPPORT */ + case 'B': + p = va_arg(args, unsigned char *); + for (n = prec; n > 0; --n) { + c = *p++; + if (fillch == ' ') + OUTCHAR(' '); + OUTCHAR(hexchars[(c >> 4) & 0xf]); + OUTCHAR(hexchars[c & 0xf]); + } + continue; + default: + *buf++ = '%'; + if (c != '%') + --fmt; /* so %z outputs %z etc. */ + --buflen; + continue; + } + if (base != 0) { + str = num + sizeof(num); + *--str = 0; + while (str > num + neg) { + *--str = hexchars[val % base]; + val = val / base; + if (--prec <= 0 && val == 0) + break; + } + switch (neg) { + case 1: + *--str = '-'; + break; + case 2: + *--str = 'x'; + *--str = '0'; + break; + default: + break; + } + len = num + sizeof(num) - 1 - str; + } else { + len = strlen(str); + if (prec >= 0 && len > prec) + len = prec; + } + if (width > 0) { + if (width > buflen) + width = buflen; + if ((n = width - len) > 0) { + buflen -= n; + for (; n > 0; --n) + *buf++ = fillch; + } + } + if (len > buflen) + len = buflen; + memcpy(buf, str, len); + buf += len; + buflen -= len; + } + *buf = 0; + return buf - buf0; +} + +#if PRINTPKT_SUPPORT +/* + * vslp_printer - used in processing a %P format + */ +static void ppp_vslp_printer(void *arg, const char *fmt, ...) { + int n; + va_list pvar; + struct buffer_info *bi; + + va_start(pvar, fmt); + bi = (struct buffer_info *) arg; + n = ppp_vslprintf(bi->ptr, bi->len, fmt, pvar); + va_end(pvar); + + bi->ptr += n; + bi->len -= n; +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * log_packet - format a packet and log it. + */ + +void +log_packet(p, len, prefix, level) + u_char *p; + int len; + char *prefix; + int level; +{ + init_pr_log(prefix, level); + ppp_format_packet(p, len, pr_log, &level); + end_pr_log(); +} +#endif /* UNUSED */ + +#if PRINTPKT_SUPPORT +/* + * ppp_format_packet - make a readable representation of a packet, + * calling `printer(arg, format, ...)' to output it. + */ +static void ppp_format_packet(u_char *p, int len, + void (*printer) (void *, const char *, ...), void *arg) { + int i, n; + u_short proto; + const struct protent *protp; + + if (len >= 2) { + GETSHORT(proto, p); + len -= 2; + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (proto == protp->protocol) + break; + if (protp != NULL) { + printer(arg, "[%s", protp->name); + n = (*protp->printpkt)(p, len, printer, arg); + printer(arg, "]"); + p += n; + len -= n; + } else { + for (i = 0; (protp = protocols[i]) != NULL; ++i) + if (proto == (protp->protocol & ~0x8000)) + break; + if (protp != 0 && protp->data_name != 0) { + printer(arg, "[%s data]", protp->data_name); + if (len > 8) + printer(arg, "%.8B ...", p); + else + printer(arg, "%.*B", len, p); + len = 0; + } else + printer(arg, "[proto=0x%x]", proto); + } + } + + if (len > 32) + printer(arg, "%.32B ...", p); + else + printer(arg, "%.*B", len, p); +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* UNUSED */ +/* + * init_pr_log, end_pr_log - initialize and finish use of pr_log. + */ + +static char line[256]; /* line to be logged accumulated here */ +static char *linep; /* current pointer within line */ +static int llevel; /* level for logging */ + +void +init_pr_log(prefix, level) + const char *prefix; + int level; +{ + linep = line; + if (prefix != NULL) { + ppp_strlcpy(line, prefix, sizeof(line)); + linep = line + strlen(line); + } + llevel = level; +} + +void +end_pr_log() +{ + if (linep != line) { + *linep = 0; + ppp_log_write(llevel, line); + } +} + +/* + * pr_log - printer routine for outputting to log + */ +void +pr_log (void *arg, const char *fmt, ...) +{ + int l, n; + va_list pvar; + char *p, *eol; + char buf[256]; + + va_start(pvar, fmt); + n = ppp_vslprintf(buf, sizeof(buf), fmt, pvar); + va_end(pvar); + + p = buf; + eol = strchr(buf, '\n'); + if (linep != line) { + l = (eol == NULL)? n: eol - buf; + if (linep + l < line + sizeof(line)) { + if (l > 0) { + memcpy(linep, buf, l); + linep += l; + } + if (eol == NULL) + return; + p = eol + 1; + eol = strchr(p, '\n'); + } + *linep = 0; + ppp_log_write(llevel, line); + linep = line; + } + + while (eol != NULL) { + *eol = 0; + ppp_log_write(llevel, p); + p = eol + 1; + eol = strchr(p, '\n'); + } + + /* assumes sizeof(buf) <= sizeof(line) */ + l = buf + n - p; + if (l > 0) { + memcpy(line, p, n); + linep = line + l; + } +} +#endif /* UNUSED */ + +/* + * ppp_print_string - print a readable representation of a string using + * printer. + */ +void ppp_print_string(char *p, int len, void (*printer) (void *, const char *, ...), void *arg) { + int c; + + printer(arg, "\""); + for (; len > 0; --len) { + c = *p++; + if (' ' <= c && c <= '~') { + if (c == '\\' || c == '"') + printer(arg, "\\"); + printer(arg, "%c", c); + } else { + switch (c) { + case '\n': + printer(arg, "\\n"); + break; + case '\r': + printer(arg, "\\r"); + break; + case '\t': + printer(arg, "\\t"); + break; + default: + printer(arg, "\\%.3o", c); + /* no break */ + } + } + } + printer(arg, "\""); +} + +/* + * ppp_logit - does the hard work for fatal et al. + */ +static void ppp_logit(int level, const char *fmt, va_list args) { + char buf[1024]; + + ppp_vslprintf(buf, sizeof(buf), fmt, args); + ppp_log_write(level, buf); +} + +static void ppp_log_write(int level, char *buf) { + LWIP_UNUSED_ARG(level); /* necessary if PPPDEBUG is defined to an empty function */ + LWIP_UNUSED_ARG(buf); + PPPDEBUG(level, ("%s\n", buf) ); +#if 0 + if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) { + int n = strlen(buf); + + if (n > 0 && buf[n-1] == '\n') + --n; + if (write(log_to_fd, buf, n) != n + || write(log_to_fd, "\n", 1) != 1) + log_to_fd = -1; + } +#endif +} + +/* + * ppp_fatal - log an error message and die horribly. + */ +void ppp_fatal(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_ERR, fmt, pvar); + va_end(pvar); + +/* FIXME: find a way to die */ +#if 0 + die(1); /* as promised */ +#endif +} + +/* + * ppp_error - log an error message. + */ +void ppp_error(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_ERR, fmt, pvar); + va_end(pvar); +#if 0 /* UNUSED */ + ++error_count; +#endif /* UNUSED */ +} + +/* + * ppp_warn - log a warning message. + */ +void ppp_warn(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_WARNING, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_notice - log a notice-level message. + */ +void ppp_notice(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_NOTICE, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_info - log an informational message. + */ +void ppp_info(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_INFO, fmt, pvar); + va_end(pvar); +} + +/* + * ppp_dbglog - log a debug message. + */ +void ppp_dbglog(const char *fmt, ...) { + va_list pvar; + + va_start(pvar, fmt); + ppp_logit(LOG_DEBUG, fmt, pvar); + va_end(pvar); +} + +#if PRINTPKT_SUPPORT +/* + * ppp_dump_packet - print out a packet in readable form if it is interesting. + * Assumes len >= PPP_HDRLEN. + */ +void ppp_dump_packet(const char *tag, unsigned char *p, int len) { + int proto; + + /* + * don't print IPv4 and IPv6 packets. + */ + proto = (p[0] << 8) + p[1]; + if (proto == PPP_IP) + return; +#if PPP_IPV6_SUPPORT + if (proto == PPP_IPV6) + return; +#endif + + /* + * don't print LCP echo request/reply packets if the link is up. + */ + if (proto == PPP_LCP && len >= 2 + HEADERLEN) { + unsigned char *lcp = p + 2; + int l = (lcp[2] << 8) + lcp[3]; + + if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP) + && l >= HEADERLEN && l <= len - 2) + return; + } + + ppp_dbglog("%s %P", tag, p, len); +} +#endif /* PRINTPKT_SUPPORT */ + +#if 0 /* Unused */ + +/* + * complete_read - read a full `count' bytes from fd, + * unless end-of-file or an error other than EINTR is encountered. + */ +ssize_t +complete_read(int fd, void *buf, size_t count) +{ + size_t done; + ssize_t nb; + char *ptr = buf; + + for (done = 0; done < count; ) { + nb = read(fd, ptr, count - done); + if (nb < 0) { + if (errno == EINTR) + continue; + return -1; + } + if (nb == 0) + break; + done += nb; + ptr += nb; + } + return done; +} + +/* Procedures for locking the serial device using a lock file. */ +#ifndef LOCK_DIR +#ifdef __linux__ +#define LOCK_DIR "/var/lock" +#else +#ifdef SVR4 +#define LOCK_DIR "/var/spool/locks" +#else +#define LOCK_DIR "/var/spool/lock" +#endif +#endif +#endif /* LOCK_DIR */ + +static char lock_file[MAXPATHLEN]; + +/* + * lock - create a lock file for the named device + */ +int +lock(dev) + char *dev; +{ +#ifdef LOCKLIB + int result; + + result = mklock (dev, (void *) 0); + if (result == 0) { + ppp_strlcpy(lock_file, dev, sizeof(lock_file)); + return 0; + } + + if (result > 0) + ppp_notice("Device %s is locked by pid %d", dev, result); + else + ppp_error("Can't create lock file %s", lock_file); + return -1; + +#else /* LOCKLIB */ + + char lock_buffer[12]; + int fd, pid, n; + +#ifdef SVR4 + struct stat sbuf; + + if (stat(dev, &sbuf) < 0) { + ppp_error("Can't get device number for %s: %m", dev); + return -1; + } + if ((sbuf.st_mode & S_IFMT) != S_IFCHR) { + ppp_error("Can't lock %s: not a character device", dev); + return -1; + } + ppp_slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d", + LOCK_DIR, major(sbuf.st_dev), + major(sbuf.st_rdev), minor(sbuf.st_rdev)); +#else + char *p; + char lockdev[MAXPATHLEN]; + + if ((p = strstr(dev, "dev/")) != NULL) { + dev = p + 4; + strncpy(lockdev, dev, MAXPATHLEN-1); + lockdev[MAXPATHLEN-1] = 0; + while ((p = strrchr(lockdev, '/')) != NULL) { + *p = '_'; + } + dev = lockdev; + } else + if ((p = strrchr(dev, '/')) != NULL) + dev = p + 1; + + ppp_slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev); +#endif + + while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) { + if (errno != EEXIST) { + ppp_error("Can't create lock file %s: %m", lock_file); + break; + } + + /* Read the lock file to find out who has the device locked. */ + fd = open(lock_file, O_RDONLY, 0); + if (fd < 0) { + if (errno == ENOENT) /* This is just a timing problem. */ + continue; + ppp_error("Can't open existing lock file %s: %m", lock_file); + break; + } +#ifndef LOCK_BINARY + n = read(fd, lock_buffer, 11); +#else + n = read(fd, &pid, sizeof(pid)); +#endif /* LOCK_BINARY */ + close(fd); + fd = -1; + if (n <= 0) { + ppp_error("Can't read pid from lock file %s", lock_file); + break; + } + + /* See if the process still exists. */ +#ifndef LOCK_BINARY + lock_buffer[n] = 0; + pid = atoi(lock_buffer); +#endif /* LOCK_BINARY */ + if (pid == getpid()) + return 1; /* somebody else locked it for us */ + if (pid == 0 + || (kill(pid, 0) == -1 && errno == ESRCH)) { + if (unlink (lock_file) == 0) { + ppp_notice("Removed stale lock on %s (pid %d)", dev, pid); + continue; + } + ppp_warn("Couldn't remove stale lock on %s", dev); + } else + ppp_notice("Device %s is locked by pid %d", dev, pid); + break; + } + + if (fd < 0) { + lock_file[0] = 0; + return -1; + } + + pid = getpid(); +#ifndef LOCK_BINARY + ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); + write (fd, lock_buffer, 11); +#else + write(fd, &pid, sizeof (pid)); +#endif + close(fd); + return 0; + +#endif +} + +/* + * relock - called to update our lockfile when we are about to detach, + * thus changing our pid (we fork, the child carries on, and the parent dies). + * Note that this is called by the parent, with pid equal to the pid + * of the child. This avoids a potential race which would exist if + * we had the child rewrite the lockfile (the parent might die first, + * and another process could think the lock was stale if it checked + * between when the parent died and the child rewrote the lockfile). + */ +int +relock(pid) + int pid; +{ +#ifdef LOCKLIB + /* XXX is there a way to do this? */ + return -1; +#else /* LOCKLIB */ + + int fd; + char lock_buffer[12]; + + if (lock_file[0] == 0) + return -1; + fd = open(lock_file, O_WRONLY, 0); + if (fd < 0) { + ppp_error("Couldn't reopen lock file %s: %m", lock_file); + lock_file[0] = 0; + return -1; + } + +#ifndef LOCK_BINARY + ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid); + write (fd, lock_buffer, 11); +#else + write(fd, &pid, sizeof(pid)); +#endif /* LOCK_BINARY */ + close(fd); + return 0; + +#endif /* LOCKLIB */ +} + +/* + * unlock - remove our lockfile + */ +void +unlock() +{ + if (lock_file[0]) { +#ifdef LOCKLIB + (void) rmlock(lock_file, (void *) 0); +#else + unlink(lock_file); +#endif + lock_file[0] = 0; + } +} + +#endif /* Unused */ + +#endif /* PPP_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/vj.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/vj.c new file mode 100644 index 0000000..dc805ad --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/ppp/vj.c @@ -0,0 +1,657 @@ +/* + * Routines to compress and uncompess tcp packets (for transmission + * over low speed serial lines. + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * Initial distribution. + * + * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au, + * so that the entire packet being decompressed doesn't have + * to be in contiguous memory (just the compressed header). + * + * Modified March 1998 by Guy Lancaster, glanca@gesn.com, + * for a 16 bit processor. + */ + +#include "lwip/opt.h" +#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */ + +#include "netif/ppp/ppp_impl.h" +#include "netif/ppp/pppdebug.h" + +#include "netif/ppp/vj.h" + +#include + +#if LINK_STATS +#define INCR(counter) ++comp->stats.counter +#else +#define INCR(counter) +#endif + +void +vj_compress_init(struct vjcompress *comp) +{ + register u_char i; + register struct cstate *tstate = comp->tstate; + +#if MAX_SLOTS == 0 + memset((char *)comp, 0, sizeof(*comp)); +#endif + comp->maxSlotIndex = MAX_SLOTS - 1; + comp->compressSlot = 0; /* Disable slot ID compression by default. */ + for (i = MAX_SLOTS - 1; i > 0; --i) { + tstate[i].cs_id = i; + tstate[i].cs_next = &tstate[i - 1]; + } + tstate[0].cs_next = &tstate[MAX_SLOTS - 1]; + tstate[0].cs_id = 0; + comp->last_cs = &tstate[0]; + comp->last_recv = 255; + comp->last_xmit = 255; + comp->flags = VJF_TOSS; +} + + +/* ENCODE encodes a number that is known to be non-zero. ENCODEZ + * checks for zero (since zero has to be encoded in the long, 3 byte + * form). + */ +#define ENCODE(n) { \ + if ((u_short)(n) >= 256) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} +#define ENCODEZ(n) { \ + if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \ + *cp++ = 0; \ + cp[1] = (u_char)(n); \ + cp[0] = (u_char)((n) >> 8); \ + cp += 2; \ + } else { \ + *cp++ = (u_char)(n); \ + } \ +} + +#define DECODEL(f) { \ + if (*cp == 0) {\ + u32_t tmp_ = ntohl(f) + ((cp[1] << 8) | cp[2]); \ + (f) = htonl(tmp_); \ + cp += 3; \ + } else { \ + u32_t tmp_ = ntohl(f) + (u32_t)*cp++; \ + (f) = htonl(tmp_); \ + } \ +} + +#define DECODES(f) { \ + if (*cp == 0) {\ + u_short tmp_ = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \ + (f) = htons(tmp_); \ + cp += 3; \ + } else { \ + u_short tmp_ = ntohs(f) + (u_short)*cp++; \ + (f) = htons(tmp_); \ + } \ +} + +#define DECODEU(f) { \ + if (*cp == 0) {\ + (f) = htons(((u_short)cp[1] << 8) | cp[2]); \ + cp += 3; \ + } else { \ + (f) = htons((u_short)*cp++); \ + } \ +} + +/* + * vj_compress_tcp - Attempt to do Van Jacobson header compression on a + * packet. This assumes that nb and comp are not null and that the first + * buffer of the chain contains a valid IP header. + * Return the VJ type code indicating whether or not the packet was + * compressed. + */ +u_int +vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb) +{ + register struct ip_hdr *ip = (struct ip_hdr *)pb->payload; + register struct cstate *cs = comp->last_cs->cs_next; + register u_short hlen = IPH_HL(ip); + register struct tcp_hdr *oth; + register struct tcp_hdr *th; + register u_short deltaS, deltaA = 0; + register u_long deltaL; + register u_int changes = 0; + u_char new_seq[16]; + register u_char *cp = new_seq; + + /* + * Check that the packet is IP proto TCP. + */ + if (IPH_PROTO(ip) != IP_PROTO_TCP) { + return (TYPE_IP); + } + + /* + * Bail if this is an IP fragment or if the TCP packet isn't + * `compressible' (i.e., ACK isn't set or some other control bit is + * set). + */ + if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) { + return (TYPE_IP); + } + th = (struct tcp_hdr *)&((long *)ip)[hlen]; + if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) { + return (TYPE_IP); + } + /* + * Packet is compressible -- we're going to send either a + * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need + * to locate (or create) the connection state. Special case the + * most recently used connection since it's most likely to be used + * again & we don't have to do any reordering if it's used. + */ + INCR(vjs_packets); + if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src) + || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + /* + * Wasn't the first -- search for it. + * + * States are kept in a circularly linked list with + * last_cs pointing to the end of the list. The + * list is kept in lru order by moving a state to the + * head of the list whenever it is referenced. Since + * the list is short and, empirically, the connection + * we want is almost always near the front, we locate + * states via linear search. If we don't find a state + * for the datagram, the oldest state is (re-)used. + */ + register struct cstate *lcs; + register struct cstate *lastcs = comp->last_cs; + + do { + lcs = cs; cs = cs->cs_next; + INCR(vjs_searches); + if (ip_addr_cmp(&ip->src, &cs->cs_ip.src) + && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest) + && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) { + goto found; + } + } while (cs != lastcs); + + /* + * Didn't find it -- re-use oldest cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * last_cs to update the lru linkage. + */ + INCR(vjs_misses); + comp->last_cs = lcs; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + return (TYPE_IP); + } + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (cs == lastcs) { + comp->last_cs = lcs; + } else { + lcs->cs_next = cs->cs_next; + cs->cs_next = lastcs->cs_next; + lastcs->cs_next = cs; + } + } + + oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen]; + deltaS = hlen; + hlen += TCPH_HDRLEN(th); + hlen <<= 2; + /* Check that the IP/TCP headers are contained in the first buffer. */ + if (hlen > pb->len) { + PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen)); + return (TYPE_IP); + } + + /* + * Make sure that only what we expect to change changed. The first + * line of the `if' checks the IP protocol version, header length & + * type of service. The 2nd line checks the "Don't fragment" bit. + * The 3rd line checks the time-to-live and protocol (the protocol + * check is unnecessary but costless). The 4th line checks the TCP + * header length. The 5th line checks IP options, if any. The 6th + * line checks TCP options, if any. If any of these things are + * different between the previous & current datagram, we send the + * current datagram `uncompressed'. + */ + if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0] + || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3] + || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4] + || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth) + || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) + || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) { + goto uncompressed; + } + + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (TCPH_FLAGS(th) & TCP_URG) { + deltaS = ntohs(th->urgp); + ENCODEZ(deltaS); + changes |= NEW_U; + } else if (th->urgp != oth->urgp) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + + if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) { + ENCODE(deltaS); + changes |= NEW_W; + } + + if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaA = (u_short)deltaL; + ENCODE(deltaA); + changes |= NEW_A; + } + + if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) { + if (deltaL > 0xffff) { + goto uncompressed; + } + deltaS = (u_short)deltaL; + ENCODE(deltaS); + changes |= NEW_S; + } + + switch(changes) { + case 0: + /* + * Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. + */ + if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) && + ntohs(IPH_LEN(&cs->cs_ip)) == hlen) { + break; + } + /* no break */ + /* fall through */ + + case SPECIAL_I: + case SPECIAL_D: + /* + * actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + + case NEW_S|NEW_A: + if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + + case NEW_S: + if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + default: + break; + } + + deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip))); + if (deltaS != 1) { + ENCODEZ(deltaS); + changes |= NEW_I; + } + if (TCPH_FLAGS(th) & TCP_PSH) { + changes |= TCP_PUSH_BIT; + } + /* + * Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->chksum); + MEMCPY(&cs->cs_ip, ip, hlen); + + /* + * We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how + * many bytes of the original packet to toss so subtract the two to + * get the new packet size. + */ + deltaS = (u_short)(cp - new_seq); + if (!comp->compressSlot || comp->last_xmit != cs->cs_id) { + comp->last_xmit = cs->cs_id; + hlen -= deltaS + 4; + if(pbuf_header(pb, -hlen)){ + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)(changes | NEW_C); + *cp++ = cs->cs_id; + } else { + hlen -= deltaS + 3; + if(pbuf_header(pb, -hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + } + cp = (u_char *)pb->payload; + *cp++ = (u_char)changes; + } + *cp++ = (u_char)(deltaA >> 8); + *cp++ = (u_char)deltaA; + MEMCPY(cp, new_seq, deltaS); + INCR(vjs_compressed); + return (TYPE_COMPRESSED_TCP); + + /* + * Update connection state cs & send uncompressed packet (that is, + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ +uncompressed: + MEMCPY(&cs->cs_ip, ip, hlen); + IPH_PROTO_SET(ip, cs->cs_id); + comp->last_xmit = cs->cs_id; + return (TYPE_UNCOMPRESSED_TCP); +} + +/* + * Called when we may have missed a packet. + */ +void +vj_uncompress_err(struct vjcompress *comp) +{ + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); +} + +/* + * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP. + * Return 0 on success, -1 on failure. + */ +int +vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp) +{ + register u_int hlen; + register struct cstate *cs; + register struct ip_hdr *ip; + + ip = (struct ip_hdr *)nb->payload; + hlen = IPH_HL(ip) << 2; + if (IPH_PROTO(ip) >= MAX_SLOTS + || hlen + sizeof(struct tcp_hdr) > nb->len + || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2) + > nb->len + || hlen > MAX_HDR) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n", + IPH_PROTO(ip), hlen, nb->len)); + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return -1; + } + cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)]; + comp->flags &=~ VJF_TOSS; + IPH_PROTO_SET(ip, IP_PROTO_TCP); + MEMCPY(&cs->cs_ip, ip, hlen); + cs->cs_hlen = (u_short)hlen; + INCR(vjs_uncompressedin); + return 0; +} + +/* + * Uncompress a packet of type TYPE_COMPRESSED_TCP. + * The packet is composed of a buffer chain and the first buffer + * must contain an accurate chain length. + * The first buffer must include the entire compressed TCP/IP header. + * This procedure replaces the compressed header with the uncompressed + * header and returns the length of the VJ header. + */ +int +vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp) +{ + u_char *cp; + struct tcp_hdr *th; + struct cstate *cs; + u_short *bp; + struct pbuf *n0 = *nb; + u32_t tmp; + u_int vjlen, hlen, changes; + + INCR(vjs_compressedin); + cp = (u_char *)n0->payload; + changes = *cp++; + if (changes & NEW_C) { + /* + * Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + if (*cp >= MAX_SLOTS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp)); + goto bad; + } + + comp->flags &=~ VJF_TOSS; + comp->last_recv = *cp++; + } else { + /* + * this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. + */ + if (comp->flags & VJF_TOSS) { + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n")); + INCR(vjs_tossed); + return (-1); + } + } + cs = &comp->rstate[comp->last_recv]; + hlen = IPH_HL(&cs->cs_ip) << 2; + th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen]; + th->chksum = htons((*cp << 8) | cp[1]); + cp += 2; + if (changes & TCP_PUSH_BIT) { + TCPH_SET_FLAG(th, TCP_PSH); + } else { + TCPH_UNSET_FLAG(th, TCP_PSH); + } + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: + { + register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->ackno) + i; + th->ackno = htonl(tmp); + tmp = ntohl(th->seqno) + i; + th->seqno = htonl(tmp); + } + break; + + case SPECIAL_D: + /* some compilers can't nest inline assembler.. */ + tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen; + th->seqno = htonl(tmp); + break; + + default: + if (changes & NEW_U) { + TCPH_SET_FLAG(th, TCP_URG); + DECODEU(th->urgp); + } else { + TCPH_UNSET_FLAG(th, TCP_URG); + } + if (changes & NEW_W) { + DECODES(th->wnd); + } + if (changes & NEW_A) { + DECODEL(th->ackno); + } + if (changes & NEW_S) { + DECODEL(th->seqno); + } + break; + } + if (changes & NEW_I) { + DECODES(cs->cs_ip._id); + } else { + IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1); + IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip))); + } + + /* + * At this point, cp points to the first byte of data in the + * packet. Fill in the IP total length and update the IP + * header checksum. + */ + vjlen = (u_short)(cp - (u_char*)n0->payload); + if (n0->len < vjlen) { + /* + * We must have dropped some characters (crc should detect + * this but the old slip framing won't) + */ + PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n", + n0->len, vjlen)); + goto bad; + } + +#if BYTE_ORDER == LITTLE_ENDIAN + tmp = n0->tot_len - vjlen + cs->cs_hlen; + IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp)); +#else + IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen)); +#endif + + /* recompute the ip header checksum */ + bp = (u_short *) &cs->cs_ip; + IPH_CHKSUM_SET(&cs->cs_ip, 0); + for (tmp = 0; hlen > 0; hlen -= 2) { + tmp += *bp++; + } + tmp = (tmp & 0xffff) + (tmp >> 16); + tmp = (tmp & 0xffff) + (tmp >> 16); + IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp)); + + /* Remove the compressed header and prepend the uncompressed header. */ + if(pbuf_header(n0, -((s16_t)(vjlen)))) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) { + struct pbuf *np, *q; + u8_t *bufptr; + +#if IP_FORWARD + /* If IP forwarding is enabled we are using a PBUF_LINK packet type so + * the packet is being allocated with enough header space to be + * forwarded (to Ethernet for example). + */ + np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL); +#else /* IP_FORWARD */ + np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL); +#endif /* IP_FORWARD */ + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n")); + goto bad; + } + + if(pbuf_header(np, -cs->cs_hlen)) { + /* Can we cope with this failing? Just assert for now */ + LWIP_ASSERT("pbuf_header failed\n", 0); + goto bad; + } + + bufptr = (u8_t*)n0->payload; + for(q = np; q != NULL; q = q->next) { + MEMCPY(q->payload, bufptr, q->len); + bufptr += q->len; + } + + if(n0->next) { + pbuf_chain(np, n0->next); + pbuf_dechain(n0); + } + pbuf_free(n0); + n0 = np; + } + + if(pbuf_header(n0, cs->cs_hlen)) { + struct pbuf *np; + + LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE); + np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL); + if(!np) { + PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n")); + goto bad; + } + pbuf_cat(np, n0); + n0 = np; + } + LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen); + MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen); + + *nb = n0; + + return vjlen; + +bad: + comp->flags |= VJF_TOSS; + INCR(vjs_errorin); + return (-1); +} + +#endif /* PPP_SUPPORT && VJ_SUPPORT */ diff --git a/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/slipif.c b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/slipif.c new file mode 100644 index 0000000..ca9f0b9 --- /dev/null +++ b/component/common/network/lwip/lwip_v1.5.0.beta/src/netif/slipif.c @@ -0,0 +1,546 @@ +/** + * @file + * SLIP Interface + * + */ + +/* + * 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. 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 built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + * Simon Goldschmidt + * + * Usage: This netif can be used in three ways: + * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read() + * until data is received. + * 2) In your main loop, call slipif_poll() to check for new RX bytes, + * completed packets are fed into netif->input(). + * 3) Call slipif_received_byte[s]() from your serial RX ISR and + * slipif_process_rxqueue() from your main loop. ISR level decodes + * packets and puts completed packets on a queue which is fed into + * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for + * pbuf_alloc to work on ISR level!). + * + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be + * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_SLIPIF + +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/sio.h" + +#define SLIP_END 0xC0 /* 0300: start and end of every packet */ +#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */ +#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */ +#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */ + +/** Maximum packet size that is received by this netif */ +#ifndef SLIP_MAX_SIZE +#define SLIP_MAX_SIZE 1500 +#endif + +/** Define this to the interface speed for SNMP + * (sio_fd is the sio_fd_t returned by sio_open). + * The default value of zero means 'unknown'. + */ +#ifndef SLIP_SIO_SPEED +#define SLIP_SIO_SPEED(sio_fd) 0 +#endif + +enum slipif_recv_state { + SLIP_RECV_NORMAL, + SLIP_RECV_ESCAPE, +}; + +struct slipif_priv { + sio_fd_t sd; + /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */ + struct pbuf *p, *q; + u8_t state; + u16_t i, recved; +#if SLIP_RX_FROM_ISR + struct pbuf *rxpackets; +#endif +}; + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output(struct netif *netif, struct pbuf *p) +{ + struct slipif_priv *priv; + struct pbuf *q; + u16_t i; + u8_t c; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + LWIP_ASSERT("p != NULL", (p != NULL)); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len)); + priv = netif->state; + + /* Send pbuf out on the serial I/O device. */ + /* Start with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + + for (q = p; q != NULL; q = q->next) { + for (i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_END, priv->sd); + break; + case SLIP_ESC: + /* need to escape this byte (0xDB -> 0xDB, 0xDD) */ + sio_send(SLIP_ESC, priv->sd); + sio_send(SLIP_ESC_ESC, priv->sd); + break; + default: + /* normal byte - no need for escaping */ + sio_send(c, priv->sd); + break; + } + } + } + /* End with packet delimiter. */ + sio_send(SLIP_END, priv->sd); + return ERR_OK; +} + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} + +#if LWIP_IPV6 +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + * + * @param netif the lwip network interface structure for this slipif + * @param p the pbuf chain packet to send + * @param ipaddr the ip address to send the packet to (not used for slipif) + * @return always returns ERR_OK since the serial layer does not provide return values + */ +static err_t +slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr) +{ + LWIP_UNUSED_ARG(ipaddr); + return slipif_output(netif, p); +} +#endif /* LWIP_IPV6 */ + +/** + * Handle the incoming SLIP stream character by character + * + * @param netif the lwip network interface structure for this slipif + * @param c received character (multiple calls to this function will + * return a complete packet, NULL is returned before - used for polling) + * @return The IP packet when SLIP_END is received + */ +static struct pbuf* +slipif_rxbyte(struct netif *netif, u8_t c) +{ + struct slipif_priv *priv; + struct pbuf *t; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = netif->state; + + switch (priv->state) { + case SLIP_RECV_NORMAL: + switch (c) { + case SLIP_END: + if (priv->recved > 0) { + /* Received whole packet. */ + /* Trim the pbuf to the size of the received packet. */ + pbuf_realloc(priv->q, priv->recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved)); + t = priv->q; + priv->p = priv->q = NULL; + priv->i = priv->recved = 0; + return t; + } + return NULL; + case SLIP_ESC: + priv->state = SLIP_RECV_ESCAPE; + return NULL; + } /* end switch (c) */ + break; + case SLIP_RECV_ESCAPE: + /* un-escape END or ESC bytes, leave other bytes + (although that would be a protocol error) */ + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + priv->state = SLIP_RECV_NORMAL; + break; + } /* end switch (priv->state) */ + + /* byte received, packet not yet completely received */ + if (priv->p == NULL) { + /* allocate a new pbuf */ + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL); + + if (priv->p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + /* don't process any further since we got no pbuf to receive to */ + return NULL; + } + + if (priv->q != NULL) { + /* 'chain' the pbuf to the existing chain */ + pbuf_cat(priv->q, priv->p); + } else { + /* p is the first pbuf in the chain */ + priv->q = priv->p; + } + } + + /* this automatically drops bytes if > SLIP_MAX_SIZE */ + if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) { + ((u8_t *)priv->p->payload)[priv->i] = c; + priv->recved++; + priv->i++; + if (priv->i >= priv->p->len) { + /* on to the next pbuf */ + priv->i = 0; + if (priv->p->next != NULL && priv->p->next->len > 0) { + /* p is a chain, on to the next in the chain */ + priv->p = priv->p->next; + } else { + /* p is a single pbuf, set it to NULL so next time a new + * pbuf is allocated */ + priv->p = NULL; + } + } + } + return NULL; +} + +/** Like slipif_rxbyte, but passes completed packets to netif->input + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +static void +slipif_rxbyte_input(struct netif *netif, u8_t c) +{ + struct pbuf *p; + p = slipif_rxbyte(netif, c); + if (p != NULL) { + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + } +} + +#if SLIP_USE_RX_THREAD +/** + * The SLIP input thread. + * + * Feed the IP layer with incoming packets + * + * @param nf the lwip network interface structure for this slipif + */ +static void +slipif_loop_thread(void *nf) +{ + u8_t c; + struct netif *netif = (struct netif *)nf; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + + while (1) { + if (sio_read(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } + } +} +#endif /* SLIP_USE_RX_THREAD */ + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + * + * @param netif the lwip network interface structure for this slipif + * @return ERR_OK if serial line could be opened, + * ERR_MEM if no memory could be allocated, + * ERR_IF is serial line couldn't be opened + * + * @note netif->num must contain the number of the serial port to open + * (0 by default). If netif->state is != NULL, it is interpreted as an + * u8_t pointer pointing to the serial port number instead of netif->num. + * + */ +err_t +slipif_init(struct netif *netif) +{ + struct slipif_priv *priv; + u8_t sio_num; + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num)); + + /* Allocate private data */ + priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv)); + if (!priv) { + return ERR_MEM; + } + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output_v4; +#if LWIP_IPV6 + netif->output_ip6 = slipif_output_v6; +#endif /* LWIP_IPV6 */ + netif->mtu = SLIP_MAX_SIZE; + netif->flags |= NETIF_FLAG_POINTTOPOINT; + + /* netif->state or netif->num contain the port number */ + if (netif->state != NULL) { + sio_num = *(u8_t*)netif->state; + } else { + sio_num = netif->num; + } + /* Try to open the serial port. */ + priv->sd = sio_open(sio_num); + if (!priv->sd) { + /* Opening the serial port failed. */ + mem_free(priv); + return ERR_IF; + } + + /* Initialize private data */ + priv->p = NULL; + priv->q = NULL; + priv->state = SLIP_RECV_NORMAL; + priv->i = 0; + priv->recved = 0; +#if SLIP_RX_FROM_ISR + priv->rxpackets = NULL; +#endif + + netif->state = priv; + + /* initialize the snmp variables and counters inside the struct netif */ + NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd)); + +#if SLIP_USE_RX_THREAD + /* Create a thread to poll the serial line. */ + sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif, + SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO); +#endif /* SLIP_USE_RX_THREAD */ + return ERR_OK; +} + +/** + * Polls the serial device and feeds the IP layer with incoming packets. + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_poll(struct netif *netif) +{ + u8_t c; + struct slipif_priv *priv; + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + while (sio_tryread(priv->sd, &c, 1) > 0) { + slipif_rxbyte_input(netif, c); + } +} + +#if SLIP_RX_FROM_ISR +/** + * Feeds the IP layer with incoming packets that were receive + * + * @param netif The lwip network interface structure for this slipif + */ +void +slipif_process_rxqueue(struct netif *netif) +{ + struct slipif_priv *priv; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + priv = (struct slipif_priv *)netif->state; + + SYS_ARCH_PROTECT(old_level); + while (priv->rxpackets != NULL) { + struct pbuf *p = priv->rxpackets; +#if SLIP_RX_QUEUE + /* dequeue packet */ + struct pbuf *q = p; + while ((q->len != q->tot_len) && (q->next != NULL)) { + q = q->next; + } + priv->rxpackets = q->next; + q->next = NULL; +#else /* SLIP_RX_QUEUE */ + priv->rxpackets = NULL; +#endif /* SLIP_RX_QUEUE */ + SYS_ARCH_UNPROTECT(old_level); + if (netif->input(p, netif) != ERR_OK) { + pbuf_free(p); + } + SYS_ARCH_PROTECT(old_level); + } +} + +/** Like slipif_rxbyte, but queues completed packets. + * + * @param netif The lwip network interface structure for this slipif + * @param data Received serial byte + */ +static void +slipif_rxbyte_enqueue(struct netif *netif, u8_t data) +{ + struct pbuf *p; + struct slipif_priv *priv = (struct slipif_priv *)netif->state; + SYS_ARCH_DECL_PROTECT(old_level); + + p = slipif_rxbyte(netif, data); + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + if (priv->rxpackets != NULL) { +#if SLIP_RX_QUEUE + /* queue multiple pbufs */ + struct pbuf *q = p; + while(q->next != NULL) { + q = q->next; + } + q->next = p; + } else { +#else /* SLIP_RX_QUEUE */ + pbuf_free(priv->rxpackets); + } + { +#endif /* SLIP_RX_QUEUE */ + priv->rxpackets = p; + } + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Process a received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + */ +void +slipif_received_byte(struct netif *netif, u8_t data) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + slipif_rxbyte_enqueue(netif, data); +} + +/** + * Process multiple received byte, completed packets are put on a queue that is + * fed into IP through slipif_process_rxqueue(). + * + * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled. + * + * @param netif The lwip network interface structure for this slipif + * @param data received character + * @param len Number of received characters + */ +void +slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len) +{ + u8_t i; + u8_t *rxdata = data; + LWIP_ASSERT("netif != NULL", (netif != NULL)); + LWIP_ASSERT("netif->state != NULL", (netif->state != NULL)); + + for (i = 0; i < len; i++, rxdata++) { + slipif_rxbyte_enqueue(netif, *rxdata); + } +} +#endif /* SLIP_RX_FROM_ISR */ + +#endif /* LWIP_HAVE_SLIPIF */ diff --git a/component/common/network/mdns/mDNS.h b/component/common/network/mdns/mDNS.h new file mode 100644 index 0000000..c7ecedd --- /dev/null +++ b/component/common/network/mdns/mDNS.h @@ -0,0 +1,26 @@ +#ifndef _MDNS_H +#define _MDNS_H + +#include + +/* Text Record */ +typedef struct _TXTRecordRef_t { + char PrivateData[16]; +} TXTRecordRef; + +extern void TXTRecordCreate(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer); +extern int TXTRecordSetValue(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value); +extern void TXTRecordDeallocate(TXTRecordRef *txtRecord); + +/* mDNS */ +typedef void *DNSServiceRef; + +extern int mDNSResponderInit(void); +extern void mDNSResponderDeinit(void); +extern DNSServiceRef mDNSRegisterService(char *name, char *service_type, char *domain, unsigned short port, TXTRecordRef *txtRecord); +extern void mDNSDeregisterService(DNSServiceRef serviceRef); +extern void mDNSUpdateService(DNSServiceRef serviceRef, TXTRecordRef *txtRecord); +extern void mDNSRegisterAllInterfaces(void); +extern void mDNSDeregisterAllInterfaces(void); + +#endif /* _MDNS_H */ diff --git a/component/common/network/mdns/mDNSPlatform.c b/component/common/network/mdns/mDNSPlatform.c new file mode 100644 index 0000000..29d61d1 --- /dev/null +++ b/component/common/network/mdns/mDNSPlatform.c @@ -0,0 +1,37 @@ +#include +#include +extern struct netif xnetif[]; + +/*----------------------------------------------------------------------- + * Mandatory functions + *-----------------------------------------------------------------------*/ + +// Mandatory function for custom initialization +// called when mDNS initialization +void mDNSPlatformCustomInit(void) +{ + xnetif[0].flags |= NETIF_FLAG_IGMP; +} + +uint16_t mDNSPlatformHtons(uint16_t hostshort) +{ + return htons(hostshort); +} + +uint32_t mDNSPlatformInetAddr(char *cp) +{ + return inet_addr(cp); +} + +// Mandatory function to get hostname +// called when mDNS initialization +char *mDNSPlatformHostname(void) +{ +#if LWIP_NETIF_HOSTNAME + return xnetif[0].hostname; +#else + return "ameba"; +#endif +} + +/*-----------------------------------------------------------------------*/ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aes.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aes.h new file mode 100644 index 0000000..d08073c --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aes.h @@ -0,0 +1,264 @@ +/** + * \file aes.h + * + * \brief AES block cipher + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_AES_H +#define POLARSSL_AES_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +// Rename to fix function conflict to ROM codes +#define aes_init polarssl_aes_init + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +/* padlock.c and aesni.c rely on these values! */ +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0020 /**< Invalid key length. */ +#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0022 /**< Invalid data input length. */ + +#if !defined(POLARSSL_AES_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief AES context structure + * + * \note buf is able to hold 32 extra bytes, which can be used: + * - for alignment purposes if VIA padlock is used, and/or + * - to simplify key expansion in the 256-bit case by + * generating an extra round key + */ +typedef struct +{ + int nr; /*!< number of rounds */ + uint32_t *rk; /*!< AES round keys */ + uint32_t buf[68]; /*!< unaligned data */ +#ifdef RTL_HW_CRYPTO + unsigned char enc_key[32]; + unsigned char dec_key[32]; +#endif +} +aes_context; + +/** + * \brief Initialize AES context + * + * \param ctx AES context to be initialized + */ +void aes_init( aes_context *ctx ); + +/** + * \brief Clear AES context + * + * \param ctx AES context to be cleared + */ +void aes_free( aes_context *ctx ); + +/** + * \brief AES key schedule (encryption) + * + * \param ctx AES context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief AES key schedule (decryption) + * + * \param ctx AES context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief AES-ECB block encryption/decryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if successful + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief AES-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/** + * \brief AES-CFB128 buffer encryption/decryption. + * + * Note: Due to the nature of CFB you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +/** + * \brief AES-CFB8 buffer encryption/decryption. + * + * Note: Due to the nature of CFB you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int aes_crypt_cfb8( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/** + * \brief AES-CTR buffer encryption/decryption + * + * Warning: You have to keep the maximum use of your counter in mind! + * + * Note: Due to the nature of CTR you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT. + * + * \param ctx AES context + * \param length The length of the data + * \param nc_off The offset in the current stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be 0 at the start of a stream. + * \param nonce_counter The 128-bit nonce and counter. + * \param stream_block The saved stream-block for resuming. Is overwritten + * by the function. + * \param input The input data stream + * \param output The output data stream + * + * \return 0 if successful + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_AES_ALT */ +#include "aes_alt.h" +#endif /* POLARSSL_AES_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int aes_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aesni.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aesni.h new file mode 100644 index 0000000..92b23cd --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/aesni.h @@ -0,0 +1,107 @@ +/** + * \file aesni.h + * + * \brief AES-NI for hardware AES acceleration on some Intel processors + * + * Copyright (C) 2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_AESNI_H +#define POLARSSL_AESNI_H + +#include "aes.h" + +#define POLARSSL_AESNI_AES 0x02000000u +#define POLARSSL_AESNI_CLMUL 0x00000002u + +#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && \ + ( defined(__amd64__) || defined(__x86_64__) ) && \ + ! defined(POLARSSL_HAVE_X86_64) +#define POLARSSL_HAVE_X86_64 +#endif + +#if defined(POLARSSL_HAVE_X86_64) + +/** + * \brief AES-NI features detection routine + * + * \param what The feature to detect + * (POLARSSL_AESNI_AES or POLARSSL_AESNI_CLMUL) + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int aesni_supports( unsigned int what ); + +/** + * \brief AES-NI AES-ECB block en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 on success (cannot fail) + */ +int aesni_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief GCM multiplication: c = a * b in GF(2^128) + * + * \param c Result + * \param a First operand + * \param b Second operand + * + * \note Both operands and result are bit strings interpreted as + * elements of GF(2^128) as per the GCM spec. + */ +void aesni_gcm_mult( unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16] ); + +/** + * \brief Compute decryption round keys from encryption round keys + * + * \param invkey Round keys for the equivalent inverse cipher + * \param fwdkey Original round keys (for encryption) + * \param nr Number of rounds (that is, number of round keys minus one) + */ +void aesni_inverse_key( unsigned char *invkey, + const unsigned char *fwdkey, int nr ); + +/** + * \brief Perform key expansion (for encryption) + * + * \param rk Destination buffer where the round keys are written + * \param key Encryption key + * \param bits Key size in bits (must be 128, 192 or 256) + * + * \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH + */ +int aesni_setkey_enc( unsigned char *rk, + const unsigned char *key, + size_t bits ); + +#endif /* POLARSSL_HAVE_X86_64 */ + +#endif /* POLARSSL_AESNI_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/arc4.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/arc4.h new file mode 100644 index 0000000..555f54f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/arc4.h @@ -0,0 +1,117 @@ +/** + * \file arc4.h + * + * \brief The ARCFOUR stream cipher + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ARC4_H +#define POLARSSL_ARC4_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if !defined(POLARSSL_ARC4_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief ARC4 context structure + */ +typedef struct +{ + int x; /*!< permutation index */ + int y; /*!< permutation index */ + unsigned char m[256]; /*!< permutation table */ +} +arc4_context; + +/** + * \brief Initialize ARC4 context + * + * \param ctx ARC4 context to be initialized + */ +void arc4_init( arc4_context *ctx ); + +/** + * \brief Clear ARC4 context + * + * \param ctx ARC4 context to be cleared + */ +void arc4_free( arc4_context *ctx ); + +/** + * \brief ARC4 key schedule + * + * \param ctx ARC4 context to be setup + * \param key the secret key + * \param keylen length of the key, in bytes + */ +void arc4_setup( arc4_context *ctx, const unsigned char *key, + unsigned int keylen ); + +/** + * \brief ARC4 cipher function + * + * \param ctx ARC4 context + * \param length length of the input data + * \param input buffer holding the input data + * \param output buffer for the output data + * + * \return 0 if successful + */ +int arc4_crypt( arc4_context *ctx, size_t length, const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_ARC4_ALT */ +#include "arc4_alt.h" +#endif /* POLARSSL_ARC4_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int arc4_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* arc4.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1.h new file mode 100644 index 0000000..eacdd08 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1.h @@ -0,0 +1,347 @@ +/** + * \file asn1.h + * + * \brief Generic ASN.1 parsing + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ASN1_H +#define POLARSSL_ASN1_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_BIGNUM_C) +#include "bignum.h" +#endif + +#include + +/** + * \addtogroup asn1_module + * \{ + */ + +/** + * \name ASN1 Error codes + * These error codes are OR'ed to X509 error codes for + * higher error granularity. + * ASN1 is a standard to specify data structures. + * \{ + */ +#define POLARSSL_ERR_ASN1_OUT_OF_DATA -0x0060 /**< Out of data when parsing an ASN1 data structure. */ +#define POLARSSL_ERR_ASN1_UNEXPECTED_TAG -0x0062 /**< ASN1 tag was of an unexpected value. */ +#define POLARSSL_ERR_ASN1_INVALID_LENGTH -0x0064 /**< Error when trying to determine the length or invalid length. */ +#define POLARSSL_ERR_ASN1_LENGTH_MISMATCH -0x0066 /**< Actual length differs from expected length. */ +#define POLARSSL_ERR_ASN1_INVALID_DATA -0x0068 /**< Data is invalid. (not used) */ +#define POLARSSL_ERR_ASN1_MALLOC_FAILED -0x006A /**< Memory allocation failed */ +#define POLARSSL_ERR_ASN1_BUF_TOO_SMALL -0x006C /**< Buffer too small when writing ASN.1 data structure. */ + +/* \} name */ + +/** + * \name DER constants + * These constants comply with DER encoded the ANS1 type tags. + * DER encoding uses hexadecimal representation. + * An example DER sequence is:\n + * - 0x02 -- tag indicating INTEGER + * - 0x01 -- length in octets + * - 0x05 -- value + * Such sequences are typically read into \c ::x509_buf. + * \{ + */ +#define ASN1_BOOLEAN 0x01 +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_UTF8_STRING 0x0C +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_PRINTABLE_STRING 0x13 +#define ASN1_T61_STRING 0x14 +#define ASN1_IA5_STRING 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_GENERALIZED_TIME 0x18 +#define ASN1_UNIVERSAL_STRING 0x1C +#define ASN1_BMP_STRING 0x1E +#define ASN1_PRIMITIVE 0x00 +#define ASN1_CONSTRUCTED 0x20 +#define ASN1_CONTEXT_SPECIFIC 0x80 +/* \} name */ +/* \} addtogroup asn1_module */ + +/** Returns the size of the binary string, without the trailing \\0 */ +#define OID_SIZE(x) (sizeof(x) - 1) + +/** + * Compares an asn1_buf structure to a reference OID. + * + * Only works for 'defined' oid_str values (OID_HMAC_SHA1), you cannot use a + * 'unsigned char *oid' here! + * + * Warning: returns true when the OIDs are equal (unlike memcmp)! + */ +#define OID_CMP(oid_str, oid_buf) \ + ( ( OID_SIZE(oid_str) == (oid_buf)->len ) && \ + memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) == 0 ) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Functions to parse ASN.1 data structures + * \{ + */ + +/** + * Type-length-value structure that allows for ASN1 using DER. + */ +typedef struct _asn1_buf +{ + int tag; /**< ASN1 type, e.g. ASN1_UTF8_STRING. */ + size_t len; /**< ASN1 length, e.g. in octets. */ + unsigned char *p; /**< ASN1 data, e.g. in ASCII. */ +} +asn1_buf; + +/** + * Container for ASN1 bit strings. + */ +typedef struct _asn1_bitstring +{ + size_t len; /**< ASN1 length, e.g. in octets. */ + unsigned char unused_bits; /**< Number of unused bits at the end of the string */ + unsigned char *p; /**< Raw ASN1 data for the bit string */ +} +asn1_bitstring; + +/** + * Container for a sequence of ASN.1 items + */ +typedef struct _asn1_sequence +{ + asn1_buf buf; /**< Buffer containing the given ASN.1 item. */ + struct _asn1_sequence *next; /**< The next entry in the sequence. */ +} +asn1_sequence; + +/** + * Container for a sequence or list of 'named' ASN.1 data items + */ +typedef struct _asn1_named_data +{ + asn1_buf oid; /**< The object identifier. */ + asn1_buf val; /**< The named value. */ + struct _asn1_named_data *next; /**< The next entry in the sequence. */ +} +asn1_named_data; + +/** + * \brief Get the length of an ASN.1 element. + * Updates the pointer to immediately behind the length. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param len The variable that will receive the value + * + * \return 0 if successful, POLARSSL_ERR_ASN1_OUT_OF_DATA on reaching + * end of data, POLARSSL_ERR_ASN1_INVALID_LENGTH if length is + * unparseable. + */ +int asn1_get_len( unsigned char **p, + const unsigned char *end, + size_t *len ); + +/** + * \brief Get the tag and length of the tag. Check for the requested tag. + * Updates the pointer to immediately behind the tag and length. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param len The variable that will receive the length + * \param tag The expected tag + * + * \return 0 if successful, POLARSSL_ERR_ASN1_UNEXPECTED_TAG if tag did + * not match requested tag, or another specific ASN.1 error code. + */ +int asn1_get_tag( unsigned char **p, + const unsigned char *end, + size_t *len, int tag ); + +/** + * \brief Retrieve a boolean ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param val The variable that will receive the value + * + * \return 0 if successful or a specific ASN.1 error code. + */ +int asn1_get_bool( unsigned char **p, + const unsigned char *end, + int *val ); + +/** + * \brief Retrieve an integer ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param val The variable that will receive the value + * + * \return 0 if successful or a specific ASN.1 error code. + */ +int asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ); + +/** + * \brief Retrieve a bitstring ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param bs The variable that will receive the value + * + * \return 0 if successful or a specific ASN.1 error code. + */ +int asn1_get_bitstring( unsigned char **p, const unsigned char *end, + asn1_bitstring *bs); + +/** + * \brief Retrieve a bitstring ASN.1 tag without unused bits and its + * value. + * Updates the pointer to the beginning of the bit/octet string. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param len Length of the actual bit/octect string in bytes + * + * \return 0 if successful or a specific ASN.1 error code. + */ +int asn1_get_bitstring_null( unsigned char **p, const unsigned char *end, + size_t *len ); + +/** + * \brief Parses and splits an ASN.1 "SEQUENCE OF " + * Updated the pointer to immediately behind the full sequence tag. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param cur First variable in the chain to fill + * \param tag Type of sequence + * + * \return 0 if successful or a specific ASN.1 error code. + */ +int asn1_get_sequence_of( unsigned char **p, + const unsigned char *end, + asn1_sequence *cur, + int tag); + +#if defined(POLARSSL_BIGNUM_C) +/** + * \brief Retrieve a MPI value from an integer ASN.1 tag. + * Updates the pointer to immediately behind the full tag. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param X The MPI that will receive the value + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mpi *X ); +#endif /* POLARSSL_BIGNUM_C */ + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param alg The buffer to receive the OID + * \param params The buffer to receive the params (if any) + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int asn1_get_alg( unsigned char **p, + const unsigned char *end, + asn1_buf *alg, asn1_buf *params ); + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence with NULL or no + * params. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p The position in the ASN.1 data + * \param end End of data + * \param alg The buffer to receive the OID + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int asn1_get_alg_null( unsigned char **p, + const unsigned char *end, + asn1_buf *alg ); + +/** + * \brief Find a specific named_data entry in a sequence or list based on + * the OID. + * + * \param list The list to seek through + * \param oid The OID to look for + * \param len Size of the OID + * + * \return NULL if not found, or a pointer to the existing entry. + */ +asn1_named_data *asn1_find_named_data( asn1_named_data *list, + const char *oid, size_t len ); + +/** + * \brief Free a asn1_named_data entry + * + * \param entry The named data entry to free + */ +void asn1_free_named_data( asn1_named_data *entry ); + +/** + * \brief Free all entries in a asn1_named_data list + * Head will be set to NULL + * + * \param head Pointer to the head of the list of named data entries to free + */ +void asn1_free_named_data_list( asn1_named_data **head ); + +#ifdef __cplusplus +} +#endif + +#endif /* asn1.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1write.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1write.h new file mode 100644 index 0000000..7a7fbf7 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/asn1write.h @@ -0,0 +1,243 @@ +/** + * \file asn1write.h + * + * \brief ASN.1 buffer writing functionality + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ASN1_WRITE_H +#define POLARSSL_ASN1_WRITE_H + +#include "asn1.h" + +#define ASN1_CHK_ADD(g, f) do { if( ( ret = f ) < 0 ) return( ret ); else \ + g += ret; } while( 0 ) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Write a length field in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param len the length to write + * + * \return the length written or a negative error code + */ +int asn1_write_len( unsigned char **p, unsigned char *start, size_t len ); + +/** + * \brief Write a ASN.1 tag in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param tag the tag to write + * + * \return the length written or a negative error code + */ +int asn1_write_tag( unsigned char **p, unsigned char *start, + unsigned char tag ); + +/** + * \brief Write raw buffer data + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param buf data buffer to write + * \param size length of the data buffer + * + * \return the length written or a negative error code + */ +int asn1_write_raw_buffer( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t size ); + +#if defined(POLARSSL_BIGNUM_C) +/** + * \brief Write a big number (ASN1_INTEGER) in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param X the MPI to write + * + * \return the length written or a negative error code + */ +int asn1_write_mpi( unsigned char **p, unsigned char *start, mpi *X ); +#endif /* POLARSSL_BIGNUM_C */ + +/** + * \brief Write a NULL tag (ASN1_NULL) with zero data in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * + * \return the length written or a negative error code + */ +int asn1_write_null( unsigned char **p, unsigned char *start ); + +/** + * \brief Write an OID tag (ASN1_OID) and data in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param oid the OID to write + * \param oid_len length of the OID + * + * \return the length written or a negative error code + */ +int asn1_write_oid( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len ); + +/** + * \brief Write an AlgorithmIdentifier sequence in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param oid the OID of the algorithm + * \param oid_len length of the OID + * \param par_len length of parameters, which must be already written. + * If 0, NULL parameters are added + * + * \return the length written or a negative error code + */ +int asn1_write_algorithm_identifier( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + size_t par_len ); + +/** + * \brief Write a boolean tag (ASN1_BOOLEAN) and value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param boolean 0 or 1 + * + * \return the length written or a negative error code + */ +int asn1_write_bool( unsigned char **p, unsigned char *start, int boolean ); + +/** + * \brief Write an int tag (ASN1_INTEGER) and value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param val the integer value + * + * \return the length written or a negative error code + */ +int asn1_write_int( unsigned char **p, unsigned char *start, int val ); + +/** + * \brief Write a printable string tag (ASN1_PRINTABLE_STRING) and + * value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param text the text to write + * \param text_len length of the text + * + * \return the length written or a negative error code + */ +int asn1_write_printable_string( unsigned char **p, unsigned char *start, + const char *text, size_t text_len ); + +/** + * \brief Write an IA5 string tag (ASN1_IA5_STRING) and + * value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param text the text to write + * \param text_len length of the text + * + * \return the length written or a negative error code + */ +int asn1_write_ia5_string( unsigned char **p, unsigned char *start, + const char *text, size_t text_len ); + +/** + * \brief Write a bitstring tag (ASN1_BIT_STRING) and + * value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param buf the bitstring + * \param bits the total number of bits in the bitstring + * + * \return the length written or a negative error code + */ +int asn1_write_bitstring( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t bits ); + +/** + * \brief Write an octet string tag (ASN1_OCTET_STRING) and + * value in ASN.1 format + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param buf data buffer to write + * \param size length of the data buffer + * + * \return the length written or a negative error code + */ +int asn1_write_octet_string( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t size ); + +/** + * \brief Create or find a specific named_data entry for writing in a + * sequence or list based on the OID. If not already in there, + * a new entry is added to the head of the list. + * Warning: Destructive behaviour for the val data! + * + * \param list Pointer to the location of the head of the list to seek + * through (will be updated in case of a new entry) + * \param oid The OID to look for + * \param oid_len Size of the OID + * \param val Data to store (can be NULL if you want to fill it by hand) + * \param val_len Minimum length of the data buffer needed + * + * \return NULL if if there was a memory allocation error, or a pointer + * to the new / existing entry. + */ +asn1_named_data *asn1_store_named_data( asn1_named_data **list, + const char *oid, size_t oid_len, + const unsigned char *val, + size_t val_len ); + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_ASN1_WRITE_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/base64.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/base64.h new file mode 100644 index 0000000..d041493 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/base64.h @@ -0,0 +1,87 @@ +/** + * \file base64.h + * + * \brief RFC 1521 base64 encoding/decoding + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BASE64_H +#define POLARSSL_BASE64_H + +#include + +#define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */ +#define POLARSSL_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Encode a buffer into base64 format + * + * \param dst destination buffer + * \param dlen size of the buffer + * \param src source buffer + * \param slen amount of data to be encoded + * + * \return 0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL. + * *dlen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dlen = 0 to obtain the + * required buffer size in *dlen + */ +int base64_encode( unsigned char *dst, size_t *dlen, + const unsigned char *src, size_t slen ); + +/** + * \brief Decode a base64-formatted buffer + * + * \param dst destination buffer (can be NULL for checking size) + * \param dlen size of the buffer + * \param src source buffer + * \param slen amount of data to be decoded + * + * \return 0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or + * POLARSSL_ERR_BASE64_INVALID_CHARACTER if the input data is + * not correct. *dlen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dst = NULL or *dlen = 0 to obtain + * the required buffer size in *dlen + */ +int base64_decode( unsigned char *dst, size_t *dlen, + const unsigned char *src, size_t slen ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int base64_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* base64.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bignum.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bignum.h new file mode 100644 index 0000000..cd7592c --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bignum.h @@ -0,0 +1,752 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BIGNUM_H +#define POLARSSL_BIGNUM_H + +#include +#include + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +#if (_MSC_VER <= 1200) +typedef signed short int16_t; +typedef unsigned short uint16_t; +#else +typedef INT16 int16_t; +typedef UINT16 uint16_t; +#endif +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif /* _MSC_VER && !EFIX64 && !EFI32 */ + +#define POLARSSL_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */ +#define POLARSSL_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */ +#define POLARSSL_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */ +#define POLARSSL_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */ +#define POLARSSL_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */ +#define POLARSSL_ERR_MPI_MALLOC_FAILED -0x0010 /**< Memory allocation failed. */ + +#define MPI_CHK(f) do { if( ( ret = f ) != 0 ) goto cleanup; } while( 0 ) + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define POLARSSL_MPI_MAX_LIMBS 10000 + +#if !defined(POLARSSL_MPI_WINDOW_SIZE) +/* + * Maximum window size used for modular exponentiation. Default: 6 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 << POLARSSL_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +#endif /* !POLARSSL_MPI_WINDOW_SIZE */ + +#if !defined(POLARSSL_MPI_MAX_SIZE) +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can results temporarily in larger MPIs. So the number + * of limbs required (POLARSSL_MPI_MAX_LIMBS) is higher. + */ +#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ +#endif /* !POLARSSL_MPI_MAX_SIZE */ + +#define POLARSSL_MPI_MAX_BITS ( 8 * POLARSSL_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mpi_read_file() and writing to files with + * mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of POLARSSL_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * POLARSSL_MPI_RW_BUFFER_SIZE = ceil(POLARSSL_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define POLARSSL_MPI_MAX_BITS_SCALE100 ( 100 * POLARSSL_MPI_MAX_BITS ) +#define LN_2_DIV_LN_10_SCALE100 332 +#define POLARSSL_MPI_RW_BUFFER_SIZE ( ((POLARSSL_MPI_MAX_BITS_SCALE100 + LN_2_DIV_LN_10_SCALE100 - 1) / LN_2_DIV_LN_10_SCALE100) + 10 + 6 ) + +/* + * Define the base integer type, architecture-wise + */ +#if defined(POLARSSL_HAVE_INT8) +typedef signed char t_sint; +typedef unsigned char t_uint; +typedef uint16_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else +#if defined(POLARSSL_HAVE_INT16) +typedef int16_t t_sint; +typedef uint16_t t_uint; +typedef uint32_t t_udbl; +#define POLARSSL_HAVE_UDBL +#else + /* + * 32-bit integers can be forced on 64-bit arches (eg. for testing purposes) + * by defining POLARSSL_HAVE_INT32 and undefining POLARSSL_HAVE_ASM + */ + #if ( ! defined(POLARSSL_HAVE_INT32) && \ + defined(_MSC_VER) && defined(_M_AMD64) ) + #define POLARSSL_HAVE_INT64 + typedef int64_t t_sint; + typedef uint64_t t_uint; + #else + #if ( ! defined(POLARSSL_HAVE_INT32) && \ + defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) ) ) + #define POLARSSL_HAVE_INT64 + typedef int64_t t_sint; + typedef uint64_t t_uint; + typedef unsigned int t_udbl __attribute__((mode(TI))); + #define POLARSSL_HAVE_UDBL + #else + #define POLARSSL_HAVE_INT32 + typedef int32_t t_sint; + typedef uint32_t t_uint; + #if ( defined(_MSC_VER) && defined(_M_IX86) ) + typedef uint64_t t_udbl; + #define POLARSSL_HAVE_UDBL + #else + #if defined( POLARSSL_HAVE_LONGLONG ) + typedef unsigned long long t_udbl; + #define POLARSSL_HAVE_UDBL + #endif + #endif + #endif /* !POLARSSL_HAVE_INT32 && __GNUC__ && 64-bit platform */ + #endif /* !POLARSSL_HAVE_INT32 && _MSC_VER && _M_AMD64 */ +#endif /* POLARSSL_HAVE_INT16 */ +#endif /* POLARSSL_HAVE_INT8 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MPI structure + */ +typedef struct +{ + int s; /*!< integer sign */ + size_t n; /*!< total # of limbs */ + t_uint *p; /*!< pointer to limbs */ +} +mpi; + +/** + * \brief Initialize one MPI + * + * \param X One MPI to initialize. + */ +void mpi_init( mpi *X ); + +/** + * \brief Unallocate one MPI + * + * \param X One MPI to unallocate. + */ +void mpi_free( mpi *X ); + +/** + * \brief Enlarge to the specified number of limbs + * + * \param X MPI to grow + * \param nblimbs The target number of limbs + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_grow( mpi *X, size_t nblimbs ); + +/** + * \brief Resize down, keeping at least the specified number of limbs + * + * \param X MPI to shrink + * \param nblimbs The minimum number of limbs to keep + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shrink( mpi *X, size_t nblimbs ); + +/** + * \brief Copy the contents of Y into X + * + * \param X Destination MPI + * \param Y Source MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_copy( mpi *X, const mpi *Y ); + +/** + * \brief Swap the contents of X and Y + * + * \param X First MPI value + * \param Y Second MPI value + */ +void mpi_swap( mpi *X, mpi *Y ); + +/** + * \brief Safe conditional assignement X = Y if assign is 1 + * + * \param X MPI to conditionally assign to + * \param Y Value to be assigned + * \param assign 1: perform the assignment, 0: keep X's original value + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * + * \note This function is equivalent to + * if( assign ) mpi_copy( X, Y ); + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + */ +int mpi_safe_cond_assign( mpi *X, const mpi *Y, unsigned char assign ); + +/** + * \brief Safe conditional swap X <-> Y if swap is 1 + * + * \param X First mpi value + * \param Y Second mpi value + * \param assign 1: perform the swap, 0: keep X and Y's original values + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * + * \note This function is equivalent to + * if( assign ) mpi_swap( X, Y ); + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + */ +int mpi_safe_cond_swap( mpi *X, mpi *Y, unsigned char assign ); + +/** + * \brief Set value from integer + * + * \param X MPI to set + * \param z Value to use + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_lset( mpi *X, t_sint z ); + +/** + * \brief Get a specific bit from X + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * + * \return Either a 0 or a 1 + */ +int mpi_get_bit( const mpi *X, size_t pos ); + +/** + * \brief Set a bit of X to a specific value of 0 or 1 + * + * \note Will grow X if necessary to set a bit to 1 in a not yet + * existing limb. Will not grow if bit should be set to 0 + * + * \param X MPI to use + * \param pos Zero-based index of the bit in X + * \param val The value to set the bit to (0 or 1) + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ); + +/** + * \brief Return the number of zero-bits before the least significant + * '1' bit + * + * Note: Thus also the zero-based index of the least significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_lsb( const mpi *X ); + +/** + * \brief Return the number of bits up to and including the most + * significant '1' bit' + * + * Note: Thus also the one-based index of the most significant '1' bit + * + * \param X MPI to use + */ +size_t mpi_msb( const mpi *X ); + +/** + * \brief Return the total size in bytes + * + * \param X MPI to use + */ +size_t mpi_size( const mpi *X ); + +/** + * \brief Import from an ASCII string + * + * \param X Destination MPI + * \param radix Input numeric base + * \param s Null-terminated string buffer + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_string( mpi *X, int radix, const char *s ); + +/** + * \brief Export into an ASCII string + * + * \param X Source MPI + * \param radix Output numeric base + * \param s String buffer + * \param slen String buffer size + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code. + * *slen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *slen = 0 to obtain the + * minimum required buffer size in *slen. + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Read X from an opened file + * + * \param X Destination MPI + * \param radix Input numeric base + * \param fin Input file handle + * + * \return 0 if successful, POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if + * the file read buffer is too small or a + * POLARSSL_ERR_MPI_XXX error code + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ); + +/** + * \brief Write X into an opened file, or stdout if fout is NULL + * + * \param p Prefix, can be NULL + * \param X Source MPI + * \param radix Output numeric base + * \param fout Output file handle (can be NULL) + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Set fout == NULL to print X on the console. + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Import X from unsigned binary data, big endian + * + * \param X Destination MPI + * \param buf Input buffer + * \param buflen Input buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ); + +/** + * \brief Export X into unsigned binary data, big endian. + * Always fills the whole buffer, which will start with zeros + * if the number is smaller. + * + * \param X Source MPI + * \param buf Output buffer + * \param buflen Output buffer size + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ); + +/** + * \brief Left-shift: X <<= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_l( mpi *X, size_t count ); + +/** + * \brief Right-shift: X >>= count + * + * \param X MPI to shift + * \param count Amount to shift + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_shift_r( mpi *X, size_t count ); + +/** + * \brief Compare unsigned values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if |X| is greater than |Y|, + * -1 if |X| is lesser than |Y| or + * 0 if |X| is equal to |Y| + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param Y Right-hand MPI + * + * \return 1 if X is greater than Y, + * -1 if X is lesser than Y or + * 0 if X is equal to Y + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ); + +/** + * \brief Compare signed values + * + * \param X Left-hand MPI + * \param z The integer value to compare to + * + * \return 1 if X is greater than z, + * -1 if X is lesser than z or + * 0 if X is equal to z + */ +int mpi_cmp_int( const mpi *X, t_sint z ); + +/** + * \brief Unsigned addition: X = |A| + |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Unsigned subtraction: X = |A| - |B| + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B is greater than A + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed subtraction: X = A - B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Signed addition: X = A + b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to add + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Signed subtraction: X = A - b + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to subtract + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Baseline multiplication: X = A * B + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ); + +/** + * \brief Baseline multiplication: X = A * b + * Note: despite the functon signature, b is treated as a + * t_uint. Negative values of b are treated as large positive + * values. + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param b The integer value to multiply with + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ); + +/** + * \brief Division by mpi: A = Q * B + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Division by int: A = Q * b + R + * + * \param Q Destination MPI for the quotient + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0 + * + * \note Either Q or R can be NULL. + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ); + +/** + * \brief Modulo: R = A mod B + * + * \param R Destination MPI for the rest value + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if B == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if B < 0 + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ); + +/** + * \brief Modulo: r = A mod b + * + * \param r Destination t_uint + * \param A Left-hand MPI + * \param b Integer to divide by + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_DIVISION_BY_ZERO if b == 0, + * POLARSSL_ERR_MPI_NEGATIVE_VALUE if b < 0 + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ); + +/** + * \brief Sliding-window exponentiation: X = A^E mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param E Exponent MPI + * \param N Modular MPI + * \param _RR Speed-up MPI used for recalculations + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or even or + * if E is negative + * + * \note _RR is used to avoid re-computing R*R mod N across + * multiple calls, which speeds up things a bit. It can + * be set to NULL if the extra performance is unneeded. + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ); + +/** + * \brief Fill an MPI X with size bytes of random + * + * \param X Destination MPI + * \param size Size in bytes + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Greatest common divisor: G = gcd(A, B) + * + * \param G Destination MPI + * \param A Left-hand MPI + * \param B Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ); + +/** + * \brief Modular inverse: X = A^-1 mod N + * + * \param X Destination MPI + * \param A Left-hand MPI + * \param N Right-hand MPI + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if N is negative or nil + POLARSSL_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ); + +/** + * \brief Miller-Rabin primality test + * + * \param X MPI to check + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE if X is not prime + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Prime number generation + * + * \param X Destination MPI + * \param nbits Required size of X in bits + * ( 3 <= nbits <= POLARSSL_MPI_MAX_BITS ) + * \param dh_flag If 1, then (X-1)/2 will be prime too + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful (probably prime), + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_MPI_BAD_INPUT_DATA if nbits is < 3 + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mpi_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/blowfish.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/blowfish.h new file mode 100644 index 0000000..c652b46 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/blowfish.h @@ -0,0 +1,197 @@ +/** + * \file blowfish.h + * + * \brief Blowfish block cipher + * + * Copyright (C) 2012-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_BLOWFISH_H +#define POLARSSL_BLOWFISH_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define BLOWFISH_ENCRYPT 1 +#define BLOWFISH_DECRYPT 0 +#define BLOWFISH_MAX_KEY 448 +#define BLOWFISH_MIN_KEY 32 +#define BLOWFISH_ROUNDS 16 /**< Rounds to use. When increasing this value, make sure to extend the initialisation vectors */ +#define BLOWFISH_BLOCKSIZE 8 /* Blowfish uses 64 bit blocks */ + +#define POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH -0x0016 /**< Invalid key length. */ +#define POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH -0x0018 /**< Invalid data input length. */ + +#if !defined(POLARSSL_BLOWFISH_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Blowfish context structure + */ +typedef struct +{ + uint32_t P[BLOWFISH_ROUNDS + 2]; /*!< Blowfish round keys */ + uint32_t S[4][256]; /*!< key dependent S-boxes */ +} +blowfish_context; + +/** + * \brief Initialize Blowfish context + * + * \param ctx Blowfish context to be initialized + */ +void blowfish_init( blowfish_context *ctx ); + +/** + * \brief Clear Blowfish context + * + * \param ctx Blowfish context to be cleared + */ +void blowfish_free( blowfish_context *ctx ); + +/** + * \brief Blowfish key schedule + * + * \param ctx Blowfish context to be initialized + * \param key encryption key + * \param keysize must be between 32 and 448 bits + * + * \return 0 if successful, or POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH + */ +int blowfish_setkey( blowfish_context *ctx, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief Blowfish-ECB block encryption/decryption + * + * \param ctx Blowfish context + * \param mode BLOWFISH_ENCRYPT or BLOWFISH_DECRYPT + * \param input 8-byte input block + * \param output 8-byte output block + * + * \return 0 if successful + */ +int blowfish_crypt_ecb( blowfish_context *ctx, + int mode, + const unsigned char input[BLOWFISH_BLOCKSIZE], + unsigned char output[BLOWFISH_BLOCKSIZE] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief Blowfish-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (8 bytes) + * + * \param ctx Blowfish context + * \param mode BLOWFISH_ENCRYPT or BLOWFISH_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or + * POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH + */ +int blowfish_crypt_cbc( blowfish_context *ctx, + int mode, + size_t length, + unsigned char iv[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/** + * \brief Blowfish CFB buffer encryption/decryption. + * + * \param ctx Blowfish context + * \param mode BLOWFISH_ENCRYPT or BLOWFISH_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful + */ +int blowfish_crypt_cfb64( blowfish_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ); +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/** + * \brief Blowfish-CTR buffer encryption/decryption + * + * Warning: You have to keep the maximum use of your counter in mind! + * + * \param ctx Blowfish context + * \param length The length of the data + * \param nc_off The offset in the current stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be 0 at the start of a stream. + * \param nonce_counter The 64-bit nonce and counter. + * \param stream_block The saved stream-block for resuming. Is overwritten + * by the function. + * \param input The input data stream + * \param output The output data stream + * + * \return 0 if successful + */ +int blowfish_crypt_ctr( blowfish_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[BLOWFISH_BLOCKSIZE], + unsigned char stream_block[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_BLOWFISH_ALT */ +#include "blowfish_alt.h" +#endif /* POLARSSL_BLOWFISH_ALT */ + +#endif /* blowfish.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bn_mul.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bn_mul.h new file mode 100644 index 0000000..64b59ff --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/bn_mul.h @@ -0,0 +1,873 @@ +/** + * \file bn_mul.h + * + * \brief Multi-precision integer library + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef POLARSSL_BN_MUL_H +#define POLARSSL_BN_MUL_H + +#include "bignum.h" + +#if defined(POLARSSL_HAVE_ASM) + +#if defined(__GNUC__) +#if defined(__i386__) + +#define MULADDC_INIT \ + asm( \ + "movl %%ebx, %0 \n\t" \ + "movl %5, %%esi \n\t" \ + "movl %6, %%edi \n\t" \ + "movl %7, %%ecx \n\t" \ + "movl %8, %%ebx \n\t" + +#define MULADDC_CORE \ + "lodsl \n\t" \ + "mull %%ebx \n\t" \ + "addl %%ecx, %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "addl (%%edi), %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "movl %%edx, %%ecx \n\t" \ + "stosl \n\t" + +#if defined(POLARSSL_HAVE_SSE2) + +#define MULADDC_HUIT \ + "movd %%ecx, %%mm1 \n\t" \ + "movd %%ebx, %%mm0 \n\t" \ + "movd (%%edi), %%mm3 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd (%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "movd 4(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "movd 8(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd 12(%%esi), %%mm7 \n\t" \ + "pmuludq %%mm0, %%mm7 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 4(%%edi), %%mm3 \n\t" \ + "paddq %%mm4, %%mm3 \n\t" \ + "movd 8(%%edi), %%mm5 \n\t" \ + "paddq %%mm6, %%mm5 \n\t" \ + "movd 12(%%edi), %%mm4 \n\t" \ + "paddq %%mm4, %%mm7 \n\t" \ + "movd %%mm1, (%%edi) \n\t" \ + "movd 16(%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 20(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd 24(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd %%mm1, 4(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 28(%%esi), %%mm3 \n\t" \ + "pmuludq %%mm0, %%mm3 \n\t" \ + "paddq %%mm5, %%mm1 \n\t" \ + "movd 16(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm2 \n\t" \ + "movd %%mm1, 8(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm7, %%mm1 \n\t" \ + "movd 20(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm4 \n\t" \ + "movd %%mm1, 12(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 24(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm6 \n\t" \ + "movd %%mm1, 16(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm4, %%mm1 \n\t" \ + "movd 28(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm3 \n\t" \ + "movd %%mm1, 20(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm6, %%mm1 \n\t" \ + "movd %%mm1, 24(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd %%mm1, 28(%%edi) \n\t" \ + "addl $32, %%edi \n\t" \ + "addl $32, %%esi \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd %%mm1, %%ecx \n\t" + +#define MULADDC_STOP \ + "emms \n\t" \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); + +#else + +#define MULADDC_STOP \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ecx", "edx", "esi", "edi" \ + ); +#endif /* SSE2 */ +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_INIT \ + asm( \ + "movq %3, %%rsi \n\t" \ + "movq %4, %%rdi \n\t" \ + "movq %5, %%rcx \n\t" \ + "movq %6, %%rbx \n\t" \ + "xorq %%r8, %%r8 \n\t" + +#define MULADDC_CORE \ + "movq (%%rsi), %%rax \n\t" \ + "mulq %%rbx \n\t" \ + "addq $8, %%rsi \n\t" \ + "addq %%rcx, %%rax \n\t" \ + "movq %%r8, %%rcx \n\t" \ + "adcq $0, %%rdx \n\t" \ + "nop \n\t" \ + "addq %%rax, (%%rdi) \n\t" \ + "adcq %%rdx, %%rcx \n\t" \ + "addq $8, %%rdi \n\t" + +#define MULADDC_STOP \ + "movq %%rcx, %0 \n\t" \ + "movq %%rdi, %1 \n\t" \ + "movq %%rsi, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "rax", "rcx", "rdx", "rbx", "rsi", "rdi", "r8" \ + ); + +#endif /* AMD64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_INIT \ + asm( \ + "movl %3, %%a2 \n\t" \ + "movl %4, %%a3 \n\t" \ + "movl %5, %%d3 \n\t" \ + "movl %6, %%d2 \n\t" \ + "moveq #0, %%d0 \n\t" + +#define MULADDC_CORE \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "moveq #0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d4, %%d3 \n\t" + +#define MULADDC_STOP \ + "movl %%d3, %0 \n\t" \ + "movl %%a3, %1 \n\t" \ + "movl %%a2, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "d2", "d3", "d4", "a2", "a3" \ + ); + +#define MULADDC_HUIT \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d0, %%d3 \n\t" + +#endif /* MC68000 */ + +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( \ + "ld r3, %3 \n\t" \ + "ld r4, %4 \n\t" \ + "ld r5, %5 \n\t" \ + "ld r6, %6 \n\t" \ + "addi r3, r3, -8 \n\t" \ + "addi r4, r4, -8 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_CORE \ + "ldu r7, 8(r3) \n\t" \ + "mulld r8, r7, r6 \n\t" \ + "mulhdu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "ld r7, 8(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stdu r8, 8(r4) \n\t" + +#define MULADDC_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 8 \n\t" \ + "addi r3, r3, 8 \n\t" \ + "std r5, %0 \n\t" \ + "std r4, %1 \n\t" \ + "std r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_INIT \ + asm( \ + "ld %%r3, %3 \n\t" \ + "ld %%r4, %4 \n\t" \ + "ld %%r5, %5 \n\t" \ + "ld %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -8 \n\t" \ + "addi %%r4, %%r4, -8 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_CORE \ + "ldu %%r7, 8(%%r3) \n\t" \ + "mulld %%r8, %%r7, %%r6 \n\t" \ + "mulhdu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "ld %%r7, 8(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stdu %%r8, 8(%%r4) \n\t" + +#define MULADDC_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 8 \n\t" \ + "addi %%r3, %%r3, 8 \n\t" \ + "std %%r5, %0 \n\t" \ + "std %%r4, %1 \n\t" \ + "std %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#elif defined(__powerpc__) || defined(__ppc__) /* end PPC64/begin PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_INIT \ + asm( \ + "lwz r3, %3 \n\t" \ + "lwz r4, %4 \n\t" \ + "lwz r5, %5 \n\t" \ + "lwz r6, %6 \n\t" \ + "addi r3, r3, -4 \n\t" \ + "addi r4, r4, -4 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_CORE \ + "lwzu r7, 4(r3) \n\t" \ + "mullw r8, r7, r6 \n\t" \ + "mulhwu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "lwz r7, 4(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stwu r8, 4(r4) \n\t" + +#define MULADDC_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 4 \n\t" \ + "addi r3, r3, 4 \n\t" \ + "stw r5, %0 \n\t" \ + "stw r4, %1 \n\t" \ + "stw r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_INIT \ + asm( \ + "lwz %%r3, %3 \n\t" \ + "lwz %%r4, %4 \n\t" \ + "lwz %%r5, %5 \n\t" \ + "lwz %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -4 \n\t" \ + "addi %%r4, %%r4, -4 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_CORE \ + "lwzu %%r7, 4(%%r3) \n\t" \ + "mullw %%r8, %%r7, %%r6 \n\t" \ + "mulhwu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "lwz %%r7, 4(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stwu %%r8, 4(%%r4) \n\t" + +#define MULADDC_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 4 \n\t" \ + "addi %%r3, %%r3, 4 \n\t" \ + "stw %%r5, %0 \n\t" \ + "stw %%r4, %1 \n\t" \ + "stw %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#endif /* PPC32 */ + +/* + * The Sparc64 assembly is reported to be broken. + * Disable it for now, until we're able to fix it. + */ +#if 0 && defined(__sparc__) && defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + "ldx %3, %%o0 \n\t" \ + "ldx %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + + #define MULADDC_STOP \ + "st %%o2, %0 \n\t" \ + "stx %%o1, %1 \n\t" \ + "stx %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); +#endif /* SPARCv9 */ + +#if defined(__sparc__) && !defined(__sparc64__) + +#define MULADDC_INIT \ + asm( \ + "ld %3, %%o0 \n\t" \ + "ld %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + +#define MULADDC_STOP \ + "st %%o2, %0 \n\t" \ + "st %%o1, %1 \n\t" \ + "st %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#endif /* SPARCv8 */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_INIT \ + asm( \ + "lwi r3, %3 \n\t" \ + "lwi r4, %4 \n\t" \ + "lwi r5, %5 \n\t" \ + "lwi r6, %6 \n\t" \ + "andi r7, r6, 0xffff \n\t" \ + "bsrli r6, r6, 16 \n\t" + +#define MULADDC_CORE \ + "lhui r8, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "lhui r9, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "mul r10, r9, r6 \n\t" \ + "mul r11, r8, r7 \n\t" \ + "mul r12, r9, r7 \n\t" \ + "mul r13, r8, r6 \n\t" \ + "bsrli r8, r10, 16 \n\t" \ + "bsrli r9, r11, 16 \n\t" \ + "add r13, r13, r8 \n\t" \ + "add r13, r13, r9 \n\t" \ + "bslli r10, r10, 16 \n\t" \ + "bslli r11, r11, 16 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r11 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "lwi r10, r4, 0 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r5 \n\t" \ + "addc r5, r13, r0 \n\t" \ + "swi r12, r4, 0 \n\t" \ + "addi r4, r4, 4 \n\t" + +#define MULADDC_STOP \ + "swi r5, %0 \n\t" \ + "swi r4, %1 \n\t" \ + "swi r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4" "r5", "r6", "r7", "r8", \ + "r9", "r10", "r11", "r12", "r13" \ + ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_INIT \ + asm( \ + "ld.a %%a2, %3 \n\t" \ + "ld.a %%a3, %4 \n\t" \ + "ld.w %%d4, %5 \n\t" \ + "ld.w %%d1, %6 \n\t" \ + "xor %%d5, %%d5 \n\t" + +#define MULADDC_CORE \ + "ld.w %%d0, [%%a2+] \n\t" \ + "madd.u %%e2, %%e4, %%d0, %%d1 \n\t" \ + "ld.w %%d0, [%%a3] \n\t" \ + "addx %%d2, %%d2, %%d0 \n\t" \ + "addc %%d3, %%d3, 0 \n\t" \ + "mov %%d4, %%d3 \n\t" \ + "st.w [%%a3+], %%d2 \n\t" + +#define MULADDC_STOP \ + "st.w %0, %%d4 \n\t" \ + "st.a %1, %%a3 \n\t" \ + "st.a %2, %%a2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "e2", "d4", "a2", "a3" \ + ); + +#endif /* TriCore */ + +#if defined(__arm__) + +#if defined(__thumb__) && !defined(__thumb2__) + +#define MULADDC_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" \ + "lsr r7, r3, #16 \n\t" \ + "mov r9, r7 \n\t" \ + "lsl r7, r3, #16 \n\t" \ + "lsr r7, r7, #16 \n\t" \ + "mov r8, r7 \n\t" + +#define MULADDC_CORE \ + "ldmia r0!, {r6} \n\t" \ + "lsr r7, r6, #16 \n\t" \ + "lsl r6, r6, #16 \n\t" \ + "lsr r6, r6, #16 \n\t" \ + "mov r4, r8 \n\t" \ + "mul r4, r6 \n\t" \ + "mov r3, r9 \n\t" \ + "mul r6, r3 \n\t" \ + "mov r5, r9 \n\t" \ + "mul r5, r7 \n\t" \ + "mov r3, r8 \n\t" \ + "mul r7, r3 \n\t" \ + "lsr r3, r6, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "lsr r3, r7, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "add r4, r4, r2 \n\t" \ + "mov r2, #0 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r6, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r7, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "ldr r3, [r1] \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r2, r5 \n\t" \ + "stmia r1!, {r4} \n\t" + +#define MULADDC_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "r8", "r9", "cc" \ + ); + +#else + +#define MULADDC_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" + +#define MULADDC_CORE \ + "ldr r4, [r0], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r1] \n\t" \ + "umlal r2, r5, r3, r4 \n\t" \ + "adds r7, r6, r2 \n\t" \ + "adc r2, r5, #0 \n\t" \ + "str r7, [r1], #4 \n\t" + +#define MULADDC_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "cc" \ + ); + +#endif /* Thumb */ + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_INIT \ + asm( \ + "ldq $1, %3 \n\t" \ + "ldq $2, %4 \n\t" \ + "ldq $3, %5 \n\t" \ + "ldq $4, %6 \n\t" + +#define MULADDC_CORE \ + "ldq $6, 0($1) \n\t" \ + "addq $1, 8, $1 \n\t" \ + "mulq $6, $4, $7 \n\t" \ + "umulh $6, $4, $6 \n\t" \ + "addq $7, $3, $7 \n\t" \ + "cmpult $7, $3, $3 \n\t" \ + "ldq $5, 0($2) \n\t" \ + "addq $7, $5, $7 \n\t" \ + "cmpult $7, $5, $5 \n\t" \ + "stq $7, 0($2) \n\t" \ + "addq $2, 8, $2 \n\t" \ + "addq $6, $3, $3 \n\t" \ + "addq $5, $3, $3 \n\t" + +#define MULADDC_STOP \ + "stq $3, %0 \n\t" \ + "stq $2, %1 \n\t" \ + "stq $1, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$1", "$2", "$3", "$4", "$5", "$6", "$7" \ + ); +#endif /* Alpha */ + +#if defined(__mips__) && !defined(__mips64__) + +#define MULADDC_INIT \ + asm( \ + "lw $10, %3 \n\t" \ + "lw $11, %4 \n\t" \ + "lw $12, %5 \n\t" \ + "lw $13, %6 \n\t" + +#define MULADDC_CORE \ + "lw $14, 0($10) \n\t" \ + "multu $13, $14 \n\t" \ + "addi $10, $10, 4 \n\t" \ + "mflo $14 \n\t" \ + "mfhi $9 \n\t" \ + "addu $14, $12, $14 \n\t" \ + "lw $15, 0($11) \n\t" \ + "sltu $12, $14, $12 \n\t" \ + "addu $15, $14, $15 \n\t" \ + "sltu $14, $15, $14 \n\t" \ + "addu $12, $12, $9 \n\t" \ + "sw $15, 0($11) \n\t" \ + "addu $12, $12, $14 \n\t" \ + "addi $11, $11, 4 \n\t" + +#define MULADDC_STOP \ + "sw $12, %0 \n\t" \ + "sw $11, %1 \n\t" \ + "sw $10, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$9", "$10", "$11", "$12", "$13", "$14", "$15" \ + ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#if defined(POLARSSL_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_HUIT \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#else + +#define MULADDC_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi \ + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* POLARSSL_HAVE_ASM */ + +#if !defined(MULADDC_CORE) +#if defined(POLARSSL_HAVE_UDBL) + +#define MULADDC_INIT \ +{ \ + t_udbl r; \ + t_uint r0, r1; + +#define MULADDC_CORE \ + r = *(s++) * (t_udbl) b; \ + r0 = r; \ + r1 = r >> biL; \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#else +#define MULADDC_INIT \ +{ \ + t_uint s0, s1, b0, b1; \ + t_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_STOP \ +} + +#endif /* C (generic) */ +#endif /* C (longlong) */ + +#endif /* bn_mul.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/camellia.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/camellia.h new file mode 100644 index 0000000..8488d1d --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/camellia.h @@ -0,0 +1,229 @@ +/** + * \file camellia.h + * + * \brief Camellia block cipher + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_CAMELLIA_H +#define POLARSSL_CAMELLIA_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define CAMELLIA_ENCRYPT 1 +#define CAMELLIA_DECRYPT 0 + +#define POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH -0x0024 /**< Invalid key length. */ +#define POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026 /**< Invalid data input length. */ + +#if !defined(POLARSSL_CAMELLIA_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief CAMELLIA context structure + */ +typedef struct +{ + int nr; /*!< number of rounds */ + uint32_t rk[68]; /*!< CAMELLIA round keys */ +} +camellia_context; + +/** + * \brief Initialize CAMELLIA context + * + * \param ctx CAMELLIA context to be initialized + */ +void camellia_init( camellia_context *ctx ); + +/** + * \brief Clear CAMELLIA context + * + * \param ctx CAMELLIA context to be cleared + */ +void camellia_free( camellia_context *ctx ); + +/** + * \brief CAMELLIA key schedule (encryption) + * + * \param ctx CAMELLIA context to be initialized + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH + */ +int camellia_setkey_enc( camellia_context *ctx, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief CAMELLIA key schedule (decryption) + * + * \param ctx CAMELLIA context to be initialized + * \param key decryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH + */ +int camellia_setkey_dec( camellia_context *ctx, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief CAMELLIA-ECB block encryption/decryption + * + * \param ctx CAMELLIA context + * \param mode CAMELLIA_ENCRYPT or CAMELLIA_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if successful + */ +int camellia_crypt_ecb( camellia_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief CAMELLIA-CBC buffer encryption/decryption + * Length should be a multiple of the block + * size (16 bytes) + * + * \param ctx CAMELLIA context + * \param mode CAMELLIA_ENCRYPT or CAMELLIA_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or + * POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH + */ +int camellia_crypt_cbc( camellia_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/** + * \brief CAMELLIA-CFB128 buffer encryption/decryption + * + * Note: Due to the nature of CFB you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * camellia_setkey_enc() for both CAMELLIA_ENCRYPT and CAMELLIE_DECRYPT. + * + * \param ctx CAMELLIA context + * \param mode CAMELLIA_ENCRYPT or CAMELLIA_DECRYPT + * \param length length of the input data + * \param iv_off offset in IV (updated after use) + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or + * POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH + */ +int camellia_crypt_cfb128( camellia_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/** + * \brief CAMELLIA-CTR buffer encryption/decryption + * + * Warning: You have to keep the maximum use of your counter in mind! + * + * Note: Due to the nature of CTR you should use the same key schedule for + * both encryption and decryption. So a context initialized with + * camellia_setkey_enc() for both CAMELLIA_ENCRYPT and CAMELLIA_DECRYPT. + * + * \param ctx CAMELLIA context + * \param length The length of the data + * \param nc_off The offset in the current stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be 0 at the start of a stream. + * \param nonce_counter The 128-bit nonce and counter. + * \param stream_block The saved stream-block for resuming. Is overwritten + * by the function. + * \param input The input data stream + * \param output The output data stream + * + * \return 0 if successful + */ +int camellia_crypt_ctr( camellia_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_CAMELLIA_ALT */ +#include "camellia_alt.h" +#endif /* POLARSSL_CAMELLIA_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int camellia_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* camellia.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ccm.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ccm.h new file mode 100644 index 0000000..439152f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ccm.h @@ -0,0 +1,134 @@ +/** + * \file ccm.h + * + * \brief Counter with CBC-MAC (CCM) for 128-bit block ciphers + * + * Copyright (C) 2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_CCM_H +#define POLARSSL_CCM_H + +#include "cipher.h" + +#define POLARSSL_ERR_CCM_BAD_INPUT -0x000D /**< Bad input parameters to function. */ +#define POLARSSL_ERR_CCM_AUTH_FAILED -0x000F /**< Authenticated decryption failed. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief CCM context structure + */ +typedef struct { + cipher_context_t cipher_ctx; /*!< cipher context used */ +} +ccm_context; + +/** + * \brief CCM initialization (encryption and decryption) + * + * \param ctx CCM context to be initialized + * \param cipher cipher to use (a 128-bit block cipher) + * \param key encryption key + * \param keysize key size in bits (must be acceptable by the cipher) + * + * \return 0 if successful, or a cipher specific error code + */ +int ccm_init( ccm_context *ctx, cipher_id_t cipher, + const unsigned char *key, unsigned int keysize ); + +/** + * \brief Free a CCM context and underlying cipher sub-context + * + * \param ctx CCM context to free + */ +void ccm_free( ccm_context *ctx ); + +/** + * \brief CCM buffer encryption + * + * \param ctx CCM context + * \param length length of the input data in bytes + * \param iv nonce (initialization vector) + * \param iv_len length of IV in bytes + * must be 2, 3, 4, 5, 6, 7 or 8 + * \param add additional data + * \param add_len length of additional data in bytes + * must be less than 2^16 - 2^8 + * \param input buffer holding the input data + * \param output buffer for holding the output data + * must be at least 'length' bytes wide + * \param tag buffer for holding the tag + * \param tag_len length of the tag to generate in bytes + * must be 4, 6, 8, 10, 14 or 16 + * + * \note The tag is written to a separate buffer. To get the tag + * concatenated with the output as in the CCM spec, use + * tag = output + length and make sure the output buffer is + * at least length + tag_len wide. + * + * \return 0 if successful + */ +int ccm_encrypt_and_tag( ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ); + +/** + * \brief CCM buffer authenticated decryption + * + * \param ctx CCM context + * \param length length of the input data + * \param iv initialization vector + * \param iv_len length of IV + * \param add additional data + * \param add_len length of additional data + * \param input buffer holding the input data + * \param output buffer for holding the output data + * \param tag buffer holding the tag + * \param tag_len length of the tag + * + * \return 0 if successful and authenticated, + * POLARSSL_ERR_CCM_AUTH_FAILED if tag does not match + */ +int ccm_auth_decrypt( ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ); + +#if defined(POLARSSL_SELF_TEST) && defined(POLARSSL_AES_C) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int ccm_self_test( int verbose ); +#endif /* POLARSSL_SELF_TEST && POLARSSL_AES_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_CGM_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/certs.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/certs.h new file mode 100644 index 0000000..ba7c028 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/certs.h @@ -0,0 +1,77 @@ +/** + * \file certs.h + * + * \brief Sample certificates and DHM parameters for testing + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_CERTS_H +#define POLARSSL_CERTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Concatenation of all available CA certificates */ +extern const char test_ca_list[]; + +/* + * Convenience for users who just want a certificate: + * RSA by default, or ECDSA if RSA i not available + */ +extern const char *test_ca_crt; +extern const char *test_ca_key; +extern const char *test_ca_pwd; +extern const char *test_srv_crt; +extern const char *test_srv_key; +extern const char *test_cli_crt; +extern const char *test_cli_key; + +#if defined(POLARSSL_ECDSA_C) +extern const char test_ca_crt_ec[]; +extern const char test_ca_key_ec[]; +extern const char test_ca_pwd_ec[]; +extern const char test_srv_crt_ec[]; +extern const char test_srv_key_ec[]; +extern const char test_cli_crt_ec[]; +extern const char test_cli_key_ec[]; +#endif + +#if defined(POLARSSL_RSA_C) +extern const char test_ca_crt_rsa[]; +extern const char test_ca_key_rsa[]; +extern const char test_ca_pwd_rsa[]; +extern const char test_srv_crt_rsa[]; +extern const char test_srv_key_rsa[]; +extern const char test_cli_crt_rsa[]; +extern const char test_cli_key_rsa[]; +#endif + +#if defined(POLARSSL_DHM_C) +extern const char test_dhm_params[]; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* certs.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/check_config.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/check_config.h new file mode 100644 index 0000000..328b881 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/check_config.h @@ -0,0 +1,326 @@ +/** + * \file check_config.h + * + * \brief Consistency checks for configuration options + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * It is recommended to include this file from your config.h + * in order to catch dependency issues early. + */ + +#ifndef POLARSSL_CHECK_CONFIG_H +#define POLARSSL_CHECK_CONFIG_H + +#if defined(POLARSSL_AESNI_C) && !defined(POLARSSL_HAVE_ASM) +#error "POLARSSL_AESNI_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_CERTS_C) && !defined(POLARSSL_PEM_PARSE_C) +#error "POLARSSL_CERTS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_CTR_DRBG_C) && !defined(POLARSSL_AES_C) +#error "POLARSSL_CTR_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_DHM_C) && !defined(POLARSSL_BIGNUM_C) +#error "POLARSSL_DHM_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECDH_C) && !defined(POLARSSL_ECP_C) +#error "POLARSSL_ECDH_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECDSA_C) && \ + ( !defined(POLARSSL_ECP_C) || \ + !defined(POLARSSL_ASN1_PARSE_C) || \ + !defined(POLARSSL_ASN1_WRITE_C) ) +#error "POLARSSL_ECDSA_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) && !defined(POLARSSL_HMAC_DRBG_C) +#error "POLARSSL_ECDSA_DETERMINISTIC defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECP_C) && ( !defined(POLARSSL_BIGNUM_C) || ( \ + !defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP256R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP384R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP512R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) ) ) +#error "POLARSSL_ECP_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ENTROPY_C) && (!defined(POLARSSL_SHA512_C) && \ + !defined(POLARSSL_SHA256_C)) +#error "POLARSSL_ENTROPY_C defined, but not all prerequisites" +#endif +#if defined(POLARSSL_ENTROPY_C) && defined(POLARSSL_SHA512_C) && \ + defined(CTR_DRBG_ENTROPY_LEN) && (CTR_DRBG_ENTROPY_LEN > 64) +#error "CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(POLARSSL_ENTROPY_C) && \ + ( !defined(POLARSSL_SHA512_C) || defined(POLARSSL_ENTROPY_FORCE_SHA256) ) \ + && defined(CTR_DRBG_ENTROPY_LEN) && (CTR_DRBG_ENTROPY_LEN > 32) +#error "CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(POLARSSL_ENTROPY_C) && \ + defined(POLARSSL_ENTROPY_FORCE_SHA256) && !defined(POLARSSL_SHA256_C) +#error "POLARSSL_ENTROPY_FORCE_SHA256 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_GCM_C) && ( \ + !defined(POLARSSL_AES_C) && !defined(POLARSSL_CAMELLIA_C) ) +#error "POLARSSL_GCM_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_HAVEGE_C) && !defined(POLARSSL_TIMING_C) +#error "POLARSSL_HAVEGE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_HMAC_DRBG) && !defined(POLARSSL_MD_C) +#error "POLARSSL_HMAC_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) && !defined(POLARSSL_DHM_C) +#error "POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && \ + !defined(POLARSSL_ECDH_C) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + ( !defined(POLARSSL_DHM_C) || !defined(POLARSSL_RSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) || !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_RSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) || !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_ECDSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) && \ + ( !defined(POLARSSL_RSA_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ||\ + !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + ( !defined(POLARSSL_RSA_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ||\ + !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C) && \ + ( !defined(POLARSSL_PLATFORM_C) || !defined(POLARSSL_PLATFORM_MEMORY) ) +#error "POLARSSL_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PADLOCK_C) && !defined(POLARSSL_HAVE_ASM) +#error "POLARSSL_PADLOCK_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PBKDF2_C) && !defined(POLARSSL_MD_C) +#error "POLARSSL_PBKDF2_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PEM_PARSE_C) && !defined(POLARSSL_BASE64_C) +#error "POLARSSL_PEM_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PEM_WRITE_C) && !defined(POLARSSL_BASE64_C) +#error "POLARSSL_PEM_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PK_PARSE_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PK_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PK_WRITE_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PK_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PKCS11_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PKCS11_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_RSA_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) ) +#error "POLARSSL_RSA_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) && \ + ( !defined(POLARSSL_RSA_C) || !defined(POLARSSL_PKCS1_V21) ) +#error "POLARSSL_X509_RSASSA_PSS_SUPPORT defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_SSL3) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_SSL3 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_TLS1 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_1) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_TLS1_1 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) && ( !defined(POLARSSL_SHA1_C) && \ + !defined(POLARSSL_SHA256_C) && !defined(POLARSSL_SHA512_C) ) +#error "POLARSSL_SSL_PROTO_TLS1_2 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_CLI_C) && !defined(POLARSSL_SSL_TLS_C) +#error "POLARSSL_SSL_CLI_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && ( !defined(POLARSSL_CIPHER_C) || \ + !defined(POLARSSL_MD_C) ) +#error "POLARSSL_SSL_TLS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_SRV_C) && !defined(POLARSSL_SSL_TLS_C) +#error "POLARSSL_SSL_SRV_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (!defined(POLARSSL_SSL_PROTO_SSL3) && \ + !defined(POLARSSL_SSL_PROTO_TLS1) && !defined(POLARSSL_SSL_PROTO_TLS1_1) && \ + !defined(POLARSSL_SSL_PROTO_TLS1_2)) +#error "POLARSSL_SSL_TLS_C defined, but no protocols are active" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_SSL3) && \ + defined(POLARSSL_SSL_PROTO_TLS1_1) && !defined(POLARSSL_SSL_PROTO_TLS1)) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_TLS1) && \ + defined(POLARSSL_SSL_PROTO_TLS1_2) && !defined(POLARSSL_SSL_PROTO_TLS1_1)) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_SSL3) && \ + defined(POLARSSL_SSL_PROTO_TLS1_2) && (!defined(POLARSSL_SSL_PROTO_TLS1) || \ + !defined(POLARSSL_SSL_PROTO_TLS1_1))) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) && defined(POLARSSL_SSL_TLS_C) && \ + ( !defined(POLARSSL_AES_C) || !defined(POLARSSL_SHA256_C) || \ + !defined(POLARSSL_CIPHER_MODE_CBC) ) +#error "POLARSSL_SSL_SESSION_TICKETS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) && \ + !defined(POLARSSL_X509_CRT_PARSE_C) +#error "POLARSSL_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_THREADING_PTHREAD) +#if !defined(POLARSSL_THREADING_C) || defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_PTHREAD defined, but not all prerequisites" +#endif +#define POLARSSL_THREADING_IMPL +#endif + +#if defined(POLARSSL_THREADING_ALT) +#if !defined(POLARSSL_THREADING_C) || defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_ALT defined, but not all prerequisites" +#endif +#define POLARSSL_THREADING_IMPL +#endif + +#if defined(POLARSSL_THREADING_C) && !defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_C defined, single threading implementation required" +#endif +#undef POLARSSL_THREADING_IMPL + +#if defined(POLARSSL_VERSION_FEATURES) && !defined(POLARSSL_VERSION_C) +#error "POLARSSL_VERSION_FEATURES defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_USE_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) || !defined(POLARSSL_ASN1_PARSE_C) || \ + !defined(POLARSSL_PK_PARSE_C) ) +#error "POLARSSL_X509_USE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CREATE_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) || !defined(POLARSSL_ASN1_WRITE_C) || \ + !defined(POLARSSL_PK_WRITE_C) ) +#error "POLARSSL_X509_CREATE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CRT_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRL_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CRL_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CSR_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CSR_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRT_WRITE_C) && ( !defined(POLARSSL_X509_CREATE_C) ) +#error "POLARSSL_X509_CRT_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CSR_WRITE_C) && ( !defined(POLARSSL_X509_CREATE_C) ) +#error "POLARSSL_X509_CSR_WRITE_C defined, but not all prerequisites" +#endif + +#endif /* POLARSSL_CHECK_CONFIG_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher.h new file mode 100644 index 0000000..087e590 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher.h @@ -0,0 +1,760 @@ +/** + * \file cipher.h + * + * \brief Generic cipher wrapper. + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef POLARSSL_CIPHER_H +#define POLARSSL_CIPHER_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_GCM_C) || defined(POLARSSL_CCM_C) +#define POLARSSL_CIPHER_MODE_AEAD +#endif + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#define POLARSSL_CIPHER_MODE_WITH_PADDING +#endif + +#include + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +#define POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE -0x6080 /**< The selected feature is not available. */ +#define POLARSSL_ERR_CIPHER_BAD_INPUT_DATA -0x6100 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_CIPHER_ALLOC_FAILED -0x6180 /**< Failed to allocate memory. */ +#define POLARSSL_ERR_CIPHER_INVALID_PADDING -0x6200 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED -0x6280 /**< Decryption of block requires a full block. */ +#define POLARSSL_ERR_CIPHER_AUTH_FAILED -0x6300 /**< Authentication failed (for AEAD modes). */ + +#define POLARSSL_CIPHER_VARIABLE_IV_LEN 0x01 /**< Cipher accepts IVs of variable length */ +#define POLARSSL_CIPHER_VARIABLE_KEY_LEN 0x02 /**< Cipher accepts keys of variable length */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + POLARSSL_CIPHER_ID_NONE = 0, + POLARSSL_CIPHER_ID_NULL, + POLARSSL_CIPHER_ID_AES, + POLARSSL_CIPHER_ID_DES, + POLARSSL_CIPHER_ID_3DES, + POLARSSL_CIPHER_ID_CAMELLIA, + POLARSSL_CIPHER_ID_BLOWFISH, + POLARSSL_CIPHER_ID_ARC4, +} cipher_id_t; + +typedef enum { + POLARSSL_CIPHER_NONE = 0, + POLARSSL_CIPHER_NULL, + POLARSSL_CIPHER_AES_128_ECB, + POLARSSL_CIPHER_AES_192_ECB, + POLARSSL_CIPHER_AES_256_ECB, + POLARSSL_CIPHER_AES_128_CBC, + POLARSSL_CIPHER_AES_192_CBC, + POLARSSL_CIPHER_AES_256_CBC, + POLARSSL_CIPHER_AES_128_CFB128, + POLARSSL_CIPHER_AES_192_CFB128, + POLARSSL_CIPHER_AES_256_CFB128, + POLARSSL_CIPHER_AES_128_CTR, + POLARSSL_CIPHER_AES_192_CTR, + POLARSSL_CIPHER_AES_256_CTR, + POLARSSL_CIPHER_AES_128_GCM, + POLARSSL_CIPHER_AES_192_GCM, + POLARSSL_CIPHER_AES_256_GCM, + POLARSSL_CIPHER_CAMELLIA_128_ECB, + POLARSSL_CIPHER_CAMELLIA_192_ECB, + POLARSSL_CIPHER_CAMELLIA_256_ECB, + POLARSSL_CIPHER_CAMELLIA_128_CBC, + POLARSSL_CIPHER_CAMELLIA_192_CBC, + POLARSSL_CIPHER_CAMELLIA_256_CBC, + POLARSSL_CIPHER_CAMELLIA_128_CFB128, + POLARSSL_CIPHER_CAMELLIA_192_CFB128, + POLARSSL_CIPHER_CAMELLIA_256_CFB128, + POLARSSL_CIPHER_CAMELLIA_128_CTR, + POLARSSL_CIPHER_CAMELLIA_192_CTR, + POLARSSL_CIPHER_CAMELLIA_256_CTR, + POLARSSL_CIPHER_CAMELLIA_128_GCM, + POLARSSL_CIPHER_CAMELLIA_192_GCM, + POLARSSL_CIPHER_CAMELLIA_256_GCM, + POLARSSL_CIPHER_DES_ECB, + POLARSSL_CIPHER_DES_CBC, + POLARSSL_CIPHER_DES_EDE_ECB, + POLARSSL_CIPHER_DES_EDE_CBC, + POLARSSL_CIPHER_DES_EDE3_ECB, + POLARSSL_CIPHER_DES_EDE3_CBC, + POLARSSL_CIPHER_BLOWFISH_ECB, + POLARSSL_CIPHER_BLOWFISH_CBC, + POLARSSL_CIPHER_BLOWFISH_CFB64, + POLARSSL_CIPHER_BLOWFISH_CTR, + POLARSSL_CIPHER_ARC4_128, + POLARSSL_CIPHER_AES_128_CCM, + POLARSSL_CIPHER_AES_192_CCM, + POLARSSL_CIPHER_AES_256_CCM, + POLARSSL_CIPHER_CAMELLIA_128_CCM, + POLARSSL_CIPHER_CAMELLIA_192_CCM, + POLARSSL_CIPHER_CAMELLIA_256_CCM, +} cipher_type_t; + +typedef enum { + POLARSSL_MODE_NONE = 0, + POLARSSL_MODE_ECB, + POLARSSL_MODE_CBC, + POLARSSL_MODE_CFB, + POLARSSL_MODE_OFB, /* Unused! */ + POLARSSL_MODE_CTR, + POLARSSL_MODE_GCM, + POLARSSL_MODE_STREAM, + POLARSSL_MODE_CCM, +} cipher_mode_t; + +typedef enum { + POLARSSL_PADDING_PKCS7 = 0, /**< PKCS7 padding (default) */ + POLARSSL_PADDING_ONE_AND_ZEROS, /**< ISO/IEC 7816-4 padding */ + POLARSSL_PADDING_ZEROS_AND_LEN, /**< ANSI X.923 padding */ + POLARSSL_PADDING_ZEROS, /**< zero padding (not reversible!) */ + POLARSSL_PADDING_NONE, /**< never pad (full blocks only) */ +} cipher_padding_t; + +typedef enum { + POLARSSL_OPERATION_NONE = -1, + POLARSSL_DECRYPT = 0, + POLARSSL_ENCRYPT, +} operation_t; + +enum { + /** Undefined key length */ + POLARSSL_KEY_LENGTH_NONE = 0, + /** Key length, in bits (including parity), for DES keys */ + POLARSSL_KEY_LENGTH_DES = 64, + /** Key length, in bits (including parity), for DES in two key EDE */ + POLARSSL_KEY_LENGTH_DES_EDE = 128, + /** Key length, in bits (including parity), for DES in three-key EDE */ + POLARSSL_KEY_LENGTH_DES_EDE3 = 192, +}; + +/** Maximum length of any IV, in bytes */ +#define POLARSSL_MAX_IV_LENGTH 16 +/** Maximum block size of any cipher, in bytes */ +#define POLARSSL_MAX_BLOCK_LENGTH 16 + +/** + * Base cipher information. The non-mode specific functions and values. + */ +typedef struct { + + /** Base Cipher type (e.g. POLARSSL_CIPHER_ID_AES) */ + cipher_id_t cipher; + + /** Encrypt using ECB */ + int (*ecb_func)( void *ctx, operation_t mode, + const unsigned char *input, unsigned char *output ); + + /** Encrypt using CBC */ + int (*cbc_func)( void *ctx, operation_t mode, size_t length, + unsigned char *iv, const unsigned char *input, + unsigned char *output ); + + /** Encrypt using CFB (Full length) */ + int (*cfb_func)( void *ctx, operation_t mode, size_t length, size_t *iv_off, + unsigned char *iv, const unsigned char *input, + unsigned char *output ); + + /** Encrypt using CTR */ + int (*ctr_func)( void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output ); + + /** Encrypt using STREAM */ + int (*stream_func)( void *ctx, size_t length, + const unsigned char *input, unsigned char *output ); + + /** Set key for encryption purposes */ + int (*setkey_enc_func)( void *ctx, const unsigned char *key, + unsigned int key_length ); + + /** Set key for decryption purposes */ + int (*setkey_dec_func)( void *ctx, const unsigned char *key, + unsigned int key_length); + + /** Allocate a new context */ + void * (*ctx_alloc_func)( void ); + + /** Free the given context */ + void (*ctx_free_func)( void *ctx ); + +} cipher_base_t; + +/** + * Cipher information. Allows cipher functions to be called in a generic way. + */ +typedef struct { + /** Full cipher identifier (e.g. POLARSSL_CIPHER_AES_256_CBC) */ + cipher_type_t type; + + /** Cipher mode (e.g. POLARSSL_MODE_CBC) */ + cipher_mode_t mode; + + /** Cipher key length, in bits (default length for variable sized ciphers) + * (Includes parity bits for ciphers like DES) */ + unsigned int key_length; + + /** Name of the cipher */ + const char * name; + + /** IV/NONCE size, in bytes. + * For cipher that accept many sizes: recommended size */ + unsigned int iv_size; + + /** Flags for variable IV size, variable key size, etc. */ + int flags; + + /** block size, in bytes */ + unsigned int block_size; + + /** Base cipher information and functions */ + const cipher_base_t *base; + +} cipher_info_t; + +/** + * Generic cipher context. + */ +typedef struct { + /** Information about the associated cipher */ + const cipher_info_t *cipher_info; + + /** Key length to use */ + int key_length; + + /** Operation that the context's key has been initialised for */ + operation_t operation; + + /** Padding functions to use, if relevant for cipher mode */ + void (*add_padding)( unsigned char *output, size_t olen, size_t data_len ); + int (*get_padding)( unsigned char *input, size_t ilen, size_t *data_len ); + + /** Buffer for data that hasn't been encrypted yet */ + unsigned char unprocessed_data[POLARSSL_MAX_BLOCK_LENGTH]; + + /** Number of bytes that still need processing */ + size_t unprocessed_len; + + /** Current IV or NONCE_COUNTER for CTR-mode */ + unsigned char iv[POLARSSL_MAX_IV_LENGTH]; + + /** IV size in bytes (for ciphers with variable-length IVs) */ + size_t iv_size; + + /** Cipher-specific context */ + void *cipher_ctx; +} cipher_context_t; + +/** + * \brief Returns the list of ciphers supported by the generic cipher module. + * + * \return a statically allocated array of ciphers, the last entry + * is 0. + */ +const int *cipher_list( void ); + +/** + * \brief Returns the cipher information structure associated + * with the given cipher name. + * + * \param cipher_name Name of the cipher to search for. + * + * \return the cipher information structure associated with the + * given cipher_name, or NULL if not found. + */ +const cipher_info_t *cipher_info_from_string( const char *cipher_name ); + +/** + * \brief Returns the cipher information structure associated + * with the given cipher type. + * + * \param cipher_type Type of the cipher to search for. + * + * \return the cipher information structure associated with the + * given cipher_type, or NULL if not found. + */ +const cipher_info_t *cipher_info_from_type( const cipher_type_t cipher_type ); + +/** + * \brief Returns the cipher information structure associated + * with the given cipher id, key size and mode. + * + * \param cipher_id Id of the cipher to search for + * (e.g. POLARSSL_CIPHER_ID_AES) + * \param key_length Length of the key in bits + * \param mode Cipher mode (e.g. POLARSSL_MODE_CBC) + * + * \return the cipher information structure associated with the + * given cipher_type, or NULL if not found. + */ +const cipher_info_t *cipher_info_from_values( const cipher_id_t cipher_id, + int key_length, + const cipher_mode_t mode ); + +/** + * \brief Initialize a cipher_context (as NONE) + */ +void cipher_init( cipher_context_t *ctx ); + +/** + * \brief Free and clear the cipher-specific context of ctx. + * Freeing ctx itself remains the responsibility of the + * caller. + */ +void cipher_free( cipher_context_t *ctx ); + +/** + * \brief Initialises and fills the cipher context structure with + * the appropriate values. + * + * \note Currently also clears structure. In future versions you + * will be required to call cipher_init() on the structure + * first. + * + * \param ctx context to initialise. May not be NULL. + * \param cipher_info cipher to use. + * + * \return 0 on success, + * POLARSSL_ERR_CIPHER_BAD_INPUT_DATA on parameter failure, + * POLARSSL_ERR_CIPHER_ALLOC_FAILED if allocation of the + * cipher-specific context failed. + */ +int cipher_init_ctx( cipher_context_t *ctx, const cipher_info_t *cipher_info ); + +/** + * \brief Free the cipher-specific context of ctx. Freeing ctx + * itself remains the responsibility of the caller. + * + * \note Deprecated: Redirects to cipher_free() + * + * \param ctx Free the cipher-specific context + * + * \returns 0 + */ +int cipher_free_ctx( cipher_context_t *ctx ); + +/** + * \brief Returns the block size of the given cipher. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return size of the cipher's blocks, or 0 if ctx has not been + * initialised. + */ +static inline unsigned int cipher_get_block_size( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return 0; + + return ctx->cipher_info->block_size; +} + +/** + * \brief Returns the mode of operation for the cipher. + * (e.g. POLARSSL_MODE_CBC) + * + * \param ctx cipher's context. Must have been initialised. + * + * \return mode of operation, or POLARSSL_MODE_NONE if ctx + * has not been initialised. + */ +static inline cipher_mode_t cipher_get_cipher_mode( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return POLARSSL_MODE_NONE; + + return ctx->cipher_info->mode; +} + +/** + * \brief Returns the size of the cipher's IV/NONCE in bytes. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return If IV has not been set yet: (recommended) IV size + * (0 for ciphers not using IV/NONCE). + * If IV has already been set: actual size. + */ +static inline int cipher_get_iv_size( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return 0; + + if( ctx->iv_size != 0 ) + return (int) ctx->iv_size; + + return ctx->cipher_info->iv_size; +} + +/** + * \brief Returns the type of the given cipher. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return type of the cipher, or POLARSSL_CIPHER_NONE if ctx has + * not been initialised. + */ +static inline cipher_type_t cipher_get_type( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return POLARSSL_CIPHER_NONE; + + return ctx->cipher_info->type; +} + +/** + * \brief Returns the name of the given cipher, as a string. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return name of the cipher, or NULL if ctx was not initialised. + */ +static inline const char *cipher_get_name( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return 0; + + return ctx->cipher_info->name; +} + +/** + * \brief Returns the key length of the cipher. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return cipher's key length, in bits, or + * POLARSSL_KEY_LENGTH_NONE if ctx has not been + * initialised. + */ +static inline int cipher_get_key_size ( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return POLARSSL_KEY_LENGTH_NONE; + + return ctx->cipher_info->key_length; +} + +/** + * \brief Returns the operation of the given cipher. + * + * \param ctx cipher's context. Must have been initialised. + * + * \return operation (POLARSSL_ENCRYPT or POLARSSL_DECRYPT), + * or POLARSSL_OPERATION_NONE if ctx has not been + * initialised. + */ +static inline operation_t cipher_get_operation( const cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return POLARSSL_OPERATION_NONE; + + return ctx->operation; +} + +/** + * \brief Set the key to use with the given context. + * + * \param ctx generic cipher context. May not be NULL. Must have been + * initialised using cipher_context_from_type or + * cipher_context_from_string. + * \param key The key to use. + * \param key_length key length to use, in bits. + * \param operation Operation that the key will be used for, either + * POLARSSL_ENCRYPT or POLARSSL_DECRYPT. + * + * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if + * parameter verification fails or a cipher specific + * error code. + */ +int cipher_setkey( cipher_context_t *ctx, const unsigned char *key, + int key_length, const operation_t operation ); + +#if defined(POLARSSL_CIPHER_MODE_WITH_PADDING) +/** + * \brief Set padding mode, for cipher modes that use padding. + * (Default: PKCS7 padding.) + * + * \param ctx generic cipher context + * \param mode padding mode + * + * \returns 0 on success, POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE + * if selected padding mode is not supported, or + * POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if the cipher mode + * does not support padding. + */ +int cipher_set_padding_mode( cipher_context_t *ctx, cipher_padding_t mode ); +#endif /* POLARSSL_CIPHER_MODE_WITH_PADDING */ + +/** + * \brief Set the initialization vector (IV) or nonce + * + * \param ctx generic cipher context + * \param iv IV to use (or NONCE_COUNTER for CTR-mode ciphers) + * \param iv_len IV length for ciphers with variable-size IV; + * discarded by ciphers with fixed-size IV. + * + * \returns 0 on success, or POLARSSL_ERR_CIPHER_BAD_INPUT_DATA + * + * \note Some ciphers don't use IVs nor NONCE. For these + * ciphers, this function has no effect. + */ +int cipher_set_iv( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len ); + +/** + * \brief Finish preparation of the given context + * + * \param ctx generic cipher context + * + * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA + * if parameter verification fails. + */ +int cipher_reset( cipher_context_t *ctx ); + +#if defined(POLARSSL_GCM_C) +/** + * \brief Add additional data (for AEAD ciphers). + * Currently only supported with GCM. + * Must be called exactly once, after cipher_reset(). + * + * \param ctx generic cipher context + * \param ad Additional data to use. + * \param ad_len Length of ad. + * + * \return 0 on success, or a specific error code. + */ +int cipher_update_ad( cipher_context_t *ctx, + const unsigned char *ad, size_t ad_len ); +#endif /* POLARSSL_GCM_C */ + +/** + * \brief Generic cipher update function. Encrypts/decrypts + * using the given cipher context. Writes as many block + * size'd blocks of data as possible to output. Any data + * that cannot be written immediately will either be added + * to the next block, or flushed when cipher_final is + * called. + * Exception: for POLARSSL_MODE_ECB, expects single block + * in size (e.g. 16 bytes for AES) + * + * \param ctx generic cipher context + * \param input buffer holding the input data + * \param ilen length of the input data + * \param output buffer for the output data. Should be able to hold at + * least ilen + block_size. Cannot be the same buffer as + * input! + * \param olen length of the output data, will be filled with the + * actual number of bytes written. + * + * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if + * parameter verification fails, + * POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE on an + * unsupported mode for a cipher or a cipher specific + * error code. + * + * \note If the underlying cipher is GCM, all calls to this + * function, except the last one before cipher_finish(), + * must have ilen a multiple of the block size. + */ +int cipher_update( cipher_context_t *ctx, const unsigned char *input, + size_t ilen, unsigned char *output, size_t *olen ); + +/** + * \brief Generic cipher finalisation function. If data still + * needs to be flushed from an incomplete block, data + * contained within it will be padded with the size of + * the last block, and written to the output buffer. + * + * \param ctx Generic cipher context + * \param output buffer to write data to. Needs block_size available. + * \param olen length of the data written to the output buffer. + * + * \returns 0 on success, POLARSSL_ERR_CIPHER_BAD_INPUT_DATA if + * parameter verification fails, + * POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED if decryption + * expected a full block but was not provided one, + * POLARSSL_ERR_CIPHER_INVALID_PADDING on invalid padding + * while decrypting or a cipher specific error code. + */ +int cipher_finish( cipher_context_t *ctx, + unsigned char *output, size_t *olen ); + +#if defined(POLARSSL_GCM_C) +/** + * \brief Write tag for AEAD ciphers. + * Currently only supported with GCM. + * Must be called after cipher_finish(). + * + * \param ctx Generic cipher context + * \param tag buffer to write the tag + * \param tag_len Length of the tag to write + * + * \return 0 on success, or a specific error code. + */ +int cipher_write_tag( cipher_context_t *ctx, + unsigned char *tag, size_t tag_len ); + +/** + * \brief Check tag for AEAD ciphers. + * Currently only supported with GCM. + * Must be called after cipher_finish(). + * + * \param ctx Generic cipher context + * \param tag Buffer holding the tag + * \param tag_len Length of the tag to check + * + * \return 0 on success, or a specific error code. + */ +int cipher_check_tag( cipher_context_t *ctx, + const unsigned char *tag, size_t tag_len ); +#endif /* POLARSSL_GCM_C */ + +/** + * \brief Generic all-in-one encryption/decryption + * (for all ciphers except AEAD constructs). + * + * \param ctx generic cipher context + * \param iv IV to use (or NONCE_COUNTER for CTR-mode ciphers) + * \param iv_len IV length for ciphers with variable-size IV; + * discarded by ciphers with fixed-size IV. + * \param input buffer holding the input data + * \param ilen length of the input data + * \param output buffer for the output data. Should be able to hold at + * least ilen + block_size. Cannot be the same buffer as + * input! + * \param olen length of the output data, will be filled with the + * actual number of bytes written. + * + * \note Some ciphers don't use IVs nor NONCE. For these + * ciphers, use iv = NULL and iv_len = 0. + * + * \returns 0 on success, or + * POLARSSL_ERR_CIPHER_BAD_INPUT_DATA, or + * POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED if decryption + * expected a full block but was not provided one, or + * POLARSSL_ERR_CIPHER_INVALID_PADDING on invalid padding + * while decrypting, or + * a cipher specific error code. + */ +int cipher_crypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen ); + +#if defined(POLARSSL_CIPHER_MODE_AEAD) +/** + * \brief Generic autenticated encryption (AEAD ciphers). + * + * \param ctx generic cipher context + * \param iv IV to use (or NONCE_COUNTER for CTR-mode ciphers) + * \param iv_len IV length for ciphers with variable-size IV; + * discarded by ciphers with fixed-size IV. + * \param ad Additional data to authenticate. + * \param ad_len Length of ad. + * \param input buffer holding the input data + * \param ilen length of the input data + * \param output buffer for the output data. + * Should be able to hold at least ilen. + * \param olen length of the output data, will be filled with the + * actual number of bytes written. + * \param tag buffer for the authentication tag + * \param tag_len desired tag length + * + * \returns 0 on success, or + * POLARSSL_ERR_CIPHER_BAD_INPUT_DATA, or + * a cipher specific error code. + */ +int cipher_auth_encrypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + unsigned char *tag, size_t tag_len ); + +/** + * \brief Generic autenticated decryption (AEAD ciphers). + * + * \param ctx generic cipher context + * \param iv IV to use (or NONCE_COUNTER for CTR-mode ciphers) + * \param iv_len IV length for ciphers with variable-size IV; + * discarded by ciphers with fixed-size IV. + * \param ad Additional data to be authenticated. + * \param ad_len Length of ad. + * \param input buffer holding the input data + * \param ilen length of the input data + * \param output buffer for the output data. + * Should be able to hold at least ilen. + * \param olen length of the output data, will be filled with the + * actual number of bytes written. + * \param tag buffer holding the authentication tag + * \param tag_len length of the authentication tag + * + * \returns 0 on success, or + * POLARSSL_ERR_CIPHER_BAD_INPUT_DATA, or + * POLARSSL_ERR_CIPHER_AUTH_FAILED if data isn't authentic, + * or a cipher specific error code. + * + * \note If the data is not authentic, then the output buffer + * is zeroed out to prevent the unauthentic plaintext to + * be used by mistake, making this interface safer. + */ +int cipher_auth_decrypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + const unsigned char *tag, size_t tag_len ); +#endif /* POLARSSL_CIPHER_MODE_AEAD */ + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int cipher_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_CIPHER_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher_wrap.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher_wrap.h new file mode 100644 index 0000000..46bc757 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/cipher_wrap.h @@ -0,0 +1,57 @@ +/** + * \file cipher_wrap.h + * + * \brief Cipher wrappers. + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_CIPHER_WRAP_H +#define POLARSSL_CIPHER_WRAP_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#include "cipher.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + cipher_type_t type; + const cipher_info_t *info; +} cipher_definition_t; + +extern const cipher_definition_t cipher_definitions[]; + +extern int supported_ciphers[]; + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_CIPHER_WRAP_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/compat-1.2.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/compat-1.2.h new file mode 100644 index 0000000..15b5aa1 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/compat-1.2.h @@ -0,0 +1,389 @@ +/** + * \file compat-1.2.h + * + * \brief Backwards compatibility header for PolarSSL-1.2 from PolarSSL-1.3 + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_COMPAT_1_2_H +#define POLARSSL_COMPAT_1_2_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +// Comment out to disable prototype change warnings +#define SHOW_PROTOTYPE_CHANGE_WARNINGS + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /* _MSC_VER */ + +#if defined(_MSC_VER) +// MSVC does not support #warning +#undef SHOW_PROTOTYPE_CHANGE_WARNINGS +#endif + +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "You can disable these warnings by commenting SHOW_PROTOTYPE_CHANGE_WARNINGS in compat-1.2.h" +#endif + +#if defined(POLARSSL_SHA256_C) +#define POLARSSL_SHA2_C +#include "sha256.h" + +/* + * SHA-2 -> SHA-256 + */ +typedef sha256_context sha2_context; + +static inline void sha2_starts( sha256_context *ctx, int is224 ) { + sha256_starts( ctx, is224 ); +} +static inline void sha2_update( sha256_context *ctx, const unsigned char *input, + size_t ilen ) { + sha256_update( ctx, input, ilen ); +} +static inline void sha2_finish( sha256_context *ctx, unsigned char output[32] ) { + sha256_finish( ctx, output ); +} +static inline int sha2_file( const char *path, unsigned char output[32], int is224 ) { + return sha256_file( path, output, is224 ); +} +static inline void sha2( const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) { + sha256( input, ilen, output, is224 ); +} +static inline void sha2_hmac_starts( sha256_context *ctx, const unsigned char *key, + size_t keylen, int is224 ) { + sha256_hmac_starts( ctx, key, keylen, is224 ); +} +static inline void sha2_hmac_update( sha256_context *ctx, const unsigned char *input, size_t ilen ) { + sha256_hmac_update( ctx, input, ilen ); +} +static inline void sha2_hmac_finish( sha256_context *ctx, unsigned char output[32] ) { + sha256_hmac_finish( ctx, output ); +} +static inline void sha2_hmac_reset( sha256_context *ctx ) { + sha256_hmac_reset( ctx ); +} +static inline void sha2_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) { + sha256_hmac( key, keylen, input, ilen, output, is224 ); +} +static inline int sha2_self_test( int verbose ) { + return sha256_self_test( verbose ); +} +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) +#define POLARSSL_SHA4_C +#include "sha512.h" + +/* + * SHA-4 -> SHA-512 + */ +typedef sha512_context sha4_context; + +static inline void sha4_starts( sha512_context *ctx, int is384 ) { + sha512_starts( ctx, is384 ); +} +static inline void sha4_update( sha512_context *ctx, const unsigned char *input, + size_t ilen ) { + sha512_update( ctx, input, ilen ); +} +static inline void sha4_finish( sha512_context *ctx, unsigned char output[64] ) { + sha512_finish( ctx, output ); +} +static inline int sha4_file( const char *path, unsigned char output[64], int is384 ) { + return sha512_file( path, output, is384 ); +} +static inline void sha4( const unsigned char *input, size_t ilen, + unsigned char output[32], int is384 ) { + sha512( input, ilen, output, is384 ); +} +static inline void sha4_hmac_starts( sha512_context *ctx, const unsigned char *key, + size_t keylen, int is384 ) { + sha512_hmac_starts( ctx, key, keylen, is384 ); +} +static inline void sha4_hmac_update( sha512_context *ctx, const unsigned char *input, size_t ilen ) { + sha512_hmac_update( ctx, input, ilen ); +} +static inline void sha4_hmac_finish( sha512_context *ctx, unsigned char output[64] ) { + sha512_hmac_finish( ctx, output ); +} +static inline void sha4_hmac_reset( sha512_context *ctx ) { + sha512_hmac_reset( ctx ); +} +static inline void sha4_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[64], int is384 ) { + sha512_hmac( key, keylen, input, ilen, output, is384 ); +} +static inline int sha4_self_test( int verbose ) { + return sha512_self_test( verbose ); +} +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_CIPHER_C) +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "cipher_reset() prototype changed. Manual change required if used" +#endif +#endif + +#if defined(POLARSSL_RSA_C) +#define SIG_RSA_RAW POLARSSL_MD_NONE +#define SIG_RSA_MD2 POLARSSL_MD_MD2 +#define SIG_RSA_MD4 POLARSSL_MD_MD4 +#define SIG_RSA_MD5 POLARSSL_MD_MD5 +#define SIG_RSA_SHA1 POLARSSL_MD_SHA1 +#define SIG_RSA_SHA224 POLARSSL_MD_SHA224 +#define SIG_RSA_SHA256 POLARSSL_MD_SHA256 +#define SIG_RSA_SHA384 POLARSSL_MD_SHA384 +#define SIG_RSA_SHA512 POLARSSL_MD_SHA512 +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "rsa_pkcs1_verify() prototype changed. Manual change required if used" +#warning "rsa_pkcs1_decrypt() prototype changed. Manual change required if used" +#endif +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_DHM_C) +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "dhm_calc_secret() prototype changed. Manual change required if used" +#endif +#endif + +#if defined(POLARSSL_GCM_C) +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "gcm_init() prototype changed. Manual change required if used" +#endif +#endif + +#if defined(POLARSSL_SSL_CLI_C) +#if defined(SHOW_PROTOTYPE_CHANGE_WARNINGS) +#warning "ssl_set_own_cert() prototype changed. Change to ssl_set_own_cert_rsa(). Manual change required if used" +#endif +#endif + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +#include "x509.h" + +#define POLARSSL_ERR_X509_CERT_INVALID_FORMAT POLARSSL_ERR_X509_INVALID_FORMAT +#define POLARSSL_ERR_X509_CERT_INVALID_VERSION POLARSSL_ERR_X509_INVALID_VERSION +#define POLARSSL_ERR_X509_CERT_INVALID_ALG POLARSSL_ERR_X509_INVALID_ALG +#define POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG POLARSSL_ERR_X509_UNKNOWN_SIG_ALG +#define POLARSSL_ERR_X509_CERT_INVALID_NAME POLARSSL_ERR_X509_INVALID_NAME +#define POLARSSL_ERR_X509_CERT_INVALID_DATE POLARSSL_ERR_X509_INVALID_DATE +#define POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS POLARSSL_ERR_X509_INVALID_EXTENSIONS +#define POLARSSL_ERR_X509_CERT_SIG_MISMATCH POLARSSL_ERR_X509_SIG_MISMATCH +#define POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE POLARSSL_ERR_X509_INVALID_SIGNATURE +#define POLARSSL_ERR_X509_CERT_INVALID_SERIAL POLARSSL_ERR_X509_INVALID_SERIAL +#define POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION POLARSSL_ERR_X509_UNKNOWN_VERSION + +static inline int x509parse_serial_gets( char *buf, size_t size, const x509_buf *serial ) { + return x509_serial_gets( buf, size, serial ); +} +static inline int x509parse_dn_gets( char *buf, size_t size, const x509_name *dn ) { + return x509_dn_gets( buf, size, dn ); +} +static inline int x509parse_time_expired( const x509_time *time ) { + return x509_time_expired( time ); +} +#endif /* POLARSSL_X509_USE_C || POLARSSL_X509_CREATE_C */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) +#define POLARSSL_X509_PARSE_C +#include "x509_crt.h" +typedef x509_crt x509_cert; + +static inline int x509parse_crt_der( x509_cert *chain, const unsigned char *buf, + size_t buflen ) { + return x509_crt_parse_der( chain, buf, buflen ); +} +static inline int x509parse_crt( x509_cert *chain, const unsigned char *buf, size_t buflen ) { + return x509_crt_parse( chain, buf, buflen ); +} +static inline int x509parse_crtfile( x509_cert *chain, const char *path ) { + return x509_crt_parse_file( chain, path ); +} +static inline int x509parse_crtpath( x509_cert *chain, const char *path ) { + return x509_crt_parse_path( chain, path ); +} +static inline int x509parse_cert_info( char *buf, size_t size, const char *prefix, + const x509_cert *crt ) { + return x509_crt_info( buf, size, prefix, crt ); +} +static inline int x509parse_verify( x509_cert *crt, x509_cert *trust_ca, + x509_crl *ca_crl, const char *cn, int *flags, + int (*f_vrfy)(void *, x509_cert *, int, int *), + void *p_vrfy ) { + return x509_crt_verify( crt, trust_ca, ca_crl, cn, flags, f_vrfy, p_vrfy ); +} +static inline int x509parse_revoked( const x509_cert *crt, const x509_crl *crl ) { + return x509_crt_revoked( crt, crl ); +} +static inline void x509_free( x509_cert *crt ) { + x509_crt_free( crt ); +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#if defined(POLARSSL_X509_CRL_PARSE_C) +#define POLARSSL_X509_PARSE_C +#include "x509_crl.h" +static inline int x509parse_crl( x509_crl *chain, const unsigned char *buf, size_t buflen ) { + return x509_crl_parse( chain, buf, buflen ); +} +static inline int x509parse_crlfile( x509_crl *chain, const char *path ) { + return x509_crl_parse_file( chain, path ); +} +static inline int x509parse_crl_info( char *buf, size_t size, const char *prefix, + const x509_crl *crl ) { + return x509_crl_info( buf, size, prefix, crl ); +} +#endif /* POLARSSL_X509_CRL_PARSE_C */ + +#if defined(POLARSSL_X509_CSR_PARSE_C) +#define POLARSSL_X509_PARSE_C +#include "x509_csr.h" +static inline int x509parse_csr( x509_csr *csr, const unsigned char *buf, size_t buflen ) { + return x509_csr_parse( csr, buf, buflen ); +} +static inline int x509parse_csrfile( x509_csr *csr, const char *path ) { + return x509_csr_parse_file( csr, path ); +} +static inline int x509parse_csr_info( char *buf, size_t size, const char *prefix, + const x509_csr *csr ) { + return x509_csr_info( buf, size, prefix, csr ); +} +#endif /* POLARSSL_X509_CSR_PARSE_C */ + +#if defined(POLARSSL_SSL_TLS_C) +#include "ssl_ciphersuites.h" + +#define ssl_default_ciphersuites ssl_list_ciphersuites() +#endif + +#if defined(POLARSSL_PK_PARSE_C) && defined(POLARSSL_RSA_C) +#include "rsa.h" +#include "pk.h" + +#define POLARSSL_ERR_X509_PASSWORD_MISMATCH POLARSSL_ERR_PK_PASSWORD_MISMATCH +#define POLARSSL_ERR_X509_KEY_INVALID_FORMAT POLARSSL_ERR_PK_KEY_INVALID_FORMAT +#define POLARSSL_ERR_X509_UNKNOWN_PK_ALG POLARSSL_ERR_PK_UNKNOWN_PK_ALG +#define POLARSSL_ERR_X509_CERT_INVALID_PUBKEY POLARSSL_ERR_PK_INVALID_PUBKEY + +#if defined(POLARSSL_FS_IO) +static inline int x509parse_keyfile( rsa_context *rsa, const char *path, + const char *pwd ) { + int ret; + pk_context pk; + pk_init( &pk ); + ret = pk_parse_keyfile( &pk, path, pwd ); + if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) ) + ret = POLARSSL_ERR_PK_TYPE_MISMATCH; + if( ret == 0 ) + rsa_copy( rsa, pk_rsa( pk ) ); + else + rsa_free( rsa ); + pk_free( &pk ); + return( ret ); +} +static inline int x509parse_public_keyfile( rsa_context *rsa, const char *path ) { + int ret; + pk_context pk; + pk_init( &pk ); + ret = pk_parse_public_keyfile( &pk, path ); + if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) ) + ret = POLARSSL_ERR_PK_TYPE_MISMATCH; + if( ret == 0 ) + rsa_copy( rsa, pk_rsa( pk ) ); + else + rsa_free( rsa ); + pk_free( &pk ); + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +static inline int x509parse_key( rsa_context *rsa, const unsigned char *key, + size_t keylen, + const unsigned char *pwd, size_t pwdlen ) { + int ret; + pk_context pk; + pk_init( &pk ); + ret = pk_parse_key( &pk, key, keylen, pwd, pwdlen ); + if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) ) + ret = POLARSSL_ERR_PK_TYPE_MISMATCH; + if( ret == 0 ) + rsa_copy( rsa, pk_rsa( pk ) ); + else + rsa_free( rsa ); + pk_free( &pk ); + return( ret ); +} + +static inline int x509parse_public_key( rsa_context *rsa, + const unsigned char *key, size_t keylen ) +{ + int ret; + pk_context pk; + pk_init( &pk ); + ret = pk_parse_public_key( &pk, key, keylen ); + if( ret == 0 && ! pk_can_do( &pk, POLARSSL_PK_RSA ) ) + ret = POLARSSL_ERR_PK_TYPE_MISMATCH; + if( ret == 0 ) + rsa_copy( rsa, pk_rsa( pk ) ); + else + rsa_free( rsa ); + pk_free( &pk ); + return( ret ); +} +#endif /* POLARSSL_PK_PARSE_C && POLARSSL_RSA_C */ + +#if defined(POLARSSL_PK_WRITE_C) && defined(POLARSSL_RSA_C) +#include "pk.h" +static inline int x509_write_pubkey_der( unsigned char *buf, size_t len, rsa_context *rsa ) { + int ret; + pk_context ctx; + if( ( ret = pk_init_ctx( &ctx, pk_info_from_type( POLARSSL_PK_RSA ) ) ) != 0 ) return( ret ); + if( ( ret = rsa_copy( pk_rsa( ctx ), rsa ) ) != 0 ) return( ret ); + ret = pk_write_pubkey_der( &ctx, buf, len ); + pk_free( &ctx ); + return( ret ); +} +static inline int x509_write_key_der( unsigned char *buf, size_t len, rsa_context *rsa ) { + int ret; + pk_context ctx; + if( ( ret = pk_init_ctx( &ctx, pk_info_from_type( POLARSSL_PK_RSA ) ) ) != 0 ) return( ret ); + if( ( ret = rsa_copy( pk_rsa( ctx ), rsa ) ) != 0 ) return( ret ); + ret = pk_write_key_der( &ctx, buf, len ); + pk_free( &ctx ); + return( ret ); +} +#endif /* POLARSSL_PK_WRITE_C && POLARSSL_RSA_C */ +#endif /* compat-1.2.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config.h new file mode 100644 index 0000000..99c1dc8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config.h @@ -0,0 +1,14 @@ +#define CONFIG_SSL_RSA 1 +#define CONFIG_SSL_SRP 0 +#define POLARSSL_PEM_PARSE_C 1 +#define POLARSSL_BASE64_C 1 + +#include "platform_stdlib.h" + +#if CONFIG_SSL_RSA +#include "polarssl/config_rsa.h" +#elif CONFIG_SSL_SRP +#include "polarssl/config_srp.h" +#else +#include "polarssl/config_all.h" +#endif diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_all.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_all.h new file mode 100644 index 0000000..d43365f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_all.h @@ -0,0 +1,2180 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def POLARSSL_HAVE_INT8 + * + * The system uses 8-bit wide native integers. + * + * Uncomment if native integers are 8-bit wide. + */ +//#define POLARSSL_HAVE_INT8 + +/** + * \def POLARSSL_HAVE_INT16 + * + * The system uses 16-bit wide native integers. + * + * Uncomment if native integers are 16-bit wide. + */ +//#define POLARSSL_HAVE_INT16 + +/** + * \def POLARSSL_HAVE_LONGLONG + * + * The compiler supports the 'long long' type. + * (Only used on 32-bit platforms) + */ +#define POLARSSL_HAVE_LONGLONG + +/** + * \def POLARSSL_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + * Comment to disable the use of assembly code. + */ +#define POLARSSL_HAVE_ASM + +/** + * \def POLARSSL_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define POLARSSL_HAVE_SSE2 + +/** + * \def POLARSSL_HAVE_TIME + * + * System has time.h and time() / localtime() / gettimeofday(). + * + * Comment if your system does not support time functions + */ +#define POLARSSL_HAVE_TIME + +/** + * \def POLARSSL_HAVE_IPV6 + * + * System supports the basic socket interface for IPv6 (RFC 3493), + * specifically getaddrinfo(), freeaddrinfo() and struct sockaddr_storage. + * + * Note: on Windows/MingW, XP or higher is required. + * + * Comment if your system does not support the IPv6 socket interface + */ +#define POLARSSL_HAVE_IPV6 + +/** + * \def POLARSSL_PLATFORM_MEMORY + * + * Enable the memory allocation layer. + * + * By default PolarSSL uses the system-provided malloc() and free(). + * This allows different allocators (self-implemented or provided) to be + * provided to the platform abstraction layer. + * + * Enabling POLARSSL_PLATFORM_MEMORY will provide "platform_set_malloc_free()" + * to allow you to set an alternative malloc() and free() function pointer. + * + * Requires: POLARSSL_PLATFORM_C + * + * Enable this layer to allow use of alternative memory allocators. + */ +//#define POLARSSL_PLATFORM_MEMORY + +/** + * \def POLARSSL_PLATFORM_NO_STD_FUNCTIONS + * + * Do not assign standard functions in the platform layer (e.g. malloc() to + * POLARSSL_PLATFORM_STD_MALLOC and printf() to POLARSSL_PLATFORM_STD_PRINTF) + * + * This makes sure there are no linking errors on platforms that do not support + * these functions. You will HAVE to provide alternatives, either at runtime + * via the platform_set_xxx() functions or at compile time by setting + * the POLARSSL_PLATFORM_STD_XXX defines. + * + * Requires: POLARSSL_PLATFORM_C + * + * Uncomment to prevent default assignment of standard functions in the + * platform layer. + */ +//#define POLARSSL_PLATFORM_NO_STD_FUNCTIONS + +/** + * \def POLARSSL_PLATFORM_XXX_ALT + * + * Uncomment a macro to let PolarSSL support the function in the platform + * abstraction layer. + * + * Example: In case you uncomment POLARSSL_PLATFORM_PRINTF_ALT, PolarSSL will + * provide a function "platform_set_printf()" that allows you to set an + * alternative printf function pointer. + * + * All these define require POLARSSL_PLATFORM_C to be defined! + * + * Uncomment a macro to enable alternate implementation of specific base + * platform function + */ +//#define POLARSSL_PLATFORM_PRINTF_ALT +//#define POLARSSL_PLATFORM_FPRINTF_ALT +/* \} name SECTION: System support */ + +/** + * \name SECTION: PolarSSL feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def POLARSSL_TIMING_ALT + * + * Uncomment to provide your own alternate implementation for hardclock(), + * get_timer(), set_alarm() and m_sleep(). + * + * Only works if you have POLARSSL_TIMING_C enabled. + * + * You will need to provide a header "timing_alt.h" and an implementation at + * compile time. + */ +//#define POLARSSL_TIMING_ALT + +/** + * \def POLARSSL_XXX_ALT + * + * Uncomment a macro to let PolarSSL use your alternate core implementation of + * a symmetric or hash algorithm (e.g. platform specific assembly optimized + * implementations). Keep in mind that the function prototypes should remain + * the same. + * + * Example: In case you uncomment POLARSSL_AES_ALT, PolarSSL will no longer + * provide the "struct aes_context" definition and omit the base function + * declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation for core algorithm + * functions + */ +//#define POLARSSL_AES_ALT +//#define POLARSSL_ARC4_ALT +//#define POLARSSL_BLOWFISH_ALT +//#define POLARSSL_CAMELLIA_ALT +//#define POLARSSL_DES_ALT +//#define POLARSSL_XTEA_ALT +//#define POLARSSL_MD2_ALT +//#define POLARSSL_MD4_ALT +//#define POLARSSL_MD5_ALT +//#define POLARSSL_RIPEMD160_ALT +//#define POLARSSL_SHA1_ALT +//#define POLARSSL_SHA256_ALT +//#define POLARSSL_SHA512_ALT + +/** + * \def POLARSSL_AES_ROM_TABLES + * + * Store the AES tables in ROM. + * + * Uncomment this macro to store the AES tables in ROM. + * + */ +//#define POLARSSL_AES_ROM_TABLES + +/** + * \def POLARSSL_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CBC + +/** + * \def POLARSSL_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CFB + +/** + * \def POLARSSL_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CTR + +/** + * \def POLARSSL_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires POLARSSL_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * TLS_ECDH_ECDSA_WITH_NULL_SHA + * TLS_ECDH_RSA_WITH_NULL_SHA + * TLS_ECDHE_ECDSA_WITH_NULL_SHA + * TLS_ECDHE_RSA_WITH_NULL_SHA + * TLS_ECDHE_PSK_WITH_NULL_SHA384 + * TLS_ECDHE_PSK_WITH_NULL_SHA256 + * TLS_ECDHE_PSK_WITH_NULL_SHA + * TLS_DHE_PSK_WITH_NULL_SHA384 + * TLS_DHE_PSK_WITH_NULL_SHA256 + * TLS_DHE_PSK_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_SHA256 + * TLS_RSA_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_MD5 + * TLS_RSA_PSK_WITH_NULL_SHA384 + * TLS_RSA_PSK_WITH_NULL_SHA256 + * TLS_RSA_PSK_WITH_NULL_SHA + * TLS_PSK_WITH_NULL_SHA384 + * TLS_PSK_WITH_NULL_SHA256 + * TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites + */ +//#define POLARSSL_CIPHER_NULL_CIPHER + +/** + * \def POLARSSL_CIPHER_PADDING_XXX + * + * Uncomment or comment macros to add support for specific padding modes + * in the cipher layer with cipher modes that support padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define POLARSSL_CIPHER_PADDING_PKCS7 +#define POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS +#define POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN +#define POLARSSL_CIPHER_PADDING_ZEROS + +/** + * \def POLARSSL_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS. + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * TLS_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites + */ +//#define POLARSSL_ENABLE_WEAK_CIPHERSUITES + +/** + * \def POLARSSL_REMOVE_ARC4_CIPHERSUITES + * + * Remove RC4 ciphersuites by default in SSL / TLS. + * This flag removes the ciphersuites based on RC4 from the default list as + * returned by ssl_list_ciphersuites(). However, it is still possible to + * enable (some of) them with ssl_set_ciphersuites() by including them + * explicitly. + * + * Uncomment this macro to remove RC4 ciphersuites by default. + */ +//#define POLARSSL_REMOVE_ARC4_CIPHERSUITES + +/** + * \def POLARSSL_ECP_XXXX_ENABLED + * + * Enables specific curves within the Elliptic Curve module. + * By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +#define POLARSSL_ECP_DP_SECP192R1_ENABLED +#define POLARSSL_ECP_DP_SECP224R1_ENABLED +#define POLARSSL_ECP_DP_SECP256R1_ENABLED +#define POLARSSL_ECP_DP_SECP384R1_ENABLED +#define POLARSSL_ECP_DP_SECP521R1_ENABLED +#define POLARSSL_ECP_DP_SECP192K1_ENABLED +#define POLARSSL_ECP_DP_SECP224K1_ENABLED +#define POLARSSL_ECP_DP_SECP256K1_ENABLED +#define POLARSSL_ECP_DP_BP256R1_ENABLED +#define POLARSSL_ECP_DP_BP384R1_ENABLED +#define POLARSSL_ECP_DP_BP512R1_ENABLED +//#define POLARSSL_ECP_DP_M221_ENABLED // Not implemented yet! +#define POLARSSL_ECP_DP_M255_ENABLED +//#define POLARSSL_ECP_DP_M383_ENABLED // Not implemented yet! +//#define POLARSSL_ECP_DP_M511_ENABLED // Not implemented yet! + +/** + * \def POLARSSL_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define POLARSSL_ECP_NIST_OPTIM + +/** + * \def POLARSSL_ECDSA_DETERMINISTIC + * + * Enable deterministic ECDSA (RFC 6979). + * Standard ECDSA is "fragile" in the sense that lack of entropy when signing + * may result in a compromise of the long-term signing key. This is avoided by + * the deterministic variant. + * + * Requires: POLARSSL_HMAC_DRBG_C + * + * Comment this macro to disable deterministic ECDSA. + */ +#define POLARSSL_ECDSA_DETERMINISTIC + +/** + * \def POLARSSL_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + */ +#define POLARSSL_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + */ +#define POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_ECDSA_C, POLARSSL_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + */ +#define POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def POLARSSL_PK_PARSE_EC_EXTENDED + * + * Enhance support for reading EC keys using variants of SEC1 not allowed by + * RFC 5915 and RFC 5480. + * + * Currently this means parsing the SpecifiedECDomain choice of EC + * parameters (only known groups are supported, not arbitrary domains, to + * avoid validation issues). + * + * Disable if you only need to support RFC 5915 + 5480 key formats. + */ +#define POLARSSL_PK_PARSE_EC_EXTENDED + +/** + * \def POLARSSL_ERROR_STRERROR_BC + * + * Make available the backward compatible error_strerror() next to the + * current polarssl_strerror(). + * + * For new code, it is recommended to use polarssl_strerror() instead and + * disable this. + * + * Disable if you run into name conflicts and want to really remove the + * error_strerror() + */ +#define POLARSSL_ERROR_STRERROR_BC + +/** + * \def POLARSSL_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of polarssl_strerror() in + * third party libraries easier when POLARSSL_ERROR_C is disabled + * (no effect when POLARSSL_ERROR_C is enabled). + * + * You can safely disable this if POLARSSL_ERROR_C is enabled, or if you're + * not using polarssl_strerror() or error_strerror() in your application. + * + * Disable if you run into name conflicts and want to really remove the + * polarssl_strerror() + */ +#define POLARSSL_ERROR_STRERROR_DUMMY + +/** + * \def POLARSSL_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: POLARSSL_BIGNUM_C + */ +#define POLARSSL_GENPRIME + +/** + * \def POLARSSL_FS_IO + * + * Enable functions that use the filesystem. + */ +#define POLARSSL_FS_IO + +/** + * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def POLARSSL_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +//#define POLARSSL_NO_PLATFORM_ENTROPY + +/** + * \def POLARSSL_ENTROPY_FORCE_SHA256 + * + * Force the entropy accumulator to use a SHA-256 accumulator instead of the + * default SHA-512 based one (if both are available). + * + * Requires: POLARSSL_SHA256_C + * + * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option + * if you have performance concerns. + * + * This option is only useful if both POLARSSL_SHA256_C and + * POLARSSL_SHA512_C are defined. Otherwise the available hash module is used. + */ +//#define POLARSSL_ENTROPY_FORCE_SHA256 + +/** + * \def POLARSSL_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define POLARSSL_MEMORY_DEBUG + +/** + * \def POLARSSL_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() an backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define POLARSSL_MEMORY_BACKTRACE + +/** + * \def POLARSSL_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: POLARSSL_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +#define POLARSSL_PKCS1_V15 + +/** + * \def POLARSSL_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: POLARSSL_MD_C, POLARSSL_RSA_C + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +#define POLARSSL_PKCS1_V21 + +/** + * \def POLARSSL_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define POLARSSL_RSA_NO_CRT + +/** + * \def POLARSSL_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +#define POLARSSL_SELF_TEST + +/** + * \def POLARSSL_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, PolarSSL can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define POLARSSL_SSL_ALERT_MESSAGES + +/** + * \def POLARSSL_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define POLARSSL_SSL_DEBUG_ALL + +/** + * \def POLARSSL_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. + */ +//#define POLARSSL_SSL_HW_RECORD_ACCEL + +/** + * \def POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (POLARSSL_SSL_SRV_C). + * + * Comment this macro to disable support for SSLv2 Client Hello messages. + */ +#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + * + * Pick the ciphersuite according to the client's preferences rather than ours + * in the SSL Server module (POLARSSL_SSL_SRV_C). + * + * Uncomment this macro to respect client's ciphersuite order + */ +//#define POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + +/** + * \def POLARSSL_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define POLARSSL_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def POLARSSL_SSL_PROTO_SSL3 + * + * Enable support for SSL 3.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for SSL 3.0 + */ +#define POLARSSL_SSL_PROTO_SSL3 + +/** + * \def POLARSSL_SSL_PROTO_TLS1 + * + * Enable support for TLS 1.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.0 + */ +#define POLARSSL_SSL_PROTO_TLS1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_1 + * + * Enable support for TLS 1.1. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.1 + */ +#define POLARSSL_SSL_PROTO_TLS1_1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2. + * + * Requires: POLARSSL_SHA1_C or POLARSSL_SHA256_C or POLARSSL_SHA512_C + * (Depends on ciphersuites) + * + * Comment this macro to disable support for TLS 1.2 + */ +#define POLARSSL_SSL_PROTO_TLS1_2 + +/** + * \def POLARSSL_SSL_ALPN + * + * Enable support for Application Layer Protocol Negotiation. + * draft-ietf-tls-applayerprotoneg-05 + * + * Comment this macro to disable support for ALPN. + */ +#define POLARSSL_SSL_ALPN + +/** + * \def POLARSSL_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * + * Requires: POLARSSL_AES_C + * POLARSSL_SHA256_C + * POLARSSL_CIPHER_MODE_CBC + * + * Comment this macro to disable support for SSL session tickets + */ +#define POLARSSL_SSL_SESSION_TICKETS + +/** + * \def POLARSSL_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Comment this macro to disable support for server name indication in SSL + */ +#define POLARSSL_SSL_SERVER_NAME_INDICATION + +/** + * \def POLARSSL_SSL_TRUNCATED_HMAC + * + * Enable support for RFC 6066 truncated HMAC in SSL. + * + * Comment this macro to disable support for truncated HMAC in SSL + */ +#define POLARSSL_SSL_TRUNCATED_HMAC + +/** + * \def POLARSSL_SSL_SET_CURVES + * + * Enable ssl_set_curves(). + * + * This is disabled by default since it breaks binary compatibility with the + * 1.3.x line. If you choose to enable it, you will need to rebuild your + * application against the new header files, relinking will not be enough. + * It will be enabled by default, or no longer an option, in the 1.4 branch. + * + * Uncomment to make ssl_set_curves() available. + */ +//#define POLARSSL_SSL_SET_CURVES + +/** + * \def POLARSSL_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define POLARSSL_THREADING_ALT + +/** + * \def POLARSSL_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define POLARSSL_THREADING_PTHREAD + +/** + * \def POLARSSL_VERSION_FEATURES + * + * Allow run-time checking of compile-time enabled features. Thus allowing users + * to check at run-time if the library is for instance compiled with threading + * support via version_check_feature(). + * + * Requires: POLARSSL_VERSION_C + * + * Comment this to disable run-time checking and save ROM space + */ +#define POLARSSL_VERSION_FEATURES + +/** + * \def POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an extension in a v1 or v2 certificate. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + +/** + * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + +/** + * \def POLARSSL_X509_CHECK_KEY_USAGE + * + * Enable verification of the keyUsage extension (CA and leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused + * (intermediate) CA and leaf certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip keyUsage checking for both CA and leaf certificates. + */ +#define POLARSSL_X509_CHECK_KEY_USAGE + +/** + * \def POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE + * + * Enable verification of the extendedKeyUsage extension (leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip extendedKeyUsage checking for certificates. + */ +#define POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE + +/** + * \def POLARSSL_X509_RSASSA_PSS_SUPPORT + * + * Enable parsing and verification of X.509 certificates, CRLs and CSRS + * signed with RSASSA-PSS (aka PKCS#1 v2.1). + * + * Comment this macro to disallow using RSASSA-PSS in certificates. + */ +#define POLARSSL_X509_RSASSA_PSS_SUPPORT + +/** + * \def POLARSSL_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * \warning TLS-level compression MAY REDUCE SECURITY! See for example the + * CRIME attack. Before enabling this option, you should examine with care if + * CRIME or similar exploits may be a applicable to your use case. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB + */ +//#define POLARSSL_ZLIB_SUPPORT +/* \} name SECTION: PolarSSL feature support */ + +/** + * \name SECTION: PolarSSL modules + * + * This section enables or disables entire modules in PolarSSL + * \{ + */ + +/** + * \def POLARSSL_AESNI_C + * + * Enable AES-NI support on x86-64. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the AES-NI instructions on x86-64 + */ +#define POLARSSL_AESNI_C + +/** + * \def POLARSSL_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/ssl_tls.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define POLARSSL_AES_C + +/** + * \def POLARSSL_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + * TLS_RSA_PSK_WITH_RC4_128_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +#define POLARSSL_ARC4_C + +/** + * \def POLARSSL_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +#define POLARSSL_ASN1_PARSE_C + +/** + * \def POLARSSL_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +#define POLARSSL_ASN1_WRITE_C + +/** + * \def POLARSSL_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define POLARSSL_BASE64_C + +/** + * \def POLARSSL_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/ecp.c + * library/ecdsa.c + * library/rsa.c + * library/ssl_tls.c + * + * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support. + */ +#define POLARSSL_BIGNUM_C + +/** + * \def POLARSSL_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +#define POLARSSL_BLOWFISH_C + +/** + * \def POLARSSL_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define POLARSSL_CAMELLIA_C + +/** + * \def POLARSSL_CCM_C + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Module: library/ccm.c + * + * Requires: POLARSSL_AES_C or POLARSSL_CAMELLIA_C + * + * This module enables the AES-CCM ciphersuites, if other requisites are + * enabled as well. + */ +#define POLARSSL_CCM_C + +/** + * \def POLARSSL_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * Requires: POLARSSL_PEM_PARSE_C + * + * This module is used for testing (ssl_client/server). + */ +#define POLARSSL_CERTS_C + +/** + * \def POLARSSL_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ssl_tls.c + * + * Uncomment to enable generic cipher wrappers. + */ +#define POLARSSL_CIPHER_C + +/** + * \def POLARSSL_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-256-based random generator. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: POLARSSL_AES_C + * + * This module provides the CTR_DRBG AES-256 random number generator. + */ +#define POLARSSL_CTR_DRBG_C + +/** + * \def POLARSSL_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define POLARSSL_DEBUG_C + +/** + * \def POLARSSL_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + */ +#define POLARSSL_DES_C + +/** + * \def POLARSSL_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + */ +#define POLARSSL_DHM_C + +/** + * \def POLARSSL_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: POLARSSL_ECP_C + */ +#define POLARSSL_ECDH_C + +/** + * \def POLARSSL_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: POLARSSL_ECP_C, POLARSSL_ASN1_WRITE_C, POLARSSL_ASN1_PARSE_C + */ +#define POLARSSL_ECDSA_C + +/** + * \def POLARSSL_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * + * Requires: POLARSSL_BIGNUM_C and at least one POLARSSL_ECP_DP_XXX_ENABLED + */ +#define POLARSSL_ECP_C + +/** + * \def POLARSSL_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: POLARSSL_SHA512_C or POLARSSL_SHA256_C + * + * This module provides a generic entropy pool + */ +#define POLARSSL_ENTROPY_C + +/** + * \def POLARSSL_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables polarssl_strerror(). + */ +#define POLARSSL_ERROR_C + +/** + * \def POLARSSL_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES. + * + * Module: library/gcm.c + * + * Requires: POLARSSL_AES_C or POLARSSL_CAMELLIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +#define POLARSSL_GCM_C + +/** + * \def POLARSSL_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: POLARSSL_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. + */ +//#define POLARSSL_HAVEGE_C + +/** + * \def POLARSSL_HMAC_DRBG_C + * + * Enable the HMAC_DRBG random generator. + * + * Module: library/hmac_drbg.c + * Caller: + * + * Requires: POLARSSL_MD_C + * + * Uncomment to enable the HMAC_DRBG random number geerator. + */ +#define POLARSSL_HMAC_DRBG_C + +/** + * \def POLARSSL_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define POLARSSL_MD_C + +/** + * \def POLARSSL_MD2_C + * + * Enable the MD2 hash algorithm. + * + * Module: library/md2.c + * Caller: + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + */ +//#define POLARSSL_MD2_C + +/** + * \def POLARSSL_MD4_C + * + * Enable the MD4 hash algorithm. + * + * Module: library/md4.c + * Caller: + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + */ +//#define POLARSSL_MD4_C + +/** + * \def POLARSSL_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for SSL/TLS and X.509. + * PEM_PARSE uses MD5 for decrypting encrypted keys. + */ +#define POLARSSL_MD5_C + +/** + * \def POLARSSL_MEMORY_C + * Deprecated since 1.3.5. Please use POLARSSL_PLATFORM_MEMORY instead. + */ +//#define POLARSSL_MEMORY_C + +/** + * \def POLARSSL_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces malloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: POLARSSL_PLATFORM_C + * POLARSSL_PLATFORM_MEMORY (to use it within PolarSSL) + * + * Enable this module to enable the buffer memory allocator. + */ +//#define POLARSSL_MEMORY_BUFFER_ALLOC_C + +/** + * \def POLARSSL_NET_C + * + * Enable the TCP/IP networking routines. + * + * Module: library/net.c + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/** + * \def POLARSSL_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define POLARSSL_OID_C + +/** + * \def POLARSSL_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +#define POLARSSL_PADLOCK_C + +/** + * \def POLARSSL_PBKDF2_C + * + * Enable PKCS#5 PBKDF2 key derivation function. + * DEPRECATED: Use POLARSSL_PKCS5_C instead + * + * Module: library/pbkdf2.c + * + * Requires: POLARSSL_PKCS5_C + * + * This module adds support for the PKCS#5 PBKDF2 key derivation function. + */ +#define POLARSSL_PBKDF2_C + +/** + * \def POLARSSL_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +#define POLARSSL_PEM_PARSE_C + +/** + * \def POLARSSL_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +#define POLARSSL_PEM_WRITE_C + +/** + * \def POLARSSL_PK_C + * + * Enable the generic public (asymetric) key layer. + * + * Module: library/pk.c + * Caller: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_RSA_C or POLARSSL_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +#define POLARSSL_PK_C + +/** + * \def POLARSSL_PK_PARSE_C + * + * Enable the generic public (asymetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +#define POLARSSL_PK_PARSE_C + +/** + * \def POLARSSL_PK_WRITE_C + * + * Enable the generic public (asymetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key write functions. + */ +#define POLARSSL_PK_WRITE_C + +/** + * \def POLARSSL_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: POLARSSL_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +#define POLARSSL_PKCS5_C + +/** + * \def POLARSSL_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/pkcs11.c + * Caller: library/pk.c + * + * Requires: POLARSSL_PK_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) + */ +//#define POLARSSL_PKCS11_C + +/** + * \def POLARSSL_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_CIPHER_C, POLARSSL_MD_C + * Can use: POLARSSL_ARC4_C + * + * This module enables PKCS#12 functions. + */ +#define POLARSSL_PKCS12_C + +/** + * \def POLARSSL_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like malloc(), free(), printf(), fprintf() + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define POLARSSL_PLATFORM_C + +/** + * \def POLARSSL_RIPEMD160_C + * + * Enable the RIPEMD-160 hash algorithm. + * + * Module: library/ripemd160.c + * Caller: library/md.c + * + */ +#define POLARSSL_RIPEMD160_C + +/** + * \def POLARSSL_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C + */ +#define POLARSSL_RSA_C + +/** + * \def POLARSSL_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509write_crt.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/** + * \def POLARSSL_SHA256_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA2_C) + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define POLARSSL_SHA256_C + +/** + * \def POLARSSL_SHA512_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA4_C) + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA512_C + +/** + * \def POLARSSL_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: POLARSSL_SSL_CACHE_C + */ +#define POLARSSL_SSL_CACHE_C + +/** + * \def POLARSSL_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/** + * \def POLARSSL_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define POLARSSL_SSL_SRV_C + +/** + * \def POLARSSL_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_CIPHER_C, POLARSSL_MD_C + * and at least one of the POLARSSL_SSL_PROTO_* defines + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/** + * \def POLARSSL_THREADING_C + * + * Enable the threading abstraction layer. + * By default PolarSSL assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either POLARSSL_THREADING_ALT or + * POLARSSL_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within PolarSSL + */ +//#define POLARSSL_THREADING_C + +/** + * \def POLARSSL_TIMING_C + * + * Enable the portable timing interface. + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +#define POLARSSL_TIMING_C + +/** + * \def POLARSSL_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define POLARSSL_VERSION_C + +/** + * \def POLARSSL_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_BIGNUM_C, POLARSSL_OID_C, + * POLARSSL_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +#define POLARSSL_X509_USE_C + +/** + * \def POLARSSL_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +#define POLARSSL_X509_CRT_PARSE_C + +/** + * \def POLARSSL_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +#define POLARSSL_X509_CRL_PARSE_C + +/** + * \def POLARSSL_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +#define POLARSSL_X509_CSR_PARSE_C + +/** + * \def POLARSSL_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C, POLARSSL_PK_WRITE_C + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +#define POLARSSL_X509_CREATE_C + +/** + * \def POLARSSL_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +#define POLARSSL_X509_CRT_WRITE_C + +/** + * \def POLARSSL_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +#define POLARSSL_X509_CSR_WRITE_C + +/** + * \def POLARSSL_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +#define POLARSSL_XTEA_C + +/* \} name SECTION: PolarSSL modules */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * + * Our advice is to enable options and change their values here + * only if you have a good reason and know the consequences. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * \{ + */ + +/* MPI / BIGNUM options */ +//#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +//#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +/* CTR_DRBG options */ +//#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +//#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* HMAC_DRBG options */ +//#define POLARSSL_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define POLARSSL_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define POLARSSL_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define POLARSSL_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* ECP options */ +//#define POLARSSL_ECP_MAX_BITS 521 /**< Maximum bit size of groups */ +//#define POLARSSL_ECP_WINDOW_SIZE 6 /**< Maximum window size used */ +//#define POLARSSL_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +/* Entropy options */ +//#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +//#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ + +/* Memory buffer allocator options */ +//#define POLARSSL_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +/* Platform options */ +//#define POLARSSL_PLATFORM_STD_MEM_HDR /**< Header to include if POLARSSL_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */ +//#define POLARSSL_PLATFORM_STD_MALLOC malloc /**< Default allocator to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_FREE free /**< Default free to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_PRINTF printf /**< Default printf to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use, can be undefined */ + +/* SSL Cache options */ +//#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +//#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +/* SSL options */ +//#define SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ +//#define SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ +//#define POLARSSL_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */ + +/** + * Complete list of ciphersuites to use, in order of preference. + * + * \warning No dependency checking is done on that field! This option can only + * be used to restrict the set of available ciphersuites. It is your + * responsibility to make sure the needed modules are active. + * + * Use this to save a few hundred bytes of ROM (default ordering of all + * available ciphersuites) and a few to a few hundred bytes of RAM. + * + * The value below is only an example, not the default. + */ +//#define SSL_CIPHERSUITES TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/* Debug options */ +//#define POLARSSL_DEBUG_DFL_MODE POLARSSL_DEBUG_LOG_FULL /**< Default log: Full or Raw */ + +/* \} name SECTION: Module configuration options */ + +#include "check_config.h" + +#endif /* POLARSSL_CONFIG_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_rsa.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_rsa.h new file mode 100644 index 0000000..8c340c2 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_rsa.h @@ -0,0 +1,2183 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def POLARSSL_HAVE_INT8 + * + * The system uses 8-bit wide native integers. + * + * Uncomment if native integers are 8-bit wide. + */ +//#define POLARSSL_HAVE_INT8 + +/** + * \def POLARSSL_HAVE_INT16 + * + * The system uses 16-bit wide native integers. + * + * Uncomment if native integers are 16-bit wide. + */ +//#define POLARSSL_HAVE_INT16 + +/** + * \def POLARSSL_HAVE_LONGLONG + * + * The compiler supports the 'long long' type. + * (Only used on 32-bit platforms) + */ +#define POLARSSL_HAVE_LONGLONG + +/** + * \def POLARSSL_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + * Comment to disable the use of assembly code. + */ +//#define POLARSSL_HAVE_ASM + +/** + * \def POLARSSL_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define POLARSSL_HAVE_SSE2 + +/** + * \def POLARSSL_HAVE_TIME + * + * System has time.h and time() / localtime() / gettimeofday(). + * + * Comment if your system does not support time functions + */ +//#define POLARSSL_HAVE_TIME + +/** + * \def POLARSSL_HAVE_IPV6 + * + * System supports the basic socket interface for IPv6 (RFC 3493), + * specifically getaddrinfo(), freeaddrinfo() and struct sockaddr_storage. + * + * Note: on Windows/MingW, XP or higher is required. + * + * Comment if your system does not support the IPv6 socket interface + */ +//#define POLARSSL_HAVE_IPV6 + +/** + * \def POLARSSL_PLATFORM_MEMORY + * + * Enable the memory allocation layer. + * + * By default PolarSSL uses the system-provided malloc() and free(). + * This allows different allocators (self-implemented or provided) to be + * provided to the platform abstraction layer. + * + * Enabling POLARSSL_PLATFORM_MEMORY will provide "platform_set_malloc_free()" + * to allow you to set an alternative malloc() and free() function pointer. + * + * Requires: POLARSSL_PLATFORM_C + * + * Enable this layer to allow use of alternative memory allocators. + */ +#define POLARSSL_PLATFORM_MEMORY + +/** + * \def POLARSSL_PLATFORM_NO_STD_FUNCTIONS + * + * Do not assign standard functions in the platform layer (e.g. malloc() to + * POLARSSL_PLATFORM_STD_MALLOC and printf() to POLARSSL_PLATFORM_STD_PRINTF) + * + * This makes sure there are no linking errors on platforms that do not support + * these functions. You will HAVE to provide alternatives, either at runtime + * via the platform_set_xxx() functions or at compile time by setting + * the POLARSSL_PLATFORM_STD_XXX defines. + * + * Requires: POLARSSL_PLATFORM_C + * + * Uncomment to prevent default assignment of standard functions in the + * platform layer. + */ +//#define POLARSSL_PLATFORM_NO_STD_FUNCTIONS + +/** + * \def POLARSSL_PLATFORM_XXX_ALT + * + * Uncomment a macro to let PolarSSL support the function in the platform + * abstraction layer. + * + * Example: In case you uncomment POLARSSL_PLATFORM_PRINTF_ALT, PolarSSL will + * provide a function "platform_set_printf()" that allows you to set an + * alternative printf function pointer. + * + * All these define require POLARSSL_PLATFORM_C to be defined! + * + * Uncomment a macro to enable alternate implementation of specific base + * platform function + */ +//#define POLARSSL_PLATFORM_PRINTF_ALT +//#define POLARSSL_PLATFORM_FPRINTF_ALT +/* \} name SECTION: System support */ + +/** + * \name SECTION: PolarSSL feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def POLARSSL_TIMING_ALT + * + * Uncomment to provide your own alternate implementation for hardclock(), + * get_timer(), set_alarm() and m_sleep(). + * + * Only works if you have POLARSSL_TIMING_C enabled. + * + * You will need to provide a header "timing_alt.h" and an implementation at + * compile time. + */ +//#define POLARSSL_TIMING_ALT + +/** + * \def POLARSSL_XXX_ALT + * + * Uncomment a macro to let PolarSSL use your alternate core implementation of + * a symmetric or hash algorithm (e.g. platform specific assembly optimized + * implementations). Keep in mind that the function prototypes should remain + * the same. + * + * Example: In case you uncomment POLARSSL_AES_ALT, PolarSSL will no longer + * provide the "struct aes_context" definition and omit the base function + * declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation for core algorithm + * functions + */ +//#define POLARSSL_AES_ALT +//#define POLARSSL_ARC4_ALT +//#define POLARSSL_BLOWFISH_ALT +//#define POLARSSL_CAMELLIA_ALT +//#define POLARSSL_DES_ALT +//#define POLARSSL_XTEA_ALT +//#define POLARSSL_MD2_ALT +//#define POLARSSL_MD4_ALT +//#define POLARSSL_MD5_ALT +//#define POLARSSL_RIPEMD160_ALT +//#define POLARSSL_SHA1_ALT +//#define POLARSSL_SHA256_ALT +//#define POLARSSL_SHA512_ALT + +/** + * \def POLARSSL_AES_ROM_TABLES + * + * Store the AES tables in ROM. + * + * Uncomment this macro to store the AES tables in ROM. + * + */ +#define POLARSSL_AES_ROM_TABLES + +/** + * \def POLARSSL_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CBC + +/** + * \def POLARSSL_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CFB + +/** + * \def POLARSSL_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CTR + +/** + * \def POLARSSL_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires POLARSSL_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * TLS_ECDH_ECDSA_WITH_NULL_SHA + * TLS_ECDH_RSA_WITH_NULL_SHA + * TLS_ECDHE_ECDSA_WITH_NULL_SHA + * TLS_ECDHE_RSA_WITH_NULL_SHA + * TLS_ECDHE_PSK_WITH_NULL_SHA384 + * TLS_ECDHE_PSK_WITH_NULL_SHA256 + * TLS_ECDHE_PSK_WITH_NULL_SHA + * TLS_DHE_PSK_WITH_NULL_SHA384 + * TLS_DHE_PSK_WITH_NULL_SHA256 + * TLS_DHE_PSK_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_SHA256 + * TLS_RSA_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_MD5 + * TLS_RSA_PSK_WITH_NULL_SHA384 + * TLS_RSA_PSK_WITH_NULL_SHA256 + * TLS_RSA_PSK_WITH_NULL_SHA + * TLS_PSK_WITH_NULL_SHA384 + * TLS_PSK_WITH_NULL_SHA256 + * TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites + */ +//#define POLARSSL_CIPHER_NULL_CIPHER + +/** + * \def POLARSSL_CIPHER_PADDING_XXX + * + * Uncomment or comment macros to add support for specific padding modes + * in the cipher layer with cipher modes that support padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define POLARSSL_CIPHER_PADDING_PKCS7 +#define POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS +#define POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN +#define POLARSSL_CIPHER_PADDING_ZEROS + +/** + * \def POLARSSL_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS. + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * TLS_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites + */ +//#define POLARSSL_ENABLE_WEAK_CIPHERSUITES + +/** + * \def POLARSSL_REMOVE_ARC4_CIPHERSUITES + * + * Remove RC4 ciphersuites by default in SSL / TLS. + * This flag removes the ciphersuites based on RC4 from the default list as + * returned by ssl_list_ciphersuites(). However, it is still possible to + * enable (some of) them with ssl_set_ciphersuites() by including them + * explicitly. + * + * Uncomment this macro to remove RC4 ciphersuites by default. + */ +//#define POLARSSL_REMOVE_ARC4_CIPHERSUITES + +/** + * \def POLARSSL_ECP_XXXX_ENABLED + * + * Enables specific curves within the Elliptic Curve module. + * By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +#define POLARSSL_ECP_DP_SECP192R1_ENABLED +#define POLARSSL_ECP_DP_SECP224R1_ENABLED +#define POLARSSL_ECP_DP_SECP256R1_ENABLED +#define POLARSSL_ECP_DP_SECP384R1_ENABLED +#define POLARSSL_ECP_DP_SECP521R1_ENABLED +#define POLARSSL_ECP_DP_SECP192K1_ENABLED +#define POLARSSL_ECP_DP_SECP224K1_ENABLED +#define POLARSSL_ECP_DP_SECP256K1_ENABLED +#define POLARSSL_ECP_DP_BP256R1_ENABLED +#define POLARSSL_ECP_DP_BP384R1_ENABLED +#define POLARSSL_ECP_DP_BP512R1_ENABLED +//#define POLARSSL_ECP_DP_M221_ENABLED // Not implemented yet! +#define POLARSSL_ECP_DP_M255_ENABLED +//#define POLARSSL_ECP_DP_M383_ENABLED // Not implemented yet! +//#define POLARSSL_ECP_DP_M511_ENABLED // Not implemented yet! + +/** + * \def POLARSSL_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define POLARSSL_ECP_NIST_OPTIM + +/** + * \def POLARSSL_ECDSA_DETERMINISTIC + * + * Enable deterministic ECDSA (RFC 6979). + * Standard ECDSA is "fragile" in the sense that lack of entropy when signing + * may result in a compromise of the long-term signing key. This is avoided by + * the deterministic variant. + * + * Requires: POLARSSL_HMAC_DRBG_C + * + * Comment this macro to disable deterministic ECDSA. + */ +//#define POLARSSL_ECDSA_DETERMINISTIC + +/** + * \def POLARSSL_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + */ +#define POLARSSL_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_ECDSA_C, POLARSSL_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +//#define POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +//#define POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def POLARSSL_PK_PARSE_EC_EXTENDED + * + * Enhance support for reading EC keys using variants of SEC1 not allowed by + * RFC 5915 and RFC 5480. + * + * Currently this means parsing the SpecifiedECDomain choice of EC + * parameters (only known groups are supported, not arbitrary domains, to + * avoid validation issues). + * + * Disable if you only need to support RFC 5915 + 5480 key formats. + */ +//#define POLARSSL_PK_PARSE_EC_EXTENDED + +/** + * \def POLARSSL_ERROR_STRERROR_BC + * + * Make available the backward compatible error_strerror() next to the + * current polarssl_strerror(). + * + * For new code, it is recommended to use polarssl_strerror() instead and + * disable this. + * + * Disable if you run into name conflicts and want to really remove the + * error_strerror() + */ +#define POLARSSL_ERROR_STRERROR_BC + +/** + * \def POLARSSL_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of polarssl_strerror() in + * third party libraries easier when POLARSSL_ERROR_C is disabled + * (no effect when POLARSSL_ERROR_C is enabled). + * + * You can safely disable this if POLARSSL_ERROR_C is enabled, or if you're + * not using polarssl_strerror() or error_strerror() in your application. + * + * Disable if you run into name conflicts and want to really remove the + * polarssl_strerror() + */ +#define POLARSSL_ERROR_STRERROR_DUMMY + +/** + * \def POLARSSL_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: POLARSSL_BIGNUM_C + */ +#define POLARSSL_GENPRIME + +/** + * \def POLARSSL_FS_IO + * + * Enable functions that use the filesystem. + */ +//#define POLARSSL_FS_IO + +/** + * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def POLARSSL_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +//#define POLARSSL_NO_PLATFORM_ENTROPY + +/** + * \def POLARSSL_ENTROPY_FORCE_SHA256 + * + * Force the entropy accumulator to use a SHA-256 accumulator instead of the + * default SHA-512 based one (if both are available). + * + * Requires: POLARSSL_SHA256_C + * + * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option + * if you have performance concerns. + * + * This option is only useful if both POLARSSL_SHA256_C and + * POLARSSL_SHA512_C are defined. Otherwise the available hash module is used. + */ +//#define POLARSSL_ENTROPY_FORCE_SHA256 + +/** + * \def POLARSSL_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define POLARSSL_MEMORY_DEBUG + +/** + * \def POLARSSL_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() an backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define POLARSSL_MEMORY_BACKTRACE + +/** + * \def POLARSSL_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: POLARSSL_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +#define POLARSSL_PKCS1_V15 + +/** + * \def POLARSSL_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: POLARSSL_MD_C, POLARSSL_RSA_C + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +//#define POLARSSL_PKCS1_V21 + +/** + * \def POLARSSL_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define POLARSSL_RSA_NO_CRT + +/** + * \def POLARSSL_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +//#define POLARSSL_SELF_TEST + +/** + * \def POLARSSL_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, PolarSSL can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define POLARSSL_SSL_ALERT_MESSAGES + +/** + * \def POLARSSL_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define POLARSSL_SSL_DEBUG_ALL + +/** + * \def POLARSSL_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. + */ +//#define POLARSSL_SSL_HW_RECORD_ACCEL + +/** + * \def POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (POLARSSL_SSL_SRV_C). + * + * Comment this macro to disable support for SSLv2 Client Hello messages. + */ +//#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + * + * Pick the ciphersuite according to the client's preferences rather than ours + * in the SSL Server module (POLARSSL_SSL_SRV_C). + * + * Uncomment this macro to respect client's ciphersuite order + */ +//#define POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + +/** + * \def POLARSSL_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define POLARSSL_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def POLARSSL_SSL_PROTO_SSL3 + * + * Enable support for SSL 3.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for SSL 3.0 + */ +#define POLARSSL_SSL_PROTO_SSL3 + +/** + * \def POLARSSL_SSL_PROTO_TLS1 + * + * Enable support for TLS 1.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.0 + */ +#define POLARSSL_SSL_PROTO_TLS1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_1 + * + * Enable support for TLS 1.1. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.1 + */ +#define POLARSSL_SSL_PROTO_TLS1_1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2. + * + * Requires: POLARSSL_SHA1_C or POLARSSL_SHA256_C or POLARSSL_SHA512_C + * (Depends on ciphersuites) + * + * Comment this macro to disable support for TLS 1.2 + */ +#define POLARSSL_SSL_PROTO_TLS1_2 + +/** + * \def POLARSSL_SSL_ALPN + * + * Enable support for Application Layer Protocol Negotiation. + * draft-ietf-tls-applayerprotoneg-05 + * + * Comment this macro to disable support for ALPN. + */ +//#define POLARSSL_SSL_ALPN + +/** + * \def POLARSSL_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * + * Requires: POLARSSL_AES_C + * POLARSSL_SHA256_C + * POLARSSL_CIPHER_MODE_CBC + * + * Comment this macro to disable support for SSL session tickets + */ +#define POLARSSL_SSL_SESSION_TICKETS + +/** + * \def POLARSSL_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Comment this macro to disable support for server name indication in SSL + */ +#define POLARSSL_SSL_SERVER_NAME_INDICATION + +/** + * \def POLARSSL_SSL_TRUNCATED_HMAC + * + * Enable support for RFC 6066 truncated HMAC in SSL. + * + * Comment this macro to disable support for truncated HMAC in SSL + */ +#define POLARSSL_SSL_TRUNCATED_HMAC + +/** + * \def POLARSSL_SSL_SET_CURVES + * + * Enable ssl_set_curves(). + * + * This is disabled by default since it breaks binary compatibility with the + * 1.3.x line. If you choose to enable it, you will need to rebuild your + * application against the new header files, relinking will not be enough. + * It will be enabled by default, or no longer an option, in the 1.4 branch. + * + * Uncomment to make ssl_set_curves() available. + */ +//#define POLARSSL_SSL_SET_CURVES + +/** + * \def POLARSSL_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define POLARSSL_THREADING_ALT + +/** + * \def POLARSSL_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define POLARSSL_THREADING_PTHREAD + +/** + * \def POLARSSL_VERSION_FEATURES + * + * Allow run-time checking of compile-time enabled features. Thus allowing users + * to check at run-time if the library is for instance compiled with threading + * support via version_check_feature(). + * + * Requires: POLARSSL_VERSION_C + * + * Comment this to disable run-time checking and save ROM space + */ +//#define POLARSSL_VERSION_FEATURES + +/** + * \def POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an extension in a v1 or v2 certificate. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + +/** + * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + +/** + * \def POLARSSL_X509_CHECK_KEY_USAGE + * + * Enable verification of the keyUsage extension (CA and leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused + * (intermediate) CA and leaf certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip keyUsage checking for both CA and leaf certificates. + */ +//#define POLARSSL_X509_CHECK_KEY_USAGE + +/** + * \def POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE + * + * Enable verification of the extendedKeyUsage extension (leaf certificates). + * + * Disabling this avoids problems with mis-issued and/or misused certificates. + * + * \warning Depending on your PKI use, disabling this can be a security risk! + * + * Comment to skip extendedKeyUsage checking for certificates. + */ +//#define POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE + +/** + * \def POLARSSL_X509_RSASSA_PSS_SUPPORT + * + * Enable parsing and verification of X.509 certificates, CRLs and CSRS + * signed with RSASSA-PSS (aka PKCS#1 v2.1). + * + * Comment this macro to disallow using RSASSA-PSS in certificates. + */ +//#define POLARSSL_X509_RSASSA_PSS_SUPPORT + +/** + * \def POLARSSL_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * \warning TLS-level compression MAY REDUCE SECURITY! See for example the + * CRIME attack. Before enabling this option, you should examine with care if + * CRIME or similar exploits may be a applicable to your use case. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB + */ +//#define POLARSSL_ZLIB_SUPPORT +/* \} name SECTION: PolarSSL feature support */ + +/** + * \name SECTION: PolarSSL modules + * + * This section enables or disables entire modules in PolarSSL + * \{ + */ + +/** + * \def POLARSSL_AESNI_C + * + * Enable AES-NI support on x86-64. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the AES-NI instructions on x86-64 + */ +//#define POLARSSL_AESNI_C + +/** + * \def POLARSSL_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/ssl_tls.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define POLARSSL_AES_C + +/** + * \def POLARSSL_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + * TLS_RSA_PSK_WITH_RC4_128_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_ARC4_C + +/** + * \def POLARSSL_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +#define POLARSSL_ASN1_PARSE_C + +/** + * \def POLARSSL_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +//#define POLARSSL_ASN1_WRITE_C + +/** + * \def POLARSSL_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +//#define POLARSSL_BASE64_C + +/** + * \def POLARSSL_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/ecp.c + * library/ecdsa.c + * library/rsa.c + * library/ssl_tls.c + * + * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support. + */ +#define POLARSSL_BIGNUM_C + +/** + * \def POLARSSL_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +//#define POLARSSL_BLOWFISH_C + +/** + * \def POLARSSL_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +//#define POLARSSL_CAMELLIA_C + +/** + * \def POLARSSL_CCM_C + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Module: library/ccm.c + * + * Requires: POLARSSL_AES_C or POLARSSL_CAMELLIA_C + * + * This module enables the AES-CCM ciphersuites, if other requisites are + * enabled as well. + */ +//#define POLARSSL_CCM_C + +/** + * \def POLARSSL_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * Requires: POLARSSL_PEM_PARSE_C + * + * This module is used for testing (ssl_client/server). + */ +//#define POLARSSL_CERTS_C + +/** + * \def POLARSSL_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ssl_tls.c + * + * Uncomment to enable generic cipher wrappers. + */ +#define POLARSSL_CIPHER_C + +/** + * \def POLARSSL_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-256-based random generator. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: POLARSSL_AES_C + * + * This module provides the CTR_DRBG AES-256 random number generator. + */ +//#define POLARSSL_CTR_DRBG_C + +/** + * \def POLARSSL_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +//#define POLARSSL_DEBUG_C + +/** + * \def POLARSSL_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + */ +//#define POLARSSL_DES_C + +/** + * \def POLARSSL_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + */ +//#define POLARSSL_DHM_C + +/** + * \def POLARSSL_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: POLARSSL_ECP_C + */ +//#define POLARSSL_ECDH_C + +/** + * \def POLARSSL_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: POLARSSL_ECP_C, POLARSSL_ASN1_WRITE_C, POLARSSL_ASN1_PARSE_C + */ +//#define POLARSSL_ECDSA_C + +/** + * \def POLARSSL_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * + * Requires: POLARSSL_BIGNUM_C and at least one POLARSSL_ECP_DP_XXX_ENABLED + */ +//#define POLARSSL_ECP_C + +/** + * \def POLARSSL_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: POLARSSL_SHA512_C or POLARSSL_SHA256_C + * + * This module provides a generic entropy pool + */ +//#define POLARSSL_ENTROPY_C + +/** + * \def POLARSSL_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables polarssl_strerror(). + */ +//#define POLARSSL_ERROR_C + +/** + * \def POLARSSL_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES. + * + * Module: library/gcm.c + * + * Requires: POLARSSL_AES_C or POLARSSL_CAMELLIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +//#define POLARSSL_GCM_C + +/** + * \def POLARSSL_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: POLARSSL_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. + */ +//#define POLARSSL_HAVEGE_C + +/** + * \def POLARSSL_HMAC_DRBG_C + * + * Enable the HMAC_DRBG random generator. + * + * Module: library/hmac_drbg.c + * Caller: + * + * Requires: POLARSSL_MD_C + * + * Uncomment to enable the HMAC_DRBG random number geerator. + */ +//#define POLARSSL_HMAC_DRBG_C + +/** + * \def POLARSSL_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define POLARSSL_MD_C + +/** + * \def POLARSSL_MD2_C + * + * Enable the MD2 hash algorithm. + * + * Module: library/md2.c + * Caller: + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + */ +//#define POLARSSL_MD2_C + +/** + * \def POLARSSL_MD4_C + * + * Enable the MD4 hash algorithm. + * + * Module: library/md4.c + * Caller: + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + */ +//#define POLARSSL_MD4_C + +/** + * \def POLARSSL_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for SSL/TLS and X.509. + * PEM_PARSE uses MD5 for decrypting encrypted keys. + */ +#define POLARSSL_MD5_C + +/** + * \def POLARSSL_MEMORY_C + * Deprecated since 1.3.5. Please use POLARSSL_PLATFORM_MEMORY instead. + */ +#define POLARSSL_MEMORY_C + +/** + * \def POLARSSL_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces malloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: POLARSSL_PLATFORM_C + * POLARSSL_PLATFORM_MEMORY (to use it within PolarSSL) + * + * Enable this module to enable the buffer memory allocator. + */ +//#define POLARSSL_MEMORY_BUFFER_ALLOC_C + +/** + * \def POLARSSL_NET_C + * + * Enable the TCP/IP networking routines. + * + * Module: library/net.c + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/** + * \def POLARSSL_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define POLARSSL_OID_C + +/** + * \def POLARSSL_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +//#define POLARSSL_PADLOCK_C + +/** + * \def POLARSSL_PBKDF2_C + * + * Enable PKCS#5 PBKDF2 key derivation function. + * DEPRECATED: Use POLARSSL_PKCS5_C instead + * + * Module: library/pbkdf2.c + * + * Requires: POLARSSL_PKCS5_C + * + * This module adds support for the PKCS#5 PBKDF2 key derivation function. + */ +//#define POLARSSL_PBKDF2_C + +/** + * \def POLARSSL_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +//#define POLARSSL_PEM_PARSE_C + +/** + * \def POLARSSL_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +//#define POLARSSL_PEM_WRITE_C + +/** + * \def POLARSSL_PK_C + * + * Enable the generic public (asymetric) key layer. + * + * Module: library/pk.c + * Caller: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_RSA_C or POLARSSL_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +#define POLARSSL_PK_C + +/** + * \def POLARSSL_PK_PARSE_C + * + * Enable the generic public (asymetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +#define POLARSSL_PK_PARSE_C + +/** + * \def POLARSSL_PK_WRITE_C + * + * Enable the generic public (asymetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key write functions. + */ +//#define POLARSSL_PK_WRITE_C + +/** + * \def POLARSSL_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: POLARSSL_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +//#define POLARSSL_PKCS5_C + +/** + * \def POLARSSL_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/pkcs11.c + * Caller: library/pk.c + * + * Requires: POLARSSL_PK_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) + */ +//#define POLARSSL_PKCS11_C + +/** + * \def POLARSSL_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_CIPHER_C, POLARSSL_MD_C + * Can use: POLARSSL_ARC4_C + * + * This module enables PKCS#12 functions. + */ +//#define POLARSSL_PKCS12_C + +/** + * \def POLARSSL_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like malloc(), free(), printf(), fprintf() + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define POLARSSL_PLATFORM_C + +/** + * \def POLARSSL_RIPEMD160_C + * + * Enable the RIPEMD-160 hash algorithm. + * + * Module: library/ripemd160.c + * Caller: library/md.c + * + */ +//#define POLARSSL_RIPEMD160_C + +/** + * \def POLARSSL_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C + */ +#define POLARSSL_RSA_C + +/** + * \def POLARSSL_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509write_crt.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/** + * \def POLARSSL_SHA256_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA2_C) + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define POLARSSL_SHA256_C + +/** + * \def POLARSSL_SHA512_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA4_C) + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA512_C + +/** + * \def POLARSSL_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: POLARSSL_SSL_CACHE_C + */ +#define POLARSSL_SSL_CACHE_C + +/** + * \def POLARSSL_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/** + * \def POLARSSL_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +//#define POLARSSL_SSL_SRV_C + +/** + * \def POLARSSL_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_CIPHER_C, POLARSSL_MD_C + * and at least one of the POLARSSL_SSL_PROTO_* defines + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/** + * \def POLARSSL_THREADING_C + * + * Enable the threading abstraction layer. + * By default PolarSSL assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either POLARSSL_THREADING_ALT or + * POLARSSL_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within PolarSSL + */ +//#define POLARSSL_THREADING_C + +/** + * \def POLARSSL_TIMING_C + * + * Enable the portable timing interface. + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +//#define POLARSSL_TIMING_C + +/** + * \def POLARSSL_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define POLARSSL_VERSION_C + +/** + * \def POLARSSL_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_BIGNUM_C, POLARSSL_OID_C, + * POLARSSL_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +#define POLARSSL_X509_USE_C + +/** + * \def POLARSSL_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +#define POLARSSL_X509_CRT_PARSE_C + +/** + * \def POLARSSL_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +//#define POLARSSL_X509_CRL_PARSE_C + +/** + * \def POLARSSL_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +//#define POLARSSL_X509_CSR_PARSE_C + +/** + * \def POLARSSL_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C, POLARSSL_PK_WRITE_C + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +//#define POLARSSL_X509_CREATE_C + +/** + * \def POLARSSL_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +//#define POLARSSL_X509_CRT_WRITE_C + +/** + * \def POLARSSL_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +//#define POLARSSL_X509_CSR_WRITE_C + +/** + * \def POLARSSL_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +//#define POLARSSL_XTEA_C + +/* \} name SECTION: PolarSSL modules */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * + * Our advice is to enable options and change their values here + * only if you have a good reason and know the consequences. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * \{ + */ + +/* MPI / BIGNUM options */ +//#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +//#define POLARSSL_MPI_MAX_SIZE 512 /**< Maximum number of bytes for usable MPIs. */ + +/* CTR_DRBG options */ +//#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +//#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* HMAC_DRBG options */ +//#define POLARSSL_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define POLARSSL_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define POLARSSL_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define POLARSSL_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* ECP options */ +//#define POLARSSL_ECP_MAX_BITS 521 /**< Maximum bit size of groups */ +//#define POLARSSL_ECP_WINDOW_SIZE 6 /**< Maximum window size used */ +//#define POLARSSL_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +/* Entropy options */ +//#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +//#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ + +/* Memory buffer allocator options */ +//#define POLARSSL_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +/* Platform options */ +//#define POLARSSL_PLATFORM_STD_MEM_HDR /**< Header to include if POLARSSL_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */ +//#define POLARSSL_PLATFORM_STD_MALLOC malloc /**< Default allocator to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_FREE free /**< Default free to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_PRINTF printf /**< Default printf to use, can be undefined */ +//#define POLARSSL_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use, can be undefined */ + +/* SSL Cache options */ +//#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +//#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +/* SSL options */ +#define SSL_MAX_CONTENT_LEN 4096 /**< Size of the input / output buffer */ +//#define SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ +//#define POLARSSL_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 bits) */ + +/* Realteck Ameba HW Crypto */ +#define RTL_HW_CRYPTO + +/** + * Complete list of ciphersuites to use, in order of preference. + * + * \warning No dependency checking is done on that field! This option can only + * be used to restrict the set of available ciphersuites. It is your + * responsibility to make sure the needed modules are active. + * + * Use this to save a few hundred bytes of ROM (default ordering of all + * available ciphersuites) and a few to a few hundred bytes of RAM. + * + * The value below is only an example, not the default. + */ +//#define SSL_CIPHERSUITES TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/* Debug options */ +//#define POLARSSL_DEBUG_DFL_MODE POLARSSL_DEBUG_LOG_FULL /**< Default log: Full or Raw */ + +/* \} name SECTION: Module configuration options */ + +#include "check_config.h" + +#endif /* POLARSSL_CONFIG_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_srp.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_srp.h new file mode 100644 index 0000000..fa174a4 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/config_srp.h @@ -0,0 +1,2194 @@ +/** + * \file config.h + * + * \brief Configuration options (set of defines) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +#ifndef POLARSSL_CONFIG_H +#define POLARSSL_CONFIG_H + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def POLARSSL_HAVE_INT8 + * + * The system uses 8-bit wide native integers. + * + * Uncomment if native integers are 8-bit wide. + */ +//#define POLARSSL_HAVE_INT8 + +/** + * \def POLARSSL_HAVE_INT16 + * + * The system uses 16-bit wide native integers. + * + * Uncomment if native integers are 16-bit wide. + */ +//#define POLARSSL_HAVE_INT16 + +/** + * \def POLARSSL_HAVE_LONGLONG + * + * The compiler supports the 'long long' type. + * (Only used on 32-bit platforms) + */ +#define POLARSSL_HAVE_LONGLONG + +/** + * \def POLARSSL_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/timing.c + * library/padlock.c + * include/polarssl/bn_mul.h + * + * Comment to disable the use of assembly code. + */ +//#define POLARSSL_HAVE_ASM + +/** + * \def POLARSSL_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define POLARSSL_HAVE_SSE2 + +/** + * \def POLARSSL_HAVE_TIME + * + * System has time.h and time() / localtime() / gettimeofday(). + * + * Comment if your system does not support time functions + */ +//#define POLARSSL_HAVE_TIME + +/** + * \def POLARSSL_HAVE_IPV6 + * + * System supports the basic socket interface for IPv6 (RFC 3493), + * specifically getaddrinfo(), freeaddrinfo() and struct sockaddr_storage. + * + * Note: on Windows/MingW, XP or higher is required. + * + * Comment if your system does not support the IPv6 socket interface + */ +//#define POLARSSL_HAVE_IPV6 +/* \} name SECTION: System support */ + +/** + * \name SECTION: PolarSSL feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def POLARSSL_XXX_ALT + * + * Uncomment a macro to let PolarSSL use your alternate core implementation of + * a symmetric or hash algorithm (e.g. platform specific assembly optimized + * implementations). Keep in mind that the function prototypes should remain + * the same. + * + * Example: In case you uncomment POLARSSL_AES_ALT, PolarSSL will no longer + * provide the "struct aes_context" definition and omit the base function + * declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation for core algorithm + * functions + */ +//#define POLARSSL_AES_ALT +//#define POLARSSL_ARC4_ALT +//#define POLARSSL_BLOWFISH_ALT +//#define POLARSSL_CAMELLIA_ALT +//#define POLARSSL_DES_ALT +//#define POLARSSL_XTEA_ALT +//#define POLARSSL_MD2_ALT +//#define POLARSSL_MD4_ALT +//#define POLARSSL_MD5_ALT +//#define POLARSSL_SHA1_ALT +//#define POLARSSL_SHA256_ALT +//#define POLARSSL_SHA512_ALT + +/** + * \def POLARSSL_AES_ROM_TABLES + * + * Store the AES tables in ROM. + * + * Uncomment this macro to store the AES tables in ROM. + * + */ +#define POLARSSL_AES_ROM_TABLES + +/** + * \def POLARSSL_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CBC + +/** + * \def POLARSSL_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CFB + +/** + * \def POLARSSL_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define POLARSSL_CIPHER_MODE_CTR + +/** + * \def POLARSSL_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * Requires POLARSSL_ENABLE_WEAK_CIPHERSUITES as well to enable + * the following ciphersuites: + * TLS_ECDH_ECDSA_WITH_NULL_SHA + * TLS_ECDH_RSA_WITH_NULL_SHA + * TLS_ECDHE_ECDSA_WITH_NULL_SHA + * TLS_ECDHE_RSA_WITH_NULL_SHA + * TLS_ECDHE_PSK_WITH_NULL_SHA384 + * TLS_ECDHE_PSK_WITH_NULL_SHA256 + * TLS_ECDHE_PSK_WITH_NULL_SHA + * TLS_DHE_PSK_WITH_NULL_SHA384 + * TLS_DHE_PSK_WITH_NULL_SHA256 + * TLS_DHE_PSK_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_SHA256 + * TLS_RSA_WITH_NULL_SHA + * TLS_RSA_WITH_NULL_MD5 + * TLS_RSA_PSK_WITH_NULL_SHA384 + * TLS_RSA_PSK_WITH_NULL_SHA256 + * TLS_RSA_PSK_WITH_NULL_SHA + * TLS_PSK_WITH_NULL_SHA384 + * TLS_PSK_WITH_NULL_SHA256 + * TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites +#define POLARSSL_CIPHER_NULL_CIPHER + */ + +/** + * \def POLARSSL_CIPHER_PADDING_XXX + * + * Uncomment or comment macros to add support for specific padding modes + * in the cipher layer with cipher modes that support padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define POLARSSL_CIPHER_PADDING_PKCS7 +#define POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS +#define POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN +#define POLARSSL_CIPHER_PADDING_ZEROS + +/** + * \def POLARSSL_ENABLE_WEAK_CIPHERSUITES + * + * Enable weak ciphersuites in SSL / TLS. + * Warning: Only do so when you know what you are doing. This allows for + * channels with virtually no security at all! + * + * This enables the following ciphersuites: + * TLS_RSA_WITH_DES_CBC_SHA + * TLS_DHE_RSA_WITH_DES_CBC_SHA + * + * Uncomment this macro to enable weak ciphersuites +#define POLARSSL_ENABLE_WEAK_CIPHERSUITES + */ + +/** + * \def POLARSSL_ECP_XXXX_ENABLED + * + * Enables specific curves within the Elliptic Curve module. + * By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +#define POLARSSL_ECP_DP_SECP192R1_ENABLED +#define POLARSSL_ECP_DP_SECP224R1_ENABLED +#define POLARSSL_ECP_DP_SECP256R1_ENABLED +#define POLARSSL_ECP_DP_SECP384R1_ENABLED +#define POLARSSL_ECP_DP_SECP521R1_ENABLED +#define POLARSSL_ECP_DP_BP256R1_ENABLED +#define POLARSSL_ECP_DP_BP384R1_ENABLED +#define POLARSSL_ECP_DP_BP512R1_ENABLED +//#define POLARSSL_ECP_DP_M221_ENABLED // Not implemented yet! +#define POLARSSL_ECP_DP_M255_ENABLED +//#define POLARSSL_ECP_DP_M383_ENABLED // Not implemented yet! +//#define POLARSSL_ECP_DP_M511_ENABLED // Not implemented yet! + +/** + * \def POLARSSL_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define POLARSSL_ECP_NIST_OPTIM + +/** + * \def POLARSSL_KEY_EXCHANGE_SRP_ENABLED + * + * Enable the SRP based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_SRP_WITH_AES_256_CBC_SHA + */ +#define POLARSSL_KEY_EXCHANGE_SRP_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + */ +//#define POLARSSL_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_DHM_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_RSA_C, POLARSSL_PKCS1_V15, + * POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_ECDSA_C, POLARSSL_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + */ +//#define POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +//#define POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: POLARSSL_ECDH_C, POLARSSL_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +//#define POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def POLARSSL_ERROR_STRERROR_BC + * + * Make available the backward compatible error_strerror() next to the + * current polarssl_strerror(). + * + * Disable if you run into name conflicts and want to really remove the + * error_strerror() + */ +#define POLARSSL_ERROR_STRERROR_BC + +/** + * \def POLARSSL_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of polarssl_strerror() in + * third party libraries easier. + * + * Disable if you run into name conflicts and want to really remove the + * polarssl_strerror() + */ +#define POLARSSL_ERROR_STRERROR_DUMMY + +/** + * \def POLARSSL_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: POLARSSL_BIGNUM_C + */ +#define POLARSSL_GENPRIME + +/** + * \def POLARSSL_FS_IO + * + * Enable functions that use the filesystem. + */ +//#define POLARSSL_FS_IO + +/** + * \def POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources. These are the platform specific, + * hardclock and HAVEGE based poll functions. + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define POLARSSL_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def POLARSSL_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +//#define POLARSSL_NO_PLATFORM_ENTROPY + +/** + * \def POLARSSL_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * fprintf() + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define POLARSSL_MEMORY_DEBUG + +/** + * \def POLARSSL_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: POLARSSL_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() an backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define POLARSSL_MEMORY_BACKTRACE + +/** + * \def POLARSSL_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: POLARSSL_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +//#define POLARSSL_PKCS1_V15 + +/** + * \def POLARSSL_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: POLARSSL_MD_C, POLARSSL_RSA_C + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +//#define POLARSSL_PKCS1_V21 + +/** + * \def POLARSSL_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define POLARSSL_RSA_NO_CRT + +/** + * \def POLARSSL_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +//#define POLARSSL_SELF_TEST + +/** + * \def POLARSSL_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, PolarSSL can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define POLARSSL_SSL_ALERT_MESSAGES + +/** + * \def POLARSSL_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define POLARSSL_SSL_DEBUG_ALL + +/** + * \def POLARSSL_SSL_HW_RECORD_ACCEL + * + * Enable hooking functions in SSL module for hardware acceleration of + * individual records. + * + * Uncomment this macro to enable hooking functions. + */ +//#define POLARSSL_SSL_HW_RECORD_ACCEL + +/** + * \def POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + * + * Enable support for receiving and parsing SSLv2 Client Hello messages for the + * SSL Server module (POLARSSL_SSL_SRV_C). + * + * Comment this macro to disable support for SSLv2 Client Hello messages. + */ +//#define POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO + +/** + * \def POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + * + * Pick the ciphersuite according to the client's preferences rather than ours + * in the SSL Server module (POLARSSL_SSL_SRV_C). + * + * Uncomment this macro to respect client's ciphersuite order + */ +//#define POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE + +/** + * \def POLARSSL_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define POLARSSL_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def POLARSSL_SSL_PROTO_SSL3 + * + * Enable support for SSL 3.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for SSL 3.0 + */ +#define POLARSSL_SSL_PROTO_SSL3 + +/** + * \def POLARSSL_SSL_PROTO_TLS1 + * + * Enable support for TLS 1.0. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.0 + */ +#define POLARSSL_SSL_PROTO_TLS1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_1 + * + * Enable support for TLS 1.1. + * + * Requires: POLARSSL_MD5_C + * POLARSSL_SHA1_C + * + * Comment this macro to disable support for TLS 1.1 + */ +#define POLARSSL_SSL_PROTO_TLS1_1 + +/** + * \def POLARSSL_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2. + * + * Requires: POLARSSL_SHA1_C or POLARSSL_SHA256_C or POLARSSL_SHA512_C + * (Depends on ciphersuites) + * + * Comment this macro to disable support for TLS 1.2 + */ +#define POLARSSL_SSL_PROTO_TLS1_2 + +/** + * \def POLARSSL_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * + * Requires: POLARSSL_AES_C + * POLARSSL_SHA256_C + * POLARSSL_CIPHER_MODE_CBC + * + * Comment this macro to disable support for SSL session tickets + */ +#define POLARSSL_SSL_SESSION_TICKETS + +/** + * \def POLARSSL_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Comment this macro to disable support for server name indication in SSL + */ +#define POLARSSL_SSL_SERVER_NAME_INDICATION + +/** + * \def POLARSSL_SSL_TRUNCATED_HMAC + * + * Enable support for RFC 6066 truncated HMAC in SSL. + * + * Comment this macro to disable support for truncated HMAC in SSL + */ +#define POLARSSL_SSL_TRUNCATED_HMAC + +/** + * \def POLARSSL_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define POLARSSL_THREADING_ALT + +/** + * \def POLARSSL_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: POLARSSL_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define POLARSSL_THREADING_PTHREAD + +/** + * \def POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an extension in a v1 or v2 certificate. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 + +/** + * \def POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + * + * If set, the X509 parser will not break-off when parsing an X509 certificate + * and encountering an unknown critical extension. + * + * Uncomment to prevent an error. + */ +//#define POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION + +/** + * \def POLARSSL_ZLIB_SUPPORT + * + * If set, the SSL/TLS module uses ZLIB to support compression and + * decompression of packet data. + * + * Used in: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This feature requires zlib library and headers to be present. + * + * Uncomment to enable use of ZLIB + */ +//#define POLARSSL_ZLIB_SUPPORT +/* \} name SECTION: PolarSSL feature support */ + +/** + * \name SECTION: PolarSSL modules + * + * This section enables or disables entire modules in PolarSSL + * \{ + */ + +/** + * \def POLARSSL_AESNI_C + * + * Enable AES-NI support on x86-64. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the AES-NI instructions on x86-64 + */ +//#define POLARSSL_AESNI_C + +/** + * \def POLARSSL_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/ssl_tls.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * TLS_RSA_WITH_AES_256_GCM_SHA384 + * TLS_RSA_WITH_AES_256_CBC_SHA256 + * TLS_RSA_WITH_AES_256_CBC_SHA + * TLS_RSA_WITH_AES_128_GCM_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA256 + * TLS_RSA_WITH_AES_128_CBC_SHA + * TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * TLS_PSK_WITH_AES_256_GCM_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA384 + * TLS_PSK_WITH_AES_256_CBC_SHA + * TLS_PSK_WITH_AES_128_GCM_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA256 + * TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define POLARSSL_AES_C + +/** + * \def POLARSSL_ARC4_C + * + * Enable the ARCFOUR stream cipher. + * + * Module: library/arc4.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_RC4_128_SHA + * TLS_ECDH_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_ECDSA_WITH_RC4_128_SHA + * TLS_ECDHE_RSA_WITH_RC4_128_SHA + * TLS_ECDHE_PSK_WITH_RC4_128_SHA + * TLS_DHE_PSK_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_SHA + * TLS_RSA_WITH_RC4_128_MD5 + * TLS_RSA_PSK_WITH_RC4_128_SHA + * TLS_PSK_WITH_RC4_128_SHA + */ +//#define POLARSSL_ARC4_C + +/** + * \def POLARSSL_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +//#define POLARSSL_ASN1_PARSE_C + +/** + * \def POLARSSL_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +//#define POLARSSL_ASN1_WRITE_C + +/** + * \def POLARSSL_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +//#define POLARSSL_BASE64_C + +/** + * \def POLARSSL_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * Caller: library/dhm.c + * library/ecp.c + * library/rsa.c + * library/ssl_tls.c + * + * This module is required for RSA and DHM support. + */ +#define POLARSSL_BIGNUM_C + +/** + * \def POLARSSL_BLOWFISH_C + * + * Enable the Blowfish block cipher. + * + * Module: library/blowfish.c + */ +//#define POLARSSL_BLOWFISH_C + +/** + * \def POLARSSL_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +//#define POLARSSL_CAMELLIA_C + +/** + * \def POLARSSL_CERTS_C + * + * Enable the test certificates. + * + * Module: library/certs.c + * Caller: + * + * Requires: POLARSSL_PEM_PARSE_C + * + * This module is used for testing (ssl_client/server). + */ +//#define POLARSSL_CERTS_C + +/** + * \def POLARSSL_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ssl_tls.c + * + * Uncomment to enable generic cipher wrappers. + */ +#define POLARSSL_CIPHER_C + +/** + * \def POLARSSL_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-256-based random generator. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: POLARSSL_AES_C + * + * This module provides the CTR_DRBG AES-256 random number generator. + */ +//#define POLARSSL_CTR_DRBG_C + +/** + * \def POLARSSL_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module provides debugging functions. + */ +#define POLARSSL_DEBUG_C + +/** + * \def POLARSSL_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/ssl_tls.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_WITH_3DES_EDE_CBC_SHA + * TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA + * TLS_PSK_WITH_3DES_EDE_CBC_SHA + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + */ +//#define POLARSSL_DES_C + +/** + * \def POLARSSL_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + */ +//#define POLARSSL_DHM_C + +/** + * \def POLARSSL_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: POLARSSL_ECP_C + */ +//#define POLARSSL_ECDH_C + +/** + * \def POLARSSL_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: POLARSSL_ECP_C, POLARSSL_ASN1_WRITE_C, POLARSSL_ASN1_PARSE_C + */ +//#define POLARSSL_ECDSA_C + +/** + * \def POLARSSL_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * + * Requires: POLARSSL_BIGNUM_C and at least one POLARSSL_ECP_DP_XXX_ENABLED + */ +//#define POLARSSL_ECP_C + +/** + * \def POLARSSL_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: POLARSSL_SHA512_C + * + * This module provides a generic entropy pool + */ +//#define POLARSSL_ENTROPY_C + +/** + * \def POLARSSL_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables err_strerror(). + */ +#define POLARSSL_ERROR_C + +/** + * \def POLARSSL_GCM_C + * + * Enable the Galois/Counter Mode (GCM) for AES. + * + * Module: library/gcm.c + * + * Requires: POLARSSL_AES_C or POLARSSL_CAMELLIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +//#define POLARSSL_GCM_C + +/** + * \def POLARSSL_HAVEGE_C + * + * Enable the HAVEGE random generator. + * + * Warning: the HAVEGE random generator is not suitable for virtualized + * environments + * + * Warning: the HAVEGE random generator is dependent on timing and specific + * processor traits. It is therefore not advised to use HAVEGE as + * your applications primary random generator or primary entropy pool + * input. As a secondary input to your entropy pool, it IS able add + * the (limited) extra entropy it provides. + * + * Module: library/havege.c + * Caller: + * + * Requires: POLARSSL_TIMING_C + * + * Uncomment to enable the HAVEGE random generator. + */ +//#define POLARSSL_HAVEGE_C + +/** + * \def POLARSSL_MD_C + * + * Enable the generic message digest layer. + * + * Module: library/md.c + * Caller: + * + * Uncomment to enable generic message digest wrappers. + */ +#define POLARSSL_MD_C + +/** + * \def POLARSSL_MD2_C + * + * Enable the MD2 hash algorithm. + * + * Module: library/md2.c + * Caller: + * + * Uncomment to enable support for (rare) MD2-signed X.509 certs. + */ +//#define POLARSSL_MD2_C + +/** + * \def POLARSSL_MD4_C + * + * Enable the MD4 hash algorithm. + * + * Module: library/md4.c + * Caller: + * + * Uncomment to enable support for (rare) MD4-signed X.509 certs. + */ +//#define POLARSSL_MD4_C + +/** + * \def POLARSSL_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for SSL/TLS and X.509. + * PEM_PARSE uses MD5 for decrypting encrypted keys. + */ +#define POLARSSL_MD5_C + +/** + * \def POLARSSL_MEMORY_C + * + * Enable the memory allocation layer. + * By default PolarSSL uses the system-provided malloc() and free(). + * (As long as POLARSSL_MEMORY_STDMALLOC and POLARSSL_MEMORY_STDFREE + * are defined and unmodified) + * + * This allows different allocators (self-implemented or provided) + * + * Enable this layer to allow use of alternative memory allocators. + */ +#define POLARSSL_MEMORY_C + +/** + * \def POLARSSL_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces malloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: POLARSSL_MEMORY_C + * + * Enable this module to enable the buffer memory allocator. + */ +//#define POLARSSL_MEMORY_BUFFER_ALLOC_C + +/** + * \def POLARSSL_NET_C + * + * Enable the TCP/IP networking routines. + * + * Module: library/net.c + * + * This module provides TCP/IP networking routines. + */ +#define POLARSSL_NET_C + +/** + * \def POLARSSL_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define POLARSSL_OID_C + +/** + * \def POLARSSL_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: POLARSSL_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +//#define POLARSSL_PADLOCK_C + +/** + * \def POLARSSL_PBKDF2_C + * + * Enable PKCS#5 PBKDF2 key derivation function. + * DEPRECATED: Use POLARSSL_PKCS5_C instead + * + * Module: library/pbkdf2.c + * + * Requires: POLARSSL_PKCS5_C + * + * This module adds support for the PKCS#5 PBKDF2 key derivation function. + */ +//#define POLARSSL_PBKDF2_C + +/** + * \def POLARSSL_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +//#define POLARSSL_PEM_PARSE_C + +/** + * \def POLARSSL_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: POLARSSL_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +//#define POLARSSL_PEM_WRITE_C + +/** + * \def POLARSSL_PK_C + * + * Enable the generic public (asymetric) key layer. + * + * Module: library/pk.c + * Caller: library/ssl_tls.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_RSA_C or POLARSSL_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +//#define POLARSSL_PK_C + +/** + * \def POLARSSL_PK_PARSE_C + * + * Enable the generic public (asymetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +//#define POLARSSL_PK_PARSE_C + +/** + * \def POLARSSL_PK_WRITE_C + * + * Enable the generic public (asymetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: POLARSSL_PK_C + * + * Uncomment to enable generic public key write functions. + */ +//#define POLARSSL_PK_WRITE_C + +/** + * \def POLARSSL_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: POLARSSL_MD_C + * + * This module adds support for the PKCS#5 functions. + */ +//#define POLARSSL_PKCS5_C + +/** + * \def POLARSSL_PKCS11_C + * + * Enable wrapper for PKCS#11 smartcard support. + * + * Module: library/pkcs11.c + * Caller: library/pk.c + * + * Requires: POLARSSL_PK_C + * + * This module enables SSL/TLS PKCS #11 smartcard support. + * Requires the presence of the PKCS#11 helper library (libpkcs11-helper) + */ +//#define POLARSSL_PKCS11_C + +/** + * \def POLARSSL_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_CIPHER_C, POLARSSL_MD_C + * Can use: POLARSSL_ARC4_C + * + * This module enables PKCS#12 functions. + */ +//#define POLARSSL_PKCS12_C + +/** + * \def POLARSSL_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C + */ +//#define POLARSSL_RSA_C + +/** + * \def POLARSSL_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * library/x509write_crt.c + * + * This module is required for SSL/TLS and SHA1-signed certificates. + */ +#define POLARSSL_SHA1_C + +/** + * \def POLARSSL_SHA256_C + * + * Enable the SHA-224 and SHA-256 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA2_C) + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * This module adds support for SHA-224 and SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define POLARSSL_SHA256_C + +/** + * \def POLARSSL_SHA512_C + * + * Enable the SHA-384 and SHA-512 cryptographic hash algorithms. + * (Used to be POLARSSL_SHA4_C) + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_cli.c + * library/ssl_srv.c + * + * This module adds support for SHA-384 and SHA-512. + */ +#define POLARSSL_SHA512_C + +/** + * \def POLARSSL_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: POLARSSL_SSL_CACHE_C + */ +#define POLARSSL_SSL_CACHE_C + +/** + * \def POLARSSL_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl_cli.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define POLARSSL_SSL_CLI_C + +/** + * \def POLARSSL_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl_srv.c + * Caller: + * + * Requires: POLARSSL_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +//#define POLARSSL_SSL_SRV_C + +/** + * \def POLARSSL_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * + * Requires: POLARSSL_CIPHER_C, POLARSSL_MD_C + * and at least one of the POLARSSL_SSL_PROTO_* defines + * + * This module is required for SSL/TLS. + */ +#define POLARSSL_SSL_TLS_C + +/** + * \def POLARSSL_THREADING_C + * + * Enable the threading abstraction layer. + * By default PolarSSL assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either POLARSSL_THREADING_ALT or + * POLARSSL_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within PolarSSL + */ +//#define POLARSSL_THREADING_C + +/** + * \def POLARSSL_TIMING_C + * + * Enable the portable timing interface. + * + * Module: library/timing.c + * Caller: library/havege.c + * + * This module is used by the HAVEGE random number generator. + */ +//#define POLARSSL_TIMING_C + +/** + * \def POLARSSL_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define POLARSSL_VERSION_C + +/** + * \def POLARSSL_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: POLARSSL_ASN1_PARSE_C, POLARSSL_BIGNUM_C, POLARSSL_OID_C, + * POLARSSL_PK_PARSE_C + * + * This module is required for the X.509 parsing modules. + */ +//#define POLARSSL_X509_USE_C + +/** + * \def POLARSSL_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_cli.c + * library/ssl_srv.c + * library/ssl_tls.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +//#define POLARSSL_X509_CRT_PARSE_C + +/** + * \def POLARSSL_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +//#define POLARSSL_X509_CRL_PARSE_C + +/** + * \def POLARSSL_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: POLARSSL_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +//#define POLARSSL_X509_CSR_PARSE_C + +/** + * \def POLARSSL_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: POLARSSL_BIGNUM_C, POLARSSL_OID_C, POLARSSL_PK_WRITE_C + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +//#define POLARSSL_X509_CREATE_C + +/** + * \def POLARSSL_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +//#define POLARSSL_X509_CRT_WRITE_C + +/** + * \def POLARSSL_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: POLARSSL_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +//#define POLARSSL_X509_CSR_WRITE_C + +/** + * \def POLARSSL_XTEA_C + * + * Enable the XTEA block cipher. + * + * Module: library/xtea.c + * Caller: + */ +//#define POLARSSL_XTEA_C + +/** + * \def POLARSSL_SRP_C + * + * Enable the SRP library. + * + * Module: library/srp.c + * Caller: library/ssl_cli.c + */ +#define POLARSSL_SRP_C + +/* \} name SECTION: PolarSSL modules */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * Our advice is to enable POLARSSL_CONFIG_OPTIONS and change values here + * only if you have a good reason and know the consequences. + * + * If POLARSSL_CONFIG_OPTIONS is undefined here the options in the module + * header file take precedence. + * + * Please check the respective header file for documentation on these + * parameters (to prevent duplicate documentation). + * + * Uncomment POLARSSL_CONFIG_OPTIONS to enable using the values defined here. + * \{ + */ +#define POLARSSL_CONFIG_OPTIONS /**< Enable config.h module value configuration */ + +#if defined(POLARSSL_CONFIG_OPTIONS) + +// MPI / BIGNUM options +// +#define POLARSSL_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */ +#define POLARSSL_MPI_MAX_SIZE 128 /**< Maximum number of bytes for usable MPIs. Default in 512. + POLARSSL_MPI_MAX_SIZE 128 for bignum and premaster size is only verified for SSL SRP handshake with group parameter of 1024 */ +// CTR_DRBG options +// +#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +// ECP options +// +#define POLARSSL_ECP_MAX_BITS 521 /**< Maximum bit size of groups */ +#define POLARSSL_ECP_WINDOW_SIZE 6 /**< Maximum window size used */ +#define POLARSSL_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +// Entropy options +// +#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ + +// Memory options +#define MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ +#define POLARSSL_MEMORY_STDMALLOC malloc /**< Default allocator to use, can be undefined */ +#define POLARSSL_MEMORY_STDFREE free /**< Default free to use, can be undefined */ + +// SSL Cache options +// +#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +// SSL options +// +#define SSL_MAX_CONTENT_LEN 512 /**< Size of the input / output buffer. Default in 16384. + SSL_MAX_CONTENT_LEN 512 is only verified for SSL SRP handshake */ +#define SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ + +#endif /* POLARSSL_CONFIG_OPTIONS */ + +/* \} name */ + +/* + * Sanity checks on defines and dependencies + */ +#if defined(POLARSSL_AESNI_C) && !defined(POLARSSL_HAVE_ASM) +#error "POLARSSL_AESNI_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_CERTS_C) && !defined(POLARSSL_PEM_PARSE_C) +#error "POLARSSL_CERTS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_CTR_DRBG_C) && !defined(POLARSSL_AES_C) +#error "POLARSSL_CTR_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_DHM_C) && !defined(POLARSSL_BIGNUM_C) +#error "POLARSSL_DHM_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECDH_C) && !defined(POLARSSL_ECP_C) +#error "POLARSSL_ECDH_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECDSA_C) && \ + ( !defined(POLARSSL_ECP_C) || \ + !defined(POLARSSL_ASN1_PARSE_C) || \ + !defined(POLARSSL_ASN1_WRITE_C) ) +#error "POLARSSL_ECDSA_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ECP_C) && ( !defined(POLARSSL_BIGNUM_C) || ( \ + !defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP256R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP384R1_ENABLED) && \ + !defined(POLARSSL_ECP_DP_BP512R1_ENABLED) ) ) +#error "POLARSSL_ECP_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_ENTROPY_C) && (!defined(POLARSSL_SHA512_C) && \ + !defined(POLARSSL_SHA256_C)) +#error "POLARSSL_ENTROPY_C defined, but not all prerequisites" +#endif +#if defined(POLARSSL_ENTROPY_C) && defined(POLARSSL_SHA512_C) && \ + defined(POLARSSL_CONFIG_OPTIONS) && (CTR_DRBG_ENTROPY_LEN > 64) +#error "CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(POLARSSL_ENTROPY_C) && !defined(POLARSSL_SHA512_C) && \ + defined(POLARSSL_CONFIG_OPTIONS) && (CTR_DRBG_ENTROPY_LEN > 32) +#error "CTR_DRBG_ENTROPY_LEN value too high" +#endif + +#if defined(POLARSSL_GCM_C) && ( \ + !defined(POLARSSL_AES_C) && !defined(POLARSSL_CAMELLIA_C) ) +#error "POLARSSL_GCM_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_HAVEGE_C) && !defined(POLARSSL_TIMING_C) +#error "POLARSSL_HAVEGE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) && !defined(POLARSSL_DHM_C) +#error "POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && \ + !defined(POLARSSL_ECDH_C) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + ( !defined(POLARSSL_DHM_C) || !defined(POLARSSL_RSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) || !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_RSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) || !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \ + ( !defined(POLARSSL_ECDH_C) || !defined(POLARSSL_ECDSA_C) || \ + !defined(POLARSSL_X509_CRT_PARSE_C) ) +#error "POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) && \ + ( !defined(POLARSSL_RSA_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ||\ + !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + ( !defined(POLARSSL_RSA_C) || !defined(POLARSSL_X509_CRT_PARSE_C) ||\ + !defined(POLARSSL_PKCS1_V15) ) +#error "POLARSSL_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C) && !defined(POLARSSL_MEMORY_C) +#error "POLARSSL_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PADLOCK_C) && !defined(POLARSSL_HAVE_ASM) +#error "POLARSSL_PADLOCK_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PBKDF2_C) && !defined(POLARSSL_MD_C) +#error "POLARSSL_PBKDF2_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PEM_PARSE_C) && !defined(POLARSSL_BASE64_C) +#error "POLARSSL_PEM_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PEM_WRITE_C) && !defined(POLARSSL_BASE64_C) +#error "POLARSSL_PEM_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PK_PARSE_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PK_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PK_WRITE_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PK_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_PKCS11_C) && !defined(POLARSSL_PK_C) +#error "POLARSSL_PKCS11_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_RSA_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) ) +#error "POLARSSL_RSA_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_SSL3) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_SSL3 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_TLS1 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_1) && ( !defined(POLARSSL_MD5_C) || \ + !defined(POLARSSL_SHA1_C) ) +#error "POLARSSL_SSL_PROTO_TLS1_1 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) && ( !defined(POLARSSL_SHA1_C) && \ + !defined(POLARSSL_SHA256_C) && !defined(POLARSSL_SHA512_C) ) +#error "POLARSSL_SSL_PROTO_TLS1_2 defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_CLI_C) && !defined(POLARSSL_SSL_TLS_C) +#error "POLARSSL_SSL_CLI_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && ( !defined(POLARSSL_CIPHER_C) || \ + !defined(POLARSSL_MD_C) ) +#error "POLARSSL_SSL_TLS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_SRV_C) && !defined(POLARSSL_SSL_TLS_C) +#error "POLARSSL_SSL_SRV_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (!defined(POLARSSL_SSL_PROTO_SSL3) && \ + !defined(POLARSSL_SSL_PROTO_TLS1) && !defined(POLARSSL_SSL_PROTO_TLS1_1) && \ + !defined(POLARSSL_SSL_PROTO_TLS1_2)) +#error "POLARSSL_SSL_TLS_C defined, but no protocols are active" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_SSL3) && \ + defined(POLARSSL_SSL_PROTO_TLS1_1) && !defined(POLARSSL_SSL_PROTO_TLS1)) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_TLS1) && \ + defined(POLARSSL_SSL_PROTO_TLS1_2) && !defined(POLARSSL_SSL_PROTO_TLS1_1)) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_TLS_C) && (defined(POLARSSL_SSL_PROTO_SSL3) && \ + defined(POLARSSL_SSL_PROTO_TLS1_2) && (!defined(POLARSSL_SSL_PROTO_TLS1) || \ + !defined(POLARSSL_SSL_PROTO_TLS1_1))) +#error "Illegal protocol selection" +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) && defined(POLARSSL_SSL_TLS_C) && \ + ( !defined(POLARSSL_AES_C) || !defined(POLARSSL_SHA256_C) || \ + !defined(POLARSSL_CIPHER_MODE_CBC) ) +#error "POLARSSL_SSL_SESSION_TICKETS_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_THREADING_PTHREAD) +#if !defined(POLARSSL_THREADING_C) || defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_PTHREAD defined, but not all prerequisites" +#endif +#define POLARSSL_THREADING_IMPL +#endif + +#if defined(POLARSSL_THREADING_ALT) +#if !defined(POLARSSL_THREADING_C) || defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_ALT defined, but not all prerequisites" +#endif +#define POLARSSL_THREADING_IMPL +#endif + +#if defined(POLARSSL_THREADING_C) && !defined(POLARSSL_THREADING_IMPL) +#error "POLARSSL_THREADING_C defined, single threading implementation required" +#endif +#undef POLARSSL_THREADING_IMPL + +#if defined(POLARSSL_X509_USE_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) || !defined(POLARSSL_ASN1_PARSE_C) || \ + !defined(POLARSSL_PK_PARSE_C) ) +#error "POLARSSL_X509_USE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CREATE_C) && ( !defined(POLARSSL_BIGNUM_C) || \ + !defined(POLARSSL_OID_C) || !defined(POLARSSL_ASN1_WRITE_C) || \ + !defined(POLARSSL_PK_WRITE_C) ) +#error "POLARSSL_X509_CREATE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CRT_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRL_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CRL_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CSR_PARSE_C) && ( !defined(POLARSSL_X509_USE_C) ) +#error "POLARSSL_X509_CSR_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CRT_WRITE_C) && ( !defined(POLARSSL_X509_CREATE_C) ) +#error "POLARSSL_X509_CRT_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_X509_CSR_WRITE_C) && ( !defined(POLARSSL_X509_CREATE_C) ) +#error "POLARSSL_X509_CSR_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_SRP_ENABLED) && ( !defined(POLARSSL_SRP_C) ) +#error "POLARSSL_KEY_EXCHANGE_SRP_ENABLED defined, but not all prerequisites" +#endif + +#endif /* config.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ctr_drbg.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ctr_drbg.h new file mode 100644 index 0000000..bebbfe9 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ctr_drbg.h @@ -0,0 +1,274 @@ +/** + * \file ctr_drbg.h + * + * \brief CTR_DRBG based on AES-256 (NIST SP 800-90) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_CTR_DRBG_H +#define POLARSSL_CTR_DRBG_H + +#include + +#include "aes.h" + +#define POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED -0x0034 /**< The entropy source failed. */ +#define POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG -0x0036 /**< Too many random requested in single call. */ +#define POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG -0x0038 /**< Input too large (Entropy + additional). */ +#define POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR -0x003A /**< Read/write error in file. */ + +#define CTR_DRBG_BLOCKSIZE 16 /**< Block size used by the cipher */ +#define CTR_DRBG_KEYSIZE 32 /**< Key size used by the cipher */ +#define CTR_DRBG_KEYBITS ( CTR_DRBG_KEYSIZE * 8 ) +#define CTR_DRBG_SEEDLEN ( CTR_DRBG_KEYSIZE + CTR_DRBG_BLOCKSIZE ) + /**< The seed length (counter + AES key) */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(CTR_DRBG_ENTROPY_LEN) +#if defined(POLARSSL_SHA512_C) && !defined(POLARSSL_ENTROPY_FORCE_SHA256) +#define CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +#else +#define CTR_DRBG_ENTROPY_LEN 32 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +#endif +#endif + +#if !defined(CTR_DRBG_RESEED_INTERVAL) +#define CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#endif + +#if !defined(CTR_DRBG_MAX_INPUT) +#define CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#endif + +#if !defined(CTR_DRBG_MAX_REQUEST) +#define CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#endif + +#if !defined(CTR_DRBG_MAX_SEED_INPUT) +#define CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ +#endif + +/* \} name SECTION: Module settings */ + +#define CTR_DRBG_PR_OFF 0 /**< No prediction resistance */ +#define CTR_DRBG_PR_ON 1 /**< Prediction resistance enabled */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief CTR_DRBG context structure + */ +typedef struct +{ + unsigned char counter[16]; /*!< counter (V) */ + int reseed_counter; /*!< reseed counter */ + int prediction_resistance; /*!< enable prediction resistance (Automatic + reseed before every random generation) */ + size_t entropy_len; /*!< amount of entropy grabbed on each + (re)seed */ + int reseed_interval; /*!< reseed interval */ + + aes_context aes_ctx; /*!< AES context */ + + /* + * Callbacks (Entropy) + */ + int (*f_entropy)(void *, unsigned char *, size_t); + + void *p_entropy; /*!< context for the entropy function */ +} +ctr_drbg_context; + +/** + * \brief CTR_DRBG initialization + * + * Note: Personalization data can be provided in addition to the more generic + * entropy source to make this instantiation as unique as possible. + * + * \param ctx CTR_DRBG context to be initialized + * \param f_entropy Entropy callback (p_entropy, buffer to fill, buffer + * length) + * \param p_entropy Entropy context + * \param custom Personalization data (Device specific identifiers) + * (Can be NULL) + * \param len Length of personalization data + * + * \return 0 if successful, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED + */ +int ctr_drbg_init( ctr_drbg_context *ctx, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len ); + +/** + * \brief Clear CTR_CRBG context data + * + * \param ctx CTR_DRBG context to clear + */ +void ctr_drbg_free( ctr_drbg_context *ctx ); + +/** + * \brief Enable / disable prediction resistance (Default: Off) + * + * Note: If enabled, entropy is used for ctx->entropy_len before each call! + * Only use this if you have ample supply of good entropy! + * + * \param ctx CTR_DRBG context + * \param resistance CTR_DRBG_PR_ON or CTR_DRBG_PR_OFF + */ +void ctr_drbg_set_prediction_resistance( ctr_drbg_context *ctx, + int resistance ); + +/** + * \brief Set the amount of entropy grabbed on each (re)seed + * (Default: CTR_DRBG_ENTROPY_LEN) + * + * \param ctx CTR_DRBG context + * \param len Amount of entropy to grab + */ +void ctr_drbg_set_entropy_len( ctr_drbg_context *ctx, + size_t len ); + +/** + * \brief Set the reseed interval + * (Default: CTR_DRBG_RESEED_INTERVAL) + * + * \param ctx CTR_DRBG context + * \param interval Reseed interval + */ +void ctr_drbg_set_reseed_interval( ctr_drbg_context *ctx, + int interval ); + +/** + * \brief CTR_DRBG reseeding (extracts data from entropy source) + * + * \param ctx CTR_DRBG context + * \param additional Additional data to add to state (Can be NULL) + * \param len Length of additional data + * + * \return 0 if successful, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED + */ +int ctr_drbg_reseed( ctr_drbg_context *ctx, + const unsigned char *additional, size_t len ); + +/** + * \brief CTR_DRBG update state + * + * \param ctx CTR_DRBG context + * \param additional Additional data to update state with + * \param add_len Length of additional data + */ +void ctr_drbg_update( ctr_drbg_context *ctx, + const unsigned char *additional, size_t add_len ); + +/** + * \brief CTR_DRBG generate random with additional update input + * + * Note: Automatically reseeds if reseed_counter is reached. + * + * \param p_rng CTR_DRBG context + * \param output Buffer to fill + * \param output_len Length of the buffer + * \param additional Additional data to update with (Can be NULL) + * \param add_len Length of additional data + * + * \return 0 if successful, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED, or + * POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG + */ +int ctr_drbg_random_with_add( void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, size_t add_len ); + +/** + * \brief CTR_DRBG generate random + * + * Note: Automatically reseeds if reseed_counter is reached. + * + * \param p_rng CTR_DRBG context + * \param output Buffer to fill + * \param output_len Length of the buffer + * + * \return 0 if successful, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED, or + * POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG + */ +int ctr_drbg_random( void *p_rng, + unsigned char *output, size_t output_len ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Write a seed file + * + * \param ctx CTR_DRBG context + * \param path Name of the file + * + * \return 0 if successful, + * POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR on file error, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED + */ +int ctr_drbg_write_seed_file( ctr_drbg_context *ctx, const char *path ); + +/** + * \brief Read and update a seed file. Seed is added to this + * instance + * + * \param ctx CTR_DRBG context + * \param path Name of the file + * + * \return 0 if successful, + * POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR on file error, + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or + * POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG + */ +int ctr_drbg_update_seed_file( ctr_drbg_context *ctx, const char *path ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int ctr_drbg_self_test( int verbose ); + +/* Internal functions (do not call directly) */ +int ctr_drbg_init_entropy_len( ctr_drbg_context *, + int (*)(void *, unsigned char *, size_t), void *, + const unsigned char *, size_t, size_t ); + +#ifdef __cplusplus +} +#endif + +#endif /* ctr_drbg.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/debug.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/debug.h new file mode 100644 index 0000000..0dd79d5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/debug.h @@ -0,0 +1,152 @@ +/** + * \file debug.h + * + * \brief Debug functions + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_DEBUG_H +#define POLARSSL_DEBUG_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#include "ssl.h" +#if defined(POLARSSL_ECP_C) +#include "ecp.h" +#endif + +#if defined(POLARSSL_DEBUG_C) + +#define POLARSSL_DEBUG_LOG_FULL 0 /**< Include file:line in log lines */ +#define POLARSSL_DEBUG_LOG_RAW 1 /**< Only log raw debug lines */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(POLARSSL_DEBUG_DFL_MODE) +#define POLARSSL_DEBUG_DFL_MODE POLARSSL_DEBUG_LOG_FULL /**< Default log: Full or Raw */ +#endif + +/* \} name SECTION: Module settings */ + + +#define SSL_DEBUG_MSG( level, args ) \ + debug_print_msg( ssl, level, __FILE__, __LINE__, debug_fmt args ); + +#define SSL_DEBUG_RET( level, text, ret ) \ + debug_print_ret( ssl, level, __FILE__, __LINE__, text, ret ); + +#define SSL_DEBUG_BUF( level, text, buf, len ) \ + debug_print_buf( ssl, level, __FILE__, __LINE__, text, buf, len ); + +#if defined(POLARSSL_BIGNUM_C) +#define SSL_DEBUG_MPI( level, text, X ) \ + debug_print_mpi( ssl, level, __FILE__, __LINE__, text, X ); +#endif + +#if defined(POLARSSL_ECP_C) +#define SSL_DEBUG_ECP( level, text, X ) \ + debug_print_ecp( ssl, level, __FILE__, __LINE__, text, X ); +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) +#define SSL_DEBUG_CRT( level, text, crt ) \ + debug_print_crt( ssl, level, __FILE__, __LINE__, text, crt ); +#endif + +#else /* POLARSSL_DEBUG_C */ + +#define SSL_DEBUG_MSG( level, args ) do { } while( 0 ) +#define SSL_DEBUG_RET( level, text, ret ) do { } while( 0 ) +#define SSL_DEBUG_BUF( level, text, buf, len ) do { } while( 0 ) +#define SSL_DEBUG_MPI( level, text, X ) do { } while( 0 ) +#define SSL_DEBUG_ECP( level, text, X ) do { } while( 0 ) +#define SSL_DEBUG_CRT( level, text, crt ) do { } while( 0 ) + +#endif /* POLARSSL_DEBUG_C */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Set the log mode for the debug functions globally + * (Default value: POLARSSL_DEBUG_DFL_MODE) + * + * \param log_mode The log mode to use (POLARSSL_DEBUG_LOG_FULL or + * POLARSSL_DEBUG_LOG_RAW) + */ +void debug_set_log_mode( int log_mode ); + +/** + * \brief Set the level threshold to handle globally. Messages that have a + * level over the threshold value are ignored. + * (Default value: 0 (No debug)) + * + * \param threshold maximum level of messages to pass on + */ +void debug_set_threshold( int threshold ); + +char *debug_fmt( const char *format, ... ); + +void debug_print_msg( const ssl_context *ssl, int level, + const char *file, int line, const char *text ); + +void debug_print_ret( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, int ret ); + +void debug_print_buf( const ssl_context *ssl, int level, + const char *file, int line, const char *text, + unsigned char *buf, size_t len ); + +#if defined(POLARSSL_BIGNUM_C) +void debug_print_mpi( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mpi *X ); +#endif + +#if defined(POLARSSL_ECP_C) +void debug_print_ecp( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const ecp_point *X ); +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) +void debug_print_crt( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const x509_crt *crt ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* debug.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/des.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/des.h new file mode 100644 index 0000000..8cf1a3b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/des.h @@ -0,0 +1,300 @@ +/** + * \file des.h + * + * \brief DES block cipher + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_DES_H +#define POLARSSL_DES_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +#define POLARSSL_ERR_DES_INVALID_INPUT_LENGTH -0x0032 /**< The data input has an invalid length. */ + +#define DES_KEY_SIZE 8 + +#if !defined(POLARSSL_DES_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief DES context structure + */ +typedef struct +{ + int mode; /*!< encrypt/decrypt */ + uint32_t sk[32]; /*!< DES subkeys */ +#ifdef RTL_HW_CRYPTO + unsigned char enc_key[DES_KEY_SIZE]; + unsigned char dec_key[DES_KEY_SIZE]; +#endif +} +des_context; + +/** + * \brief Triple-DES context structure + */ +typedef struct +{ + int mode; /*!< encrypt/decrypt */ + uint32_t sk[96]; /*!< 3DES subkeys */ +#ifdef RTL_HW_CRYPTO + unsigned char enc_key[DES_KEY_SIZE * 3]; + unsigned char dec_key[DES_KEY_SIZE * 3]; +#endif +} +des3_context; + +/** + * \brief Initialize DES context + * + * \param ctx DES context to be initialized + */ +void des_init( des_context *ctx ); + +/** + * \brief Clear DES context + * + * \param ctx DES context to be cleared + */ +void des_free( des_context *ctx ); + +/** + * \brief Initialize Triple-DES context + * + * \param ctx DES3 context to be initialized + */ +void des3_init( des3_context *ctx ); + +/** + * \brief Clear Triple-DES context + * + * \param ctx DES3 context to be cleared + */ +void des3_free( des3_context *ctx ); + +/** + * \brief Set key parity on the given key to odd. + * + * DES keys are 56 bits long, but each byte is padded with + * a parity bit to allow verification. + * + * \param key 8-byte secret key + */ +void des_key_set_parity( unsigned char key[DES_KEY_SIZE] ); + +/** + * \brief Check that key parity on the given key is odd. + * + * DES keys are 56 bits long, but each byte is padded with + * a parity bit to allow verification. + * + * \param key 8-byte secret key + * + * \return 0 is parity was ok, 1 if parity was not correct. + */ +int des_key_check_key_parity( const unsigned char key[DES_KEY_SIZE] ); + +/** + * \brief Check that key is not a weak or semi-weak DES key + * + * \param key 8-byte secret key + * + * \return 0 if no weak key was found, 1 if a weak key was identified. + */ +int des_key_check_weak( const unsigned char key[DES_KEY_SIZE] ); + +/** + * \brief DES key schedule (56-bit, encryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + * + * \return 0 + */ +int des_setkey_enc( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ); + +/** + * \brief DES key schedule (56-bit, decryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + * + * \return 0 + */ +int des_setkey_dec( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ); + +/** + * \brief Triple-DES key schedule (112-bit, encryption) + * + * \param ctx 3DES context to be initialized + * \param key 16-byte secret key + * + * \return 0 + */ +int des3_set2key_enc( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 2] ); + +/** + * \brief Triple-DES key schedule (112-bit, decryption) + * + * \param ctx 3DES context to be initialized + * \param key 16-byte secret key + * + * \return 0 + */ +int des3_set2key_dec( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 2] ); + +/** + * \brief Triple-DES key schedule (168-bit, encryption) + * + * \param ctx 3DES context to be initialized + * \param key 24-byte secret key + * + * \return 0 + */ +int des3_set3key_enc( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 3] ); + +/** + * \brief Triple-DES key schedule (168-bit, decryption) + * + * \param ctx 3DES context to be initialized + * \param key 24-byte secret key + * + * \return 0 + */ +int des3_set3key_dec( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 3] ); + +/** + * \brief DES-ECB block encryption/decryption + * + * \param ctx DES context + * \param input 64-bit input block + * \param output 64-bit output block + * + * \return 0 if successful + */ +int des_crypt_ecb( des_context *ctx, + const unsigned char input[8], + unsigned char output[8] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief DES-CBC buffer encryption/decryption + * + * \param ctx DES context + * \param mode DES_ENCRYPT or DES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + */ +int des_crypt_cbc( des_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +/** + * \brief 3DES-ECB block encryption/decryption + * + * \param ctx 3DES context + * \param input 64-bit input block + * \param output 64-bit output block + * + * \return 0 if successful + */ +int des3_crypt_ecb( des3_context *ctx, + const unsigned char input[8], + unsigned char output[8] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief 3DES-CBC buffer encryption/decryption + * + * \param ctx 3DES context + * \param mode DES_ENCRYPT or DES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or POLARSSL_ERR_DES_INVALID_INPUT_LENGTH + */ +int des3_crypt_cbc( des3_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_DES_ALT */ +#include "des_alt.h" +#endif /* POLARSSL_DES_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int des_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* des.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/dhm.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/dhm.h new file mode 100644 index 0000000..064472f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/dhm.h @@ -0,0 +1,311 @@ +/** + * \file dhm.h + * + * \brief Diffie-Hellman-Merkle key exchange + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_DHM_H +#define POLARSSL_DHM_H + +#include "bignum.h" + +/* + * DHM Error codes + */ +#define POLARSSL_ERR_DHM_BAD_INPUT_DATA -0x3080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_DHM_READ_PARAMS_FAILED -0x3100 /**< Reading of the DHM parameters failed. */ +#define POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED -0x3180 /**< Making of the DHM parameters failed. */ +#define POLARSSL_ERR_DHM_READ_PUBLIC_FAILED -0x3200 /**< Reading of the public values failed. */ +#define POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED -0x3280 /**< Making of the public value failed. */ +#define POLARSSL_ERR_DHM_CALC_SECRET_FAILED -0x3300 /**< Calculation of the DHM secret failed. */ +#define POLARSSL_ERR_DHM_INVALID_FORMAT -0x3380 /**< The ASN.1 data is not formatted correctly. */ +#define POLARSSL_ERR_DHM_MALLOC_FAILED -0x3400 /**< Allocation of memory failed. */ +#define POLARSSL_ERR_DHM_FILE_IO_ERROR -0x3480 /**< Read/write of file failed. */ + +/** + * RFC 2409 defines a number of standardized Diffie-Hellman groups + * that can be used. + * RFC 3526 defines a number of standardized Diffie-Hellman groups + * for IKE. + * RFC 5114 defines a number of standardized Diffie-Hellman groups + * that can be used. + * + * Some are included here for convenience. + * + * Included are: + * RFC 2409 6.2. 1024-bit MODP Group (Second Oakley Group) + * RFC 3526 3. 2048-bit MODP Group + * RFC 3526 4. 3072-bit MODP Group + * RFC 5114 2.1. 1024-bit MODP Group with 160-bit Prime Order Subgroup + * RFC 5114 2.2. 2048-bit MODP Group with 224-bit Prime Order Subgroup + */ +#define POLARSSL_DHM_RFC2409_MODP_1024_P \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ + "FFFFFFFFFFFFFFFF" + +#define POLARSSL_DHM_RFC2409_MODP_1024_G "02" + +#define POLARSSL_DHM_RFC3526_MODP_2048_P \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ + "15728E5A8AACAA68FFFFFFFFFFFFFFFF" + +#define POLARSSL_DHM_RFC3526_MODP_2048_G "02" + +#define POLARSSL_DHM_RFC3526_MODP_3072_P \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" + +#define POLARSSL_DHM_RFC3526_MODP_3072_G "02" + +#define POLARSSL_DHM_RFC5114_MODP_1024_P \ + "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6" \ + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" \ + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70" \ + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" \ + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708" \ + "DF1FB2BC2E4A4371" + +#define POLARSSL_DHM_RFC5114_MODP_1024_G \ + "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F" \ + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" \ + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1" \ + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" \ + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24" \ + "855E6EEB22B3B2E5" + +#define POLARSSL_DHM_RFC5114_MODP_2048_P \ + "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" \ + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" \ + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212" \ + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" \ + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708" \ + "B3BF8A317091883681286130BC8985DB1602E714415D9330" \ + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D" \ + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" \ + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763" \ + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" \ + "CF9DE5384E71B81C0AC4DFFE0C10E64F" + +#define POLARSSL_DHM_RFC5114_MODP_2048_G \ + "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"\ + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA"\ + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"\ + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A"\ + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"\ + "F180EB34118E98D119529A45D6F834566E3025E316A330EF"\ + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"\ + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381"\ + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"\ + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179"\ + "81BC087F2A7065B384B890D3191F2BFA" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief DHM context structure + */ +typedef struct +{ + size_t len; /*!< size(P) in chars */ + mpi P; /*!< prime modulus */ + mpi G; /*!< generator */ + mpi X; /*!< secret value */ + mpi GX; /*!< self = G^X mod P */ + mpi GY; /*!< peer = G^Y mod P */ + mpi K; /*!< key = GY^X mod P */ + mpi RP; /*!< cached R^2 mod P */ + mpi Vi; /*!< blinding value */ + mpi Vf; /*!< un-blinding value */ + mpi pX; /*!< previous X */ +} +dhm_context; + +/** + * \brief Initialize DHM context + * + * \param ctx DHM context to be initialized + */ +void dhm_init( dhm_context *ctx ); + +/** + * \brief Parse the ServerKeyExchange parameters + * + * \param ctx DHM context + * \param p &(start of input buffer) + * \param end end of buffer + * + * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + */ +int dhm_read_params( dhm_context *ctx, + unsigned char **p, + const unsigned char *end ); + +/** + * \brief Setup and write the ServerKeyExchange parameters + * + * \param ctx DHM context + * \param x_size private value size in bytes + * \param output destination buffer + * \param olen number of chars written + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note This function assumes that ctx->P and ctx->G + * have already been properly set (for example + * using mpi_read_string or mpi_read_binary). + * + * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + */ +int dhm_make_params( dhm_context *ctx, int x_size, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Import the peer's public value G^Y + * + * \param ctx DHM context + * \param input input buffer + * \param ilen size of buffer + * + * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + */ +int dhm_read_public( dhm_context *ctx, + const unsigned char *input, size_t ilen ); + +/** + * \brief Create own private value X and export G^X + * + * \param ctx DHM context + * \param x_size private value size in bytes + * \param output destination buffer + * \param olen must be equal to ctx->P.len + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + */ +int dhm_make_public( dhm_context *ctx, int x_size, + unsigned char *output, size_t olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Derive and export the shared secret (G^Y)^X mod P + * + * \param ctx DHM context + * \param output destination buffer + * \param olen on entry, must hold the size of the destination buffer + * on exit, holds the actual number of bytes written + * \param f_rng RNG function, for blinding purposes + * \param p_rng RNG parameter + * + * \return 0 if successful, or an POLARSSL_ERR_DHM_XXX error code + * + * \note If non-NULL, f_rng is used to blind the input as + * countermeasure against timing attacks. Blinding is + * automatically used if and only if our secret value X is + * re-used and costs nothing otherwise, so it is recommended + * to always pass a non-NULL f_rng argument. + */ +int dhm_calc_secret( dhm_context *ctx, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Free and clear the components of a DHM key + * + * \param ctx DHM context to free and clear + */ +void dhm_free( dhm_context *ctx ); + +#if defined(POLARSSL_ASN1_PARSE_C) +/** \ingroup x509_module */ +/** + * \brief Parse DHM parameters + * + * \param dhm DHM context to be initialized + * \param dhmin input buffer + * \param dhminlen size of the buffer + * + * \return 0 if successful, or a specific DHM or PEM error code + */ +int dhm_parse_dhm( dhm_context *dhm, const unsigned char *dhmin, + size_t dhminlen ); + +#if defined(POLARSSL_FS_IO) +/** \ingroup x509_module */ +/** + * \brief Load and parse DHM parameters + * + * \param dhm DHM context to be initialized + * \param path filename to read the DHM Parameters from + * + * \return 0 if successful, or a specific DHM or PEM error code + */ +int dhm_parse_dhmfile( dhm_context *dhm, const char *path ); +#endif /* POLARSSL_FS_IO */ +#endif /* POLARSSL_ASN1_PARSE_C */ + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int dhm_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* dhm.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdh.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdh.h new file mode 100644 index 0000000..525cade --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdh.h @@ -0,0 +1,225 @@ +/** + * \file ecdh.h + * + * \brief Elliptic curve Diffie-Hellman + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ECDH_H +#define POLARSSL_ECDH_H + +#include "ecp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When importing from an EC key, select if it is our key or the peer's key + */ +typedef enum +{ + POLARSSL_ECDH_OURS, + POLARSSL_ECDH_THEIRS, +} ecdh_side; + +/** + * \brief ECDH context structure + */ +typedef struct +{ + ecp_group grp; /*!< elliptic curve used */ + mpi d; /*!< our secret value (private key) */ + ecp_point Q; /*!< our public value (public key) */ + ecp_point Qp; /*!< peer's public value (public key) */ + mpi z; /*!< shared secret */ + int point_format; /*!< format for point export in TLS messages */ + ecp_point Vi; /*!< blinding value (for later) */ + ecp_point Vf; /*!< un-blinding value (for later) */ + mpi _d; /*!< previous d (for later) */ +} +ecdh_context; + +/** + * \brief Generate a public key. + * Raw function that only does the core computation. + * + * \param grp ECP group + * \param d Destination MPI (secret exponent, aka private key) + * \param Q Destination point (public key) + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + */ +int ecdh_gen_public( ecp_group *grp, mpi *d, ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Compute shared secret + * Raw function that only does the core computation. + * + * \param grp ECP group + * \param z Destination MPI (shared secret) + * \param Q Public key from other party + * \param d Our secret exponent (private key) + * \param f_rng RNG function (see notes) + * \param p_rng RNG parameter + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + * + * \note If f_rng is not NULL, it is used to implement + * countermeasures against potential elaborate timing + * attacks, see \c ecp_mul() for details. + */ +int ecdh_compute_shared( ecp_group *grp, mpi *z, + const ecp_point *Q, const mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Initialize context + * + * \param ctx Context to initialize + */ +void ecdh_init( ecdh_context *ctx ); + +/** + * \brief Free context + * + * \param ctx Context to free + */ +void ecdh_free( ecdh_context *ctx ); + +/** + * \brief Generate a public key and a TLS ServerKeyExchange payload. + * (First function used by a TLS server for ECDHE.) + * + * \param ctx ECDH context + * \param olen number of chars written + * \param buf destination buffer + * \param blen length of buffer + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note This function assumes that ctx->grp has already been + * properly set (for example using ecp_use_known_dp). + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_make_params( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Parse and procress a TLS ServerKeyExhange payload. + * (First function used by a TLS client for ECDHE.) + * + * \param ctx ECDH context + * \param buf pointer to start of input buffer + * \param end one past end of buffer + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_read_params( ecdh_context *ctx, + const unsigned char **buf, const unsigned char *end ); + +/** + * \brief Setup an ECDH context from an EC key. + * (Used by clients and servers in place of the + * ServerKeyEchange for static ECDH: import ECDH parameters + * from a certificate's EC key information.) + * + * \param ctx ECDH constext to set + * \param key EC key to use + * \param side Is it our key (1) or the peer's key (0) ? + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_get_params( ecdh_context *ctx, const ecp_keypair *key, + ecdh_side side ); + +/** + * \brief Generate a public key and a TLS ClientKeyExchange payload. + * (Second function used by a TLS client for ECDH(E).) + * + * \param ctx ECDH context + * \param olen number of bytes actually written + * \param buf destination buffer + * \param blen size of destination buffer + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_make_public( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Parse and process a TLS ClientKeyExchange payload. + * (Second function used by a TLS server for ECDH(E).) + * + * \param ctx ECDH context + * \param buf start of input buffer + * \param blen length of input buffer + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_read_public( ecdh_context *ctx, + const unsigned char *buf, size_t blen ); + +/** + * \brief Derive and export the shared secret. + * (Last function used by both TLS client en servers.) + * + * \param ctx ECDH context + * \param olen number of bytes written + * \param buf destination buffer + * \param blen buffer length + * \param f_rng RNG function, see notes for \c ecdh_compute_shared() + * \param p_rng RNG parameter + * + * \return 0 if successful, or an POLARSSL_ERR_ECP_XXX error code + */ +int ecdh_calc_secret( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int ecdh_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* ecdh.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdsa.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdsa.h new file mode 100644 index 0000000..d99a17a --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecdsa.h @@ -0,0 +1,236 @@ +/** + * \file ecdsa.h + * + * \brief Elliptic curve DSA + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ECDSA_H +#define POLARSSL_ECDSA_H + +#include "ecp.h" + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +#include "md.h" +#endif + +/** + * \brief ECDSA context structure + * + * \note Purposefully begins with the same members as struct ecp_keypair. + */ +typedef struct +{ + ecp_group grp; /*!< elliptic curve used */ + mpi d; /*!< secret signature key */ + ecp_point Q; /*!< public signature key */ + mpi r; /*!< first integer from signature */ + mpi s; /*!< second integer from signature */ +} +ecdsa_context; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Compute ECDSA signature of a previously hashed message + * + * \param grp ECP group + * \param r First output integer + * \param s Second output integer + * \param d Private signing key + * \param buf Message hash + * \param blen Length of buf + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + */ +int ecdsa_sign( ecp_group *grp, mpi *r, mpi *s, + const mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +/** + * \brief Compute ECDSA signature of a previously hashed message + * (deterministic version) + * + * \param grp ECP group + * \param r First output integer + * \param s Second output integer + * \param d Private signing key + * \param buf Message hash + * \param blen Length of buf + * \param md_alg MD algorithm used to hash the message + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + */ +int ecdsa_sign_det( ecp_group *grp, mpi *r, mpi *s, + const mpi *d, const unsigned char *buf, size_t blen, + md_type_t md_alg ); +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ + +/** + * \brief Verify ECDSA signature of a previously hashed message + * + * \param grp ECP group + * \param buf Message hash + * \param blen Length of buf + * \param Q Public key to use for verification + * \param r First integer of the signature + * \param s Second integer of the signature + * + * \return 0 if successful, + * POLARSSL_ERR_ECP_BAD_INPUT_DATA if signature is invalid + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + */ +int ecdsa_verify( ecp_group *grp, + const unsigned char *buf, size_t blen, + const ecp_point *Q, const mpi *r, const mpi *s); + +/** + * \brief Compute ECDSA signature and write it to buffer, + * serialized as defined in RFC 4492 page 20. + * (Not thread-safe to use same context in multiple threads) + * + * \param ctx ECDSA context + * \param hash Message hash + * \param hlen Length of hash + * \param sig Buffer that will hold the signature + * \param slen Length of the signature written + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note The "sig" buffer must be at least as large as twice the + * size of the curve used, plus 7 (eg. 71 bytes if a 256-bit + * curve is used). + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP, POLARSSL_ERR_MPI or + * POLARSSL_ERR_ASN1 error code + */ +int ecdsa_write_signature( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +/** + * \brief Compute ECDSA signature and write it to buffer, + * serialized as defined in RFC 4492 page 20. + * Deterministic version, RFC 6979. + * (Not thread-safe to use same context in multiple threads) + * + * \param ctx ECDSA context + * \param hash Message hash + * \param hlen Length of hash + * \param sig Buffer that will hold the signature + * \param slen Length of the signature written + * \param md_alg MD algorithm used to hash the message + * + * \note The "sig" buffer must be at least as large as twice the + * size of the curve used, plus 7 (eg. 71 bytes if a 256-bit + * curve is used). + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP, POLARSSL_ERR_MPI or + * POLARSSL_ERR_ASN1 error code + */ +int ecdsa_write_signature_det( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + md_type_t md_alg ); +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ + +/** + * \brief Read and verify an ECDSA signature + * + * \param ctx ECDSA context + * \param hash Message hash + * \param hlen Size of hash + * \param sig Signature to read and verify + * \param slen Size of sig + * + * \return 0 if successful, + * POLARSSL_ERR_ECP_BAD_INPUT_DATA if signature is invalid, + * POLARSSL_ERR_ECP_SIG_LEN_MISTMATCH if the signature is + * valid but its actual length is less than siglen, + * or a POLARSSL_ERR_ECP or POLARSSL_ERR_MPI error code + */ +int ecdsa_read_signature( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen ); + +/** + * \brief Generate an ECDSA keypair on the given curve + * + * \param ctx ECDSA context in which the keypair should be stored + * \param gid Group (elliptic curve) to use. One of the various + * POLARSSL_ECP_DP_XXX macros depending on configuration. + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 on success, or a POLARSSL_ERR_ECP code. + */ +int ecdsa_genkey( ecdsa_context *ctx, ecp_group_id gid, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Set an ECDSA context from an EC key pair + * + * \param ctx ECDSA context to set + * \param key EC key to use + * + * \return 0 on success, or a POLARSSL_ERR_ECP code. + */ +int ecdsa_from_keypair( ecdsa_context *ctx, const ecp_keypair *key ); + +/** + * \brief Initialize context + * + * \param ctx Context to initialize + */ +void ecdsa_init( ecdsa_context *ctx ); + +/** + * \brief Free context + * + * \param ctx Context to free + */ +void ecdsa_free( ecdsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int ecdsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* ecdsa.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecp.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecp.h new file mode 100644 index 0000000..7192f1e --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ecp.h @@ -0,0 +1,651 @@ +/** + * \file ecp.h + * + * \brief Elliptic curves over GF(p) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ECP_H +#define POLARSSL_ECP_H + +#include "bignum.h" + +/* + * ECP error codes + */ +#define POLARSSL_ERR_ECP_BAD_INPUT_DATA -0x4F80 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_ECP_BUFFER_TOO_SMALL -0x4F00 /**< The buffer is too small to write to. */ +#define POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE -0x4E80 /**< Requested curve not available. */ +#define POLARSSL_ERR_ECP_VERIFY_FAILED -0x4E00 /**< The signature is not valid. */ +#define POLARSSL_ERR_ECP_MALLOC_FAILED -0x4D80 /**< Memory allocation failed. */ +#define POLARSSL_ERR_ECP_RANDOM_FAILED -0x4D00 /**< Generation of random value, such as (ephemeral) key, failed. */ +#define POLARSSL_ERR_ECP_INVALID_KEY -0x4C80 /**< Invalid private or public key. */ +#define POLARSSL_ERR_ECP_SIG_LEN_MISMATCH -0x4C00 /**< Signature is valid but shorter than the user-supplied length. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Domain parameters (curve, subgroup and generator) identifiers. + * + * Only curves over prime fields are supported. + * + * \warning This library does not support validation of arbitrary domain + * parameters. Therefore, only well-known domain parameters from trusted + * sources should be used. See ecp_use_known_dp(). + */ +typedef enum +{ + POLARSSL_ECP_DP_NONE = 0, + POLARSSL_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */ + POLARSSL_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */ + POLARSSL_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */ + POLARSSL_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */ + POLARSSL_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */ + POLARSSL_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */ + POLARSSL_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */ + POLARSSL_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */ + POLARSSL_ECP_DP_M221, /*!< (not implemented yet) */ + POLARSSL_ECP_DP_M255, /*!< Curve25519 */ + POLARSSL_ECP_DP_M383, /*!< (not implemented yet) */ + POLARSSL_ECP_DP_M511, /*!< (not implemented yet) */ + POLARSSL_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */ + POLARSSL_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */ + POLARSSL_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */ +} ecp_group_id; + +/** + * Number of supported curves (plus one for NONE). + * + * (Montgomery curves excluded for now.) + */ +#define POLARSSL_ECP_DP_MAX 12 + +/** + * Curve information for use by other modules + */ +typedef struct +{ + ecp_group_id grp_id; /*!< Internal identifier */ + uint16_t tls_id; /*!< TLS NamedCurve identifier */ + uint16_t size; /*!< Curve size in bits */ + const char *name; /*!< Human-friendly name */ +} ecp_curve_info; + +/** + * \brief ECP point structure (jacobian coordinates) + * + * \note All functions expect and return points satisfying + * the following condition: Z == 0 or Z == 1. (Other + * values of Z are used by internal functions only.) + * The point is zero, or "at infinity", if Z == 0. + * Otherwise, X and Y are its standard (affine) coordinates. + */ +typedef struct +{ + mpi X; /*!< the point's X coordinate */ + mpi Y; /*!< the point's Y coordinate */ + mpi Z; /*!< the point's Z coordinate */ +} +ecp_point; + +/** + * \brief ECP group structure + * + * We consider two types of curves equations: + * 1. Short Weierstrass y^2 = x^3 + A x + B mod P (SEC1 + RFC 4492) + * 2. Montgomery, y^2 = x^3 + A x^2 + x mod P (M255 + draft) + * In both cases, a generator G for a prime-order subgroup is fixed. In the + * short weierstrass, this subgroup is actually the whole curve, and its + * cardinal is denoted by N. + * + * In the case of Short Weierstrass curves, our code requires that N is an odd + * prime. (Use odd in ecp_mul() and prime in ecdsa_sign() for blinding.) + * + * In the case of Montgomery curves, we don't store A but (A + 2) / 4 which is + * the quantity actually used in the formulas. Also, nbits is not the size of N + * but the required size for private keys. + * + * If modp is NULL, reduction modulo P is done using a generic algorithm. + * Otherwise, it must point to a function that takes an mpi in the range + * 0..2^(2*pbits)-1 and transforms it in-place in an integer of little more + * than pbits, so that the integer may be efficiently brought in the 0..P-1 + * range by a few additions or substractions. It must return 0 on success and + * non-zero on failure. + */ +typedef struct +{ + ecp_group_id id; /*!< internal group identifier */ + mpi P; /*!< prime modulus of the base field */ + mpi A; /*!< 1. A in the equation, or 2. (A + 2) / 4 */ + mpi B; /*!< 1. B in the equation, or 2. unused */ + ecp_point G; /*!< generator of the (sub)group used */ + mpi N; /*!< 1. the order of G, or 2. unused */ + size_t pbits; /*!< number of bits in P */ + size_t nbits; /*!< number of bits in 1. P, or 2. private keys */ + unsigned int h; /*!< internal: 1 if the constants are static */ + int (*modp)(mpi *); /*!< function for fast reduction mod P */ + int (*t_pre)(ecp_point *, void *); /*!< unused */ + int (*t_post)(ecp_point *, void *); /*!< unused */ + void *t_data; /*!< unused */ + ecp_point *T; /*!< pre-computed points for ecp_mul_comb() */ + size_t T_size; /*!< number for pre-computed points */ +} +ecp_group; + +/** + * \brief ECP key pair structure + * + * A generic key pair that could be used for ECDSA, fixed ECDH, etc. + * + * \note Members purposefully in the same order as struc ecdsa_context. + */ +typedef struct +{ + ecp_group grp; /*!< Elliptic curve and base point */ + mpi d; /*!< our secret value */ + ecp_point Q; /*!< our public value */ +} +ecp_keypair; + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(POLARSSL_ECP_MAX_BITS) +/** + * Maximum size of the groups (that is, of N and P) + */ +#define POLARSSL_ECP_MAX_BITS 521 /**< Maximum bit size of groups */ +#endif + +#define POLARSSL_ECP_MAX_BYTES ( ( POLARSSL_ECP_MAX_BITS + 7 ) / 8 ) +#define POLARSSL_ECP_MAX_PT_LEN ( 2 * POLARSSL_ECP_MAX_BYTES + 1 ) + +#if !defined(POLARSSL_ECP_WINDOW_SIZE) +/* + * Maximum "window" size used for point multiplication. + * Default: 6. + * Minimum value: 2. Maximum value: 7. + * + * Result is an array of at most ( 1 << ( POLARSSL_ECP_WINDOW_SIZE - 1 ) ) + * points used for point multiplication. This value is directly tied to EC + * peak memory usage, so decreasing it by one should roughly cut memory usage + * by two (if large curves are in use). + * + * Reduction in size may reduce speed, but larger curves are impacted first. + * Sample performances (in ECDHE handshakes/s, with FIXED_POINT_OPTIM = 1): + * w-size: 6 5 4 3 2 + * 521 145 141 135 120 97 + * 384 214 209 198 177 146 + * 256 320 320 303 262 226 + + * 224 475 475 453 398 342 + * 192 640 640 633 587 476 + */ +#define POLARSSL_ECP_WINDOW_SIZE 6 /**< Maximum window size used */ +#endif /* POLARSSL_ECP_WINDOW_SIZE */ + +#if !defined(POLARSSL_ECP_FIXED_POINT_OPTIM) +/* + * Trade memory for speed on fixed-point multiplication. + * + * This speeds up repeated multiplication of the generator (that is, the + * multiplication in ECDSA signatures, and half of the multiplications in + * ECDSA verification and ECDHE) by a factor roughly 3 to 4. + * + * The cost is increasing EC peak memory usage by a factor roughly 2. + * + * Change this value to 0 to reduce peak memory usage. + */ +#define POLARSSL_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ +#endif /* POLARSSL_ECP_FIXED_POINT_OPTIM */ + +/* \} name SECTION: Module settings */ + +/* + * Point formats, from RFC 4492's enum ECPointFormat + */ +#define POLARSSL_ECP_PF_UNCOMPRESSED 0 /**< Uncompressed point format */ +#define POLARSSL_ECP_PF_COMPRESSED 1 /**< Compressed point format */ + +/* + * Some other constants from RFC 4492 + */ +#define POLARSSL_ECP_TLS_NAMED_CURVE 3 /**< ECCurveType's named_curve */ + +/** + * \brief Get the list of supported curves in order of preferrence + * (full information) + * + * \return A statically allocated array, the last entry is 0. + */ +const ecp_curve_info *ecp_curve_list( void ); + +/** + * \brief Get the list of supported curves in order of preferrence + * (grp_id only) + * + * \return A statically allocated array, + * terminated with POLARSSL_ECP_DP_NONE. + */ +const ecp_group_id *ecp_grp_id_list( void ); + +/** + * \brief Get curve information from an internal group identifier + * + * \param grp_id A POLARSSL_ECP_DP_XXX value + * + * \return The associated curve information or NULL + */ +const ecp_curve_info *ecp_curve_info_from_grp_id( ecp_group_id grp_id ); + +/** + * \brief Get curve information from a TLS NamedCurve value + * + * \param tls_id A POLARSSL_ECP_DP_XXX value + * + * \return The associated curve information or NULL + */ +const ecp_curve_info *ecp_curve_info_from_tls_id( uint16_t tls_id ); + +/** + * \brief Get curve information from a human-readable name + * + * \param name The name + * + * \return The associated curve information or NULL + */ +const ecp_curve_info *ecp_curve_info_from_name( const char *name ); + +/** + * \brief Initialize a point (as zero) + */ +void ecp_point_init( ecp_point *pt ); + +/** + * \brief Initialize a group (to something meaningless) + */ +void ecp_group_init( ecp_group *grp ); + +/** + * \brief Initialize a key pair (as an invalid one) + */ +void ecp_keypair_init( ecp_keypair *key ); + +/** + * \brief Free the components of a point + */ +void ecp_point_free( ecp_point *pt ); + +/** + * \brief Free the components of an ECP group + */ +void ecp_group_free( ecp_group *grp ); + +/** + * \brief Free the components of a key pair + */ +void ecp_keypair_free( ecp_keypair *key ); + +/** + * \brief Copy the contents of point Q into P + * + * \param P Destination point + * \param Q Source point + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int ecp_copy( ecp_point *P, const ecp_point *Q ); + +/** + * \brief Copy the contents of a group object + * + * \param dst Destination group + * \param src Source group + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int ecp_group_copy( ecp_group *dst, const ecp_group *src ); + +/** + * \brief Set a point to zero + * + * \param pt Destination point + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + */ +int ecp_set_zero( ecp_point *pt ); + +/** + * \brief Tell if a point is zero + * + * \param pt Point to test + * + * \return 1 if point is zero, 0 otherwise + */ +int ecp_is_zero( ecp_point *pt ); + +/** + * \brief Import a non-zero point from two ASCII strings + * + * \param P Destination point + * \param radix Input numeric base + * \param x First affine coordinate as a null-terminated string + * \param y Second affine coordinate as a null-terminated string + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + */ +int ecp_point_read_string( ecp_point *P, int radix, + const char *x, const char *y ); + +/** + * \brief Export a point into unsigned binary data + * + * \param grp Group to which the point should belong + * \param P Point to export + * \param format Point format, should be a POLARSSL_ECP_PF_XXX macro + * \param olen Length of the actual output + * \param buf Output buffer + * \param buflen Length of the output buffer + * + * \return 0 if successful, + * or POLARSSL_ERR_ECP_BAD_INPUT_DATA + * or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL + */ +int ecp_point_write_binary( const ecp_group *grp, const ecp_point *P, + int format, size_t *olen, + unsigned char *buf, size_t buflen ); + +/** + * \brief Import a point from unsigned binary data + * + * \param grp Group to which the point should belong + * \param P Point to import + * \param buf Input buffer + * \param ilen Actual length of input + * + * \return 0 if successful, + * POLARSSL_ERR_ECP_BAD_INPUT_DATA if input is invalid, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE if the point format + * is not implemented. + * + * \note This function does NOT check that the point actually + * belongs to the given group, see ecp_check_pubkey() for + * that. + */ +int ecp_point_read_binary( const ecp_group *grp, ecp_point *P, + const unsigned char *buf, size_t ilen ); + +/** + * \brief Import a point from a TLS ECPoint record + * + * \param grp ECP group used + * \param pt Destination point + * \param buf $(Start of input buffer) + * \param len Buffer length + * + * \return O if successful, + * POLARSSL_ERR_MPI_XXX if initialization failed + * POLARSSL_ERR_ECP_BAD_INPUT_DATA if input is invalid + */ +int ecp_tls_read_point( const ecp_group *grp, ecp_point *pt, + const unsigned char **buf, size_t len ); + +/** + * \brief Export a point as a TLS ECPoint record + * + * \param grp ECP group used + * \param pt Point to export + * \param format Export format + * \param olen length of data written + * \param buf Buffer to write to + * \param blen Buffer length + * + * \return 0 if successful, + * or POLARSSL_ERR_ECP_BAD_INPUT_DATA + * or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL + */ +int ecp_tls_write_point( const ecp_group *grp, const ecp_point *pt, + int format, size_t *olen, + unsigned char *buf, size_t blen ); + +/** + * \brief Import an ECP group from null-terminated ASCII strings + * + * \param grp Destination group + * \param radix Input numeric base + * \param p Prime modulus of the base field + * \param b Constant term in the equation + * \param gx The generator's X coordinate + * \param gy The generator's Y coordinate + * \param n The generator's order + * + * \return 0 if successful, or a POLARSSL_ERR_MPI_XXX error code + * + * \note Sets all fields except modp. + */ +int ecp_group_read_string( ecp_group *grp, int radix, + const char *p, const char *b, + const char *gx, const char *gy, const char *n); + +/** + * \brief Set a group using well-known domain parameters + * + * \param grp Destination group + * \param index Index in the list of well-known domain parameters + * + * \return O if successful, + * POLARSSL_ERR_MPI_XXX if initialization failed + * POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE for unkownn groups + * + * \note Index should be a value of RFC 4492's enum NamdeCurve, + * possibly in the form of a POLARSSL_ECP_DP_XXX macro. + */ +int ecp_use_known_dp( ecp_group *grp, ecp_group_id index ); + +/** + * \brief Set a group from a TLS ECParameters record + * + * \param grp Destination group + * \param buf &(Start of input buffer) + * \param len Buffer length + * + * \return O if successful, + * POLARSSL_ERR_MPI_XXX if initialization failed + * POLARSSL_ERR_ECP_BAD_INPUT_DATA if input is invalid + */ +int ecp_tls_read_group( ecp_group *grp, const unsigned char **buf, size_t len ); + +/** + * \brief Write the TLS ECParameters record for a group + * + * \param grp ECP group used + * \param olen Number of bytes actually written + * \param buf Buffer to write to + * \param blen Buffer length + * + * \return 0 if successful, + * or POLARSSL_ERR_ECP_BUFFER_TOO_SMALL + */ +int ecp_tls_write_group( const ecp_group *grp, size_t *olen, + unsigned char *buf, size_t blen ); + +/** + * \brief Addition: R = P + Q + * + * \param grp ECP group + * \param R Destination point + * \param P Left-hand point + * \param Q Right-hand point + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + * + * \note This function does not support Montgomery curves, such as + * Curve25519. + */ +int ecp_add( const ecp_group *grp, ecp_point *R, + const ecp_point *P, const ecp_point *Q ); + +/** + * \brief Subtraction: R = P - Q + * + * \param grp ECP group + * \param R Destination point + * \param P Left-hand point + * \param Q Right-hand point + * + * \return 0 if successful, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + * + * \note This function does not support Montgomery curves, such as + * Curve25519. + */ +int ecp_sub( const ecp_group *grp, ecp_point *R, + const ecp_point *P, const ecp_point *Q ); + +/** + * \brief Multiplication by an integer: R = m * P + * (Not thread-safe to use same group in multiple threads) + * + * \param grp ECP group + * \param R Destination point + * \param m Integer by which to multiply + * \param P Point to multiply + * \param f_rng RNG function (see notes) + * \param p_rng RNG parameter + * + * \return 0 if successful, + * POLARSSL_ERR_ECP_INVALID_KEY if m is not a valid privkey + * or P is not a valid pubkey, + * POLARSSL_ERR_MPI_MALLOC_FAILED if memory allocation failed + * + * \note In order to prevent timing attacks, this function + * executes the exact same sequence of (base field) + * operations for any valid m. It avoids any if-branch or + * array index depending on the value of m. + * + * \note If f_rng is not NULL, it is used to randomize intermediate + * results in order to prevent potential timing attacks + * targeting these results. It is recommended to always + * provide a non-NULL f_rng (the overhead is negligible). + */ +int ecp_mul( ecp_group *grp, ecp_point *R, + const mpi *m, const ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Check that a point is a valid public key on this curve + * + * \param grp Curve/group the point should belong to + * \param pt Point to check + * + * \return 0 if point is a valid public key, + * POLARSSL_ERR_ECP_INVALID_KEY otherwise. + * + * \note This function only checks the point is non-zero, has valid + * coordinates and lies on the curve, but not that it is + * indeed a multiple of G. This is additional check is more + * expensive, isn't required by standards, and shouldn't be + * necessary if the group used has a small cofactor. In + * particular, it is useless for the NIST groups which all + * have a cofactor of 1. + * + * \note Uses bare components rather than an ecp_keypair structure + * in order to ease use with other structures such as + * ecdh_context of ecdsa_context. + */ +int ecp_check_pubkey( const ecp_group *grp, const ecp_point *pt ); + +/** + * \brief Check that an mpi is a valid private key for this curve + * + * \param grp Group used + * \param d Integer to check + * + * \return 0 if point is a valid private key, + * POLARSSL_ERR_ECP_INVALID_KEY otherwise. + * + * \note Uses bare components rather than an ecp_keypair structure + * in order to ease use with other structures such as + * ecdh_context of ecdsa_context. + */ +int ecp_check_privkey( const ecp_group *grp, const mpi *d ); + +/** + * \brief Generate a keypair + * + * \param grp ECP group + * \param d Destination MPI (secret part) + * \param Q Destination point (public part) + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + * + * \note Uses bare components rather than an ecp_keypair structure + * in order to ease use with other structures such as + * ecdh_context of ecdsa_context. + */ +int ecp_gen_keypair( ecp_group *grp, mpi *d, ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Generate a keypair + * + * \param grp_id ECP group identifier + * \param key Destination keypair + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 if successful, + * or a POLARSSL_ERR_ECP_XXX or POLARSSL_MPI_XXX error code + */ +int ecp_gen_key( ecp_group_id grp_id, ecp_keypair *key, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +#if defined(POLARSSL_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if a test failed + */ +int ecp_self_test( int verbose ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ecp.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy.h new file mode 100644 index 0000000..f5fa928 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy.h @@ -0,0 +1,246 @@ +/** + * \file entropy.h + * + * \brief Entropy accumulator implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ENTROPY_H +#define POLARSSL_ENTROPY_H + +#include + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SHA512_C) && !defined(POLARSSL_ENTROPY_FORCE_SHA256) +#include "sha512.h" +#define POLARSSL_ENTROPY_SHA512_ACCUMULATOR +#else +#if defined(POLARSSL_SHA256_C) +#define POLARSSL_ENTROPY_SHA256_ACCUMULATOR +#include "sha256.h" +#endif +#endif + +#if defined(POLARSSL_THREADING_C) +#include "threading.h" +#endif + +#if defined(POLARSSL_HAVEGE_C) +#include "havege.h" +#endif + +#define POLARSSL_ERR_ENTROPY_SOURCE_FAILED -0x003C /**< Critical entropy source failure. */ +#define POLARSSL_ERR_ENTROPY_MAX_SOURCES -0x003E /**< No more sources can be added. */ +#define POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED -0x0040 /**< No sources have been added to poll. */ +#define POLARSSL_ERR_ENTROPY_FILE_IO_ERROR -0x0058 /**< Read/write error in file. */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(ENTROPY_MAX_SOURCES) +#define ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +#endif + +#if !defined(ENTROPY_MAX_GATHER) +#define ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ +#endif + +/* \} name SECTION: Module settings */ + +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) +#define ENTROPY_BLOCK_SIZE 64 /**< Block size of entropy accumulator (SHA-512) */ +#else +#define ENTROPY_BLOCK_SIZE 32 /**< Block size of entropy accumulator (SHA-256) */ +#endif + +#define ENTROPY_MAX_SEED_SIZE 1024 /**< Maximum size of seed we read from seed file */ +#define ENTROPY_SOURCE_MANUAL ENTROPY_MAX_SOURCES + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Entropy poll callback pointer + * + * \param data Callback-specific data pointer + * \param output Data to fill + * \param len Maximum size to provide + * \param olen The actual amount of bytes put into the buffer (Can be 0) + * + * \return 0 if no critical failures occurred, + * POLARSSL_ERR_ENTROPY_SOURCE_FAILED otherwise + */ +typedef int (*f_source_ptr)(void *data, unsigned char *output, size_t len, + size_t *olen); + +/** + * \brief Entropy source state + */ +typedef struct +{ + f_source_ptr f_source; /**< The entropy source callback */ + void * p_source; /**< The callback data pointer */ + size_t size; /**< Amount received */ + size_t threshold; /**< Minimum level required before release */ +} +source_state; + +/** + * \brief Entropy context structure + */ +typedef struct +{ +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) + sha512_context accumulator; +#else + sha256_context accumulator; +#endif + int source_count; + source_state source[ENTROPY_MAX_SOURCES]; +#if defined(POLARSSL_HAVEGE_C) + havege_state havege_data; +#endif +#if defined(POLARSSL_THREADING_C) + threading_mutex_t mutex; /*!< mutex */ +#endif +} +entropy_context; + +/** + * \brief Initialize the context + * + * \param ctx Entropy context to initialize + */ +void entropy_init( entropy_context *ctx ); + +/** + * \brief Free the data in the context + * + * \param ctx Entropy context to free + */ +void entropy_free( entropy_context *ctx ); + +/** + * \brief Adds an entropy source to poll + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param ctx Entropy context + * \param f_source Entropy function + * \param p_source Function data + * \param threshold Minimum required from source before entropy is released + * ( with entropy_func() ) + * + * \return 0 if successful or POLARSSL_ERR_ENTROPY_MAX_SOURCES + */ +int entropy_add_source( entropy_context *ctx, + f_source_ptr f_source, void *p_source, + size_t threshold ); + +/** + * \brief Trigger an extra gather poll for the accumulator + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param ctx Entropy context + * + * \return 0 if successful, or POLARSSL_ERR_ENTROPY_SOURCE_FAILED + */ +int entropy_gather( entropy_context *ctx ); + +/** + * \brief Retrieve entropy from the accumulator + * (Maximum length: ENTROPY_BLOCK_SIZE) + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param data Entropy context + * \param output Buffer to fill + * \param len Number of bytes desired, must be at most ENTROPY_BLOCK_SIZE + * + * \return 0 if successful, or POLARSSL_ERR_ENTROPY_SOURCE_FAILED + */ +int entropy_func( void *data, unsigned char *output, size_t len ); + +/** + * \brief Add data to the accumulator manually + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param ctx Entropy context + * \param data Data to add + * \param len Length of data + * + * \return 0 if successful + */ +int entropy_update_manual( entropy_context *ctx, + const unsigned char *data, size_t len ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Write a seed file + * + * \param ctx Entropy context + * \param path Name of the file + * + * \return 0 if successful, + * POLARSSL_ERR_ENTROPY_FILE_IO_ERROR on file error, or + * POLARSSL_ERR_ENTROPY_SOURCE_FAILED + */ +int entropy_write_seed_file( entropy_context *ctx, const char *path ); + +/** + * \brief Read and update a seed file. Seed is added to this + * instance. No more than ENTROPY_MAX_SEED_SIZE bytes are + * read from the seed file. The rest is ignored. + * + * \param ctx Entropy context + * \param path Name of the file + * + * \return 0 if successful, + * POLARSSL_ERR_ENTROPY_FILE_IO_ERROR on file error, + * POLARSSL_ERR_ENTROPY_SOURCE_FAILED + */ +int entropy_update_seed_file( entropy_context *ctx, const char *path ); +#endif /* POLARSSL_FS_IO */ + +#if defined(POLARSSL_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if a test failed + */ +int entropy_self_test( int verbose ); +#endif /* POLARSSL_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* entropy.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy_poll.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy_poll.h new file mode 100644 index 0000000..92efa00 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/entropy_poll.h @@ -0,0 +1,79 @@ +/** + * \file entropy_poll.h + * + * \brief Platform-specific and custom entropy polling functions + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ENTROPY_POLL_H +#define POLARSSL_ENTROPY_POLL_H + +#include + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default thresholds for built-in sources + */ +#define ENTROPY_MIN_PLATFORM 128 /**< Minimum for platform source */ +#define ENTROPY_MIN_HAVEGE 128 /**< Minimum for HAVEGE */ +#define ENTROPY_MIN_HARDCLOCK 32 /**< Minimum for hardclock() */ + +#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) +/** + * \brief Platform-specific entropy poll callback + */ +int platform_entropy_poll( void *data, + unsigned char *output, size_t len, size_t *olen ); +#endif + +#if defined(POLARSSL_HAVEGE_C) +/** + * \brief HAVEGE based entropy poll callback + * + * Requires an HAVEGE state as its data pointer. + */ +int havege_poll( void *data, + unsigned char *output, size_t len, size_t *olen ); +#endif + +#if defined(POLARSSL_TIMING_C) +/** + * \brief hardclock-based entropy poll callback + */ +int hardclock_poll( void *data, + unsigned char *output, size_t len, size_t *olen ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* entropy_poll.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/error.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/error.h new file mode 100644 index 0000000..cdee952 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/error.h @@ -0,0 +1,123 @@ +/** + * \file error.h + * + * \brief Error to string translation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_ERROR_H +#define POLARSSL_ERROR_H + +#include + +/** + * Error code layout. + * + * Currently we try to keep all error codes within the negative space of 16 + * bytes signed integers to support all platforms (-0x0000 - -0x8000). In + * addition we'd like to give two layers of information on the error if + * possible. + * + * For that purpose the error codes are segmented in the following manner: + * + * 16 bit error code bit-segmentation + * + * 1 bit - Sign bit + * 3 bits - High level module ID + * 5 bits - Module-dependent error code + * 7 bits - Low level module errors + * + * For historical reasons, low-level error codes are divided in even and odd, + * even codes were assigned first, and -1 is reserved for other errors. + * + * Low-level module errors (0x0002-0x007E, 0x0003-0x007F) + * + * Module Nr Codes assigned + * MPI 7 0x0002-0x0010 + * GCM 2 0x0012-0x0014 + * BLOWFISH 2 0x0016-0x0018 + * THREADING 3 0x001A-0x001E + * AES 2 0x0020-0x0022 + * CAMELLIA 2 0x0024-0x0026 + * XTEA 1 0x0028-0x0028 + * BASE64 2 0x002A-0x002C + * OID 1 0x002E-0x002E 0x000B-0x000B + * PADLOCK 1 0x0030-0x0030 + * DES 1 0x0032-0x0032 + * CTR_DBRG 4 0x0034-0x003A + * ENTROPY 3 0x003C-0x0040 + * NET 11 0x0042-0x0056 + * ENTROPY 1 0x0058-0x0058 + * ASN1 7 0x0060-0x006C + * MD2 1 0x0070-0x0070 + * MD4 1 0x0072-0x0072 + * MD5 1 0x0074-0x0074 + * SHA1 1 0x0076-0x0076 + * SHA256 1 0x0078-0x0078 + * SHA512 1 0x007A-0x007A + * PBKDF2 1 0x007C-0x007C + * RIPEMD160 1 0x007E-0x007E + * HMAC_DRBG 4 0x0003-0x0009 + * CCM 2 0x000D-0x000F + * + * High-level module nr (3 bits - 0x0...-0x7...) + * Name ID Nr of Errors + * PEM 1 9 + * PKCS#12 1 4 (Started from top) + * X509 2 18 + * PK 2 14 (Started from top, plus 0x2000) + * DHM 3 9 + * PKCS5 3 4 (Started from top) + * RSA 4 9 + * ECP 4 8 (Started from top) + * MD 5 4 + * CIPHER 6 6 + * SSL 6 9 (Started from top) + * SSL 7 31 + * + * Module dependent error code (5 bits 0x.00.-0x.F8.) + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Translate a PolarSSL error code into a string representation, + * Result is truncated if necessary and always includes a terminating + * null byte. + * + * \param errnum error code + * \param buffer buffer to place representation in + * \param buflen length of the buffer + */ +void polarssl_strerror( int errnum, char *buffer, size_t buflen ); + +#if defined(POLARSSL_ERROR_STRERROR_BC) +void error_strerror( int errnum, char *buffer, size_t buflen ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* error.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/gcm.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/gcm.h new file mode 100644 index 0000000..c2829a0 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/gcm.h @@ -0,0 +1,219 @@ +/** + * \file gcm.h + * + * \brief Galois/Counter mode for 128-bit block ciphers + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_GCM_H +#define POLARSSL_GCM_H + +#include "cipher.h" + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +#define GCM_ENCRYPT 1 +#define GCM_DECRYPT 0 + +#define POLARSSL_ERR_GCM_AUTH_FAILED -0x0012 /**< Authenticated decryption failed. */ +#define POLARSSL_ERR_GCM_BAD_INPUT -0x0014 /**< Bad input parameters to function. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief GCM context structure + */ +typedef struct { + cipher_context_t cipher_ctx;/*!< cipher context used */ + uint64_t HL[16]; /*!< Precalculated HTable */ + uint64_t HH[16]; /*!< Precalculated HTable */ + uint64_t len; /*!< Total data length */ + uint64_t add_len; /*!< Total add length */ + unsigned char base_ectr[16];/*!< First ECTR for tag */ + unsigned char y[16]; /*!< Y working value */ + unsigned char buf[16]; /*!< buf working value */ + int mode; /*!< Encrypt or Decrypt */ +} +gcm_context; + +/** + * \brief GCM initialization (encryption) + * + * \param ctx GCM context to be initialized + * \param cipher cipher to use (a 128-bit block cipher) + * \param key encryption key + * \param keysize must be 128, 192 or 256 + * + * \return 0 if successful, or a cipher specific error code + */ +int gcm_init( gcm_context *ctx, cipher_id_t cipher, const unsigned char *key, + unsigned int keysize ); + +/** + * \brief GCM buffer encryption/decryption using a block cipher + * + * \note On encryption, the output buffer can be the same as the input buffer. + * On decryption, the output buffer cannot be the same as input buffer. + * If buffers overlap, the output buffer must trail at least 8 bytes + * behind the input buffer. + * + * \param ctx GCM context + * \param mode GCM_ENCRYPT or GCM_DECRYPT + * \param length length of the input data + * \param iv initialization vector + * \param iv_len length of IV + * \param add additional data + * \param add_len length of additional data + * \param input buffer holding the input data + * \param output buffer for holding the output data + * \param tag_len length of the tag to generate + * \param tag buffer for holding the tag + * + * \return 0 if successful + */ +int gcm_crypt_and_tag( gcm_context *ctx, + int mode, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *input, + unsigned char *output, + size_t tag_len, + unsigned char *tag ); + +/** + * \brief GCM buffer authenticated decryption using a block cipher + * + * \note On decryption, the output buffer cannot be the same as input buffer. + * If buffers overlap, the output buffer must trail at least 8 bytes + * behind the input buffer. + * + * \param ctx GCM context + * \param length length of the input data + * \param iv initialization vector + * \param iv_len length of IV + * \param add additional data + * \param add_len length of additional data + * \param tag buffer holding the tag + * \param tag_len length of the tag + * \param input buffer holding the input data + * \param output buffer for holding the output data + * + * \return 0 if successful and authenticated, + * POLARSSL_ERR_GCM_AUTH_FAILED if tag does not match + */ +int gcm_auth_decrypt( gcm_context *ctx, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *tag, + size_t tag_len, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic GCM stream start function + * + * \param ctx GCM context + * \param mode GCM_ENCRYPT or GCM_DECRYPT + * \param iv initialization vector + * \param iv_len length of IV + * \param add additional data (or NULL if length is 0) + * \param add_len length of additional data + * + * \return 0 if successful + */ +int gcm_starts( gcm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len ); + +/** + * \brief Generic GCM update function. Encrypts/decrypts using the + * given GCM context. Expects input to be a multiple of 16 + * bytes! Only the last call before gcm_finish() can be less + * than 16 bytes! + * + * \note On decryption, the output buffer cannot be the same as input buffer. + * If buffers overlap, the output buffer must trail at least 8 bytes + * behind the input buffer. + * + * \param ctx GCM context + * \param length length of the input data + * \param input buffer holding the input data + * \param output buffer for holding the output data + * + * \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT + */ +int gcm_update( gcm_context *ctx, + size_t length, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic GCM finalisation function. Wraps up the GCM stream + * and generates the tag. The tag can have a maximum length of + * 16 bytes. + * + * \param ctx GCM context + * \param tag buffer for holding the tag (may be NULL if tag_len is 0) + * \param tag_len length of the tag to generate + * + * \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT + */ +int gcm_finish( gcm_context *ctx, + unsigned char *tag, + size_t tag_len ); + +/** + * \brief Free a GCM context and underlying cipher sub-context + * + * \param ctx GCM context to free + */ +void gcm_free( gcm_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int gcm_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* gcm.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/havege.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/havege.h new file mode 100644 index 0000000..536eb08 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/havege.h @@ -0,0 +1,78 @@ +/** + * \file havege.h + * + * \brief HAVEGE: HArdware Volatile Entropy Gathering and Expansion + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_HAVEGE_H +#define POLARSSL_HAVEGE_H + +#include + +#define COLLECT_SIZE 1024 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief HAVEGE state structure + */ +typedef struct +{ + int PT1, PT2, offset[2]; + int pool[COLLECT_SIZE]; + int WALK[8192]; +} +havege_state; + +/** + * \brief HAVEGE initialization + * + * \param hs HAVEGE state to be initialized + */ +void havege_init( havege_state *hs ); + +/** + * \brief Clear HAVEGE state + * + * \param hs HAVEGE state to be cleared + */ +void havege_free( havege_state *hs ); + +/** + * \brief HAVEGE rand function + * + * \param p_rng A HAVEGE state + * \param output Buffer to fill + * \param len Length of buffer + * + * \return 0 + */ +int havege_random( void *p_rng, unsigned char *output, size_t len ); + +#ifdef __cplusplus +} +#endif + +#endif /* havege.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/hmac_drbg.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/hmac_drbg.h new file mode 100644 index 0000000..2d765d5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/hmac_drbg.h @@ -0,0 +1,284 @@ +/** + * \file hmac_drbg.h + * + * \brief HMAC_DRBG (NIST SP 800-90A) + * + * Copyright (C) 2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_HMAC_DRBG_H +#define POLARSSL_HMAC_DRBG_H + +#include "md.h" + +/* + * Error codes + */ +#define POLARSSL_ERR_HMAC_DRBG_REQUEST_TOO_BIG -0x0003 /**< Too many random requested in single call. */ +#define POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG -0x0005 /**< Input too large (Entropy + additional). */ +#define POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR -0x0007 /**< Read/write error in file. */ +#define POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED -0x0009 /**< The entropy source failed. */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(POLARSSL_HMAC_DRBG_RESEED_INTERVAL) +#define POLARSSL_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#endif + +#if !defined(POLARSSL_HMAC_DRBG_MAX_INPUT) +#define POLARSSL_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#endif + +#if !defined(POLARSSL_HMAC_DRBG_MAX_REQUEST) +#define POLARSSL_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#endif + +#if !defined(POLARSSL_HMAC_DRBG_MAX_SEED_INPUT) +#define POLARSSL_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ +#endif + +/* \} name SECTION: Module settings */ + +#define POLARSSL_HMAC_DRBG_PR_OFF 0 /**< No prediction resistance */ +#define POLARSSL_HMAC_DRBG_PR_ON 1 /**< Prediction resistance enabled */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * HMAC_DRBG context. + */ +typedef struct +{ + /* Working state: the key K is not stored explicitely, + * but is implied by the HMAC context */ + md_context_t md_ctx; /*!< HMAC context (inc. K) */ + unsigned char V[POLARSSL_MD_MAX_SIZE]; /*!< V in the spec */ + int reseed_counter; /*!< reseed counter */ + + /* Administrative state */ + size_t entropy_len; /*!< entropy bytes grabbed on each (re)seed */ + int prediction_resistance; /*!< enable prediction resistance (Automatic + reseed before every random generation) */ + int reseed_interval; /*!< reseed interval */ + + /* Callbacks */ + int (*f_entropy)(void *, unsigned char *, size_t); /*!< entropy function */ + void *p_entropy; /*!< context for the entropy function */ +} hmac_drbg_context; + +/** + * \brief HMAC_DRBG initialisation + * + * \param ctx HMAC_DRBG context to be initialised + * \param md_info MD algorithm to use for HMAC_DRBG + * \param f_entropy Entropy callback (p_entropy, buffer to fill, buffer + * length) + * \param p_entropy Entropy context + * \param custom Personalization data (Device specific identifiers) + * (Can be NULL) + * \param len Length of personalization data + * + * \note The "security strength" as defined by NIST is set to: + * 128 bits if md_alg is SHA-1, + * 192 bits if md_alg is SHA-224, + * 256 bits if md_alg is SHA-256 or higher. + * Note that SHA-256 is just as efficient as SHA-224. + * + * \return 0 if successful, or + * POLARSSL_ERR_MD_BAD_INPUT_DATA, or + * POLARSSL_ERR_MD_ALLOC_FAILED, or + * POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED. + */ +int hmac_drbg_init( hmac_drbg_context *ctx, + const md_info_t * md_info, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len ); + +/** + * \brief Initilisation of simpified HMAC_DRBG (never reseeds). + * (For use with deterministic ECDSA.) + * + * \param ctx HMAC_DRBG context to be initialised + * \param md_info MD algorithm to use for HMAC_DRBG + * \param data Concatenation of entropy string and additional data + * \param data_len Length of data in bytes + * + * \return 0 if successful, or + * POLARSSL_ERR_MD_BAD_INPUT_DATA, or + * POLARSSL_ERR_MD_ALLOC_FAILED. + */ +int hmac_drbg_init_buf( hmac_drbg_context *ctx, + const md_info_t * md_info, + const unsigned char *data, size_t data_len ); + +/** + * \brief Enable / disable prediction resistance (Default: Off) + * + * Note: If enabled, entropy is used for ctx->entropy_len before each call! + * Only use this if you have ample supply of good entropy! + * + * \param ctx HMAC_DRBG context + * \param resistance POLARSSL_HMAC_DRBG_PR_ON or POLARSSL_HMAC_DRBG_PR_OFF + */ +void hmac_drbg_set_prediction_resistance( hmac_drbg_context *ctx, + int resistance ); + +/** + * \brief Set the amount of entropy grabbed on each reseed + * (Default: given by the security strength, which + * depends on the hash used, see \c hmac_drbg_init() ) + * + * \param ctx HMAC_DRBG context + * \param len Amount of entropy to grab, in bytes + */ +void hmac_drbg_set_entropy_len( hmac_drbg_context *ctx, + size_t len ); + +/** + * \brief Set the reseed interval + * (Default: POLARSSL_HMAC_DRBG_RESEED_INTERVAL) + * + * \param ctx HMAC_DRBG context + * \param interval Reseed interval + */ +void hmac_drbg_set_reseed_interval( hmac_drbg_context *ctx, + int interval ); + +/** + * \brief HMAC_DRBG update state + * + * \param ctx HMAC_DRBG context + * \param additional Additional data to update state with, or NULL + * \param add_len Length of additional data, or 0 + * + * \note Additional data is optional, pass NULL and 0 as second + * third argument if no additional data is being used. + */ +void hmac_drbg_update( hmac_drbg_context *ctx, + const unsigned char *additional, size_t add_len ); + +/** + * \brief HMAC_DRBG reseeding (extracts data from entropy source) + * + * \param ctx HMAC_DRBG context + * \param additional Additional data to add to state (Can be NULL) + * \param len Length of additional data + * + * \return 0 if successful, or + * POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + */ +int hmac_drbg_reseed( hmac_drbg_context *ctx, + const unsigned char *additional, size_t len ); + +/** + * \brief HMAC_DRBG generate random with additional update input + * + * Note: Automatically reseeds if reseed_counter is reached or PR is enabled. + * + * \param p_rng HMAC_DRBG context + * \param output Buffer to fill + * \param output_len Length of the buffer + * \param additional Additional data to update with (can be NULL) + * \param add_len Length of additional data (can be 0) + * + * \return 0 if successful, or + * POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED, or + * POLARSSL_ERR_HMAC_DRBG_REQUEST_TOO_BIG, or + * POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG. + */ +int hmac_drbg_random_with_add( void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, + size_t add_len ); + +/** + * \brief HMAC_DRBG generate random + * + * Note: Automatically reseeds if reseed_counter is reached or PR is enabled. + * + * \param p_rng HMAC_DRBG context + * \param output Buffer to fill + * \param out_len Length of the buffer + * + * \return 0 if successful, or + * POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED, or + * POLARSSL_ERR_HMAC_DRBG_REQUEST_TOO_BIG + */ +int hmac_drbg_random( void *p_rng, unsigned char *output, size_t out_len ); + +/** + * \brief Free an HMAC_DRBG context + * + * \param ctx HMAC_DRBG context to free. + */ +void hmac_drbg_free( hmac_drbg_context *ctx ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Write a seed file + * + * \param ctx HMAC_DRBG context + * \param path Name of the file + * + * \return 0 if successful, 1 on file error, or + * POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + */ +int hmac_drbg_write_seed_file( hmac_drbg_context *ctx, const char *path ); + +/** + * \brief Read and update a seed file. Seed is added to this + * instance + * + * \param ctx HMAC_DRBG context + * \param path Name of the file + * + * \return 0 if successful, 1 on file error, + * POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED or + * POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG + */ +int hmac_drbg_update_seed_file( hmac_drbg_context *ctx, const char *path ); +#endif /* POLARSSL_FS_IO */ + + +#if defined(POLARSSL_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int hmac_drbg_self_test( int verbose ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* hmac_drbg.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md.h new file mode 100644 index 0000000..81d8a2e --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md.h @@ -0,0 +1,395 @@ +/** + * \file md.h + * + * \brief Generic message digest wrapper + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MD_H +#define POLARSSL_MD_H + +#include + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +#define POLARSSL_ERR_MD_FEATURE_UNAVAILABLE -0x5080 /**< The selected feature is not available. */ +#define POLARSSL_ERR_MD_BAD_INPUT_DATA -0x5100 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_MD_ALLOC_FAILED -0x5180 /**< Failed to allocate memory. */ +#define POLARSSL_ERR_MD_FILE_IO_ERROR -0x5200 /**< Opening or reading of file failed. */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + POLARSSL_MD_NONE=0, + POLARSSL_MD_MD2, + POLARSSL_MD_MD4, + POLARSSL_MD_MD5, + POLARSSL_MD_SHA1, + POLARSSL_MD_SHA224, + POLARSSL_MD_SHA256, + POLARSSL_MD_SHA384, + POLARSSL_MD_SHA512, + POLARSSL_MD_RIPEMD160, +} md_type_t; + +#if defined(POLARSSL_SHA512_C) +#define POLARSSL_MD_MAX_SIZE 64 /* longest known is SHA512 */ +#else +#define POLARSSL_MD_MAX_SIZE 32 /* longest known is SHA256 or less */ +#endif + +/** + * Message digest information. Allows message digest functions to be called + * in a generic way. + */ +typedef struct { + /** Digest identifier */ + md_type_t type; + + /** Name of the message digest */ + const char * name; + + /** Output length of the digest function */ + int size; + + /** Digest initialisation function */ + void (*starts_func)( void *ctx ); + + /** Digest update function */ + void (*update_func)( void *ctx, const unsigned char *input, size_t ilen ); + + /** Digest finalisation function */ + void (*finish_func)( void *ctx, unsigned char *output ); + + /** Generic digest function */ + void (*digest_func)( const unsigned char *input, size_t ilen, + unsigned char *output ); + + /** Generic file digest function */ + int (*file_func)( const char *path, unsigned char *output ); + + /** HMAC Initialisation function */ + void (*hmac_starts_func)( void *ctx, const unsigned char *key, + size_t keylen ); + + /** HMAC update function */ + void (*hmac_update_func)( void *ctx, const unsigned char *input, + size_t ilen ); + + /** HMAC finalisation function */ + void (*hmac_finish_func)( void *ctx, unsigned char *output); + + /** HMAC context reset function */ + void (*hmac_reset_func)( void *ctx ); + + /** Generic HMAC function */ + void (*hmac_func)( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ); + + /** Allocate a new context */ + void * (*ctx_alloc_func)( void ); + + /** Free the given context */ + void (*ctx_free_func)( void *ctx ); + + /** Internal use only */ + void (*process_func)( void *ctx, const unsigned char *input ); +} md_info_t; + +/** + * Generic message digest context. + */ +typedef struct { + /** Information about the associated message digest */ + const md_info_t *md_info; + + /** Digest-specific context */ + void *md_ctx; +} md_context_t; + +#define MD_CONTEXT_T_INIT { \ + NULL, /* md_info */ \ + NULL, /* md_ctx */ \ +} + +/** + * \brief Returns the list of digests supported by the generic digest module. + * + * \return a statically allocated array of digests, the last entry + * is 0. + */ +const int *md_list( void ); + +/** + * \brief Returns the message digest information associated with the + * given digest name. + * + * \param md_name Name of the digest to search for. + * + * \return The message digest information associated with md_name or + * NULL if not found. + */ +const md_info_t *md_info_from_string( const char *md_name ); + +/** + * \brief Returns the message digest information associated with the + * given digest type. + * + * \param md_type type of digest to search for. + * + * \return The message digest information associated with md_type or + * NULL if not found. + */ +const md_info_t *md_info_from_type( md_type_t md_type ); + +/** + * \brief Initialize a md_context (as NONE) + */ +void md_init( md_context_t *ctx ); + +/** + * \brief Free and clear the message-specific context of ctx. + * Freeing ctx itself remains the responsibility of the + * caller. + */ +void md_free( md_context_t *ctx ); + +/** + * \brief Initialises and fills the message digest context structure + * with the appropriate values. + * + * \note Currently also clears structure. In future versions you + * will be required to call md_init() on the structure + * first. + * + * \param ctx context to initialise. May not be NULL. The + * digest-specific context (ctx->md_ctx) must be NULL. It will + * be allocated, and must be freed using md_free_ctx() later. + * \param md_info message digest to use. + * + * \returns \c 0 on success, \c POLARSSL_ERR_MD_BAD_INPUT_DATA on + * parameter failure, \c POLARSSL_ERR_MD_ALLOC_FAILED if + * allocation of the digest-specific context failed. + */ +int md_init_ctx( md_context_t *ctx, const md_info_t *md_info ); + +/** + * \brief Free the message-specific context of ctx. Freeing ctx itself + * remains the responsibility of the caller. + * + * \note Deprecated: Redirects to md_free() + * + * \param ctx Free the message-specific context + * + * \returns 0 + */ +int md_free_ctx( md_context_t *ctx ); + +/** + * \brief Returns the size of the message digest output. + * + * \param md_info message digest info + * + * \return size of the message digest output. + */ +static inline unsigned char md_get_size( const md_info_t *md_info ) +{ + if( md_info == NULL ) + return( 0 ); + + return md_info->size; +} + +/** + * \brief Returns the type of the message digest output. + * + * \param md_info message digest info + * + * \return type of the message digest output. + */ +static inline md_type_t md_get_type( const md_info_t *md_info ) +{ + if( md_info == NULL ) + return( POLARSSL_MD_NONE ); + + return md_info->type; +} + +/** + * \brief Returns the name of the message digest output. + * + * \param md_info message digest info + * + * \return name of the message digest output. + */ +static inline const char *md_get_name( const md_info_t *md_info ) +{ + if( md_info == NULL ) + return( NULL ); + + return md_info->name; +} + +/** + * \brief Set-up the given context for a new message digest + * + * \param ctx generic message digest context. + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_starts( md_context_t *ctx ); + +/** + * \brief Generic message digest process buffer + * + * \param ctx Generic message digest context + * \param input buffer holding the datal + * \param ilen length of the input data + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_update( md_context_t *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief Generic message digest final digest + * + * \param ctx Generic message digest context + * \param output Generic message digest checksum result + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_finish( md_context_t *ctx, unsigned char *output ); + +/** + * \brief Output = message_digest( input buffer ) + * + * \param md_info message digest info + * \param input buffer holding the data + * \param ilen length of the input data + * \param output Generic message digest checksum result + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md( const md_info_t *md_info, const unsigned char *input, size_t ilen, + unsigned char *output ); + +/** + * \brief Output = message_digest( file contents ) + * + * \param md_info message digest info + * \param path input file name + * \param output generic message digest checksum result + * + * \return 0 if successful, POLARSSL_ERR_MD_FILE_OPEN_FAILED if fopen + * failed, POLARSSL_ERR_MD_FILE_READ_FAILED if fread failed, + * POLARSSL_ERR_MD_BAD_INPUT_DATA if md_info was NULL. + */ +int md_file( const md_info_t *md_info, const char *path, + unsigned char *output ); + +/** + * \brief Generic HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_hmac_starts( md_context_t *ctx, const unsigned char *key, + size_t keylen ); + +/** + * \brief Generic HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_hmac_update( md_context_t *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief Generic HMAC final digest + * + * \param ctx HMAC context + * \param output Generic HMAC checksum result + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_hmac_finish( md_context_t *ctx, unsigned char *output); + +/** + * \brief Generic HMAC context reset + * + * \param ctx HMAC context to be reset + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_hmac_reset( md_context_t *ctx ); + +/** + * \brief Output = Generic_HMAC( hmac key, input buffer ) + * + * \param md_info message digest info + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output Generic HMAC-result + * + * \returns 0 on success, POLARSSL_ERR_MD_BAD_INPUT_DATA if parameter + * verification fails. + */ +int md_hmac( const md_info_t *md_info, const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ); + +/* Internal use */ +int md_process( md_context_t *ctx, const unsigned char *data ); + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_MD_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md2.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md2.h new file mode 100644 index 0000000..952b0bf --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md2.h @@ -0,0 +1,194 @@ +/** + * \file md2.h + * + * \brief MD2 message digest algorithm (hash function) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MD2_H +#define POLARSSL_MD2_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#define POLARSSL_ERR_MD2_FILE_IO_ERROR -0x0070 /**< Read/write error in file. */ + +#if !defined(POLARSSL_MD2_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD2 context structure + */ +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 */ + + unsigned char ipad[16]; /*!< HMAC: inner padding */ + unsigned char opad[16]; /*!< HMAC: outer padding */ + size_t left; /*!< amount of data in buffer */ +} +md2_context; + +/** + * \brief Initialize MD2 context + * + * \param ctx MD2 context to be initialized + */ +void md2_init( md2_context *ctx ); + +/** + * \brief Clear MD2 context + * + * \param ctx MD2 context to be cleared + */ +void md2_free( md2_context *ctx ); + +/** + * \brief MD2 context setup + * + * \param ctx context to be initialized + */ +void md2_starts( md2_context *ctx ); + +/** + * \brief MD2 process buffer + * + * \param ctx MD2 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md2_update( md2_context *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief MD2 final digest + * + * \param ctx MD2 context + * \param output MD2 checksum result + */ +void md2_finish( md2_context *ctx, unsigned char output[16] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_MD2_ALT */ +#include "md2_alt.h" +#endif /* POLARSSL_MD2_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = MD2( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD2 checksum result + */ +void md2( const unsigned char *input, size_t ilen, unsigned char output[16] ); + +/** + * \brief Output = MD2( file contents ) + * + * \param path input file name + * \param output MD2 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_MD2_FILE_IO_ERROR + */ +int md2_file( const char *path, unsigned char output[16] ); + +/** + * \brief MD2 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void md2_hmac_starts( md2_context *ctx, const unsigned char *key, + size_t keylen ); + +/** + * \brief MD2 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md2_hmac_update( md2_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief MD2 HMAC final digest + * + * \param ctx HMAC context + * \param output MD2 HMAC checksum result + */ +void md2_hmac_finish( md2_context *ctx, unsigned char output[16] ); + +/** + * \brief MD2 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void md2_hmac_reset( md2_context *ctx ); + +/** + * \brief Output = HMAC-MD2( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-MD2 result + */ +void md2_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int md2_self_test( int verbose ); + +/* Internal use */ +void md2_process( md2_context *ctx ); + +#ifdef __cplusplus +} +#endif + +#endif /* md2.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md4.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md4.h new file mode 100644 index 0000000..fc5a5cd --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md4.h @@ -0,0 +1,200 @@ +/** + * \file md4.h + * + * \brief MD4 message digest algorithm (hash function) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MD4_H +#define POLARSSL_MD4_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_MD4_FILE_IO_ERROR -0x0072 /**< Read/write error in file. */ + +#if !defined(POLARSSL_MD4_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD4 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +md4_context; + +/** + * \brief Initialize MD4 context + * + * \param ctx MD4 context to be initialized + */ +void md4_init( md4_context *ctx ); + +/** + * \brief Clear MD4 context + * + * \param ctx MD4 context to be cleared + */ +void md4_free( md4_context *ctx ); + +/** + * \brief MD4 context setup + * + * \param ctx context to be initialized + */ +void md4_starts( md4_context *ctx ); + +/** + * \brief MD4 process buffer + * + * \param ctx MD4 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_update( md4_context *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief MD4 final digest + * + * \param ctx MD4 context + * \param output MD4 checksum result + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_MD4_ALT */ +#include "md4_alt.h" +#endif /* POLARSSL_MD4_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = MD4( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD4 checksum result + */ +void md4( const unsigned char *input, size_t ilen, unsigned char output[16] ); + +/** + * \brief Output = MD4( file contents ) + * + * \param path input file name + * \param output MD4 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_MD4_FILE_IO_ERROR + */ +int md4_file( const char *path, unsigned char output[16] ); + +/** + * \brief MD4 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void md4_hmac_starts( md4_context *ctx, const unsigned char *key, + size_t keylen ); + +/** + * \brief MD4 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md4_hmac_update( md4_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief MD4 HMAC final digest + * + * \param ctx HMAC context + * \param output MD4 HMAC checksum result + */ +void md4_hmac_finish( md4_context *ctx, unsigned char output[16] ); + +/** + * \brief MD4 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void md4_hmac_reset( md4_context *ctx ); + +/** + * \brief Output = HMAC-MD4( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-MD4 result + */ +void md4_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int md4_self_test( int verbose ); + +/* Internal use */ +void md4_process( md4_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#endif /* md4.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md5.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md5.h new file mode 100644 index 0000000..2f378f6 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md5.h @@ -0,0 +1,200 @@ +/** + * \file md5.h + * + * \brief MD5 message digest algorithm (hash function) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MD5_H +#define POLARSSL_MD5_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_MD5_FILE_IO_ERROR -0x0074 /**< Read/write error in file. */ + +#if !defined(POLARSSL_MD5_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MD5 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[4]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +md5_context; + +/** + * \brief Initialize MD5 context + * + * \param ctx MD5 context to be initialized + */ +void md5_init( md5_context *ctx ); + +/** + * \brief Clear MD5 context + * + * \param ctx MD5 context to be cleared + */ +void md5_free( md5_context *ctx ); + +/** + * \brief MD5 context setup + * + * \param ctx context to be initialized + */ +void md5_starts( md5_context *ctx ); + +/** + * \brief MD5 process buffer + * + * \param ctx MD5 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief MD5 final digest + * + * \param ctx MD5 context + * \param output MD5 checksum result + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ); + +/* Internal use */ +void md5_process( md5_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_MD5_ALT */ +#include "md5_alt.h" +#endif /* POLARSSL_MD5_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = MD5( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD5 checksum result + */ +void md5( const unsigned char *input, size_t ilen, unsigned char output[16] ); + +/** + * \brief Output = MD5( file contents ) + * + * \param path input file name + * \param output MD5 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_MD5_FILE_IO_ERROR + */ +int md5_file( const char *path, unsigned char output[16] ); + +/** + * \brief MD5 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void md5_hmac_starts( md5_context *ctx, + const unsigned char *key, size_t keylen ); + +/** + * \brief MD5 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void md5_hmac_update( md5_context *ctx, + const unsigned char *input, size_t ilen ); + +/** + * \brief MD5 HMAC final digest + * + * \param ctx HMAC context + * \param output MD5 HMAC checksum result + */ +void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ); + +/** + * \brief MD5 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void md5_hmac_reset( md5_context *ctx ); + +/** + * \brief Output = HMAC-MD5( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-MD5 result + */ +void md5_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int md5_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* md5.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md_wrap.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md_wrap.h new file mode 100644 index 0000000..eb1db0f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/md_wrap.h @@ -0,0 +1,71 @@ +/** + * \file md_wrap.h + * + * \brief Message digest wrappers. + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MD_WRAP_H +#define POLARSSL_MD_WRAP_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#include "md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(POLARSSL_MD2_C) +extern const md_info_t md2_info; +#endif +#if defined(POLARSSL_MD4_C) +extern const md_info_t md4_info; +#endif +#if defined(POLARSSL_MD5_C) +extern const md_info_t md5_info; +#endif +#if defined(POLARSSL_RIPEMD160_C) +extern const md_info_t ripemd160_info; +#endif +#if defined(POLARSSL_SHA1_C) +extern const md_info_t sha1_info; +#endif +#if defined(POLARSSL_SHA256_C) +extern const md_info_t sha224_info; +extern const md_info_t sha256_info; +#endif +#if defined(POLARSSL_SHA512_C) +extern const md_info_t sha384_info; +extern const md_info_t sha512_info; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_MD_WRAP_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory.h new file mode 100644 index 0000000..188f855 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory.h @@ -0,0 +1,52 @@ +/** + * \file memory.h + * + * \brief Memory allocation layer (Deprecated to platform layer) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MEMORY_H +#define POLARSSL_MEMORY_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(POLARSSL_MEMORY_C) && !defined(POLARSSL_PLATFORM_MEMORY) +#define POLARSSL_PLATFORM_MEMORY +#endif + +#include "platform.h" +#include "memory_buffer_alloc.h" + +static int memory_set_own( void * (*malloc_func)( size_t ), + void (*free_func)( void * ) ) +{ + return platform_set_malloc_free( malloc_func, free_func ); +} + + +#endif /* memory.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory_buffer_alloc.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory_buffer_alloc.h new file mode 100644 index 0000000..c449752 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/memory_buffer_alloc.h @@ -0,0 +1,122 @@ +/** + * \file memory_buffer_alloc.h + * + * \brief Buffer-based memory allocator + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_MEMORY_BUFFER_ALLOC_H +#define POLARSSL_MEMORY_BUFFER_ALLOC_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(POLARSSL_MEMORY_ALIGN_MULTIPLE) +#define POLARSSL_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ +#endif + +/* \} name SECTION: Module settings */ + +#define MEMORY_VERIFY_NONE 0 +#define MEMORY_VERIFY_ALLOC (1 << 0) +#define MEMORY_VERIFY_FREE (1 << 1) +#define MEMORY_VERIFY_ALWAYS (MEMORY_VERIFY_ALLOC | MEMORY_VERIFY_FREE) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize use of stack-based memory allocator. + * The stack-based allocator does memory management inside the + * presented buffer and does not call malloc() and free(). + * It sets the global polarssl_malloc() and polarssl_free() pointers + * to its own functions. + * (Provided polarssl_malloc() and polarssl_free() are thread-safe if + * POLARSSL_THREADING_C is defined) + * + * \note This code is not optimized and provides a straight-forward + * implementation of a stack-based memory allocator. + * + * \param buf buffer to use as heap + * \param len size of the buffer + * + * \return 0 if successful + */ +int memory_buffer_alloc_init( unsigned char *buf, size_t len ); + +/** + * \brief Free the mutex for thread-safety and clear remaining memory + */ +void memory_buffer_alloc_free( void ); + +/** + * \brief Determine when the allocator should automatically verify the state + * of the entire chain of headers / meta-data. + * (Default: MEMORY_VERIFY_NONE) + * + * \param verify One of MEMORY_VERIFY_NONE, MEMORY_VERIFY_ALLOC, + * MEMORY_VERIFY_FREE or MEMORY_VERIFY_ALWAYS + */ +void memory_buffer_set_verify( int verify ); + +#if defined(POLARSSL_MEMORY_DEBUG) +/** + * \brief Print out the status of the allocated memory (primarily for use + * after a program should have de-allocated all memory) + * Prints out a list of 'still allocated' blocks and their stack + * trace if POLARSSL_MEMORY_BACKTRACE is defined. + */ +void memory_buffer_alloc_status( void ); +#endif /* POLARSSL_MEMORY_DEBUG */ + +/** + * \brief Verifies that all headers in the memory buffer are correct + * and contain sane values. Helps debug buffer-overflow errors. + * + * Prints out first failure if POLARSSL_MEMORY_DEBUG is defined. + * Prints out full header information if POLARSSL_MEMORY_DEBUG_HEADERS + * is defined. (Includes stack trace information for each block if + * POLARSSL_MEMORY_BACKTRACE is defined as well). + * + * \returns 0 if verified, 1 otherwise + */ +int memory_buffer_alloc_verify( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* memory_buffer_alloc.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/net.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/net.h new file mode 100644 index 0000000..22698b4 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/net.h @@ -0,0 +1,160 @@ +/** + * \file net.h + * + * \brief Network communication functions + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_NET_H +#define POLARSSL_NET_H + +#include + +#define POLARSSL_ERR_NET_UNKNOWN_HOST -0x0056 /**< Failed to get an IP address for the given hostname. */ +#define POLARSSL_ERR_NET_SOCKET_FAILED -0x0042 /**< Failed to open a socket. */ +#define POLARSSL_ERR_NET_CONNECT_FAILED -0x0044 /**< The connection to the given server / port failed. */ +#define POLARSSL_ERR_NET_BIND_FAILED -0x0046 /**< Binding of the socket failed. */ +#define POLARSSL_ERR_NET_LISTEN_FAILED -0x0048 /**< Could not listen on the socket. */ +#define POLARSSL_ERR_NET_ACCEPT_FAILED -0x004A /**< Could not accept the incoming connection. */ +#define POLARSSL_ERR_NET_RECV_FAILED -0x004C /**< Reading information from the socket failed. */ +#define POLARSSL_ERR_NET_SEND_FAILED -0x004E /**< Sending information through the socket failed. */ +#define POLARSSL_ERR_NET_CONN_RESET -0x0050 /**< Connection was reset by peer. */ +#define POLARSSL_ERR_NET_WANT_READ -0x0052 /**< Connection requires a read call. */ +#define POLARSSL_ERR_NET_WANT_WRITE -0x0054 /**< Connection requires a write call. */ + +#define POLARSSL_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initiate a TCP connection with host:port + * + * \param fd Socket to use + * \param host Host to connect to + * \param port Port to connect to + * + * \return 0 if successful, or one of: + * POLARSSL_ERR_NET_SOCKET_FAILED, + * POLARSSL_ERR_NET_UNKNOWN_HOST, + * POLARSSL_ERR_NET_CONNECT_FAILED + */ +int net_connect( int *fd, const char *host, int port ); + +/** + * \brief Create a listening socket on bind_ip:port. + * If bind_ip == NULL, all interfaces are binded. + * + * \param fd Socket to use + * \param bind_ip IP to bind to, can be NULL + * \param port Port number to use + * + * \return 0 if successful, or one of: + * POLARSSL_ERR_NET_SOCKET_FAILED, + * POLARSSL_ERR_NET_BIND_FAILED, + * POLARSSL_ERR_NET_LISTEN_FAILED + */ +int net_bind( int *fd, const char *bind_ip, int port ); + +/** + * \brief Accept a connection from a remote client + * + * \param bind_fd Relevant socket + * \param client_fd Will contain the connected client socket + * \param client_ip Will contain the client IP address + * Must be at least 4 bytes, or 16 if IPv6 is supported + * + * \return 0 if successful, POLARSSL_ERR_NET_ACCEPT_FAILED, or + * POLARSSL_ERR_NET_WANT_READ is bind_fd was set to + * non-blocking and accept() is blocking. + */ +int net_accept( int bind_fd, int *client_fd, void *client_ip ); + +/** + * \brief Set the socket blocking + * + * \param fd Socket to set + * + * \return 0 if successful, or a non-zero error code + */ +int net_set_block( int fd ); + +/** + * \brief Set the socket non-blocking + * + * \param fd Socket to set + * + * \return 0 if successful, or a non-zero error code + */ +int net_set_nonblock( int fd ); + +/** + * \brief Portable usleep helper + * + * \param usec Amount of microseconds to sleep + * + * \note Real amount of time slept will not be less than + * select()'s timeout granularity (typically, 10ms). + */ +void net_usleep( unsigned long usec ); + +/** + * \brief Read at most 'len' characters. If no error occurs, + * the actual amount read is returned. + * + * \param ctx Socket + * \param buf The buffer to write to + * \param len Maximum length of the buffer + * + * \return This function returns the number of bytes received, + * or a non-zero error code; POLARSSL_ERR_NET_WANT_READ + * indicates read() is blocking. + */ +int net_recv( void *ctx, unsigned char *buf, size_t len ); + +/** + * \brief Write at most 'len' characters. If no error occurs, + * the actual amount read is returned. + * + * \param ctx Socket + * \param buf The buffer to read from + * \param len The length of the buffer + * + * \return This function returns the number of bytes sent, + * or a non-zero error code; POLARSSL_ERR_NET_WANT_WRITE + * indicates write() is blocking. + */ +int net_send( void *ctx, const unsigned char *buf, size_t len ); + +/** + * \brief Gracefully shutdown the connection + * + * \param fd The socket to close + */ +void net_close( int fd ); + +#ifdef __cplusplus +} +#endif + +#endif /* net.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/oid.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/oid.h new file mode 100644 index 0000000..c4d5c3f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/oid.h @@ -0,0 +1,570 @@ +/** + * \file oid.h + * + * \brief Object Identifier (OID) database + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_OID_H +#define POLARSSL_OID_H + +#include +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#include "asn1.h" +#include "pk.h" +#if defined(POLARSSL_CIPHER_C) +#include "cipher.h" +#endif + +#if defined(POLARSSL_MD_C) +#include "md.h" +#endif + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +#include "x509.h" +#endif + +#define POLARSSL_ERR_OID_NOT_FOUND -0x002E /**< OID is not found. */ +#define POLARSSL_ERR_OID_BUF_TOO_SMALL -0x000B /**< output buffer is too small */ + +/* + * Top level OID tuples + */ +#define OID_ISO_MEMBER_BODIES "\x2a" /* {iso(1) member-body(2)} */ +#define OID_ISO_IDENTIFIED_ORG "\x2b" /* {iso(1) identified-organization(3)} */ +#define OID_ISO_CCITT_DS "\x55" /* {joint-iso-ccitt(2) ds(5)} */ +#define OID_ISO_ITU_COUNTRY "\x60" /* {joint-iso-itu-t(2) country(16)} */ + +/* + * ISO Member bodies OID parts + */ +#define OID_COUNTRY_US "\x86\x48" /* {us(840)} */ +#define OID_ORG_RSA_DATA_SECURITY "\x86\xf7\x0d" /* {rsadsi(113549)} */ +#define OID_RSA_COMPANY OID_ISO_MEMBER_BODIES OID_COUNTRY_US \ + OID_ORG_RSA_DATA_SECURITY /* {iso(1) member-body(2) us(840) rsadsi(113549)} */ +#define OID_ORG_ANSI_X9_62 "\xce\x3d" /* ansi-X9-62(10045) */ +#define OID_ANSI_X9_62 OID_ISO_MEMBER_BODIES OID_COUNTRY_US \ + OID_ORG_ANSI_X9_62 + +/* + * ISO Identified organization OID parts + */ +#define OID_ORG_DOD "\x06" /* {dod(6)} */ +#define OID_ORG_OIW "\x0e" +#define OID_OIW_SECSIG OID_ORG_OIW "\x03" +#define OID_OIW_SECSIG_ALG OID_OIW_SECSIG "\x02" +#define OID_OIW_SECSIG_SHA1 OID_OIW_SECSIG_ALG "\x1a" +#define OID_ORG_CERTICOM "\x81\x04" /* certicom(132) */ +#define OID_CERTICOM OID_ISO_IDENTIFIED_ORG OID_ORG_CERTICOM +#define OID_ORG_TELETRUST "\x24" /* teletrust(36) */ +#define OID_TELETRUST OID_ISO_IDENTIFIED_ORG OID_ORG_TELETRUST + +/* + * ISO ITU OID parts + */ +#define OID_ORGANIZATION "\x01" /* {organization(1)} */ +#define OID_ISO_ITU_US_ORG OID_ISO_ITU_COUNTRY OID_COUNTRY_US OID_ORGANIZATION /* {joint-iso-itu-t(2) country(16) us(840) organization(1)} */ + +#define OID_ORG_GOV "\x65" /* {gov(101)} */ +#define OID_GOV OID_ISO_ITU_US_ORG OID_ORG_GOV /* {joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)} */ + +#define OID_ORG_NETSCAPE "\x86\xF8\x42" /* {netscape(113730)} */ +#define OID_NETSCAPE OID_ISO_ITU_US_ORG OID_ORG_NETSCAPE /* Netscape OID {joint-iso-itu-t(2) country(16) us(840) organization(1) netscape(113730)} */ + +/* ISO arc for standard certificate and CRL extensions */ +#define OID_ID_CE OID_ISO_CCITT_DS "\x1D" /**< id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} */ + +/** + * Private Internet Extensions + * { iso(1) identified-organization(3) dod(6) internet(1) + * security(5) mechanisms(5) pkix(7) } + */ +#define OID_PKIX OID_ISO_IDENTIFIED_ORG OID_ORG_DOD "\x01\x05\x05\x07" + +/* + * Arc for standard naming attributes + */ +#define OID_AT OID_ISO_CCITT_DS "\x04" /**< id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} */ +#define OID_AT_CN OID_AT "\x03" /**< id-at-commonName AttributeType:= {id-at 3} */ +#define OID_AT_SUR_NAME OID_AT "\x04" /**< id-at-surName AttributeType:= {id-at 4} */ +#define OID_AT_SERIAL_NUMBER OID_AT "\x05" /**< id-at-serialNumber AttributeType:= {id-at 5} */ +#define OID_AT_COUNTRY OID_AT "\x06" /**< id-at-countryName AttributeType:= {id-at 6} */ +#define OID_AT_LOCALITY OID_AT "\x07" /**< id-at-locality AttributeType:= {id-at 7} */ +#define OID_AT_STATE OID_AT "\x08" /**< id-at-state AttributeType:= {id-at 8} */ +#define OID_AT_ORGANIZATION OID_AT "\x0A" /**< id-at-organizationName AttributeType:= {id-at 10} */ +#define OID_AT_ORG_UNIT OID_AT "\x0B" /**< id-at-organizationalUnitName AttributeType:= {id-at 11} */ +#define OID_AT_TITLE OID_AT "\x0C" /**< id-at-title AttributeType:= {id-at 12} */ +#define OID_AT_POSTAL_ADDRESS OID_AT "\x10" /**< id-at-postalAddress AttributeType:= {id-at 16} */ +#define OID_AT_POSTAL_CODE OID_AT "\x11" /**< id-at-postalCode AttributeType:= {id-at 17} */ +#define OID_AT_GIVEN_NAME OID_AT "\x2A" /**< id-at-givenName AttributeType:= {id-at 42} */ +#define OID_AT_INITIALS OID_AT "\x2B" /**< id-at-initials AttributeType:= {id-at 43} */ +#define OID_AT_GENERATION_QUALIFIER OID_AT "\x2C" /**< id-at-generationQualifier AttributeType:= {id-at 44} */ +#define OID_AT_DN_QUALIFIER OID_AT "\x2E" /**< id-at-dnQualifier AttributeType:= {id-at 46} */ +#define OID_AT_PSEUDONYM OID_AT "\x41" /**< id-at-pseudonym AttributeType:= {id-at 65} */ + +#define OID_DOMAIN_COMPONENT "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) domainComponent(25)} */ + +/* + * OIDs for standard certificate extensions + */ +#define OID_AUTHORITY_KEY_IDENTIFIER OID_ID_CE "\x23" /**< id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } */ +#define OID_SUBJECT_KEY_IDENTIFIER OID_ID_CE "\x0E" /**< id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } */ +#define OID_KEY_USAGE OID_ID_CE "\x0F" /**< id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } */ +#define OID_CERTIFICATE_POLICIES OID_ID_CE "\x20" /**< id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } */ +#define OID_POLICY_MAPPINGS OID_ID_CE "\x21" /**< id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } */ +#define OID_SUBJECT_ALT_NAME OID_ID_CE "\x11" /**< id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } */ +#define OID_ISSUER_ALT_NAME OID_ID_CE "\x12" /**< id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } */ +#define OID_SUBJECT_DIRECTORY_ATTRS OID_ID_CE "\x09" /**< id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } */ +#define OID_BASIC_CONSTRAINTS OID_ID_CE "\x13" /**< id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } */ +#define OID_NAME_CONSTRAINTS OID_ID_CE "\x1E" /**< id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } */ +#define OID_POLICY_CONSTRAINTS OID_ID_CE "\x24" /**< id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } */ +#define OID_EXTENDED_KEY_USAGE OID_ID_CE "\x25" /**< id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } */ +#define OID_CRL_DISTRIBUTION_POINTS OID_ID_CE "\x1F" /**< id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 } */ +#define OID_INIHIBIT_ANYPOLICY OID_ID_CE "\x36" /**< id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } */ +#define OID_FRESHEST_CRL OID_ID_CE "\x2E" /**< id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } */ + +/* + * Netscape certificate extensions + */ +#define OID_NS_CERT OID_NETSCAPE "\x01" +#define OID_NS_CERT_TYPE OID_NS_CERT "\x01" +#define OID_NS_BASE_URL OID_NS_CERT "\x02" +#define OID_NS_REVOCATION_URL OID_NS_CERT "\x03" +#define OID_NS_CA_REVOCATION_URL OID_NS_CERT "\x04" +#define OID_NS_RENEWAL_URL OID_NS_CERT "\x07" +#define OID_NS_CA_POLICY_URL OID_NS_CERT "\x08" +#define OID_NS_SSL_SERVER_NAME OID_NS_CERT "\x0C" +#define OID_NS_COMMENT OID_NS_CERT "\x0D" +#define OID_NS_DATA_TYPE OID_NETSCAPE "\x02" +#define OID_NS_CERT_SEQUENCE OID_NS_DATA_TYPE "\x05" + +/* + * OIDs for CRL extensions + */ +#define OID_PRIVATE_KEY_USAGE_PERIOD OID_ID_CE "\x10" +#define OID_CRL_NUMBER OID_ID_CE "\x14" /**< id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } */ + +/* + * X.509 v3 Extended key usage OIDs + */ +#define OID_ANY_EXTENDED_KEY_USAGE OID_EXTENDED_KEY_USAGE "\x00" /**< anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } */ + +#define OID_KP OID_PKIX "\x03" /**< id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } */ +#define OID_SERVER_AUTH OID_KP "\x01" /**< id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } */ +#define OID_CLIENT_AUTH OID_KP "\x02" /**< id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } */ +#define OID_CODE_SIGNING OID_KP "\x03" /**< id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } */ +#define OID_EMAIL_PROTECTION OID_KP "\x04" /**< id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } */ +#define OID_TIME_STAMPING OID_KP "\x08" /**< id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } */ +#define OID_OCSP_SIGNING OID_KP "\x09" /**< id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } */ + +/* + * PKCS definition OIDs + */ + +#define OID_PKCS OID_RSA_COMPANY "\x01" /**< pkcs OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) 1 } */ +#define OID_PKCS1 OID_PKCS "\x01" /**< pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } */ +#define OID_PKCS5 OID_PKCS "\x05" /**< pkcs-5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } */ +#define OID_PKCS9 OID_PKCS "\x09" /**< pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } */ +#define OID_PKCS12 OID_PKCS "\x0c" /**< pkcs-12 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } */ + +/* + * PKCS#1 OIDs + */ +#define OID_PKCS1_RSA OID_PKCS1 "\x01" /**< rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } */ +#define OID_PKCS1_MD2 OID_PKCS1 "\x02" /**< md2WithRSAEncryption ::= { pkcs-1 2 } */ +#define OID_PKCS1_MD4 OID_PKCS1 "\x03" /**< md4WithRSAEncryption ::= { pkcs-1 3 } */ +#define OID_PKCS1_MD5 OID_PKCS1 "\x04" /**< md5WithRSAEncryption ::= { pkcs-1 4 } */ +#define OID_PKCS1_SHA1 OID_PKCS1 "\x05" /**< sha1WithRSAEncryption ::= { pkcs-1 5 } */ +#define OID_PKCS1_SHA224 OID_PKCS1 "\x0e" /**< sha224WithRSAEncryption ::= { pkcs-1 14 } */ +#define OID_PKCS1_SHA256 OID_PKCS1 "\x0b" /**< sha256WithRSAEncryption ::= { pkcs-1 11 } */ +#define OID_PKCS1_SHA384 OID_PKCS1 "\x0c" /**< sha384WithRSAEncryption ::= { pkcs-1 12 } */ +#define OID_PKCS1_SHA512 OID_PKCS1 "\x0d" /**< sha512WithRSAEncryption ::= { pkcs-1 13 } */ + +#define OID_RSA_SHA_OBS "\x2B\x0E\x03\x02\x1D" + +#define OID_PKCS9_EMAIL OID_PKCS9 "\x01" /**< emailAddress AttributeType ::= { pkcs-9 1 } */ + +/* RFC 4055 */ +#define OID_RSASSA_PSS OID_PKCS1 "\x0a" /**< id-RSASSA-PSS ::= { pkcs-1 10 } */ +#define OID_MGF1 OID_PKCS1 "\x08" /**< id-mgf1 ::= { pkcs-1 8 } */ + +/* + * Digest algorithms + */ +#define OID_DIGEST_ALG_MD2 OID_RSA_COMPANY "\x02\x02" /**< id-md2 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 2 } */ +#define OID_DIGEST_ALG_MD4 OID_RSA_COMPANY "\x02\x04" /**< id-md4 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 4 } */ +#define OID_DIGEST_ALG_MD5 OID_RSA_COMPANY "\x02\x05" /**< id-md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } */ +#define OID_DIGEST_ALG_SHA1 OID_ISO_IDENTIFIED_ORG OID_OIW_SECSIG_SHA1 /**< id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } */ +#define OID_DIGEST_ALG_SHA224 OID_GOV "\x03\x04\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */ +#define OID_DIGEST_ALG_SHA256 OID_GOV "\x03\x04\x02\x01" /**< id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */ + +#define OID_DIGEST_ALG_SHA384 OID_GOV "\x03\x04\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */ + +#define OID_DIGEST_ALG_SHA512 OID_GOV "\x03\x04\x02\x03" /**< id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */ + +#define OID_HMAC_SHA1 OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */ + +/* + * Encryption algorithms + */ +#define OID_DES_CBC OID_ISO_IDENTIFIED_ORG OID_OIW_SECSIG_ALG "\x07" /**< desCBC OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 7 } */ +#define OID_DES_EDE3_CBC OID_RSA_COMPANY "\x03\x07" /**< des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) -- us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } */ + +/* + * PKCS#5 OIDs + */ +#define OID_PKCS5_PBKDF2 OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */ +#define OID_PKCS5_PBES2 OID_PKCS5 "\x0d" /**< id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} */ +#define OID_PKCS5_PBMAC1 OID_PKCS5 "\x0e" /**< id-PBMAC1 OBJECT IDENTIFIER ::= {pkcs-5 14} */ + +/* + * PKCS#5 PBES1 algorithms + */ +#define OID_PKCS5_PBE_MD2_DES_CBC OID_PKCS5 "\x01" /**< pbeWithMD2AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 1} */ +#define OID_PKCS5_PBE_MD2_RC2_CBC OID_PKCS5 "\x04" /**< pbeWithMD2AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 4} */ +#define OID_PKCS5_PBE_MD5_DES_CBC OID_PKCS5 "\x03" /**< pbeWithMD5AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 3} */ +#define OID_PKCS5_PBE_MD5_RC2_CBC OID_PKCS5 "\x06" /**< pbeWithMD5AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 6} */ +#define OID_PKCS5_PBE_SHA1_DES_CBC OID_PKCS5 "\x0a" /**< pbeWithSHA1AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 10} */ +#define OID_PKCS5_PBE_SHA1_RC2_CBC OID_PKCS5 "\x0b" /**< pbeWithSHA1AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 11} */ + +/* + * PKCS#8 OIDs + */ +#define OID_PKCS9_CSR_EXT_REQ OID_PKCS9 "\x0e" /**< extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14} */ + +/* + * PKCS#12 PBE OIDs + */ +#define OID_PKCS12_PBE OID_PKCS12 "\x01" /**< pkcs-12PbeIds OBJECT IDENTIFIER ::= {pkcs-12 1} */ + +#define OID_PKCS12_PBE_SHA1_RC4_128 OID_PKCS12_PBE "\x01" /**< pbeWithSHAAnd128BitRC4 OBJECT IDENTIFIER ::= {pkcs-12PbeIds 1} */ +#define OID_PKCS12_PBE_SHA1_RC4_40 OID_PKCS12_PBE "\x02" /**< pbeWithSHAAnd40BitRC4 OBJECT IDENTIFIER ::= {pkcs-12PbeIds 2} */ +#define OID_PKCS12_PBE_SHA1_DES3_EDE_CBC OID_PKCS12_PBE "\x03" /**< pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3} */ +#define OID_PKCS12_PBE_SHA1_DES2_EDE_CBC OID_PKCS12_PBE "\x04" /**< pbeWithSHAAnd2-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 4} */ +#define OID_PKCS12_PBE_SHA1_RC2_128_CBC OID_PKCS12_PBE "\x05" /**< pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 5} */ +#define OID_PKCS12_PBE_SHA1_RC2_40_CBC OID_PKCS12_PBE "\x06" /**< pbeWithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 6} */ + +/* + * EC key algorithms from RFC 5480 + */ + +/* id-ecPublicKey OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } */ +#define OID_EC_ALG_UNRESTRICTED OID_ANSI_X9_62 "\x02\01" + +/* id-ecDH OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) + * schemes(1) ecdh(12) } */ +#define OID_EC_ALG_ECDH OID_CERTICOM "\x01\x0c" + +/* + * ECParameters namedCurve identifiers, from RFC 5480, RFC 5639, and SEC2 + */ + +/* secp192r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 1 } */ +#define OID_EC_GRP_SECP192R1 OID_ANSI_X9_62 "\x03\x01\x01" + +/* secp224r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 33 } */ +#define OID_EC_GRP_SECP224R1 OID_CERTICOM "\x00\x21" + +/* secp256r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 7 } */ +#define OID_EC_GRP_SECP256R1 OID_ANSI_X9_62 "\x03\x01\x07" + +/* secp384r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 34 } */ +#define OID_EC_GRP_SECP384R1 OID_CERTICOM "\x00\x22" + +/* secp521r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 35 } */ +#define OID_EC_GRP_SECP521R1 OID_CERTICOM "\x00\x23" + +/* secp192k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 31 } */ +#define OID_EC_GRP_SECP192K1 OID_CERTICOM "\x00\x1f" + +/* secp224k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 32 } */ +#define OID_EC_GRP_SECP224K1 OID_CERTICOM "\x00\x20" + +/* secp256k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 10 } */ +#define OID_EC_GRP_SECP256K1 OID_CERTICOM "\x00\x0a" + +/* RFC 5639 4.1 + * ecStdCurvesAndGeneration OBJECT IDENTIFIER::= {iso(1) + * identified-organization(3) teletrust(36) algorithm(3) signature- + * algorithm(3) ecSign(2) 8} + * ellipticCurve OBJECT IDENTIFIER ::= {ecStdCurvesAndGeneration 1} + * versionOne OBJECT IDENTIFIER ::= {ellipticCurve 1} */ +#define OID_EC_BRAINPOOL_V1 OID_TELETRUST "\x03\x03\x02\x08\x01\x01" + +/* brainpoolP256r1 OBJECT IDENTIFIER ::= {versionOne 7} */ +#define OID_EC_GRP_BP256R1 OID_EC_BRAINPOOL_V1 "\x07" + +/* brainpoolP384r1 OBJECT IDENTIFIER ::= {versionOne 11} */ +#define OID_EC_GRP_BP384R1 OID_EC_BRAINPOOL_V1 "\x0B" + +/* brainpoolP512r1 OBJECT IDENTIFIER ::= {versionOne 13} */ +#define OID_EC_GRP_BP512R1 OID_EC_BRAINPOOL_V1 "\x0D" + +/* + * SEC1 C.1 + * + * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + * id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1)} + */ +#define OID_ANSI_X9_62_FIELD_TYPE OID_ANSI_X9_62 "\x01" +#define OID_ANSI_X9_62_PRIME_FIELD OID_ANSI_X9_62_FIELD_TYPE "\x01" + +/* + * ECDSA signature identifiers, from RFC 5480 + */ +#define OID_ANSI_X9_62_SIG OID_ANSI_X9_62 "\x04" /* signatures(4) */ +#define OID_ANSI_X9_62_SIG_SHA2 OID_ANSI_X9_62_SIG "\x03" /* ecdsa-with-SHA2(3) */ + +/* ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) 1 } */ +#define OID_ECDSA_SHA1 OID_ANSI_X9_62_SIG "\x01" + +/* ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 1 } */ +#define OID_ECDSA_SHA224 OID_ANSI_X9_62_SIG_SHA2 "\x01" + +/* ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 2 } */ +#define OID_ECDSA_SHA256 OID_ANSI_X9_62_SIG_SHA2 "\x02" + +/* ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 3 } */ +#define OID_ECDSA_SHA384 OID_ANSI_X9_62_SIG_SHA2 "\x03" + +/* ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 4 } */ +#define OID_ECDSA_SHA512 OID_ANSI_X9_62_SIG_SHA2 "\x04" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Base OID descriptor structure + */ +typedef struct { + const char *asn1; /*!< OID ASN.1 representation */ + size_t asn1_len; /*!< length of asn1 */ + const char *name; /*!< official name (e.g. from RFC) */ + const char *description; /*!< human friendly description */ +} oid_descriptor_t; + +/** + * \brief Translate an ASN.1 OID into its numeric representation + * (e.g. "\x2A\x86\x48\x86\xF7\x0D" into "1.2.840.113549") + * + * \param buf buffer to put representation in + * \param size size of the buffer + * \param oid OID to translate + * + * \return Length of the string written (excluding final NULL) or + * POLARSSL_ERR_OID_BUF_TO_SMALL in case of error + */ +int oid_get_numeric_string( char *buf, size_t size, const asn1_buf *oid ); + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +/** + * \brief Translate an X.509 extension OID into local values + * + * \param oid OID to use + * \param ext_type place to store the extension type + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_x509_ext_type( const asn1_buf *oid, int *ext_type ); +#endif + +/** + * \brief Translate an X.509 attribute type OID into the short name + * (e.g. the OID for an X520 Common Name into "CN") + * + * \param oid OID to use + * \param short_name place to store the string pointer + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_attr_short_name( const asn1_buf *oid, const char **short_name ); + +/** + * \brief Translate PublicKeyAlgorithm OID into pk_type + * + * \param oid OID to use + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_pk_alg( const asn1_buf *oid, pk_type_t *pk_alg ); + +/** + * \brief Translate pk_type into PublicKeyAlgorithm OID + * + * \param pk_alg Public key type to look for + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_oid_by_pk_alg( pk_type_t pk_alg, + const char **oid, size_t *olen ); + +#if defined(POLARSSL_ECP_C) +/** + * \brief Translate NamedCurve OID into an EC group identifier + * + * \param oid OID to use + * \param grp_id place to store group id + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_ec_grp( const asn1_buf *oid, ecp_group_id *grp_id ); + +/** + * \brief Translate EC group identifier into NamedCurve OID + * + * \param grp_id EC group identifier + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_oid_by_ec_grp( ecp_group_id grp_id, + const char **oid, size_t *olen ); +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_MD_C) +/** + * \brief Translate SignatureAlgorithm OID into md_type and pk_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_sig_alg( const asn1_buf *oid, + md_type_t *md_alg, pk_type_t *pk_alg ); + +/** + * \brief Translate SignatureAlgorithm OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_sig_alg_desc( const asn1_buf *oid, const char **desc ); + +/** + * \brief Translate md_type and pk_type into SignatureAlgorithm OID + * + * \param md_alg message digest algorithm + * \param pk_alg public key algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_oid_by_sig_alg( pk_type_t pk_alg, md_type_t md_alg, + const char **oid, size_t *olen ); + +/** + * \brief Translate hash algorithm OID into md_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_md_alg( const asn1_buf *oid, md_type_t *md_alg ); +#endif /* POLARSSL_MD_C */ + +/** + * \brief Translate Extended Key Usage OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_extended_key_usage( const asn1_buf *oid, const char **desc ); + +/** + * \brief Translate md_type into hash algorithm OID + * + * \param md_alg message digest algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_oid_by_md( md_type_t md_alg, const char **oid, size_t *olen ); + +#if defined(POLARSSL_CIPHER_C) +/** + * \brief Translate encryption algorithm OID into cipher_type + * + * \param oid OID to use + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_cipher_alg( const asn1_buf *oid, cipher_type_t *cipher_alg ); +#endif /* POLARSSL_CIPHER_C */ + +#if defined(POLARSSL_PKCS12_C) +/** + * \brief Translate PKCS#12 PBE algorithm OID into md_type and + * cipher_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or POLARSSL_ERR_OID_NOT_FOUND + */ +int oid_get_pkcs12_pbe_alg( const asn1_buf *oid, md_type_t *md_alg, + cipher_type_t *cipher_alg ); +#endif /* POLARSSL_PKCS12_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* oid.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/openssl.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/openssl.h new file mode 100644 index 0000000..b77e7da --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/openssl.h @@ -0,0 +1,140 @@ +/** + * \file openssl.h + * + * \brief OpenSSL wrapper (definitions, inline functions). + * + * Copyright (C) 2006-2010, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * OpenSSL wrapper contributed by David Barett + */ +#ifndef POLARSSL_OPENSSL_H +#define POLARSSL_OPENSSL_H + +#include "aes.h" +#include "md5.h" +#include "rsa.h" +#include "sha1.h" + +#define AES_SIZE 16 +#define AES_BLOCK_SIZE 16 +#define AES_KEY aes_context +#define MD5_CTX md5_context +#define SHA_CTX sha1_context + +#define SHA1_Init( CTX ) \ + sha1_starts( (CTX) ) +#define SHA1_Update( CTX, BUF, LEN ) \ + sha1_update( (CTX), (unsigned char *)(BUF), (LEN) ) +#define SHA1_Final( OUT, CTX ) \ + sha1_finish( (CTX), (OUT) ) + +#define MD5_Init( CTX ) \ + md5_starts( (CTX) ) +#define MD5_Update( CTX, BUF, LEN ) \ + md5_update( (CTX), (unsigned char *)(BUF), (LEN) ) +#define MD5_Final( OUT, CTX ) \ + md5_finish( (CTX), (OUT) ) + +#define AES_set_encrypt_key( KEY, KEYSIZE, CTX ) \ + aes_setkey_enc( (CTX), (KEY), (KEYSIZE) ) +#define AES_set_decrypt_key( KEY, KEYSIZE, CTX ) \ + aes_setkey_dec( (CTX), (KEY), (KEYSIZE) ) +#define AES_cbc_encrypt( INPUT, OUTPUT, LEN, CTX, IV, MODE ) \ + aes_crypt_cbc( (CTX), (MODE), (LEN), (IV), (INPUT), (OUTPUT) ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * RSA stuff follows. TODO: needs cleanup + */ +inline int __RSA_Passthrough( void *output, void *input, int size ) +{ + memcpy( output, input, size ); + return size; +} + +inline rsa_context* d2i_RSA_PUBKEY( void *ignore, unsigned char **bufptr, + int len ) +{ + unsigned char *buffer = *(unsigned char **) bufptr; + rsa_context *rsa; + + /* + * Not a general-purpose parser: only parses public key from *exactly* + * openssl genrsa -out privkey.pem 512 (or 1024) + * openssl rsa -in privkey.pem -out privatekey.der -outform der + * openssl rsa -in privkey.pem -out pubkey.der -outform der -pubout + * + * TODO: make a general-purpose parse + */ + if( ignore != 0 || ( len != 94 && len != 162 ) ) + return( 0 ); + + rsa = (rsa_context *) malloc( sizeof( rsa_rsa ) ); + if( rsa == NULL ) + return( 0 ); + + memset( rsa, 0, sizeof( rsa_context ) ); + + if( ( len == 94 && + mpi_read_binary( &rsa->N, &buffer[ 25], 64 ) == 0 && + mpi_read_binary( &rsa->E, &buffer[ 91], 3 ) == 0 ) || + ( len == 162 && + mpi_read_binary( &rsa->N, &buffer[ 29], 128 ) == 0 ) && + mpi_read_binary( &rsa->E, &buffer[159], 3 ) == 0 ) + { + /* + * key read successfully + */ + rsa->len = ( mpi_msb( &rsa->N ) + 7 ) >> 3; + return( rsa ); + } + else + { + memset( rsa, 0, sizeof( rsa_context ) ); + free( rsa ); + return( 0 ); + } +} + +#define RSA rsa_context +#define RSA_PKCS1_PADDING 1 /* ignored; always encrypt with this */ +#define RSA_size( CTX ) (CTX)->len +#define RSA_free( CTX ) rsa_free( CTX ) +#define ERR_get_error( ) "ERR_get_error() not supported" +#define RSA_blinding_off( IGNORE ) + +#define d2i_RSAPrivateKey( a, b, c ) new rsa_context /* TODO: C++ bleh */ + +inline int RSA_public_decrypt ( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { int outsize=size; if( !rsa_pkcs1_decrypt( key, RSA_PUBLIC, &outsize, input, output ) ) return outsize; else return -1; } +inline int RSA_private_decrypt( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { int outsize=size; if( !rsa_pkcs1_decrypt( key, RSA_PRIVATE, &outsize, input, output ) ) return outsize; else return -1; } +inline int RSA_public_encrypt ( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { if( !rsa_pkcs1_encrypt( key, RSA_PUBLIC, size, input, output ) ) return RSA_size(key); else return -1; } +inline int RSA_private_encrypt( int size, unsigned char* input, unsigned char* output, RSA* key, int ignore ) { if( !rsa_pkcs1_encrypt( key, RSA_PRIVATE, size, input, output ) ) return RSA_size(key); else return -1; } + +#ifdef __cplusplus +} +#endif + +#endif /* openssl.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/padlock.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/padlock.h new file mode 100644 index 0000000..3c5f725 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/padlock.h @@ -0,0 +1,109 @@ +/** + * \file padlock.h + * + * \brief VIA PadLock ACE for HW encryption/decryption supported by some + * processors + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PADLOCK_H +#define POLARSSL_PADLOCK_H + +#include "aes.h" + +#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x0030 /**< Input data should be aligned. */ + +#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) + +#ifndef POLARSSL_HAVE_X86 +#define POLARSSL_HAVE_X86 +#endif + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef INT32 int32_t; +#else +#include +#endif + + +#define PADLOCK_RNG 0x000C +#define PADLOCK_ACE 0x00C0 +#define PADLOCK_PHE 0x0C00 +#define PADLOCK_PMM 0x3000 + +#define PADLOCK_ALIGN16(x) (uint32_t *) (16 + ((int32_t) x & ~15)) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PadLock detection routine + * + * \param feature The feature to detect + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int padlock_supports( int feature ); + +/** + * \brief PadLock AES-ECB block en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ); + +/** + * \brief PadLock AES-CBC buffer en(de)cryption + * + * \param ctx AES context + * \param mode AES_ENCRYPT or AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if success, 1 if operation failed + */ +int padlock_xcryptcbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_X86 */ + +#endif /* padlock.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pbkdf2.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pbkdf2.h new file mode 100644 index 0000000..5ccb2fa --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pbkdf2.h @@ -0,0 +1,82 @@ +/** + * \file pbkdf2.h + * + * \brief Password-Based Key Derivation Function 2 (from PKCS#5) + * DEPRECATED: use pkcs5.h instead. + * + * \author Mathias Olsson + * + * Copyright (C) 2006-2012, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PBKDF2_H +#define POLARSSL_PBKDF2_H + +#include + +#include "md.h" + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_PBKDF2_BAD_INPUT_DATA -0x007C /**< Bad input parameters to function. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PKCS#5 PBKDF2 using HMAC + * DEPRECATED: Use pkcs5_pbkdf2_hmac() instead! + * + * \param ctx Generic HMAC context + * \param password Password to use when generating key + * \param plen Length of password + * \param salt Salt to use when generating key + * \param slen Length of salt + * \param iteration_count Iteration count + * \param key_length Length of generated key + * \param output Generated key. Must be at least as big as key_length + * + * \returns 0 on success, or a PolarSSL error code if verification fails. + */ +int pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output ); + +/** + * \brief Checkup routine + * DEPRECATED: Use pkcs5_self_test() instead! + * + * \return 0 if successful, or 1 if the test failed + */ +int pbkdf2_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* pbkdf2.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pem.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pem.h new file mode 100644 index 0000000..e606cf0 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pem.h @@ -0,0 +1,133 @@ +/** + * \file pem.h + * + * \brief Privacy Enhanced Mail (PEM) decoding + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PEM_H +#define POLARSSL_PEM_H + +#include + +/** + * \name PEM Error codes + * These error codes are returned in case of errors reading the + * PEM data. + * \{ + */ +#define POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT -0x1080 /**< No PEM header or footer found. */ +#define POLARSSL_ERR_PEM_INVALID_DATA -0x1100 /**< PEM string is not as expected. */ +#define POLARSSL_ERR_PEM_MALLOC_FAILED -0x1180 /**< Failed to allocate memory. */ +#define POLARSSL_ERR_PEM_INVALID_ENC_IV -0x1200 /**< RSA IV is not in hex-format. */ +#define POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG -0x1280 /**< Unsupported key encryption algorithm. */ +#define POLARSSL_ERR_PEM_PASSWORD_REQUIRED -0x1300 /**< Private key password can't be empty. */ +#define POLARSSL_ERR_PEM_PASSWORD_MISMATCH -0x1380 /**< Given private key password does not allow for correct decryption. */ +#define POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE -0x1400 /**< Unavailable feature, e.g. hashing/encryption combination. */ +#define POLARSSL_ERR_PEM_BAD_INPUT_DATA -0x1480 /**< Bad input parameters to function. */ +/* \} name */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(POLARSSL_PEM_PARSE_C) +/** + * \brief PEM context structure + */ +typedef struct +{ + unsigned char *buf; /*!< buffer for decoded data */ + size_t buflen; /*!< length of the buffer */ + unsigned char *info; /*!< buffer for extra header information */ +} +pem_context; + +/** + * \brief PEM context setup + * + * \param ctx context to be initialized + */ +void pem_init( pem_context *ctx ); + +/** + * \brief Read a buffer for PEM information and store the resulting + * data into the specified context buffers. + * + * \param ctx context to use + * \param header header string to seek and expect + * \param footer footer string to seek and expect + * \param data source data to look in + * \param pwd password for decryption (can be NULL) + * \param pwdlen length of password + * \param use_len destination for total length used (set after header is + * correctly read, so unless you get + * POLARSSL_ERR_PEM_BAD_INPUT_DATA or + * POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT, use_len is + * the length to skip) + * + * \note Attempts to check password correctness by verifying if + * the decrypted text starts with an ASN.1 sequence of + * appropriate length + * + * \return 0 on success, or a specific PEM error code + */ +int pem_read_buffer( pem_context *ctx, const char *header, const char *footer, + const unsigned char *data, + const unsigned char *pwd, + size_t pwdlen, size_t *use_len ); + +/** + * \brief PEM context memory freeing + * + * \param ctx context to be freed + */ +void pem_free( pem_context *ctx ); +#endif /* POLARSSL_PEM_PARSE_C */ + +#if defined(POLARSSL_PEM_WRITE_C) +/** + * \brief Write a buffer of PEM information from a DER encoded + * buffer. + * + * \param header header string to write + * \param footer footer string to write + * \param der_data DER data to write + * \param der_len length of the DER data + * \param buf buffer to write to + * \param buf_len length of output buffer + * \param olen total length written / required (if buf_len is not enough) + * + * \return 0 on success, or a specific PEM or BASE64 error code. On + * POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL olen is the required + * size. + */ +int pem_write_buffer( const char *header, const char *footer, + const unsigned char *der_data, size_t der_len, + unsigned char *buf, size_t buf_len, size_t *olen ); +#endif /* POLARSSL_PEM_WRITE_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* pem.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk.h new file mode 100644 index 0000000..754dda2 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk.h @@ -0,0 +1,632 @@ +/** + * \file pk.h + * + * \brief Public Key abstraction layer + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef POLARSSL_PK_H +#define POLARSSL_PK_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "md.h" + +#if defined(POLARSSL_RSA_C) +#include "rsa.h" +#endif + +#if defined(POLARSSL_ECP_C) +#include "ecp.h" +#endif + +#if defined(POLARSSL_ECDSA_C) +#include "ecdsa.h" +#endif + +#define POLARSSL_ERR_PK_MALLOC_FAILED -0x2F80 /**< Memory alloation failed. */ +#define POLARSSL_ERR_PK_TYPE_MISMATCH -0x2F00 /**< Type mismatch, eg attempt to encrypt with an ECDSA key */ +#define POLARSSL_ERR_PK_BAD_INPUT_DATA -0x2E80 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_PK_FILE_IO_ERROR -0x2E00 /**< Read/write of file failed. */ +#define POLARSSL_ERR_PK_KEY_INVALID_VERSION -0x2D80 /**< Unsupported key version */ +#define POLARSSL_ERR_PK_KEY_INVALID_FORMAT -0x2D00 /**< Invalid key tag or value. */ +#define POLARSSL_ERR_PK_UNKNOWN_PK_ALG -0x2C80 /**< Key algorithm is unsupported (only RSA and EC are supported). */ +#define POLARSSL_ERR_PK_PASSWORD_REQUIRED -0x2C00 /**< Private key password can't be empty. */ +#define POLARSSL_ERR_PK_PASSWORD_MISMATCH -0x2B80 /**< Given private key password does not allow for correct decryption. */ +#define POLARSSL_ERR_PK_INVALID_PUBKEY -0x2B00 /**< The pubkey tag or value is invalid (only RSA and EC are supported). */ +#define POLARSSL_ERR_PK_INVALID_ALG -0x2A80 /**< The algorithm tag or value is invalid. */ +#define POLARSSL_ERR_PK_UNKNOWN_NAMED_CURVE -0x2A00 /**< Elliptic curve is unsupported (only NIST curves are supported). */ +#define POLARSSL_ERR_PK_FEATURE_UNAVAILABLE -0x2980 /**< Unavailable feature, e.g. RSA disabled for RSA key. */ +#define POLARSSL_ERR_PK_SIG_LEN_MISMATCH -0x2000 /**< The signature is valid but its length is less than expected. */ + + +#if defined(POLARSSL_RSA_C) +/** + * Quick access to an RSA context inside a PK context. + * + * \warning You must make sure the PK context actually holds an RSA context + * before using this macro! + */ +#define pk_rsa( pk ) ( (rsa_context *) (pk).pk_ctx ) +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) +/** + * Quick access to an EC context inside a PK context. + * + * \warning You must make sure the PK context actually holds an EC context + * before using this macro! + */ +#define pk_ec( pk ) ( (ecp_keypair *) (pk).pk_ctx ) +#endif /* POLARSSL_ECP_C */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Public key types + */ +typedef enum { + POLARSSL_PK_NONE=0, + POLARSSL_PK_RSA, + POLARSSL_PK_ECKEY, + POLARSSL_PK_ECKEY_DH, + POLARSSL_PK_ECDSA, + POLARSSL_PK_RSA_ALT, + POLARSSL_PK_RSASSA_PSS, +} pk_type_t; + +/** + * \brief Options for RSASSA-PSS signature verification. + * See \c rsa_rsassa_pss_verify_ext() + */ +typedef struct +{ + md_type_t mgf1_hash_id; + int expected_salt_len; + +} pk_rsassa_pss_options; + +/** + * \brief Types for interfacing with the debug module + */ +typedef enum +{ + POLARSSL_PK_DEBUG_NONE = 0, + POLARSSL_PK_DEBUG_MPI, + POLARSSL_PK_DEBUG_ECP, +} pk_debug_type; + +/** + * \brief Item to send to the debug module + */ +typedef struct +{ + pk_debug_type type; + const char *name; + void *value; +} pk_debug_item; + +/** Maximum number of item send for debugging, plus 1 */ +#define POLARSSL_PK_DEBUG_MAX_ITEMS 3 + +/** + * \brief Public key information and operations + */ +typedef struct +{ + /** Public key type */ + pk_type_t type; + + /** Type name */ + const char *name; + + /** Get key size in bits */ + size_t (*get_size)( const void * ); + + /** Tell if the context implements this type (e.g. ECKEY can do ECDSA) */ + int (*can_do)( pk_type_t type ); + + /** Verify signature */ + int (*verify_func)( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + + /** Make signature */ + int (*sign_func)( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + + /** Decrypt message */ + int (*decrypt_func)( void *ctx, const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + + /** Encrypt message */ + int (*encrypt_func)( void *ctx, const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + + /** Allocate a new context */ + void * (*ctx_alloc_func)( void ); + + /** Free the given context */ + void (*ctx_free_func)( void *ctx ); + + /** Interface with the debug module */ + void (*debug_func)( const void *ctx, pk_debug_item *items ); + +} pk_info_t; + +/** + * \brief Public key container + */ +typedef struct +{ + const pk_info_t * pk_info; /**< Public key informations */ + void * pk_ctx; /**< Underlying public key context */ +} pk_context; + +/** + * \brief Types for RSA-alt abstraction + */ +typedef int (*pk_rsa_alt_decrypt_func)( void *ctx, int mode, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len ); +typedef int (*pk_rsa_alt_sign_func)( void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + int mode, md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig ); +typedef size_t (*pk_rsa_alt_key_len_func)( void *ctx ); + +/** + * \brief Return information associated with the given PK type + * + * \param pk_type PK type to search for. + * + * \return The PK info associated with the type or NULL if not found. + */ +const pk_info_t *pk_info_from_type( pk_type_t pk_type ); + +/** + * \brief Initialize a pk_context (as NONE) + */ +void pk_init( pk_context *ctx ); + +/** + * \brief Free a pk_context + */ +void pk_free( pk_context *ctx ); + +/** + * \brief Initialize a PK context with the information given + * and allocates the type-specific PK subcontext. + * + * \param ctx Context to initialize. Must be empty (type NONE). + * \param info Information to use + * + * \return 0 on success, + * POLARSSL_ERR_PK_BAD_INPUT_DATA on invalid input, + * POLARSSL_ERR_PK_MALLOC_FAILED on allocation failure. + * + * \note For contexts holding an RSA-alt key, use + * \c pk_init_ctx_rsa_alt() instead. + */ +int pk_init_ctx( pk_context *ctx, const pk_info_t *info ); + +/** + * \brief Initialize an RSA-alt context + * + * \param ctx Context to initialize. Must be empty (type NONE). + * \param key RSA key pointer + * \param decrypt_func Decryption function + * \param sign_func Signing function + * \param key_len_func Function returning key length in bytes + * + * \return 0 on success, or POLARSSL_ERR_PK_BAD_INPUT_DATA if the + * context wasn't already initialized as RSA_ALT. + * + * \note This function replaces \c pk_init_ctx() for RSA-alt. + */ +int pk_init_ctx_rsa_alt( pk_context *ctx, void * key, + pk_rsa_alt_decrypt_func decrypt_func, + pk_rsa_alt_sign_func sign_func, + pk_rsa_alt_key_len_func key_len_func ); + +/** + * \brief Get the size in bits of the underlying key + * + * \param ctx Context to use + * + * \return Key size in bits, or 0 on error + */ +size_t pk_get_size( const pk_context *ctx ); + +/** + * \brief Get the length in bytes of the underlying key + * \param ctx Context to use + * + * \return Key length in bytes, or 0 on error + */ +static inline size_t pk_get_len( const pk_context *ctx ) +{ + return( ( pk_get_size( ctx ) + 7 ) / 8 ); +} + +/** + * \brief Tell if a context can do the operation given by type + * + * \param ctx Context to test + * \param type Target type + * + * \return 0 if context can't do the operations, + * 1 otherwise. + */ +int pk_can_do( pk_context *ctx, pk_type_t type ); + +/** + * \brief Verify signature (including padding if relevant). + * + * \param ctx PK context to use + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * POLARSSL_ERR_PK_SIG_LEN_MISMATCH if the signature is + * valid but its actual length is less than sig_len, + * or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * Use \c pk_verify_ext( POLARSSL_PK_RSASSA_PSS, ... ) + * to verify RSASSA_PSS signatures. + * + * \note If hash_len is 0, then the length associated with md_alg + * is used instead, or an error returned if it is invalid. + * + * \note md_alg may be POLARSSL_MD_NONE, only if hash_len != 0 + */ +int pk_verify( pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + +/** + * \brief Verify signature, with options. + * (Includes verification of the padding depending on type.) + * + * \param type Signature type (inc. possible padding type) to verify + * \param options Pointer to type-specific options, or NULL + * \param ctx PK context to use + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * POLARSSL_ERR_PK_TYPE_MISMATCH if the PK context can't be + * used for this type of signatures, + * POLARSSL_ERR_PK_SIG_LEN_MISMATCH if the signature is + * valid but its actual length is less than sig_len, + * or a specific error code. + * + * \note If hash_len is 0, then the length associated with md_alg + * is used instead, or an error returned if it is invalid. + * + * \note md_alg may be POLARSSL_MD_NONE, only if hash_len != 0 + * + * \note If type is POLARSSL_PK_RSASSA_PSS, then options must point + * to a pk_rsassa_pss_options structure, + * otherwise it must be NULL. + */ +int pk_verify_ext( pk_type_t type, const void *options, + pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + +/** + * \brief Make signature, including padding if relevant. + * + * \param ctx PK context to use + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Place to write the signature + * \param sig_len Number of bytes written + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \return 0 on success, or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * There is no interface in the PK module to make RSASSA-PSS + * signatures yet. + * + * \note If hash_len is 0, then the length associated with md_alg + * is used instead, or an error returned if it is invalid. + * + * \note md_alg may be POLARSSL_MD_NONE, only if hash_len != 0 + */ +int pk_sign( pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Decrypt message (including padding if relevant). + * + * \param ctx PK context to use + * \param input Input to decrypt + * \param ilen Input size + * \param output Decrypted output + * \param olen Decrypted message length + * \param osize Size of the output buffer + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int pk_decrypt( pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Encrypt message (including padding if relevant). + * + * \param ctx PK context to use + * \param input Message to encrypt + * \param ilen Message size + * \param output Encrypted output + * \param olen Encrypted output length + * \param osize Size of the output buffer + * \param f_rng RNG function + * \param p_rng RNG parameter + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int pk_encrypt( pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +/** + * \brief Export debug information + * + * \param ctx Context to use + * \param items Place to write debug items + * + * \return 0 on success or POLARSSL_ERR_PK_BAD_INPUT_DATA + */ +int pk_debug( const pk_context *ctx, pk_debug_item *items ); + +/** + * \brief Access the type name + * + * \param ctx Context to use + * + * \return Type name on success, or "invalid PK" + */ +const char * pk_get_name( const pk_context *ctx ); + +/** + * \brief Get the key type + * + * \param ctx Context to use + * + * \return Type on success, or POLARSSL_PK_NONE + */ +pk_type_t pk_get_type( const pk_context *ctx ); + +#if defined(POLARSSL_PK_PARSE_C) +/** \ingroup pk_module */ +/** + * \brief Parse a private key + * + * \param ctx key to be initialized + * \param key input buffer + * \param keylen size of the buffer + * \param pwd password for decryption (optional) + * \param pwdlen size of the password + * + * \note On entry, ctx must be empty, either freshly initialised + * with pk_init() or reset with pk_free(). If you need a + * specific key type, check the result with pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int pk_parse_key( pk_context *ctx, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen ); + +/** \ingroup pk_module */ +/** + * \brief Parse a public key + * + * \param ctx key to be initialized + * \param key input buffer + * \param keylen size of the buffer + * + * \note On entry, ctx must be empty, either freshly initialised + * with pk_init() or reset with pk_free(). If you need a + * specific key type, check the result with pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int pk_parse_public_key( pk_context *ctx, + const unsigned char *key, size_t keylen ); + +#if defined(POLARSSL_FS_IO) +/** \ingroup pk_module */ +/** + * \brief Load and parse a private key + * + * \param ctx key to be initialized + * \param path filename to read the private key from + * \param password password to decrypt the file (can be NULL) + * + * \note On entry, ctx must be empty, either freshly initialised + * with pk_init() or reset with pk_free(). If you need a + * specific key type, check the result with pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int pk_parse_keyfile( pk_context *ctx, + const char *path, const char *password ); + +/** \ingroup pk_module */ +/** + * \brief Load and parse a public key + * + * \param ctx key to be initialized + * \param path filename to read the private key from + * + * \note On entry, ctx must be empty, either freshly initialised + * with pk_init() or reset with pk_free(). If you need a + * specific key type, check the result with pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int pk_parse_public_keyfile( pk_context *ctx, const char *path ); +#endif /* POLARSSL_FS_IO */ +#endif /* POLARSSL_PK_PARSE_C */ + +#if defined(POLARSSL_PK_WRITE_C) +/** + * \brief Write a private key to a PKCS#1 or SEC1 DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx private to write away + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int pk_write_key_der( pk_context *ctx, unsigned char *buf, size_t size ); + +/** + * \brief Write a public key to a SubjectPublicKeyInfo DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx public key to write away + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int pk_write_pubkey_der( pk_context *ctx, unsigned char *buf, size_t size ); + +#if defined(POLARSSL_PEM_WRITE_C) +/** + * \brief Write a public key to a PEM string + * + * \param ctx public key to write away + * \param buf buffer to write to + * \param size size of the buffer + * + * \return 0 successful, or a specific error code + */ +int pk_write_pubkey_pem( pk_context *ctx, unsigned char *buf, size_t size ); + +/** + * \brief Write a private key to a PKCS#1 or SEC1 PEM string + * + * \param ctx private to write away + * \param buf buffer to write to + * \param size size of the buffer + * + * \return 0 successful, or a specific error code + */ +int pk_write_key_pem( pk_context *ctx, unsigned char *buf, size_t size ); +#endif /* POLARSSL_PEM_WRITE_C */ +#endif /* POLARSSL_PK_WRITE_C */ + +/* + * WARNING: Low-level functions. You probably do not want to use these unless + * you are certain you do ;) + */ + +#if defined(POLARSSL_PK_PARSE_C) +/** + * \brief Parse a SubjectPublicKeyInfo DER structure + * + * \param p the position in the ASN.1 data + * \param end end of the buffer + * \param pk the key to fill + * + * \return 0 if successful, or a specific PK error code + */ +int pk_parse_subpubkey( unsigned char **p, const unsigned char *end, + pk_context *pk ); +#endif /* POLARSSL_PK_PARSE_C */ + +#if defined(POLARSSL_PK_WRITE_C) +/** + * \brief Write a subjectPublicKey to ASN.1 data + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param key public key to write away + * + * \return the length written or a negative error code + */ +int pk_write_pubkey( unsigned char **p, unsigned char *start, + const pk_context *key ); +#endif /* POLARSSL_PK_WRITE_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_PK_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk_wrap.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk_wrap.h new file mode 100644 index 0000000..7baafb9 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pk_wrap.h @@ -0,0 +1,63 @@ +/** + * \file pk.h + * + * \brief Public Key abstraction layer: wrapper functions + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef POLARSSL_PK_WRAP_H +#define POLARSSL_PK_WRAP_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "pk.h" + +/* Container for RSA-alt */ +typedef struct +{ + void *key; + pk_rsa_alt_decrypt_func decrypt_func; + pk_rsa_alt_sign_func sign_func; + pk_rsa_alt_key_len_func key_len_func; +} rsa_alt_context; + +#if defined(POLARSSL_RSA_C) +extern const pk_info_t rsa_info; +#endif + +#if defined(POLARSSL_ECP_C) +extern const pk_info_t eckey_info; +extern const pk_info_t eckeydh_info; +#endif + +#if defined(POLARSSL_ECDSA_C) +extern const pk_info_t ecdsa_info; +#endif + +extern const pk_info_t rsa_alt_info; + +#endif /* POLARSSL_PK_WRAP_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs11.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs11.h new file mode 100644 index 0000000..84f862d --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs11.h @@ -0,0 +1,174 @@ +/** + * \file pkcs11.h + * + * \brief Wrapper for PKCS#11 library libpkcs11-helper + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PKCS11_H +#define POLARSSL_PKCS11_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PKCS11_C) + +#include "x509_crt.h" + +#include + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Context for PKCS #11 private keys. + */ +typedef struct { + pkcs11h_certificate_t pkcs11h_cert; + int len; +} pkcs11_context; + +/** + * Fill in a PolarSSL certificate, based on the given PKCS11 helper certificate. + * + * \param cert X.509 certificate to fill + * \param pkcs11h_cert PKCS #11 helper certificate + * + * \return 0 on success. + */ +int pkcs11_x509_cert_init( x509_crt *cert, pkcs11h_certificate_t pkcs11h_cert ); + +/** + * Initialise a pkcs11_context, storing the given certificate. Note that the + * pkcs11_context will take over control of the certificate, freeing it when + * done. + * + * \param priv_key Private key structure to fill. + * \param pkcs11_cert PKCS #11 helper certificate + * + * \return 0 on success + */ +int pkcs11_priv_key_init( pkcs11_context *priv_key, + pkcs11h_certificate_t pkcs11_cert ); + +/** + * Free the contents of the given private key context. Note that the structure + * itself is not freed. + * + * \param priv_key Private key structure to cleanup + */ +void pkcs11_priv_key_free( pkcs11_context *priv_key ); + +/** + * \brief Do an RSA private key decrypt, then remove the message + * padding + * + * \param ctx PKCS #11 context + * \param mode must be RSA_PRIVATE, for compatibility with rsa.c's signature + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param olen will contain the plaintext length + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int pkcs11_decrypt( pkcs11_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Do a private RSA to sign a message digest + * + * \param ctx PKCS #11 context + * \param mode must be RSA_PRIVATE, for compatibility with rsa.c's signature + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int pkcs11_sign( pkcs11_context *ctx, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * SSL/TLS wrappers for PKCS#11 functions + */ +static inline int ssl_pkcs11_decrypt( void *ctx, int mode, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len ) +{ + return pkcs11_decrypt( (pkcs11_context *) ctx, mode, olen, input, output, + output_max_len ); +} + +static inline int ssl_pkcs11_sign( void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + int mode, md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig ) +{ + ((void) f_rng); + ((void) p_rng); + return pkcs11_sign( (pkcs11_context *) ctx, mode, md_alg, + hashlen, hash, sig ); +} + +static inline size_t ssl_pkcs11_key_len( void *ctx ) +{ + return ( (pkcs11_context *) ctx )->len; +} + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_PKCS11_C */ + +#endif /* POLARSSL_PKCS11_H */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs12.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs12.h new file mode 100644 index 0000000..4bd5018 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs12.h @@ -0,0 +1,123 @@ +/** + * \file pkcs12.h + * + * \brief PKCS#12 Personal Information Exchange Syntax + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PKCS12_H +#define POLARSSL_PKCS12_H + +#include + +#include "md.h" +#include "cipher.h" +#include "asn1.h" + +#define POLARSSL_ERR_PKCS12_BAD_INPUT_DATA -0x1F80 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE -0x1F00 /**< Feature not available, e.g. unsupported encryption scheme. */ +#define POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT -0x1E80 /**< PBE ASN.1 data not as expected. */ +#define POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH -0x1E00 /**< Given private key password does not allow for correct decryption. */ + +#define PKCS12_DERIVE_KEY 1 /**< encryption/decryption key */ +#define PKCS12_DERIVE_IV 2 /**< initialization vector */ +#define PKCS12_DERIVE_MAC_KEY 3 /**< integrity / MAC key */ + +#define PKCS12_PBE_DECRYPT 0 +#define PKCS12_PBE_ENCRYPT 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PKCS12 Password Based function (encryption / decryption) + * for pbeWithSHAAnd128BitRC4 + * + * \param pbe_params an ASN1 buffer containing the pkcs-12PbeParams structure + * \param mode either PKCS12_PBE_ENCRYPT or PKCS12_PBE_DECRYPT + * \param pwd the password used (may be NULL if no password is used) + * \param pwdlen length of the password (may be 0) + * \param input the input data + * \param len data length + * \param output the output buffer + * + * \return 0 if successful, or a PolarSSL error code + */ +int pkcs12_pbe_sha1_rc4_128( asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *input, size_t len, + unsigned char *output ); + +/** + * \brief PKCS12 Password Based function (encryption / decryption) + * for cipher-based and md-based PBE's + * + * \param pbe_params an ASN1 buffer containing the pkcs-12PbeParams structure + * \param mode either PKCS12_PBE_ENCRYPT or PKCS12_PBE_DECRYPT + * \param cipher_type the cipher used + * \param md_type the md used + * \param pwd the password used (may be NULL if no password is used) + * \param pwdlen length of the password (may be 0) + * \param input the input data + * \param len data length + * \param output the output buffer + * + * \return 0 if successful, or a PolarSSL error code + */ +int pkcs12_pbe( asn1_buf *pbe_params, int mode, + cipher_type_t cipher_type, md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *input, size_t len, + unsigned char *output ); + +/** + * \brief The PKCS#12 derivation function uses a password and a salt + * to produce pseudo-random bits for a particular "purpose". + * + * Depending on the given id, this function can produce an + * encryption/decryption key, an nitialization vector or an + * integrity key. + * + * \param data buffer to store the derived data in + * \param datalen length to fill + * \param pwd password to use (may be NULL if no password is used) + * \param pwdlen length of the password (may be 0) + * \param salt salt buffer to use + * \param saltlen length of the salt + * \param md md type to use during the derivation + * \param id id that describes the purpose (can be PKCS12_DERIVE_KEY, + * PKCS12_DERIVE_IV or PKCS12_DERIVE_MAC_KEY) + * \param iterations number of iterations + * + * \return 0 if successful, or a MD, BIGNUM type error. + */ +int pkcs12_derivation( unsigned char *data, size_t datalen, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *salt, size_t saltlen, + md_type_t md, int id, int iterations ); + +#ifdef __cplusplus +} +#endif + +#endif /* pkcs12.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs5.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs5.h new file mode 100644 index 0000000..34e824b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/pkcs5.h @@ -0,0 +1,104 @@ +/** + * \file pkcs5.h + * + * \brief PKCS#5 functions + * + * \author Mathias Olsson + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PKCS5_H +#define POLARSSL_PKCS5_H + +#include + +#include "asn1.h" +#include "md.h" + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_PKCS5_BAD_INPUT_DATA -0x3f80 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_PKCS5_INVALID_FORMAT -0x3f00 /**< Unexpected ASN.1 data. */ +#define POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE -0x3e80 /**< Requested encryption or digest alg not available. */ +#define POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH -0x3e00 /**< Given private key password does not allow for correct decryption. */ + +#define PKCS5_DECRYPT 0 +#define PKCS5_ENCRYPT 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief PKCS#5 PBES2 function + * + * \param pbe_params the ASN.1 algorithm parameters + * \param mode either PKCS5_DECRYPT or PKCS5_ENCRYPT + * \param pwd password to use when generating key + * \param pwdlen length of password + * \param data data to process + * \param datalen length of data + * \param output output buffer + * + * \returns 0 on success, or a PolarSSL error code if verification fails. + */ +int pkcs5_pbes2( asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t datalen, + unsigned char *output ); + +/** + * \brief PKCS#5 PBKDF2 using HMAC + * + * \param ctx Generic HMAC context + * \param password Password to use when generating key + * \param plen Length of password + * \param salt Salt to use when generating key + * \param slen Length of salt + * \param iteration_count Iteration count + * \param key_length Length of generated key + * \param output Generated key. Must be at least as big as key_length + * + * \returns 0 on success, or a PolarSSL error code if verification fails. + */ +int pkcs5_pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int pkcs5_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* pkcs5.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/platform.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/platform.h new file mode 100644 index 0000000..eae887a --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/platform.h @@ -0,0 +1,128 @@ +/** + * \file platform.h + * + * \brief PolarSSL Platform abstraction layer + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_PLATFORM_H +#define POLARSSL_PLATFORM_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(POLARSSL_PLATFORM_NO_STD_FUNCTIONS) +#include +#if !defined(POLARSSL_PLATFORM_STD_PRINTF) +#define POLARSSL_PLATFORM_STD_PRINTF printf /**< Default printf to use */ +#endif +#if !defined(POLARSSL_PLATFORM_STD_FPRINTF) +#define POLARSSL_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use */ +#endif +#if !defined(POLARSSL_PLATFORM_STD_MALLOC) +#define POLARSSL_PLATFORM_STD_MALLOC malloc /**< Default allocator to use */ +#endif +#if !defined(POLARSSL_PLATFORM_STD_FREE) +#define POLARSSL_PLATFORM_STD_FREE free /**< Default free to use */ +#endif +#else /* POLARSSL_PLATFORM_NO_STD_FUNCTIONS */ +#if defined(POLARSSL_PLATFORM_STD_MEM_HDR) +#include POLARSSL_PLATFORM_STD_MEM_HDR +#endif +#endif /* POLARSSL_PLATFORM_NO_STD_FUNCTIONS */ + +/* \} name SECTION: Module settings */ + +/* + * The function pointers for malloc and free + */ +#if defined(POLARSSL_PLATFORM_MEMORY) +extern void * (*polarssl_malloc)( size_t len ); +extern void (*polarssl_free)( void *ptr ); + +/** + * \brief Set your own memory implementation function pointers + * + * \param malloc_func the malloc function implementation + * \param free_func the free function implementation + * + * \return 0 if successful + */ +int platform_set_malloc_free( void * (*malloc_func)( size_t ), + void (*free_func)( void * ) ); +#else /* POLARSSL_PLATFORM_ENTROPY */ +#define polarssl_malloc malloc +#define polarssl_free free +#endif /* POLARSSL_PLATFORM_ENTROPY */ + +/* + * The function pointers for printf + */ +#if defined(POLARSSL_PLATFORM_PRINTF_ALT) +extern int (*polarssl_printf)( const char *format, ... ); + +/** + * \brief Set your own printf function pointer + * + * \param printf_func the printf function implementation + * + * \return 0 + */ +int platform_set_printf( int (*printf_func)( const char *, ... ) ); +#else /* POLARSSL_PLATFORM_PRINTF_ALT */ +#define polarssl_printf printf +#endif /* POLARSSL_PLATFORM_PRINTF_ALT */ + +/* + * The function pointers for fprintf + */ +#if defined(POLARSSL_PLATFORM_FPRINTF_ALT) +extern int (*polarssl_fprintf)( FILE *stream, const char *format, ... ); + +int platform_set_fprintf( int (*fprintf_func)( FILE *stream, const char *, + ... ) ); +#else +#define polarssl_fprintf fprintf +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* platform.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ripemd160.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ripemd160.h new file mode 100644 index 0000000..e3b66c9 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ripemd160.h @@ -0,0 +1,204 @@ +/** + * \file ripemd160.h + * + * \brief RIPE MD-160 message digest + * + * Copyright (C) 2014-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RIPEMD160_H +#define POLARSSL_RIPEMD160_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_RIPEMD160_FILE_IO_ERROR -0x007E /**< Read/write error in file. */ + +#if !defined(POLARSSL_RIPEMD160_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief RIPEMD-160 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +ripemd160_context; + +/** + * \brief Initialize RIPEMD-160 context + * + * \param ctx RIPEMD-160 context to be initialized + */ +void ripemd160_init( ripemd160_context *ctx ); + +/** + * \brief Clear RIPEMD-160 context + * + * \param ctx RIPEMD-160 context to be cleared + */ +void ripemd160_free( ripemd160_context *ctx ); + +/** + * \brief RIPEMD-160 context setup + * + * \param ctx context to be initialized + */ +void ripemd160_starts( ripemd160_context *ctx ); + +/** + * \brief RIPEMD-160 process buffer + * + * \param ctx RIPEMD-160 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void ripemd160_update( ripemd160_context *ctx, + const unsigned char *input, size_t ilen ); + +/** + * \brief RIPEMD-160 final digest + * + * \param ctx RIPEMD-160 context + * \param output RIPEMD-160 checksum result + */ +void ripemd160_finish( ripemd160_context *ctx, unsigned char output[20] ); + +/* Internal use */ +void ripemd160_process( ripemd160_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_RIPEMD160_ALT */ +#include "ripemd160.h" +#endif /* POLARSSL_RIPEMD160_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = RIPEMD-160( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output RIPEMD-160 checksum result + */ +void ripemd160( const unsigned char *input, size_t ilen, + unsigned char output[20] ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Output = RIPEMD-160( file contents ) + * + * \param path input file name + * \param output RIPEMD-160 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_RIPEMD160_FILE_IO_ERROR + */ +int ripemd160_file( const char *path, unsigned char output[20] ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief RIPEMD-160 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void ripemd160_hmac_starts( ripemd160_context *ctx, + const unsigned char *key, size_t keylen ); + +/** + * \brief RIPEMD-160 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void ripemd160_hmac_update( ripemd160_context *ctx, + const unsigned char *input, size_t ilen ); + +/** + * \brief RIPEMD-160 HMAC final digest + * + * \param ctx HMAC context + * \param output RIPEMD-160 HMAC checksum result + */ +void ripemd160_hmac_finish( ripemd160_context *ctx, unsigned char output[20] ); + +/** + * \brief RIPEMD-160 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void ripemd160_hmac_reset( ripemd160_context *ctx ); + +/** + * \brief Output = HMAC-RIPEMD-160( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-RIPEMD-160 result + */ +void ripemd160_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20] ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int ripemd160_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* ripemd160.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/rsa.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/rsa.h new file mode 100644 index 0000000..c06c7d5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/rsa.h @@ -0,0 +1,647 @@ +/** + * \file rsa.h + * + * \brief The RSA public-key cryptosystem + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_RSA_H +#define POLARSSL_RSA_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "bignum.h" +#include "md.h" + +#if defined(POLARSSL_THREADING_C) +#include "threading.h" +#endif + +/* + * RSA Error codes + */ +#define POLARSSL_ERR_RSA_BAD_INPUT_DATA -0x4080 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_RSA_INVALID_PADDING -0x4100 /**< Input data contains invalid padding and is rejected. */ +#define POLARSSL_ERR_RSA_KEY_GEN_FAILED -0x4180 /**< Something failed during generation of a key. */ +#define POLARSSL_ERR_RSA_KEY_CHECK_FAILED -0x4200 /**< Key failed to pass the libraries validity check. */ +#define POLARSSL_ERR_RSA_PUBLIC_FAILED -0x4280 /**< The public key operation failed. */ +#define POLARSSL_ERR_RSA_PRIVATE_FAILED -0x4300 /**< The private key operation failed. */ +#define POLARSSL_ERR_RSA_VERIFY_FAILED -0x4380 /**< The PKCS#1 verification failed. */ +#define POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 /**< The output buffer for decryption is not large enough. */ +#define POLARSSL_ERR_RSA_RNG_FAILED -0x4480 /**< The random generator failed to generate non-zeros. */ + +/* + * RSA constants + */ +#define RSA_PUBLIC 0 +#define RSA_PRIVATE 1 + +#define RSA_PKCS_V15 0 +#define RSA_PKCS_V21 1 + +#define RSA_SIGN 1 +#define RSA_CRYPT 2 + +#define RSA_SALT_LEN_ANY -1 + +/* + * The above constants may be used even if the RSA module is compile out, + * eg for alternative (PKCS#11) RSA implemenations in the PK layers. + */ +#if defined(POLARSSL_RSA_C) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief RSA context structure + */ +typedef struct +{ + int ver; /*!< always 0 */ + size_t len; /*!< size(N) in chars */ + + mpi N; /*!< public modulus */ + mpi E; /*!< public exponent */ + + mpi D; /*!< private exponent */ + mpi P; /*!< 1st prime factor */ + mpi Q; /*!< 2nd prime factor */ + mpi DP; /*!< D % (P - 1) */ + mpi DQ; /*!< D % (Q - 1) */ + mpi QP; /*!< 1 / (Q % P) */ + + mpi RN; /*!< cached R^2 mod N */ + mpi RP; /*!< cached R^2 mod P */ + mpi RQ; /*!< cached R^2 mod Q */ + +#if !defined(POLARSSL_RSA_NO_CRT) + mpi Vi; /*!< cached blinding value */ + mpi Vf; /*!< cached un-blinding value */ +#endif + + int padding; /*!< RSA_PKCS_V15 for 1.5 padding and + RSA_PKCS_v21 for OAEP/PSS */ + int hash_id; /*!< Hash identifier of md_type_t as + specified in the md.h header file + for the EME-OAEP and EMSA-PSS + encoding */ +#if defined(POLARSSL_THREADING_C) + threading_mutex_t mutex; /*!< Thread-safety mutex */ +#endif +} +rsa_context; + +/** + * \brief Initialize an RSA context + * + * Note: Set padding to RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \param ctx RSA context to be initialized + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + * + * \note The hash_id parameter is actually ignored + * when using RSA_PKCS_V15 padding. + * + * \note Choice of padding mode is strictly enforced for private key + * operations, since there might be security concerns in + * mixing padding modes. For public key operations it's merely + * a default value, which can be overriden by calling specific + * rsa_rsaes_xxx or rsa_rsassa_xxx functions. + * + * \note The chosen hash is always used for OEAP encryption. + * For PSS signatures, it's always used for making signatures, + * but can be overriden (and always is, if set to + * POLARSSL_MD_NONE) for verifying them. + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id); + +/** + * \brief Set padding for an already initialized RSA context + * See \c rsa_init() for details. + * + * \param ctx RSA context to be set + * \param padding RSA_PKCS_V15 or RSA_PKCS_V21 + * \param hash_id RSA_PKCS_V21 hash identifier + */ +void rsa_set_padding( rsa_context *ctx, int padding, int hash_id); + +/** + * \brief Generate an RSA keypair + * + * \param ctx RSA context that will hold the key + * \param f_rng RNG function + * \param p_rng RNG parameter + * \param nbits size of the public key in bits + * \param exponent public exponent (e.g., 65537) + * + * \note rsa_init() must be called beforehand to setup + * the RSA context. + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ); + +/** + * \brief Check a public RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_pubkey( const rsa_context *ctx ); + +/** + * \brief Check a private RSA key + * + * \param ctx RSA context to be checked + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + */ +int rsa_check_privkey( const rsa_context *ctx ); + +/** + * \brief Do an RSA public key operation + * + * \param ctx RSA context + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note This function does NOT take care of message + * padding. Also, be sure to set input[0] = 0 or assure that + * input is smaller than N. + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Do an RSA private key operation + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for blinding) + * \param p_rng RNG parameter + * \param input input buffer + * \param output output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The input and output buffers must be large + * enough (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 encryption using the + * mode from the context. Add the message padding, then do an + * RSA operation. + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v1.5 encryption (RSAES-PKCS1-v1_5-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP encryption (RSAES-OAEP-ENCRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for padding and PKCS#1 v2.1 encoding + * and RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param ilen contains the plaintext length + * \param input buffer holding the data to be encrypted + * \param output buffer that will hold the ciphertext + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ); + +/** + * \brief Generic wrapper to perform a PKCS#1 decryption using the + * mode from the context. Do an RSA operation, then remove + * the message padding + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v1.5 decryption (RSAES-PKCS1-v1_5-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Perform a PKCS#1 v2.1 OAEP decryption (RSAES-OAEP-DECRYPT) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param label buffer holding the custom label to use + * \param label_len contains the label length + * \param olen will contain the plaintext length + * \param input buffer holding the encrypted data + * \param output buffer that will hold the plaintext + * \param output_max_len maximum length of the output buffer + * + * \return 0 if successful, or an POLARSSL_ERR_RSA_XXX error code + * + * \note The output buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used) otherwise + * an error is thrown. + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ); + +/** + * \brief Generic wrapper to perform a PKCS#1 signature using the + * mode from the context. Do a private RSA operation to sign + * a message digest + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding, see comments on + * \note \c rsa_rsassa_pss_sign() for details on md_alg and hash_id. + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 signature (RSASSA-PKCS1-v1_5-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS signature (RSASSA-PSS-SIGN) + * + * \param ctx RSA context + * \param f_rng RNG function (Needed for PKCS#1 v2.1 encoding and for + * RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer that will hold the ciphertext + * + * \return 0 if the signing operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note The hash_id in the RSA context is the one used for the + * encoding. md_alg in the function call is the type of hash + * that is encoded. According to RFC 3447 it is advised to + * keep both hashes the same. + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ); + +/** + * \brief Generic wrapper to perform a PKCS#1 verification using the + * mode from the context. Do a public RSA operation and check + * the message digest + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note In case of PKCS#1 v2.1 encoding, see comments on + * \c rsa_rsassa_pss_verify() about md_alg and hash_id. + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v1.5 verification (RSASSA-PKCS1-v1_5-VERIFY) + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * (This is the "simple" version.) + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note The hash_id in the RSA context is the one used for the + * verification. md_alg in the function call is the type of + * hash that is verified. According to RFC 3447 it is advised to + * keep both hashes the same. If hash_id in the RSA context is + * unset, the md_alg from the function call is used. + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ); + +/** + * \brief Perform a PKCS#1 v2.1 PSS verification (RSASSA-PSS-VERIFY) + * (This is the version with "full" options.) + * + * \param ctx points to an RSA public key + * \param f_rng RNG function (Only needed for RSA_PRIVATE) + * \param p_rng RNG parameter + * \param mode RSA_PUBLIC or RSA_PRIVATE + * \param md_alg a POLARSSL_MD_* (use POLARSSL_MD_NONE for signing raw data) + * \param hashlen message digest length (for POLARSSL_MD_NONE only) + * \param hash buffer holding the message digest + * \param mgf1_hash_id message digest used for mask generation + * \param expected_salt_len Length of the salt used in padding, use + * RSA_SALT_LEN_ANY to accept any salt length + * \param sig buffer holding the ciphertext + * + * \return 0 if the verify operation was successful, + * or an POLARSSL_ERR_RSA_XXX error code + * + * \note The "sig" buffer must be as large as the size + * of ctx->N (eg. 128 bytes if RSA-1024 is used). + * + * \note The hash_id in the RSA context is ignored. + */ +int rsa_rsassa_pss_verify_ext( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + md_type_t mgf1_hash_id, + int expected_salt_len, + const unsigned char *sig ); + +/** + * \brief Copy the components of an RSA context + * + * \param dst Destination context + * \param src Source context + * + * \return O on success, + * POLARSSL_ERR_MPI_MALLOC_FAILED on memory allocation failure + */ +int rsa_copy( rsa_context *dst, const rsa_context *src ); + +/** + * \brief Free the components of an RSA key + * + * \param ctx RSA Context to free + */ +void rsa_free( rsa_context *ctx ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int rsa_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_RSA_C */ + +#endif /* rsa.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha1.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha1.h new file mode 100644 index 0000000..cb0c436 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha1.h @@ -0,0 +1,200 @@ +/** + * \file sha1.h + * + * \brief SHA-1 cryptographic hash function + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SHA1_H +#define POLARSSL_SHA1_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_SHA1_FILE_IO_ERROR -0x0076 /**< Read/write error in file. */ + +#if !defined(POLARSSL_SHA1_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-1 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ +} +sha1_context; + +/** + * \brief Initialize SHA-1 context + * + * \param ctx SHA-1 context to be initialized + */ +void sha1_init( sha1_context *ctx ); + +/** + * \brief Clear SHA-1 context + * + * \param ctx SHA-1 context to be cleared + */ +void sha1_free( sha1_context *ctx ); + +/** + * \brief SHA-1 context setup + * + * \param ctx context to be initialized + */ +void sha1_starts( sha1_context *ctx ); + +/** + * \brief SHA-1 process buffer + * + * \param ctx SHA-1 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); + +/** + * \brief SHA-1 final digest + * + * \param ctx SHA-1 context + * \param output SHA-1 checksum result + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ); + +/* Internal use */ +void sha1_process( sha1_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_SHA1_ALT */ +#include "sha1_alt.h" +#endif /* POLARSSL_SHA1_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = SHA-1( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-1 checksum result + */ +void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ); + +/** + * \brief Output = SHA-1( file contents ) + * + * \param path input file name + * \param output SHA-1 checksum result + * + * \return 0 if successful, or POLARSSL_ERR_SHA1_FILE_IO_ERROR + */ +int sha1_file( const char *path, unsigned char output[20] ); + +/** + * \brief SHA-1 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, + size_t keylen ); + +/** + * \brief SHA-1 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief SHA-1 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-1 HMAC checksum result + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); + +/** + * \brief SHA-1 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void sha1_hmac_reset( sha1_context *ctx ); + +/** + * \brief Output = HMAC-SHA-1( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-1 result + */ +void sha1_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20] ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha1_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha1.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha256.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha256.h new file mode 100644 index 0000000..b143674 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha256.h @@ -0,0 +1,208 @@ +/** + * \file sha256.h + * + * \brief SHA-224 and SHA-256 cryptographic hash function + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SHA256_H +#define POLARSSL_SHA256_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define POLARSSL_ERR_SHA256_FILE_IO_ERROR -0x0078 /**< Read/write error in file. */ + +#if !defined(POLARSSL_SHA256_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-256 context structure + */ +typedef struct +{ + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[8]; /*!< intermediate digest state */ + unsigned char buffer[64]; /*!< data block being processed */ + + unsigned char ipad[64]; /*!< HMAC: inner padding */ + unsigned char opad[64]; /*!< HMAC: outer padding */ + int is224; /*!< 0 => SHA-256, else SHA-224 */ +} +sha256_context; + +/** + * \brief Initialize SHA-256 context + * + * \param ctx SHA-256 context to be initialized + */ +void sha256_init( sha256_context *ctx ); + +/** + * \brief Clear SHA-256 context + * + * \param ctx SHA-256 context to be cleared + */ +void sha256_free( sha256_context *ctx ); + +/** + * \brief SHA-256 context setup + * + * \param ctx context to be initialized + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha256_starts( sha256_context *ctx, int is224 ); + +/** + * \brief SHA-256 process buffer + * + * \param ctx SHA-256 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha256_update( sha256_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief SHA-256 final digest + * + * \param ctx SHA-256 context + * \param output SHA-224/256 checksum result + */ +void sha256_finish( sha256_context *ctx, unsigned char output[32] ); + +/* Internal use */ +void sha256_process( sha256_context *ctx, const unsigned char data[64] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_SHA256_ALT */ +#include "sha256_alt.h" +#endif /* POLARSSL_SHA256_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = SHA-256( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha256( const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ); + +/** + * \brief Output = SHA-256( file contents ) + * + * \param path input file name + * \param output SHA-224/256 checksum result + * \param is224 0 = use SHA256, 1 = use SHA224 + * + * \return 0 if successful, or POLARSSL_ERR_SHA256_FILE_IO_ERROR + */ +int sha256_file( const char *path, unsigned char output[32], int is224 ); + +/** + * \brief SHA-256 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha256_hmac_starts( sha256_context *ctx, const unsigned char *key, + size_t keylen, int is224 ); + +/** + * \brief SHA-256 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha256_hmac_update( sha256_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief SHA-256 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-224/256 HMAC checksum result + */ +void sha256_hmac_finish( sha256_context *ctx, unsigned char output[32] ); + +/** + * \brief SHA-256 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void sha256_hmac_reset( sha256_context *ctx ); + +/** + * \brief Output = HMAC-SHA-256( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-224/256 result + * \param is224 0 = use SHA256, 1 = use SHA224 + */ +void sha256_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha256_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha256.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha512.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha512.h new file mode 100644 index 0000000..dfbae4a --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/sha512.h @@ -0,0 +1,209 @@ +/** + * \file sha512.h + * + * \brief SHA-384 and SHA-512 cryptographic hash function + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SHA512_H +#define POLARSSL_SHA512_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) || defined(__WATCOMC__) + #define UL64(x) x##ui64 + typedef unsigned __int64 uint64_t; +#else + #include + #define UL64(x) x##ULL +#endif + +#define POLARSSL_ERR_SHA512_FILE_IO_ERROR -0x007A /**< Read/write error in file. */ + +#if !defined(POLARSSL_SHA512_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief SHA-512 context structure + */ +typedef struct +{ + uint64_t total[2]; /*!< number of bytes processed */ + uint64_t state[8]; /*!< intermediate digest state */ + unsigned char buffer[128]; /*!< data block being processed */ + + unsigned char ipad[128]; /*!< HMAC: inner padding */ + unsigned char opad[128]; /*!< HMAC: outer padding */ + int is384; /*!< 0 => SHA-512, else SHA-384 */ +} +sha512_context; + +/** + * \brief Initialize SHA-512 context + * + * \param ctx SHA-512 context to be initialized + */ +void sha512_init( sha512_context *ctx ); + +/** + * \brief Clear SHA-512 context + * + * \param ctx SHA-512 context to be cleared + */ +void sha512_free( sha512_context *ctx ); + +/** + * \brief SHA-512 context setup + * + * \param ctx context to be initialized + * \param is384 0 = use SHA512, 1 = use SHA384 + */ +void sha512_starts( sha512_context *ctx, int is384 ); + +/** + * \brief SHA-512 process buffer + * + * \param ctx SHA-512 context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha512_update( sha512_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief SHA-512 final digest + * + * \param ctx SHA-512 context + * \param output SHA-384/512 checksum result + */ +void sha512_finish( sha512_context *ctx, unsigned char output[64] ); + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_SHA512_ALT */ +#include "sha512_alt.h" +#endif /* POLARSSL_SHA512_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Output = SHA-512( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output SHA-384/512 checksum result + * \param is384 0 = use SHA512, 1 = use SHA384 + */ +void sha512( const unsigned char *input, size_t ilen, + unsigned char output[64], int is384 ); + +/** + * \brief Output = SHA-512( file contents ) + * + * \param path input file name + * \param output SHA-384/512 checksum result + * \param is384 0 = use SHA512, 1 = use SHA384 + * + * \return 0 if successful, or POLARSSL_ERR_SHA512_FILE_IO_ERROR + */ +int sha512_file( const char *path, unsigned char output[64], int is384 ); + +/** + * \brief SHA-512 HMAC context setup + * + * \param ctx HMAC context to be initialized + * \param is384 0 = use SHA512, 1 = use SHA384 + * \param key HMAC secret key + * \param keylen length of the HMAC key + */ +void sha512_hmac_starts( sha512_context *ctx, const unsigned char *key, + size_t keylen, int is384 ); + +/** + * \brief SHA-512 HMAC process buffer + * + * \param ctx HMAC context + * \param input buffer holding the data + * \param ilen length of the input data + */ +void sha512_hmac_update( sha512_context *ctx, const unsigned char *input, + size_t ilen ); + +/** + * \brief SHA-512 HMAC final digest + * + * \param ctx HMAC context + * \param output SHA-384/512 HMAC checksum result + */ +void sha512_hmac_finish( sha512_context *ctx, unsigned char output[64] ); + +/** + * \brief SHA-512 HMAC context reset + * + * \param ctx HMAC context to be reset + */ +void sha512_hmac_reset( sha512_context *ctx ); + +/** + * \brief Output = HMAC-SHA-512( hmac key, input buffer ) + * + * \param key HMAC secret key + * \param keylen length of the HMAC key + * \param input buffer holding the data + * \param ilen length of the input data + * \param output HMAC-SHA-384/512 result + * \param is384 0 = use SHA512, 1 = use SHA384 + */ +void sha512_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[64], int is384 ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int sha512_self_test( int verbose ); + +/* Internal use */ +void sha512_process( sha512_context *ctx, const unsigned char data[128] ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha512.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl.h new file mode 100644 index 0000000..bd7f1f7 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl.h @@ -0,0 +1,1809 @@ +/** + * \file ssl.h + * + * \brief SSL/TLS functions. + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SSL_H +#define POLARSSL_SSL_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#include "net.h" +#include "bignum.h" +#include "ecp.h" + +#include "ssl_ciphersuites.h" + +#if defined(POLARSSL_MD5_C) +#include "md5.h" +#endif + +#if defined(POLARSSL_SHA1_C) +#include "sha1.h" +#endif + +#if defined(POLARSSL_SHA256_C) +#include "sha256.h" +#endif + +#if defined(POLARSSL_SHA512_C) +#include "sha512.h" +#endif + +// for session tickets +#if defined(POLARSSL_AES_C) +#include "aes.h" +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) +#include "x509_crt.h" +#include "x509_crl.h" +#endif + +#if defined(POLARSSL_DHM_C) +#include "dhm.h" +#endif + +#if defined(POLARSSL_ECDH_C) +#include "ecdh.h" +#endif + +#if defined(POLARSSL_ZLIB_SUPPORT) +#include "zlib.h" +#endif + +#if defined(POLARSSL_HAVE_TIME) +#include +#endif + +/* For convenience below and in programs */ +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#define POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#define POLARSSL_KEY_EXCHANGE__SOME__ECDHE_ENABLED +#endif + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +/* + * SSL Error codes + */ +#define POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE -0x7080 /**< The requested feature is not available. */ +#define POLARSSL_ERR_SSL_BAD_INPUT_DATA -0x7100 /**< Bad input parameters to function. */ +#define POLARSSL_ERR_SSL_INVALID_MAC -0x7180 /**< Verification of the message MAC failed. */ +#define POLARSSL_ERR_SSL_INVALID_RECORD -0x7200 /**< An invalid SSL record was received. */ +#define POLARSSL_ERR_SSL_CONN_EOF -0x7280 /**< The connection indicated an EOF. */ +#define POLARSSL_ERR_SSL_UNKNOWN_CIPHER -0x7300 /**< An unknown cipher was received. */ +#define POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN -0x7380 /**< The server has no ciphersuites in common with the client. */ +#define POLARSSL_ERR_SSL_NO_RNG -0x7400 /**< No RNG was provided to the SSL module. */ +#define POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE -0x7480 /**< No client certification received from the client, but required by the authentication mode. */ +#define POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE -0x7500 /**< Our own certificate(s) is/are too large to send in an SSL message.*/ +#define POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED -0x7580 /**< The own certificate is not set, but needed by the server. */ +#define POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED -0x7600 /**< The own private key or pre-shared key is not set, but needed. */ +#define POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED -0x7680 /**< No CA Chain is set, but required to operate. */ +#define POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE -0x7700 /**< An unexpected message was received from our peer. */ +#define POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE -0x7780 /**< A fatal alert message was received from our peer. */ +#define POLARSSL_ERR_SSL_PEER_VERIFY_FAILED -0x7800 /**< Verification of our peer failed. */ +#define POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY -0x7880 /**< The peer notified us that the connection is going to be closed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO -0x7900 /**< Processing of the ClientHello handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO -0x7980 /**< Processing of the ServerHello handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE -0x7A00 /**< Processing of the Certificate handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST -0x7A80 /**< Processing of the CertificateRequest handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE -0x7B00 /**< Processing of the ServerKeyExchange handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE -0x7B80 /**< Processing of the ServerHelloDone handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE -0x7C00 /**< Processing of the ClientKeyExchange handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP -0x7C80 /**< Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public. */ +#define POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS -0x7D00 /**< Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret. */ +#define POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY -0x7D80 /**< Processing of the CertificateVerify handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC -0x7E00 /**< Processing of the ChangeCipherSpec handshake message failed. */ +#define POLARSSL_ERR_SSL_BAD_HS_FINISHED -0x7E80 /**< Processing of the Finished handshake message failed. */ +#define POLARSSL_ERR_SSL_MALLOC_FAILED -0x7F00 /**< Memory allocation failed */ +#define POLARSSL_ERR_SSL_HW_ACCEL_FAILED -0x7F80 /**< Hardware acceleration function returned with error */ +#define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80 /**< Hardware acceleration function skipped / left alone data */ +#define POLARSSL_ERR_SSL_COMPRESSION_FAILED -0x6F00 /**< Processing of the compression / decompression failed */ +#define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION -0x6E80 /**< Handshake protocol not within min/max boundaries */ +#define POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET -0x6E00 /**< Processing of the NewSessionTicket handshake message failed. */ +#define POLARSSL_ERR_SSL_SESSION_TICKET_EXPIRED -0x6D80 /**< Session ticket has expired. */ +#define POLARSSL_ERR_SSL_PK_TYPE_MISMATCH -0x6D00 /**< Public key type mismatch (eg, asked for RSA key exchange and presented EC key) */ +#define POLARSSL_ERR_SSL_UNKNOWN_IDENTITY -0x6C80 /**< Unknown identity received (eg, PSK identity) */ +#define POLARSSL_ERR_SSL_INTERNAL_ERROR -0x6C00 /**< Internal error (eg, unexpected failure in lower-level module) */ +#define POLARSSL_ERR_SSL_COUNTER_WRAPPING -0x6B80 /**< A counter would wrap (eg, too many messages exchanged). */ + +/* + * Various constants + */ +#define SSL_MAJOR_VERSION_3 3 +#define SSL_MINOR_VERSION_0 0 /*!< SSL v3.0 */ +#define SSL_MINOR_VERSION_1 1 /*!< TLS v1.0 */ +#define SSL_MINOR_VERSION_2 2 /*!< TLS v1.1 */ +#define SSL_MINOR_VERSION_3 3 /*!< TLS v1.2 */ + +/* Determine minimum supported version */ +#define SSL_MIN_MAJOR_VERSION SSL_MAJOR_VERSION_3 + +#if defined(POLARSSL_SSL_PROTO_SSL3) +#define SSL_MIN_MINOR_VERSION SSL_MINOR_VERSION_0 +#else +#if defined(POLARSSL_SSL_PROTO_TLS1) +#define SSL_MIN_MINOR_VERSION SSL_MINOR_VERSION_1 +#else +#if defined(POLARSSL_SSL_PROTO_TLS1_1) +#define SSL_MIN_MINOR_VERSION SSL_MINOR_VERSION_2 +#else +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#define SSL_MIN_MINOR_VERSION SSL_MINOR_VERSION_3 +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ +#endif /* POLARSSL_SSL_PROTO_TLS1_1 */ +#endif /* POLARSSL_SSL_PROTO_TLS1 */ +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +/* Determine maximum supported version */ +#define SSL_MAX_MAJOR_VERSION SSL_MAJOR_VERSION_3 + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#define SSL_MAX_MINOR_VERSION SSL_MINOR_VERSION_3 +#else +#if defined(POLARSSL_SSL_PROTO_TLS1_1) +#define SSL_MAX_MINOR_VERSION SSL_MINOR_VERSION_2 +#else +#if defined(POLARSSL_SSL_PROTO_TLS1) +#define SSL_MAX_MINOR_VERSION SSL_MINOR_VERSION_1 +#else +#if defined(POLARSSL_SSL_PROTO_SSL3) +#define SSL_MAX_MINOR_VERSION SSL_MINOR_VERSION_0 +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#endif /* POLARSSL_SSL_PROTO_TLS1 */ +#endif /* POLARSSL_SSL_PROTO_TLS1_1 */ +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +/* RFC 6066 section 4, see also mfl_code_to_length in ssl_tls.c + * NONE must be zero so that memset()ing structure to zero works */ +#define SSL_MAX_FRAG_LEN_NONE 0 /*!< don't use this extension */ +#define SSL_MAX_FRAG_LEN_512 1 /*!< MaxFragmentLength 2^9 */ +#define SSL_MAX_FRAG_LEN_1024 2 /*!< MaxFragmentLength 2^10 */ +#define SSL_MAX_FRAG_LEN_2048 3 /*!< MaxFragmentLength 2^11 */ +#define SSL_MAX_FRAG_LEN_4096 4 /*!< MaxFragmentLength 2^12 */ +#define SSL_MAX_FRAG_LEN_INVALID 5 /*!< first invalid value */ + +#define SSL_IS_CLIENT 0 +#define SSL_IS_SERVER 1 +#define SSL_COMPRESS_NULL 0 +#define SSL_COMPRESS_DEFLATE 1 + +#define SSL_VERIFY_NONE 0 +#define SSL_VERIFY_OPTIONAL 1 +#define SSL_VERIFY_REQUIRED 2 + +#define SSL_INITIAL_HANDSHAKE 0 +#define SSL_RENEGOTIATION 1 /* In progress */ +#define SSL_RENEGOTIATION_DONE 2 /* Done */ +#define SSL_RENEGOTIATION_PENDING 3 /* Requested (server only) */ + +#define SSL_LEGACY_RENEGOTIATION 0 +#define SSL_SECURE_RENEGOTIATION 1 + +#define SSL_RENEGOTIATION_DISABLED 0 +#define SSL_RENEGOTIATION_ENABLED 1 + +#define SSL_RENEGOTIATION_NOT_ENFORCED -1 +#define SSL_RENEGO_MAX_RECORDS_DEFAULT 16 + +#define SSL_LEGACY_NO_RENEGOTIATION 0 +#define SSL_LEGACY_ALLOW_RENEGOTIATION 1 +#define SSL_LEGACY_BREAK_HANDSHAKE 2 + +#define SSL_TRUNC_HMAC_DISABLED 0 +#define SSL_TRUNC_HMAC_ENABLED 1 +#define SSL_TRUNCATED_HMAC_LEN 10 /* 80 bits, rfc 6066 section 7 */ + +#define SSL_SESSION_TICKETS_DISABLED 0 +#define SSL_SESSION_TICKETS_ENABLED 1 + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(SSL_DEFAULT_TICKET_LIFETIME) +#define SSL_DEFAULT_TICKET_LIFETIME 86400 /**< Lifetime of session tickets (if enabled) */ +#endif + +/* + * Size of the input / output buffer. + * Note: the RFC defines the default size of SSL / TLS messages. If you + * change the value here, other clients / servers may not be able to + * communicate with you anymore. Only change this value if you control + * both sides of the connection and have it reduced at both sides, or + * if you're using the Max Fragment Length extension and you know all your + * peers are using it too! + */ +#if !defined(SSL_MAX_CONTENT_LEN) +#define SSL_MAX_CONTENT_LEN 16384 /**< Size of the input / output buffer */ +#endif + +/* \} name SECTION: Module settings */ + +/* + * Allow extra bytes for record, authentication and encryption overhead: + * counter (8) + header (5) + IV(16) + MAC (16-48) + padding (0-256) + * and allow for a maximum of 1024 of compression expansion if + * enabled. + */ +#if defined(POLARSSL_ZLIB_SUPPORT) +#define SSL_COMPRESSION_ADD 1024 +#else +#define SSL_COMPRESSION_ADD 0 +#endif + +#if defined(POLARSSL_RC4_C) || defined(POLARSSL_CIPHER_MODE_CBC) +/* Ciphersuites using HMAC */ +#if defined(POLARSSL_SHA512_C) +#define SSL_MAC_ADD 48 /* SHA-384 used for HMAC */ +#elif defined(POLARSSL_SHA256_C) +#define SSL_MAC_ADD 32 /* SHA-256 used for HMAC */ +#else +#define SSL_MAC_ADD 20 /* SHA-1 used for HMAC */ +#endif +#else +/* AEAD ciphersuites: GCM and CCM use a 128 bits tag */ +#define SSL_MAC_ADD 16 +#endif + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#define SSL_PADDING_ADD 256 +#else +#define SSL_PADDING_ADD 0 +#endif + +#define SSL_BUFFER_LEN ( SSL_MAX_CONTENT_LEN \ + + SSL_COMPRESSION_ADD \ + + 29 /* counter + header + IV */ \ + + SSL_MAC_ADD \ + + SSL_PADDING_ADD \ + ) + +/* + * Signaling ciphersuite values (SCSV) + */ +#define SSL_EMPTY_RENEGOTIATION_INFO 0xFF /**< renegotiation info ext */ + +/* + * Supported Signature and Hash algorithms (For TLS 1.2) + * RFC 5246 section 7.4.1.4.1 + */ +#define SSL_HASH_NONE 0 +#define SSL_HASH_MD5 1 +#define SSL_HASH_SHA1 2 +#define SSL_HASH_SHA224 3 +#define SSL_HASH_SHA256 4 +#define SSL_HASH_SHA384 5 +#define SSL_HASH_SHA512 6 + +#define SSL_SIG_ANON 0 +#define SSL_SIG_RSA 1 +#define SSL_SIG_ECDSA 3 + +/* + * Client Certificate Types + * RFC 5246 section 7.4.4 plus RFC 4492 section 5.5 + */ +#define SSL_CERT_TYPE_RSA_SIGN 1 +#define SSL_CERT_TYPE_ECDSA_SIGN 64 + +/* + * Message, alert and handshake types + */ +#define SSL_MSG_CHANGE_CIPHER_SPEC 20 +#define SSL_MSG_ALERT 21 +#define SSL_MSG_HANDSHAKE 22 +#define SSL_MSG_APPLICATION_DATA 23 + +#define SSL_ALERT_LEVEL_WARNING 1 +#define SSL_ALERT_LEVEL_FATAL 2 + +#define SSL_ALERT_MSG_CLOSE_NOTIFY 0 /* 0x00 */ +#define SSL_ALERT_MSG_UNEXPECTED_MESSAGE 10 /* 0x0A */ +#define SSL_ALERT_MSG_BAD_RECORD_MAC 20 /* 0x14 */ +#define SSL_ALERT_MSG_DECRYPTION_FAILED 21 /* 0x15 */ +#define SSL_ALERT_MSG_RECORD_OVERFLOW 22 /* 0x16 */ +#define SSL_ALERT_MSG_DECOMPRESSION_FAILURE 30 /* 0x1E */ +#define SSL_ALERT_MSG_HANDSHAKE_FAILURE 40 /* 0x28 */ +#define SSL_ALERT_MSG_NO_CERT 41 /* 0x29 */ +#define SSL_ALERT_MSG_BAD_CERT 42 /* 0x2A */ +#define SSL_ALERT_MSG_UNSUPPORTED_CERT 43 /* 0x2B */ +#define SSL_ALERT_MSG_CERT_REVOKED 44 /* 0x2C */ +#define SSL_ALERT_MSG_CERT_EXPIRED 45 /* 0x2D */ +#define SSL_ALERT_MSG_CERT_UNKNOWN 46 /* 0x2E */ +#define SSL_ALERT_MSG_ILLEGAL_PARAMETER 47 /* 0x2F */ +#define SSL_ALERT_MSG_UNKNOWN_CA 48 /* 0x30 */ +#define SSL_ALERT_MSG_ACCESS_DENIED 49 /* 0x31 */ +#define SSL_ALERT_MSG_DECODE_ERROR 50 /* 0x32 */ +#define SSL_ALERT_MSG_DECRYPT_ERROR 51 /* 0x33 */ +#define SSL_ALERT_MSG_EXPORT_RESTRICTION 60 /* 0x3C */ +#define SSL_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */ +#define SSL_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */ +#define SSL_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */ +#define SSL_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ +#define SSL_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ +#define SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ +#define SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ +#define SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */ +#define SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */ + +#define SSL_HS_HELLO_REQUEST 0 +#define SSL_HS_CLIENT_HELLO 1 +#define SSL_HS_SERVER_HELLO 2 +#define SSL_HS_NEW_SESSION_TICKET 4 +#define SSL_HS_CERTIFICATE 11 +#define SSL_HS_SERVER_KEY_EXCHANGE 12 +#define SSL_HS_CERTIFICATE_REQUEST 13 +#define SSL_HS_SERVER_HELLO_DONE 14 +#define SSL_HS_CERTIFICATE_VERIFY 15 +#define SSL_HS_CLIENT_KEY_EXCHANGE 16 +#define SSL_HS_FINISHED 20 + +/* + * TLS extensions + */ +#define TLS_EXT_SERVERNAME 0 +#define TLS_EXT_SERVERNAME_HOSTNAME 0 + +#define TLS_EXT_MAX_FRAGMENT_LENGTH 1 + +#define TLS_EXT_TRUNCATED_HMAC 4 + +#define TLS_EXT_SUPPORTED_ELLIPTIC_CURVES 10 +#define TLS_EXT_SUPPORTED_POINT_FORMATS 11 + +#define TLS_EXT_SIG_ALG 13 + +#define TLS_EXT_ALPN 16 + +#define TLS_EXT_SESSION_TICKET 35 + +#define TLS_EXT_RENEGOTIATION_INFO 0xFF01 + +/* + * TLS extension flags (for extensions with outgoing ServerHello content + * that need it (e.g. for RENEGOTIATION_INFO the server already knows because + * of state of the renegotiation flag, so no indicator is required) + */ +#define TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT (1 << 0) + +/* + * Size defines + */ +#if !defined(POLARSSL_PSK_MAX_LEN) +#define POLARSSL_PSK_MAX_LEN 32 /* 256 bits */ +#endif + +/* Dummy type used only for its size */ +union _ssl_premaster_secret +{ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) + unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) + unsigned char _pms_dhm[POLARSSL_MPI_MAX_SIZE]; /* RFC 5246 8.1.2 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + unsigned char _pms_ecdh[POLARSSL_ECP_MAX_BYTES]; /* RFC 4492 5.10 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) + unsigned char _pms_psk[4 + 2 * POLARSSL_PSK_MAX_LEN]; /* RFC 4279 2 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + unsigned char _pms_dhe_psk[4 + POLARSSL_MPI_MAX_SIZE + + POLARSSL_PSK_MAX_LEN]; /* RFC 4279 3 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + unsigned char _pms_rsa_psk[52 + POLARSSL_PSK_MAX_LEN]; /* RFC 4279 4 */ +#endif +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + unsigned char _pms_ecdhe_psk[4 + POLARSSL_ECP_MAX_BYTES + + POLARSSL_PSK_MAX_LEN]; /* RFC 5489 2 */ +#endif +}; + +#define POLARSSL_PREMASTER_SIZE sizeof( union _ssl_premaster_secret ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Generic function pointers for allowing external RSA private key + * implementations. + */ +typedef int (*rsa_decrypt_func)( void *ctx, int mode, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len ); +typedef int (*rsa_sign_func)( void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + int mode, md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig ); +typedef size_t (*rsa_key_len_func)( void *ctx ); + +/* + * SSL state machine + */ +typedef enum +{ + SSL_HELLO_REQUEST, + SSL_CLIENT_HELLO, + SSL_SERVER_HELLO, + SSL_SERVER_CERTIFICATE, + SSL_SERVER_KEY_EXCHANGE, + SSL_CERTIFICATE_REQUEST, + SSL_SERVER_HELLO_DONE, + SSL_CLIENT_CERTIFICATE, + SSL_CLIENT_KEY_EXCHANGE, + SSL_CERTIFICATE_VERIFY, + SSL_CLIENT_CHANGE_CIPHER_SPEC, + SSL_CLIENT_FINISHED, + SSL_SERVER_CHANGE_CIPHER_SPEC, + SSL_SERVER_FINISHED, + SSL_FLUSH_BUFFERS, + SSL_HANDSHAKE_WRAPUP, + SSL_HANDSHAKE_OVER, + SSL_SERVER_NEW_SESSION_TICKET, +} +ssl_states; + +typedef struct _ssl_session ssl_session; +typedef struct _ssl_context ssl_context; +typedef struct _ssl_transform ssl_transform; +typedef struct _ssl_handshake_params ssl_handshake_params; +#if defined(POLARSSL_SSL_SESSION_TICKETS) +typedef struct _ssl_ticket_keys ssl_ticket_keys; +#endif +#if defined(POLARSSL_X509_CRT_PARSE_C) +typedef struct _ssl_key_cert ssl_key_cert; +#endif + +/* + * This structure is used for storing current session data. + */ +struct _ssl_session +{ +#if defined(POLARSSL_HAVE_TIME) + time_t start; /*!< starting time */ +#endif + int ciphersuite; /*!< chosen ciphersuite */ + int compression; /*!< chosen compression */ + size_t length; /*!< session id length */ + unsigned char id[32]; /*!< session identifier */ + unsigned char master[48]; /*!< the master secret */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) + x509_crt *peer_cert; /*!< peer X.509 cert chain */ +#endif /* POLARSSL_X509_CRT_PARSE_C */ + int verify_result; /*!< verification result */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + unsigned char *ticket; /*!< RFC 5077 session ticket */ + size_t ticket_len; /*!< session ticket length */ + uint32_t ticket_lifetime; /*!< ticket lifetime hint */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + int trunc_hmac; /*!< flag for truncated hmac activation */ +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +}; + +/* + * This structure contains a full set of runtime transform parameters + * either in negotiation or active. + */ +struct _ssl_transform +{ + /* + * Session specific crypto layer + */ + const ssl_ciphersuite_t *ciphersuite_info; + /*!< Chosen cipersuite_info */ + unsigned int keylen; /*!< symmetric key length */ + size_t minlen; /*!< min. ciphertext length */ + size_t ivlen; /*!< IV length */ + size_t fixed_ivlen; /*!< Fixed part of IV (AEAD) */ + size_t maclen; /*!< MAC length */ + + unsigned char iv_enc[16]; /*!< IV (encryption) */ + unsigned char iv_dec[16]; /*!< IV (decryption) */ + +#if defined(POLARSSL_SSL_PROTO_SSL3) + /* Needed only for SSL v3.0 secret */ + unsigned char mac_enc[48]; /*!< SSL v3.0 secret (enc) */ + unsigned char mac_dec[48]; /*!< SSL v3.0 secret (dec) */ +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + + md_context_t md_ctx_enc; /*!< MAC (encryption) */ + md_context_t md_ctx_dec; /*!< MAC (decryption) */ + + cipher_context_t cipher_ctx_enc; /*!< encryption context */ + cipher_context_t cipher_ctx_dec; /*!< decryption context */ + + /* + * Session specific compression layer + */ +#if defined(POLARSSL_ZLIB_SUPPORT) + z_stream ctx_deflate; /*!< compression context */ + z_stream ctx_inflate; /*!< decompression context */ +#endif +}; + +/* + * This structure contains the parameters only needed during handshake. + */ +struct _ssl_handshake_params +{ + /* + * Handshake specific crypto variables + */ + int sig_alg; /*!< Hash algorithm for signature */ + int cert_type; /*!< Requested cert type */ + int verify_sig_alg; /*!< Signature algorithm for verify */ +#if defined(POLARSSL_DHM_C) + dhm_context dhm_ctx; /*!< DHM key exchange */ +#endif +#if defined(POLARSSL_ECDH_C) + ecdh_context ecdh_ctx; /*!< ECDH key exchange */ +#endif +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + const ecp_curve_info **curves; /*!< Supported elliptic curves */ +#endif +#if defined(POLARSSL_X509_CRT_PARSE_C) + /** + * Current key/cert or key/cert list. + * On client: pointer to ssl->key_cert, only the first entry used. + * On server: starts as a pointer to ssl->key_cert, then becomes + * a pointer to the chosen key from this list or the SNI list. + */ + ssl_key_cert *key_cert; +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + ssl_key_cert *sni_key_cert; /*!< key/cert list from SNI */ +#endif +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + /* + * Checksum contexts + */ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + md5_context fin_md5; + sha1_context fin_sha1; +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) + sha256_context fin_sha256; +#endif +#if defined(POLARSSL_SHA512_C) + sha512_context fin_sha512; +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + void (*update_checksum)(ssl_context *, const unsigned char *, size_t); + void (*calc_verify)(ssl_context *, unsigned char *); + void (*calc_finished)(ssl_context *, unsigned char *, int); + int (*tls_prf)(const unsigned char *, size_t, const char *, + const unsigned char *, size_t, + unsigned char *, size_t); + + size_t pmslen; /*!< premaster length */ + + unsigned char randbytes[64]; /*!< random bytes */ + unsigned char premaster[POLARSSL_PREMASTER_SIZE]; + /*!< premaster secret */ + + int resume; /*!< session resume indicator*/ + int max_major_ver; /*!< max. major version client*/ + int max_minor_ver; /*!< max. minor version client*/ + int cli_exts; /*!< client extension presence*/ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + int new_session_ticket; /*!< use NewSessionTicket? */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ +}; + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/* + * Parameters needed to secure session tickets + */ +struct _ssl_ticket_keys +{ + unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ + aes_context enc; /*!< encryption context */ + aes_context dec; /*!< decryption context */ + unsigned char mac_key[16]; /*!< authentication key */ +}; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/* + * List of certificate + private key pairs + */ +struct _ssl_key_cert +{ + x509_crt *cert; /*!< cert */ + pk_context *key; /*!< private key */ + int key_own_alloc; /*!< did we allocate key? */ + ssl_key_cert *next; /*!< next key/cert pair */ +}; +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +struct _ssl_context +{ + /* + * Miscellaneous + */ + int state; /*!< SSL handshake: current state */ + int renegotiation; /*!< Initial or renegotiation */ + int renego_records_seen; /*!< Records since renego request */ + + int major_ver; /*!< equal to SSL_MAJOR_VERSION_3 */ + int minor_ver; /*!< either 0 (SSL3) or 1 (TLS1.0) */ + + int max_major_ver; /*!< max. major version used */ + int max_minor_ver; /*!< max. minor version used */ + int min_major_ver; /*!< min. major version used */ + int min_minor_ver; /*!< min. minor version used */ + + /* + * Callbacks (RNG, debug, I/O, verification) + */ + int (*f_rng)(void *, unsigned char *, size_t); + void (*f_dbg)(void *, int, const char *); + int (*f_recv)(void *, unsigned char *, size_t); + int (*f_send)(void *, const unsigned char *, size_t); + int (*f_get_cache)(void *, ssl_session *); + int (*f_set_cache)(void *, const ssl_session *); + + void *p_rng; /*!< context for the RNG function */ + void *p_dbg; /*!< context for the debug function */ + void *p_recv; /*!< context for reading operations */ + void *p_send; /*!< context for writing operations */ + void *p_get_cache; /*!< context for cache retrieval */ + void *p_set_cache; /*!< context for cache store */ + void *p_hw_data; /*!< context for HW acceleration */ + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + int (*f_sni)(void *, ssl_context *, const unsigned char *, size_t); + void *p_sni; /*!< context for SNI extension */ +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) + int (*f_vrfy)(void *, x509_crt *, int, int *); + void *p_vrfy; /*!< context for verification */ +#endif + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + int (*f_psk)(void *, ssl_context *, const unsigned char *, size_t); + void *p_psk; /*!< context for PSK retrieval */ +#endif + + /* + * Session layer + */ + ssl_session *session_in; /*!< current session data (in) */ + ssl_session *session_out; /*!< current session data (out) */ + ssl_session *session; /*!< negotiated session data */ + ssl_session *session_negotiate; /*!< session data in negotiation */ + + ssl_handshake_params *handshake; /*!< params required only during + the handshake process */ + + /* + * Record layer transformations + */ + ssl_transform *transform_in; /*!< current transform params (in) */ + ssl_transform *transform_out; /*!< current transform params (in) */ + ssl_transform *transform; /*!< negotiated transform params */ + ssl_transform *transform_negotiate; /*!< transform params in negotiation */ + + /* + * Record layer (incoming data) + */ + unsigned char *in_ctr; /*!< 64-bit incoming message counter */ + unsigned char *in_hdr; /*!< 5-byte record header (in_ctr+8) */ + unsigned char *in_iv; /*!< ivlen-byte IV (in_hdr+5) */ + unsigned char *in_msg; /*!< message contents (in_iv+ivlen) */ + unsigned char *in_offt; /*!< read offset in application data */ + + int in_msgtype; /*!< record header: message type */ + size_t in_msglen; /*!< record header: message length */ + size_t in_left; /*!< amount of data read so far */ + + size_t in_hslen; /*!< current handshake message length */ + int nb_zero; /*!< # of 0-length encrypted messages */ + int record_read; /*!< record is already present */ + + /* + * Record layer (outgoing data) + */ + unsigned char *out_ctr; /*!< 64-bit outgoing message counter */ + unsigned char *out_hdr; /*!< 5-byte record header (out_ctr+8) */ + unsigned char *out_iv; /*!< ivlen-byte IV (out_hdr+5) */ + unsigned char *out_msg; /*!< message contents (out_iv+ivlen) */ + + int out_msgtype; /*!< record header: message type */ + size_t out_msglen; /*!< record header: message length */ + size_t out_left; /*!< amount of data not yet written */ + +#if defined(POLARSSL_ZLIB_SUPPORT) + unsigned char *compress_buf; /*!< zlib data buffer */ +#endif +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + unsigned char mfl_code; /*!< MaxFragmentLength chosen by us */ +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + + /* + * PKI layer + */ +#if defined(POLARSSL_X509_CRT_PARSE_C) + ssl_key_cert *key_cert; /*!< own certificate(s)/key(s) */ + + x509_crt *ca_chain; /*!< own trusted CA chain */ + x509_crl *ca_crl; /*!< trusted CA CRLs */ + const char *peer_cn; /*!< expected peer CN */ +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + /* + * Support for generating and checking session tickets + */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) + ssl_ticket_keys *ticket_keys; /*!< keys for ticket encryption */ +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + + /* + * User settings + */ + int endpoint; /*!< 0: client, 1: server */ + int authmode; /*!< verification mode */ + int client_auth; /*!< flag for client auth. */ + int verify_result; /*!< verification result */ + int disable_renegotiation; /*!< enable/disable renegotiation */ + int allow_legacy_renegotiation; /*!< allow legacy renegotiation */ + int renego_max_records; /*!< grace period for renegotiation */ + const int *ciphersuite_list[4]; /*!< allowed ciphersuites / version */ +#if defined(POLARSSL_SSL_SET_CURVES) + const ecp_group_id *curve_list; /*!< allowed curves */ +#endif +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + int trunc_hmac; /*!< negotiate truncated hmac? */ +#endif +#if defined(POLARSSL_SSL_SESSION_TICKETS) + int session_tickets; /*!< use session tickets? */ + int ticket_lifetime; /*!< session ticket lifetime */ +#endif + +#if defined(POLARSSL_DHM_C) + mpi dhm_P; /*!< prime modulus for DHM */ + mpi dhm_G; /*!< generator for DHM */ +#endif + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + /* + * PSK values + */ + unsigned char *psk; + size_t psk_len; + unsigned char *psk_identity; + size_t psk_identity_len; +#endif + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + /* + * SNI extension + */ + unsigned char *hostname; + size_t hostname_len; +#endif + +#if defined(POLARSSL_SSL_ALPN) + /* + * ALPN extension + */ + const char **alpn_list; /*!< ordered list of supported protocols */ + const char *alpn_chosen; /*!< negotiated protocol */ +#endif + + /* + * Secure renegotiation + */ + int secure_renegotiation; /*!< does peer support legacy or + secure renegotiation */ + size_t verify_data_len; /*!< length of verify data stored */ + char own_verify_data[36]; /*!< previous handshake verify data */ + char peer_verify_data[36]; /*!< previous handshake verify data */ +}; + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + +#define SSL_CHANNEL_OUTBOUND 0 +#define SSL_CHANNEL_INBOUND 1 + +extern int (*ssl_hw_record_init)(ssl_context *ssl, + const unsigned char *key_enc, const unsigned char *key_dec, + size_t keylen, + const unsigned char *iv_enc, const unsigned char *iv_dec, + size_t ivlen, + const unsigned char *mac_enc, const unsigned char *mac_dec, + size_t maclen); +extern int (*ssl_hw_record_activate)(ssl_context *ssl, int direction); +extern int (*ssl_hw_record_reset)(ssl_context *ssl); +extern int (*ssl_hw_record_write)(ssl_context *ssl); +extern int (*ssl_hw_record_read)(ssl_context *ssl); +extern int (*ssl_hw_record_finish)(ssl_context *ssl); +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ + +/** + * \brief Returns the list of ciphersuites supported by the SSL/TLS module. + * + * \return a statically allocated array of ciphersuites, the last + * entry is 0. + */ +const int *ssl_list_ciphersuites( void ); + +/** + * \brief Return the name of the ciphersuite associated with the + * given ID + * + * \param ciphersuite_id SSL ciphersuite ID + * + * \return a string containing the ciphersuite name + */ +const char *ssl_get_ciphersuite_name( const int ciphersuite_id ); + +/** + * \brief Return the ID of the ciphersuite associated with the + * given name + * + * \param ciphersuite_name SSL ciphersuite name + * + * \return the ID with the ciphersuite or 0 if not found + */ +int ssl_get_ciphersuite_id( const char *ciphersuite_name ); + +/** + * \brief Initialize an SSL context + * (An individual SSL context is not thread-safe) + * + * \param ssl SSL context + * + * \return 0 if successful, or POLARSSL_ERR_SSL_MALLOC_FAILED if + * memory allocation failed + */ +int ssl_init( ssl_context *ssl ); + +/** + * \brief Reset an already initialized SSL context for re-use + * while retaining application-set variables, function + * pointers and data. + * + * \param ssl SSL context + * \return 0 if successful, or POLASSL_ERR_SSL_MALLOC_FAILED, + POLARSSL_ERR_SSL_HW_ACCEL_FAILED or + * POLARSSL_ERR_SSL_COMPRESSION_FAILED + */ +int ssl_session_reset( ssl_context *ssl ); + +/** + * \brief Set the current endpoint type + * + * \param ssl SSL context + * \param endpoint must be SSL_IS_CLIENT or SSL_IS_SERVER + * + * \note This function should be called right after ssl_init() since + * some other ssl_set_foo() functions depend on it. + */ +void ssl_set_endpoint( ssl_context *ssl, int endpoint ); + +/** + * \brief Set the certificate verification mode + * + * \param ssl SSL context + * \param authmode can be: + * + * SSL_VERIFY_NONE: peer certificate is not checked (default), + * this is insecure and SHOULD be avoided. + * + * SSL_VERIFY_OPTIONAL: peer certificate is checked, however the + * handshake continues even if verification failed; + * ssl_get_verify_result() can be called after the + * handshake is complete. + * + * SSL_VERIFY_REQUIRED: peer *must* present a valid certificate, + * handshake is aborted if verification failed. + * + * \note On client, SSL_VERIFY_REQUIRED is the recommended mode. + * With SSL_VERIFY_OPTIONAL, the user needs to call ssl_get_verify_result() at + * the right time(s), which may not be obvious, while REQUIRED always perform + * the verification as soon as possible. For example, REQUIRED was protecting + * against the "triple handshake" attack even before it was found. + */ +void ssl_set_authmode( ssl_context *ssl, int authmode ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/** + * \brief Set the verification callback (Optional). + * + * If set, the verify callback is called for each + * certificate in the chain. For implementation + * information, please see \c x509parse_verify() + * + * \param ssl SSL context + * \param f_vrfy verification function + * \param p_vrfy verification parameter + */ +void ssl_set_verify( ssl_context *ssl, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +/** + * \brief Set the random number generator callback + * + * \param ssl SSL context + * \param f_rng RNG function + * \param p_rng RNG parameter + */ +void ssl_set_rng( ssl_context *ssl, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief Set the debug callback + * + * \param ssl SSL context + * \param f_dbg debug function + * \param p_dbg debug parameter + */ +void ssl_set_dbg( ssl_context *ssl, + void (*f_dbg)(void *, int, const char *), + void *p_dbg ); + +/** + * \brief Set the underlying BIO read and write callbacks + * + * \param ssl SSL context + * \param f_recv read callback + * \param p_recv read parameter + * \param f_send write callback + * \param p_send write parameter + */ +void ssl_set_bio( ssl_context *ssl, + int (*f_recv)(void *, unsigned char *, size_t), void *p_recv, + int (*f_send)(void *, const unsigned char *, size_t), void *p_send ); + +/** + * \brief Set the session cache callbacks (server-side only) + * If not set, no session resuming is done. + * + * The session cache has the responsibility to check for stale + * entries based on timeout. See RFC 5246 for recommendations. + * + * Warning: session.peer_cert is cleared by the SSL/TLS layer on + * connection shutdown, so do not cache the pointer! Either set + * it to NULL or make a full copy of the certificate. + * + * The get callback is called once during the initial handshake + * to enable session resuming. The get function has the + * following parameters: (void *parameter, ssl_session *session) + * If a valid entry is found, it should fill the master of + * the session object with the cached values and return 0, + * return 1 otherwise. Optionally peer_cert can be set as well + * if it is properly present in cache entry. + * + * The set callback is called once during the initial handshake + * to enable session resuming after the entire handshake has + * been finished. The set function has the following parameters: + * (void *parameter, const ssl_session *session). The function + * should create a cache entry for future retrieval based on + * the data in the session structure and should keep in mind + * that the ssl_session object presented (and all its referenced + * data) is cleared by the SSL/TLS layer when the connection is + * terminated. It is recommended to add metadata to determine if + * an entry is still valid in the future. Return 0 if + * successfully cached, return 1 otherwise. + * + * \param ssl SSL context + * \param f_get_cache session get callback + * \param p_get_cache session get parameter + * \param f_set_cache session set callback + * \param p_set_cache session set parameter + */ +void ssl_set_session_cache( ssl_context *ssl, + int (*f_get_cache)(void *, ssl_session *), void *p_get_cache, + int (*f_set_cache)(void *, const ssl_session *), void *p_set_cache ); + +/** + * \brief Request resumption of session (client-side only) + * Session data is copied from presented session structure. + * + * \param ssl SSL context + * \param session session context + * + * \return 0 if successful, + * POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or + * arguments are otherwise invalid + * + * \sa ssl_get_session() + */ +int ssl_set_session( ssl_context *ssl, const ssl_session *session ); + +/** + * \brief Set the list of allowed ciphersuites and the preference + * order. First in the list has the highest preference. + * (Overrides all version specific lists) + * + * Note: The PolarSSL SSL server uses its own preferences + * over the preference of the connection SSL client unless + * POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE is defined! + * + * \param ssl SSL context + * \param ciphersuites 0-terminated list of allowed ciphersuites + */ +void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites ); + +/** + * \brief Set the list of allowed ciphersuites and the + * preference order for a specific version of the protocol. + * (Only useful on the server side) + * + * \param ssl SSL context + * \param ciphersuites 0-terminated list of allowed ciphersuites + * \param major Major version number (only SSL_MAJOR_VERSION_3 + * supported) + * \param minor Minor version number (SSL_MINOR_VERSION_0, + * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, + * SSL_MINOR_VERSION_3 supported) + */ +void ssl_set_ciphersuites_for_version( ssl_context *ssl, + const int *ciphersuites, + int major, int minor ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/** + * \brief Set the data required to verify peer certificate + * + * \param ssl SSL context + * \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs) + * \param ca_crl trusted CA CRLs + * \param peer_cn expected peer CommonName (or NULL) + */ +void ssl_set_ca_chain( ssl_context *ssl, x509_crt *ca_chain, + x509_crl *ca_crl, const char *peer_cn ); + +/** + * \brief Set own certificate chain and private key + * + * \note own_cert should contain in order from the bottom up your + * certificate chain. The top certificate (self-signed) + * can be omitted. + * + * \note This function may be called more than once if you want to + * support multiple certificates (eg, one using RSA and one + * using ECDSA). However, on client, currently only the first + * certificate is used (subsequent calls have no effect). + * + * \param ssl SSL context + * \param own_cert own public certificate chain + * \param pk_key own private key + * + * \return 0 on success or POLARSSL_ERR_SSL_MALLOC_FAILED + */ +int ssl_set_own_cert( ssl_context *ssl, x509_crt *own_cert, + pk_context *pk_key ); + +#if defined(POLARSSL_RSA_C) +/** + * \brief Set own certificate chain and private RSA key + * + * Note: own_cert should contain IN order from the bottom + * up your certificate chain. The top certificate (self-signed) + * can be omitted. + * + * \warning This backwards-compatibility function is deprecated! + * Please use \c ssl_set_own_cert() instead. + * + * \param ssl SSL context + * \param own_cert own public certificate chain + * \param rsa_key own private RSA key + * + * \return 0 on success, or a specific error code. + */ +int ssl_set_own_cert_rsa( ssl_context *ssl, x509_crt *own_cert, + rsa_context *rsa_key ); +#endif /* POLARSSL_RSA_C */ + +/** + * \brief Set own certificate and alternate non-PolarSSL RSA private + * key and handling callbacks, such as the PKCS#11 wrappers + * or any other external private key handler. + * (see the respective RSA functions in rsa.h for documentation + * of the callback parameters, with the only change being + * that the rsa_context * is a void * in the callbacks) + * + * Note: own_cert should contain IN order from the bottom + * up your certificate chain. The top certificate (self-signed) + * can be omitted. + * + * \warning This backwards-compatibility function is deprecated! + * Please use \c pk_init_ctx_rsa_alt() + * and \c ssl_set_own_cert() instead. + * + * \param ssl SSL context + * \param own_cert own public certificate chain + * \param rsa_key alternate implementation private RSA key + * \param rsa_decrypt alternate implementation of \c rsa_pkcs1_decrypt() + * \param rsa_sign alternate implementation of \c rsa_pkcs1_sign() + * \param rsa_key_len function returning length of RSA key in bytes + * + * \return 0 on success, or a specific error code. + */ +int ssl_set_own_cert_alt( ssl_context *ssl, x509_crt *own_cert, + void *rsa_key, + rsa_decrypt_func rsa_decrypt, + rsa_sign_func rsa_sign, + rsa_key_len_func rsa_key_len ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +/** + * \brief Set the Pre Shared Key (PSK) and the identity name connected + * to it. + * + * \param ssl SSL context + * \param psk pointer to the pre-shared key + * \param psk_len pre-shared key length + * \param psk_identity pointer to the pre-shared key identity + * \param psk_identity_len identity key length + * + * \return 0 if successful or POLARSSL_ERR_SSL_MALLOC_FAILED + */ +int ssl_set_psk( ssl_context *ssl, const unsigned char *psk, size_t psk_len, + const unsigned char *psk_identity, size_t psk_identity_len ); + +/** + * \brief Set the PSK callback (server-side only) (Optional). + * + * If set, the PSK callback is called for each + * handshake where a PSK ciphersuite was negotiated. + * The caller provides the identity received and wants to + * receive the actual PSK data and length. + * + * The callback has the following parameters: (void *parameter, + * ssl_context *ssl, const unsigned char *psk_identity, + * size_t identity_len) + * If a valid PSK identity is found, the callback should use + * ssl_set_psk() on the ssl context to set the correct PSK and + * identity and return 0. + * Any other return value will result in a denied PSK identity. + * + * \param ssl SSL context + * \param f_psk PSK identity function + * \param p_psk PSK identity parameter + */ +void ssl_set_psk_cb( ssl_context *ssl, + int (*f_psk)(void *, ssl_context *, const unsigned char *, + size_t), + void *p_psk ); +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#if defined(POLARSSL_DHM_C) +/** + * \brief Set the Diffie-Hellman public P and G values, + * read as hexadecimal strings (server-side only) + * (Default: POLARSSL_DHM_RFC5114_MODP_1024_[PG]) + * + * \param ssl SSL context + * \param dhm_P Diffie-Hellman-Merkle modulus + * \param dhm_G Diffie-Hellman-Merkle generator + * + * \return 0 if successful + */ +int ssl_set_dh_param( ssl_context *ssl, const char *dhm_P, const char *dhm_G ); + +/** + * \brief Set the Diffie-Hellman public P and G values, + * read from existing context (server-side only) + * + * \param ssl SSL context + * \param dhm_ctx Diffie-Hellman-Merkle context + * + * \return 0 if successful + */ +int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ); +#endif /* POLARSSL_DHM_C */ + +#if defined(POLARSSL_SSL_SET_CURVES) +/** + * \brief Set the allowed curves in order of preference. + * (Default: all defined curves.) + * + * On server: this only affects selection of the ECDHE curve; + * the curves used for ECDH and ECDSA are determined by the + * list of available certificates instead. + * + * On client: this affects the list of curves offered for any + * use. The server can override our preference order. + * + * Both sides: limits the set of curves used by peer to the + * listed curves for any use (ECDH(E), certificates). + * + * \param ssl SSL context + * \param curves Ordered list of allowed curves, + * terminated by POLARSSL_ECP_DP_NONE. + */ +void ssl_set_curves( ssl_context *ssl, const ecp_group_id *curves ); +#endif /* POLARSSL_SSL_SET_CURVES */ + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) +/** + * \brief Set hostname for ServerName TLS extension + * (client-side only) + * + * + * \param ssl SSL context + * \param hostname the server hostname + * + * \return 0 if successful or POLARSSL_ERR_SSL_MALLOC_FAILED + */ +int ssl_set_hostname( ssl_context *ssl, const char *hostname ); + +/** + * \brief Set server side ServerName TLS extension callback + * (optional, server-side only). + * + * If set, the ServerName callback is called whenever the + * server receives a ServerName TLS extension from the client + * during a handshake. The ServerName callback has the + * following parameters: (void *parameter, ssl_context *ssl, + * const unsigned char *hostname, size_t len). If a suitable + * certificate is found, the callback should set the + * certificate and key to use with ssl_set_own_cert() (and + * possibly adjust the CA chain as well) and return 0. The + * callback should return -1 to abort the handshake at this + * point. + * + * \param ssl SSL context + * \param f_sni verification function + * \param p_sni verification parameter + */ +void ssl_set_sni( ssl_context *ssl, + int (*f_sni)(void *, ssl_context *, const unsigned char *, + size_t), + void *p_sni ); +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ + +#if defined(POLARSSL_SSL_ALPN) +/** + * \brief Set the supported Application Layer Protocols. + * + * \param ssl SSL context + * \param protos NULL-terminated list of supported protocols, + * in decreasing preference order. + * + * \return 0 on success, or POLARSSL_ERR_SSL_BAD_INPUT_DATA. + */ +int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ); + +/** + * \brief Get the name of the negotiated Application Layer Protocol. + * This function should be called after the handshake is + * completed. + * + * \param ssl SSL context + * + * \return Protcol name, or NULL if no protocol was negotiated. + */ +const char *ssl_get_alpn_protocol( const ssl_context *ssl ); +#endif /* POLARSSL_SSL_ALPN */ + +/** + * \brief Set the maximum supported version sent from the client side + * and/or accepted at the server side + * (Default: SSL_MAX_MAJOR_VERSION, SSL_MAX_MINOR_VERSION) + * + * Note: This ignores ciphersuites from 'higher' versions. + * Note: Input outside of the SSL_MAX_XXXXX_VERSION and + * SSL_MIN_XXXXX_VERSION range is ignored. + * + * \param ssl SSL context + * \param major Major version number (only SSL_MAJOR_VERSION_3 supported) + * \param minor Minor version number (SSL_MINOR_VERSION_0, + * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, + * SSL_MINOR_VERSION_3 supported) + */ +void ssl_set_max_version( ssl_context *ssl, int major, int minor ); + + +/** + * \brief Set the minimum accepted SSL/TLS protocol version + * (Default: SSL_MIN_MAJOR_VERSION, SSL_MIN_MINOR_VERSION) + * + * Note: Input outside of the SSL_MAX_XXXXX_VERSION and + * SSL_MIN_XXXXX_VERSION range is ignored. + * + * \param ssl SSL context + * \param major Major version number (only SSL_MAJOR_VERSION_3 supported) + * \param minor Minor version number (SSL_MINOR_VERSION_0, + * SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2, + * SSL_MINOR_VERSION_3 supported) + */ +void ssl_set_min_version( ssl_context *ssl, int major, int minor ); + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +/** + * \brief Set the maximum fragment length to emit and/or negotiate + * (Default: SSL_MAX_CONTENT_LEN, usually 2^14 bytes) + * (Server: set maximum fragment length to emit, + * usually negotiated by the client during handshake + * (Client: set maximum fragment length to emit *and* + * negotiate with the server during handshake) + * + * \param ssl SSL context + * \param mfl_code Code for maximum fragment length (allowed values: + * SSL_MAX_FRAG_LEN_512, SSL_MAX_FRAG_LEN_1024, + * SSL_MAX_FRAG_LEN_2048, SSL_MAX_FRAG_LEN_4096) + * + * \return O if successful or POLARSSL_ERR_SSL_BAD_INPUT_DATA + */ +int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code ); +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +/** + * \brief Activate negotiation of truncated HMAC (Client only) + * (Default: SSL_TRUNC_HMAC_ENABLED) + * + * \param ssl SSL context + * \param truncate Enable or disable (SSL_TRUNC_HMAC_ENABLED or + * SSL_TRUNC_HMAC_DISABLED) + * + * \return O if successful, + * POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side + */ +int ssl_set_truncated_hmac( ssl_context *ssl, int truncate ); +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/** + * \brief Enable / Disable session tickets + * (Default: SSL_SESSION_TICKETS_ENABLED on client, + * SSL_SESSION_TICKETS_DISABLED on server) + * + * \note On server, ssl_set_rng() must be called before this function + * to allow generating the ticket encryption and + * authentication keys. + * + * \param ssl SSL context + * \param use_tickets Enable or disable (SSL_SESSION_TICKETS_ENABLED or + * SSL_SESSION_TICKETS_DISABLED) + * + * \return O if successful, + * or a specific error code (server only). + */ +int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ); + +/** + * \brief Set session ticket lifetime (server only) + * (Default: SSL_DEFAULT_TICKET_LIFETIME (86400 secs / 1 day)) + * + * \param ssl SSL context + * \param lifetime session ticket lifetime + */ +void ssl_set_session_ticket_lifetime( ssl_context *ssl, int lifetime ); +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +/** + * \brief Enable / Disable renegotiation support for connection when + * initiated by peer + * (Default: SSL_RENEGOTIATION_DISABLED) + * + * Note: A server with support enabled is more vulnerable for a + * resource DoS by a malicious client. You should enable this on + * a client to enable server-initiated renegotiation. + * + * \param ssl SSL context + * \param renegotiation Enable or disable (SSL_RENEGOTIATION_ENABLED or + * SSL_RENEGOTIATION_DISABLED) + */ +void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ); + +/** + * \brief Prevent or allow legacy renegotiation. + * (Default: SSL_LEGACY_NO_RENEGOTIATION) + * + * SSL_LEGACY_NO_RENEGOTIATION allows connections to + * be established even if the peer does not support + * secure renegotiation, but does not allow renegotiation + * to take place if not secure. + * (Interoperable and secure option) + * + * SSL_LEGACY_ALLOW_RENEGOTIATION allows renegotiations + * with non-upgraded peers. Allowing legacy renegotiation + * makes the connection vulnerable to specific man in the + * middle attacks. (See RFC 5746) + * (Most interoperable and least secure option) + * + * SSL_LEGACY_BREAK_HANDSHAKE breaks off connections + * if peer does not support secure renegotiation. Results + * in interoperability issues with non-upgraded peers + * that do not support renegotiation altogether. + * (Most secure option, interoperability issues) + * + * \param ssl SSL context + * \param allow_legacy Prevent or allow (SSL_NO_LEGACY_RENEGOTIATION, + * SSL_ALLOW_LEGACY_RENEGOTIATION or + * SSL_LEGACY_BREAK_HANDSHAKE) + */ +void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ); + +/** + * \brief Enforce server-requested renegotiation. + * (Default: enforced, max_records = 16) + * (No effect on client.) + * + * When a server requests a renegotiation, the client can + * comply or ignore the request. This function allows the + * server to decide if it should enforce its renegotiation + * requests by closing the connection if the client doesn't + * initiate a renegotiation. + * + * However, records could already be in transit from the + * client to the server when the request is emitted. In order + * to increase reliability, the server can accept a number of + * records containing application data before the ClientHello + * that was requested. + * + * The optimal value is highly dependent on the specific usage + * scenario. + * + * \param ssl SSL context + * \param max_records Use SSL_RENEGOTIATION_NOT_ENFORCED if you don't want to + * enforce renegotiation, or a non-negative value to enforce + * it but allow for a grace period of max_records records. + */ +void ssl_set_renegotiation_enforced( ssl_context *ssl, int max_records ); + +/** + * \brief Return the number of data bytes available to read + * + * \param ssl SSL context + * + * \return how many bytes are available in the read buffer + */ +size_t ssl_get_bytes_avail( const ssl_context *ssl ); + +/** + * \brief Return the result of the certificate verification + * + * \param ssl SSL context + * + * \return 0 if successful, or a combination of: + * BADCERT_EXPIRED + * BADCERT_REVOKED + * BADCERT_CN_MISMATCH + * BADCERT_NOT_TRUSTED + */ +int ssl_get_verify_result( const ssl_context *ssl ); + +/** + * \brief Return the name of the current ciphersuite + * + * \param ssl SSL context + * + * \return a string containing the ciphersuite name + */ +const char *ssl_get_ciphersuite( const ssl_context *ssl ); + +/** + * \brief Return the current SSL version (SSLv3/TLSv1/etc) + * + * \param ssl SSL context + * + * \return a string containing the SSL version + */ +const char *ssl_get_version( const ssl_context *ssl ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/** + * \brief Return the peer certificate from the current connection + * + * Note: Can be NULL in case no certificate was sent during + * the handshake. Different calls for the same connection can + * return the same or different pointers for the same + * certificate and even a different certificate altogether. + * The peer cert CAN change in a single connection if + * renegotiation is performed. + * + * \param ssl SSL context + * + * \return the current peer certificate + */ +const x509_crt *ssl_get_peer_cert( const ssl_context *ssl ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +/** + * \brief Save session in order to resume it later (client-side only) + * Session data is copied to presented session structure. + * + * \warning Currently, peer certificate is lost in the operation. + * + * \param ssl SSL context + * \param session session context + * + * \return 0 if successful, + * POLARSSL_ERR_SSL_MALLOC_FAILED if memory allocation failed, + * POLARSSL_ERR_SSL_BAD_INPUT_DATA if used server-side or + * arguments are otherwise invalid + * + * \sa ssl_set_session() + */ +int ssl_get_session( const ssl_context *ssl, ssl_session *session ); + +/** + * \brief Perform the SSL handshake + * + * \param ssl SSL context + * + * \return 0 if successful, POLARSSL_ERR_NET_WANT_READ, + * POLARSSL_ERR_NET_WANT_WRITE, or a specific SSL error code. + */ +int ssl_handshake( ssl_context *ssl ); + +/** + * \brief Perform a single step of the SSL handshake + * + * Note: the state of the context (ssl->state) will be at + * the following state after execution of this function. + * Do not call this function if state is SSL_HANDSHAKE_OVER. + * + * \param ssl SSL context + * + * \return 0 if successful, POLARSSL_ERR_NET_WANT_READ, + * POLARSSL_ERR_NET_WANT_WRITE, or a specific SSL error code. + */ +int ssl_handshake_step( ssl_context *ssl ); + +/** + * \brief Initiate an SSL renegotiation on the running connection. + * Client: perform the renegotiation right now. + * Server: request renegotiation, which will be performed + * during the next call to ssl_read() if honored by client. + * + * \param ssl SSL context + * + * \return 0 if successful, or any ssl_handshake() return value. + */ +int ssl_renegotiate( ssl_context *ssl ); + +/** + * \brief Read at most 'len' application data bytes + * + * \param ssl SSL context + * \param buf buffer that will hold the data + * \param len how many bytes must be read + * + * \return This function returns the number of bytes read, 0 for EOF, + * or a negative error code. + */ +int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ); + +/** + * \brief Write exactly 'len' application data bytes + * + * \param ssl SSL context + * \param buf buffer holding the data + * \param len how many bytes must be written + * + * \return This function returns the number of bytes written, + * or a negative error code. + * + * \note When this function returns POLARSSL_ERR_NET_WANT_WRITE, + * it must be called later with the *same* arguments, + * until it returns a positive value. + */ +int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len ); + +/** + * \brief Send an alert message + * + * \param ssl SSL context + * \param level The alert level of the message + * (SSL_ALERT_LEVEL_WARNING or SSL_ALERT_LEVEL_FATAL) + * \param message The alert message (SSL_ALERT_MSG_*) + * + * \return 0 if successful, or a specific SSL error code. + */ +int ssl_send_alert_message( ssl_context *ssl, + unsigned char level, + unsigned char message ); +/** + * \brief Notify the peer that the connection is being closed + * + * \param ssl SSL context + */ +int ssl_close_notify( ssl_context *ssl ); + +/** + * \brief Free referenced items in an SSL context and clear memory + * + * \param ssl SSL context + */ +void ssl_free( ssl_context *ssl ); + +/** + * \brief Initialize SSL session structure + * + * \param session SSL session + */ +void ssl_session_init( ssl_session *session ); + +/** + * \brief Free referenced items in an SSL session including the + * peer certificate and clear memory + * + * \param session SSL session + */ +void ssl_session_free( ssl_session *session ); + +/** + * \brief Free referenced items in an SSL transform context and clear + * memory + * + * \param transform SSL transform context + */ +void ssl_transform_free( ssl_transform *transform ); + +/** + * \brief Free referenced items in an SSL handshake context and clear + * memory + * + * \param handshake SSL handshake context + */ +void ssl_handshake_free( ssl_handshake_params *handshake ); + +/* + * Internal functions (do not call directly) + */ +int ssl_handshake_client_step( ssl_context *ssl ); +int ssl_handshake_server_step( ssl_context *ssl ); +void ssl_handshake_wrapup( ssl_context *ssl ); + +int ssl_send_fatal_handshake_failure( ssl_context *ssl ); + +int ssl_derive_keys( ssl_context *ssl ); + +int ssl_read_record( ssl_context *ssl ); +/** + * \return 0 if successful, POLARSSL_ERR_SSL_CONN_EOF on EOF or + * another negative error code. + */ +int ssl_fetch_input( ssl_context *ssl, size_t nb_want ); + +int ssl_write_record( ssl_context *ssl ); +int ssl_flush_output( ssl_context *ssl ); + +int ssl_parse_certificate( ssl_context *ssl ); +int ssl_write_certificate( ssl_context *ssl ); + +int ssl_parse_change_cipher_spec( ssl_context *ssl ); +int ssl_write_change_cipher_spec( ssl_context *ssl ); + +int ssl_parse_finished( ssl_context *ssl ); +int ssl_write_finished( ssl_context *ssl ); + +void ssl_optimize_checksum( ssl_context *ssl, + const ssl_ciphersuite_t *ciphersuite_info ); + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +int ssl_psk_derive_premaster( ssl_context *ssl, key_exchange_type_t key_ex ); +#endif + +#if defined(POLARSSL_PK_C) +unsigned char ssl_sig_from_pk( pk_context *pk ); +pk_type_t ssl_pk_alg_from_sig( unsigned char sig ); +#endif + +md_type_t ssl_md_alg_from_hash( unsigned char hash ); + +#if defined(POLARSSL_SSL_SET_CURVES) +int ssl_curve_is_acceptable( const ssl_context *ssl, ecp_group_id grp_id ); +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) +static inline pk_context *ssl_own_key( ssl_context *ssl ) +{ + return( ssl->handshake->key_cert == NULL ? NULL + : ssl->handshake->key_cert->key ); +} + +static inline x509_crt *ssl_own_cert( ssl_context *ssl ) +{ + return( ssl->handshake->key_cert == NULL ? NULL + : ssl->handshake->key_cert->cert ); +} + +/* + * Check usage of a certificate wrt extensions: + * keyUsage, extendedKeyUsage (later), and nSCertType (later). + * + * Warning: cert_endpoint is the endpoint of the cert (ie, of our peer when we + * check a cert we received from them)! + * + * Return 0 if everything is OK, -1 if not. + */ +int ssl_check_cert_usage( const x509_crt *cert, + const ssl_ciphersuite_t *ciphersuite, + int cert_endpoint ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +/* constant-time buffer comparison */ +static inline int safer_memcmp( const void *a, const void *b, size_t n ) +{ + size_t i; + const unsigned char *A = (const unsigned char *) a; + const unsigned char *B = (const unsigned char *) b; + unsigned char diff = 0; + + for( i = 0; i < n; i++ ) + diff |= A[i] ^ B[i]; + + return( diff ); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ssl.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_cache.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_cache.h new file mode 100644 index 0000000..918fb60 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_cache.h @@ -0,0 +1,147 @@ +/** + * \file ssl_cache.h + * + * \brief SSL session cache implementation + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SSL_CACHE_H +#define POLARSSL_SSL_CACHE_H + +#include "ssl.h" + +#if defined(POLARSSL_THREADING_C) +#include "threading.h" +#endif + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(SSL_CACHE_DEFAULT_TIMEOUT) +#define SSL_CACHE_DEFAULT_TIMEOUT 86400 /*!< 1 day */ +#endif + +#if !defined(SSL_CACHE_DEFAULT_MAX_ENTRIES) +#define SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /*!< Maximum entries in cache */ +#endif + +/* \} name SECTION: Module settings */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _ssl_cache_context ssl_cache_context; +typedef struct _ssl_cache_entry ssl_cache_entry; + +/** + * \brief This structure is used for storing cache entries + */ +struct _ssl_cache_entry +{ +#if defined(POLARSSL_HAVE_TIME) + time_t timestamp; /*!< entry timestamp */ +#endif + ssl_session session; /*!< entry session */ +#if defined(POLARSSL_X509_CRT_PARSE_C) + x509_buf peer_cert; /*!< entry peer_cert */ +#endif + ssl_cache_entry *next; /*!< chain pointer */ +}; + +/** + * \brief Cache context + */ +struct _ssl_cache_context +{ + ssl_cache_entry *chain; /*!< start of the chain */ + int timeout; /*!< cache entry timeout */ + int max_entries; /*!< maximum entries */ +#if defined(POLARSSL_THREADING_C) + threading_mutex_t mutex; /*!< mutex */ +#endif +}; + +/** + * \brief Initialize an SSL cache context + * + * \param cache SSL cache context + */ +void ssl_cache_init( ssl_cache_context *cache ); + +/** + * \brief Cache get callback implementation + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param data SSL cache context + * \param session session to retrieve entry for + */ +int ssl_cache_get( void *data, ssl_session *session ); + +/** + * \brief Cache set callback implementation + * (Thread-safe if POLARSSL_THREADING_C is enabled) + * + * \param data SSL cache context + * \param session session to store entry for + */ +int ssl_cache_set( void *data, const ssl_session *session ); + +#if defined(POLARSSL_HAVE_TIME) +/** + * \brief Set the cache timeout + * (Default: SSL_CACHE_DEFAULT_TIMEOUT (1 day)) + * + * A timeout of 0 indicates no timeout. + * + * \param cache SSL cache context + * \param timeout cache entry timeout in seconds + */ +void ssl_cache_set_timeout( ssl_cache_context *cache, int timeout ); +#endif /* POLARSSL_HAVE_TIME */ + +/** + * \brief Set the cache timeout + * (Default: SSL_CACHE_DEFAULT_MAX_ENTRIES (50)) + * + * \param cache SSL cache context + * \param max cache entry maximum + */ +void ssl_cache_set_max_entries( ssl_cache_context *cache, int max ); + +/** + * \brief Free referenced items in a cache context and clear memory + * + * \param cache SSL cache context + */ +void ssl_cache_free( ssl_cache_context *cache ); + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_cache.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_ciphersuites.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_ciphersuites.h new file mode 100644 index 0000000..c4f1ffe --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/ssl_ciphersuites.h @@ -0,0 +1,293 @@ +/** + * \file ssl_ciphersuites.h + * + * \brief SSL Ciphersuites for PolarSSL + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_SSL_CIPHERSUITES_H +#define POLARSSL_SSL_CIPHERSUITES_H + +#include "pk.h" +#include "cipher.h" +#include "md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Supported ciphersuites (Official IANA names) + */ +#define TLS_RSA_WITH_NULL_MD5 0x01 /**< Weak! */ +#define TLS_RSA_WITH_NULL_SHA 0x02 /**< Weak! */ + +#define TLS_RSA_WITH_RC4_128_MD5 0x04 +#define TLS_RSA_WITH_RC4_128_SHA 0x05 +#define TLS_RSA_WITH_DES_CBC_SHA 0x09 /**< Weak! Not in TLS 1.2 */ + +#define TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x0A + +#define TLS_DHE_RSA_WITH_DES_CBC_SHA 0x15 /**< Weak! Not in TLS 1.2 */ +#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x16 + +#define TLS_PSK_WITH_NULL_SHA 0x2C /**< Weak! */ +#define TLS_DHE_PSK_WITH_NULL_SHA 0x2D /**< Weak! */ +#define TLS_RSA_PSK_WITH_NULL_SHA 0x2E /**< Weak! */ +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x2F + +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x33 +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x35 +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x39 + +#define TLS_RSA_WITH_NULL_SHA256 0x3B /**< Weak! */ +#define TLS_RSA_WITH_AES_128_CBC_SHA256 0x3C /**< TLS 1.2 */ +#define TLS_RSA_WITH_AES_256_CBC_SHA256 0x3D /**< TLS 1.2 */ + +#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x41 +#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x45 + +#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x67 /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x6B /**< TLS 1.2 */ + +#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x84 +#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x88 + +#define TLS_PSK_WITH_RC4_128_SHA 0x8A +#define TLS_PSK_WITH_3DES_EDE_CBC_SHA 0x8B +#define TLS_PSK_WITH_AES_128_CBC_SHA 0x8C +#define TLS_PSK_WITH_AES_256_CBC_SHA 0x8D + +#define TLS_DHE_PSK_WITH_RC4_128_SHA 0x8E +#define TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA 0x8F +#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA 0x90 +#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA 0x91 + +#define TLS_RSA_PSK_WITH_RC4_128_SHA 0x92 +#define TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA 0x93 +#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA 0x94 +#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA 0x95 + +#define TLS_RSA_WITH_AES_128_GCM_SHA256 0x9C /**< TLS 1.2 */ +#define TLS_RSA_WITH_AES_256_GCM_SHA384 0x9D /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x9E /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x9F /**< TLS 1.2 */ + +#define TLS_PSK_WITH_AES_128_GCM_SHA256 0xA8 /**< TLS 1.2 */ +#define TLS_PSK_WITH_AES_256_GCM_SHA384 0xA9 /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 0xAA /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 0xAB /**< TLS 1.2 */ +#define TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 0xAC /**< TLS 1.2 */ +#define TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 0xAD /**< TLS 1.2 */ + +#define TLS_PSK_WITH_AES_128_CBC_SHA256 0xAE +#define TLS_PSK_WITH_AES_256_CBC_SHA384 0xAF +#define TLS_PSK_WITH_NULL_SHA256 0xB0 /**< Weak! */ +#define TLS_PSK_WITH_NULL_SHA384 0xB1 /**< Weak! */ + +#define TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 0xB2 +#define TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 0xB3 +#define TLS_DHE_PSK_WITH_NULL_SHA256 0xB4 /**< Weak! */ +#define TLS_DHE_PSK_WITH_NULL_SHA384 0xB5 /**< Weak! */ + +#define TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 0xB6 +#define TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 0xB7 +#define TLS_RSA_PSK_WITH_NULL_SHA256 0xB8 /**< Weak! */ +#define TLS_RSA_PSK_WITH_NULL_SHA384 0xB9 /**< Weak! */ + +#define TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBA /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBE /**< TLS 1.2 */ + +#define TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC0 /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC4 /**< TLS 1.2 */ + +#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 /**< Weak! */ +#define TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002 /**< Not in SSL3! */ +#define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003 /**< Not in SSL3! */ +#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 /**< Not in SSL3! */ +#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 /**< Not in SSL3! */ + +#define TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 /**< Weak! */ +#define TLS_ECDHE_ECDSA_WITH_RC4_128_SHA 0xC007 /**< Not in SSL3! */ +#define TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC008 /**< Not in SSL3! */ +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 /**< Not in SSL3! */ +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A /**< Not in SSL3! */ + +#define TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B /**< Weak! */ +#define TLS_ECDH_RSA_WITH_RC4_128_SHA 0xC00C /**< Not in SSL3! */ +#define TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA 0xC00D /**< Not in SSL3! */ +#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E /**< Not in SSL3! */ +#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F /**< Not in SSL3! */ + +#define TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 /**< Weak! */ +#define TLS_ECDHE_RSA_WITH_RC4_128_SHA 0xC011 /**< Not in SSL3! */ +#define TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA 0xC012 /**< Not in SSL3! */ +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 /**< Not in SSL3! */ +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 /**< Not in SSL3! */ + +#define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A /**< TLS 1.2 */ + +#define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 /**< TLS 1.2 */ + +#define TLS_ECDHE_PSK_WITH_RC4_128_SHA 0xC033 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA 0xC034 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA 0xC035 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA 0xC036 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 0xC037 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 0xC038 /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_NULL_SHA 0xC039 /**< Weak! No SSL3! */ +#define TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A /**< Weak! No SSL3! */ +#define TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B /**< Weak! No SSL3! */ + +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072 /**< Not in SSL3! */ +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073 /**< Not in SSL3! */ +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074 /**< Not in SSL3! */ +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC075 /**< Not in SSL3! */ +#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC076 /**< Not in SSL3! */ +#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC077 /**< Not in SSL3! */ +#define TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC078 /**< Not in SSL3! */ +#define TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC079 /**< Not in SSL3! */ + +#define TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07A /**< TLS 1.2 */ +#define TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07B /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07C /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07D /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC086 /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC087 /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC088 /**< TLS 1.2 */ +#define TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC089 /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08A /**< TLS 1.2 */ +#define TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08B /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08C /**< TLS 1.2 */ +#define TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08D /**< TLS 1.2 */ + +#define TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC08E /**< TLS 1.2 */ +#define TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC08F /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC090 /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC091 /**< TLS 1.2 */ +#define TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC092 /**< TLS 1.2 */ +#define TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC093 /**< TLS 1.2 */ + +#define TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC094 +#define TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC095 +#define TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC096 +#define TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC097 +#define TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC098 +#define TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC099 +#define TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC09A /**< Not in SSL3! */ +#define TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC09B /**< Not in SSL3! */ + +#define TLS_RSA_WITH_AES_128_CCM 0xC09C /**< TLS 1.2 */ +#define TLS_RSA_WITH_AES_256_CCM 0xC09D /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_128_CCM 0xC09E /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_256_CCM 0xC09F /**< TLS 1.2 */ +#define TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 /**< TLS 1.2 */ +#define TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_128_CCM_8 0xC0A2 /**< TLS 1.2 */ +#define TLS_DHE_RSA_WITH_AES_256_CCM_8 0xC0A3 /**< TLS 1.2 */ +#define TLS_PSK_WITH_AES_128_CCM 0xC0A4 /**< TLS 1.2 */ +#define TLS_PSK_WITH_AES_256_CCM 0xC0A5 /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_128_CCM 0xC0A6 /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_256_CCM 0xC0A7 /**< TLS 1.2 */ +#define TLS_PSK_WITH_AES_128_CCM_8 0xC0A8 /**< TLS 1.2 */ +#define TLS_PSK_WITH_AES_256_CCM_8 0xC0A9 /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_128_CCM_8 0xC0AA /**< TLS 1.2 */ +#define TLS_DHE_PSK_WITH_AES_256_CCM_8 0xC0AB /**< TLS 1.2 */ +/* The last two are named with PSK_DHE in the RFC, which looks like a typo */ + +#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_AES_256_CCM 0xC0AD /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 0xC0AE /**< TLS 1.2 */ +#define TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 0xC0AF /**< TLS 1.2 */ + +/* Reminder: update _ssl_premaster_secret when adding a new key exchange */ +typedef enum { + POLARSSL_KEY_EXCHANGE_NONE = 0, + POLARSSL_KEY_EXCHANGE_RSA, + POLARSSL_KEY_EXCHANGE_DHE_RSA, + POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + POLARSSL_KEY_EXCHANGE_PSK, + POLARSSL_KEY_EXCHANGE_DHE_PSK, + POLARSSL_KEY_EXCHANGE_RSA_PSK, + POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + POLARSSL_KEY_EXCHANGE_ECDH_RSA, + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, +} key_exchange_type_t; + +typedef struct _ssl_ciphersuite_t ssl_ciphersuite_t; + +#define POLARSSL_CIPHERSUITE_WEAK 0x01 /**< Weak ciphersuite flag */ +#define POLARSSL_CIPHERSUITE_SHORT_TAG 0x02 /**< Short authentication tag, + eg for CCM_8 */ + +/** + * \brief This structure is used for storing ciphersuite information + */ +struct _ssl_ciphersuite_t +{ + int id; + const char * name; + + cipher_type_t cipher; + md_type_t mac; + key_exchange_type_t key_exchange; + + int min_major_ver; + int min_minor_ver; + int max_major_ver; + int max_minor_ver; + + unsigned char flags; +}; + +const int *ssl_list_ciphersuites( void ); + +const ssl_ciphersuite_t *ssl_ciphersuite_from_string( const char *ciphersuite_name ); +const ssl_ciphersuite_t *ssl_ciphersuite_from_id( int ciphersuite_id ); + +#if defined(POLARSSL_PK_C) +pk_type_t ssl_get_ciphersuite_sig_pk_alg( const ssl_ciphersuite_t *info ); +#endif + +int ssl_ciphersuite_uses_ec( const ssl_ciphersuite_t *info ); +int ssl_ciphersuite_uses_psk( const ssl_ciphersuite_t *info ); + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_ciphersuites.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/threading.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/threading.h new file mode 100644 index 0000000..1fc9f98 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/threading.h @@ -0,0 +1,86 @@ +/** + * \file threading.h + * + * \brief Threading abstraction layer + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_THREADING_H +#define POLARSSL_THREADING_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define POLARSSL_ERR_THREADING_FEATURE_UNAVAILABLE -0x001A /**< The selected feature is not available. */ +#define POLARSSL_ERR_THREADING_BAD_INPUT_DATA -0x001C /**< Bad input parameters to function. */ +#define POLARSSL_ERR_THREADING_MUTEX_ERROR -0x001E /**< Locking / unlocking / free failed with error code. */ + +#if defined(POLARSSL_THREADING_PTHREAD) +#include +typedef pthread_mutex_t threading_mutex_t; +#endif + +#if defined(POLARSSL_THREADING_ALT) +/* You should define the threading_mutex_t type in your header */ +#include "threading_alt.h" + +/** + * \brief Set your alternate threading implementation function + * pointers + * + * \param mutex_init the init function implementation + * \param mutex_free the free function implementation + * \param mutex_lock the lock function implementation + * \param mutex_unlock the unlock function implementation + * + * \return 0 if successful + */ +int threading_set_alt( int (*mutex_init)( threading_mutex_t * ), + int (*mutex_free)( threading_mutex_t * ), + int (*mutex_lock)( threading_mutex_t * ), + int (*mutex_unlock)( threading_mutex_t * ) ); +#endif /* POLARSSL_THREADING_ALT_C */ + +/* + * The function pointers for mutex_init, mutex_free, mutex_ and mutex_unlock + * + * All these functions are expected to work or the result will be undefined. + */ +extern int (*polarssl_mutex_init)( threading_mutex_t *mutex ); +extern int (*polarssl_mutex_free)( threading_mutex_t *mutex ); +extern int (*polarssl_mutex_lock)( threading_mutex_t *mutex ); +extern int (*polarssl_mutex_unlock)( threading_mutex_t *mutex ); + +#ifdef __cplusplus +} +#endif + +#endif /* threading.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/timing.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/timing.h new file mode 100644 index 0000000..383120e --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/timing.h @@ -0,0 +1,98 @@ +/** + * \file timing.h + * + * \brief Portable interface to the CPU cycle counter + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_TIMING_H +#define POLARSSL_TIMING_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if !defined(POLARSSL_TIMING_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief timer structure + */ +struct hr_time +{ + unsigned char opaque[32]; +}; + +extern volatile int alarmed; + +/** + * \brief Return the CPU cycle counter value + */ +unsigned long hardclock( void ); + +/** + * \brief Return the elapsed time in milliseconds + * + * \param val points to a timer structure + * \param reset if set to 1, the timer is restarted + */ +unsigned long get_timer( struct hr_time *val, int reset ); + +/** + * \brief Setup an alarm clock + * + * \param seconds delay before the "alarmed" flag is set + */ +void set_alarm( int seconds ); + +/** + * \brief Sleep for a certain amount of time + * + * \param milliseconds delay in milliseconds + */ +void m_sleep( int milliseconds ); + +#if defined(POLARSSL_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if a test failed + */ +int timing_self_test( int verbose ); +#endif + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_TIMING_ALT */ +#include "timing_alt.h" +#endif /* POLARSSL_TIMING_ALT */ + +#endif /* timing.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/version.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/version.h new file mode 100644 index 0000000..1ee2a3b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/version.h @@ -0,0 +1,114 @@ +/** + * \file version.h + * + * \brief Run-time version information + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This set of compile-time defines and run-time variables can be used to + * determine the version number of the PolarSSL library used. + */ +#ifndef POLARSSL_VERSION_H +#define POLARSSL_VERSION_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +/** + * The version number x.y.z is split into three parts. + * Major, Minor, Patchlevel + */ +#define POLARSSL_VERSION_MAJOR 1 +#define POLARSSL_VERSION_MINOR 3 +#define POLARSSL_VERSION_PATCH 8 + +/** + * The single version number has the following structure: + * MMNNPP00 + * Major version | Minor version | Patch version + */ +#define POLARSSL_VERSION_NUMBER 0x01030800 +#define POLARSSL_VERSION_STRING "1.3.8" +#define POLARSSL_VERSION_STRING_FULL "PolarSSL 1.3.8" + +#if defined(POLARSSL_VERSION_C) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the version number. + * + * \return The constructed version number in the format + * MMNNPP00 (Major, Minor, Patch). + */ +unsigned int version_get_number( void ); + +/** + * Get the version string ("x.y.z"). + * + * \param string The string that will receive the value. + * (Should be at least 9 bytes in size) + */ +void version_get_string( char *string ); + +/** + * Get the full version string ("PolarSSL x.y.z"). + * + * \param string The string that will receive the value. The PolarSSL version + * string will use 18 bytes AT MOST including a terminating + * null byte. + * (So the buffer should be at least 18 bytes to receive this + * version string). + */ +void version_get_string_full( char *string ); + +/** + * \brief Check if support for a feature was compiled into this + * PolarSSL binary. This allows you to see at runtime if the + * library was for instance compiled with or without + * Multi-threading support. + * + * Note: only checks against defines in the sections "System + * support", "PolarSSL modules" and "PolarSSL feature + * support" in config.h + * + * \param feature The string for the define to check (e.g. "POLARSSL_AES_C") + * + * \return 0 if the feature is present, -1 if the feature is not + * present and -2 if support for feature checking as a whole + * was not compiled in. + */ +int version_check_feature( const char *feature ); + +#ifdef __cplusplus +} +#endif + +#endif /* POLARSSL_VERSION_C */ + +#endif /* version.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509.h new file mode 100644 index 0000000..583cb83 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509.h @@ -0,0 +1,317 @@ +/** + * \file x509.h + * + * \brief X.509 generic defines and structures + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_X509_H +#define POLARSSL_X509_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "asn1.h" +#include "pk.h" + +#if defined(POLARSSL_RSA_C) +#include "rsa.h" +#endif + +/** + * \addtogroup x509_module + * \{ + */ + +/** + * \name X509 Error codes + * \{ + */ +#define POLARSSL_ERR_X509_FEATURE_UNAVAILABLE -0x2080 /**< Unavailable feature, e.g. RSA hashing/encryption combination. */ +#define POLARSSL_ERR_X509_UNKNOWN_OID -0x2100 /**< Requested OID is unknown. */ +#define POLARSSL_ERR_X509_INVALID_FORMAT -0x2180 /**< The CRT/CRL/CSR format is invalid, e.g. different type expected. */ +#define POLARSSL_ERR_X509_INVALID_VERSION -0x2200 /**< The CRT/CRL/CSR version element is invalid. */ +#define POLARSSL_ERR_X509_INVALID_SERIAL -0x2280 /**< The serial tag or value is invalid. */ +#define POLARSSL_ERR_X509_INVALID_ALG -0x2300 /**< The algorithm tag or value is invalid. */ +#define POLARSSL_ERR_X509_INVALID_NAME -0x2380 /**< The name tag or value is invalid. */ +#define POLARSSL_ERR_X509_INVALID_DATE -0x2400 /**< The date tag or value is invalid. */ +#define POLARSSL_ERR_X509_INVALID_SIGNATURE -0x2480 /**< The signature tag or value invalid. */ +#define POLARSSL_ERR_X509_INVALID_EXTENSIONS -0x2500 /**< The extension tag or value is invalid. */ +#define POLARSSL_ERR_X509_UNKNOWN_VERSION -0x2580 /**< CRT/CRL/CSR has an unsupported version number. */ +#define POLARSSL_ERR_X509_UNKNOWN_SIG_ALG -0x2600 /**< Signature algorithm (oid) is unsupported. */ +#define POLARSSL_ERR_X509_SIG_MISMATCH -0x2680 /**< Signature algorithms do not match. (see \c ::x509_crt sig_oid) */ +#define POLARSSL_ERR_X509_CERT_VERIFY_FAILED -0x2700 /**< Certificate verification failed, e.g. CRL, CA or signature check failed. */ +#define POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT -0x2780 /**< Format not recognized as DER or PEM. */ +#define POLARSSL_ERR_X509_BAD_INPUT_DATA -0x2800 /**< Input invalid. */ +#define POLARSSL_ERR_X509_MALLOC_FAILED -0x2880 /**< Allocation of memory failed. */ +#define POLARSSL_ERR_X509_FILE_IO_ERROR -0x2900 /**< Read/write of file failed. */ +/* \} name */ + +/** + * \name X509 Verify codes + * \{ + */ +#define BADCERT_EXPIRED 0x01 /**< The certificate validity has expired. */ +#define BADCERT_REVOKED 0x02 /**< The certificate has been revoked (is on a CRL). */ +#define BADCERT_CN_MISMATCH 0x04 /**< The certificate Common Name (CN) does not match with the expected CN. */ +#define BADCERT_NOT_TRUSTED 0x08 /**< The certificate is not correctly signed by the trusted CA. */ +#define BADCRL_NOT_TRUSTED 0x10 /**< CRL is not correctly signed by the trusted CA. */ +#define BADCRL_EXPIRED 0x20 /**< CRL is expired. */ +#define BADCERT_MISSING 0x40 /**< Certificate was missing. */ +#define BADCERT_SKIP_VERIFY 0x80 /**< Certificate verification was skipped. */ +#define BADCERT_OTHER 0x0100 /**< Other reason (can be used by verify callback) */ +#define BADCERT_FUTURE 0x0200 /**< The certificate validity starts in the future. */ +#define BADCRL_FUTURE 0x0400 /**< The CRL is from the future */ +/* \} name */ +/* \} addtogroup x509_module */ + +/* + * X.509 v3 Key Usage Extension flags + */ +#define KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */ +#define KU_NON_REPUDIATION (0x40) /* bit 1 */ +#define KU_KEY_ENCIPHERMENT (0x20) /* bit 2 */ +#define KU_DATA_ENCIPHERMENT (0x10) /* bit 3 */ +#define KU_KEY_AGREEMENT (0x08) /* bit 4 */ +#define KU_KEY_CERT_SIGN (0x04) /* bit 5 */ +#define KU_CRL_SIGN (0x02) /* bit 6 */ + +/* + * Netscape certificate types + * (http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html) + */ + +#define NS_CERT_TYPE_SSL_CLIENT (0x80) /* bit 0 */ +#define NS_CERT_TYPE_SSL_SERVER (0x40) /* bit 1 */ +#define NS_CERT_TYPE_EMAIL (0x20) /* bit 2 */ +#define NS_CERT_TYPE_OBJECT_SIGNING (0x10) /* bit 3 */ +#define NS_CERT_TYPE_RESERVED (0x08) /* bit 4 */ +#define NS_CERT_TYPE_SSL_CA (0x04) /* bit 5 */ +#define NS_CERT_TYPE_EMAIL_CA (0x02) /* bit 6 */ +#define NS_CERT_TYPE_OBJECT_SIGNING_CA (0x01) /* bit 7 */ + +/* + * X.509 extension types + * + * Comments refer to the status for using certificates. Status can be + * different for writing certificates or reading CRLs or CSRs. + */ +#define EXT_AUTHORITY_KEY_IDENTIFIER (1 << 0) +#define EXT_SUBJECT_KEY_IDENTIFIER (1 << 1) +#define EXT_KEY_USAGE (1 << 2) /* Parsed but not used */ +#define EXT_CERTIFICATE_POLICIES (1 << 3) +#define EXT_POLICY_MAPPINGS (1 << 4) +#define EXT_SUBJECT_ALT_NAME (1 << 5) /* Supported (DNS) */ +#define EXT_ISSUER_ALT_NAME (1 << 6) +#define EXT_SUBJECT_DIRECTORY_ATTRS (1 << 7) +#define EXT_BASIC_CONSTRAINTS (1 << 8) /* Supported */ +#define EXT_NAME_CONSTRAINTS (1 << 9) +#define EXT_POLICY_CONSTRAINTS (1 << 10) +#define EXT_EXTENDED_KEY_USAGE (1 << 11) /* Parsed but not used */ +#define EXT_CRL_DISTRIBUTION_POINTS (1 << 12) +#define EXT_INIHIBIT_ANYPOLICY (1 << 13) +#define EXT_FRESHEST_CRL (1 << 14) + +#define EXT_NS_CERT_TYPE (1 << 16) /* Parsed (and then ?) */ + +/* + * Storage format identifiers + * Recognized formats: PEM and DER + */ +#define X509_FORMAT_DER 1 +#define X509_FORMAT_PEM 2 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures for parsing X.509 certificates, CRLs and CSRs + * \{ + */ + +/** + * Type-length-value structure that allows for ASN1 using DER. + */ +typedef asn1_buf x509_buf; + +/** + * Container for ASN1 bit strings. + */ +typedef asn1_bitstring x509_bitstring; + +/** + * Container for ASN1 named information objects. + * It allows for Relative Distinguished Names (e.g. cn=polarssl,ou=code,etc.). + */ +typedef asn1_named_data x509_name; + +/** + * Container for a sequence of ASN.1 items + */ +typedef asn1_sequence x509_sequence; + +/** Container for date and time (precision in seconds). */ +typedef struct _x509_time +{ + int year, mon, day; /**< Date. */ + int hour, min, sec; /**< Time. */ +} +x509_time; + +/** \} name Structures for parsing X.509 certificates, CRLs and CSRs */ +/** \} addtogroup x509_module */ + +/** + * \brief Store the certificate DN in printable form into buf; + * no more than size characters will be written. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param dn The X509 name to represent + * + * \return The amount of data written to the buffer, or -1 in + * case of an error. + */ +int x509_dn_gets( char *buf, size_t size, const x509_name *dn ); + +/** + * \brief Store the certificate serial in printable form into buf; + * no more than size characters will be written. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param serial The X509 serial to represent + * + * \return The amount of data written to the buffer, or -1 in + * case of an error. + */ +int x509_serial_gets( char *buf, size_t size, const x509_buf *serial ); + +/** + * \brief Give an known OID, return its descriptive string. + * (Deprecated. Use oid_get_extended_key_usage() instead.) + * Warning: only works for extended_key_usage OIDs! + * + * \param oid buffer containing the oid + * + * \return Return a string if the OID is known, + * or NULL otherwise. + */ +const char *x509_oid_get_description( x509_buf *oid ); + +/** + * \brief Give an OID, return a string version of its OID number. + * (Deprecated. Use oid_get_numeric_string() instead) + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param oid Buffer containing the OID + * + * \return Length of the string written (excluding final NULL) or + * POLARSSL_ERR_OID_BUF_TO_SMALL in case of error + */ +int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid ); + +/** + * \brief Check a given x509_time against the system time and check + * if it is not expired. + * + * \param time x509_time to check + * + * \return 0 if the x509_time is still valid, + * 1 otherwise. + */ +int x509_time_expired( const x509_time *time ); + +/** + * \brief Check a given x509_time against the system time and check + * if it is not from the future. + * + * \param time x509_time to check + * + * \return 0 if the x509_time is already valid, + * 1 otherwise. + */ +int x509_time_future( const x509_time *time ); + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int x509_self_test( int verbose ); + +/* + * Internal module functions. You probably do not want to use these unless you + * know you do. + */ +int x509_get_name( unsigned char **p, const unsigned char *end, + x509_name *cur ); +int x509_get_alg_null( unsigned char **p, const unsigned char *end, + x509_buf *alg ); +int x509_get_alg( unsigned char **p, const unsigned char *end, + x509_buf *alg, x509_buf *params ); +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) +int x509_get_rsassa_pss_params( const x509_buf *params, + md_type_t *md_alg, md_type_t *mgf_md, + int *salt_len ); +#endif +int x509_get_sig( unsigned char **p, const unsigned char *end, x509_buf *sig ); +int x509_get_sig_alg( const x509_buf *sig_oid, const x509_buf *sig_params, + md_type_t *md_alg, pk_type_t *pk_alg, + void **sig_opts ); +int x509_get_time( unsigned char **p, const unsigned char *end, + x509_time *time ); +int x509_get_serial( unsigned char **p, const unsigned char *end, + x509_buf *serial ); +int x509_get_ext( unsigned char **p, const unsigned char *end, + x509_buf *ext, int tag ); +int x509_load_file( const char *path, unsigned char **buf, size_t *n ); +int x509_sig_alg_gets( char *buf, size_t size, const x509_buf *sig_oid, + pk_type_t pk_alg, md_type_t md_alg, + const void *sig_opts ); +int x509_key_size_helper( char *buf, size_t size, const char *name ); +int x509_string_to_names( asn1_named_data **head, const char *name ); +int x509_set_extension( asn1_named_data **head, const char *oid, size_t oid_len, + int critical, const unsigned char *val, + size_t val_len ); +int x509_write_extensions( unsigned char **p, unsigned char *start, + asn1_named_data *first ); +int x509_write_names( unsigned char **p, unsigned char *start, + asn1_named_data *first ); +int x509_write_sig( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + unsigned char *sig, size_t size ); + +#ifdef __cplusplus +} +#endif + +#endif /* x509.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crl.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crl.h new file mode 100644 index 0000000..9f597a8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crl.h @@ -0,0 +1,162 @@ +/** + * \file x509_crl.h + * + * \brief X.509 certificate revocation list parsing + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_X509_CRL_H +#define POLARSSL_X509_CRL_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures and functions for parsing CRLs + * \{ + */ + +/** + * Certificate revocation list entry. + * Contains the CA-specific serial numbers and revocation dates. + */ +typedef struct _x509_crl_entry +{ + x509_buf raw; + + x509_buf serial; + + x509_time revocation_date; + + x509_buf entry_ext; + + struct _x509_crl_entry *next; +} +x509_crl_entry; + +/** + * Certificate revocation list structure. + * Every CRL may have multiple entries. + */ +typedef struct _x509_crl +{ + x509_buf raw; /**< The raw certificate data (DER). */ + x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ + + int version; /**< CRL version (1=v1, 2=v2) */ + x509_buf sig_oid1; + + x509_buf issuer_raw; /**< The raw issuer data (DER). */ + + x509_name issuer; /**< The parsed issuer data (named information object). */ + + x509_time this_update; + x509_time next_update; + + x509_crl_entry entry; /**< The CRL entries containing the certificate revocation times for this CA. */ + + x509_buf crl_ext; + + x509_buf sig_oid2; + x509_buf sig; + md_type_t sig_md; /**< Internal representation of the MD algorithm of the signature algorithm, e.g. POLARSSL_MD_SHA256 */ + pk_type_t sig_pk; /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. POLARSSL_PK_RSA */ + void *sig_opts; /**< Signature options to be passed to pk_verify_ext(), e.g. for RSASSA-PSS */ + + struct _x509_crl *next; +} +x509_crl; + +/** + * \brief Parse one or more CRLs and add them + * to the chained list + * + * \param chain points to the start of the chain + * \param buf buffer holding the CRL data + * \param buflen size of the buffer + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int x509_crl_parse( x509_crl *chain, const unsigned char *buf, size_t buflen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Load one or more CRLs and add them + * to the chained list + * + * \param chain points to the start of the chain + * \param path filename to read the CRLs from + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int x509_crl_parse_file( x509_crl *chain, const char *path ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Returns an informational string about the CRL. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param crl The X509 CRL to represent + * + * \return The amount of data written to the buffer, or -1 in + * case of an error. + */ +int x509_crl_info( char *buf, size_t size, const char *prefix, + const x509_crl *crl ); + +/** + * \brief Initialize a CRL (chain) + * + * \param crl CRL chain to initialize + */ +void x509_crl_init( x509_crl *crl ); + +/** + * \brief Unallocate all CRL data + * + * \param crl CRL chain to free + */ +void x509_crl_free( x509_crl *crl ); + +/* \} name */ +/* \} addtogroup x509_module */ + +#ifdef __cplusplus +} +#endif + +#endif /* x509_crl.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crt.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crt.h new file mode 100644 index 0000000..4bf8e56 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_crt.h @@ -0,0 +1,558 @@ +/** + * \file x509_crt.h + * + * \brief X.509 certificate parsing and writing + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_X509_CRT_H +#define POLARSSL_X509_CRT_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "x509.h" + +#include "x509_crl.h" + +/** + * \addtogroup x509_module + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Structures and functions for parsing and writing X.509 certificates + * \{ + */ + +/** + * Container for an X.509 certificate. The certificate may be chained. + */ +typedef struct _x509_crt +{ + x509_buf raw; /**< The raw certificate data (DER). */ + x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ + + int version; /**< The X.509 version. (1=v1, 2=v2, 3=v3) */ + x509_buf serial; /**< Unique id for certificate issued by a specific CA. */ + x509_buf sig_oid1; /**< Signature algorithm, e.g. sha1RSA */ + + x509_buf issuer_raw; /**< The raw issuer data (DER). Used for quick comparison. */ + x509_buf subject_raw; /**< The raw subject data (DER). Used for quick comparison. */ + + x509_name issuer; /**< The parsed issuer data (named information object). */ + x509_name subject; /**< The parsed subject data (named information object). */ + + x509_time valid_from; /**< Start time of certificate validity. */ + x509_time valid_to; /**< End time of certificate validity. */ + + pk_context pk; /**< Container for the public key context. */ + + x509_buf issuer_id; /**< Optional X.509 v2/v3 issuer unique identifier. */ + x509_buf subject_id; /**< Optional X.509 v2/v3 subject unique identifier. */ + x509_buf v3_ext; /**< Optional X.509 v3 extensions. */ + x509_sequence subject_alt_names; /**< Optional list of Subject Alternative Names (Only dNSName supported). */ + + int ext_types; /**< Bit string containing detected and parsed extensions */ + int ca_istrue; /**< Optional Basic Constraint extension value: 1 if this certificate belongs to a CA, 0 otherwise. */ + int max_pathlen; /**< Optional Basic Constraint extension value: The maximum path length to the root certificate. Path length is 1 higher than RFC 5280 'meaning', so 1+ */ + + unsigned char key_usage; /**< Optional key usage extension value: See the values in x509.h */ + + x509_sequence ext_key_usage; /**< Optional list of extended key usage OIDs. */ + + unsigned char ns_cert_type; /**< Optional Netscape certificate type extension value: See the values in x509.h */ + + x509_buf sig_oid2; /**< Signature algorithm. Must match sig_oid1. */ + x509_buf sig; /**< Signature: hash of the tbs part signed with the private key. */ + md_type_t sig_md; /**< Internal representation of the MD algorithm of the signature algorithm, e.g. POLARSSL_MD_SHA256 */ + pk_type_t sig_pk; /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. POLARSSL_PK_RSA */ + void *sig_opts; /**< Signature options to be passed to pk_verify_ext(), e.g. for RSASSA-PSS */ + + struct _x509_crt *next; /**< Next certificate in the CA-chain. */ +} +x509_crt; + +#define X509_CRT_VERSION_1 0 +#define X509_CRT_VERSION_2 1 +#define X509_CRT_VERSION_3 2 + +#define X509_RFC5280_MAX_SERIAL_LEN 32 +#define X509_RFC5280_UTC_TIME_LEN 15 + +/** + * Container for writing a certificate (CRT) + */ +typedef struct _x509write_cert +{ + int version; + mpi serial; + pk_context *subject_key; + pk_context *issuer_key; + asn1_named_data *subject; + asn1_named_data *issuer; + md_type_t md_alg; + char not_before[X509_RFC5280_UTC_TIME_LEN + 1]; + char not_after[X509_RFC5280_UTC_TIME_LEN + 1]; + asn1_named_data *extensions; +} +x509write_cert; + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/** + * \brief Parse a single DER formatted certificate and add it + * to the chained list. + * + * \param chain points to the start of the chain + * \param buf buffer holding the certificate DER data + * \param buflen size of the buffer + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int x509_crt_parse_der( x509_crt *chain, const unsigned char *buf, + size_t buflen ); + +/** + * \brief Parse one or more certificates and add them + * to the chained list. Parses permissively. If some + * certificates can be parsed, the result is the number + * of failed certificates it encountered. If none complete + * correctly, the first error is returned. + * + * \param chain points to the start of the chain + * \param buf buffer holding the certificate data + * \param buflen size of the buffer + * + * \return 0 if all certificates parsed successfully, a positive number + * if partly successful or a specific X509 or PEM error code + */ +int x509_crt_parse( x509_crt *chain, const unsigned char *buf, size_t buflen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Load one or more certificates and add them + * to the chained list. Parses permissively. If some + * certificates can be parsed, the result is the number + * of failed certificates it encountered. If none complete + * correctly, the first error is returned. + * + * \param chain points to the start of the chain + * \param path filename to read the certificates from + * + * \return 0 if all certificates parsed successfully, a positive number + * if partly successful or a specific X509 or PEM error code + */ +int x509_crt_parse_file( x509_crt *chain, const char *path ); + +/** + * \brief Load one or more certificate files from a path and add them + * to the chained list. Parses permissively. If some + * certificates can be parsed, the result is the number + * of failed certificates it encountered. If none complete + * correctly, the first error is returned. + * + * \warning This function is NOT thread-safe unless + * POLARSSL_THREADING_PTHREADS is defined. If you're using an + * alternative threading implementation, you should either use + * this function only in the main thread, or mutex it. + * + * \param chain points to the start of the chain + * \param path directory / folder to read the certificate files from + * + * \return 0 if all certificates parsed successfully, a positive number + * if partly successful or a specific X509 or PEM error code + */ +int x509_crt_parse_path( x509_crt *chain, const char *path ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Returns an informational string about the + * certificate. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param crt The X509 certificate to represent + * + * \return The amount of data written to the buffer, or -1 in + * case of an error. + */ +int x509_crt_info( char *buf, size_t size, const char *prefix, + const x509_crt *crt ); + +/** + * \brief Verify the certificate signature + * + * The verify callback is a user-supplied callback that + * can clear / modify / add flags for a certificate. If set, + * the verification callback is called for each + * certificate in the chain (from the trust-ca down to the + * presented crt). The parameters for the callback are: + * (void *parameter, x509_crt *crt, int certificate_depth, + * int *flags). With the flags representing current flags for + * that specific certificate and the certificate depth from + * the bottom (Peer cert depth = 0). + * + * All flags left after returning from the callback + * are also returned to the application. The function should + * return 0 for anything but a fatal error. + * + * \param crt a certificate to be verified + * \param trust_ca the trusted CA chain + * \param ca_crl the CRL chain for trusted CA's + * \param cn expected Common Name (can be set to + * NULL if the CN must not be verified) + * \param flags result of the verification + * \param f_vrfy verification function + * \param p_vrfy verification parameter + * + * \return 0 if successful or POLARSSL_ERR_X509_SIG_VERIFY_FAILED, + * in which case *flags will have one or more of + * the following values set: + * BADCERT_EXPIRED -- + * BADCERT_REVOKED -- + * BADCERT_CN_MISMATCH -- + * BADCERT_NOT_TRUSTED + * or another error in case of a fatal error encountered + * during the verification process. + */ +int x509_crt_verify( x509_crt *crt, + x509_crt *trust_ca, + x509_crl *ca_crl, + const char *cn, int *flags, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ); + +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) +/** + * \brief Check usage of certificate against keyUsage extension. + * + * \param crt Leaf certificate used. + * \param usage Intended usage(s) (eg KU_KEY_ENCIPHERMENT before using the + * certificate to perform an RSA key exchange). + * + * \return 0 is these uses of the certificate are allowed, + * POLARSSL_ERR_X509_BAD_INPUT_DATA if the keyUsage extension + * is present but does not contain all the bits set in the + * usage argument. + * + * \note You should only call this function on leaf certificates, on + * (intermediate) CAs the keyUsage extension is automatically + * checked by \c x509_crt_verify(). + */ +int x509_crt_check_key_usage( const x509_crt *crt, int usage ); +#endif /* POLARSSL_X509_CHECK_KEY_USAGE) */ + +#if defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) +/** + * \brief Check usage of certificate against extentedJeyUsage. + * + * \param crt Leaf certificate used. + * \param usage_oid Intended usage (eg OID_SERVER_AUTH or OID_CLIENT_AUTH). + * \param usage_len Length of usage_oid (eg given by OID_SIZE()). + * + * \return 0 is this use of the certificate is allowed, + * POLARSSL_ERR_X509_BAD_INPUT_DATA if not. + * + * \note Usually only makes sense on leaf certificates. + */ +int x509_crt_check_extended_key_usage( const x509_crt *crt, + const char *usage_oid, + size_t usage_len ); +#endif /* POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) */ + +#if defined(POLARSSL_X509_CRL_PARSE_C) +/** + * \brief Verify the certificate revocation status + * + * \param crt a certificate to be verified + * \param crl the CRL to verify against + * + * \return 1 if the certificate is revoked, 0 otherwise + * + */ +int x509_crt_revoked( const x509_crt *crt, const x509_crl *crl ); +#endif /* POLARSSL_X509_CRL_PARSE_C */ + +/** + * \brief Initialize a certificate (chain) + * + * \param crt Certificate chain to initialize + */ +void x509_crt_init( x509_crt *crt ); + +/** + * \brief Unallocate all certificate data + * + * \param crt Certificate chain to free + */ +void x509_crt_free( x509_crt *crt ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +/* \} name */ +/* \} addtogroup x509_module */ + +#if defined(POLARSSL_X509_CRT_WRITE_C) +/** + * \brief Initialize a CRT writing context + * + * \param ctx CRT context to initialize + */ +void x509write_crt_init( x509write_cert *ctx ); + +/** + * \brief Set the verion for a Certificate + * Default: X509_CRT_VERSION_3 + * + * \param ctx CRT context to use + * \param version version to set (X509_CRT_VERSION_1, X509_CRT_VERSION_2 or + * X509_CRT_VERSION_3) + */ +void x509write_crt_set_version( x509write_cert *ctx, int version ); + +/** + * \brief Set the serial number for a Certificate. + * + * \param ctx CRT context to use + * \param serial serial number to set + * + * \return 0 if successful + */ +int x509write_crt_set_serial( x509write_cert *ctx, const mpi *serial ); + +/** + * \brief Set the validity period for a Certificate + * Timestamps should be in string format for UTC timezone + * i.e. "YYYYMMDDhhmmss" + * e.g. "20131231235959" for December 31st 2013 + * at 23:59:59 + * + * \param ctx CRT context to use + * \param not_before not_before timestamp + * \param not_after not_after timestamp + * + * \return 0 if timestamp was parsed successfully, or + * a specific error code + */ +int x509write_crt_set_validity( x509write_cert *ctx, const char *not_before, + const char *not_after ); + +/** + * \brief Set the issuer name for a Certificate + * Issuer names should contain a comma-separated list + * of OID types and values: + * e.g. "C=NL,O=Offspark,CN=PolarSSL CA" + * + * \param ctx CRT context to use + * \param issuer_name issuer name to set + * + * \return 0 if issuer name was parsed successfully, or + * a specific error code + */ +int x509write_crt_set_issuer_name( x509write_cert *ctx, + const char *issuer_name ); + +/** + * \brief Set the subject name for a Certificate + * Subject names should contain a comma-separated list + * of OID types and values: + * e.g. "C=NL,O=Offspark,CN=PolarSSL Server 1" + * + * \param ctx CRT context to use + * \param subject_name subject name to set + * + * \return 0 if subject name was parsed successfully, or + * a specific error code + */ +int x509write_crt_set_subject_name( x509write_cert *ctx, + const char *subject_name ); + +/** + * \brief Set the subject public key for the certificate + * + * \param ctx CRT context to use + * \param key public key to include + */ +void x509write_crt_set_subject_key( x509write_cert *ctx, pk_context *key ); + +/** + * \brief Set the issuer key used for signing the certificate + * + * \param ctx CRT context to use + * \param key private key to sign with + */ +void x509write_crt_set_issuer_key( x509write_cert *ctx, pk_context *key ); + +/** + * \brief Set the MD algorithm to use for the signature + * (e.g. POLARSSL_MD_SHA1) + * + * \param ctx CRT context to use + * \param md_alg MD algorithm to use + */ +void x509write_crt_set_md_alg( x509write_cert *ctx, md_type_t md_alg ); + +/** + * \brief Generic function to add to or replace an extension in the + * CRT + * + * \param ctx CRT context to use + * \param oid OID of the extension + * \param oid_len length of the OID + * \param critical if the extension is critical (per the RFC's definition) + * \param val value of the extension OCTET STRING + * \param val_len length of the value data + * + * \return 0 if successful, or a POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_extension( x509write_cert *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len ); + +/** + * \brief Set the basicConstraints extension for a CRT + * + * \param ctx CRT context to use + * \param is_ca is this a CA certificate + * \param max_pathlen maximum length of certificate chains below this + * certificate (only for CA certificates, -1 is + * inlimited) + * + * \return 0 if successful, or a POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_basic_constraints( x509write_cert *ctx, + int is_ca, int max_pathlen ); + +#if defined(POLARSSL_SHA1_C) +/** + * \brief Set the subjectKeyIdentifier extension for a CRT + * Requires that x509write_crt_set_subject_key() has been + * called before + * + * \param ctx CRT context to use + * + * \return 0 if successful, or a POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_subject_key_identifier( x509write_cert *ctx ); + +/** + * \brief Set the authorityKeyIdentifier extension for a CRT + * Requires that x509write_crt_set_issuer_key() has been + * called before + * + * \param ctx CRT context to use + * + * \return 0 if successful, or a POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_authority_key_identifier( x509write_cert *ctx ); +#endif /* POLARSSL_SHA1_C */ + +/** + * \brief Set the Key Usage Extension flags + * (e.g. KU_DIGITAL_SIGNATURE | KU_KEY_CERT_SIGN) + * + * \param ctx CRT context to use + * \param key_usage key usage flags to set + * + * \return 0 if successful, or POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_key_usage( x509write_cert *ctx, unsigned char key_usage ); + +/** + * \brief Set the Netscape Cert Type flags + * (e.g. NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_EMAIL) + * + * \param ctx CRT context to use + * \param ns_cert_type Netscape Cert Type flags to set + * + * \return 0 if successful, or POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_crt_set_ns_cert_type( x509write_cert *ctx, + unsigned char ns_cert_type ); + +/** + * \brief Free the contents of a CRT write context + * + * \param ctx CRT context to free + */ +void x509write_crt_free( x509write_cert *ctx ); + +/** + * \brief Write a built up certificate to a X509 DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx certificate to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function (for signature, see note) + * \param p_rng RNG parameter + * + * \return length of data written if successful, or a specific + * error code + * + * \note f_rng may be NULL if RSA is used for signature and the + * signature is made offline (otherwise f_rng is desirable + * for countermeasures against timing attacks). + * ECDSA signatures always require a non-NULL f_rng. + */ +int x509write_crt_der( x509write_cert *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +#if defined(POLARSSL_PEM_WRITE_C) +/** + * \brief Write a built up certificate to a X509 PEM string + * + * \param ctx certificate to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function (for signature, see note) + * \param p_rng RNG parameter + * + * \return 0 successful, or a specific error code + * + * \note f_rng may be NULL if RSA is used for signature and the + * signature is made offline (otherwise f_rng is desirable + * for countermeasures against timing attacks). + * ECDSA signatures always require a non-NULL f_rng. + */ +int x509write_crt_pem( x509write_cert *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +#endif /* POLARSSL_PEM_WRITE_C */ +#endif /* POLARSSL_X509_CRT_WRITE_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* x509_crt.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_csr.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_csr.h new file mode 100644 index 0000000..6591e38 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/x509_csr.h @@ -0,0 +1,295 @@ +/** + * \file x509_csr.h + * + * \brief X.509 certificate signing request parsing and writing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_X509_CSR_H +#define POLARSSL_X509_CSR_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include "x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures and functions for X.509 Certificate Signing Requests (CSR) + * \{ + */ + +/** + * Certificate Signing Request (CSR) structure. + */ +typedef struct _x509_csr +{ + x509_buf raw; /**< The raw CSR data (DER). */ + x509_buf cri; /**< The raw CertificateRequestInfo body (DER). */ + + int version; /**< CSR version (1=v1). */ + + x509_buf subject_raw; /**< The raw subject data (DER). */ + x509_name subject; /**< The parsed subject data (named information object). */ + + pk_context pk; /**< Container for the public key context. */ + + x509_buf sig_oid; + x509_buf sig; + md_type_t sig_md; /**< Internal representation of the MD algorithm of the signature algorithm, e.g. POLARSSL_MD_SHA256 */ + pk_type_t sig_pk; /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. POLARSSL_PK_RSA */ + void *sig_opts; /**< Signature options to be passed to pk_verify_ext(), e.g. for RSASSA-PSS */ +} +x509_csr; + +/** + * Container for writing a CSR + */ +typedef struct _x509write_csr +{ + pk_context *key; + asn1_named_data *subject; + md_type_t md_alg; + asn1_named_data *extensions; +} +x509write_csr; + +#if defined(POLARSSL_X509_CSR_PARSE_C) +/** + * \brief Load a Certificate Signing Request (CSR) in DER format + * + * \param csr CSR context to fill + * \param buf buffer holding the CRL data + * \param buflen size of the buffer + * + * \return 0 if successful, or a specific X509 error code + */ +int x509_csr_parse_der( x509_csr *csr, + const unsigned char *buf, size_t buflen ); + +/** + * \brief Load a Certificate Signing Request (CSR), DER or PEM format + * + * \param csr CSR context to fill + * \param buf buffer holding the CRL data + * \param buflen size of the buffer + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int x509_csr_parse( x509_csr *csr, const unsigned char *buf, size_t buflen ); + +#if defined(POLARSSL_FS_IO) +/** + * \brief Load a Certificate Signing Request (CSR) + * + * \param csr CSR context to fill + * \param path filename to read the CSR from + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int x509_csr_parse_file( x509_csr *csr, const char *path ); +#endif /* POLARSSL_FS_IO */ + +/** + * \brief Returns an informational string about the + * CSR. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param csr The X509 CSR to represent + * + * \return The length of the string written (exluding the terminating + * null byte), or a negative value in case of an error. + */ +int x509_csr_info( char *buf, size_t size, const char *prefix, + const x509_csr *csr ); + +/** + * \brief Initialize a CSR + * + * \param csr CSR to initialize + */ +void x509_csr_init( x509_csr *csr ); + +/** + * \brief Unallocate all CSR data + * + * \param csr CSR to free + */ +void x509_csr_free( x509_csr *csr ); +#endif /* POLARSSL_X509_CSR_PARSE_C */ + +/* \} name */ +/* \} addtogroup x509_module */ + +#if defined(POLARSSL_X509_CSR_WRITE_C) +/** + * \brief Initialize a CSR context + * + * \param ctx CSR context to initialize + */ +void x509write_csr_init( x509write_csr *ctx ); + +/** + * \brief Set the subject name for a CSR + * Subject names should contain a comma-separated list + * of OID types and values: + * e.g. "C=NL,O=Offspark,CN=PolarSSL Server 1" + * + * \param ctx CSR context to use + * \param subject_name subject name to set + * + * \return 0 if subject name was parsed successfully, or + * a specific error code + */ +int x509write_csr_set_subject_name( x509write_csr *ctx, + const char *subject_name ); + +/** + * \brief Set the key for a CSR (public key will be included, + * private key used to sign the CSR when writing it) + * + * \param ctx CSR context to use + * \param key Asymetric key to include + */ +void x509write_csr_set_key( x509write_csr *ctx, pk_context *key ); + +/** + * \brief Set the MD algorithm to use for the signature + * (e.g. POLARSSL_MD_SHA1) + * + * \param ctx CSR context to use + * \param md_alg MD algorithm to use + */ +void x509write_csr_set_md_alg( x509write_csr *ctx, md_type_t md_alg ); + +/** + * \brief Set the Key Usage Extension flags + * (e.g. KU_DIGITAL_SIGNATURE | KU_KEY_CERT_SIGN) + * + * \param ctx CSR context to use + * \param key_usage key usage flags to set + * + * \return 0 if successful, or POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_csr_set_key_usage( x509write_csr *ctx, unsigned char key_usage ); + +/** + * \brief Set the Netscape Cert Type flags + * (e.g. NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_EMAIL) + * + * \param ctx CSR context to use + * \param ns_cert_type Netscape Cert Type flags to set + * + * \return 0 if successful, or POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_csr_set_ns_cert_type( x509write_csr *ctx, + unsigned char ns_cert_type ); + +/** + * \brief Generic function to add to or replace an extension in the + * CSR + * + * \param ctx CSR context to use + * \param oid OID of the extension + * \param oid_len length of the OID + * \param val value of the extension OCTET STRING + * \param val_len length of the value data + * + * \return 0 if successful, or a POLARSSL_ERR_X509WRITE_MALLOC_FAILED + */ +int x509write_csr_set_extension( x509write_csr *ctx, + const char *oid, size_t oid_len, + const unsigned char *val, size_t val_len ); + +/** + * \brief Free the contents of a CSR context + * + * \param ctx CSR context to free + */ +void x509write_csr_free( x509write_csr *ctx ); + +/** + * \brief Write a CSR (Certificate Signing Request) to a + * DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx CSR to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function (for signature, see note) + * \param p_rng RNG parameter + * + * \return length of data written if successful, or a specific + * error code + * + * \note f_rng may be NULL if RSA is used for signature and the + * signature is made offline (otherwise f_rng is desirable + * for countermeasures against timing attacks). + * ECDSA signatures always require a non-NULL f_rng. + */ +int x509write_csr_der( x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); + +#if defined(POLARSSL_PEM_WRITE_C) +/** + * \brief Write a CSR (Certificate Signing Request) to a + * PEM string + * + * \param ctx CSR to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function (for signature, see note) + * \param p_rng RNG parameter + * + * \return 0 successful, or a specific error code + * + * \note f_rng may be NULL if RSA is used for signature and the + * signature is made offline (otherwise f_rng is desirable + * for couermeasures against timing attacks). + * ECDSA signatures always require a non-NULL f_rng. + */ +int x509write_csr_pem( x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ); +#endif /* POLARSSL_PEM_WRITE_C */ +#endif /* POLARSSL_X509_CSR_WRITE_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* x509_csr.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/include/polarssl/xtea.h b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/xtea.h new file mode 100644 index 0000000..794c5ef --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/include/polarssl/xtea.h @@ -0,0 +1,149 @@ +/** + * \file xtea.h + * + * \brief XTEA block cipher (32-bit) + * + * Copyright (C) 2006-2013, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef POLARSSL_XTEA_H +#define POLARSSL_XTEA_H + +#if !defined(POLARSSL_CONFIG_FILE) +#include "config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#define XTEA_ENCRYPT 1 +#define XTEA_DECRYPT 0 + +#define POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH -0x0028 /**< The data input has an invalid length. */ + +#if !defined(POLARSSL_XTEA_ALT) +// Regular implementation +// + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief XTEA context structure + */ +typedef struct +{ + uint32_t k[4]; /*!< key */ +} +xtea_context; + +/** + * \brief Initialize XTEA context + * + * \param ctx XTEA context to be initialized + */ +void xtea_init( xtea_context *ctx ); + +/** + * \brief Clear XTEA context + * + * \param ctx XTEA context to be cleared + */ +void xtea_free( xtea_context *ctx ); + +/** + * \brief XTEA key schedule + * + * \param ctx XTEA context to be initialized + * \param key the secret key + */ +void xtea_setup( xtea_context *ctx, const unsigned char key[16] ); + +/** + * \brief XTEA cipher function + * + * \param ctx XTEA context + * \param mode XTEA_ENCRYPT or XTEA_DECRYPT + * \param input 8-byte input block + * \param output 8-byte output block + * + * \return 0 if successful + */ +int xtea_crypt_ecb( xtea_context *ctx, + int mode, + const unsigned char input[8], + unsigned char output[8] ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/** + * \brief XTEA CBC cipher function + * + * \param ctx XTEA context + * \param mode XTEA_ENCRYPT or XTEA_DECRYPT + * \param length the length of input, multiple of 8 + * \param iv initialization vector for CBC mode + * \param input input block + * \param output output block + * + * \return 0 if successful, + * POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH if the length % 8 != 0 + */ +int xtea_crypt_cbc( xtea_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#ifdef __cplusplus +} +#endif + +#else /* POLARSSL_XTEA_ALT */ +#include "xtea_alt.h" +#endif /* POLARSSL_XTEA_ALT */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int xtea_self_test( int verbose ); + +#ifdef __cplusplus +} +#endif + +#endif /* xtea.h */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/.gitignore b/component/common/network/ssl/polarssl-1.3.8/library/.gitignore new file mode 100644 index 0000000..9d80fa4 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/.gitignore @@ -0,0 +1,2 @@ +*.o +libpolarssl* diff --git a/component/common/network/ssl/polarssl-1.3.8/library/CMakeLists.txt b/component/common/network/ssl/polarssl-1.3.8/library/CMakeLists.txt new file mode 100644 index 0000000..bc986ee --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/CMakeLists.txt @@ -0,0 +1,136 @@ +option(USE_STATIC_POLARSSL_LIBRARY "Build PolarSSL static library." ON) +option(USE_SHARED_POLARSSL_LIBRARY "Build PolarSSL shared library." OFF) +option(LINK_WITH_PTHREAD "Explicitly link PolarSSL library to pthread." OFF) + +set(src + aes.c + aesni.c + arc4.c + asn1parse.c + asn1write.c + base64.c + bignum.c + blowfish.c + camellia.c + ccm.c + certs.c + cipher.c + cipher_wrap.c + ctr_drbg.c + debug.c + des.c + dhm.c + ecp.c + ecp_curves.c + ecdh.c + ecdsa.c + entropy.c + entropy_poll.c + error.c + gcm.c + havege.c + hmac_drbg.c + md.c + md_wrap.c + md2.c + md4.c + md5.c + memory_buffer_alloc.c + net.c + oid.c + padlock.c + pbkdf2.c + pem.c + pkcs5.c + pkcs11.c + pkcs12.c + pk.c + pk_wrap.c + pkparse.c + pkwrite.c + platform.c + ripemd160.c + rsa.c + sha1.c + sha256.c + sha512.c + ssl_cache.c + ssl_ciphersuites.c + ssl_cli.c + ssl_srv.c + ssl_tls.c + threading.c + timing.c + version.c + version_features.c + x509.c + x509_crt.c + x509_crl.c + x509_csr.c + x509_create.c + x509write_crt.c + x509write_csr.c + xtea.c +) + +if(WIN32) +set(libs ws2_32) +endif(WIN32) + +if(CMAKE_COMPILER_IS_GNUCC) + set(CMAKE_C_FLAGS_CHECK "${CMAKE_C_FLAGS_CHECK} -Wmissing-declarations -Wmissing-prototypes") + set(CMAKE_C_FLAGS_CHECKFULL "${CMAKE_C_FLAGS_CHECK} -Wcast-qual") +endif(CMAKE_COMPILER_IS_GNUCC) + +if(CMAKE_COMPILER_IS_CLANG) + set(CMAKE_C_FLAGS_CHECK "${CMAKE_C_FLAGS_CHECK} -Wmissing-declarations -Wmissing-prototypes") +endif(CMAKE_COMPILER_IS_CLANG) + +if (NOT USE_STATIC_POLARSSL_LIBRARY AND NOT USE_SHARED_POLARSSL_LIBRARY) + message(FATAL_ERROR "Need to choose static or shared polarssl build!") +endif(NOT USE_STATIC_POLARSSL_LIBRARY AND NOT USE_SHARED_POLARSSL_LIBRARY) + +if(USE_STATIC_POLARSSL_LIBRARY AND USE_SHARED_POLARSSL_LIBRARY) + # if we build both static an shared, then let + # tests and programs link to the shared lib target + set(polarssl_static_target "polarssl_static") +elseif(USE_STATIC_POLARSSL_LIBRARY) + set(polarssl_static_target "polarssl") +endif() + +if(USE_STATIC_POLARSSL_LIBRARY) + add_library(${polarssl_static_target} STATIC ${src}) + set_target_properties(${polarssl_static_target} PROPERTIES OUTPUT_NAME polarssl) + target_link_libraries(${polarssl_static_target} ${libs}) + + if(ZLIB_FOUND) + target_link_libraries(${polarssl_static_target} ${ZLIB_LIBRARIES}) + endif(ZLIB_FOUND) + + if(LINK_WITH_PTHREAD) + target_link_libraries(${polarssl_static_target} pthread) + endif() + + install(TARGETS ${polarssl_static_target} + DESTINATION ${LIB_INSTALL_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +endif() + +if(USE_SHARED_POLARSSL_LIBRARY) + add_library(polarssl SHARED ${src}) + set_target_properties(polarssl PROPERTIES VERSION 1.3.8 SOVERSION 7) + + target_link_libraries(polarssl ${libs}) + + if(ZLIB_FOUND) + target_link_libraries(polarssl ${ZLIB_LIBRARIES}) + endif(ZLIB_FOUND) + + if(LINK_WITH_PTHREAD) + target_link_libraries(polarssl pthread) + endif() + + install(TARGETS polarssl + DESTINATION ${LIB_INSTALL_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +endif(USE_SHARED_POLARSSL_LIBRARY) diff --git a/component/common/network/ssl/polarssl-1.3.8/library/Makefile b/component/common/network/ssl/polarssl-1.3.8/library/Makefile new file mode 100644 index 0000000..d637417 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/Makefile @@ -0,0 +1,110 @@ + +# Also see "include/polarssl/config.h" + +# To compile on MinGW: add "-lws2_32" to LDFLAGS or define WINDOWS in your +# environment +# +CFLAGS += -I../include -D_FILE_OFFSET_BITS=64 -Wall -W -Wdeclaration-after-statement +OFLAGS = -O2 + +ifdef DEBUG +CFLAGS += -g3 +endif + +# MicroBlaze specific options: +# CFLAGS += -mno-xl-soft-mul -mxl-barrel-shift + +# To compile on Plan9: +# CFLAGS += -D_BSD_EXTENSION + +# To compile as a shared library: +ifdef SHARED +CFLAGS += -fPIC +endif + +SONAME=libpolarssl.so.7 + +DLEXT=so.7 +# OSX shared library extension: +# DLEXT=dylib + +# Windows shared library extension: +ifdef WINDOWS +DLEXT=dll +LDFLAGS += -lws2_32 +endif + +OBJS= aes.o aesni.o arc4.o \ + asn1parse.o \ + asn1write.o base64.o bignum.o \ + blowfish.o camellia.o ccm.o \ + certs.o cipher.o cipher_wrap.o \ + ctr_drbg.o debug.o des.o \ + dhm.o ecdh.o ecdsa.o \ + ecp.o ecp_curves.o \ + entropy.o entropy_poll.o \ + error.o gcm.o havege.o \ + hmac_drbg.o \ + md.o md_wrap.o md2.o \ + md4.o md5.o \ + memory_buffer_alloc.o net.o \ + oid.o \ + padlock.o pbkdf2.o pem.o \ + pkcs5.o pkcs11.o pkcs12.o \ + pk.o pk_wrap.o pkparse.o \ + pkwrite.o platform.o ripemd160.o \ + rsa.o sha1.o sha256.o \ + sha512.o ssl_cache.o ssl_cli.o \ + ssl_srv.o ssl_ciphersuites.o \ + ssl_tls.o threading.o timing.o \ + version.o version_features.o \ + x509.o x509_create.o \ + x509_crl.o x509_crt.o x509_csr.o \ + x509write_crt.o x509write_csr.o \ + xtea.o + +.SILENT: + +ifndef SHARED +all: static +else +all: shared +endif + +static: libpolarssl.a + +shared: libpolarssl.$(DLEXT) libpolarssl.so + +libpolarssl.a: $(OBJS) + echo " AR $@" + $(AR) r $@ $(OBJS) + echo " RL $@" + $(AR) s $@ + +libpolarssl.${DLEXT}: libpolarssl.a + echo " LD $@" + $(CC) ${LDFLAGS} -shared -Wl,-soname,$(SONAME) -o $@ $(OBJS) + +libpolarssl.so: libpolarssl.${DLEXT} + echo " LN $@ -> libpolarssl.${DLEXT}" + ln -sf libpolarssl.${DLEXT} $@ + +libpolarssl.dylib: libpolarssl.a + echo " LD $@" + $(CC) ${LDFLAGS} -dynamiclib -o $@ $(OBJS) + +libpolarssl.dll: libpolarssl.a + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS) -lws2_32 -lwinmm -lgdi32 + +.c.o: + echo " CC $<" + $(CC) $(CFLAGS) $(OFLAGS) -c $< + +clean: +ifndef WINDOWS + rm -f *.o libpolarssl.* +endif +ifdef WINDOWS + del /Q /F *.o libpolarssl.* +endif diff --git a/component/common/network/ssl/polarssl-1.3.8/library/aes.c b/component/common/network/ssl/polarssl-1.3.8/library/aes.c new file mode 100644 index 0000000..8d08ddb --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/aes.c @@ -0,0 +1,1554 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#ifdef RTL_HW_CRYPTO +#include +#endif + +#if defined(POLARSSL_AES_C) + +#include "polarssl/aes.h" +#if defined(POLARSSL_PADLOCK_C) +#include "polarssl/padlock.h" +#endif +#if defined(POLARSSL_AESNI_C) +#include "polarssl/aesni.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +#if !defined(POLARSSL_AES_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#ifndef RTL_HW_CRYPTO + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +#if defined(POLARSSL_PADLOCK_C) && \ + ( defined(POLARSSL_HAVE_X86) || defined(PADLOCK_ALIGN16) ) +static int aes_padlock_ace = -1; +#endif + +#if defined(POLARSSL_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[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 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5,63,63,C6), V(84,7C,7C,F8), V(99,77,77,EE), V(8D,7B,7B,F6), \ + V(0D,F2,F2,FF), V(BD,6B,6B,D6), V(B1,6F,6F,DE), V(54,C5,C5,91), \ + V(50,30,30,60), V(03,01,01,02), V(A9,67,67,CE), V(7D,2B,2B,56), \ + V(19,FE,FE,E7), V(62,D7,D7,B5), V(E6,AB,AB,4D), V(9A,76,76,EC), \ + V(45,CA,CA,8F), V(9D,82,82,1F), V(40,C9,C9,89), V(87,7D,7D,FA), \ + V(15,FA,FA,EF), V(EB,59,59,B2), V(C9,47,47,8E), V(0B,F0,F0,FB), \ + V(EC,AD,AD,41), V(67,D4,D4,B3), V(FD,A2,A2,5F), V(EA,AF,AF,45), \ + V(BF,9C,9C,23), V(F7,A4,A4,53), V(96,72,72,E4), V(5B,C0,C0,9B), \ + V(C2,B7,B7,75), V(1C,FD,FD,E1), V(AE,93,93,3D), V(6A,26,26,4C), \ + V(5A,36,36,6C), V(41,3F,3F,7E), V(02,F7,F7,F5), V(4F,CC,CC,83), \ + V(5C,34,34,68), V(F4,A5,A5,51), V(34,E5,E5,D1), V(08,F1,F1,F9), \ + V(93,71,71,E2), V(73,D8,D8,AB), V(53,31,31,62), V(3F,15,15,2A), \ + V(0C,04,04,08), V(52,C7,C7,95), V(65,23,23,46), V(5E,C3,C3,9D), \ + V(28,18,18,30), V(A1,96,96,37), V(0F,05,05,0A), V(B5,9A,9A,2F), \ + V(09,07,07,0E), V(36,12,12,24), V(9B,80,80,1B), V(3D,E2,E2,DF), \ + V(26,EB,EB,CD), V(69,27,27,4E), V(CD,B2,B2,7F), V(9F,75,75,EA), \ + V(1B,09,09,12), V(9E,83,83,1D), V(74,2C,2C,58), V(2E,1A,1A,34), \ + V(2D,1B,1B,36), V(B2,6E,6E,DC), V(EE,5A,5A,B4), V(FB,A0,A0,5B), \ + V(F6,52,52,A4), V(4D,3B,3B,76), V(61,D6,D6,B7), V(CE,B3,B3,7D), \ + V(7B,29,29,52), V(3E,E3,E3,DD), V(71,2F,2F,5E), V(97,84,84,13), \ + V(F5,53,53,A6), V(68,D1,D1,B9), V(00,00,00,00), V(2C,ED,ED,C1), \ + V(60,20,20,40), V(1F,FC,FC,E3), V(C8,B1,B1,79), V(ED,5B,5B,B6), \ + V(BE,6A,6A,D4), V(46,CB,CB,8D), V(D9,BE,BE,67), V(4B,39,39,72), \ + V(DE,4A,4A,94), V(D4,4C,4C,98), V(E8,58,58,B0), V(4A,CF,CF,85), \ + V(6B,D0,D0,BB), V(2A,EF,EF,C5), V(E5,AA,AA,4F), V(16,FB,FB,ED), \ + V(C5,43,43,86), V(D7,4D,4D,9A), V(55,33,33,66), V(94,85,85,11), \ + V(CF,45,45,8A), V(10,F9,F9,E9), V(06,02,02,04), V(81,7F,7F,FE), \ + V(F0,50,50,A0), V(44,3C,3C,78), V(BA,9F,9F,25), V(E3,A8,A8,4B), \ + V(F3,51,51,A2), V(FE,A3,A3,5D), V(C0,40,40,80), V(8A,8F,8F,05), \ + V(AD,92,92,3F), V(BC,9D,9D,21), V(48,38,38,70), V(04,F5,F5,F1), \ + V(DF,BC,BC,63), V(C1,B6,B6,77), V(75,DA,DA,AF), V(63,21,21,42), \ + V(30,10,10,20), V(1A,FF,FF,E5), V(0E,F3,F3,FD), V(6D,D2,D2,BF), \ + V(4C,CD,CD,81), V(14,0C,0C,18), V(35,13,13,26), V(2F,EC,EC,C3), \ + V(E1,5F,5F,BE), V(A2,97,97,35), V(CC,44,44,88), V(39,17,17,2E), \ + V(57,C4,C4,93), V(F2,A7,A7,55), V(82,7E,7E,FC), V(47,3D,3D,7A), \ + V(AC,64,64,C8), V(E7,5D,5D,BA), V(2B,19,19,32), V(95,73,73,E6), \ + V(A0,60,60,C0), V(98,81,81,19), V(D1,4F,4F,9E), V(7F,DC,DC,A3), \ + V(66,22,22,44), V(7E,2A,2A,54), V(AB,90,90,3B), V(83,88,88,0B), \ + V(CA,46,46,8C), V(29,EE,EE,C7), V(D3,B8,B8,6B), V(3C,14,14,28), \ + V(79,DE,DE,A7), V(E2,5E,5E,BC), V(1D,0B,0B,16), V(76,DB,DB,AD), \ + V(3B,E0,E0,DB), V(56,32,32,64), V(4E,3A,3A,74), V(1E,0A,0A,14), \ + V(DB,49,49,92), V(0A,06,06,0C), V(6C,24,24,48), V(E4,5C,5C,B8), \ + V(5D,C2,C2,9F), V(6E,D3,D3,BD), V(EF,AC,AC,43), V(A6,62,62,C4), \ + V(A8,91,91,39), V(A4,95,95,31), V(37,E4,E4,D3), V(8B,79,79,F2), \ + V(32,E7,E7,D5), V(43,C8,C8,8B), V(59,37,37,6E), V(B7,6D,6D,DA), \ + V(8C,8D,8D,01), V(64,D5,D5,B1), V(D2,4E,4E,9C), V(E0,A9,A9,49), \ + V(B4,6C,6C,D8), V(FA,56,56,AC), V(07,F4,F4,F3), V(25,EA,EA,CF), \ + V(AF,65,65,CA), V(8E,7A,7A,F4), V(E9,AE,AE,47), V(18,08,08,10), \ + V(D5,BA,BA,6F), V(88,78,78,F0), V(6F,25,25,4A), V(72,2E,2E,5C), \ + V(24,1C,1C,38), V(F1,A6,A6,57), V(C7,B4,B4,73), V(51,C6,C6,97), \ + V(23,E8,E8,CB), V(7C,DD,DD,A1), V(9C,74,74,E8), V(21,1F,1F,3E), \ + V(DD,4B,4B,96), V(DC,BD,BD,61), V(86,8B,8B,0D), V(85,8A,8A,0F), \ + V(90,70,70,E0), V(42,3E,3E,7C), V(C4,B5,B5,71), V(AA,66,66,CC), \ + V(D8,48,48,90), V(05,03,03,06), V(01,F6,F6,F7), V(12,0E,0E,1C), \ + V(A3,61,61,C2), V(5F,35,35,6A), V(F9,57,57,AE), V(D0,B9,B9,69), \ + V(91,86,86,17), V(58,C1,C1,99), V(27,1D,1D,3A), V(B9,9E,9E,27), \ + V(38,E1,E1,D9), V(13,F8,F8,EB), V(B3,98,98,2B), V(33,11,11,22), \ + V(BB,69,69,D2), V(70,D9,D9,A9), V(89,8E,8E,07), V(A7,94,94,33), \ + V(B6,9B,9B,2D), V(22,1E,1E,3C), V(92,87,87,15), V(20,E9,E9,C9), \ + V(49,CE,CE,87), V(FF,55,55,AA), V(78,28,28,50), V(7A,DF,DF,A5), \ + V(8F,8C,8C,03), V(F8,A1,A1,59), V(80,89,89,09), V(17,0D,0D,1A), \ + V(DA,BF,BF,65), V(31,E6,E6,D7), V(C6,42,42,84), V(B8,68,68,D0), \ + V(C3,41,41,82), V(B0,99,99,29), V(77,2D,2D,5A), V(11,0F,0F,1E), \ + V(CB,B0,B0,7B), V(FC,54,54,A8), V(D6,BB,BB,6D), V(3A,16,16,2C) + +#define V(a,b,c,d) 0x##a##b##c##d +static const uint32_t FT0[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const uint32_t FT1[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const uint32_t FT2[256] = { FT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t FT3[256] = { FT }; +#undef V + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[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 +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50,A7,F4,51), V(53,65,41,7E), V(C3,A4,17,1A), V(96,5E,27,3A), \ + V(CB,6B,AB,3B), V(F1,45,9D,1F), V(AB,58,FA,AC), V(93,03,E3,4B), \ + V(55,FA,30,20), V(F6,6D,76,AD), V(91,76,CC,88), V(25,4C,02,F5), \ + V(FC,D7,E5,4F), V(D7,CB,2A,C5), V(80,44,35,26), V(8F,A3,62,B5), \ + V(49,5A,B1,DE), V(67,1B,BA,25), V(98,0E,EA,45), V(E1,C0,FE,5D), \ + V(02,75,2F,C3), V(12,F0,4C,81), V(A3,97,46,8D), V(C6,F9,D3,6B), \ + V(E7,5F,8F,03), V(95,9C,92,15), V(EB,7A,6D,BF), V(DA,59,52,95), \ + V(2D,83,BE,D4), V(D3,21,74,58), V(29,69,E0,49), V(44,C8,C9,8E), \ + V(6A,89,C2,75), V(78,79,8E,F4), V(6B,3E,58,99), V(DD,71,B9,27), \ + V(B6,4F,E1,BE), V(17,AD,88,F0), V(66,AC,20,C9), V(B4,3A,CE,7D), \ + V(18,4A,DF,63), V(82,31,1A,E5), V(60,33,51,97), V(45,7F,53,62), \ + V(E0,77,64,B1), V(84,AE,6B,BB), V(1C,A0,81,FE), V(94,2B,08,F9), \ + V(58,68,48,70), V(19,FD,45,8F), V(87,6C,DE,94), V(B7,F8,7B,52), \ + V(23,D3,73,AB), V(E2,02,4B,72), V(57,8F,1F,E3), V(2A,AB,55,66), \ + V(07,28,EB,B2), V(03,C2,B5,2F), V(9A,7B,C5,86), V(A5,08,37,D3), \ + V(F2,87,28,30), V(B2,A5,BF,23), V(BA,6A,03,02), V(5C,82,16,ED), \ + V(2B,1C,CF,8A), V(92,B4,79,A7), V(F0,F2,07,F3), V(A1,E2,69,4E), \ + V(CD,F4,DA,65), V(D5,BE,05,06), V(1F,62,34,D1), V(8A,FE,A6,C4), \ + V(9D,53,2E,34), V(A0,55,F3,A2), V(32,E1,8A,05), V(75,EB,F6,A4), \ + V(39,EC,83,0B), V(AA,EF,60,40), V(06,9F,71,5E), V(51,10,6E,BD), \ + V(F9,8A,21,3E), V(3D,06,DD,96), V(AE,05,3E,DD), V(46,BD,E6,4D), \ + V(B5,8D,54,91), V(05,5D,C4,71), V(6F,D4,06,04), V(FF,15,50,60), \ + V(24,FB,98,19), V(97,E9,BD,D6), V(CC,43,40,89), V(77,9E,D9,67), \ + V(BD,42,E8,B0), V(88,8B,89,07), V(38,5B,19,E7), V(DB,EE,C8,79), \ + V(47,0A,7C,A1), V(E9,0F,42,7C), V(C9,1E,84,F8), V(00,00,00,00), \ + V(83,86,80,09), V(48,ED,2B,32), V(AC,70,11,1E), V(4E,72,5A,6C), \ + V(FB,FF,0E,FD), V(56,38,85,0F), V(1E,D5,AE,3D), V(27,39,2D,36), \ + V(64,D9,0F,0A), V(21,A6,5C,68), V(D1,54,5B,9B), V(3A,2E,36,24), \ + V(B1,67,0A,0C), V(0F,E7,57,93), V(D2,96,EE,B4), V(9E,91,9B,1B), \ + V(4F,C5,C0,80), V(A2,20,DC,61), V(69,4B,77,5A), V(16,1A,12,1C), \ + V(0A,BA,93,E2), V(E5,2A,A0,C0), V(43,E0,22,3C), V(1D,17,1B,12), \ + V(0B,0D,09,0E), V(AD,C7,8B,F2), V(B9,A8,B6,2D), V(C8,A9,1E,14), \ + V(85,19,F1,57), V(4C,07,75,AF), V(BB,DD,99,EE), V(FD,60,7F,A3), \ + V(9F,26,01,F7), V(BC,F5,72,5C), V(C5,3B,66,44), V(34,7E,FB,5B), \ + V(76,29,43,8B), V(DC,C6,23,CB), V(68,FC,ED,B6), V(63,F1,E4,B8), \ + V(CA,DC,31,D7), V(10,85,63,42), V(40,22,97,13), V(20,11,C6,84), \ + V(7D,24,4A,85), V(F8,3D,BB,D2), V(11,32,F9,AE), V(6D,A1,29,C7), \ + V(4B,2F,9E,1D), V(F3,30,B2,DC), V(EC,52,86,0D), V(D0,E3,C1,77), \ + V(6C,16,B3,2B), V(99,B9,70,A9), V(FA,48,94,11), V(22,64,E9,47), \ + V(C4,8C,FC,A8), V(1A,3F,F0,A0), V(D8,2C,7D,56), V(EF,90,33,22), \ + V(C7,4E,49,87), V(C1,D1,38,D9), V(FE,A2,CA,8C), V(36,0B,D4,98), \ + V(CF,81,F5,A6), V(28,DE,7A,A5), V(26,8E,B7,DA), V(A4,BF,AD,3F), \ + V(E4,9D,3A,2C), V(0D,92,78,50), V(9B,CC,5F,6A), V(62,46,7E,54), \ + V(C2,13,8D,F6), V(E8,B8,D8,90), V(5E,F7,39,2E), V(F5,AF,C3,82), \ + V(BE,80,5D,9F), V(7C,93,D0,69), V(A9,2D,D5,6F), V(B3,12,25,CF), \ + V(3B,99,AC,C8), V(A7,7D,18,10), V(6E,63,9C,E8), V(7B,BB,3B,DB), \ + V(09,78,26,CD), V(F4,18,59,6E), V(01,B7,9A,EC), V(A8,9A,4F,83), \ + V(65,6E,95,E6), V(7E,E6,FF,AA), V(08,CF,BC,21), V(E6,E8,15,EF), \ + V(D9,9B,E7,BA), V(CE,36,6F,4A), V(D4,09,9F,EA), V(D6,7C,B0,29), \ + V(AF,B2,A4,31), V(31,23,3F,2A), V(30,94,A5,C6), V(C0,66,A2,35), \ + V(37,BC,4E,74), V(A6,CA,82,FC), V(B0,D0,90,E0), V(15,D8,A7,33), \ + V(4A,98,04,F1), V(F7,DA,EC,41), V(0E,50,CD,7F), V(2F,F6,91,17), \ + V(8D,D6,4D,76), V(4D,B0,EF,43), V(54,4D,AA,CC), V(DF,04,96,E4), \ + V(E3,B5,D1,9E), V(1B,88,6A,4C), V(B8,1F,2C,C1), V(7F,51,65,46), \ + V(04,EA,5E,9D), V(5D,35,8C,01), V(73,74,87,FA), V(2E,41,0B,FB), \ + V(5A,1D,67,B3), V(52,D2,DB,92), V(33,56,10,E9), V(13,47,D6,6D), \ + V(8C,61,D7,9A), V(7A,0C,A1,37), V(8E,14,F8,59), V(89,3C,13,EB), \ + V(EE,27,A9,CE), V(35,C9,61,B7), V(ED,E5,1C,E1), V(3C,B1,47,7A), \ + V(59,DF,D2,9C), V(3F,73,F2,55), V(79,CE,14,18), V(BF,37,C7,73), \ + V(EA,CD,F7,53), V(5B,AA,FD,5F), V(14,6F,3D,DF), V(86,DB,44,78), \ + V(81,F3,AF,CA), V(3E,C4,68,B9), V(2C,34,24,38), V(5F,40,A3,C2), \ + V(72,C3,1D,16), V(0C,25,E2,BC), V(8B,49,3C,28), V(41,95,0D,FF), \ + V(71,01,A8,39), V(DE,B3,0C,08), V(9C,E4,B4,D8), V(90,C1,56,64), \ + V(61,84,CB,7B), V(70,B6,32,D5), V(74,5C,6C,48), V(42,57,B8,D0) + +#define V(a,b,c,d) 0x##a##b##c##d +static const uint32_t RT0[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##b##c##d##a +static const uint32_t RT1[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##c##d##a##b +static const uint32_t RT2[256] = { RT }; +#undef V + +#define V(a,b,c,d) 0x##d##a##b##c +static const uint32_t RT3[256] = { RT }; +#undef V + +#undef RT + +/* + * Round constants + */ +static const uint32_t RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else /* POLARSSL_AES_ROM_TABLES */ + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static uint32_t FT0[256]; +static uint32_t FT1[256]; +static uint32_t FT2[256]; +static uint32_t FT3[256]; + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static uint32_t RT0[256]; +static uint32_t RT1[256]; +static uint32_t RT2[256]; +static uint32_t RT3[256]; + +/* + * Round constants + */ +static uint32_t RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) +#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) +#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) + +static int aes_init_done = 0; + +static void aes_gen_tables( void ) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for( i = 0, x = 1; i < 256; i++ ) + { + pow[i] = x; + log[x] = i; + x = ( x ^ XTIME( x ) ) & 0xFF; + } + + /* + * calculate the round constants + */ + for( i = 0, x = 1; i < 10; i++ ) + { + RCON[i] = (uint32_t) x; + x = XTIME( x ) & 0xFF; + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for( i = 1; i < 256; i++ ) + { + x = pow[255 - log[i]]; + + y = x; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF; + x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF; + x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF; + x ^= y; y = ( ( y << 1 ) | ( y >> 7 ) ) & 0xFF; + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for( i = 0; i < 256; i++ ) + { + x = FSb[i]; + y = XTIME( x ) & 0xFF; + z = ( y ^ x ) & 0xFF; + + FT0[i] = ( (uint32_t) y ) ^ + ( (uint32_t) x << 8 ) ^ + ( (uint32_t) x << 16 ) ^ + ( (uint32_t) z << 24 ); + + FT1[i] = ROTL8( FT0[i] ); + FT2[i] = ROTL8( FT1[i] ); + FT3[i] = ROTL8( FT2[i] ); + + x = RSb[i]; + + RT0[i] = ( (uint32_t) MUL( 0x0E, x ) ) ^ + ( (uint32_t) MUL( 0x09, x ) << 8 ) ^ + ( (uint32_t) MUL( 0x0D, x ) << 16 ) ^ + ( (uint32_t) MUL( 0x0B, x ) << 24 ); + + RT1[i] = ROTL8( RT0[i] ); + RT2[i] = ROTL8( RT1[i] ); + RT3[i] = ROTL8( RT2[i] ); + } +} + +#endif /* POLARSSL_AES_ROM_TABLES */ +#endif /* RTL_HW_CRYPTO */ + +void aes_init( aes_context *ctx ) +{ + memset( ctx, 0, sizeof( aes_context ) ); +} + +void aes_free( aes_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( aes_context ) ); +} + +/* + * AES key schedule (encryption) + */ +int aes_setkey_enc( aes_context *ctx, const unsigned char *key, + unsigned int keysize ) +{ +#ifdef RTL_HW_CRYPTO + switch(keysize) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return(POLARSSL_ERR_AES_INVALID_KEY_LENGTH); + } + + memcpy(ctx->enc_key, key, (keysize / 8)); + return 0; +#else /* RTL_HW_CRYPTO */ + unsigned int i; + uint32_t *RK; + +#if !defined(POLARSSL_AES_ROM_TABLES) + if( aes_init_done == 0 ) + { + aes_gen_tables(); + aes_init_done = 1; + + } +#endif + + switch( keysize ) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + +#if defined(POLARSSL_AESNI_C) && defined(POLARSSL_HAVE_X86_64) + if( aesni_supports( POLARSSL_AESNI_AES ) ) + return( aesni_setkey_enc( (unsigned char *) ctx->rk, key, keysize ) ); +#endif + + for( i = 0; i < ( keysize >> 5 ); i++ ) + { + GET_UINT32_LE( RK[i], key, i << 2 ); + } + + switch( ctx->nr ) + { + case 10: + + for( i = 0; i < 10; i++, RK += 4 ) + { + RK[4] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[3] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[3] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[3] ) & 0xFF ] << 24 ); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for( i = 0; i < 8; i++, RK += 6 ) + { + RK[6] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[5] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[5] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[5] ) & 0xFF ] << 24 ); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for( i = 0; i < 7; i++, RK += 8 ) + { + RK[8] = RK[0] ^ RCON[i] ^ + ( (uint32_t) FSb[ ( RK[7] >> 8 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 16 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[7] >> 24 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[7] ) & 0xFF ] << 24 ); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ( (uint32_t) FSb[ ( RK[11] ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( RK[11] >> 24 ) & 0xFF ] << 24 ); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + } + + return( 0 ); +#endif /* RTL_HW_CRYPTO */ +} + +/* + * AES key schedule (decryption) + */ +int aes_setkey_dec( aes_context *ctx, const unsigned char *key, + unsigned int keysize ) +{ +#ifdef RTL_HW_CRYPTO + switch(keysize) + { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default : return(POLARSSL_ERR_AES_INVALID_KEY_LENGTH); + } + + memcpy(ctx->dec_key, key, (keysize / 8)); + return 0; +#else /* RTL_HW_CRYPTO */ + int i, j, ret; + aes_context cty; + uint32_t *RK; + uint32_t *SK; + + aes_init( &cty ); + +#if defined(POLARSSL_PADLOCK_C) && defined(PADLOCK_ALIGN16) + if( aes_padlock_ace == -1 ) + aes_padlock_ace = padlock_supports( PADLOCK_ACE ); + + if( aes_padlock_ace ) + ctx->rk = RK = PADLOCK_ALIGN16( ctx->buf ); + else +#endif + ctx->rk = RK = ctx->buf; + + /* Also checks keysize */ + if( ( ret = aes_setkey_enc( &cty, key, keysize ) ) != 0 ) + goto exit; + + ctx->nr = cty.nr; + +#if defined(POLARSSL_AESNI_C) && defined(POLARSSL_HAVE_X86_64) + if( aesni_supports( POLARSSL_AESNI_AES ) ) + { + aesni_inverse_key( (unsigned char *) ctx->rk, + (const unsigned char *) cty.rk, ctx->nr ); + goto exit; + } +#endif + + SK = cty.rk + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8 ) + { + for( j = 0; j < 4; j++, SK++ ) + { + *RK++ = RT0[ FSb[ ( *SK ) & 0xFF ] ] ^ + RT1[ FSb[ ( *SK >> 8 ) & 0xFF ] ] ^ + RT2[ FSb[ ( *SK >> 16 ) & 0xFF ] ] ^ + RT3[ FSb[ ( *SK >> 24 ) & 0xFF ] ]; + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + +exit: + aes_free( &cty ); + + return( ret ); +#endif /* RTL_HW_CRYPTO */ +} + +#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ + FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ + FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y0 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ + FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ + FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + FT3[ ( Y2 >> 24 ) & 0xFF ]; \ +} + +#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ +{ \ + X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ + RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y1 >> 24 ) & 0xFF ]; \ + \ + X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ + RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y2 >> 24 ) & 0xFF ]; \ + \ + X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ + RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y3 >> 24 ) & 0xFF ]; \ + \ + X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ + RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ + RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ + RT3[ ( Y0 >> 24 ) & 0xFF ]; \ +} + +/* + * AES-ECB block encryption/decryption + */ +int aes_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ +#ifdef RTL_HW_CRYPTO + unsigned char key_buf[32 + 4], *key_buf_aligned; + unsigned char *output_buf[16 + 4], *output_buf_aligned; + + key_buf_aligned = (unsigned char *) (((unsigned int) key_buf + 4) / 4 * 4); + output_buf_aligned = (unsigned char *) (((unsigned int) output_buf + 4) / 4 * 4); + memset(output_buf, 0, 16 + 4); + + if(mode == AES_DECRYPT) + { + memcpy(key_buf_aligned, ctx->dec_key, ((ctx->nr - 6) * 4)); + rtl_crypto_aes_ecb_init(key_buf_aligned, ((ctx->nr - 6) * 4)); + rtl_crypto_aes_ecb_decrypt(input, 16, NULL, 0, output_buf_aligned); + } + else + { + memcpy(key_buf_aligned, ctx->enc_key, ((ctx->nr - 6) * 4)); + rtl_crypto_aes_ecb_init(key_buf_aligned,((ctx->nr - 6) * 4)); + rtl_crypto_aes_ecb_encrypt(input, 16, NULL, 0, output_buf_aligned); + } + + memcpy(output, output_buf_aligned, 16); + + return 0; +#else /* RTL_HW_CRYPTO */ + int i; + uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; + +#if defined(POLARSSL_AESNI_C) && defined(POLARSSL_HAVE_X86_64) + if( aesni_supports( POLARSSL_AESNI_AES ) ) + return( aesni_crypt_ecb( ctx, mode, input, output ) ); +#endif + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptecb( ctx, mode, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + RK = ctx->rk; + + GET_UINT32_LE( X0, input, 0 ); X0 ^= *RK++; + GET_UINT32_LE( X1, input, 4 ); X1 ^= *RK++; + GET_UINT32_LE( X2, input, 8 ); X2 ^= *RK++; + GET_UINT32_LE( X3, input, 12 ); X3 ^= *RK++; + + if( mode == AES_DECRYPT ) + { + for( i = ( ctx->nr >> 1 ) - 1; i > 0; i-- ) + { + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) RSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) RSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) RSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) RSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + } + else /* AES_ENCRYPT */ + { + for( i = ( ctx->nr >> 1 ) - 1; i > 0; i-- ) + { + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); + } + + AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); + + X0 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y0 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y1 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 24 ) & 0xFF ] << 24 ); + + X1 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y1 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y2 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y3 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 24 ) & 0xFF ] << 24 ); + + X2 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y2 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y3 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y0 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 24 ) & 0xFF ] << 24 ); + + X3 = *RK++ ^ \ + ( (uint32_t) FSb[ ( Y3 ) & 0xFF ] ) ^ + ( (uint32_t) FSb[ ( Y0 >> 8 ) & 0xFF ] << 8 ) ^ + ( (uint32_t) FSb[ ( Y1 >> 16 ) & 0xFF ] << 16 ) ^ + ( (uint32_t) FSb[ ( Y2 >> 24 ) & 0xFF ] << 24 ); + } + + PUT_UINT32_LE( X0, output, 0 ); + PUT_UINT32_LE( X1, output, 4 ); + PUT_UINT32_LE( X2, output, 8 ); + PUT_UINT32_LE( X3, output, 12 ); + + return( 0 ); +#endif /* RTL_HW_CRYPTO */ +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * AES-CBC buffer encryption/decryption + */ +int aes_crypt_cbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ +#ifdef RTL_HW_CRYPTO + unsigned char key_buf[32 + 4], *key_buf_aligned; + unsigned char iv_buf[16 + 4], *iv_buf_aligned, iv_tmp[16]; + unsigned char *output_buf, *output_buf_aligned; + + if(length % 16) + return(POLARSSL_ERR_AES_INVALID_INPUT_LENGTH); + + if(length > 0) + { + key_buf_aligned = (unsigned char *) (((unsigned int) key_buf + 4) / 4 * 4); + iv_buf_aligned = (unsigned char *) (((unsigned int) iv_buf + 4) / 4 * 4); + output_buf = polarssl_malloc(length + 4); + output_buf_aligned = (unsigned char *) (((unsigned int) output_buf + 4) / 4 * 4); + + memcpy(iv_buf_aligned, iv, 16); + memset(output_buf, 0, length + 4); + + if(mode == AES_DECRYPT) + { + memcpy(key_buf_aligned, ctx->dec_key, ((ctx->nr - 6) * 4)); + memcpy(iv_tmp, (input + length - 16), 16); + rtl_crypto_aes_cbc_init(key_buf_aligned, ((ctx->nr - 6) * 4)); + rtl_crypto_aes_cbc_decrypt(input, length, iv_buf_aligned, 16, output_buf_aligned); + memcpy(iv, iv_tmp, 16); + } + else + { + memcpy(key_buf_aligned, ctx->enc_key, ((ctx->nr - 6) * 4)); + rtl_crypto_aes_cbc_init(key_buf_aligned,((ctx->nr - 6) * 4)); + rtl_crypto_aes_cbc_encrypt(input, length, iv_buf_aligned, 16, output_buf_aligned); + memcpy(iv, (output_buf_aligned + length - 16), 16); + } + + memcpy(output, output_buf_aligned, length); + polarssl_free(output_buf); + } + + return 0; +#else /* RTL_HW_CRYPTO */ + int i; + unsigned char temp[16]; + + if( length % 16 ) + return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH ); + +#if defined(POLARSSL_PADLOCK_C) && defined(POLARSSL_HAVE_X86) + if( aes_padlock_ace ) + { + if( padlock_xcryptcbc( ctx, mode, length, iv, input, output ) == 0 ) + return( 0 ); + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if( mode == AES_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + aes_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + aes_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +#endif /* RTL_HW_CRYPTO */ +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 buffer encryption/decryption + */ +int aes_crypt_cfb128( aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + if( mode == AES_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = ( n + 1 ) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = ( n + 1 ) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} + +/* + * AES-CFB8 buffer encryption/decryption + */ +#include +int aes_crypt_cfb8( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + unsigned char c; + unsigned char ov[17]; + + while( length-- ) + { + memcpy( ov, iv, 16 ); + aes_crypt_ecb( ctx, AES_ENCRYPT, iv, iv ); + + if( mode == AES_DECRYPT ) + ov[16] = *input; + + c = *output++ = (unsigned char)( iv[0] ^ *input++ ); + + if( mode == AES_ENCRYPT ) + ov[16] = c; + + memcpy( iv, ov + 1, 16 ); + } + + return( 0 ); +} +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR buffer encryption/decryption + */ +int aes_crypt_ctr( aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + while( length-- ) + { + if( n == 0 ) { + aes_crypt_ecb( ctx, AES_ENCRYPT, nonce_counter, stream_block ); + + for( i = 16; i > 0; i-- ) + if( ++nonce_counter[i - 1] != 0 ) + break; + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = ( n + 1 ) & 0x0F; + } + + *nc_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#endif /* !POLARSSL_AES_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * AES-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc3686.html + */ + +static const unsigned char aes_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char aes_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char aes_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char aes_test_ctr_ct[3][48] = +{ + { 0xE4, 0x09, 0x5D, 0x4F, 0xB7, 0xA7, 0xB3, 0x79, + 0x2D, 0x61, 0x75, 0xA3, 0x26, 0x13, 0x11, 0xB8 }, + { 0x51, 0x04, 0xA1, 0x06, 0x16, 0x8A, 0x72, 0xD9, + 0x79, 0x0D, 0x41, 0xEE, 0x8E, 0xDA, 0xD3, 0x88, + 0xEB, 0x2E, 0x1E, 0xFC, 0x46, 0xDA, 0x57, 0xC8, + 0xFC, 0xE6, 0x30, 0xDF, 0x91, 0x41, 0xBE, 0x28 }, + { 0xC1, 0xCF, 0x48, 0xA8, 0x9F, 0x2F, 0xFD, 0xD9, + 0xCF, 0x46, 0x52, 0xE9, 0xEF, 0xDB, 0x72, 0xD7, + 0x45, 0x40, 0xA4, 0x2B, 0xDE, 0x6D, 0x78, 0x36, + 0xD5, 0x9A, 0x5C, 0xEA, 0xAE, 0xF3, 0x10, 0x53, + 0x25, 0xB2, 0x07, 0x2F } +}; + +static const int aes_test_ctr_len[3] = + { 16, 32, 36 }; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +/* + * Checkup routine + */ +int aes_self_test( int verbose ) +{ + int ret = 0, i, j, u, v; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char iv[16]; +#if defined(POLARSSL_CIPHER_MODE_CBC) + unsigned char prv[16]; +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) || defined(POLARSSL_CIPHER_MODE_CFB) + size_t offset; +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + int len; + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + aes_context ctx; + + memset( key, 0, 32 ); + aes_init( &ctx ); + + /* + * ECB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " AES-ECB-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_ecb( &ctx, v, buf, buf ); + + if( memcmp( buf, aes_test_ecb_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " AES-CBC-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memset( iv , 0, 16 ); + memset( prv, 0, 16 ); + memset( buf, 0, 16 ); + + if( v == AES_DECRYPT ) + { + aes_setkey_dec( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + if( memcmp( buf, aes_test_cbc_dec[u], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + else + { + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + for( j = 0; j < 10000; j++ ) + { + unsigned char tmp[16]; + + aes_crypt_cbc( &ctx, v, 16, iv, buf, buf ); + + memcpy( tmp, prv, 16 ); + memcpy( prv, buf, 16 ); + memcpy( buf, tmp, 16 ); + } + + if( memcmp( prv, aes_test_cbc_enc[u], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) + /* + * CFB128 mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " AES-CFB128-%3d (%s): ", 128 + u * 64, + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, aes_test_cfb128_iv, 16 ); + memcpy( key, aes_test_cfb128_key[u], 16 + u * 8 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 + u * 64 ); + + if( v == AES_DECRYPT ) + { + memcpy( buf, aes_test_cfb128_ct[u], 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_pt, 64 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + else + { + memcpy( buf, aes_test_cfb128_pt, 64 ); + aes_crypt_cfb128( &ctx, v, 64, &offset, iv, buf, buf ); + + if( memcmp( buf, aes_test_cfb128_ct[u], 64 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " AES-CTR-128 (%s): ", + ( v == AES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( nonce_counter, aes_test_ctr_nonce_counter[u], 16 ); + memcpy( key, aes_test_ctr_key[u], 16 ); + + offset = 0; + aes_setkey_enc( &ctx, key, 128 ); + + if( v == AES_DECRYPT ) + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_ct[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, + buf, buf ); + + if( memcmp( buf, aes_test_ctr_pt[u], len ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + else + { + len = aes_test_ctr_len[u]; + memcpy( buf, aes_test_ctr_pt[u], len ); + + aes_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, + buf, buf ); + + if( memcmp( buf, aes_test_ctr_ct[u], len ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + + ret = 0; + +exit: + aes_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_AES_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/aesni.c b/component/common/network/ssl/polarssl-1.3.8/library/aesni.c new file mode 100644 index 0000000..97f646e --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/aesni.c @@ -0,0 +1,463 @@ +/* + * AES-NI support functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * [AES-WP] http://software.intel.com/en-us/articles/intel-advanced-encryption-standard-aes-instructions-set + * [CLMUL-WP] http://software.intel.com/en-us/articles/intel-carry-less-multiplication-instruction-and-its-usage-for-computing-the-gcm-mode/ + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_AESNI_C) + +#include "polarssl/aesni.h" +#include + +#if defined(POLARSSL_HAVE_X86_64) + +/* + * AES-NI support detection routine + */ +int aesni_supports( unsigned int what ) +{ + static int done = 0; + static unsigned int c = 0; + + if( ! done ) + { + asm( "movl $1, %%eax \n\t" + "cpuid \n\t" + : "=c" (c) + : + : "eax", "ebx", "edx" ); + done = 1; + } + + return( ( c & what ) != 0 ); +} + +/* + * Binutils needs to be at least 2.19 to support AES-NI instructions. + * Unfortunately, a lot of users have a lower version now (2014-04). + * Emit bytecode directly in order to support "old" version of gas. + * + * Opcodes from the Intel architecture reference manual, vol. 3. + * We always use registers, so we don't need prefixes for memory operands. + * Operand macros are in gas order (src, dst) as opposed to Intel order + * (dst, src) in order to blend better into the surrounding assembly code. + */ +#define AESDEC ".byte 0x66,0x0F,0x38,0xDE," +#define AESDECLAST ".byte 0x66,0x0F,0x38,0xDF," +#define AESENC ".byte 0x66,0x0F,0x38,0xDC," +#define AESENCLAST ".byte 0x66,0x0F,0x38,0xDD," +#define AESIMC ".byte 0x66,0x0F,0x38,0xDB," +#define AESKEYGENA ".byte 0x66,0x0F,0x3A,0xDF," +#define PCLMULQDQ ".byte 0x66,0x0F,0x3A,0x44," + +#define xmm0_xmm0 "0xC0" +#define xmm0_xmm1 "0xC8" +#define xmm0_xmm2 "0xD0" +#define xmm0_xmm3 "0xD8" +#define xmm0_xmm4 "0xE0" +#define xmm1_xmm0 "0xC1" +#define xmm1_xmm2 "0xD1" + +/* + * AES-NI AES-ECB block en(de)cryption + */ +int aesni_crypt_ecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + asm( "movdqu (%3), %%xmm0 \n\t" // load input + "movdqu (%1), %%xmm1 \n\t" // load round key 0 + "pxor %%xmm1, %%xmm0 \n\t" // round 0 + "addq $16, %1 \n\t" // point to next round key + "subl $1, %0 \n\t" // normal rounds = nr - 1 + "test %2, %2 \n\t" // mode? + "jz 2f \n\t" // 0 = decrypt + + "1: \n\t" // encryption loop + "movdqu (%1), %%xmm1 \n\t" // load round key + AESENC xmm1_xmm0 "\n\t" // do round + "addq $16, %1 \n\t" // point to next round key + "subl $1, %0 \n\t" // loop + "jnz 1b \n\t" + "movdqu (%1), %%xmm1 \n\t" // load round key + AESENCLAST xmm1_xmm0 "\n\t" // last round + "jmp 3f \n\t" + + "2: \n\t" // decryption loop + "movdqu (%1), %%xmm1 \n\t" + AESDEC xmm1_xmm0 "\n\t" // do round + "addq $16, %1 \n\t" + "subl $1, %0 \n\t" + "jnz 2b \n\t" + "movdqu (%1), %%xmm1 \n\t" // load round key + AESDECLAST xmm1_xmm0 "\n\t" // last round + + "3: \n\t" + "movdqu %%xmm0, (%4) \n\t" // export output + : + : "r" (ctx->nr), "r" (ctx->rk), "r" (mode), "r" (input), "r" (output) + : "memory", "cc", "xmm0", "xmm1" ); + + + return( 0 ); +} + +/* + * GCM multiplication: c = a times b in GF(2^128) + * Based on [CLMUL-WP] algorithms 1 (with equation 27) and 5. + */ +void aesni_gcm_mult( unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16] ) +{ + unsigned char aa[16], bb[16], cc[16]; + size_t i; + + /* The inputs are in big-endian order, so byte-reverse them */ + for( i = 0; i < 16; i++ ) + { + aa[i] = a[15 - i]; + bb[i] = b[15 - i]; + } + + asm( "movdqu (%0), %%xmm0 \n\t" // a1:a0 + "movdqu (%1), %%xmm1 \n\t" // b1:b0 + + /* + * Caryless multiplication xmm2:xmm1 = xmm0 * xmm1 + * using [CLMUL-WP] algorithm 1 (p. 13). + */ + "movdqa %%xmm1, %%xmm2 \n\t" // copy of b1:b0 + "movdqa %%xmm1, %%xmm3 \n\t" // same + "movdqa %%xmm1, %%xmm4 \n\t" // same + PCLMULQDQ xmm0_xmm1 ",0x00 \n\t" // a0*b0 = c1:c0 + PCLMULQDQ xmm0_xmm2 ",0x11 \n\t" // a1*b1 = d1:d0 + PCLMULQDQ xmm0_xmm3 ",0x10 \n\t" // a0*b1 = e1:e0 + PCLMULQDQ xmm0_xmm4 ",0x01 \n\t" // a1*b0 = f1:f0 + "pxor %%xmm3, %%xmm4 \n\t" // e1+f1:e0+f0 + "movdqa %%xmm4, %%xmm3 \n\t" // same + "psrldq $8, %%xmm4 \n\t" // 0:e1+f1 + "pslldq $8, %%xmm3 \n\t" // e0+f0:0 + "pxor %%xmm4, %%xmm2 \n\t" // d1:d0+e1+f1 + "pxor %%xmm3, %%xmm1 \n\t" // c1+e0+f1:c0 + + /* + * Now shift the result one bit to the left, + * taking advantage of [CLMUL-WP] eq 27 (p. 20) + */ + "movdqa %%xmm1, %%xmm3 \n\t" // r1:r0 + "movdqa %%xmm2, %%xmm4 \n\t" // r3:r2 + "psllq $1, %%xmm1 \n\t" // r1<<1:r0<<1 + "psllq $1, %%xmm2 \n\t" // r3<<1:r2<<1 + "psrlq $63, %%xmm3 \n\t" // r1>>63:r0>>63 + "psrlq $63, %%xmm4 \n\t" // r3>>63:r2>>63 + "movdqa %%xmm3, %%xmm5 \n\t" // r1>>63:r0>>63 + "pslldq $8, %%xmm3 \n\t" // r0>>63:0 + "pslldq $8, %%xmm4 \n\t" // r2>>63:0 + "psrldq $8, %%xmm5 \n\t" // 0:r1>>63 + "por %%xmm3, %%xmm1 \n\t" // r1<<1|r0>>63:r0<<1 + "por %%xmm4, %%xmm2 \n\t" // r3<<1|r2>>62:r2<<1 + "por %%xmm5, %%xmm2 \n\t" // r3<<1|r2>>62:r2<<1|r1>>63 + + /* + * Now reduce modulo the GCM polynomial x^128 + x^7 + x^2 + x + 1 + * using [CLMUL-WP] algorithm 5 (p. 20). + * Currently xmm2:xmm1 holds x3:x2:x1:x0 (already shifted). + */ + /* Step 2 (1) */ + "movdqa %%xmm1, %%xmm3 \n\t" // x1:x0 + "movdqa %%xmm1, %%xmm4 \n\t" // same + "movdqa %%xmm1, %%xmm5 \n\t" // same + "psllq $63, %%xmm3 \n\t" // x1<<63:x0<<63 = stuff:a + "psllq $62, %%xmm4 \n\t" // x1<<62:x0<<62 = stuff:b + "psllq $57, %%xmm5 \n\t" // x1<<57:x0<<57 = stuff:c + + /* Step 2 (2) */ + "pxor %%xmm4, %%xmm3 \n\t" // stuff:a+b + "pxor %%xmm5, %%xmm3 \n\t" // stuff:a+b+c + "pslldq $8, %%xmm3 \n\t" // a+b+c:0 + "pxor %%xmm3, %%xmm1 \n\t" // x1+a+b+c:x0 = d:x0 + + /* Steps 3 and 4 */ + "movdqa %%xmm1,%%xmm0 \n\t" // d:x0 + "movdqa %%xmm1,%%xmm4 \n\t" // same + "movdqa %%xmm1,%%xmm5 \n\t" // same + "psrlq $1, %%xmm0 \n\t" // e1:x0>>1 = e1:e0' + "psrlq $2, %%xmm4 \n\t" // f1:x0>>2 = f1:f0' + "psrlq $7, %%xmm5 \n\t" // g1:x0>>7 = g1:g0' + "pxor %%xmm4, %%xmm0 \n\t" // e1+f1:e0'+f0' + "pxor %%xmm5, %%xmm0 \n\t" // e1+f1+g1:e0'+f0'+g0' + // e0'+f0'+g0' is almost e0+f0+g0, ex\tcept for some missing + // bits carried from d. Now get those\t bits back in. + "movdqa %%xmm1,%%xmm3 \n\t" // d:x0 + "movdqa %%xmm1,%%xmm4 \n\t" // same + "movdqa %%xmm1,%%xmm5 \n\t" // same + "psllq $63, %%xmm3 \n\t" // d<<63:stuff + "psllq $62, %%xmm4 \n\t" // d<<62:stuff + "psllq $57, %%xmm5 \n\t" // d<<57:stuff + "pxor %%xmm4, %%xmm3 \n\t" // d<<63+d<<62:stuff + "pxor %%xmm5, %%xmm3 \n\t" // missing bits of d:stuff + "psrldq $8, %%xmm3 \n\t" // 0:missing bits of d + "pxor %%xmm3, %%xmm0 \n\t" // e1+f1+g1:e0+f0+g0 + "pxor %%xmm1, %%xmm0 \n\t" // h1:h0 + "pxor %%xmm2, %%xmm0 \n\t" // x3+h1:x2+h0 + + "movdqu %%xmm0, (%2) \n\t" // done + : + : "r" (aa), "r" (bb), "r" (cc) + : "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5" ); + + /* Now byte-reverse the outputs */ + for( i = 0; i < 16; i++ ) + c[i] = cc[15 - i]; + + return; +} + +/* + * Compute decryption round keys from encryption round keys + */ +void aesni_inverse_key( unsigned char *invkey, + const unsigned char *fwdkey, int nr ) +{ + unsigned char *ik = invkey; + const unsigned char *fk = fwdkey + 16 * nr; + + memcpy( ik, fk, 16 ); + + for( fk -= 16, ik += 16; fk > fwdkey; fk -= 16, ik += 16 ) + asm( "movdqu (%0), %%xmm0 \n\t" + AESIMC xmm0_xmm0 "\n\t" + "movdqu %%xmm0, (%1) \n\t" + : + : "r" (fk), "r" (ik) + : "memory", "xmm0" ); + + memcpy( ik, fk, 16 ); +} + +/* + * Key expansion, 128-bit case + */ +static void aesni_setkey_enc_128( unsigned char *rk, + const unsigned char *key ) +{ + asm( "movdqu (%1), %%xmm0 \n\t" // copy the original key + "movdqu %%xmm0, (%0) \n\t" // as round key 0 + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next round key. + * + * On entry xmm0 is r3:r2:r1:r0 and xmm1 is X:stuff:stuff:stuff + * with X = rot( sub( r3 ) ) ^ RCON. + * + * On exit, xmm0 is r7:r6:r5:r4 + * with r4 = X + r0, r5 = r4 + r1, r6 = r5 + r2, r7 = r6 + r3 + * and those are written to the round key buffer. + */ + "1: \n\t" + "pshufd $0xff, %%xmm1, %%xmm1 \n\t" // X:X:X:X + "pxor %%xmm0, %%xmm1 \n\t" // X+r3:X+r2:X+r1:r4 + "pslldq $4, %%xmm0 \n\t" // r2:r1:r0:0 + "pxor %%xmm0, %%xmm1 \n\t" // X+r3+r2:X+r2+r1:r5:r4 + "pslldq $4, %%xmm0 \n\t" // etc + "pxor %%xmm0, %%xmm1 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm1, %%xmm0 \n\t" // update xmm0 for next time! + "add $16, %0 \n\t" // point to next round key + "movdqu %%xmm0, (%0) \n\t" // write it + "ret \n\t" + + /* Main "loop" */ + "2: \n\t" + AESKEYGENA xmm0_xmm1 ",0x01 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x02 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x04 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x08 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x10 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x20 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x40 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x80 \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x1B \n\tcall 1b \n\t" + AESKEYGENA xmm0_xmm1 ",0x36 \n\tcall 1b \n\t" + : + : "r" (rk), "r" (key) + : "memory", "cc", "0" ); +} + +/* + * Key expansion, 192-bit case + */ +static void aesni_setkey_enc_192( unsigned char *rk, + const unsigned char *key ) +{ + asm( "movdqu (%1), %%xmm0 \n\t" // copy original round key + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "movq 16(%1), %%xmm1 \n\t" + "movq %%xmm1, (%0) \n\t" + "add $8, %0 \n\t" + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next 6 quarter-keys. + * + * On entry xmm0 is r3:r2:r1:r0, xmm1 is stuff:stuff:r5:r4 + * and xmm2 is stuff:stuff:X:stuff with X = rot( sub( r3 ) ) ^ RCON. + * + * On exit, xmm0 is r9:r8:r7:r6 and xmm1 is stuff:stuff:r11:r10 + * and those are written to the round key buffer. + */ + "1: \n\t" + "pshufd $0x55, %%xmm2, %%xmm2 \n\t" // X:X:X:X + "pxor %%xmm0, %%xmm2 \n\t" // X+r3:X+r2:X+r1:r4 + "pslldq $4, %%xmm0 \n\t" // etc + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm2, %%xmm0 \n\t" // update xmm0 = r9:r8:r7:r6 + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "pshufd $0xff, %%xmm0, %%xmm2 \n\t" // r9:r9:r9:r9 + "pxor %%xmm1, %%xmm2 \n\t" // stuff:stuff:r9+r5:r10 + "pslldq $4, %%xmm1 \n\t" // r2:r1:r0:0 + "pxor %%xmm2, %%xmm1 \n\t" // xmm1 = stuff:stuff:r11:r10 + "movq %%xmm1, (%0) \n\t" + "add $8, %0 \n\t" + "ret \n\t" + + "2: \n\t" + AESKEYGENA xmm1_xmm2 ",0x01 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x02 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x04 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x08 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x10 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x20 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x40 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x80 \n\tcall 1b \n\t" + + : + : "r" (rk), "r" (key) + : "memory", "cc", "0" ); +} + +/* + * Key expansion, 256-bit case + */ +static void aesni_setkey_enc_256( unsigned char *rk, + const unsigned char *key ) +{ + asm( "movdqu (%1), %%xmm0 \n\t" + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "movdqu 16(%1), %%xmm1 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next two round keys. + * + * On entry xmm0 is r3:r2:r1:r0, xmm1 is r7:r6:r5:r4 and + * xmm2 is X:stuff:stuff:stuff with X = rot( sub( r7 )) ^ RCON + * + * On exit, xmm0 is r11:r10:r9:r8 and xmm1 is r15:r14:r13:r12 + * and those have been written to the output buffer. + */ + "1: \n\t" + "pshufd $0xff, %%xmm2, %%xmm2 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm2, %%xmm0 \n\t" + "add $16, %0 \n\t" + "movdqu %%xmm0, (%0) \n\t" + + /* Set xmm2 to stuff:Y:stuff:stuff with Y = subword( r11 ) + * and proceed to generate next round key from there */ + AESKEYGENA xmm0_xmm2 ",0x00 \n\t" + "pshufd $0xaa, %%xmm2, %%xmm2 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm2, %%xmm1 \n\t" + "add $16, %0 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "ret \n\t" + + /* + * Main "loop" - Generating one more key than necessary, + * see definition of aes_context.buf + */ + "2: \n\t" + AESKEYGENA xmm1_xmm2 ",0x01 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x02 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x04 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x08 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x10 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x20 \n\tcall 1b \n\t" + AESKEYGENA xmm1_xmm2 ",0x40 \n\tcall 1b \n\t" + : + : "r" (rk), "r" (key) + : "memory", "cc", "0" ); +} + +/* + * Key expansion, wrapper + */ +int aesni_setkey_enc( unsigned char *rk, + const unsigned char *key, + size_t bits ) +{ + switch( bits ) + { + case 128: aesni_setkey_enc_128( rk, key ); break; + case 192: aesni_setkey_enc_192( rk, key ); break; + case 256: aesni_setkey_enc_256( rk, key ); break; + default : return( POLARSSL_ERR_AES_INVALID_KEY_LENGTH ); + } + + return( 0 ); +} + +#endif /* POLARSSL_HAVE_X86_64 */ + +#endif /* POLARSSL_AESNI_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/arc4.c b/component/common/network/ssl/polarssl-1.3.8/library/arc4.c new file mode 100644 index 0000000..54e89ea --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/arc4.c @@ -0,0 +1,208 @@ +/* + * An implementation of the ARCFOUR algorithm + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ARCFOUR algorithm was publicly disclosed on 94/09. + * + * http://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0 + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ARC4_C) + +#include "polarssl/arc4.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +#if !defined(POLARSSL_ARC4_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +void arc4_init( arc4_context *ctx ) +{ + memset( ctx, 0, sizeof( arc4_context ) ); +} + +void arc4_free( arc4_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( arc4_context ) ); +} + +/* + * ARC4 key schedule + */ +void arc4_setup( arc4_context *ctx, const unsigned char *key, + unsigned int keylen ) +{ + int i, j, a; + unsigned int k; + unsigned char *m; + + ctx->x = 0; + ctx->y = 0; + m = ctx->m; + + for( i = 0; i < 256; i++ ) + m[i] = (unsigned char) i; + + j = k = 0; + + for( i = 0; i < 256; i++, k++ ) + { + if( k >= keylen ) k = 0; + + a = m[i]; + j = ( j + a + key[k] ) & 0xFF; + m[i] = m[j]; + m[j] = (unsigned char) a; + } +} + +/* + * ARC4 cipher function + */ +int arc4_crypt( arc4_context *ctx, size_t length, const unsigned char *input, + unsigned char *output ) +{ + int x, y, a, b; + size_t i; + unsigned char *m; + + x = ctx->x; + y = ctx->y; + m = ctx->m; + + for( i = 0; i < length; i++ ) + { + x = ( x + 1 ) & 0xFF; a = m[x]; + y = ( y + a ) & 0xFF; b = m[y]; + + m[x] = (unsigned char) b; + m[y] = (unsigned char) a; + + output[i] = (unsigned char) + ( input[i] ^ m[(unsigned char)( a + b )] ); + } + + ctx->x = x; + ctx->y = y; + + return( 0 ); +} + +#endif /* !POLARSSL_ARC4_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include +#include + +/* + * ARC4 tests vectors as posted by Eric Rescorla in sep. 1994: + * + * http://groups.google.com/group/comp.security.misc/msg/10a300c9d21afca0 + */ +static const unsigned char arc4_test_key[3][8] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +static const unsigned char arc4_test_pt[3][8] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +static const unsigned char arc4_test_ct[3][8] = +{ + { 0x75, 0xB7, 0x87, 0x80, 0x99, 0xE0, 0xC5, 0x96 }, + { 0x74, 0x94, 0xC2, 0xE7, 0x10, 0x4B, 0x08, 0x79 }, + { 0xDE, 0x18, 0x89, 0x41, 0xA3, 0x37, 0x5D, 0x3A } +}; + +/* + * Checkup routine + */ +int arc4_self_test( int verbose ) +{ + int i, ret = 0; + unsigned char ibuf[8]; + unsigned char obuf[8]; + arc4_context ctx; + + arc4_init( &ctx ); + + for( i = 0; i < 3; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " ARC4 test #%d: ", i + 1 ); + + memcpy( ibuf, arc4_test_pt[i], 8 ); + + arc4_setup( &ctx, arc4_test_key[i], 8 ); + arc4_crypt( &ctx, 8, ibuf, obuf ); + + if( memcmp( obuf, arc4_test_ct[i], 8 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + arc4_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_ARC4_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/asn1parse.c b/component/common/network/ssl/polarssl-1.3.8/library/asn1parse.c new file mode 100644 index 0000000..9744352 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/asn1parse.c @@ -0,0 +1,391 @@ +/* + * Generic ASN.1 parsing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ASN1_PARSE_C) + +#include "polarssl/asn1.h" + +#if defined(POLARSSL_BIGNUM_C) +#include "polarssl/bignum.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include + +/* + * ASN.1 DER decoding routines + */ +int asn1_get_len( unsigned char **p, + const unsigned char *end, + size_t *len ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( ( **p & 0x80 ) == 0 ) + *len = *(*p)++; + else + { + switch( **p & 0x7F ) + { + case 1: + if( ( end - *p ) < 2 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if( ( end - *p ) < 3 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (*p)[1] << 8 ) | (*p)[2]; + (*p) += 3; + break; + + case 3: + if( ( end - *p ) < 4 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (*p)[1] << 16 ) | ( (*p)[2] << 8 ) | (*p)[3]; + (*p) += 4; + break; + + case 4: + if( ( end - *p ) < 5 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + *len = ( (*p)[1] << 24 ) | ( (*p)[2] << 16 ) | ( (*p)[3] << 8 ) | + (*p)[4]; + (*p) += 5; + break; + + default: + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + } + } + + if( *len > (size_t) ( end - *p ) ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + return( 0 ); +} + +int asn1_get_tag( unsigned char **p, + const unsigned char *end, + size_t *len, int tag ) +{ + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != tag ) + return( POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + (*p)++; + + return( asn1_get_len( p, end, len ) ); +} + +int asn1_get_bool( unsigned char **p, + const unsigned char *end, + int *val ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_BOOLEAN ) ) != 0 ) + return( ret ); + + if( len != 1 ) + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + *val = ( **p != 0 ) ? 1 : 0; + (*p)++; + + return( 0 ); +} + +int asn1_get_int( unsigned char **p, + const unsigned char *end, + int *val ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + if( len > sizeof( int ) || ( **p & 0x80 ) != 0 ) + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + *val = 0; + + while( len-- > 0 ) + { + *val = ( *val << 8 ) | **p; + (*p)++; + } + + return( 0 ); +} + +#if defined(POLARSSL_BIGNUM_C) +int asn1_get_mpi( unsigned char **p, + const unsigned char *end, + mpi *X ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_INTEGER ) ) != 0 ) + return( ret ); + + ret = mpi_read_binary( X, *p, len ); + + *p += len; + + return( ret ); +} +#endif /* POLARSSL_BIGNUM_C */ + +int asn1_get_bitstring( unsigned char **p, const unsigned char *end, + asn1_bitstring *bs) +{ + int ret; + + /* Certificate type is a single byte bitstring */ + if( ( ret = asn1_get_tag( p, end, &bs->len, ASN1_BIT_STRING ) ) != 0 ) + return( ret ); + + /* Check length, subtract one for actual bit string length */ + if( bs->len < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + bs->len -= 1; + + /* Get number of unused bits, ensure unused bits <= 7 */ + bs->unused_bits = **p; + if( bs->unused_bits > 7 ) + return( POLARSSL_ERR_ASN1_INVALID_LENGTH ); + (*p)++; + + /* Get actual bitstring */ + bs->p = *p; + *p += bs->len; + + if( *p != end ) + return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * Get a bit string without unused bits + */ +int asn1_get_bitstring_null( unsigned char **p, const unsigned char *end, + size_t *len ) +{ + int ret; + + if( ( ret = asn1_get_tag( p, end, len, ASN1_BIT_STRING ) ) != 0 ) + return( ret ); + + if( (*len)-- < 2 || *(*p)++ != 0 ) + return( POLARSSL_ERR_ASN1_INVALID_DATA ); + + return( 0 ); +} + + + +/* + * Parses and splits an ASN.1 "SEQUENCE OF " + */ +int asn1_get_sequence_of( unsigned char **p, + const unsigned char *end, + asn1_sequence *cur, + int tag) +{ + int ret; + size_t len; + asn1_buf *buf; + + /* Get main sequence tag */ + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( ret ); + + if( *p + len != end ) + return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + buf = &(cur->buf); + buf->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &buf->len, tag ) ) != 0 ) + return( ret ); + + buf->p = *p; + *p += buf->len; + + /* Allocate and assign next pointer */ + if( *p < end ) + { + cur->next = (asn1_sequence *) polarssl_malloc( + sizeof( asn1_sequence ) ); + + if( cur->next == NULL ) + return( POLARSSL_ERR_ASN1_MALLOC_FAILED ); + + cur = cur->next; + } + } + + /* Set final sequence entry's next pointer to NULL */ + cur->next = NULL; + + if( *p != end ) + return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +int asn1_get_alg( unsigned char **p, + const unsigned char *end, + asn1_buf *alg, asn1_buf *params ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( ret ); + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + alg->tag = **p; + end = *p + len; + + if( ( ret = asn1_get_tag( p, end, &alg->len, ASN1_OID ) ) != 0 ) + return( ret ); + + alg->p = *p; + *p += alg->len; + + if( *p == end ) + { + memset( params, 0, sizeof(asn1_buf) ); + return( 0 ); + } + + params->tag = **p; + (*p)++; + + if( ( ret = asn1_get_len( p, end, ¶ms->len ) ) != 0 ) + return( ret ); + + params->p = *p; + *p += params->len; + + if( *p != end ) + return( POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +int asn1_get_alg_null( unsigned char **p, + const unsigned char *end, + asn1_buf *alg ) +{ + int ret; + asn1_buf params; + + memset( ¶ms, 0, sizeof(asn1_buf) ); + + if( ( ret = asn1_get_alg( p, end, alg, ¶ms ) ) != 0 ) + return( ret ); + + if( ( params.tag != ASN1_NULL && params.tag != 0 ) || params.len != 0 ) + return( POLARSSL_ERR_ASN1_INVALID_DATA ); + + return( 0 ); +} + +void asn1_free_named_data( asn1_named_data *cur ) +{ + if( cur == NULL ) + return; + + polarssl_free( cur->oid.p ); + polarssl_free( cur->val.p ); + + memset( cur, 0, sizeof( asn1_named_data ) ); +} + +void asn1_free_named_data_list( asn1_named_data **head ) +{ + asn1_named_data *cur; + + while( ( cur = *head ) != NULL ) + { + *head = cur->next; + asn1_free_named_data( cur ); + polarssl_free( cur ); + } +} + +asn1_named_data *asn1_find_named_data( asn1_named_data *list, + const char *oid, size_t len ) +{ + while( list != NULL ) + { + if( list->oid.len == len && + memcmp( list->oid.p, oid, len ) == 0 ) + { + break; + } + + list = list->next; + } + + return( list ); +} + +#endif /* POLARSSL_ASN1_PARSE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/asn1write.c b/component/common/network/ssl/polarssl-1.3.8/library/asn1write.c new file mode 100644 index 0000000..ebc0e97 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/asn1write.c @@ -0,0 +1,366 @@ +/* + * ASN.1 buffer writing functionality + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ASN1_WRITE_C) + +#include "polarssl/asn1write.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +int asn1_write_len( unsigned char **p, unsigned char *start, size_t len ) +{ + if( len < 0x80 ) + { + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = (unsigned char) len; + return( 1 ); + } + + if( len <= 0xFF ) + { + if( *p - start < 2 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = (unsigned char) len; + *--(*p) = 0x81; + return( 2 ); + } + + if( *p - start < 3 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + // We assume we never have lengths larger than 65535 bytes + // + *--(*p) = len % 256; + *--(*p) = ( len / 256 ) % 256; + *--(*p) = 0x82; + + return( 3 ); +} + +int asn1_write_tag( unsigned char **p, unsigned char *start, unsigned char tag ) +{ + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = tag; + + return( 1 ); +} + +int asn1_write_raw_buffer( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t size ) +{ + size_t len = 0; + + if( *p - start < (int) size ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + len = size; + (*p) -= len; + memcpy( *p, buf, len ); + + return( (int) len ); +} + +#if defined(POLARSSL_BIGNUM_C) +int asn1_write_mpi( unsigned char **p, unsigned char *start, mpi *X ) +{ + int ret; + size_t len = 0; + + // Write the MPI + // + len = mpi_size( X ); + + if( *p - start < (int) len ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + (*p) -= len; + MPI_CHK( mpi_write_binary( X, *p, len ) ); + + // DER format assumes 2s complement for numbers, so the leftmost bit + // should be 0 for positive numbers and 1 for negative numbers. + // + if( X->s ==1 && **p & 0x80 ) + { + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = 0x00; + len += 1; + } + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_INTEGER ) ); + + ret = (int) len; + +cleanup: + return( ret ); +} +#endif /* POLARSSL_BIGNUM_C */ + +int asn1_write_null( unsigned char **p, unsigned char *start ) +{ + int ret; + size_t len = 0; + + // Write NULL + // + ASN1_CHK_ADD( len, asn1_write_len( p, start, 0) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_NULL ) ); + + return( (int) len ); +} + +int asn1_write_oid( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, + (const unsigned char *) oid, oid_len ) ); + ASN1_CHK_ADD( len , asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len , asn1_write_tag( p, start, ASN1_OID ) ); + + return( (int) len ); +} + +int asn1_write_algorithm_identifier( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + size_t par_len ) +{ + int ret; + size_t len = 0; + + if( par_len == 0 ) + ASN1_CHK_ADD( len, asn1_write_null( p, start ) ); + else + len += par_len; + + ASN1_CHK_ADD( len, asn1_write_oid( p, start, oid, oid_len ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +int asn1_write_bool( unsigned char **p, unsigned char *start, int boolean ) +{ + int ret; + size_t len = 0; + + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = (boolean) ? 1 : 0; + len++; + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_BOOLEAN ) ); + + return( (int) len ); +} + +int asn1_write_int( unsigned char **p, unsigned char *start, int val ) +{ + int ret; + size_t len = 0; + + // TODO negative values and values larger than 128 + // DER format assumes 2s complement for numbers, so the leftmost bit + // should be 0 for positive numbers and 1 for negative numbers. + // + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + len += 1; + *--(*p) = val; + + if( val > 0 && **p & 0x80 ) + { + if( *p - start < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *--(*p) = 0x00; + len += 1; + } + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_INTEGER ) ); + + return( (int) len ); +} + +int asn1_write_printable_string( unsigned char **p, unsigned char *start, + const char *text, size_t text_len ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, + (const unsigned char *) text, text_len ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_PRINTABLE_STRING ) ); + + return( (int) len ); +} + +int asn1_write_ia5_string( unsigned char **p, unsigned char *start, + const char *text, size_t text_len ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, + (const unsigned char *) text, text_len ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_IA5_STRING ) ); + + return( (int) len ); +} + +int asn1_write_bitstring( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t bits ) +{ + int ret; + size_t len = 0, size; + + size = ( bits / 8 ) + ( ( bits % 8 ) ? 1 : 0 ); + + // Calculate byte length + // + if( *p - start < (int) size + 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + len = size + 1; + (*p) -= size; + memcpy( *p, buf, size ); + + // Write unused bits + // + *--(*p) = (unsigned char) (size * 8 - bits); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_BIT_STRING ) ); + + return( (int) len ); +} + +int asn1_write_octet_string( unsigned char **p, unsigned char *start, + const unsigned char *buf, size_t size ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, buf, size ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_OCTET_STRING ) ); + + return( (int) len ); +} + +asn1_named_data *asn1_store_named_data( asn1_named_data **head, + const char *oid, size_t oid_len, + const unsigned char *val, + size_t val_len ) +{ + asn1_named_data *cur; + + if( ( cur = asn1_find_named_data( *head, oid, oid_len ) ) == NULL ) + { + // Add new entry if not present yet based on OID + // + if( ( cur = polarssl_malloc( sizeof(asn1_named_data) ) ) == NULL ) + return( NULL ); + + memset( cur, 0, sizeof(asn1_named_data) ); + + cur->oid.len = oid_len; + cur->oid.p = polarssl_malloc( oid_len ); + if( cur->oid.p == NULL ) + { + polarssl_free( cur ); + return( NULL ); + } + + cur->val.len = val_len; + cur->val.p = polarssl_malloc( val_len ); + if( cur->val.p == NULL ) + { + polarssl_free( cur->oid.p ); + polarssl_free( cur ); + return( NULL ); + } + + memcpy( cur->oid.p, oid, oid_len ); + + cur->next = *head; + *head = cur; + } + else if( cur->val.len < val_len ) + { + // Enlarge existing value buffer if needed + // + polarssl_free( cur->val.p ); + cur->val.p = NULL; + + cur->val.len = val_len; + cur->val.p = polarssl_malloc( val_len ); + if( cur->val.p == NULL ) + { + polarssl_free( cur->oid.p ); + polarssl_free( cur ); + return( NULL ); + } + } + + if( val != NULL ) + memcpy( cur->val.p, val, val_len ); + + return( cur ); +} +#endif /* POLARSSL_ASN1_WRITE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/base64.c b/component/common/network/ssl/polarssl-1.3.8/library/base64.c new file mode 100644 index 0000000..39a8323 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/base64.c @@ -0,0 +1,273 @@ +/* + * RFC 1521 base64 encoding/decoding + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_BASE64_C) + +#include "polarssl/base64.h" + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +static const unsigned char base64_enc_map[64] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/' +}; + +static const unsigned char base64_dec_map[128] = +{ + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, + 127, 64, 127, 127, 127, 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, 127, 127, 127, 127, 127, 127, 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, 127, 127, 127, 127, 127 +}; + +/* + * Encode a buffer into base64 format + */ +int base64_encode( unsigned char *dst, size_t *dlen, + const unsigned char *src, size_t slen ) +{ + size_t i, n; + int C1, C2, C3; + unsigned char *p; + + if( slen == 0 ) + return( 0 ); + + n = ( slen << 3 ) / 6; + + switch( ( slen << 3 ) - ( n * 6 ) ) + { + case 2: n += 3; break; + case 4: n += 2; break; + default: break; + } + + if( *dlen < n + 1 ) + { + *dlen = n + 1; + return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); + } + + n = ( slen / 3 ) * 3; + + for( i = 0, p = dst; i < n; i += 3 ) + { + C1 = *src++; + C2 = *src++; + C3 = *src++; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; + *p++ = base64_enc_map[C3 & 0x3F]; + } + + if( i < slen ) + { + C1 = *src++; + C2 = ( ( i + 1 ) < slen ) ? *src++ : 0; + + *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; + *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; + + if( ( i + 1 ) < slen ) + *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; + else *p++ = '='; + + *p++ = '='; + } + + *dlen = p - dst; + *p = 0; + + return( 0 ); +} + +/* + * Decode a base64-formatted buffer + */ +int base64_decode( unsigned char *dst, size_t *dlen, + const unsigned char *src, size_t slen ) +{ + size_t i, n; + uint32_t j, x; + unsigned char *p; + + for( i = n = j = 0; i < slen; i++ ) + { + if( ( slen - i ) >= 2 && + src[i] == '\r' && src[i + 1] == '\n' ) + continue; + + if( src[i] == '\n' ) + continue; + + if( src[i] == '=' && ++j > 2 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + if( base64_dec_map[src[i]] < 64 && j != 0 ) + return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); + + n++; + } + + if( n == 0 ) + return( 0 ); + + n = ( ( n * 6 ) + 7 ) >> 3; + n -= j; + + if( dst == NULL || *dlen < n ) + { + *dlen = n; + return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); + } + + for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) + { + if( *src == '\r' || *src == '\n' ) + continue; + + j -= ( base64_dec_map[*src] == 64 ); + x = ( x << 6 ) | ( base64_dec_map[*src] & 0x3F ); + + if( ++n == 4 ) + { + n = 0; + if( j > 0 ) *p++ = (unsigned char)( x >> 16 ); + if( j > 1 ) *p++ = (unsigned char)( x >> 8 ); + if( j > 2 ) *p++ = (unsigned char)( x ); + } + } + + *dlen = p - dst; + + return( 0 ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include +#include + +static const unsigned char base64_test_dec[64] = +{ + 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, + 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, + 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, + 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, + 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, + 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, + 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, + 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 +}; + +static const unsigned char base64_test_enc[] = + "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" + "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; + +/* + * Checkup routine + */ +int base64_self_test( int verbose ) +{ + size_t len; + const unsigned char *src; + unsigned char buffer[128]; + + if( verbose != 0 ) + polarssl_printf( " Base64 encoding test: " ); + + len = sizeof( buffer ); + src = base64_test_dec; + + if( base64_encode( buffer, &len, src, 64 ) != 0 || + memcmp( base64_test_enc, buffer, 88 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n Base64 decoding test: " ); + + len = sizeof( buffer ); + src = base64_test_enc; + + if( base64_decode( buffer, &len, src, 88 ) != 0 || + memcmp( base64_test_dec, buffer, 64 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_BASE64_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/bignum.c b/component/common/network/ssl/polarssl-1.3.8/library/bignum.c new file mode 100644 index 0000000..80cf0f8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/bignum.c @@ -0,0 +1,2344 @@ +/* + * Multi-precision integer library + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This MPI implementation is based on: + * + * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf + * http://www.stillhq.com/extracted/gnupg-api/mpi/ + * http://math.libtomcrypt.com/files/tommath.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_BIGNUM_C) + +#include "polarssl/bignum.h" +#include "polarssl/bn_mul.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#define ciL (sizeof(t_uint)) /* chars in limb */ +#define biL (ciL << 3) /* bits in limb */ +#define biH (ciL << 2) /* half limb size */ + +/* + * Convert between bits/chars and number of limbs + */ +#define BITS_TO_LIMBS(i) (((i) + biL - 1) / biL) +#define CHARS_TO_LIMBS(i) (((i) + ciL - 1) / ciL) + +/* + * Initialize one MPI + */ +void mpi_init( mpi *X ) +{ + if( X == NULL ) + return; + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mpi_free( mpi *X ) +{ + if( X == NULL ) + return; + + if( X->p != NULL ) + { + polarssl_zeroize( X->p, X->n * ciL ); + polarssl_free( X->p ); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mpi_grow( mpi *X, size_t nblimbs ) +{ + t_uint *p; + + if( nblimbs > POLARSSL_MPI_MAX_LIMBS ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + if( X->n < nblimbs ) + { + if( ( p = (t_uint *) polarssl_malloc( nblimbs * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, nblimbs * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, X->n * ciL ); + polarssl_zeroize( X->p, X->n * ciL ); + polarssl_free( X->p ); + } + + X->n = nblimbs; + X->p = p; + } + + return( 0 ); +} + +/* + * Resize down as much as possible, + * while keeping at least the specified number of limbs + */ +int mpi_shrink( mpi *X, size_t nblimbs ) +{ + t_uint *p; + size_t i; + + /* Actually resize up in this case */ + if( X->n <= nblimbs ) + return( mpi_grow( X, nblimbs ) ); + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + i++; + + if( i < nblimbs ) + i = nblimbs; + + if( ( p = (t_uint *) polarssl_malloc( i * ciL ) ) == NULL ) + return( POLARSSL_ERR_MPI_MALLOC_FAILED ); + + memset( p, 0, i * ciL ); + + if( X->p != NULL ) + { + memcpy( p, X->p, i * ciL ); + polarssl_zeroize( X->p, X->n * ciL ); + polarssl_free( X->p ); + } + + X->n = i; + X->p = p; + + return( 0 ); +} + +/* + * Copy the contents of Y into X + */ +int mpi_copy( mpi *X, const mpi *Y ) +{ + int ret; + size_t i; + + if( X == Y ) + return( 0 ); + + if( Y->p == NULL ) + { + mpi_free( X ); + return( 0 ); + } + + for( i = Y->n - 1; i > 0; i-- ) + if( Y->p[i] != 0 ) + break; + i++; + + X->s = Y->s; + + MPI_CHK( mpi_grow( X, i ) ); + + memset( X->p, 0, X->n * ciL ); + memcpy( X->p, Y->p, i * ciL ); + +cleanup: + + return( ret ); +} + +/* + * Swap the contents of X and Y + */ +void mpi_swap( mpi *X, mpi *Y ) +{ + mpi T; + + memcpy( &T, X, sizeof( mpi ) ); + memcpy( X, Y, sizeof( mpi ) ); + memcpy( Y, &T, sizeof( mpi ) ); +} + +/* + * Conditionally assign X = Y, without leaking information + * about whether the assignment was made or not. + * (Leaking information about the respective sizes of X and Y is ok however.) + */ +int mpi_safe_cond_assign( mpi *X, const mpi *Y, unsigned char assign ) +{ + int ret = 0; + size_t i; + + /* make sure assign is 0 or 1 */ + assign = ( assign != 0 ); + + MPI_CHK( mpi_grow( X, Y->n ) ); + + X->s = X->s * ( 1 - assign ) + Y->s * assign; + + for( i = 0; i < Y->n; i++ ) + X->p[i] = X->p[i] * ( 1 - assign ) + Y->p[i] * assign; + + for( ; i < X->n; i++ ) + X->p[i] *= ( 1 - assign ); + +cleanup: + return( ret ); +} + +/* + * Conditionally swap X and Y, without leaking information + * about whether the swap was made or not. + * Here it is not ok to simply swap the pointers, which whould lead to + * different memory access patterns when X and Y are used afterwards. + */ +int mpi_safe_cond_swap( mpi *X, mpi *Y, unsigned char swap ) +{ + int ret, s; + size_t i; + t_uint tmp; + + if( X == Y ) + return( 0 ); + + /* make sure swap is 0 or 1 */ + swap = ( swap != 0 ); + + MPI_CHK( mpi_grow( X, Y->n ) ); + MPI_CHK( mpi_grow( Y, X->n ) ); + + s = X->s; + X->s = X->s * ( 1 - swap ) + Y->s * swap; + Y->s = Y->s * ( 1 - swap ) + s * swap; + + + for( i = 0; i < X->n; i++ ) + { + tmp = X->p[i]; + X->p[i] = X->p[i] * ( 1 - swap ) + Y->p[i] * swap; + Y->p[i] = Y->p[i] * ( 1 - swap ) + tmp * swap; + } + +cleanup: + return( ret ); +} + +/* + * Set value from integer + */ +int mpi_lset( mpi *X, t_sint z ) +{ + int ret; + + MPI_CHK( mpi_grow( X, 1 ) ); + memset( X->p, 0, X->n * ciL ); + + X->p[0] = ( z < 0 ) ? -z : z; + X->s = ( z < 0 ) ? -1 : 1; + +cleanup: + + return( ret ); +} + +/* + * Get a specific bit + */ +int mpi_get_bit( const mpi *X, size_t pos ) +{ + if( X->n * biL <= pos ) + return( 0 ); + + return( ( X->p[pos / biL] >> ( pos % biL ) ) & 0x01 ); +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mpi_set_bit( mpi *X, size_t pos, unsigned char val ) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + + if( val != 0 && val != 1 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( X->n * biL <= pos ) + { + if( val == 0 ) + return( 0 ); + + MPI_CHK( mpi_grow( X, off + 1 ) ); + } + + X->p[off] &= ~( (t_uint) 0x01 << idx ); + X->p[off] |= (t_uint) val << idx; + +cleanup: + + return( ret ); +} + +/* + * Return the number of least significant bits + */ +size_t mpi_lsb( const mpi *X ) +{ + size_t i, j, count = 0; + + for( i = 0; i < X->n; i++ ) + for( j = 0; j < biL; j++, count++ ) + if( ( ( X->p[i] >> j ) & 1 ) != 0 ) + return( count ); + + return( 0 ); +} + +/* + * Return the number of most significant bits + */ +size_t mpi_msb( const mpi *X ) +{ + size_t i, j; + + for( i = X->n - 1; i > 0; i-- ) + if( X->p[i] != 0 ) + break; + + for( j = biL; j > 0; j-- ) + if( ( ( X->p[i] >> ( j - 1 ) ) & 1 ) != 0 ) + break; + + return( ( i * biL ) + j ); +} + +/* + * Return the total size in bytes + */ +size_t mpi_size( const mpi *X ) +{ + return( ( mpi_msb( X ) + 7 ) >> 3 ); +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit( t_uint *d, int radix, char c ) +{ + *d = 255; + + if( c >= 0x30 && c <= 0x39 ) *d = c - 0x30; + if( c >= 0x41 && c <= 0x46 ) *d = c - 0x37; + if( c >= 0x61 && c <= 0x66 ) *d = c - 0x57; + + if( *d >= (t_uint) radix ) + return( POLARSSL_ERR_MPI_INVALID_CHARACTER ); + + return( 0 ); +} + +/* + * Import from an ASCII string + */ +int mpi_read_string( mpi *X, int radix, const char *s ) +{ + int ret; + size_t i, j, slen, n; + t_uint d; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &T ); + + slen = strlen( s ); + + if( radix == 16 ) + { + n = BITS_TO_LIMBS( slen << 2 ); + + MPI_CHK( mpi_grow( X, n ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = slen, j = 0; i > 0; i--, j++ ) + { + if( i == 1 && s[i - 1] == '-' ) + { + X->s = -1; + break; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i - 1] ) ); + X->p[j / ( 2 * ciL )] |= d << ( ( j % ( 2 * ciL ) ) << 2 ); + } + } + else + { + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = 0; i < slen; i++ ) + { + if( i == 0 && s[i] == '-' ) + { + X->s = -1; + continue; + } + + MPI_CHK( mpi_get_digit( &d, radix, s[i] ) ); + MPI_CHK( mpi_mul_int( &T, X, radix ) ); + + if( X->s == 1 ) + { + MPI_CHK( mpi_add_int( X, &T, d ) ); + } + else + { + MPI_CHK( mpi_sub_int( X, &T, d ) ); + } + } + } + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +/* + * Helper to write the digits high-order first + */ +static int mpi_write_hlp( mpi *X, int radix, char **p ) +{ + int ret; + t_uint r; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( mpi_mod_int( &r, X, radix ) ); + MPI_CHK( mpi_div_int( X, NULL, X, radix ) ); + + if( mpi_cmp_int( X, 0 ) != 0 ) + MPI_CHK( mpi_write_hlp( X, radix, p ) ); + + if( r < 10 ) + *(*p)++ = (char)( r + 0x30 ); + else + *(*p)++ = (char)( r + 0x37 ); + +cleanup: + + return( ret ); +} + +/* + * Export into an ASCII string + */ +int mpi_write_string( const mpi *X, int radix, char *s, size_t *slen ) +{ + int ret = 0; + size_t n; + char *p; + mpi T; + + if( radix < 2 || radix > 16 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + n = mpi_msb( X ); + if( radix >= 4 ) n >>= 1; + if( radix >= 16 ) n >>= 1; + n += 3; + + if( *slen < n ) + { + *slen = n; + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + } + + p = s; + mpi_init( &T ); + + if( X->s == -1 ) + *p++ = '-'; + + if( radix == 16 ) + { + int c; + size_t i, j, k; + + for( i = X->n, k = 0; i > 0; i-- ) + { + for( j = ciL; j > 0; j-- ) + { + c = ( X->p[i - 1] >> ( ( j - 1 ) << 3) ) & 0xFF; + + if( c == 0 && k == 0 && ( i + j ) != 2 ) + continue; + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } + else + { + MPI_CHK( mpi_copy( &T, X ) ); + + if( T.s == -1 ) + T.s = 1; + + MPI_CHK( mpi_write_hlp( &T, radix, &p ) ); + } + + *p++ = '\0'; + *slen = p - s; + +cleanup: + + mpi_free( &T ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Read X from an opened file + */ +int mpi_read_file( mpi *X, int radix, FILE *fin ) +{ + t_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + memset( s, 0, sizeof( s ) ); + if( fgets( s, sizeof( s ) - 1, fin ) == NULL ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + + slen = strlen( s ); + if( slen == sizeof( s ) - 2 ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + if( s[slen - 1] == '\n' ) { slen--; s[slen] = '\0'; } + if( s[slen - 1] == '\r' ) { slen--; s[slen] = '\0'; } + + p = s + slen; + while( --p >= s ) + if( mpi_get_digit( &d, radix, *p ) != 0 ) + break; + + return( mpi_read_string( X, radix, p + 1 ) ); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mpi_write_file( const char *p, const mpi *X, int radix, FILE *fout ) +{ + int ret; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[ POLARSSL_MPI_RW_BUFFER_SIZE ]; + + n = sizeof( s ); + memset( s, 0, n ); + n -= 2; + + MPI_CHK( mpi_write_string( X, radix, s, (size_t *) &n ) ); + + if( p == NULL ) p = ""; + + plen = strlen( p ); + slen = strlen( s ); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if( fout != NULL ) + { + if( fwrite( p, 1, plen, fout ) != plen || + fwrite( s, 1, slen, fout ) != slen ) + return( POLARSSL_ERR_MPI_FILE_IO_ERROR ); + } + else + polarssl_printf( "%s%s", p, s ); + +cleanup: + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Import X from unsigned binary data, big endian + */ +int mpi_read_binary( mpi *X, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t i, j, n; + + for( n = 0; n < buflen; n++ ) + if( buf[n] != 0 ) + break; + + MPI_CHK( mpi_grow( X, CHARS_TO_LIMBS( buflen - n ) ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i = buflen, j = 0; i > n; i--, j++ ) + X->p[j / ciL] |= ((t_uint) buf[i - 1]) << ((j % ciL) << 3); + +cleanup: + + return( ret ); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mpi_write_binary( const mpi *X, unsigned char *buf, size_t buflen ) +{ + size_t i, j, n; + + n = mpi_size( X ); + + if( buflen < n ) + return( POLARSSL_ERR_MPI_BUFFER_TOO_SMALL ); + + memset( buf, 0, buflen ); + + for( i = buflen - 1, j = 0; n > 0; i--, j++, n-- ) + buf[i] = (unsigned char)( X->p[j / ciL] >> ((j % ciL) << 3) ); + + return( 0 ); +} + +/* + * Left-shift: X <<= count + */ +int mpi_shift_l( mpi *X, size_t count ) +{ + int ret; + size_t i, v0, t1; + t_uint r0 = 0, r1; + + v0 = count / (biL ); + t1 = count & (biL - 1); + + i = mpi_msb( X ) + count; + + if( X->n * biL < i ) + MPI_CHK( mpi_grow( X, BITS_TO_LIMBS( i ) ) ); + + ret = 0; + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = X->n; i > v0; i-- ) + X->p[i - 1] = X->p[i - v0 - 1]; + + for( ; i > 0; i-- ) + X->p[i - 1] = 0; + } + + /* + * shift by count % limb_size + */ + if( t1 > 0 ) + { + for( i = v0; i < X->n; i++ ) + { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return( ret ); +} + +/* + * Right-shift: X >>= count + */ +int mpi_shift_r( mpi *X, size_t count ) +{ + size_t i, v0, v1; + t_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if( v0 > X->n || ( v0 == X->n && v1 > 0 ) ) + return mpi_lset( X, 0 ); + + /* + * shift by count / limb_size + */ + if( v0 > 0 ) + { + for( i = 0; i < X->n - v0; i++ ) + X->p[i] = X->p[i + v0]; + + for( ; i < X->n; i++ ) + X->p[i] = 0; + } + + /* + * shift by count % limb_size + */ + if( v1 > 0 ) + { + for( i = X->n; i > 0; i-- ) + { + r1 = X->p[i - 1] << (biL - v1); + X->p[i - 1] >>= v1; + X->p[i - 1] |= r0; + r0 = r1; + } + } + + return( 0 ); +} + +/* + * Compare unsigned values + */ +int mpi_cmp_abs( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( 1 ); + if( j > i ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( 1 ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -1 ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_mpi( const mpi *X, const mpi *Y ) +{ + size_t i, j; + + for( i = X->n; i > 0; i-- ) + if( X->p[i - 1] != 0 ) + break; + + for( j = Y->n; j > 0; j-- ) + if( Y->p[j - 1] != 0 ) + break; + + if( i == 0 && j == 0 ) + return( 0 ); + + if( i > j ) return( X->s ); + if( j > i ) return( -Y->s ); + + if( X->s > 0 && Y->s < 0 ) return( 1 ); + if( Y->s > 0 && X->s < 0 ) return( -1 ); + + for( ; i > 0; i-- ) + { + if( X->p[i - 1] > Y->p[i - 1] ) return( X->s ); + if( X->p[i - 1] < Y->p[i - 1] ) return( -X->s ); + } + + return( 0 ); +} + +/* + * Compare signed values + */ +int mpi_cmp_int( const mpi *X, t_sint z ) +{ + mpi Y; + t_uint p[1]; + + *p = ( z < 0 ) ? -z : z; + Y.s = ( z < 0 ) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return( mpi_cmp_mpi( X, &Y ) ); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mpi_add_abs( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + t_uint *o, *p, c; + + if( X == B ) + { + const mpi *T = A; A = X; B = T; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned additions. + */ + X->s = 1; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, j ) ); + + o = B->p; p = X->p; c = 0; + + for( i = 0; i < j; i++, o++, p++ ) + { + *p += c; c = ( *p < c ); + *p += *o; c += ( *p < *o ); + } + + while( c != 0 ) + { + if( i >= X->n ) + { + MPI_CHK( mpi_grow( X, i + 1 ) ); + p = X->p + i; + } + + *p += c; c = ( *p < c ); i++; p++; + } + +cleanup: + + return( ret ); +} + +/* + * Helper for mpi subtraction + */ +static void mpi_sub_hlp( size_t n, t_uint *s, t_uint *d ) +{ + size_t i; + t_uint c, z; + + for( i = c = 0; i < n; i++, s++, d++ ) + { + z = ( *d < c ); *d -= c; + c = ( *d < *s ) + z; *d -= *s; + } + + while( c != 0 ) + { + z = ( *d < c ); *d -= c; + c = z; i++; d++; + } +} + +/* + * Unsigned subtraction: X = |A| - |B| (HAC 14.9) + */ +int mpi_sub_abs( mpi *X, const mpi *A, const mpi *B ) +{ + mpi TB; + int ret; + size_t n; + + if( mpi_cmp_abs( A, B ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + mpi_init( &TB ); + + if( X == B ) + { + MPI_CHK( mpi_copy( &TB, B ) ); + B = &TB; + } + + if( X != A ) + MPI_CHK( mpi_copy( X, A ) ); + + /* + * X should always be positive as a result of unsigned subtractions. + */ + X->s = 1; + + ret = 0; + + for( n = B->n; n > 0; n-- ) + if( B->p[n - 1] != 0 ) + break; + + mpi_sub_hlp( n, B->p, X->p ); + +cleanup: + + mpi_free( &TB ); + + return( ret ); +} + +/* + * Signed addition: X = A + B + */ +int mpi_add_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s < 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed subtraction: X = A - B + */ +int mpi_sub_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret, s = A->s; + + if( A->s * B->s > 0 ) + { + if( mpi_cmp_abs( A, B ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( X, A, B ) ); + X->s = s; + } + else + { + MPI_CHK( mpi_sub_abs( X, B, A ) ); + X->s = -s; + } + } + else + { + MPI_CHK( mpi_add_abs( X, A, B ) ); + X->s = s; + } + +cleanup: + + return( ret ); +} + +/* + * Signed addition: X = A + b + */ +int mpi_add_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_add_mpi( X, A, &_B ) ); +} + +/* + * Signed subtraction: X = A - b + */ +int mpi_sub_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_sub_mpi( X, A, &_B ) ); +} + +/* + * Helper for mpi multiplication + */ +static +#if defined(__APPLE__) && defined(__arm__) +/* + * Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn) + * appears to need this to prevent bad ARM code generation at -O3. + */ +__attribute__ ((noinline)) +#endif +void mpi_mul_hlp( size_t i, t_uint *s, t_uint *d, t_uint b ) +{ + t_uint c = 0, t = 0; + +#if defined(MULADDC_HUIT) + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_HUIT + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#else /* MULADDC_HUIT */ + for( ; i >= 16; i -= 16 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i >= 8; i -= 8 ) + { + MULADDC_INIT + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + + MULADDC_CORE MULADDC_CORE + MULADDC_CORE MULADDC_CORE + MULADDC_STOP + } + + for( ; i > 0; i-- ) + { + MULADDC_INIT + MULADDC_CORE + MULADDC_STOP + } +#endif /* MULADDC_HUIT */ + + t++; + + do { + *d += c; c = ( *d < c ); d++; + } + while( c != 0 ); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mpi_mul_mpi( mpi *X, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, j; + mpi TA, TB; + + mpi_init( &TA ); mpi_init( &TB ); + + if( X == A ) { MPI_CHK( mpi_copy( &TA, A ) ); A = &TA; } + if( X == B ) { MPI_CHK( mpi_copy( &TB, B ) ); B = &TB; } + + for( i = A->n; i > 0; i-- ) + if( A->p[i - 1] != 0 ) + break; + + for( j = B->n; j > 0; j-- ) + if( B->p[j - 1] != 0 ) + break; + + MPI_CHK( mpi_grow( X, i + j ) ); + MPI_CHK( mpi_lset( X, 0 ) ); + + for( i++; j > 0; j-- ) + mpi_mul_hlp( i - 1, A->p, X->p + j - 1, B->p[j - 1] ); + + X->s = A->s * B->s; + +cleanup: + + mpi_free( &TB ); mpi_free( &TA ); + + return( ret ); +} + +/* + * Baseline multiplication: X = A * b + */ +int mpi_mul_int( mpi *X, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + _B.s = 1; + _B.n = 1; + _B.p = p; + p[0] = b; + + return( mpi_mul_mpi( X, A, &_B ) ); +} + +/* + * Division by mpi: A = Q * B + R (HAC 14.20) + */ +int mpi_div_mpi( mpi *Q, mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + size_t i, n, t, k; + mpi X, Y, Z, T1, T2; + + if( mpi_cmp_int( B, 0 ) == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + mpi_init( &T1 ); mpi_init( &T2 ); + + if( mpi_cmp_abs( A, B ) < 0 ) + { + if( Q != NULL ) MPI_CHK( mpi_lset( Q, 0 ) ); + if( R != NULL ) MPI_CHK( mpi_copy( R, A ) ); + return( 0 ); + } + + MPI_CHK( mpi_copy( &X, A ) ); + MPI_CHK( mpi_copy( &Y, B ) ); + X.s = Y.s = 1; + + MPI_CHK( mpi_grow( &Z, A->n + 2 ) ); + MPI_CHK( mpi_lset( &Z, 0 ) ); + MPI_CHK( mpi_grow( &T1, 2 ) ); + MPI_CHK( mpi_grow( &T2, 3 ) ); + + k = mpi_msb( &Y ) % biL; + if( k < biL - 1 ) + { + k = biL - 1 - k; + MPI_CHK( mpi_shift_l( &X, k ) ); + MPI_CHK( mpi_shift_l( &Y, k ) ); + } + else k = 0; + + n = X.n - 1; + t = Y.n - 1; + MPI_CHK( mpi_shift_l( &Y, biL * ( n - t ) ) ); + + while( mpi_cmp_mpi( &X, &Y ) >= 0 ) + { + Z.p[n - t]++; + MPI_CHK( mpi_sub_mpi( &X, &X, &Y ) ); + } + MPI_CHK( mpi_shift_r( &Y, biL * ( n - t ) ) ); + + for( i = n; i > t ; i-- ) + { + if( X.p[i] >= Y.p[t] ) + Z.p[i - t - 1] = ~0; + else + { + /* + * The version of Clang shipped by Apple with Mavericks around + * 2014-03 can't handle 128-bit division properly. Disable + * 128-bits division for this version. Let's be optimistic and + * assume it'll be fixed in the next minor version (next + * patchlevel is probably a bit too optimistic). + */ +#if defined(POLARSSL_HAVE_UDBL) && \ + ! ( defined(__x86_64__) && defined(__APPLE__) && \ + defined(__clang_major__) && __clang_major__ == 5 && \ + defined(__clang_minor__) && __clang_minor__ == 0 ) + t_udbl r; + + r = (t_udbl) X.p[i] << biL; + r |= (t_udbl) X.p[i - 1]; + r /= Y.p[t]; + if( r > ( (t_udbl) 1 << biL ) - 1 ) + r = ( (t_udbl) 1 << biL ) - 1; + + Z.p[i - t - 1] = (t_uint) r; +#else + /* + * __udiv_qrnnd_c, from gmp/longlong.h + */ + t_uint q0, q1, r0, r1; + t_uint d0, d1, d, m; + + d = Y.p[t]; + d0 = ( d << biH ) >> biH; + d1 = ( d >> biH ); + + q1 = X.p[i] / d1; + r1 = X.p[i] - d1 * q1; + r1 <<= biH; + r1 |= ( X.p[i - 1] >> biH ); + + m = q1 * d0; + if( r1 < m ) + { + q1--, r1 += d; + while( r1 >= d && r1 < m ) + q1--, r1 += d; + } + r1 -= m; + + q0 = r1 / d1; + r0 = r1 - d1 * q0; + r0 <<= biH; + r0 |= ( X.p[i - 1] << biH ) >> biH; + + m = q0 * d0; + if( r0 < m ) + { + q0--, r0 += d; + while( r0 >= d && r0 < m ) + q0--, r0 += d; + } + r0 -= m; + + Z.p[i - t - 1] = ( q1 << biH ) | q0; +#endif /* POLARSSL_HAVE_UDBL && !64-bit Apple with Clang 5.0 */ + } + + Z.p[i - t - 1]++; + do + { + Z.p[i - t - 1]--; + + MPI_CHK( mpi_lset( &T1, 0 ) ); + T1.p[0] = ( t < 1 ) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MPI_CHK( mpi_mul_int( &T1, &T1, Z.p[i - t - 1] ) ); + + MPI_CHK( mpi_lset( &T2, 0 ) ); + T2.p[0] = ( i < 2 ) ? 0 : X.p[i - 2]; + T2.p[1] = ( i < 1 ) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + } + while( mpi_cmp_mpi( &T1, &T2 ) > 0 ); + + MPI_CHK( mpi_mul_int( &T1, &Y, Z.p[i - t - 1] ) ); + MPI_CHK( mpi_shift_l( &T1, biL * ( i - t - 1 ) ) ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); + + if( mpi_cmp_int( &X, 0 ) < 0 ) + { + MPI_CHK( mpi_copy( &T1, &Y ) ); + MPI_CHK( mpi_shift_l( &T1, biL * ( i - t - 1 ) ) ); + MPI_CHK( mpi_add_mpi( &X, &X, &T1 ) ); + Z.p[i - t - 1]--; + } + } + + if( Q != NULL ) + { + MPI_CHK( mpi_copy( Q, &Z ) ); + Q->s = A->s * B->s; + } + + if( R != NULL ) + { + MPI_CHK( mpi_shift_r( &X, k ) ); + X.s = A->s; + MPI_CHK( mpi_copy( R, &X ) ); + + if( mpi_cmp_int( R, 0 ) == 0 ) + R->s = 1; + } + +cleanup: + + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + mpi_free( &T1 ); mpi_free( &T2 ); + + return( ret ); +} + +/* + * Division by int: A = Q * b + R + */ +int mpi_div_int( mpi *Q, mpi *R, const mpi *A, t_sint b ) +{ + mpi _B; + t_uint p[1]; + + p[0] = ( b < 0 ) ? -b : b; + _B.s = ( b < 0 ) ? -1 : 1; + _B.n = 1; + _B.p = p; + + return( mpi_div_mpi( Q, R, A, &_B ) ); +} + +/* + * Modulo: R = A mod B + */ +int mpi_mod_mpi( mpi *R, const mpi *A, const mpi *B ) +{ + int ret; + + if( mpi_cmp_int( B, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + MPI_CHK( mpi_div_mpi( NULL, R, A, B ) ); + + while( mpi_cmp_int( R, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( R, R, B ) ); + + while( mpi_cmp_mpi( R, B ) >= 0 ) + MPI_CHK( mpi_sub_mpi( R, R, B ) ); + +cleanup: + + return( ret ); +} + +/* + * Modulo: r = A mod b + */ +int mpi_mod_int( t_uint *r, const mpi *A, t_sint b ) +{ + size_t i; + t_uint x, y, z; + + if( b == 0 ) + return( POLARSSL_ERR_MPI_DIVISION_BY_ZERO ); + + if( b < 0 ) + return( POLARSSL_ERR_MPI_NEGATIVE_VALUE ); + + /* + * handle trivial cases + */ + if( b == 1 ) + { + *r = 0; + return( 0 ); + } + + if( b == 2 ) + { + *r = A->p[0] & 1; + return( 0 ); + } + + /* + * general case + */ + for( i = A->n, y = 0; i > 0; i-- ) + { + x = A->p[i - 1]; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + + x <<= biH; + y = ( y << biH ) | ( x >> biH ); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if( A->s < 0 && y != 0 ) + y = b - y; + + *r = y; + + return( 0 ); +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis) + */ +static void mpi_montg_init( t_uint *mm, const mpi *N ) +{ + t_uint x, m0 = N->p[0]; + unsigned int i; + + x = m0; + x += ( ( m0 + 2 ) & 4 ) << 1; + + for( i = biL; i >= 8; i /= 2 ) + x *= ( 2 - ( m0 * x ) ); + + *mm = ~x + 1; +} + +/* + * Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + */ +static void mpi_montmul( mpi *A, const mpi *B, const mpi *N, t_uint mm, + const mpi *T ) +{ + size_t i, n, m; + t_uint u0, u1, *d; + + memset( T->p, 0, T->n * ciL ); + + d = T->p; + n = N->n; + m = ( B->n < n ) ? B->n : n; + + for( i = 0; i < n; i++ ) + { + /* + * T = (T + u0*B + u1*N) / 2^biL + */ + u0 = A->p[i]; + u1 = ( d[0] + u0 * B->p[0] ) * mm; + + mpi_mul_hlp( m, B->p, d, u0 ); + mpi_mul_hlp( n, N->p, d, u1 ); + + *d++ = u0; d[n + 1] = 0; + } + + memcpy( A->p, d, ( n + 1 ) * ciL ); + + if( mpi_cmp_abs( A, N ) >= 0 ) + mpi_sub_hlp( n, N->p, A->p ); + else + /* prevent timing attacks */ + mpi_sub_hlp( n, A->p, T->p ); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + */ +static void mpi_montred( mpi *A, const mpi *N, t_uint mm, const mpi *T ) +{ + t_uint z = 1; + mpi U; + + U.n = U.s = (int) z; + U.p = &z; + + mpi_montmul( A, &U, N, mm, T ); +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mpi_exp_mod( mpi *X, const mpi *A, const mpi *E, const mpi *N, mpi *_RR ) +{ + int ret; + size_t wbits, wsize, one = 1; + size_t i, j, nblimbs; + size_t bufsize, nbits; + t_uint ei, mm, state; + mpi RR, T, W[ 2 << POLARSSL_MPI_WINDOW_SIZE ], Apos; + int neg; + + if( mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + if( mpi_cmp_int( E, 0 ) < 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + /* + * Init temps and window size + */ + mpi_montg_init( &mm, N ); + mpi_init( &RR ); mpi_init( &T ); + mpi_init( &Apos ); + memset( W, 0, sizeof( W ) ); + + i = mpi_msb( E ); + + wsize = ( i > 671 ) ? 6 : ( i > 239 ) ? 5 : + ( i > 79 ) ? 4 : ( i > 23 ) ? 3 : 1; + + if( wsize > POLARSSL_MPI_WINDOW_SIZE ) + wsize = POLARSSL_MPI_WINDOW_SIZE; + + j = N->n + 1; + MPI_CHK( mpi_grow( X, j ) ); + MPI_CHK( mpi_grow( &W[1], j ) ); + MPI_CHK( mpi_grow( &T, j * 2 ) ); + + /* + * Compensate for negative A (and correct at the end) + */ + neg = ( A->s == -1 ); + if( neg ) + { + MPI_CHK( mpi_copy( &Apos, A ) ); + Apos.s = 1; + A = &Apos; + } + + /* + * If 1st call, pre-compute R^2 mod N + */ + if( _RR == NULL || _RR->p == NULL ) + { + MPI_CHK( mpi_lset( &RR, 1 ) ); + MPI_CHK( mpi_shift_l( &RR, N->n * 2 * biL ) ); + MPI_CHK( mpi_mod_mpi( &RR, &RR, N ) ); + + if( _RR != NULL ) + memcpy( _RR, &RR, sizeof( mpi ) ); + } + else + memcpy( &RR, _RR, sizeof( mpi ) ); + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if( mpi_cmp_mpi( A, N ) >= 0 ) + MPI_CHK( mpi_mod_mpi( &W[1], A, N ) ); + else + MPI_CHK( mpi_copy( &W[1], A ) ); + + mpi_montmul( &W[1], &RR, N, mm, &T ); + + /* + * X = R^2 * R^-1 mod N = R mod N + */ + MPI_CHK( mpi_copy( X, &RR ) ); + mpi_montred( X, N, mm, &T ); + + if( wsize > 1 ) + { + /* + * W[1 << (wsize - 1)] = W[1] ^ (wsize - 1) + */ + j = one << ( wsize - 1 ); + + MPI_CHK( mpi_grow( &W[j], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[j], &W[1] ) ); + + for( i = 0; i < wsize - 1; i++ ) + mpi_montmul( &W[j], &W[j], N, mm, &T ); + + /* + * W[i] = W[i - 1] * W[1] + */ + for( i = j + 1; i < ( one << wsize ); i++ ) + { + MPI_CHK( mpi_grow( &W[i], N->n + 1 ) ); + MPI_CHK( mpi_copy( &W[i], &W[i - 1] ) ); + + mpi_montmul( &W[i], &W[1], N, mm, &T ); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + wbits = 0; + state = 0; + + while( 1 ) + { + if( bufsize == 0 ) + { + if( nblimbs == 0 ) + break; + + nblimbs--; + + bufsize = sizeof( t_uint ) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if( ei == 0 && state == 0 ) + continue; + + if( ei == 0 && state == 1 ) + { + /* + * out of window, square X + */ + mpi_montmul( X, X, N, mm, &T ); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + wbits |= ( ei << ( wsize - nbits ) ); + + if( nbits == wsize ) + { + /* + * X = X^wsize R^-1 mod N + */ + for( i = 0; i < wsize; i++ ) + mpi_montmul( X, X, N, mm, &T ); + + /* + * X = X * W[wbits] R^-1 mod N + */ + mpi_montmul( X, &W[wbits], N, mm, &T ); + + state--; + nbits = 0; + wbits = 0; + } + } + + /* + * process the remaining bits + */ + for( i = 0; i < nbits; i++ ) + { + mpi_montmul( X, X, N, mm, &T ); + + wbits <<= 1; + + if( ( wbits & ( one << wsize ) ) != 0 ) + mpi_montmul( X, &W[1], N, mm, &T ); + } + + /* + * X = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred( X, N, mm, &T ); + + if( neg ) + { + X->s = -1; + MPI_CHK( mpi_add_mpi( X, N, X ) ); + } + +cleanup: + + for( i = ( one << ( wsize - 1 ) ); i < ( one << wsize ); i++ ) + mpi_free( &W[i] ); + + mpi_free( &W[1] ); mpi_free( &T ); mpi_free( &Apos ); + + if( _RR == NULL || _RR->p == NULL ) + mpi_free( &RR ); + + return( ret ); +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +int mpi_gcd( mpi *G, const mpi *A, const mpi *B ) +{ + int ret; + size_t lz, lzt; + mpi TG, TA, TB; + + mpi_init( &TG ); mpi_init( &TA ); mpi_init( &TB ); + + MPI_CHK( mpi_copy( &TA, A ) ); + MPI_CHK( mpi_copy( &TB, B ) ); + + lz = mpi_lsb( &TA ); + lzt = mpi_lsb( &TB ); + + if( lzt < lz ) + lz = lzt; + + MPI_CHK( mpi_shift_r( &TA, lz ) ); + MPI_CHK( mpi_shift_r( &TB, lz ) ); + + TA.s = TB.s = 1; + + while( mpi_cmp_int( &TA, 0 ) != 0 ) + { + MPI_CHK( mpi_shift_r( &TA, mpi_lsb( &TA ) ) ); + MPI_CHK( mpi_shift_r( &TB, mpi_lsb( &TB ) ) ); + + if( mpi_cmp_mpi( &TA, &TB ) >= 0 ) + { + MPI_CHK( mpi_sub_abs( &TA, &TA, &TB ) ); + MPI_CHK( mpi_shift_r( &TA, 1 ) ); + } + else + { + MPI_CHK( mpi_sub_abs( &TB, &TB, &TA ) ); + MPI_CHK( mpi_shift_r( &TB, 1 ) ); + } + } + + MPI_CHK( mpi_shift_l( &TB, lz ) ); + MPI_CHK( mpi_copy( G, &TB ) ); + +cleanup: + + mpi_free( &TG ); mpi_free( &TA ); mpi_free( &TB ); + + return( ret ); +} + +/* + * Fill X with size bytes of random. + * + * Use a temporary bytes representation to make sure the result is the same + * regardless of the platform endianness (useful when f_rng is actually + * deterministic, eg for tests). + */ +int mpi_fill_random( mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( size > POLARSSL_MPI_MAX_SIZE ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + MPI_CHK( f_rng( p_rng, buf, size ) ); + MPI_CHK( mpi_read_binary( X, buf, size ) ); + +cleanup: + return( ret ); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mpi_inv_mod( mpi *X, const mpi *A, const mpi *N ) +{ + int ret; + mpi G, TA, TU, U1, U2, TB, TV, V1, V2; + + if( mpi_cmp_int( N, 0 ) <= 0 ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &TA ); mpi_init( &TU ); mpi_init( &U1 ); mpi_init( &U2 ); + mpi_init( &G ); mpi_init( &TB ); mpi_init( &TV ); + mpi_init( &V1 ); mpi_init( &V2 ); + + MPI_CHK( mpi_gcd( &G, A, N ) ); + + if( mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + MPI_CHK( mpi_mod_mpi( &TA, A, N ) ); + MPI_CHK( mpi_copy( &TU, &TA ) ); + MPI_CHK( mpi_copy( &TB, N ) ); + MPI_CHK( mpi_copy( &TV, N ) ); + + MPI_CHK( mpi_lset( &U1, 1 ) ); + MPI_CHK( mpi_lset( &U2, 0 ) ); + MPI_CHK( mpi_lset( &V1, 0 ) ); + MPI_CHK( mpi_lset( &V2, 1 ) ); + + do + { + while( ( TU.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TU, 1 ) ); + + if( ( U1.p[0] & 1 ) != 0 || ( U2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &U1, &U1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &U1, 1 ) ); + MPI_CHK( mpi_shift_r( &U2, 1 ) ); + } + + while( ( TV.p[0] & 1 ) == 0 ) + { + MPI_CHK( mpi_shift_r( &TV, 1 ) ); + + if( ( V1.p[0] & 1 ) != 0 || ( V2.p[0] & 1 ) != 0 ) + { + MPI_CHK( mpi_add_mpi( &V1, &V1, &TB ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &TA ) ); + } + + MPI_CHK( mpi_shift_r( &V1, 1 ) ); + MPI_CHK( mpi_shift_r( &V2, 1 ) ); + } + + if( mpi_cmp_mpi( &TU, &TV ) >= 0 ) + { + MPI_CHK( mpi_sub_mpi( &TU, &TU, &TV ) ); + MPI_CHK( mpi_sub_mpi( &U1, &U1, &V1 ) ); + MPI_CHK( mpi_sub_mpi( &U2, &U2, &V2 ) ); + } + else + { + MPI_CHK( mpi_sub_mpi( &TV, &TV, &TU ) ); + MPI_CHK( mpi_sub_mpi( &V1, &V1, &U1 ) ); + MPI_CHK( mpi_sub_mpi( &V2, &V2, &U2 ) ); + } + } + while( mpi_cmp_int( &TU, 0 ) != 0 ); + + while( mpi_cmp_int( &V1, 0 ) < 0 ) + MPI_CHK( mpi_add_mpi( &V1, &V1, N ) ); + + while( mpi_cmp_mpi( &V1, N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( &V1, &V1, N ) ); + + MPI_CHK( mpi_copy( X, &V1 ) ); + +cleanup: + + mpi_free( &TA ); mpi_free( &TU ); mpi_free( &U1 ); mpi_free( &U2 ); + mpi_free( &G ); mpi_free( &TB ); mpi_free( &TV ); + mpi_free( &V1 ); mpi_free( &V2 ); + + return( ret ); +} + +#if defined(POLARSSL_GENPRIME) + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Small divisors test (X must be positive) + * + * Return values: + * 0: no small factor (possible prime, more tests needed) + * 1: certain prime + * POLARSSL_ERR_MPI_NOT_ACCEPTABLE: certain non-prime + * other negative: error + */ +static int mpi_check_small_factors( const mpi *X ) +{ + int ret = 0; + size_t i; + t_uint r; + + if( ( X->p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + for( i = 0; small_prime[i] > 0; i++ ) + { + if( mpi_cmp_int( X, small_prime[i] ) <= 0 ) + return( 1 ); + + MPI_CHK( mpi_mod_int( &r, X, small_prime[i] ) ); + + if( r == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + +cleanup: + return( ret ); +} + +/* + * Miller-Rabin pseudo-primality test (HAC 4.24) + */ +static int mpi_miller_rabin( const mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t i, j, n, s; + mpi W, R, T, A, RR; + + mpi_init( &W ); mpi_init( &R ); mpi_init( &T ); mpi_init( &A ); + mpi_init( &RR ); + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MPI_CHK( mpi_sub_int( &W, X, 1 ) ); + s = mpi_lsb( &W ); + MPI_CHK( mpi_copy( &R, &W ) ); + MPI_CHK( mpi_shift_r( &R, s ) ); + + i = mpi_msb( X ); + /* + * HAC, table 4.4 + */ + n = ( ( i >= 1300 ) ? 2 : ( i >= 850 ) ? 3 : + ( i >= 650 ) ? 4 : ( i >= 350 ) ? 8 : + ( i >= 250 ) ? 12 : ( i >= 150 ) ? 18 : 27 ); + + for( i = 0; i < n; i++ ) + { + /* + * pick a random A, 1 < A < |X| - 1 + */ + MPI_CHK( mpi_fill_random( &A, X->n * ciL, f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &A, &W ) >= 0 ) + { + j = mpi_msb( &A ) - mpi_msb( &W ); + MPI_CHK( mpi_shift_r( &A, j + 1 ) ); + } + A.p[0] |= 3; + + /* + * A = A^R mod |X| + */ + MPI_CHK( mpi_exp_mod( &A, &A, &R, X, &RR ) ); + + if( mpi_cmp_mpi( &A, &W ) == 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + continue; + + j = 1; + while( j < s && mpi_cmp_mpi( &A, &W ) != 0 ) + { + /* + * A = A * A mod |X| + */ + MPI_CHK( mpi_mul_mpi( &T, &A, &A ) ); + MPI_CHK( mpi_mod_mpi( &A, &T, X ) ); + + if( mpi_cmp_int( &A, 1 ) == 0 ) + break; + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if( mpi_cmp_mpi( &A, &W ) != 0 || + mpi_cmp_int( &A, 1 ) == 0 ) + { + ret = POLARSSL_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + mpi_free( &W ); mpi_free( &R ); mpi_free( &T ); mpi_free( &A ); + mpi_free( &RR ); + + return( ret ); +} + +/* + * Pseudo-primality test: small factors, then Miller-Rabin + */ +int mpi_is_prime( mpi *X, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + const mpi XX = { 1, X->n, X->p }; /* Abs(X) */ + + if( mpi_cmp_int( &XX, 0 ) == 0 || + mpi_cmp_int( &XX, 1 ) == 0 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + + if( mpi_cmp_int( &XX, 2 ) == 0 ) + return( 0 ); + + if( ( ret = mpi_check_small_factors( &XX ) ) != 0 ) + { + if( ret == 1 ) + return( 0 ); + + return( ret ); + } + + return( mpi_miller_rabin( &XX, f_rng, p_rng ) ); +} + +/* + * Prime number generation + */ +int mpi_gen_prime( mpi *X, size_t nbits, int dh_flag, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t k, n; + t_uint r; + mpi Y; + + if( nbits < 3 || nbits > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_MPI_BAD_INPUT_DATA ); + + mpi_init( &Y ); + + n = BITS_TO_LIMBS( nbits ); + + MPI_CHK( mpi_fill_random( X, n * ciL, f_rng, p_rng ) ); + + k = mpi_msb( X ); + if( k < nbits ) MPI_CHK( mpi_shift_l( X, nbits - k ) ); + if( k > nbits ) MPI_CHK( mpi_shift_r( X, k - nbits ) ); + + X->p[0] |= 3; + + if( dh_flag == 0 ) + { + while( ( ret = mpi_is_prime( X, f_rng, p_rng ) ) != 0 ) + { + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + MPI_CHK( mpi_add_int( X, X, 2 ) ); + } + } + else + { + /* + * An necessary condition for Y and X = 2Y + 1 to be prime + * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3). + * Make sure it is satisfied, while keeping X = 3 mod 4 + */ + MPI_CHK( mpi_mod_int( &r, X, 3 ) ); + if( r == 0 ) + MPI_CHK( mpi_add_int( X, X, 8 ) ); + else if( r == 1 ) + MPI_CHK( mpi_add_int( X, X, 4 ) ); + + /* Set Y = (X-1) / 2, which is X / 2 because X is odd */ + MPI_CHK( mpi_copy( &Y, X ) ); + MPI_CHK( mpi_shift_r( &Y, 1 ) ); + + while( 1 ) + { + /* + * First, check small factors for X and Y + * before doing Miller-Rabin on any of them + */ + if( ( ret = mpi_check_small_factors( X ) ) == 0 && + ( ret = mpi_check_small_factors( &Y ) ) == 0 && + ( ret = mpi_miller_rabin( X, f_rng, p_rng ) ) == 0 && + ( ret = mpi_miller_rabin( &Y, f_rng, p_rng ) ) == 0 ) + { + break; + } + + if( ret != POLARSSL_ERR_MPI_NOT_ACCEPTABLE ) + goto cleanup; + + /* + * Next candidates. We want to preserve Y = (X-1) / 2 and + * Y = 1 mod 2 and Y = 2 mod 3 (eq X = 3 mod 4 and X = 2 mod 3) + * so up Y by 6 and X by 12. + */ + MPI_CHK( mpi_add_int( X, X, 12 ) ); + MPI_CHK( mpi_add_int( &Y, &Y, 6 ) ); + } + } + +cleanup: + + mpi_free( &Y ); + + return( ret ); +} + +#endif /* POLARSSL_GENPRIME */ + +#if defined(POLARSSL_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mpi_self_test( int verbose ) +{ + int ret, i; + mpi A, E, N, X, Y, U, V; + + mpi_init( &A ); mpi_init( &E ); mpi_init( &N ); mpi_init( &X ); + mpi_init( &Y ); mpi_init( &U ); mpi_init( &V ); + + MPI_CHK( mpi_read_string( &A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6" ) ); + + MPI_CHK( mpi_read_string( &E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E" ) ); + + MPI_CHK( mpi_read_string( &N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5" ) ); + + MPI_CHK( mpi_mul_mpi( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E" ) ); + + if( verbose != 0 ) + polarssl_printf( " MPI test #1 (mul_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + MPI_CHK( mpi_div_mpi( &X, &Y, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "256567336059E52CAE22925474705F39A94" ) ); + + MPI_CHK( mpi_read_string( &V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642" ) ); + + if( verbose != 0 ) + polarssl_printf( " MPI test #2 (div_mpi): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 || + mpi_cmp_mpi( &Y, &V ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + MPI_CHK( mpi_exp_mod( &X, &A, &E, &N, NULL ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87" ) ); + + if( verbose != 0 ) + polarssl_printf( " MPI test #3 (exp_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + MPI_CHK( mpi_inv_mod( &X, &A, &N ) ); + + MPI_CHK( mpi_read_string( &U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61" ) ); + + if( verbose != 0 ) + polarssl_printf( " MPI test #4 (inv_mod): " ); + + if( mpi_cmp_mpi( &X, &U ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto cleanup; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " MPI test #5 (simple gcd): " ); + + for( i = 0; i < GCD_PAIR_COUNT; i++ ) + { + MPI_CHK( mpi_lset( &X, gcd_pairs[i][0] ) ); + MPI_CHK( mpi_lset( &Y, gcd_pairs[i][1] ) ); + + MPI_CHK( mpi_gcd( &A, &X, &Y ) ); + + if( mpi_cmp_int( &A, gcd_pairs[i][2] ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed at %d\n", i ); + + ret = 1; + goto cleanup; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + +cleanup: + + if( ret != 0 && verbose != 0 ) + polarssl_printf( "Unexpected error, return code = %08X\n", ret ); + + mpi_free( &A ); mpi_free( &E ); mpi_free( &N ); mpi_free( &X ); + mpi_free( &Y ); mpi_free( &U ); mpi_free( &V ); + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_BIGNUM_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/blowfish.c b/component/common/network/ssl/polarssl-1.3.8/library/blowfish.c new file mode 100644 index 0000000..87396dc --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/blowfish.c @@ -0,0 +1,658 @@ +/* + * Blowfish implementation + * + * Copyright (C) 2012-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The Blowfish block cipher was designed by Bruce Schneier in 1993. + * http://www.schneier.com/blowfish.html + * http://en.wikipedia.org/wiki/Blowfish_%28cipher%29 + * + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_BLOWFISH_C) + +#include "polarssl/blowfish.h" + +#if !defined(POLARSSL_BLOWFISH_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +static const uint32_t P[BLOWFISH_ROUNDS + 2] = { + 0x243F6A88L, 0x85A308D3L, 0x13198A2EL, 0x03707344L, + 0xA4093822L, 0x299F31D0L, 0x082EFA98L, 0xEC4E6C89L, + 0x452821E6L, 0x38D01377L, 0xBE5466CFL, 0x34E90C6CL, + 0xC0AC29B7L, 0xC97C50DDL, 0x3F84D5B5L, 0xB5470917L, + 0x9216D5D9L, 0x8979FB1BL +}; + +/* declarations of data at the end of this file */ +static const uint32_t S[4][256]; + +static uint32_t F( blowfish_context *ctx, uint32_t x ) +{ + unsigned short a, b, c, d; + uint32_t y; + + d = (unsigned short)(x & 0xFF); + x >>= 8; + c = (unsigned short)(x & 0xFF); + x >>= 8; + b = (unsigned short)(x & 0xFF); + x >>= 8; + a = (unsigned short)(x & 0xFF); + y = ctx->S[0][a] + ctx->S[1][b]; + y = y ^ ctx->S[2][c]; + y = y + ctx->S[3][d]; + + return( y ); +} + +static void blowfish_enc( blowfish_context *ctx, uint32_t *xl, uint32_t *xr ) +{ + uint32_t Xl, Xr, temp; + short i; + + Xl = *xl; + Xr = *xr; + + for( i = 0; i < BLOWFISH_ROUNDS; ++i ) + { + Xl = Xl ^ ctx->P[i]; + Xr = F( ctx, Xl ) ^ Xr; + + temp = Xl; + Xl = Xr; + Xr = temp; + } + + temp = Xl; + Xl = Xr; + Xr = temp; + + Xr = Xr ^ ctx->P[BLOWFISH_ROUNDS]; + Xl = Xl ^ ctx->P[BLOWFISH_ROUNDS + 1]; + + *xl = Xl; + *xr = Xr; +} + +static void blowfish_dec( blowfish_context *ctx, uint32_t *xl, uint32_t *xr ) +{ + uint32_t Xl, Xr, temp; + short i; + + Xl = *xl; + Xr = *xr; + + for( i = BLOWFISH_ROUNDS + 1; i > 1; --i ) + { + Xl = Xl ^ ctx->P[i]; + Xr = F( ctx, Xl ) ^ Xr; + + temp = Xl; + Xl = Xr; + Xr = temp; + } + + temp = Xl; + Xl = Xr; + Xr = temp; + + Xr = Xr ^ ctx->P[1]; + Xl = Xl ^ ctx->P[0]; + + *xl = Xl; + *xr = Xr; +} + +void blowfish_init( blowfish_context *ctx ) +{ + memset( ctx, 0, sizeof( blowfish_context ) ); +} + +void blowfish_free( blowfish_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( blowfish_context ) ); +} + +/* + * Blowfish key schedule + */ +int blowfish_setkey( blowfish_context *ctx, const unsigned char *key, + unsigned int keysize ) +{ + unsigned int i, j, k; + uint32_t data, datal, datar; + + if( keysize < BLOWFISH_MIN_KEY || keysize > BLOWFISH_MAX_KEY || + ( keysize % 8 ) ) + { + return( POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH ); + } + + keysize >>= 3; + + for( i = 0; i < 4; i++ ) + { + for( j = 0; j < 256; j++ ) + ctx->S[i][j] = S[i][j]; + } + + j = 0; + for( i = 0; i < BLOWFISH_ROUNDS + 2; ++i ) + { + data = 0x00000000; + for( k = 0; k < 4; ++k ) + { + data = ( data << 8 ) | key[j++]; + if( j >= keysize ) + j = 0; + } + ctx->P[i] = P[i] ^ data; + } + + datal = 0x00000000; + datar = 0x00000000; + + for( i = 0; i < BLOWFISH_ROUNDS + 2; i += 2 ) + { + blowfish_enc( ctx, &datal, &datar ); + ctx->P[i] = datal; + ctx->P[i + 1] = datar; + } + + for( i = 0; i < 4; i++ ) + { + for( j = 0; j < 256; j += 2 ) + { + blowfish_enc( ctx, &datal, &datar ); + ctx->S[i][j] = datal; + ctx->S[i][j + 1] = datar; + } + } + return( 0 ); +} + +/* + * Blowfish-ECB block encryption/decryption + */ +int blowfish_crypt_ecb( blowfish_context *ctx, + int mode, + const unsigned char input[BLOWFISH_BLOCKSIZE], + unsigned char output[BLOWFISH_BLOCKSIZE] ) +{ + uint32_t X0, X1; + + GET_UINT32_BE( X0, input, 0 ); + GET_UINT32_BE( X1, input, 4 ); + + if( mode == BLOWFISH_DECRYPT ) + { + blowfish_dec( ctx, &X0, &X1 ); + } + else /* BLOWFISH_ENCRYPT */ + { + blowfish_enc( ctx, &X0, &X1 ); + } + + PUT_UINT32_BE( X0, output, 0 ); + PUT_UINT32_BE( X1, output, 4 ); + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * Blowfish-CBC buffer encryption/decryption + */ +int blowfish_crypt_cbc( blowfish_context *ctx, + int mode, + size_t length, + unsigned char iv[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[BLOWFISH_BLOCKSIZE]; + + if( length % BLOWFISH_BLOCKSIZE ) + return( POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH ); + + if( mode == BLOWFISH_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, BLOWFISH_BLOCKSIZE ); + blowfish_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < BLOWFISH_BLOCKSIZE;i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, BLOWFISH_BLOCKSIZE ); + + input += BLOWFISH_BLOCKSIZE; + output += BLOWFISH_BLOCKSIZE; + length -= BLOWFISH_BLOCKSIZE; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < BLOWFISH_BLOCKSIZE; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + blowfish_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, BLOWFISH_BLOCKSIZE ); + + input += BLOWFISH_BLOCKSIZE; + output += BLOWFISH_BLOCKSIZE; + length -= BLOWFISH_BLOCKSIZE; + } + } + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * Blowfish CFB buffer encryption/decryption + */ +int blowfish_crypt_cfb64( blowfish_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + if( mode == BLOWFISH_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = ( n + 1 ) % BLOWFISH_BLOCKSIZE; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = ( n + 1 ) % BLOWFISH_BLOCKSIZE; + } + } + + *iv_off = n; + + return( 0 ); +} +#endif /*POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * Blowfish CTR buffer encryption/decryption + */ +int blowfish_crypt_ctr( blowfish_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[BLOWFISH_BLOCKSIZE], + unsigned char stream_block[BLOWFISH_BLOCKSIZE], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + while( length-- ) + { + if( n == 0 ) { + blowfish_crypt_ecb( ctx, BLOWFISH_ENCRYPT, nonce_counter, + stream_block ); + + for( i = BLOWFISH_BLOCKSIZE; i > 0; i-- ) + if( ++nonce_counter[i - 1] != 0 ) + break; + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = ( n + 1 ) % BLOWFISH_BLOCKSIZE; + } + + *nc_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +static const uint32_t S[4][256] = { + { 0xD1310BA6L, 0x98DFB5ACL, 0x2FFD72DBL, 0xD01ADFB7L, + 0xB8E1AFEDL, 0x6A267E96L, 0xBA7C9045L, 0xF12C7F99L, + 0x24A19947L, 0xB3916CF7L, 0x0801F2E2L, 0x858EFC16L, + 0x636920D8L, 0x71574E69L, 0xA458FEA3L, 0xF4933D7EL, + 0x0D95748FL, 0x728EB658L, 0x718BCD58L, 0x82154AEEL, + 0x7B54A41DL, 0xC25A59B5L, 0x9C30D539L, 0x2AF26013L, + 0xC5D1B023L, 0x286085F0L, 0xCA417918L, 0xB8DB38EFL, + 0x8E79DCB0L, 0x603A180EL, 0x6C9E0E8BL, 0xB01E8A3EL, + 0xD71577C1L, 0xBD314B27L, 0x78AF2FDAL, 0x55605C60L, + 0xE65525F3L, 0xAA55AB94L, 0x57489862L, 0x63E81440L, + 0x55CA396AL, 0x2AAB10B6L, 0xB4CC5C34L, 0x1141E8CEL, + 0xA15486AFL, 0x7C72E993L, 0xB3EE1411L, 0x636FBC2AL, + 0x2BA9C55DL, 0x741831F6L, 0xCE5C3E16L, 0x9B87931EL, + 0xAFD6BA33L, 0x6C24CF5CL, 0x7A325381L, 0x28958677L, + 0x3B8F4898L, 0x6B4BB9AFL, 0xC4BFE81BL, 0x66282193L, + 0x61D809CCL, 0xFB21A991L, 0x487CAC60L, 0x5DEC8032L, + 0xEF845D5DL, 0xE98575B1L, 0xDC262302L, 0xEB651B88L, + 0x23893E81L, 0xD396ACC5L, 0x0F6D6FF3L, 0x83F44239L, + 0x2E0B4482L, 0xA4842004L, 0x69C8F04AL, 0x9E1F9B5EL, + 0x21C66842L, 0xF6E96C9AL, 0x670C9C61L, 0xABD388F0L, + 0x6A51A0D2L, 0xD8542F68L, 0x960FA728L, 0xAB5133A3L, + 0x6EEF0B6CL, 0x137A3BE4L, 0xBA3BF050L, 0x7EFB2A98L, + 0xA1F1651DL, 0x39AF0176L, 0x66CA593EL, 0x82430E88L, + 0x8CEE8619L, 0x456F9FB4L, 0x7D84A5C3L, 0x3B8B5EBEL, + 0xE06F75D8L, 0x85C12073L, 0x401A449FL, 0x56C16AA6L, + 0x4ED3AA62L, 0x363F7706L, 0x1BFEDF72L, 0x429B023DL, + 0x37D0D724L, 0xD00A1248L, 0xDB0FEAD3L, 0x49F1C09BL, + 0x075372C9L, 0x80991B7BL, 0x25D479D8L, 0xF6E8DEF7L, + 0xE3FE501AL, 0xB6794C3BL, 0x976CE0BDL, 0x04C006BAL, + 0xC1A94FB6L, 0x409F60C4L, 0x5E5C9EC2L, 0x196A2463L, + 0x68FB6FAFL, 0x3E6C53B5L, 0x1339B2EBL, 0x3B52EC6FL, + 0x6DFC511FL, 0x9B30952CL, 0xCC814544L, 0xAF5EBD09L, + 0xBEE3D004L, 0xDE334AFDL, 0x660F2807L, 0x192E4BB3L, + 0xC0CBA857L, 0x45C8740FL, 0xD20B5F39L, 0xB9D3FBDBL, + 0x5579C0BDL, 0x1A60320AL, 0xD6A100C6L, 0x402C7279L, + 0x679F25FEL, 0xFB1FA3CCL, 0x8EA5E9F8L, 0xDB3222F8L, + 0x3C7516DFL, 0xFD616B15L, 0x2F501EC8L, 0xAD0552ABL, + 0x323DB5FAL, 0xFD238760L, 0x53317B48L, 0x3E00DF82L, + 0x9E5C57BBL, 0xCA6F8CA0L, 0x1A87562EL, 0xDF1769DBL, + 0xD542A8F6L, 0x287EFFC3L, 0xAC6732C6L, 0x8C4F5573L, + 0x695B27B0L, 0xBBCA58C8L, 0xE1FFA35DL, 0xB8F011A0L, + 0x10FA3D98L, 0xFD2183B8L, 0x4AFCB56CL, 0x2DD1D35BL, + 0x9A53E479L, 0xB6F84565L, 0xD28E49BCL, 0x4BFB9790L, + 0xE1DDF2DAL, 0xA4CB7E33L, 0x62FB1341L, 0xCEE4C6E8L, + 0xEF20CADAL, 0x36774C01L, 0xD07E9EFEL, 0x2BF11FB4L, + 0x95DBDA4DL, 0xAE909198L, 0xEAAD8E71L, 0x6B93D5A0L, + 0xD08ED1D0L, 0xAFC725E0L, 0x8E3C5B2FL, 0x8E7594B7L, + 0x8FF6E2FBL, 0xF2122B64L, 0x8888B812L, 0x900DF01CL, + 0x4FAD5EA0L, 0x688FC31CL, 0xD1CFF191L, 0xB3A8C1ADL, + 0x2F2F2218L, 0xBE0E1777L, 0xEA752DFEL, 0x8B021FA1L, + 0xE5A0CC0FL, 0xB56F74E8L, 0x18ACF3D6L, 0xCE89E299L, + 0xB4A84FE0L, 0xFD13E0B7L, 0x7CC43B81L, 0xD2ADA8D9L, + 0x165FA266L, 0x80957705L, 0x93CC7314L, 0x211A1477L, + 0xE6AD2065L, 0x77B5FA86L, 0xC75442F5L, 0xFB9D35CFL, + 0xEBCDAF0CL, 0x7B3E89A0L, 0xD6411BD3L, 0xAE1E7E49L, + 0x00250E2DL, 0x2071B35EL, 0x226800BBL, 0x57B8E0AFL, + 0x2464369BL, 0xF009B91EL, 0x5563911DL, 0x59DFA6AAL, + 0x78C14389L, 0xD95A537FL, 0x207D5BA2L, 0x02E5B9C5L, + 0x83260376L, 0x6295CFA9L, 0x11C81968L, 0x4E734A41L, + 0xB3472DCAL, 0x7B14A94AL, 0x1B510052L, 0x9A532915L, + 0xD60F573FL, 0xBC9BC6E4L, 0x2B60A476L, 0x81E67400L, + 0x08BA6FB5L, 0x571BE91FL, 0xF296EC6BL, 0x2A0DD915L, + 0xB6636521L, 0xE7B9F9B6L, 0xFF34052EL, 0xC5855664L, + 0x53B02D5DL, 0xA99F8FA1L, 0x08BA4799L, 0x6E85076AL }, + { 0x4B7A70E9L, 0xB5B32944L, 0xDB75092EL, 0xC4192623L, + 0xAD6EA6B0L, 0x49A7DF7DL, 0x9CEE60B8L, 0x8FEDB266L, + 0xECAA8C71L, 0x699A17FFL, 0x5664526CL, 0xC2B19EE1L, + 0x193602A5L, 0x75094C29L, 0xA0591340L, 0xE4183A3EL, + 0x3F54989AL, 0x5B429D65L, 0x6B8FE4D6L, 0x99F73FD6L, + 0xA1D29C07L, 0xEFE830F5L, 0x4D2D38E6L, 0xF0255DC1L, + 0x4CDD2086L, 0x8470EB26L, 0x6382E9C6L, 0x021ECC5EL, + 0x09686B3FL, 0x3EBAEFC9L, 0x3C971814L, 0x6B6A70A1L, + 0x687F3584L, 0x52A0E286L, 0xB79C5305L, 0xAA500737L, + 0x3E07841CL, 0x7FDEAE5CL, 0x8E7D44ECL, 0x5716F2B8L, + 0xB03ADA37L, 0xF0500C0DL, 0xF01C1F04L, 0x0200B3FFL, + 0xAE0CF51AL, 0x3CB574B2L, 0x25837A58L, 0xDC0921BDL, + 0xD19113F9L, 0x7CA92FF6L, 0x94324773L, 0x22F54701L, + 0x3AE5E581L, 0x37C2DADCL, 0xC8B57634L, 0x9AF3DDA7L, + 0xA9446146L, 0x0FD0030EL, 0xECC8C73EL, 0xA4751E41L, + 0xE238CD99L, 0x3BEA0E2FL, 0x3280BBA1L, 0x183EB331L, + 0x4E548B38L, 0x4F6DB908L, 0x6F420D03L, 0xF60A04BFL, + 0x2CB81290L, 0x24977C79L, 0x5679B072L, 0xBCAF89AFL, + 0xDE9A771FL, 0xD9930810L, 0xB38BAE12L, 0xDCCF3F2EL, + 0x5512721FL, 0x2E6B7124L, 0x501ADDE6L, 0x9F84CD87L, + 0x7A584718L, 0x7408DA17L, 0xBC9F9ABCL, 0xE94B7D8CL, + 0xEC7AEC3AL, 0xDB851DFAL, 0x63094366L, 0xC464C3D2L, + 0xEF1C1847L, 0x3215D908L, 0xDD433B37L, 0x24C2BA16L, + 0x12A14D43L, 0x2A65C451L, 0x50940002L, 0x133AE4DDL, + 0x71DFF89EL, 0x10314E55L, 0x81AC77D6L, 0x5F11199BL, + 0x043556F1L, 0xD7A3C76BL, 0x3C11183BL, 0x5924A509L, + 0xF28FE6EDL, 0x97F1FBFAL, 0x9EBABF2CL, 0x1E153C6EL, + 0x86E34570L, 0xEAE96FB1L, 0x860E5E0AL, 0x5A3E2AB3L, + 0x771FE71CL, 0x4E3D06FAL, 0x2965DCB9L, 0x99E71D0FL, + 0x803E89D6L, 0x5266C825L, 0x2E4CC978L, 0x9C10B36AL, + 0xC6150EBAL, 0x94E2EA78L, 0xA5FC3C53L, 0x1E0A2DF4L, + 0xF2F74EA7L, 0x361D2B3DL, 0x1939260FL, 0x19C27960L, + 0x5223A708L, 0xF71312B6L, 0xEBADFE6EL, 0xEAC31F66L, + 0xE3BC4595L, 0xA67BC883L, 0xB17F37D1L, 0x018CFF28L, + 0xC332DDEFL, 0xBE6C5AA5L, 0x65582185L, 0x68AB9802L, + 0xEECEA50FL, 0xDB2F953BL, 0x2AEF7DADL, 0x5B6E2F84L, + 0x1521B628L, 0x29076170L, 0xECDD4775L, 0x619F1510L, + 0x13CCA830L, 0xEB61BD96L, 0x0334FE1EL, 0xAA0363CFL, + 0xB5735C90L, 0x4C70A239L, 0xD59E9E0BL, 0xCBAADE14L, + 0xEECC86BCL, 0x60622CA7L, 0x9CAB5CABL, 0xB2F3846EL, + 0x648B1EAFL, 0x19BDF0CAL, 0xA02369B9L, 0x655ABB50L, + 0x40685A32L, 0x3C2AB4B3L, 0x319EE9D5L, 0xC021B8F7L, + 0x9B540B19L, 0x875FA099L, 0x95F7997EL, 0x623D7DA8L, + 0xF837889AL, 0x97E32D77L, 0x11ED935FL, 0x16681281L, + 0x0E358829L, 0xC7E61FD6L, 0x96DEDFA1L, 0x7858BA99L, + 0x57F584A5L, 0x1B227263L, 0x9B83C3FFL, 0x1AC24696L, + 0xCDB30AEBL, 0x532E3054L, 0x8FD948E4L, 0x6DBC3128L, + 0x58EBF2EFL, 0x34C6FFEAL, 0xFE28ED61L, 0xEE7C3C73L, + 0x5D4A14D9L, 0xE864B7E3L, 0x42105D14L, 0x203E13E0L, + 0x45EEE2B6L, 0xA3AAABEAL, 0xDB6C4F15L, 0xFACB4FD0L, + 0xC742F442L, 0xEF6ABBB5L, 0x654F3B1DL, 0x41CD2105L, + 0xD81E799EL, 0x86854DC7L, 0xE44B476AL, 0x3D816250L, + 0xCF62A1F2L, 0x5B8D2646L, 0xFC8883A0L, 0xC1C7B6A3L, + 0x7F1524C3L, 0x69CB7492L, 0x47848A0BL, 0x5692B285L, + 0x095BBF00L, 0xAD19489DL, 0x1462B174L, 0x23820E00L, + 0x58428D2AL, 0x0C55F5EAL, 0x1DADF43EL, 0x233F7061L, + 0x3372F092L, 0x8D937E41L, 0xD65FECF1L, 0x6C223BDBL, + 0x7CDE3759L, 0xCBEE7460L, 0x4085F2A7L, 0xCE77326EL, + 0xA6078084L, 0x19F8509EL, 0xE8EFD855L, 0x61D99735L, + 0xA969A7AAL, 0xC50C06C2L, 0x5A04ABFCL, 0x800BCADCL, + 0x9E447A2EL, 0xC3453484L, 0xFDD56705L, 0x0E1E9EC9L, + 0xDB73DBD3L, 0x105588CDL, 0x675FDA79L, 0xE3674340L, + 0xC5C43465L, 0x713E38D8L, 0x3D28F89EL, 0xF16DFF20L, + 0x153E21E7L, 0x8FB03D4AL, 0xE6E39F2BL, 0xDB83ADF7L }, + { 0xE93D5A68L, 0x948140F7L, 0xF64C261CL, 0x94692934L, + 0x411520F7L, 0x7602D4F7L, 0xBCF46B2EL, 0xD4A20068L, + 0xD4082471L, 0x3320F46AL, 0x43B7D4B7L, 0x500061AFL, + 0x1E39F62EL, 0x97244546L, 0x14214F74L, 0xBF8B8840L, + 0x4D95FC1DL, 0x96B591AFL, 0x70F4DDD3L, 0x66A02F45L, + 0xBFBC09ECL, 0x03BD9785L, 0x7FAC6DD0L, 0x31CB8504L, + 0x96EB27B3L, 0x55FD3941L, 0xDA2547E6L, 0xABCA0A9AL, + 0x28507825L, 0x530429F4L, 0x0A2C86DAL, 0xE9B66DFBL, + 0x68DC1462L, 0xD7486900L, 0x680EC0A4L, 0x27A18DEEL, + 0x4F3FFEA2L, 0xE887AD8CL, 0xB58CE006L, 0x7AF4D6B6L, + 0xAACE1E7CL, 0xD3375FECL, 0xCE78A399L, 0x406B2A42L, + 0x20FE9E35L, 0xD9F385B9L, 0xEE39D7ABL, 0x3B124E8BL, + 0x1DC9FAF7L, 0x4B6D1856L, 0x26A36631L, 0xEAE397B2L, + 0x3A6EFA74L, 0xDD5B4332L, 0x6841E7F7L, 0xCA7820FBL, + 0xFB0AF54EL, 0xD8FEB397L, 0x454056ACL, 0xBA489527L, + 0x55533A3AL, 0x20838D87L, 0xFE6BA9B7L, 0xD096954BL, + 0x55A867BCL, 0xA1159A58L, 0xCCA92963L, 0x99E1DB33L, + 0xA62A4A56L, 0x3F3125F9L, 0x5EF47E1CL, 0x9029317CL, + 0xFDF8E802L, 0x04272F70L, 0x80BB155CL, 0x05282CE3L, + 0x95C11548L, 0xE4C66D22L, 0x48C1133FL, 0xC70F86DCL, + 0x07F9C9EEL, 0x41041F0FL, 0x404779A4L, 0x5D886E17L, + 0x325F51EBL, 0xD59BC0D1L, 0xF2BCC18FL, 0x41113564L, + 0x257B7834L, 0x602A9C60L, 0xDFF8E8A3L, 0x1F636C1BL, + 0x0E12B4C2L, 0x02E1329EL, 0xAF664FD1L, 0xCAD18115L, + 0x6B2395E0L, 0x333E92E1L, 0x3B240B62L, 0xEEBEB922L, + 0x85B2A20EL, 0xE6BA0D99L, 0xDE720C8CL, 0x2DA2F728L, + 0xD0127845L, 0x95B794FDL, 0x647D0862L, 0xE7CCF5F0L, + 0x5449A36FL, 0x877D48FAL, 0xC39DFD27L, 0xF33E8D1EL, + 0x0A476341L, 0x992EFF74L, 0x3A6F6EABL, 0xF4F8FD37L, + 0xA812DC60L, 0xA1EBDDF8L, 0x991BE14CL, 0xDB6E6B0DL, + 0xC67B5510L, 0x6D672C37L, 0x2765D43BL, 0xDCD0E804L, + 0xF1290DC7L, 0xCC00FFA3L, 0xB5390F92L, 0x690FED0BL, + 0x667B9FFBL, 0xCEDB7D9CL, 0xA091CF0BL, 0xD9155EA3L, + 0xBB132F88L, 0x515BAD24L, 0x7B9479BFL, 0x763BD6EBL, + 0x37392EB3L, 0xCC115979L, 0x8026E297L, 0xF42E312DL, + 0x6842ADA7L, 0xC66A2B3BL, 0x12754CCCL, 0x782EF11CL, + 0x6A124237L, 0xB79251E7L, 0x06A1BBE6L, 0x4BFB6350L, + 0x1A6B1018L, 0x11CAEDFAL, 0x3D25BDD8L, 0xE2E1C3C9L, + 0x44421659L, 0x0A121386L, 0xD90CEC6EL, 0xD5ABEA2AL, + 0x64AF674EL, 0xDA86A85FL, 0xBEBFE988L, 0x64E4C3FEL, + 0x9DBC8057L, 0xF0F7C086L, 0x60787BF8L, 0x6003604DL, + 0xD1FD8346L, 0xF6381FB0L, 0x7745AE04L, 0xD736FCCCL, + 0x83426B33L, 0xF01EAB71L, 0xB0804187L, 0x3C005E5FL, + 0x77A057BEL, 0xBDE8AE24L, 0x55464299L, 0xBF582E61L, + 0x4E58F48FL, 0xF2DDFDA2L, 0xF474EF38L, 0x8789BDC2L, + 0x5366F9C3L, 0xC8B38E74L, 0xB475F255L, 0x46FCD9B9L, + 0x7AEB2661L, 0x8B1DDF84L, 0x846A0E79L, 0x915F95E2L, + 0x466E598EL, 0x20B45770L, 0x8CD55591L, 0xC902DE4CL, + 0xB90BACE1L, 0xBB8205D0L, 0x11A86248L, 0x7574A99EL, + 0xB77F19B6L, 0xE0A9DC09L, 0x662D09A1L, 0xC4324633L, + 0xE85A1F02L, 0x09F0BE8CL, 0x4A99A025L, 0x1D6EFE10L, + 0x1AB93D1DL, 0x0BA5A4DFL, 0xA186F20FL, 0x2868F169L, + 0xDCB7DA83L, 0x573906FEL, 0xA1E2CE9BL, 0x4FCD7F52L, + 0x50115E01L, 0xA70683FAL, 0xA002B5C4L, 0x0DE6D027L, + 0x9AF88C27L, 0x773F8641L, 0xC3604C06L, 0x61A806B5L, + 0xF0177A28L, 0xC0F586E0L, 0x006058AAL, 0x30DC7D62L, + 0x11E69ED7L, 0x2338EA63L, 0x53C2DD94L, 0xC2C21634L, + 0xBBCBEE56L, 0x90BCB6DEL, 0xEBFC7DA1L, 0xCE591D76L, + 0x6F05E409L, 0x4B7C0188L, 0x39720A3DL, 0x7C927C24L, + 0x86E3725FL, 0x724D9DB9L, 0x1AC15BB4L, 0xD39EB8FCL, + 0xED545578L, 0x08FCA5B5L, 0xD83D7CD3L, 0x4DAD0FC4L, + 0x1E50EF5EL, 0xB161E6F8L, 0xA28514D9L, 0x6C51133CL, + 0x6FD5C7E7L, 0x56E14EC4L, 0x362ABFCEL, 0xDDC6C837L, + 0xD79A3234L, 0x92638212L, 0x670EFA8EL, 0x406000E0L }, + { 0x3A39CE37L, 0xD3FAF5CFL, 0xABC27737L, 0x5AC52D1BL, + 0x5CB0679EL, 0x4FA33742L, 0xD3822740L, 0x99BC9BBEL, + 0xD5118E9DL, 0xBF0F7315L, 0xD62D1C7EL, 0xC700C47BL, + 0xB78C1B6BL, 0x21A19045L, 0xB26EB1BEL, 0x6A366EB4L, + 0x5748AB2FL, 0xBC946E79L, 0xC6A376D2L, 0x6549C2C8L, + 0x530FF8EEL, 0x468DDE7DL, 0xD5730A1DL, 0x4CD04DC6L, + 0x2939BBDBL, 0xA9BA4650L, 0xAC9526E8L, 0xBE5EE304L, + 0xA1FAD5F0L, 0x6A2D519AL, 0x63EF8CE2L, 0x9A86EE22L, + 0xC089C2B8L, 0x43242EF6L, 0xA51E03AAL, 0x9CF2D0A4L, + 0x83C061BAL, 0x9BE96A4DL, 0x8FE51550L, 0xBA645BD6L, + 0x2826A2F9L, 0xA73A3AE1L, 0x4BA99586L, 0xEF5562E9L, + 0xC72FEFD3L, 0xF752F7DAL, 0x3F046F69L, 0x77FA0A59L, + 0x80E4A915L, 0x87B08601L, 0x9B09E6ADL, 0x3B3EE593L, + 0xE990FD5AL, 0x9E34D797L, 0x2CF0B7D9L, 0x022B8B51L, + 0x96D5AC3AL, 0x017DA67DL, 0xD1CF3ED6L, 0x7C7D2D28L, + 0x1F9F25CFL, 0xADF2B89BL, 0x5AD6B472L, 0x5A88F54CL, + 0xE029AC71L, 0xE019A5E6L, 0x47B0ACFDL, 0xED93FA9BL, + 0xE8D3C48DL, 0x283B57CCL, 0xF8D56629L, 0x79132E28L, + 0x785F0191L, 0xED756055L, 0xF7960E44L, 0xE3D35E8CL, + 0x15056DD4L, 0x88F46DBAL, 0x03A16125L, 0x0564F0BDL, + 0xC3EB9E15L, 0x3C9057A2L, 0x97271AECL, 0xA93A072AL, + 0x1B3F6D9BL, 0x1E6321F5L, 0xF59C66FBL, 0x26DCF319L, + 0x7533D928L, 0xB155FDF5L, 0x03563482L, 0x8ABA3CBBL, + 0x28517711L, 0xC20AD9F8L, 0xABCC5167L, 0xCCAD925FL, + 0x4DE81751L, 0x3830DC8EL, 0x379D5862L, 0x9320F991L, + 0xEA7A90C2L, 0xFB3E7BCEL, 0x5121CE64L, 0x774FBE32L, + 0xA8B6E37EL, 0xC3293D46L, 0x48DE5369L, 0x6413E680L, + 0xA2AE0810L, 0xDD6DB224L, 0x69852DFDL, 0x09072166L, + 0xB39A460AL, 0x6445C0DDL, 0x586CDECFL, 0x1C20C8AEL, + 0x5BBEF7DDL, 0x1B588D40L, 0xCCD2017FL, 0x6BB4E3BBL, + 0xDDA26A7EL, 0x3A59FF45L, 0x3E350A44L, 0xBCB4CDD5L, + 0x72EACEA8L, 0xFA6484BBL, 0x8D6612AEL, 0xBF3C6F47L, + 0xD29BE463L, 0x542F5D9EL, 0xAEC2771BL, 0xF64E6370L, + 0x740E0D8DL, 0xE75B1357L, 0xF8721671L, 0xAF537D5DL, + 0x4040CB08L, 0x4EB4E2CCL, 0x34D2466AL, 0x0115AF84L, + 0xE1B00428L, 0x95983A1DL, 0x06B89FB4L, 0xCE6EA048L, + 0x6F3F3B82L, 0x3520AB82L, 0x011A1D4BL, 0x277227F8L, + 0x611560B1L, 0xE7933FDCL, 0xBB3A792BL, 0x344525BDL, + 0xA08839E1L, 0x51CE794BL, 0x2F32C9B7L, 0xA01FBAC9L, + 0xE01CC87EL, 0xBCC7D1F6L, 0xCF0111C3L, 0xA1E8AAC7L, + 0x1A908749L, 0xD44FBD9AL, 0xD0DADECBL, 0xD50ADA38L, + 0x0339C32AL, 0xC6913667L, 0x8DF9317CL, 0xE0B12B4FL, + 0xF79E59B7L, 0x43F5BB3AL, 0xF2D519FFL, 0x27D9459CL, + 0xBF97222CL, 0x15E6FC2AL, 0x0F91FC71L, 0x9B941525L, + 0xFAE59361L, 0xCEB69CEBL, 0xC2A86459L, 0x12BAA8D1L, + 0xB6C1075EL, 0xE3056A0CL, 0x10D25065L, 0xCB03A442L, + 0xE0EC6E0EL, 0x1698DB3BL, 0x4C98A0BEL, 0x3278E964L, + 0x9F1F9532L, 0xE0D392DFL, 0xD3A0342BL, 0x8971F21EL, + 0x1B0A7441L, 0x4BA3348CL, 0xC5BE7120L, 0xC37632D8L, + 0xDF359F8DL, 0x9B992F2EL, 0xE60B6F47L, 0x0FE3F11DL, + 0xE54CDA54L, 0x1EDAD891L, 0xCE6279CFL, 0xCD3E7E6FL, + 0x1618B166L, 0xFD2C1D05L, 0x848FD2C5L, 0xF6FB2299L, + 0xF523F357L, 0xA6327623L, 0x93A83531L, 0x56CCCD02L, + 0xACF08162L, 0x5A75EBB5L, 0x6E163697L, 0x88D273CCL, + 0xDE966292L, 0x81B949D0L, 0x4C50901BL, 0x71C65614L, + 0xE6C6C7BDL, 0x327A140AL, 0x45E1D006L, 0xC3F27B9AL, + 0xC9AA53FDL, 0x62A80F00L, 0xBB25BFE2L, 0x35BDD2F6L, + 0x71126905L, 0xB2040222L, 0xB6CBCF7CL, 0xCD769C2BL, + 0x53113EC0L, 0x1640E3D3L, 0x38ABBD60L, 0x2547ADF0L, + 0xBA38209CL, 0xF746CE76L, 0x77AFA1C5L, 0x20756060L, + 0x85CBFE4EL, 0x8AE88DD8L, 0x7AAAF9B0L, 0x4CF9AA7EL, + 0x1948C25CL, 0x02FB8A8CL, 0x01C36AE4L, 0xD6EBE1F9L, + 0x90D4F869L, 0xA65CDEA0L, 0x3F09252DL, 0xC208E69FL, + 0xB74E6132L, 0xCE77E25BL, 0x578FDFE3L, 0x3AC372E6L } +}; + +#endif /* !POLARSSL_BLOWFISH_ALT */ +#endif /* POLARSSL_BLOWFISH_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/camellia.c b/component/common/network/ssl/polarssl-1.3.8/library/camellia.c new file mode 100644 index 0000000..a4968f4 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/camellia.c @@ -0,0 +1,1073 @@ +/* + * Camellia implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The Camellia block cipher was designed by NTT and Mitsubishi Electric + * Corporation. + * + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/01espec.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CAMELLIA_C) + +#include "polarssl/camellia.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +#if !defined(POLARSSL_CAMELLIA_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +static const unsigned char SIGMA_CHARS[6][8] = +{ + { 0xa0, 0x9e, 0x66, 0x7f, 0x3b, 0xcc, 0x90, 0x8b }, + { 0xb6, 0x7a, 0xe8, 0x58, 0x4c, 0xaa, 0x73, 0xb2 }, + { 0xc6, 0xef, 0x37, 0x2f, 0xe9, 0x4f, 0x82, 0xbe }, + { 0x54, 0xff, 0x53, 0xa5, 0xf1, 0xd3, 0x6f, 0x1c }, + { 0x10, 0xe5, 0x27, 0xfa, 0xde, 0x68, 0x2d, 0x1d }, + { 0xb0, 0x56, 0x88, 0xc2, 0xb3, 0xe6, 0xc1, 0xfd } +}; + +#if defined(POLARSSL_CAMELLIA_SMALL_MEMORY) + +static const unsigned char FSb[256] = +{ + 112,130, 44,236,179, 39,192,229,228,133, 87, 53,234, 12,174, 65, + 35,239,107,147, 69, 25,165, 33,237, 14, 79, 78, 29,101,146,189, + 134,184,175,143,124,235, 31,206, 62, 48,220, 95, 94,197, 11, 26, + 166,225, 57,202,213, 71, 93, 61,217, 1, 90,214, 81, 86,108, 77, + 139, 13,154,102,251,204,176, 45,116, 18, 43, 32,240,177,132,153, + 223, 76,203,194, 52,126,118, 5,109,183,169, 49,209, 23, 4,215, + 20, 88, 58, 97,222, 27, 17, 28, 50, 15,156, 22, 83, 24,242, 34, + 254, 68,207,178,195,181,122,145, 36, 8,232,168, 96,252,105, 80, + 170,208,160,125,161,137, 98,151, 84, 91, 30,149,224,255,100,210, + 16,196, 0, 72,163,247,117,219,138, 3,230,218, 9, 63,221,148, + 135, 92,131, 2,205, 74,144, 51,115,103,246,243,157,127,191,226, + 82,155,216, 38,200, 55,198, 59,129,150,111, 75, 19,190, 99, 46, + 233,121,167,140,159,110,188,142, 41,245,249,182, 47,253,180, 89, + 120,152, 6,106,231, 70,113,186,212, 37,171, 66,136,162,141,250, + 114, 7,185, 85,248,238,172, 10, 54, 73, 42,104, 60, 56,241,164, + 64, 40,211,123,187,201, 67,193, 21,227,173,244,119,199,128,158 +}; + +#define SBOX1(n) FSb[(n)] +#define SBOX2(n) (unsigned char)((FSb[(n)] >> 7 ^ FSb[(n)] << 1) & 0xff) +#define SBOX3(n) (unsigned char)((FSb[(n)] >> 1 ^ FSb[(n)] << 7) & 0xff) +#define SBOX4(n) FSb[((n) << 1 ^ (n) >> 7) &0xff] + +#else /* POLARSSL_CAMELLIA_SMALL_MEMORY */ + +static const unsigned char FSb[256] = +{ + 112, 130, 44, 236, 179, 39, 192, 229, 228, 133, 87, 53, 234, 12, 174, 65, + 35, 239, 107, 147, 69, 25, 165, 33, 237, 14, 79, 78, 29, 101, 146, 189, + 134, 184, 175, 143, 124, 235, 31, 206, 62, 48, 220, 95, 94, 197, 11, 26, + 166, 225, 57, 202, 213, 71, 93, 61, 217, 1, 90, 214, 81, 86, 108, 77, + 139, 13, 154, 102, 251, 204, 176, 45, 116, 18, 43, 32, 240, 177, 132, 153, + 223, 76, 203, 194, 52, 126, 118, 5, 109, 183, 169, 49, 209, 23, 4, 215, + 20, 88, 58, 97, 222, 27, 17, 28, 50, 15, 156, 22, 83, 24, 242, 34, + 254, 68, 207, 178, 195, 181, 122, 145, 36, 8, 232, 168, 96, 252, 105, 80, + 170, 208, 160, 125, 161, 137, 98, 151, 84, 91, 30, 149, 224, 255, 100, 210, + 16, 196, 0, 72, 163, 247, 117, 219, 138, 3, 230, 218, 9, 63, 221, 148, + 135, 92, 131, 2, 205, 74, 144, 51, 115, 103, 246, 243, 157, 127, 191, 226, + 82, 155, 216, 38, 200, 55, 198, 59, 129, 150, 111, 75, 19, 190, 99, 46, + 233, 121, 167, 140, 159, 110, 188, 142, 41, 245, 249, 182, 47, 253, 180, 89, + 120, 152, 6, 106, 231, 70, 113, 186, 212, 37, 171, 66, 136, 162, 141, 250, + 114, 7, 185, 85, 248, 238, 172, 10, 54, 73, 42, 104, 60, 56, 241, 164, + 64, 40, 211, 123, 187, 201, 67, 193, 21, 227, 173, 244, 119, 199, 128, 158 +}; + +static const unsigned char FSb2[256] = +{ + 224, 5, 88, 217, 103, 78, 129, 203, 201, 11, 174, 106, 213, 24, 93, 130, + 70, 223, 214, 39, 138, 50, 75, 66, 219, 28, 158, 156, 58, 202, 37, 123, + 13, 113, 95, 31, 248, 215, 62, 157, 124, 96, 185, 190, 188, 139, 22, 52, + 77, 195, 114, 149, 171, 142, 186, 122, 179, 2, 180, 173, 162, 172, 216, 154, + 23, 26, 53, 204, 247, 153, 97, 90, 232, 36, 86, 64, 225, 99, 9, 51, + 191, 152, 151, 133, 104, 252, 236, 10, 218, 111, 83, 98, 163, 46, 8, 175, + 40, 176, 116, 194, 189, 54, 34, 56, 100, 30, 57, 44, 166, 48, 229, 68, + 253, 136, 159, 101, 135, 107, 244, 35, 72, 16, 209, 81, 192, 249, 210, 160, + 85, 161, 65, 250, 67, 19, 196, 47, 168, 182, 60, 43, 193, 255, 200, 165, + 32, 137, 0, 144, 71, 239, 234, 183, 21, 6, 205, 181, 18, 126, 187, 41, + 15, 184, 7, 4, 155, 148, 33, 102, 230, 206, 237, 231, 59, 254, 127, 197, + 164, 55, 177, 76, 145, 110, 141, 118, 3, 45, 222, 150, 38, 125, 198, 92, + 211, 242, 79, 25, 63, 220, 121, 29, 82, 235, 243, 109, 94, 251, 105, 178, + 240, 49, 12, 212, 207, 140, 226, 117, 169, 74, 87, 132, 17, 69, 27, 245, + 228, 14, 115, 170, 241, 221, 89, 20, 108, 146, 84, 208, 120, 112, 227, 73, + 128, 80, 167, 246, 119, 147, 134, 131, 42, 199, 91, 233, 238, 143, 1, 61 +}; + +static const unsigned char FSb3[256] = +{ + 56, 65, 22, 118, 217, 147, 96, 242, 114, 194, 171, 154, 117, 6, 87, 160, + 145, 247, 181, 201, 162, 140, 210, 144, 246, 7, 167, 39, 142, 178, 73, 222, + 67, 92, 215, 199, 62, 245, 143, 103, 31, 24, 110, 175, 47, 226, 133, 13, + 83, 240, 156, 101, 234, 163, 174, 158, 236, 128, 45, 107, 168, 43, 54, 166, + 197, 134, 77, 51, 253, 102, 88, 150, 58, 9, 149, 16, 120, 216, 66, 204, + 239, 38, 229, 97, 26, 63, 59, 130, 182, 219, 212, 152, 232, 139, 2, 235, + 10, 44, 29, 176, 111, 141, 136, 14, 25, 135, 78, 11, 169, 12, 121, 17, + 127, 34, 231, 89, 225, 218, 61, 200, 18, 4, 116, 84, 48, 126, 180, 40, + 85, 104, 80, 190, 208, 196, 49, 203, 42, 173, 15, 202, 112, 255, 50, 105, + 8, 98, 0, 36, 209, 251, 186, 237, 69, 129, 115, 109, 132, 159, 238, 74, + 195, 46, 193, 1, 230, 37, 72, 153, 185, 179, 123, 249, 206, 191, 223, 113, + 41, 205, 108, 19, 100, 155, 99, 157, 192, 75, 183, 165, 137, 95, 177, 23, + 244, 188, 211, 70, 207, 55, 94, 71, 148, 250, 252, 91, 151, 254, 90, 172, + 60, 76, 3, 53, 243, 35, 184, 93, 106, 146, 213, 33, 68, 81, 198, 125, + 57, 131, 220, 170, 124, 119, 86, 5, 27, 164, 21, 52, 30, 28, 248, 82, + 32, 20, 233, 189, 221, 228, 161, 224, 138, 241, 214, 122, 187, 227, 64, 79 +}; + +static const unsigned char FSb4[256] = +{ + 112, 44, 179, 192, 228, 87, 234, 174, 35, 107, 69, 165, 237, 79, 29, 146, + 134, 175, 124, 31, 62, 220, 94, 11, 166, 57, 213, 93, 217, 90, 81, 108, + 139, 154, 251, 176, 116, 43, 240, 132, 223, 203, 52, 118, 109, 169, 209, 4, + 20, 58, 222, 17, 50, 156, 83, 242, 254, 207, 195, 122, 36, 232, 96, 105, + 170, 160, 161, 98, 84, 30, 224, 100, 16, 0, 163, 117, 138, 230, 9, 221, + 135, 131, 205, 144, 115, 246, 157, 191, 82, 216, 200, 198, 129, 111, 19, 99, + 233, 167, 159, 188, 41, 249, 47, 180, 120, 6, 231, 113, 212, 171, 136, 141, + 114, 185, 248, 172, 54, 42, 60, 241, 64, 211, 187, 67, 21, 173, 119, 128, + 130, 236, 39, 229, 133, 53, 12, 65, 239, 147, 25, 33, 14, 78, 101, 189, + 184, 143, 235, 206, 48, 95, 197, 26, 225, 202, 71, 61, 1, 214, 86, 77, + 13, 102, 204, 45, 18, 32, 177, 153, 76, 194, 126, 5, 183, 49, 23, 215, + 88, 97, 27, 28, 15, 22, 24, 34, 68, 178, 181, 145, 8, 168, 252, 80, + 208, 125, 137, 151, 91, 149, 255, 210, 196, 72, 247, 219, 3, 218, 63, 148, + 92, 2, 74, 51, 103, 243, 127, 226, 155, 38, 55, 59, 150, 75, 190, 46, + 121, 140, 110, 142, 245, 182, 253, 89, 152, 106, 70, 186, 37, 66, 162, 250, + 7, 85, 238, 10, 73, 104, 56, 164, 40, 123, 201, 193, 227, 244, 199, 158 +}; + +#define SBOX1(n) FSb[(n)] +#define SBOX2(n) FSb2[(n)] +#define SBOX3(n) FSb3[(n)] +#define SBOX4(n) FSb4[(n)] + +#endif /* POLARSSL_CAMELLIA_SMALL_MEMORY */ + +static const unsigned char shifts[2][4][4] = +{ + { + { 1, 1, 1, 1 }, /* KL */ + { 0, 0, 0, 0 }, /* KR */ + { 1, 1, 1, 1 }, /* KA */ + { 0, 0, 0, 0 } /* KB */ + }, + { + { 1, 0, 1, 1 }, /* KL */ + { 1, 1, 0, 1 }, /* KR */ + { 1, 1, 1, 0 }, /* KA */ + { 1, 1, 0, 1 } /* KB */ + } +}; + +static const signed char indexes[2][4][20] = +{ + { + { 0, 1, 2, 3, 8, 9, 10, 11, 38, 39, + 36, 37, 23, 20, 21, 22, 27, -1, -1, 26 }, /* KL -> RK */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /* KR -> RK */ + { 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, + 18, 19, -1, 24, 25, -1, 31, 28, 29, 30 }, /* KA -> RK */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } /* KB -> RK */ + }, + { + { 0, 1, 2, 3, 61, 62, 63, 60, -1, -1, + -1, -1, 27, 24, 25, 26, 35, 32, 33, 34 }, /* KL -> RK */ + { -1, -1, -1, -1, 8, 9, 10, 11, 16, 17, + 18, 19, -1, -1, -1, -1, 39, 36, 37, 38 }, /* KR -> RK */ + { -1, -1, -1, -1, 12, 13, 14, 15, 58, 59, + 56, 57, 31, 28, 29, 30, -1, -1, -1, -1 }, /* KA -> RK */ + { 4, 5, 6, 7, 65, 66, 67, 64, 20, 21, + 22, 23, -1, -1, -1, -1, 43, 40, 41, 42 } /* KB -> RK */ + } +}; + +static const signed char transposes[2][20] = +{ + { + 21, 22, 23, 20, + -1, -1, -1, -1, + 18, 19, 16, 17, + 11, 8, 9, 10, + 15, 12, 13, 14 + }, + { + 25, 26, 27, 24, + 29, 30, 31, 28, + 18, 19, 16, 17, + -1, -1, -1, -1, + -1, -1, -1, -1 + } +}; + +/* Shift macro for 128 bit strings with rotation smaller than 32 bits (!) */ +#define ROTL(DEST, SRC, SHIFT) \ +{ \ + (DEST)[0] = (SRC)[0] << (SHIFT) ^ (SRC)[1] >> (32 - (SHIFT)); \ + (DEST)[1] = (SRC)[1] << (SHIFT) ^ (SRC)[2] >> (32 - (SHIFT)); \ + (DEST)[2] = (SRC)[2] << (SHIFT) ^ (SRC)[3] >> (32 - (SHIFT)); \ + (DEST)[3] = (SRC)[3] << (SHIFT) ^ (SRC)[0] >> (32 - (SHIFT)); \ +} + +#define FL(XL, XR, KL, KR) \ +{ \ + (XR) = ((((XL) & (KL)) << 1) | (((XL) & (KL)) >> 31)) ^ (XR); \ + (XL) = ((XR) | (KR)) ^ (XL); \ +} + +#define FLInv(YL, YR, KL, KR) \ +{ \ + (YL) = ((YR) | (KR)) ^ (YL); \ + (YR) = ((((YL) & (KL)) << 1) | (((YL) & (KL)) >> 31)) ^ (YR); \ +} + +#define SHIFT_AND_PLACE(INDEX, OFFSET) \ +{ \ + TK[0] = KC[(OFFSET) * 4 + 0]; \ + TK[1] = KC[(OFFSET) * 4 + 1]; \ + TK[2] = KC[(OFFSET) * 4 + 2]; \ + TK[3] = KC[(OFFSET) * 4 + 3]; \ + \ + for( i = 1; i <= 4; i++ ) \ + if( shifts[(INDEX)][(OFFSET)][i -1] ) \ + ROTL(TK + i * 4, TK, ( 15 * i ) % 32); \ + \ + for( i = 0; i < 20; i++ ) \ + if( indexes[(INDEX)][(OFFSET)][i] != -1 ) { \ + RK[indexes[(INDEX)][(OFFSET)][i]] = TK[ i ]; \ + } \ +} + +static void camellia_feistel( const uint32_t x[2], const uint32_t k[2], + uint32_t z[2]) +{ + uint32_t I0, I1; + I0 = x[0] ^ k[0]; + I1 = x[1] ^ k[1]; + + I0 = (SBOX1((I0 >> 24) & 0xFF) << 24) | + (SBOX2((I0 >> 16) & 0xFF) << 16) | + (SBOX3((I0 >> 8) & 0xFF) << 8) | + (SBOX4((I0 ) & 0xFF) ); + I1 = (SBOX2((I1 >> 24) & 0xFF) << 24) | + (SBOX3((I1 >> 16) & 0xFF) << 16) | + (SBOX4((I1 >> 8) & 0xFF) << 8) | + (SBOX1((I1 ) & 0xFF) ); + + I0 ^= (I1 << 8) | (I1 >> 24); + I1 ^= (I0 << 16) | (I0 >> 16); + I0 ^= (I1 >> 8) | (I1 << 24); + I1 ^= (I0 >> 8) | (I0 << 24); + + z[0] ^= I1; + z[1] ^= I0; +} + +void camellia_init( camellia_context *ctx ) +{ + memset( ctx, 0, sizeof( camellia_context ) ); +} + +void camellia_free( camellia_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( camellia_context ) ); +} + +/* + * Camellia key schedule (encryption) + */ +int camellia_setkey_enc( camellia_context *ctx, const unsigned char *key, + unsigned int keysize ) +{ + int idx; + size_t i; + uint32_t *RK; + unsigned char t[64]; + uint32_t SIGMA[6][2]; + uint32_t KC[16]; + uint32_t TK[20]; + + RK = ctx->rk; + + memset( t, 0, 64 ); + memset( RK, 0, sizeof(ctx->rk) ); + + switch( keysize ) + { + case 128: ctx->nr = 3; idx = 0; break; + case 192: + case 256: ctx->nr = 4; idx = 1; break; + default : return( POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH ); + } + + for( i = 0; i < keysize / 8; ++i ) + t[i] = key[i]; + + if( keysize == 192 ) { + for( i = 0; i < 8; i++ ) + t[24 + i] = ~t[16 + i]; + } + + /* + * Prepare SIGMA values + */ + for( i = 0; i < 6; i++ ) { + GET_UINT32_BE( SIGMA[i][0], SIGMA_CHARS[i], 0 ); + GET_UINT32_BE( SIGMA[i][1], SIGMA_CHARS[i], 4 ); + } + + /* + * Key storage in KC + * Order: KL, KR, KA, KB + */ + memset( KC, 0, sizeof(KC) ); + + /* Store KL, KR */ + for( i = 0; i < 8; i++ ) + GET_UINT32_BE( KC[i], t, i * 4 ); + + /* Generate KA */ + for( i = 0; i < 4; ++i ) + KC[8 + i] = KC[i] ^ KC[4 + i]; + + camellia_feistel( KC + 8, SIGMA[0], KC + 10 ); + camellia_feistel( KC + 10, SIGMA[1], KC + 8 ); + + for( i = 0; i < 4; ++i ) + KC[8 + i] ^= KC[i]; + + camellia_feistel( KC + 8, SIGMA[2], KC + 10 ); + camellia_feistel( KC + 10, SIGMA[3], KC + 8 ); + + if( keysize > 128 ) { + /* Generate KB */ + for( i = 0; i < 4; ++i ) + KC[12 + i] = KC[4 + i] ^ KC[8 + i]; + + camellia_feistel( KC + 12, SIGMA[4], KC + 14 ); + camellia_feistel( KC + 14, SIGMA[5], KC + 12 ); + } + + /* + * Generating subkeys + */ + + /* Manipulating KL */ + SHIFT_AND_PLACE( idx, 0 ); + + /* Manipulating KR */ + if( keysize > 128 ) { + SHIFT_AND_PLACE( idx, 1 ); + } + + /* Manipulating KA */ + SHIFT_AND_PLACE( idx, 2 ); + + /* Manipulating KB */ + if( keysize > 128 ) { + SHIFT_AND_PLACE( idx, 3 ); + } + + /* Do transpositions */ + for( i = 0; i < 20; i++ ) { + if( transposes[idx][i] != -1 ) { + RK[32 + 12 * idx + i] = RK[transposes[idx][i]]; + } + } + + return( 0 ); +} + +/* + * Camellia key schedule (decryption) + */ +int camellia_setkey_dec( camellia_context *ctx, const unsigned char *key, + unsigned int keysize ) +{ + int idx, ret; + size_t i; + camellia_context cty; + uint32_t *RK; + uint32_t *SK; + + camellia_init( &cty ); + + /* Also checks keysize */ + if( ( ret = camellia_setkey_enc( &cty, key, keysize ) ) ) + goto exit; + + ctx->nr = cty.nr; + idx = ( ctx->nr == 4 ); + + RK = ctx->rk; + SK = cty.rk + 24 * 2 + 8 * idx * 2; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for( i = 22 + 8 * idx, SK -= 6; i > 0; i--, SK -= 4 ) + { + *RK++ = *SK++; + *RK++ = *SK++; + } + + SK -= 2; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + +exit: + camellia_free( &cty ); + + return( ret ); +} + +/* + * Camellia-ECB block encryption/decryption + */ +int camellia_crypt_ecb( camellia_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int NR; + uint32_t *RK, X[4]; + + ( (void) mode ); + + NR = ctx->nr; + RK = ctx->rk; + + GET_UINT32_BE( X[0], input, 0 ); + GET_UINT32_BE( X[1], input, 4 ); + GET_UINT32_BE( X[2], input, 8 ); + GET_UINT32_BE( X[3], input, 12 ); + + X[0] ^= *RK++; + X[1] ^= *RK++; + X[2] ^= *RK++; + X[3] ^= *RK++; + + while( NR ) { + --NR; + camellia_feistel( X, RK, X + 2 ); + RK += 2; + camellia_feistel( X + 2, RK, X ); + RK += 2; + camellia_feistel( X, RK, X + 2 ); + RK += 2; + camellia_feistel( X + 2, RK, X ); + RK += 2; + camellia_feistel( X, RK, X + 2 ); + RK += 2; + camellia_feistel( X + 2, RK, X ); + RK += 2; + + if( NR ) { + FL(X[0], X[1], RK[0], RK[1]); + RK += 2; + FLInv(X[2], X[3], RK[0], RK[1]); + RK += 2; + } + } + + X[2] ^= *RK++; + X[3] ^= *RK++; + X[0] ^= *RK++; + X[1] ^= *RK++; + + PUT_UINT32_BE( X[2], output, 0 ); + PUT_UINT32_BE( X[3], output, 4 ); + PUT_UINT32_BE( X[0], output, 8 ); + PUT_UINT32_BE( X[1], output, 12 ); + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * Camellia-CBC buffer encryption/decryption + */ +int camellia_crypt_cbc( camellia_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int i; + unsigned char temp[16]; + + if( length % 16 ) + return( POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH ); + + if( mode == CAMELLIA_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 16 ); + camellia_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 16; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + camellia_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 16 ); + + input += 16; + output += 16; + length -= 16; + } + } + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +/* + * Camellia-CFB128 buffer encryption/decryption + */ +int camellia_crypt_cfb128( camellia_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int c; + size_t n = *iv_off; + + if( mode == CAMELLIA_DECRYPT ) + { + while( length-- ) + { + if( n == 0 ) + camellia_crypt_ecb( ctx, CAMELLIA_ENCRYPT, iv, iv ); + + c = *input++; + *output++ = (unsigned char)( c ^ iv[n] ); + iv[n] = (unsigned char) c; + + n = ( n + 1 ) & 0x0F; + } + } + else + { + while( length-- ) + { + if( n == 0 ) + camellia_crypt_ecb( ctx, CAMELLIA_ENCRYPT, iv, iv ); + + iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); + + n = ( n + 1 ) & 0x0F; + } + } + + *iv_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * Camellia-CTR buffer encryption/decryption + */ +int camellia_crypt_ctr( camellia_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output ) +{ + int c, i; + size_t n = *nc_off; + + while( length-- ) + { + if( n == 0 ) { + camellia_crypt_ecb( ctx, CAMELLIA_ENCRYPT, nonce_counter, + stream_block ); + + for( i = 16; i > 0; i-- ) + if( ++nonce_counter[i - 1] != 0 ) + break; + } + c = *input++; + *output++ = (unsigned char)( c ^ stream_block[n] ); + + n = ( n + 1 ) & 0x0F; + } + + *nc_off = n; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CTR */ +#endif /* !POLARSSL_CAMELLIA_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * Camellia test vectors from: + * + * http://info.isl.ntt.co.jp/crypt/eng/camellia/technology.html: + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/intermediate.txt + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/t_camellia.txt + * (For each bitlength: Key 0, Nr 39) + */ +#define CAMELLIA_TESTS_ECB 2 + +static const unsigned char camellia_test_ecb_key[3][CAMELLIA_TESTS_ECB][32] = +{ + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, +}; + +static const unsigned char camellia_test_ecb_plain[CAMELLIA_TESTS_ECB][16] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, + { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +static const unsigned char camellia_test_ecb_cipher[3][CAMELLIA_TESTS_ECB][16] = +{ + { + { 0x67, 0x67, 0x31, 0x38, 0x54, 0x96, 0x69, 0x73, + 0x08, 0x57, 0x06, 0x56, 0x48, 0xea, 0xbe, 0x43 }, + { 0x38, 0x3C, 0x6C, 0x2A, 0xAB, 0xEF, 0x7F, 0xDE, + 0x25, 0xCD, 0x47, 0x0B, 0xF7, 0x74, 0xA3, 0x31 } + }, + { + { 0xb4, 0x99, 0x34, 0x01, 0xb3, 0xe9, 0x96, 0xf8, + 0x4e, 0xe5, 0xce, 0xe7, 0xd7, 0x9b, 0x09, 0xb9 }, + { 0xD1, 0x76, 0x3F, 0xC0, 0x19, 0xD7, 0x7C, 0xC9, + 0x30, 0xBF, 0xF2, 0xA5, 0x6F, 0x7C, 0x93, 0x64 } + }, + { + { 0x9a, 0xcc, 0x23, 0x7d, 0xff, 0x16, 0xd7, 0x6c, + 0x20, 0xef, 0x7c, 0x91, 0x9e, 0x3a, 0x75, 0x09 }, + { 0x05, 0x03, 0xFB, 0x10, 0xAB, 0x24, 0x1E, 0x7C, + 0xF4, 0x5D, 0x8C, 0xDE, 0xEE, 0x47, 0x43, 0x35 } + } +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#define CAMELLIA_TESTS_CBC 3 + +static const unsigned char camellia_test_cbc_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } + , + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B } + , + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char camellia_test_cbc_iv[16] = + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } +; + +static const unsigned char camellia_test_cbc_plain[CAMELLIA_TESTS_CBC][16] = +{ + { 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + { 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + { 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF } + +}; + +static const unsigned char camellia_test_cbc_cipher[3][CAMELLIA_TESTS_CBC][16] = +{ + { + { 0x16, 0x07, 0xCF, 0x49, 0x4B, 0x36, 0xBB, 0xF0, + 0x0D, 0xAE, 0xB0, 0xB5, 0x03, 0xC8, 0x31, 0xAB }, + { 0xA2, 0xF2, 0xCF, 0x67, 0x16, 0x29, 0xEF, 0x78, + 0x40, 0xC5, 0xA5, 0xDF, 0xB5, 0x07, 0x48, 0x87 }, + { 0x0F, 0x06, 0x16, 0x50, 0x08, 0xCF, 0x8B, 0x8B, + 0x5A, 0x63, 0x58, 0x63, 0x62, 0x54, 0x3E, 0x54 } + }, + { + { 0x2A, 0x48, 0x30, 0xAB, 0x5A, 0xC4, 0xA1, 0xA2, + 0x40, 0x59, 0x55, 0xFD, 0x21, 0x95, 0xCF, 0x93 }, + { 0x5D, 0x5A, 0x86, 0x9B, 0xD1, 0x4C, 0xE5, 0x42, + 0x64, 0xF8, 0x92, 0xA6, 0xDD, 0x2E, 0xC3, 0xD5 }, + { 0x37, 0xD3, 0x59, 0xC3, 0x34, 0x98, 0x36, 0xD8, + 0x84, 0xE3, 0x10, 0xAD, 0xDF, 0x68, 0xC4, 0x49 } + }, + { + { 0xE6, 0xCF, 0xA3, 0x5F, 0xC0, 0x2B, 0x13, 0x4A, + 0x4D, 0x2C, 0x0B, 0x67, 0x37, 0xAC, 0x3E, 0xDA }, + { 0x36, 0xCB, 0xEB, 0x73, 0xBD, 0x50, 0x4B, 0x40, + 0x70, 0xB1, 0xB7, 0xDE, 0x2B, 0x21, 0xEB, 0x50 }, + { 0xE3, 0x1A, 0x60, 0x55, 0x29, 0x7D, 0x96, 0xCA, + 0x33, 0x30, 0xCD, 0xF1, 0xB1, 0x86, 0x0A, 0x83 } + } +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +/* + * Camellia-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc5528.html + */ + +static const unsigned char camellia_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char camellia_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char camellia_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char camellia_test_ctr_ct[3][48] = +{ + { 0xD0, 0x9D, 0xC2, 0x9A, 0x82, 0x14, 0x61, 0x9A, + 0x20, 0x87, 0x7C, 0x76, 0xDB, 0x1F, 0x0B, 0x3F }, + { 0xDB, 0xF3, 0xC7, 0x8D, 0xC0, 0x83, 0x96, 0xD4, + 0xDA, 0x7C, 0x90, 0x77, 0x65, 0xBB, 0xCB, 0x44, + 0x2B, 0x8E, 0x8E, 0x0F, 0x31, 0xF0, 0xDC, 0xA7, + 0x2C, 0x74, 0x17, 0xE3, 0x53, 0x60, 0xE0, 0x48 }, + { 0xB1, 0x9D, 0x1F, 0xCD, 0xCB, 0x75, 0xEB, 0x88, + 0x2F, 0x84, 0x9C, 0xE2, 0x4D, 0x85, 0xCF, 0x73, + 0x9C, 0xE6, 0x4B, 0x2B, 0x5C, 0x9D, 0x73, 0xF1, + 0x4F, 0x2D, 0x5D, 0x9D, 0xCE, 0x98, 0x89, 0xCD, + 0xDF, 0x50, 0x86, 0x96 } +}; + +static const int camellia_test_ctr_len[3] = + { 16, 32, 36 }; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +/* + * Checkup routine + */ +int camellia_self_test( int verbose ) +{ + int i, j, u, v; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char src[16]; + unsigned char dst[16]; +#if defined(POLARSSL_CIPHER_MODE_CBC) + unsigned char iv[16]; +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + size_t offset, len; + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + + camellia_context ctx; + + memset( key, 0, 32 ); + + for( j = 0; j < 6; j++ ) { + u = j >> 1; + v = j & 1; + + if( verbose != 0 ) + polarssl_printf( " CAMELLIA-ECB-%3d (%s): ", 128 + u * 64, + (v == CAMELLIA_DECRYPT) ? "dec" : "enc"); + + for( i = 0; i < CAMELLIA_TESTS_ECB; i++ ) { + memcpy( key, camellia_test_ecb_key[u][i], 16 + 8 * u ); + + if( v == CAMELLIA_DECRYPT ) { + camellia_setkey_dec( &ctx, key, 128 + u * 64 ); + memcpy( src, camellia_test_ecb_cipher[u][i], 16 ); + memcpy( dst, camellia_test_ecb_plain[i], 16 ); + } else { /* CAMELLIA_ENCRYPT */ + camellia_setkey_enc( &ctx, key, 128 + u * 64 ); + memcpy( src, camellia_test_ecb_plain[i], 16 ); + memcpy( dst, camellia_test_ecb_cipher[u][i], 16 ); + } + + camellia_crypt_ecb( &ctx, v, src, buf ); + + if( memcmp( buf, dst, 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for( j = 0; j < 6; j++ ) + { + u = j >> 1; + v = j & 1; + + if( verbose != 0 ) + polarssl_printf( " CAMELLIA-CBC-%3d (%s): ", 128 + u * 64, + ( v == CAMELLIA_DECRYPT ) ? "dec" : "enc" ); + + memcpy( src, camellia_test_cbc_iv, 16 ); + memcpy( dst, camellia_test_cbc_iv, 16 ); + memcpy( key, camellia_test_cbc_key[u], 16 + 8 * u ); + + if( v == CAMELLIA_DECRYPT ) { + camellia_setkey_dec( &ctx, key, 128 + u * 64 ); + } else { + camellia_setkey_enc( &ctx, key, 128 + u * 64 ); + } + + for( i = 0; i < CAMELLIA_TESTS_CBC; i++ ) { + + if( v == CAMELLIA_DECRYPT ) { + memcpy( iv , src, 16 ); + memcpy( src, camellia_test_cbc_cipher[u][i], 16 ); + memcpy( dst, camellia_test_cbc_plain[i], 16 ); + } else { /* CAMELLIA_ENCRYPT */ + memcpy( iv , dst, 16 ); + memcpy( src, camellia_test_cbc_plain[i], 16 ); + memcpy( dst, camellia_test_cbc_cipher[u][i], 16 ); + } + + camellia_crypt_cbc( &ctx, v, 16, iv, src, buf ); + + if( memcmp( buf, dst, 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } +#endif /* POLARSSL_CIPHER_MODE_CBC */ + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " CAMELLIA-CTR-128 (%s): ", + ( v == CAMELLIA_DECRYPT ) ? "dec" : "enc" ); + + memcpy( nonce_counter, camellia_test_ctr_nonce_counter[u], 16 ); + memcpy( key, camellia_test_ctr_key[u], 16 ); + + offset = 0; + camellia_setkey_enc( &ctx, key, 128 ); + + if( v == CAMELLIA_DECRYPT ) + { + len = camellia_test_ctr_len[u]; + memcpy( buf, camellia_test_ctr_ct[u], len ); + + camellia_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, + buf, buf ); + + if( memcmp( buf, camellia_test_ctr_pt[u], len ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + else + { + len = camellia_test_ctr_len[u]; + memcpy( buf, camellia_test_ctr_pt[u], len ); + + camellia_crypt_ctr( &ctx, len, &offset, nonce_counter, stream_block, + buf, buf ); + + if( memcmp( buf, camellia_test_ctr_ct[u], len ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_CAMELLIA_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ccm.c b/component/common/network/ssl/polarssl-1.3.8/library/ccm.c new file mode 100644 index 0000000..72d766b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ccm.c @@ -0,0 +1,456 @@ +/* + * NIST SP800-38C compliant CCM implementation + * + * Copyright (C) 2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Definition of CCM: + * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + * RFC 3610 "Counter with CBC-MAC (CCM)" + * + * Related: + * RFC 5116 "An Interface and Algorithms for Authenticated Encryption" + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CCM_C) + +#include "polarssl/ccm.h" + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#define CCM_ENCRYPT 0 +#define CCM_DECRYPT 1 + +/* + * Initialize context + */ +int ccm_init( ccm_context *ctx, cipher_id_t cipher, + const unsigned char *key, unsigned int keysize ) +{ + int ret; + const cipher_info_t *cipher_info; + + memset( ctx, 0, sizeof( ccm_context ) ); + + cipher_init( &ctx->cipher_ctx ); + + cipher_info = cipher_info_from_values( cipher, keysize, POLARSSL_MODE_ECB ); + if( cipher_info == NULL ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + if( cipher_info->block_size != 16 ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + if( ( ret = cipher_init_ctx( &ctx->cipher_ctx, cipher_info ) ) != 0 ) + return( ret ); + + if( ( ret = cipher_setkey( &ctx->cipher_ctx, key, keysize, + POLARSSL_ENCRYPT ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} + +/* + * Free context + */ +void ccm_free( ccm_context *ctx ) +{ + cipher_free( &ctx->cipher_ctx ); + polarssl_zeroize( ctx, sizeof( ccm_context ) ); +} + +/* + * Macros for common operations. + * Results in smaller compiled code than static inline functions. + */ + +/* + * Update the CBC-MAC state in y using a block in b + * (Always using b as the source helps the compiler optimise a bit better.) + */ +#define UPDATE_CBC_MAC \ + for( i = 0; i < 16; i++ ) \ + y[i] ^= b[i]; \ + \ + if( ( ret = cipher_update( &ctx->cipher_ctx, y, 16, y, &olen ) ) != 0 ) \ + return( ret ); + +/* + * Encrypt or decrypt a partial block with CTR + * Warning: using b for temporary storage! src and dst must not be b! + * This avoids allocating one more 16 bytes buffer while allowing src == dst. + */ +#define CTR_CRYPT( dst, src, len ) \ + if( ( ret = cipher_update( &ctx->cipher_ctx, ctr, 16, b, &olen ) ) != 0 ) \ + return( ret ); \ + \ + for( i = 0; i < len; i++ ) \ + dst[i] = src[i] ^ b[i]; + +/* + * Authenticated encryption or decryption + */ +static int ccm_auth_crypt( ccm_context *ctx, int mode, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ) +{ + int ret; + unsigned char i; + unsigned char q = 16 - 1 - iv_len; + size_t len_left, olen; + unsigned char b[16]; + unsigned char y[16]; + unsigned char ctr[16]; + const unsigned char *src; + unsigned char *dst; + + /* + * Check length requirements: SP800-38C A.1 + * Additional requirement: a < 2^16 - 2^8 to simplify the code. + * 'length' checked later (when writing it to the first block) + */ + if( tag_len < 4 || tag_len > 16 || tag_len % 2 != 0 ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + /* Also implies q is within bounds */ + if( iv_len < 7 || iv_len > 13 ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + if( add_len > 0xFF00 ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + /* + * First block B_0: + * 0 .. 0 flags + * 1 .. iv_len nonce (aka iv) + * iv_len+1 .. 15 length + * + * With flags as (bits): + * 7 0 + * 6 add present? + * 5 .. 3 (t - 2) / 2 + * 2 .. 0 q - 1 + */ + b[0] = 0; + b[0] |= ( add_len > 0 ) << 6; + b[0] |= ( ( tag_len - 2 ) / 2 ) << 3; + b[0] |= q - 1; + + memcpy( b + 1, iv, iv_len ); + + for( i = 0, len_left = length; i < q; i++, len_left >>= 8 ) + b[15-i] = (unsigned char)( len_left & 0xFF ); + + if( len_left > 0 ) + return( POLARSSL_ERR_CCM_BAD_INPUT ); + + + /* Start CBC-MAC with first block */ + memset( y, 0, 16 ); + UPDATE_CBC_MAC; + + /* + * If there is additional data, update CBC-MAC with + * add_len, add, 0 (padding to a block boundary) + */ + if( add_len > 0 ) + { + size_t use_len; + len_left = add_len; + src = add; + + memset( b, 0, 16 ); + b[0] = (unsigned char)( ( add_len >> 8 ) & 0xFF ); + b[1] = (unsigned char)( ( add_len ) & 0xFF ); + + use_len = len_left < 16 - 2 ? len_left : 16 - 2; + memcpy( b + 2, src, use_len ); + len_left -= use_len; + src += use_len; + + UPDATE_CBC_MAC; + + while( len_left > 0 ) + { + use_len = len_left > 16 ? 16 : len_left; + + memset( b, 0, 16 ); + memcpy( b, src, use_len ); + UPDATE_CBC_MAC; + + len_left -= use_len; + src += use_len; + } + } + + /* + * Prepare counter block for encryption: + * 0 .. 0 flags + * 1 .. iv_len nonce (aka iv) + * iv_len+1 .. 15 counter (initially 1) + * + * With flags as (bits): + * 7 .. 3 0 + * 2 .. 0 q - 1 + */ + ctr[0] = q - 1; + memcpy( ctr + 1, iv, iv_len ); + memset( ctr + 1 + iv_len, 0, q ); + ctr[15] = 1; + + /* + * Authenticate and {en,de}crypt the message. + * + * The only difference between encryption and decryption is + * the respective order of authentication and {en,de}cryption. + */ + len_left = length; + src = input; + dst = output; + + while( len_left > 0 ) + { + unsigned char use_len = len_left > 16 ? 16 : len_left; + + if( mode == CCM_ENCRYPT ) + { + memset( b, 0, 16 ); + memcpy( b, src, use_len ); + UPDATE_CBC_MAC; + } + + CTR_CRYPT( dst, src, use_len ); + + if( mode == CCM_DECRYPT ) + { + memset( b, 0, 16 ); + memcpy( b, dst, use_len ); + UPDATE_CBC_MAC; + } + + dst += use_len; + src += use_len; + len_left -= use_len; + + /* + * Increment counter. + * No need to check for overflow thanks to the length check above. + */ + for( i = 0; i < q; i++ ) + if( ++ctr[15-i] != 0 ) + break; + } + + /* + * Authentication: reset counter and crypt/mask internal tag + */ + for( i = 0; i < q; i++ ) + ctr[15-i] = 0; + + CTR_CRYPT( y, y, 16 ); + memcpy( tag, y, tag_len ); + + return( 0 ); +} + +/* + * Authenticated encryption + */ +int ccm_encrypt_and_tag( ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len ) +{ + return( ccm_auth_crypt( ctx, CCM_ENCRYPT, length, iv, iv_len, + add, add_len, input, output, tag, tag_len ) ); +} + +/* + * Authenticated decryption + */ +int ccm_auth_decrypt( ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len ) +{ + int ret; + unsigned char check_tag[16]; + unsigned char i; + int diff; + + if( ( ret = ccm_auth_crypt( ctx, CCM_DECRYPT, length, + iv, iv_len, add, add_len, + input, output, check_tag, tag_len ) ) != 0 ) + { + return( ret ); + } + + /* Check tag in "constant-time" */ + for( diff = 0, i = 0; i < tag_len; i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) + { + polarssl_zeroize( output, length ); + return( POLARSSL_ERR_CCM_AUTH_FAILED ); + } + + return( 0 ); +} + + +#if defined(POLARSSL_SELF_TEST) && defined(POLARSSL_AES_C) + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_printf printf +#endif + +/* + * Examples 1 to 3 from SP800-38C Appendix C + */ + +#define NB_TESTS 3 + +/* + * The data is the same for all tests, only the used length changes + */ +static const unsigned char key[] = { + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f +}; + +static const unsigned char iv[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b +}; + +static const unsigned char ad[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 +}; + +static const unsigned char msg[] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +}; + +static const size_t iv_len [NB_TESTS] = { 7, 8, 12 }; +static const size_t add_len[NB_TESTS] = { 8, 16, 20 }; +static const size_t msg_len[NB_TESTS] = { 4, 16, 24 }; +static const size_t tag_len[NB_TESTS] = { 4, 6, 8 }; + +static const unsigned char res[NB_TESTS][32] = { + { 0x71, 0x62, 0x01, 0x5b, 0x4d, 0xac, 0x25, 0x5d }, + { 0xd2, 0xa1, 0xf0, 0xe0, 0x51, 0xea, 0x5f, 0x62, + 0x08, 0x1a, 0x77, 0x92, 0x07, 0x3d, 0x59, 0x3d, + 0x1f, 0xc6, 0x4f, 0xbf, 0xac, 0xcd }, + { 0xe3, 0xb2, 0x01, 0xa9, 0xf5, 0xb7, 0x1a, 0x7a, + 0x9b, 0x1c, 0xea, 0xec, 0xcd, 0x97, 0xe7, 0x0b, + 0x61, 0x76, 0xaa, 0xd9, 0xa4, 0x42, 0x8a, 0xa5, + 0x48, 0x43, 0x92, 0xfb, 0xc1, 0xb0, 0x99, 0x51 } +}; + +int ccm_self_test( int verbose ) +{ + ccm_context ctx; + unsigned char out[32]; + size_t i; + int ret; + + if( ccm_init( &ctx, POLARSSL_CIPHER_ID_AES, key, 8 * sizeof key ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( " CCM: setup failed" ); + + return( 1 ); + } + + for( i = 0; i < NB_TESTS; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " CCM-AES #%u: ", (unsigned int) i + 1 ); + + ret = ccm_encrypt_and_tag( &ctx, msg_len[i], + iv, iv_len[i], ad, add_len[i], + msg, out, + out + msg_len[i], tag_len[i] ); + + if( ret != 0 || + memcmp( out, res[i], msg_len[i] + tag_len[i] ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + ret = ccm_auth_decrypt( &ctx, msg_len[i], + iv, iv_len[i], ad, add_len[i], + res[i], out, + res[i] + msg_len[i], tag_len[i] ); + + if( ret != 0 || + memcmp( out, msg, msg_len[i] ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + ccm_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST && POLARSSL_AES_C */ + +#endif /* POLARSSL_CCM_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/certs.c b/component/common/network/ssl/polarssl-1.3.8/library/certs.c new file mode 100644 index 0000000..a782bc1 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/certs.c @@ -0,0 +1,310 @@ +/* + * X.509 test certificates + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CERTS_C) + +#if defined(POLARSSL_ECDSA_C) +#define TEST_CA_CRT_EC \ +"-----BEGIN CERTIFICATE-----\r\n" \ +"MIICUjCCAdegAwIBAgIJAMFD4n5iQ8zoMAoGCCqGSM49BAMCMD4xCzAJBgNVBAYT\r\n" \ +"Ak5MMREwDwYDVQQKEwhQb2xhclNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBF\r\n" \ +"QyBDQTAeFw0xMzA5MjQxNTQ5NDhaFw0yMzA5MjIxNTQ5NDhaMD4xCzAJBgNVBAYT\r\n" \ +"Ak5MMREwDwYDVQQKEwhQb2xhclNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBF\r\n" \ +"QyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMPaKzRBN1gvh1b+/Im6KUNLTuBu\r\n" \ +"ww5XUzM5WNRStJGVOQsj318XJGJI/BqVKc4sLYfCiFKAr9ZqqyHduNMcbli4yuiy\r\n" \ +"aY7zQa0pw7RfdadHb9UZKVVpmlM7ILRmFmAzHqOBoDCBnTAdBgNVHQ4EFgQUnW0g\r\n" \ +"JEkBPyvLeLUZvH4kydv7NnwwbgYDVR0jBGcwZYAUnW0gJEkBPyvLeLUZvH4kydv7\r\n" \ +"NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xhclNTTDEcMBoGA1UE\r\n" \ +"AxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAwGA1UdEwQFMAMBAf8w\r\n" \ +"CgYIKoZIzj0EAwIDaQAwZgIxAMO0YnNWKJUAfXgSJtJxexn4ipg+kv4znuR50v56\r\n" \ +"t4d0PCu412mUC6Nnd7izvtE2MgIxAP1nnJQjZ8BWukszFQDG48wxCCyci9qpdSMv\r\n" \ +"uCjn8pwUOkABXK8Mss90fzCfCEOtIA==\r\n" \ +"-----END CERTIFICATE-----\r\n" +const char test_ca_crt_ec[] = TEST_CA_CRT_EC; + +const char test_ca_key_ec[] = +"-----BEGIN EC PRIVATE KEY-----\r\n" +"Proc-Type: 4,ENCRYPTED\r\n" +"DEK-Info: DES-EDE3-CBC,307EAB469933D64E\r\n" +"\r\n" +"IxbrRmKcAzctJqPdTQLA4SWyBYYGYJVkYEna+F7Pa5t5Yg/gKADrFKcm6B72e7DG\r\n" +"ihExtZI648s0zdYw6qSJ74vrPSuWDe5qm93BqsfVH9svtCzWHW0pm1p0KTBCFfUq\r\n" +"UsuWTITwJImcnlAs1gaRZ3sAWm7cOUidL0fo2G0fYUFNcYoCSLffCFTEHBuPnagb\r\n" +"a77x/sY1Bvii8S9/XhDTb6pTMx06wzrm\r\n" +"-----END EC PRIVATE KEY-----\r\n"; + +const char test_ca_pwd_ec[] = "PolarSSLTest"; + +const char test_srv_crt_ec[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" +"MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" +"CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" +"2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" +"BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" +"PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\r\n" +"clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" +"CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\r\n" +"C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\r\n" +"fGa5kHvHARBPc8YAIVIqDvHH1Q==\r\n" +"-----END CERTIFICATE-----\r\n"; + +const char test_srv_key_ec[] = +"-----BEGIN EC PRIVATE KEY-----\r\n" +"MHcCAQEEIPEqEyB2AnCoPL/9U/YDHvdqXYbIogTywwyp6/UfDw6noAoGCCqGSM49\r\n" +"AwEHoUQDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCtFLX3aCJZYpJO5QDYIxH/\r\n" +"6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/w==\r\n" +"-----END EC PRIVATE KEY-----\r\n"; + +const char test_cli_crt_ec[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIICLDCCAbKgAwIBAgIBDTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" +"MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjBBMQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxHzAdBgNVBAMTFlBvbGFyU1NMIFRlc3QgQ2xpZW50IDIw\r\n" +"WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARX5a6xc9/TrLuTuIH/Eq7u5lOszlVT\r\n" +"9jQOzC7jYyUL35ji81xgNpbA1RgUcOV/n9VLRRjlsGzVXPiWj4dwo+THo4GdMIGa\r\n" +"MAkGA1UdEwQCMAAwHQYDVR0OBBYEFHoAX4Zk/OBd5REQO7LmO8QmP8/iMG4GA1Ud\r\n" +"IwRnMGWAFJ1tICRJAT8ry3i1Gbx+JMnb+zZ8oUKkQDA+MQswCQYDVQQGEwJOTDER\r\n" +"MA8GA1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0GC\r\n" +"CQDBQ+J+YkPM6DAKBggqhkjOPQQDAgNoADBlAjBKZQ17IIOimbmoD/yN7o89u3BM\r\n" +"lgOsjnhw3fIOoLIWy2WOGsk/LGF++DzvrRzuNiACMQCd8iem1XS4JK7haj8xocpU\r\n" +"LwjQje5PDGHfd3h9tP38Qknu5bJqws0md2KOKHyeV0U=\r\n" +"-----END CERTIFICATE-----\r\n"; + +const char test_cli_key_ec[] = +"-----BEGIN EC PRIVATE KEY-----\r\n" +"MHcCAQEEIPb3hmTxZ3/mZI3vyk7p3U3wBf+WIop6hDhkFzJhmLcqoAoGCCqGSM49\r\n" +"AwEHoUQDQgAEV+WusXPf06y7k7iB/xKu7uZTrM5VU/Y0Dswu42MlC9+Y4vNcYDaW\r\n" +"wNUYFHDlf5/VS0UY5bBs1Vz4lo+HcKPkxw==\r\n" +"-----END EC PRIVATE KEY-----\r\n"; +#else +#define TEST_CA_CRT_EC +#endif /* POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_RSA_C) +#define TEST_CA_CRT_RSA \ +"-----BEGIN CERTIFICATE-----\r\n" \ +"MIIDhzCCAm+gAwIBAgIBADANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" \ +"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" \ +"MTEwMjEyMTQ0NDAwWhcNMjEwMjEyMTQ0NDAwWjA7MQswCQYDVQQGEwJOTDERMA8G\r\n" \ +"A1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwggEiMA0G\r\n" \ +"CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDA3zf8F7vglp0/ht6WMn1EpRagzSHx\r\n" \ +"mdTs6st8GFgIlKXsm8WL3xoemTiZhx57wI053zhdcHgH057Zk+i5clHFzqMwUqny\r\n" \ +"50BwFMtEonILwuVA+T7lpg6z+exKY8C4KQB0nFc7qKUEkHHxvYPZP9al4jwqj+8n\r\n" \ +"YMPGn8u67GB9t+aEMr5P+1gmIgNb1LTV+/Xjli5wwOQuvfwu7uJBVcA0Ln0kcmnL\r\n" \ +"R7EUQIN9Z/SG9jGr8XmksrUuEvmEF/Bibyc+E1ixVA0hmnM3oTDPb5Lc9un8rNsu\r\n" \ +"KNF+AksjoBXyOGVkCeoMbo4bF6BxyLObyavpw/LPh5aPgAIynplYb6LVAgMBAAGj\r\n" \ +"gZUwgZIwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUtFrkpbPe0lL2udWmlQ/rPrzH\r\n" \ +"/f8wYwYDVR0jBFwwWoAUtFrkpbPe0lL2udWmlQ/rPrzH/f+hP6Q9MDsxCzAJBgNV\r\n" \ +"BAYTAk5MMREwDwYDVQQKEwhQb2xhclNTTDEZMBcGA1UEAxMQUG9sYXJTU0wgVGVz\r\n" \ +"dCBDQYIBADANBgkqhkiG9w0BAQUFAAOCAQEAuP1U2ABUkIslsCfdlc2i94QHHYeJ\r\n" \ +"SsR4EdgHtdciUI5I62J6Mom+Y0dT/7a+8S6MVMCZP6C5NyNyXw1GWY/YR82XTJ8H\r\n" \ +"DBJiCTok5DbZ6SzaONBzdWHXwWwmi5vg1dxn7YxrM9d0IjxM27WNKs4sDQhZBQkF\r\n" \ +"pjmfs2cb4oPl4Y9T9meTx/lvdkRYEug61Jfn6cA+qHpyPYdTH+UshITnmp5/Ztkf\r\n" \ +"m/UTSLBNFNHesiTZeH31NcxYGdHSme9Nc/gfidRa0FLOCfWxRlFqAI47zG9jAQCZ\r\n" \ +"7Z2mCGDNMhjQc+BYcdnl0lPXjdDK6V0qCg1dVewhUBcW5gZKzV7e9+DpVA==\r\n" \ +"-----END CERTIFICATE-----\r\n" +const char test_ca_crt_rsa[] = TEST_CA_CRT_RSA; + +const char test_ca_key_rsa[] = +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"Proc-Type: 4,ENCRYPTED\r\n" +"DEK-Info: DES-EDE3-CBC,A8A95B05D5B7206B\r\n" +"\r\n" +"9Qd9GeArejl1GDVh2lLV1bHt0cPtfbh5h/5zVpAVaFpqtSPMrElp50Rntn9et+JA\r\n" +"7VOyboR+Iy2t/HU4WvA687k3Bppe9GwKHjHhtl//8xFKwZr3Xb5yO5JUP8AUctQq\r\n" +"Nb8CLlZyuUC+52REAAthdWgsX+7dJO4yabzUcQ22Tp9JSD0hiL43BlkWYUNK3dAo\r\n" +"PZlmiptjnzVTjg1MxsBSydZinWOLBV8/JQgxSPo2yD4uEfig28qbvQ2wNIn0pnAb\r\n" +"GxnSAOazkongEGfvcjIIs+LZN9gXFhxcOh6kc4Q/c99B7QWETwLLkYgZ+z1a9VY9\r\n" +"gEU7CwCxYCD+h9hY6FPmsK0/lC4O7aeRKpYq00rPPxs6i7phiexg6ax6yTMmArQq\r\n" +"QmK3TAsJm8V/J5AWpLEV6jAFgRGymGGHnof0DXzVWZidrcZJWTNuGEX90nB3ee2w\r\n" +"PXJEFWKoD3K3aFcSLdHYr3mLGxP7H9ThQai9VsycxZKS5kwvBKQ//YMrmFfwPk8x\r\n" +"vTeY4KZMaUrveEel5tWZC94RSMKgxR6cyE1nBXyTQnDOGbfpNNgBKxyKbINWoOJU\r\n" +"WJZAwlsQn+QzCDwpri7+sV1mS3gBE6UY7aQmnmiiaC2V3Hbphxct/en5QsfDOt1X\r\n" +"JczSfpRWLlbPznZg8OQh/VgCMA58N5DjOzTIK7sJJ5r+94ZBTCpgAMbF588f0NTR\r\n" +"KCe4yrxGJR7X02M4nvD4IwOlpsQ8xQxZtOSgXv4LkxvdU9XJJKWZ/XNKJeWztxSe\r\n" +"Z1vdTc2YfsDBA2SEv33vxHx2g1vqtw8SjDRT2RaQSS0QuSaMJimdOX6mTOCBKk1J\r\n" +"9Q5mXTrER+/LnK0jEmXsBXWA5bqqVZIyahXSx4VYZ7l7w/PHiUDtDgyRhMMKi4n2\r\n" +"iQvQcWSQTjrpnlJbca1/DkpRt3YwrvJwdqb8asZU2VrNETh5x0QVefDRLFiVpif/\r\n" +"tUaeAe/P1F8OkS7OIZDs1SUbv/sD2vMbhNkUoCms3/PvNtdnvgL4F0zhaDpKCmlT\r\n" +"P8vx49E7v5CyRNmED9zZg4o3wmMqrQO93PtTug3Eu9oVx1zPQM1NVMyBa2+f29DL\r\n" +"1nuTCeXdo9+ni45xx+jAI4DCwrRdhJ9uzZyC6962H37H6D+5naNvClFR1s6li1Gb\r\n" +"nqPoiy/OBsEx9CaDGcqQBp5Wme/3XW+6z1ISOx+igwNTVCT14mHdBMbya0eIKft5\r\n" +"X+GnwtgEMyCYyyWuUct8g4RzErcY9+yW9Om5Hzpx4zOuW4NPZgPDTgK+t2RSL/Yq\r\n" +"rE1njrgeGYcVeG3f+OftH4s6fPbq7t1A5ZgUscbLMBqr9tK+OqygR4EgKBPsH6Cz\r\n" +"L6zlv/2RV0qAHvVuDJcIDIgwY5rJtINEm32rhOeFNJwZS5MNIC1czXZx5//ugX7l\r\n" +"I4sy5nbVhwSjtAk8Xg5dZbdTZ6mIrb7xqH+fdakZor1khG7bC2uIwibD3cSl2XkR\r\n" +"wN48lslbHnqqagr6Xm1nNOSVl8C/6kbJEsMpLhAezfRtGwvOucoaE+WbeUNolGde\r\n" +"P/eQiddSf0brnpiLJRh7qZrl9XuqYdpUqnoEdMAfotDOID8OtV7gt8a48ad8VPW2\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; + +const char test_ca_pwd_rsa[] = "PolarSSLTest"; + +const char test_srv_crt_rsa[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDNzCCAh+gAwIBAgIBAjANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" +"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" +"MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcN\r\n" +"AQEBBQADggEPADCCAQoCggEBAMFNo93nzR3RBNdJcriZrA545Do8Ss86ExbQWuTN\r\n" +"owCIp+4ea5anUrSQ7y1yej4kmvy2NKwk9XfgJmSMnLAofaHa6ozmyRyWvP7BBFKz\r\n" +"NtSj+uGxdtiQwWG0ZlI2oiZTqqt0Xgd9GYLbKtgfoNkNHC1JZvdbJXNG6AuKT2kM\r\n" +"tQCQ4dqCEGZ9rlQri2V5kaHiYcPNQEkI7mgM8YuG0ka/0LiqEQMef1aoGh5EGA8P\r\n" +"hYvai0Re4hjGYi/HZo36Xdh98yeJKQHFkA4/J/EwyEoO79bex8cna8cFPXrEAjya\r\n" +"HT4P6DSYW8tzS1KW2BGiLICIaTla0w+w3lkvEcf36hIBMJcCAwEAAaNNMEswCQYD\r\n" +"VR0TBAIwADAdBgNVHQ4EFgQUpQXoZLjc32APUBJNYKhkr02LQ5MwHwYDVR0jBBgw\r\n" +"FoAUtFrkpbPe0lL2udWmlQ/rPrzH/f8wDQYJKoZIhvcNAQEFBQADggEBAJxnXClY\r\n" +"oHkbp70cqBrsGXLybA74czbO5RdLEgFs7rHVS9r+c293luS/KdliLScZqAzYVylw\r\n" +"UfRWvKMoWhHYKp3dEIS4xTXk6/5zXxhv9Rw8SGc8qn6vITHk1S1mPevtekgasY5Y\r\n" +"iWQuM3h4YVlRH3HHEMAD1TnAexfXHHDFQGe+Bd1iAbz1/sH9H8l4StwX6egvTK3M\r\n" +"wXRwkKkvjKaEDA9ATbZx0mI8LGsxSuCqe9r9dyjmttd47J1p1Rulz3CLzaRcVIuS\r\n" +"RRQfaD8neM9c1S/iJ/amTVqJxA1KOdOS5780WhPfSArA+g4qAmSjelc3p4wWpha8\r\n" +"zhuYwjVuX6JHG0c=\r\n" +"-----END CERTIFICATE-----\r\n"; + +const char test_srv_key_rsa[] = +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"MIIEpAIBAAKCAQEAwU2j3efNHdEE10lyuJmsDnjkOjxKzzoTFtBa5M2jAIin7h5r\r\n" +"lqdStJDvLXJ6PiSa/LY0rCT1d+AmZIycsCh9odrqjObJHJa8/sEEUrM21KP64bF2\r\n" +"2JDBYbRmUjaiJlOqq3ReB30Zgtsq2B+g2Q0cLUlm91slc0boC4pPaQy1AJDh2oIQ\r\n" +"Zn2uVCuLZXmRoeJhw81ASQjuaAzxi4bSRr/QuKoRAx5/VqgaHkQYDw+Fi9qLRF7i\r\n" +"GMZiL8dmjfpd2H3zJ4kpAcWQDj8n8TDISg7v1t7HxydrxwU9esQCPJodPg/oNJhb\r\n" +"y3NLUpbYEaIsgIhpOVrTD7DeWS8Rx/fqEgEwlwIDAQABAoIBAQCXR0S8EIHFGORZ\r\n" +"++AtOg6eENxD+xVs0f1IeGz57Tjo3QnXX7VBZNdj+p1ECvhCE/G7XnkgU5hLZX+G\r\n" +"Z0jkz/tqJOI0vRSdLBbipHnWouyBQ4e/A1yIJdlBtqXxJ1KE/ituHRbNc4j4kL8Z\r\n" +"/r6pvwnTI0PSx2Eqs048YdS92LT6qAv4flbNDxMn2uY7s4ycS4Q8w1JXnCeaAnYm\r\n" +"WYI5wxO+bvRELR2Mcz5DmVnL8jRyml6l6582bSv5oufReFIbyPZbQWlXgYnpu6He\r\n" +"GTc7E1zKYQGG/9+DQUl/1vQuCPqQwny0tQoX2w5tdYpdMdVm+zkLtbajzdTviJJa\r\n" +"TWzL6lt5AoGBAN86+SVeJDcmQJcv4Eq6UhtRr4QGMiQMz0Sod6ettYxYzMgxtw28\r\n" +"CIrgpozCc+UaZJLo7UxvC6an85r1b2nKPCLQFaggJ0H4Q0J/sZOhBIXaoBzWxveK\r\n" +"nupceKdVxGsFi8CDy86DBfiyFivfBj+47BbaQzPBj7C4rK7UlLjab2rDAoGBAN2u\r\n" +"AM2gchoFiu4v1HFL8D7lweEpi6ZnMJjnEu/dEgGQJFjwdpLnPbsj4c75odQ4Gz8g\r\n" +"sw9lao9VVzbusoRE/JGI4aTdO0pATXyG7eG1Qu+5Yc1YGXcCrliA2xM9xx+d7f+s\r\n" +"mPzN+WIEg5GJDYZDjAzHG5BNvi/FfM1C9dOtjv2dAoGAF0t5KmwbjWHBhcVqO4Ic\r\n" +"BVvN3BIlc1ue2YRXEDlxY5b0r8N4XceMgKmW18OHApZxfl8uPDauWZLXOgl4uepv\r\n" +"whZC3EuWrSyyICNhLY21Ah7hbIEBPF3L3ZsOwC+UErL+dXWLdB56Jgy3gZaBeW7b\r\n" +"vDrEnocJbqCm7IukhXHOBK8CgYEAwqdHB0hqyNSzIOGY7v9abzB6pUdA3BZiQvEs\r\n" +"3LjHVd4HPJ2x0N8CgrBIWOE0q8+0hSMmeE96WW/7jD3fPWwCR5zlXknxBQsfv0gP\r\n" +"3BC5PR0Qdypz+d+9zfMf625kyit4T/hzwhDveZUzHnk1Cf+IG7Q+TOEnLnWAWBED\r\n" +"ISOWmrUCgYAFEmRxgwAc/u+D6t0syCwAYh6POtscq9Y0i9GyWk89NzgC4NdwwbBH\r\n" +"4AgahOxIxXx2gxJnq3yfkJfIjwf0s2DyP0kY2y6Ua1OeomPeY9mrIS4tCuDQ6LrE\r\n" +"TB6l9VGoxJL4fyHnZb8L5gGvnB1bbD8cL6YPaDiOhcRseC9vBiEuVg==\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; + + +const char test_cli_crt_rsa[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIIDPzCCAiegAwIBAgIBBDANBgkqhkiG9w0BAQUFADA7MQswCQYDVQQGEwJOTDER\r\n" +"MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN\r\n" +"MTEwMjEyMTQ0NDA3WhcNMjEwMjEyMTQ0NDA3WjA8MQswCQYDVQQGEwJOTDERMA8G\r\n" +"A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIENsaWVudCAyMIIBIjAN\r\n" +"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyHTEzLn5tXnpRdkUYLB9u5Pyax6f\r\n" +"M60Nj4o8VmXl3ETZzGaFB9X4J7BKNdBjngpuG7fa8H6r7gwQk4ZJGDTzqCrSV/Uu\r\n" +"1C93KYRhTYJQj6eVSHD1bk2y1RPD0hrt5kPqQhTrdOrA7R/UV06p86jt0uDBMHEw\r\n" +"MjDV0/YI0FZPRo7yX/k9Z5GIMC5Cst99++UMd//sMcB4j7/Cf8qtbCHWjdmLao5v\r\n" +"4Jv4EFbMs44TFeY0BGbH7vk2DmqV9gmaBmf0ZXH4yqSxJeD+PIs1BGe64E92hfx/\r\n" +"/DZrtenNLQNiTrM9AM+vdqBpVoNq0qjU51Bx5rU2BXcFbXvI5MT9TNUhXwIDAQAB\r\n" +"o00wSzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRxoQBzckAvVHZeM/xSj7zx3WtGITAf\r\n" +"BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zANBgkqhkiG9w0BAQUFAAOC\r\n" +"AQEAAn86isAM8X+mVwJqeItt6E9slhEQbAofyk+diH1Lh8Y9iLlWQSKbw/UXYjx5\r\n" +"LLPZcniovxIcARC/BjyZR9g3UwTHNGNm+rwrqa15viuNOFBchykX/Orsk02EH7NR\r\n" +"Alw5WLPorYjED6cdVQgBl9ot93HdJogRiXCxErM7NC8/eP511mjq+uLDjLKH8ZPQ\r\n" +"8I4ekHJnroLsDkIwXKGIsvIBHQy2ac/NwHLCQOK6mfum1pRx52V4Utu5dLLjD5bM\r\n" +"xOBC7KU4xZKuMXXZM6/93Yb51K/J4ahf1TxJlTWXtnzDr9saEYdNy2SKY/6ZiDNH\r\n" +"D+stpAKiQLAWaAusIWKYEyw9MQ==\r\n" +"-----END CERTIFICATE-----\r\n"; + +const char test_cli_key_rsa[] = +"-----BEGIN RSA PRIVATE KEY-----\r\n" +"MIIEpAIBAAKCAQEAyHTEzLn5tXnpRdkUYLB9u5Pyax6fM60Nj4o8VmXl3ETZzGaF\r\n" +"B9X4J7BKNdBjngpuG7fa8H6r7gwQk4ZJGDTzqCrSV/Uu1C93KYRhTYJQj6eVSHD1\r\n" +"bk2y1RPD0hrt5kPqQhTrdOrA7R/UV06p86jt0uDBMHEwMjDV0/YI0FZPRo7yX/k9\r\n" +"Z5GIMC5Cst99++UMd//sMcB4j7/Cf8qtbCHWjdmLao5v4Jv4EFbMs44TFeY0BGbH\r\n" +"7vk2DmqV9gmaBmf0ZXH4yqSxJeD+PIs1BGe64E92hfx//DZrtenNLQNiTrM9AM+v\r\n" +"dqBpVoNq0qjU51Bx5rU2BXcFbXvI5MT9TNUhXwIDAQABAoIBAGdNtfYDiap6bzst\r\n" +"yhCiI8m9TtrhZw4MisaEaN/ll3XSjaOG2dvV6xMZCMV+5TeXDHOAZnY18Yi18vzz\r\n" +"4Ut2TnNFzizCECYNaA2fST3WgInnxUkV3YXAyP6CNxJaCmv2aA0yFr2kFVSeaKGt\r\n" +"ymvljNp2NVkvm7Th8fBQBO7I7AXhz43k0mR7XmPgewe8ApZOG3hstkOaMvbWAvWA\r\n" +"zCZupdDjZYjOJqlA4eEA4H8/w7F83r5CugeBE8LgEREjLPiyejrU5H1fubEY+h0d\r\n" +"l5HZBJ68ybTXfQ5U9o/QKA3dd0toBEhhdRUDGzWtjvwkEQfqF1reGWj/tod/gCpf\r\n" +"DFi6X0ECgYEA4wOv/pjSC3ty6TuOvKX2rOUiBrLXXv2JSxZnMoMiWI5ipLQt+RYT\r\n" +"VPafL/m7Dn6MbwjayOkcZhBwk5CNz5A6Q4lJ64Mq/lqHznRCQQ2Mc1G8eyDF/fYL\r\n" +"Ze2pLvwP9VD5jTc2miDfw+MnvJhywRRLcemDFP8k4hQVtm8PMp3ZmNECgYEA4gz7\r\n" +"wzObR4gn8ibe617uQPZjWzUj9dUHYd+in1gwBCIrtNnaRn9I9U/Q6tegRYpii4ys\r\n" +"c176NmU+umy6XmuSKV5qD9bSpZWG2nLFnslrN15Lm3fhZxoeMNhBaEDTnLT26yoi\r\n" +"33gp0mSSWy94ZEqipms+ULF6sY1ZtFW6tpGFoy8CgYAQHhnnvJflIs2ky4q10B60\r\n" +"ZcxFp3rtDpkp0JxhFLhiizFrujMtZSjYNm5U7KkgPVHhLELEUvCmOnKTt4ap/vZ0\r\n" +"BxJNe1GZH3pW6SAvGDQpl9sG7uu/vTFP+lCxukmzxB0DrrDcvorEkKMom7ZCCRvW\r\n" +"KZsZ6YeH2Z81BauRj218kQKBgQCUV/DgKP2985xDTT79N08jUo3hTP5MVYCCuj/+\r\n" +"UeEw1TvZcx3LJby7P6Xad6a1/BqveaGyFKIfEFIaBUBItk801sDDpDaYc4gL00Xc\r\n" +"7lFuBHOZkxJYlss5QrGpuOEl9ZwUt5IrFLBdYaKqNHzNVC1pCPfb/JyH6Dr2HUxq\r\n" +"gxUwAQKBgQCcU6G2L8AG9d9c0UpOyL1tMvFe5Ttw0KjlQVdsh1MP6yigYo9DYuwu\r\n" +"bHFVW2r0dBTqegP2/KTOxKzaHfC1qf0RGDsUoJCNJrd1cwoCLG8P2EF4w3OBrKqv\r\n" +"8u4ytY0F+Vlanj5lm3TaoHSVF1+NWPyOTiwevIECGKwSxvlki4fDAA==\r\n" +"-----END RSA PRIVATE KEY-----\r\n"; +#else +#define TEST_CA_CRT_RSA +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_DHM_C) +const char test_dhm_params[] = +"-----BEGIN DH PARAMETERS-----\r\n" +"MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh\r\n" +"1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32\r\n" +"9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC\r\n" +"-----END DH PARAMETERS-----\r\n"; +#endif + +/* Concatenation of all available CA certificates */ +const char test_ca_list[] = TEST_CA_CRT_RSA TEST_CA_CRT_EC; + +#if defined(POLARSSL_RSA_C) +const char *test_ca_crt = test_ca_crt_rsa; +const char *test_ca_key = test_ca_key_rsa; +const char *test_ca_pwd = test_ca_pwd_rsa; +const char *test_srv_crt = test_srv_crt_rsa; +const char *test_srv_key = test_srv_key_rsa; +const char *test_cli_crt = test_cli_crt_rsa; +const char *test_cli_key = test_cli_key_rsa; +#else /* ! POLARSSL_RSA_C, so POLARSSL_ECDSA_C */ +const char *test_ca_crt = test_ca_crt_ec; +const char *test_ca_key = test_ca_key_ec; +const char *test_ca_pwd = test_ca_pwd_ec; +const char *test_srv_crt = test_srv_crt_ec; +const char *test_srv_key = test_srv_key_ec; +const char *test_cli_crt = test_cli_crt_ec; +const char *test_cli_key = test_cli_key_ec; +#endif /* POLARSSL_RSA_C */ + +#endif /* POLARSSL_CERTS_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/cipher.c b/component/common/network/ssl/polarssl-1.3.8/library/cipher.c new file mode 100644 index 0000000..5cd30f8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/cipher.c @@ -0,0 +1,917 @@ +/** + * \file cipher.c + * + * \brief Generic cipher wrapper for PolarSSL + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CIPHER_C) + +#include "polarssl/cipher.h" +#include "polarssl/cipher_wrap.h" + +#if defined(POLARSSL_GCM_C) +#include "polarssl/gcm.h" +#endif + +#if defined(POLARSSL_CCM_C) +#include "polarssl/ccm.h" +#endif + +#include + +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) +#define POLARSSL_CIPHER_MODE_STREAM +#endif + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +static int supported_init = 0; + +const int *cipher_list( void ) +{ + const cipher_definition_t *def; + int *type; + + if( ! supported_init ) + { + def = cipher_definitions; + type = supported_ciphers; + + while( def->type != 0 ) + *type++ = (*def++).type; + + *type = 0; + + supported_init = 1; + } + + return( supported_ciphers ); +} + +const cipher_info_t *cipher_info_from_type( const cipher_type_t cipher_type ) +{ + const cipher_definition_t *def; + + for( def = cipher_definitions; def->info != NULL; def++ ) + if( def->type == cipher_type ) + return( def->info ); + + return( NULL ); +} + +const cipher_info_t *cipher_info_from_string( const char *cipher_name ) +{ + const cipher_definition_t *def; + + if( NULL == cipher_name ) + return( NULL ); + + for( def = cipher_definitions; def->info != NULL; def++ ) + if( ! strcasecmp( def->info->name, cipher_name ) ) + return( def->info ); + + return( NULL ); +} + +const cipher_info_t *cipher_info_from_values( const cipher_id_t cipher_id, + int key_length, + const cipher_mode_t mode ) +{ + const cipher_definition_t *def; + + for( def = cipher_definitions; def->info != NULL; def++ ) + if( def->info->base->cipher == cipher_id && + def->info->key_length == (unsigned) key_length && + def->info->mode == mode ) + return( def->info ); + + return( NULL ); +} + +void cipher_init( cipher_context_t *ctx ) +{ + memset( ctx, 0, sizeof( cipher_context_t ) ); +} + +void cipher_free( cipher_context_t *ctx ) +{ + if( ctx == NULL ) + return; + + if( ctx->cipher_ctx ) + ctx->cipher_info->base->ctx_free_func( ctx->cipher_ctx ); + + polarssl_zeroize( ctx, sizeof(cipher_context_t) ); +} + +int cipher_init_ctx( cipher_context_t *ctx, const cipher_info_t *cipher_info ) +{ + if( NULL == cipher_info || NULL == ctx ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + memset( ctx, 0, sizeof( cipher_context_t ) ); + + if( NULL == ( ctx->cipher_ctx = cipher_info->base->ctx_alloc_func() ) ) + return( POLARSSL_ERR_CIPHER_ALLOC_FAILED ); + + ctx->cipher_info = cipher_info; + +#if defined(POLARSSL_CIPHER_MODE_WITH_PADDING) + /* + * Ignore possible errors caused by a cipher mode that doesn't use padding + */ +#if defined(POLARSSL_CIPHER_PADDING_PKCS7) + (void) cipher_set_padding_mode( ctx, POLARSSL_PADDING_PKCS7 ); +#else + (void) cipher_set_padding_mode( ctx, POLARSSL_PADDING_NONE ); +#endif +#endif /* POLARSSL_CIPHER_MODE_WITH_PADDING */ + + return( 0 ); +} + +/* Deprecated, redirects to cipher_free() */ +int cipher_free_ctx( cipher_context_t *ctx ) +{ + cipher_free( ctx ); + + return( 0 ); +} + +int cipher_setkey( cipher_context_t *ctx, const unsigned char *key, + int key_length, const operation_t operation ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + if( ( ctx->cipher_info->flags & POLARSSL_CIPHER_VARIABLE_KEY_LEN ) == 0 && + (int) ctx->cipher_info->key_length != key_length ) + { + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + + ctx->key_length = key_length; + ctx->operation = operation; + + /* + * For CFB and CTR mode always use the encryption key schedule + */ + if( POLARSSL_ENCRYPT == operation || + POLARSSL_MODE_CFB == ctx->cipher_info->mode || + POLARSSL_MODE_CTR == ctx->cipher_info->mode ) + { + return ctx->cipher_info->base->setkey_enc_func( ctx->cipher_ctx, key, + ctx->key_length ); + } + + if( POLARSSL_DECRYPT == operation ) + return ctx->cipher_info->base->setkey_dec_func( ctx->cipher_ctx, key, + ctx->key_length ); + + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); +} + +int cipher_set_iv( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len ) +{ + size_t actual_iv_size; + + if( NULL == ctx || NULL == ctx->cipher_info || NULL == iv ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + /* avoid buffer overflow in ctx->iv */ + if( iv_len > POLARSSL_MAX_IV_LENGTH ) + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); + + if( ( ctx->cipher_info->flags & POLARSSL_CIPHER_VARIABLE_IV_LEN ) != 0 ) + actual_iv_size = iv_len; + else + { + actual_iv_size = ctx->cipher_info->iv_size; + + /* avoid reading past the end of input buffer */ + if( actual_iv_size > iv_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + + memcpy( ctx->iv, iv, actual_iv_size ); + ctx->iv_size = actual_iv_size; + + return( 0 ); +} + +int cipher_reset( cipher_context_t *ctx ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + ctx->unprocessed_len = 0; + + return( 0 ); +} + +#if defined(POLARSSL_GCM_C) +int cipher_update_ad( cipher_context_t *ctx, + const unsigned char *ad, size_t ad_len ) +{ + if( NULL == ctx || NULL == ctx->cipher_info ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) + { + return gcm_starts( (gcm_context *) ctx->cipher_ctx, ctx->operation, + ctx->iv, ctx->iv_size, ad, ad_len ); + } + + return( 0 ); +} +#endif /* POLARSSL_GCM_C */ + +int cipher_update( cipher_context_t *ctx, const unsigned char *input, + size_t ilen, unsigned char *output, size_t *olen ) +{ + int ret; + + if( NULL == ctx || NULL == ctx->cipher_info || NULL == olen ) + { + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + + *olen = 0; + + if( ctx->cipher_info->mode == POLARSSL_MODE_ECB ) + { + if( ilen != cipher_get_block_size( ctx ) ) + return( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED ); + + *olen = ilen; + + if( 0 != ( ret = ctx->cipher_info->base->ecb_func( ctx->cipher_ctx, + ctx->operation, input, output ) ) ) + { + return( ret ); + } + + return( 0 ); + } + +#if defined(POLARSSL_GCM_C) + if( ctx->cipher_info->mode == POLARSSL_MODE_GCM ) + { + *olen = ilen; + return gcm_update( (gcm_context *) ctx->cipher_ctx, ilen, input, + output ); + } +#endif + + if( input == output && + ( ctx->unprocessed_len != 0 || ilen % cipher_get_block_size( ctx ) ) ) + { + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + +#if defined(POLARSSL_CIPHER_MODE_CBC) + if( ctx->cipher_info->mode == POLARSSL_MODE_CBC ) + { + size_t copy_len = 0; + + /* + * If there is not enough data for a full block, cache it. + */ + if( ( ctx->operation == POLARSSL_DECRYPT && + ilen + ctx->unprocessed_len <= cipher_get_block_size( ctx ) ) || + ( ctx->operation == POLARSSL_ENCRYPT && + ilen + ctx->unprocessed_len < cipher_get_block_size( ctx ) ) ) + { + memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input, + ilen ); + + ctx->unprocessed_len += ilen; + return( 0 ); + } + + /* + * Process cached data first + */ + if( ctx->unprocessed_len != 0 ) + { + copy_len = cipher_get_block_size( ctx ) - ctx->unprocessed_len; + + memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ), input, + copy_len ); + + if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, + ctx->operation, cipher_get_block_size( ctx ), ctx->iv, + ctx->unprocessed_data, output ) ) ) + { + return( ret ); + } + + *olen += cipher_get_block_size( ctx ); + output += cipher_get_block_size( ctx ); + ctx->unprocessed_len = 0; + + input += copy_len; + ilen -= copy_len; + } + + /* + * Cache final, incomplete block + */ + if( 0 != ilen ) + { + copy_len = ilen % cipher_get_block_size( ctx ); + if( copy_len == 0 && ctx->operation == POLARSSL_DECRYPT ) + copy_len = cipher_get_block_size( ctx ); + + memcpy( ctx->unprocessed_data, &( input[ilen - copy_len] ), + copy_len ); + + ctx->unprocessed_len += copy_len; + ilen -= copy_len; + } + + /* + * Process remaining full blocks + */ + if( ilen ) + { + if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, + ctx->operation, ilen, ctx->iv, input, output ) ) ) + { + return( ret ); + } + + *olen += ilen; + } + + return( 0 ); + } +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) + if( ctx->cipher_info->mode == POLARSSL_MODE_CFB ) + { + if( 0 != ( ret = ctx->cipher_info->base->cfb_func( ctx->cipher_ctx, + ctx->operation, ilen, &ctx->unprocessed_len, ctx->iv, + input, output ) ) ) + { + return( ret ); + } + + *olen = ilen; + + return( 0 ); + } +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) + if( ctx->cipher_info->mode == POLARSSL_MODE_CTR ) + { + if( 0 != ( ret = ctx->cipher_info->base->ctr_func( ctx->cipher_ctx, + ilen, &ctx->unprocessed_len, ctx->iv, + ctx->unprocessed_data, input, output ) ) ) + { + return( ret ); + } + + *olen = ilen; + + return( 0 ); + } +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#if defined(POLARSSL_CIPHER_MODE_STREAM) + if( ctx->cipher_info->mode == POLARSSL_MODE_STREAM ) + { + if( 0 != ( ret = ctx->cipher_info->base->stream_func( ctx->cipher_ctx, + ilen, input, output ) ) ) + { + return( ret ); + } + + *olen = ilen; + + return( 0 ); + } +#endif /* POLARSSL_CIPHER_MODE_STREAM */ + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +} + +#if defined(POLARSSL_CIPHER_MODE_WITH_PADDING) +#if defined(POLARSSL_CIPHER_PADDING_PKCS7) +/* + * PKCS7 (and PKCS5) padding: fill with ll bytes, with ll = padding_len + */ +static void add_pkcs_padding( unsigned char *output, size_t output_len, + size_t data_len ) +{ + size_t padding_len = output_len - data_len; + unsigned char i; + + for( i = 0; i < padding_len; i++ ) + output[data_len + i] = (unsigned char) padding_len; +} + +static int get_pkcs_padding( unsigned char *input, size_t input_len, + size_t *data_len ) +{ + size_t i, pad_idx; + unsigned char padding_len, bad = 0; + + if( NULL == input || NULL == data_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + padding_len = input[input_len - 1]; + *data_len = input_len - padding_len; + + /* Avoid logical || since it results in a branch */ + bad |= padding_len > input_len; + bad |= padding_len == 0; + + /* The number of bytes checked must be independent of padding_len, + * so pick input_len, which is usually 8 or 16 (one block) */ + pad_idx = input_len - padding_len; + for( i = 0; i < input_len; i++ ) + bad |= ( input[i] ^ padding_len ) * ( i >= pad_idx ); + + return( POLARSSL_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) ); +} +#endif /* POLARSSL_CIPHER_PADDING_PKCS7 */ + +#if defined(POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS) +/* + * One and zeros padding: fill with 80 00 ... 00 + */ +static void add_one_and_zeros_padding( unsigned char *output, + size_t output_len, size_t data_len ) +{ + size_t padding_len = output_len - data_len; + unsigned char i = 0; + + output[data_len] = 0x80; + for( i = 1; i < padding_len; i++ ) + output[data_len + i] = 0x00; +} + +static int get_one_and_zeros_padding( unsigned char *input, size_t input_len, + size_t *data_len ) +{ + size_t i; + unsigned char done = 0, prev_done, bad; + + if( NULL == input || NULL == data_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + bad = 0xFF; + *data_len = 0; + for( i = input_len; i > 0; i-- ) + { + prev_done = done; + done |= ( input[i-1] != 0 ); + *data_len |= ( i - 1 ) * ( done != prev_done ); + bad &= ( input[i-1] ^ 0x80 ) | ( done == prev_done ); + } + + return( POLARSSL_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) ); + +} +#endif /* POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS */ + +#if defined(POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN) +/* + * Zeros and len padding: fill with 00 ... 00 ll, where ll is padding length + */ +static void add_zeros_and_len_padding( unsigned char *output, + size_t output_len, size_t data_len ) +{ + size_t padding_len = output_len - data_len; + unsigned char i = 0; + + for( i = 1; i < padding_len; i++ ) + output[data_len + i - 1] = 0x00; + output[output_len - 1] = (unsigned char) padding_len; +} + +static int get_zeros_and_len_padding( unsigned char *input, size_t input_len, + size_t *data_len ) +{ + size_t i, pad_idx; + unsigned char padding_len, bad = 0; + + if( NULL == input || NULL == data_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + padding_len = input[input_len - 1]; + *data_len = input_len - padding_len; + + /* Avoid logical || since it results in a branch */ + bad |= padding_len > input_len; + bad |= padding_len == 0; + + /* The number of bytes checked must be independent of padding_len */ + pad_idx = input_len - padding_len; + for( i = 0; i < input_len - 1; i++ ) + bad |= input[i] * ( i >= pad_idx ); + + return( POLARSSL_ERR_CIPHER_INVALID_PADDING * ( bad != 0 ) ); +} +#endif /* POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN */ + +#if defined(POLARSSL_CIPHER_PADDING_ZEROS) +/* + * Zero padding: fill with 00 ... 00 + */ +static void add_zeros_padding( unsigned char *output, + size_t output_len, size_t data_len ) +{ + size_t i; + + for( i = data_len; i < output_len; i++ ) + output[i] = 0x00; +} + +static int get_zeros_padding( unsigned char *input, size_t input_len, + size_t *data_len ) +{ + size_t i; + unsigned char done = 0, prev_done; + + if( NULL == input || NULL == data_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + *data_len = 0; + for( i = input_len; i > 0; i-- ) + { + prev_done = done; + done |= ( input[i-1] != 0 ); + *data_len |= i * ( done != prev_done ); + } + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_PADDING_ZEROS */ + +/* + * No padding: don't pad :) + * + * There is no add_padding function (check for NULL in cipher_finish) + * but a trivial get_padding function + */ +static int get_no_padding( unsigned char *input, size_t input_len, + size_t *data_len ) +{ + if( NULL == input || NULL == data_len ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + *data_len = input_len; + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_WITH_PADDING */ + +int cipher_finish( cipher_context_t *ctx, + unsigned char *output, size_t *olen ) +{ + if( NULL == ctx || NULL == ctx->cipher_info || NULL == olen ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + *olen = 0; + + if( POLARSSL_MODE_CFB == ctx->cipher_info->mode || + POLARSSL_MODE_CTR == ctx->cipher_info->mode || + POLARSSL_MODE_GCM == ctx->cipher_info->mode || + POLARSSL_MODE_STREAM == ctx->cipher_info->mode ) + { + return( 0 ); + } + + if( POLARSSL_MODE_ECB == ctx->cipher_info->mode ) + { + if( ctx->unprocessed_len != 0 ) + return( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED ); + + return( 0 ); + } + +#if defined(POLARSSL_CIPHER_MODE_CBC) + if( POLARSSL_MODE_CBC == ctx->cipher_info->mode ) + { + int ret = 0; + + if( POLARSSL_ENCRYPT == ctx->operation ) + { + /* check for 'no padding' mode */ + if( NULL == ctx->add_padding ) + { + if( 0 != ctx->unprocessed_len ) + return( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED ); + + return( 0 ); + } + + ctx->add_padding( ctx->unprocessed_data, cipher_get_iv_size( ctx ), + ctx->unprocessed_len ); + } + else if( cipher_get_block_size( ctx ) != ctx->unprocessed_len ) + { + /* + * For decrypt operations, expect a full block, + * or an empty block if no padding + */ + if( NULL == ctx->add_padding && 0 == ctx->unprocessed_len ) + return( 0 ); + + return( POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED ); + } + + /* cipher block */ + if( 0 != ( ret = ctx->cipher_info->base->cbc_func( ctx->cipher_ctx, + ctx->operation, cipher_get_block_size( ctx ), ctx->iv, + ctx->unprocessed_data, output ) ) ) + { + return( ret ); + } + + /* Set output size for decryption */ + if( POLARSSL_DECRYPT == ctx->operation ) + return ctx->get_padding( output, cipher_get_block_size( ctx ), + olen ); + + /* Set output size for encryption */ + *olen = cipher_get_block_size( ctx ); + return( 0 ); + } +#else + ((void) output); +#endif /* POLARSSL_CIPHER_MODE_CBC */ + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +} + +#if defined(POLARSSL_CIPHER_MODE_WITH_PADDING) +int cipher_set_padding_mode( cipher_context_t *ctx, cipher_padding_t mode ) +{ + if( NULL == ctx || + POLARSSL_MODE_CBC != ctx->cipher_info->mode ) + { + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + + switch( mode ) + { +#if defined(POLARSSL_CIPHER_PADDING_PKCS7) + case POLARSSL_PADDING_PKCS7: + ctx->add_padding = add_pkcs_padding; + ctx->get_padding = get_pkcs_padding; + break; +#endif +#if defined(POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS) + case POLARSSL_PADDING_ONE_AND_ZEROS: + ctx->add_padding = add_one_and_zeros_padding; + ctx->get_padding = get_one_and_zeros_padding; + break; +#endif +#if defined(POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN) + case POLARSSL_PADDING_ZEROS_AND_LEN: + ctx->add_padding = add_zeros_and_len_padding; + ctx->get_padding = get_zeros_and_len_padding; + break; +#endif +#if defined(POLARSSL_CIPHER_PADDING_ZEROS) + case POLARSSL_PADDING_ZEROS: + ctx->add_padding = add_zeros_padding; + ctx->get_padding = get_zeros_padding; + break; +#endif + case POLARSSL_PADDING_NONE: + ctx->add_padding = NULL; + ctx->get_padding = get_no_padding; + break; + + default: + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); + } + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_WITH_PADDING */ + +#if defined(POLARSSL_GCM_C) +int cipher_write_tag( cipher_context_t *ctx, + unsigned char *tag, size_t tag_len ) +{ + if( NULL == ctx || NULL == ctx->cipher_info || NULL == tag ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + if( POLARSSL_ENCRYPT != ctx->operation ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) + return gcm_finish( (gcm_context *) ctx->cipher_ctx, tag, tag_len ); + + return( 0 ); +} + +int cipher_check_tag( cipher_context_t *ctx, + const unsigned char *tag, size_t tag_len ) +{ + int ret; + + if( NULL == ctx || NULL == ctx->cipher_info || + POLARSSL_DECRYPT != ctx->operation ) + { + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + } + + if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) + { + unsigned char check_tag[16]; + size_t i; + int diff; + + if( tag_len > sizeof( check_tag ) ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + if( 0 != ( ret = gcm_finish( (gcm_context *) ctx->cipher_ctx, + check_tag, tag_len ) ) ) + { + return( ret ); + } + + /* Check the tag in "constant-time" */ + for( diff = 0, i = 0; i < tag_len; i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) + return( POLARSSL_ERR_CIPHER_AUTH_FAILED ); + + return( 0 ); + } + + return( 0 ); +} +#endif /* POLARSSL_GCM_C */ + +/* + * Packet-oriented wrapper for non-AEAD modes + */ +int cipher_crypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen ) +{ + int ret; + size_t finish_olen; + + if( ( ret = cipher_set_iv( ctx, iv, iv_len ) ) != 0 ) + return( ret ); + + if( ( ret = cipher_reset( ctx ) ) != 0 ) + return( ret ); + + if( ( ret = cipher_update( ctx, input, ilen, output, olen ) ) != 0 ) + return( ret ); + + if( ( ret = cipher_finish( ctx, output + *olen, &finish_olen ) ) != 0 ) + return( ret ); + + *olen += finish_olen; + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_AEAD) +/* + * Packet-oriented encryption for AEAD modes + */ +int cipher_auth_encrypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + unsigned char *tag, size_t tag_len ) +{ +#if defined(POLARSSL_GCM_C) + if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) + { + *olen = ilen; + return( gcm_crypt_and_tag( ctx->cipher_ctx, GCM_ENCRYPT, ilen, + iv, iv_len, ad, ad_len, input, output, + tag_len, tag ) ); + } +#endif /* POLARSSL_GCM_C */ +#if defined(POLARSSL_CCM_C) + if( POLARSSL_MODE_CCM == ctx->cipher_info->mode ) + { + *olen = ilen; + return( ccm_encrypt_and_tag( ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, input, output, + tag, tag_len ) ); + } +#endif /* POLARSSL_CCM_C */ + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +} + +/* + * Packet-oriented decryption for AEAD modes + */ +int cipher_auth_decrypt( cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + const unsigned char *tag, size_t tag_len ) +{ +#if defined(POLARSSL_GCM_C) + if( POLARSSL_MODE_GCM == ctx->cipher_info->mode ) + { + int ret; + + *olen = ilen; + ret = gcm_auth_decrypt( ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, + tag, tag_len, input, output ); + + if( ret == POLARSSL_ERR_GCM_AUTH_FAILED ) + ret = POLARSSL_ERR_CIPHER_AUTH_FAILED; + + return( ret ); + } +#endif /* POLARSSL_GCM_C */ +#if defined(POLARSSL_CCM_C) + if( POLARSSL_MODE_CCM == ctx->cipher_info->mode ) + { + int ret; + + *olen = ilen; + ret = ccm_auth_decrypt( ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, + input, output, tag, tag_len ); + + if( ret == POLARSSL_ERR_CCM_AUTH_FAILED ) + ret = POLARSSL_ERR_CIPHER_AUTH_FAILED; + + return( ret ); + } +#endif /* POLARSSL_CCM_C */ + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +} +#endif /* POLARSSL_CIPHER_MODE_AEAD */ + + +#if defined(POLARSSL_SELF_TEST) + +/* + * Checkup routine + */ +int cipher_self_test( int verbose ) +{ + ((void) verbose); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_CIPHER_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/cipher_wrap.c b/component/common/network/ssl/polarssl-1.3.8/library/cipher_wrap.c new file mode 100644 index 0000000..27aa3db --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/cipher_wrap.c @@ -0,0 +1,1451 @@ +/** + * \file cipher_wrap.c + * + * \brief Generic cipher wrapper for PolarSSL + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CIPHER_C) + +#include "polarssl/cipher_wrap.h" + +#if defined(POLARSSL_AES_C) +#include "polarssl/aes.h" +#endif + +#if defined(POLARSSL_ARC4_C) +#include "polarssl/arc4.h" +#endif + +#if defined(POLARSSL_CAMELLIA_C) +#include "polarssl/camellia.h" +#endif + +#if defined(POLARSSL_DES_C) +#include "polarssl/des.h" +#endif + +#if defined(POLARSSL_BLOWFISH_C) +#include "polarssl/blowfish.h" +#endif + +#if defined(POLARSSL_GCM_C) +#include "polarssl/gcm.h" +#endif + +#if defined(POLARSSL_CCM_C) +#include "polarssl/ccm.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +#if defined(POLARSSL_GCM_C) +/* shared by all GCM ciphers */ +static void *gcm_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( gcm_context ) ); +} + +static void gcm_ctx_free( void *ctx ) +{ + gcm_free( ctx ); + polarssl_free( ctx ); +} +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CCM_C) +/* shared by all CCM ciphers */ +static void *ccm_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( ccm_context ) ); +} + +static void ccm_ctx_free( void *ctx ) +{ + ccm_free( ctx ); + polarssl_free( ctx ); +} +#endif /* POLARSSL_CCM_C */ + +#if defined(POLARSSL_AES_C) + +static int aes_crypt_ecb_wrap( void *ctx, operation_t operation, + const unsigned char *input, unsigned char *output ) +{ + return aes_crypt_ecb( (aes_context *) ctx, operation, input, output ); +} + +static int aes_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CBC) + return aes_crypt_cbc( (aes_context *) ctx, operation, length, iv, input, + output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ +} + +static int aes_crypt_cfb128_wrap( void *ctx, operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CFB) + return aes_crypt_cfb128( (aes_context *) ctx, operation, length, iv_off, iv, + input, output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv_off); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ +} + +static int aes_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CTR) + return aes_crypt_ctr( (aes_context *) ctx, length, nc_off, nonce_counter, + stream_block, input, output ); +#else + ((void) ctx); + ((void) length); + ((void) nc_off); + ((void) nonce_counter); + ((void) stream_block); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ +} + +static int aes_setkey_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return aes_setkey_dec( (aes_context *) ctx, key, key_length ); +} + +static int aes_setkey_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return aes_setkey_enc( (aes_context *) ctx, key, key_length ); +} + +static void * aes_ctx_alloc( void ) +{ + aes_context *aes = (aes_context *) polarssl_malloc( sizeof( aes_context ) ); + + if( aes == NULL ) + return( NULL ); + + aes_init( aes ); + + return( aes ); +} + +static void aes_ctx_free( void *ctx ) +{ + aes_free( (aes_context *) ctx ); + polarssl_free( ctx ); +} + +const cipher_base_t aes_info = { + POLARSSL_CIPHER_ID_AES, + aes_crypt_ecb_wrap, + aes_crypt_cbc_wrap, + aes_crypt_cfb128_wrap, + aes_crypt_ctr_wrap, + NULL, + aes_setkey_enc_wrap, + aes_setkey_dec_wrap, + aes_ctx_alloc, + aes_ctx_free +}; + +const cipher_info_t aes_128_ecb_info = { + POLARSSL_CIPHER_AES_128_ECB, + POLARSSL_MODE_ECB, + 128, + "AES-128-ECB", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_192_ecb_info = { + POLARSSL_CIPHER_AES_192_ECB, + POLARSSL_MODE_ECB, + 192, + "AES-192-ECB", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_256_ecb_info = { + POLARSSL_CIPHER_AES_256_ECB, + POLARSSL_MODE_ECB, + 256, + "AES-256-ECB", + 16, + 0, + 16, + &aes_info +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t aes_128_cbc_info = { + POLARSSL_CIPHER_AES_128_CBC, + POLARSSL_MODE_CBC, + 128, + "AES-128-CBC", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_192_cbc_info = { + POLARSSL_CIPHER_AES_192_CBC, + POLARSSL_MODE_CBC, + 192, + "AES-192-CBC", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_256_cbc_info = { + POLARSSL_CIPHER_AES_256_CBC, + POLARSSL_MODE_CBC, + 256, + "AES-256-CBC", + 16, + 0, + 16, + &aes_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +const cipher_info_t aes_128_cfb128_info = { + POLARSSL_CIPHER_AES_128_CFB128, + POLARSSL_MODE_CFB, + 128, + "AES-128-CFB128", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_192_cfb128_info = { + POLARSSL_CIPHER_AES_192_CFB128, + POLARSSL_MODE_CFB, + 192, + "AES-192-CFB128", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_256_cfb128_info = { + POLARSSL_CIPHER_AES_256_CFB128, + POLARSSL_MODE_CFB, + 256, + "AES-256-CFB128", + 16, + 0, + 16, + &aes_info +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +const cipher_info_t aes_128_ctr_info = { + POLARSSL_CIPHER_AES_128_CTR, + POLARSSL_MODE_CTR, + 128, + "AES-128-CTR", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_192_ctr_info = { + POLARSSL_CIPHER_AES_192_CTR, + POLARSSL_MODE_CTR, + 192, + "AES-192-CTR", + 16, + 0, + 16, + &aes_info +}; + +const cipher_info_t aes_256_ctr_info = { + POLARSSL_CIPHER_AES_256_CTR, + POLARSSL_MODE_CTR, + 256, + "AES-256-CTR", + 16, + 0, + 16, + &aes_info +}; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#if defined(POLARSSL_GCM_C) +static int gcm_aes_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return gcm_init( (gcm_context *) ctx, POLARSSL_CIPHER_ID_AES, + key, key_length ); +} + +const cipher_base_t gcm_aes_info = { + POLARSSL_CIPHER_ID_AES, + NULL, + NULL, + NULL, + NULL, + NULL, + gcm_aes_setkey_wrap, + gcm_aes_setkey_wrap, + gcm_ctx_alloc, + gcm_ctx_free, +}; + +const cipher_info_t aes_128_gcm_info = { + POLARSSL_CIPHER_AES_128_GCM, + POLARSSL_MODE_GCM, + 128, + "AES-128-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; + +const cipher_info_t aes_192_gcm_info = { + POLARSSL_CIPHER_AES_192_GCM, + POLARSSL_MODE_GCM, + 192, + "AES-192-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; + +const cipher_info_t aes_256_gcm_info = { + POLARSSL_CIPHER_AES_256_GCM, + POLARSSL_MODE_GCM, + 256, + "AES-256-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CCM_C) +static int ccm_aes_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return ccm_init( (ccm_context *) ctx, POLARSSL_CIPHER_ID_AES, + key, key_length ); +} + +const cipher_base_t ccm_aes_info = { + POLARSSL_CIPHER_ID_AES, + NULL, + NULL, + NULL, + NULL, + NULL, + ccm_aes_setkey_wrap, + ccm_aes_setkey_wrap, + ccm_ctx_alloc, + ccm_ctx_free, +}; + +const cipher_info_t aes_128_ccm_info = { + POLARSSL_CIPHER_AES_128_CCM, + POLARSSL_MODE_CCM, + 128, + "AES-128-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +const cipher_info_t aes_192_ccm_info = { + POLARSSL_CIPHER_AES_192_CCM, + POLARSSL_MODE_CCM, + 192, + "AES-192-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +const cipher_info_t aes_256_ccm_info = { + POLARSSL_CIPHER_AES_256_CCM, + POLARSSL_MODE_CCM, + 256, + "AES-256-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; +#endif /* POLARSSL_CCM_C */ + +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) + +static int camellia_crypt_ecb_wrap( void *ctx, operation_t operation, + const unsigned char *input, unsigned char *output ) +{ + return camellia_crypt_ecb( (camellia_context *) ctx, operation, input, + output ); +} + +static int camellia_crypt_cbc_wrap( void *ctx, operation_t operation, + size_t length, unsigned char *iv, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CBC) + return camellia_crypt_cbc( (camellia_context *) ctx, operation, length, iv, + input, output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ +} + +static int camellia_crypt_cfb128_wrap( void *ctx, operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CFB) + return camellia_crypt_cfb128( (camellia_context *) ctx, operation, length, + iv_off, iv, input, output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv_off); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ +} + +static int camellia_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CTR) + return camellia_crypt_ctr( (camellia_context *) ctx, length, nc_off, + nonce_counter, stream_block, input, output ); +#else + ((void) ctx); + ((void) length); + ((void) nc_off); + ((void) nonce_counter); + ((void) stream_block); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ +} + +static int camellia_setkey_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return camellia_setkey_dec( (camellia_context *) ctx, key, key_length ); +} + +static int camellia_setkey_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return camellia_setkey_enc( (camellia_context *) ctx, key, key_length ); +} + +static void * camellia_ctx_alloc( void ) +{ + camellia_context *ctx; + ctx = (camellia_context *) polarssl_malloc( sizeof( camellia_context ) ); + + if( ctx == NULL ) + return( NULL ); + + camellia_init( ctx ); + + return( ctx ); +} + +static void camellia_ctx_free( void *ctx ) +{ + camellia_free( (camellia_context *) ctx ); + polarssl_free( ctx ); +} + +const cipher_base_t camellia_info = { + POLARSSL_CIPHER_ID_CAMELLIA, + camellia_crypt_ecb_wrap, + camellia_crypt_cbc_wrap, + camellia_crypt_cfb128_wrap, + camellia_crypt_ctr_wrap, + NULL, + camellia_setkey_enc_wrap, + camellia_setkey_dec_wrap, + camellia_ctx_alloc, + camellia_ctx_free +}; + +const cipher_info_t camellia_128_ecb_info = { + POLARSSL_CIPHER_CAMELLIA_128_ECB, + POLARSSL_MODE_ECB, + 128, + "CAMELLIA-128-ECB", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_192_ecb_info = { + POLARSSL_CIPHER_CAMELLIA_192_ECB, + POLARSSL_MODE_ECB, + 192, + "CAMELLIA-192-ECB", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_256_ecb_info = { + POLARSSL_CIPHER_CAMELLIA_256_ECB, + POLARSSL_MODE_ECB, + 256, + "CAMELLIA-256-ECB", + 16, + 0, + 16, + &camellia_info +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t camellia_128_cbc_info = { + POLARSSL_CIPHER_CAMELLIA_128_CBC, + POLARSSL_MODE_CBC, + 128, + "CAMELLIA-128-CBC", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_192_cbc_info = { + POLARSSL_CIPHER_CAMELLIA_192_CBC, + POLARSSL_MODE_CBC, + 192, + "CAMELLIA-192-CBC", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_256_cbc_info = { + POLARSSL_CIPHER_CAMELLIA_256_CBC, + POLARSSL_MODE_CBC, + 256, + "CAMELLIA-256-CBC", + 16, + 0, + 16, + &camellia_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +const cipher_info_t camellia_128_cfb128_info = { + POLARSSL_CIPHER_CAMELLIA_128_CFB128, + POLARSSL_MODE_CFB, + 128, + "CAMELLIA-128-CFB128", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_192_cfb128_info = { + POLARSSL_CIPHER_CAMELLIA_192_CFB128, + POLARSSL_MODE_CFB, + 192, + "CAMELLIA-192-CFB128", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_256_cfb128_info = { + POLARSSL_CIPHER_CAMELLIA_256_CFB128, + POLARSSL_MODE_CFB, + 256, + "CAMELLIA-256-CFB128", + 16, + 0, + 16, + &camellia_info +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +const cipher_info_t camellia_128_ctr_info = { + POLARSSL_CIPHER_CAMELLIA_128_CTR, + POLARSSL_MODE_CTR, + 128, + "CAMELLIA-128-CTR", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_192_ctr_info = { + POLARSSL_CIPHER_CAMELLIA_192_CTR, + POLARSSL_MODE_CTR, + 192, + "CAMELLIA-192-CTR", + 16, + 0, + 16, + &camellia_info +}; + +const cipher_info_t camellia_256_ctr_info = { + POLARSSL_CIPHER_CAMELLIA_256_CTR, + POLARSSL_MODE_CTR, + 256, + "CAMELLIA-256-CTR", + 16, + 0, + 16, + &camellia_info +}; +#endif /* POLARSSL_CIPHER_MODE_CTR */ + +#if defined(POLARSSL_GCM_C) +static int gcm_camellia_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return gcm_init( (gcm_context *) ctx, POLARSSL_CIPHER_ID_CAMELLIA, + key, key_length ); +} + +const cipher_base_t gcm_camellia_info = { + POLARSSL_CIPHER_ID_CAMELLIA, + NULL, + NULL, + NULL, + NULL, + NULL, + gcm_camellia_setkey_wrap, + gcm_camellia_setkey_wrap, + gcm_ctx_alloc, + gcm_ctx_free, +}; + +const cipher_info_t camellia_128_gcm_info = { + POLARSSL_CIPHER_CAMELLIA_128_GCM, + POLARSSL_MODE_GCM, + 128, + "CAMELLIA-128-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; + +const cipher_info_t camellia_192_gcm_info = { + POLARSSL_CIPHER_CAMELLIA_192_GCM, + POLARSSL_MODE_GCM, + 192, + "CAMELLIA-192-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; + +const cipher_info_t camellia_256_gcm_info = { + POLARSSL_CIPHER_CAMELLIA_256_GCM, + POLARSSL_MODE_GCM, + 256, + "CAMELLIA-256-GCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CCM_C) +static int ccm_camellia_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return ccm_init( (ccm_context *) ctx, POLARSSL_CIPHER_ID_CAMELLIA, + key, key_length ); +} + +const cipher_base_t ccm_camellia_info = { + POLARSSL_CIPHER_ID_CAMELLIA, + NULL, + NULL, + NULL, + NULL, + NULL, + ccm_camellia_setkey_wrap, + ccm_camellia_setkey_wrap, + ccm_ctx_alloc, + ccm_ctx_free, +}; + +const cipher_info_t camellia_128_ccm_info = { + POLARSSL_CIPHER_CAMELLIA_128_CCM, + POLARSSL_MODE_CCM, + 128, + "CAMELLIA-128-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +const cipher_info_t camellia_192_ccm_info = { + POLARSSL_CIPHER_CAMELLIA_192_CCM, + POLARSSL_MODE_CCM, + 192, + "CAMELLIA-192-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +const cipher_info_t camellia_256_ccm_info = { + POLARSSL_CIPHER_CAMELLIA_256_CCM, + POLARSSL_MODE_CCM, + 256, + "CAMELLIA-256-CCM", + 12, + POLARSSL_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; +#endif /* POLARSSL_CCM_C */ + +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) + +static int des_crypt_ecb_wrap( void *ctx, operation_t operation, + const unsigned char *input, unsigned char *output ) +{ + ((void) operation); + return des_crypt_ecb( (des_context *) ctx, input, output ); +} + +static int des3_crypt_ecb_wrap( void *ctx, operation_t operation, + const unsigned char *input, unsigned char *output ) +{ + ((void) operation); + return des3_crypt_ecb( (des3_context *) ctx, input, output ); +} + +static int des_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CBC) + return des_crypt_cbc( (des_context *) ctx, operation, length, iv, input, + output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ +} + +static int des3_crypt_cbc_wrap( void *ctx, operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CBC) + return des3_crypt_cbc( (des3_context *) ctx, operation, length, iv, input, + output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ +} + +static int des_setkey_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des_setkey_dec( (des_context *) ctx, key ); +} + +static int des_setkey_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des_setkey_enc( (des_context *) ctx, key ); +} + +static int des3_set2key_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des3_set2key_dec( (des3_context *) ctx, key ); +} + +static int des3_set2key_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des3_set2key_enc( (des3_context *) ctx, key ); +} + +static int des3_set3key_dec_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des3_set3key_dec( (des3_context *) ctx, key ); +} + +static int des3_set3key_enc_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) key_length); + + return des3_set3key_enc( (des3_context *) ctx, key ); +} + +static void * des_ctx_alloc( void ) +{ + des_context *des = (des_context *) polarssl_malloc( sizeof( des_context ) ); + + if( des == NULL ) + return( NULL ); + + des_init( des ); + + return( des ); +} + +static void des_ctx_free( void *ctx ) +{ + des_free( (des_context *) ctx ); + polarssl_free( ctx ); +} + +static void * des3_ctx_alloc( void ) +{ + des3_context *des3; + des3 = (des3_context *) polarssl_malloc( sizeof( des3_context ) ); + + if( des3 == NULL ) + return( NULL ); + + des3_init( des3 ); + + return( des3 ); +} + +static void des3_ctx_free( void *ctx ) +{ + des3_free( (des3_context *) ctx ); + polarssl_free( ctx ); +} + +const cipher_base_t des_info = { + POLARSSL_CIPHER_ID_DES, + des_crypt_ecb_wrap, + des_crypt_cbc_wrap, + NULL, + NULL, + NULL, + des_setkey_enc_wrap, + des_setkey_dec_wrap, + des_ctx_alloc, + des_ctx_free +}; + +const cipher_info_t des_ecb_info = { + POLARSSL_CIPHER_DES_ECB, + POLARSSL_MODE_ECB, + POLARSSL_KEY_LENGTH_DES, + "DES-ECB", + 8, + 0, + 8, + &des_info +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t des_cbc_info = { + POLARSSL_CIPHER_DES_CBC, + POLARSSL_MODE_CBC, + POLARSSL_KEY_LENGTH_DES, + "DES-CBC", + 8, + 0, + 8, + &des_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +const cipher_base_t des_ede_info = { + POLARSSL_CIPHER_ID_DES, + des3_crypt_ecb_wrap, + des3_crypt_cbc_wrap, + NULL, + NULL, + NULL, + des3_set2key_enc_wrap, + des3_set2key_dec_wrap, + des3_ctx_alloc, + des3_ctx_free +}; + +const cipher_info_t des_ede_ecb_info = { + POLARSSL_CIPHER_DES_EDE_ECB, + POLARSSL_MODE_ECB, + POLARSSL_KEY_LENGTH_DES_EDE, + "DES-EDE-ECB", + 8, + 0, + 8, + &des_ede_info +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t des_ede_cbc_info = { + POLARSSL_CIPHER_DES_EDE_CBC, + POLARSSL_MODE_CBC, + POLARSSL_KEY_LENGTH_DES_EDE, + "DES-EDE-CBC", + 8, + 0, + 8, + &des_ede_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +const cipher_base_t des_ede3_info = { + POLARSSL_CIPHER_ID_DES, + des3_crypt_ecb_wrap, + des3_crypt_cbc_wrap, + NULL, + NULL, + NULL, + des3_set3key_enc_wrap, + des3_set3key_dec_wrap, + des3_ctx_alloc, + des3_ctx_free +}; + +const cipher_info_t des_ede3_ecb_info = { + POLARSSL_CIPHER_DES_EDE3_ECB, + POLARSSL_MODE_ECB, + POLARSSL_KEY_LENGTH_DES_EDE3, + "DES-EDE3-ECB", + 8, + 0, + 8, + &des_ede3_info +}; +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t des_ede3_cbc_info = { + POLARSSL_CIPHER_DES_EDE3_CBC, + POLARSSL_MODE_CBC, + POLARSSL_KEY_LENGTH_DES_EDE3, + "DES-EDE3-CBC", + 8, + 0, + 8, + &des_ede3_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_BLOWFISH_C) + +static int blowfish_crypt_ecb_wrap( void *ctx, operation_t operation, + const unsigned char *input, unsigned char *output ) +{ + return blowfish_crypt_ecb( (blowfish_context *) ctx, operation, input, + output ); +} + +static int blowfish_crypt_cbc_wrap( void *ctx, operation_t operation, + size_t length, unsigned char *iv, const unsigned char *input, + unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CBC) + return blowfish_crypt_cbc( (blowfish_context *) ctx, operation, length, iv, + input, output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CBC */ +} + +static int blowfish_crypt_cfb64_wrap( void *ctx, operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CFB) + return blowfish_crypt_cfb64( (blowfish_context *) ctx, operation, length, + iv_off, iv, input, output ); +#else + ((void) ctx); + ((void) operation); + ((void) length); + ((void) iv_off); + ((void) iv); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CFB */ +} + +static int blowfish_crypt_ctr_wrap( void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output ) +{ +#if defined(POLARSSL_CIPHER_MODE_CTR) + return blowfish_crypt_ctr( (blowfish_context *) ctx, length, nc_off, + nonce_counter, stream_block, input, output ); +#else + ((void) ctx); + ((void) length); + ((void) nc_off); + ((void) nonce_counter); + ((void) stream_block); + ((void) input); + ((void) output); + + return( POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CIPHER_MODE_CTR */ +} + +static int blowfish_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + return blowfish_setkey( (blowfish_context *) ctx, key, key_length ); +} + +static void * blowfish_ctx_alloc( void ) +{ + blowfish_context *ctx; + ctx = (blowfish_context *) polarssl_malloc( sizeof( blowfish_context ) ); + + if( ctx == NULL ) + return( NULL ); + + blowfish_init( ctx ); + + return( ctx ); +} + +static void blowfish_ctx_free( void *ctx ) +{ + blowfish_free( (blowfish_context *) ctx ); + polarssl_free( ctx ); +} + +const cipher_base_t blowfish_info = { + POLARSSL_CIPHER_ID_BLOWFISH, + blowfish_crypt_ecb_wrap, + blowfish_crypt_cbc_wrap, + blowfish_crypt_cfb64_wrap, + blowfish_crypt_ctr_wrap, + NULL, + blowfish_setkey_wrap, + blowfish_setkey_wrap, + blowfish_ctx_alloc, + blowfish_ctx_free +}; + +const cipher_info_t blowfish_ecb_info = { + POLARSSL_CIPHER_BLOWFISH_ECB, + POLARSSL_MODE_ECB, + 128, + "BLOWFISH-ECB", + 8, + POLARSSL_CIPHER_VARIABLE_KEY_LEN, + 8, + &blowfish_info +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +const cipher_info_t blowfish_cbc_info = { + POLARSSL_CIPHER_BLOWFISH_CBC, + POLARSSL_MODE_CBC, + 128, + "BLOWFISH-CBC", + 8, + POLARSSL_CIPHER_VARIABLE_KEY_LEN, + 8, + &blowfish_info +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_CIPHER_MODE_CFB) +const cipher_info_t blowfish_cfb64_info = { + POLARSSL_CIPHER_BLOWFISH_CFB64, + POLARSSL_MODE_CFB, + 128, + "BLOWFISH-CFB64", + 8, + POLARSSL_CIPHER_VARIABLE_KEY_LEN, + 8, + &blowfish_info +}; +#endif /* POLARSSL_CIPHER_MODE_CFB */ + +#if defined(POLARSSL_CIPHER_MODE_CTR) +const cipher_info_t blowfish_ctr_info = { + POLARSSL_CIPHER_BLOWFISH_CTR, + POLARSSL_MODE_CTR, + 128, + "BLOWFISH-CTR", + 8, + POLARSSL_CIPHER_VARIABLE_KEY_LEN, + 8, + &blowfish_info +}; +#endif /* POLARSSL_CIPHER_MODE_CTR */ +#endif /* POLARSSL_BLOWFISH_C */ + +#if defined(POLARSSL_ARC4_C) +static int arc4_crypt_stream_wrap( void *ctx, size_t length, + const unsigned char *input, + unsigned char *output ) +{ + return( arc4_crypt( (arc4_context *) ctx, length, input, output ) ); +} + +static int arc4_setkey_wrap( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + /* we get key_length in bits, arc4 expects it in bytes */ + if( key_length % 8 != 0 ) + return( POLARSSL_ERR_CIPHER_BAD_INPUT_DATA ); + + arc4_setup( (arc4_context *) ctx, key, key_length / 8 ); + return( 0 ); +} + +static void * arc4_ctx_alloc( void ) +{ + arc4_context *ctx; + ctx = (arc4_context *) polarssl_malloc( sizeof( arc4_context ) ); + + if( ctx == NULL ) + return( NULL ); + + arc4_init( ctx ); + + return( ctx ); +} + +static void arc4_ctx_free( void *ctx ) +{ + arc4_free( (arc4_context *) ctx ); + polarssl_free( ctx ); +} + +const cipher_base_t arc4_base_info = { + POLARSSL_CIPHER_ID_ARC4, + NULL, + NULL, + NULL, + NULL, + arc4_crypt_stream_wrap, + arc4_setkey_wrap, + arc4_setkey_wrap, + arc4_ctx_alloc, + arc4_ctx_free +}; + +const cipher_info_t arc4_128_info = { + POLARSSL_CIPHER_ARC4_128, + POLARSSL_MODE_STREAM, + 128, + "ARC4-128", + 0, + 0, + 1, + &arc4_base_info +}; +#endif /* POLARSSL_ARC4_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +static int null_crypt_stream( void *ctx, size_t length, + const unsigned char *input, + unsigned char *output ) +{ + ((void) ctx); + memmove( output, input, length ); + return( 0 ); +} + +static int null_setkey( void *ctx, const unsigned char *key, + unsigned int key_length ) +{ + ((void) ctx); + ((void) key); + ((void) key_length); + + return( 0 ); +} + +static void * null_ctx_alloc( void ) +{ + return( (void *) 1 ) +} + +static void null_ctx_free( void *ctx ) +{ + ((void) ctx); +} + +const cipher_base_t null_base_info = { + POLARSSL_CIPHER_ID_NULL, + NULL, + NULL, + NULL, + NULL, + null_crypt_stream, + null_setkey, + null_setkey, + null_ctx_alloc, + null_ctx_free +}; + +const cipher_info_t null_cipher_info = { + POLARSSL_CIPHER_NULL, + POLARSSL_MODE_STREAM, + 0, + "NULL", + 0, + 0, + 1, + &null_base_info +}; +#endif /* defined(POLARSSL_CIPHER_NULL_CIPHER) */ + +const cipher_definition_t cipher_definitions[] = +{ +#if defined(POLARSSL_AES_C) + { POLARSSL_CIPHER_AES_128_ECB, &aes_128_ecb_info }, + { POLARSSL_CIPHER_AES_192_ECB, &aes_192_ecb_info }, + { POLARSSL_CIPHER_AES_256_ECB, &aes_256_ecb_info }, +#if defined(POLARSSL_CIPHER_MODE_CBC) + { POLARSSL_CIPHER_AES_128_CBC, &aes_128_cbc_info }, + { POLARSSL_CIPHER_AES_192_CBC, &aes_192_cbc_info }, + { POLARSSL_CIPHER_AES_256_CBC, &aes_256_cbc_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CFB) + { POLARSSL_CIPHER_AES_128_CFB128, &aes_128_cfb128_info }, + { POLARSSL_CIPHER_AES_192_CFB128, &aes_192_cfb128_info }, + { POLARSSL_CIPHER_AES_256_CFB128, &aes_256_cfb128_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + { POLARSSL_CIPHER_AES_128_CTR, &aes_128_ctr_info }, + { POLARSSL_CIPHER_AES_192_CTR, &aes_192_ctr_info }, + { POLARSSL_CIPHER_AES_256_CTR, &aes_256_ctr_info }, +#endif +#if defined(POLARSSL_GCM_C) + { POLARSSL_CIPHER_AES_128_GCM, &aes_128_gcm_info }, + { POLARSSL_CIPHER_AES_192_GCM, &aes_192_gcm_info }, + { POLARSSL_CIPHER_AES_256_GCM, &aes_256_gcm_info }, +#endif +#if defined(POLARSSL_CCM_C) + { POLARSSL_CIPHER_AES_128_CCM, &aes_128_ccm_info }, + { POLARSSL_CIPHER_AES_192_CCM, &aes_192_ccm_info }, + { POLARSSL_CIPHER_AES_256_CCM, &aes_256_ccm_info }, +#endif +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_ARC4_C) + { POLARSSL_CIPHER_ARC4_128, &arc4_128_info }, +#endif + +#if defined(POLARSSL_BLOWFISH_C) + { POLARSSL_CIPHER_BLOWFISH_ECB, &blowfish_ecb_info }, +#if defined(POLARSSL_CIPHER_MODE_CBC) + { POLARSSL_CIPHER_BLOWFISH_CBC, &blowfish_cbc_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CFB) + { POLARSSL_CIPHER_BLOWFISH_CFB64, &blowfish_cfb64_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + { POLARSSL_CIPHER_BLOWFISH_CTR, &blowfish_ctr_info }, +#endif +#endif /* POLARSSL_BLOWFISH_C */ + +#if defined(POLARSSL_CAMELLIA_C) + { POLARSSL_CIPHER_CAMELLIA_128_ECB, &camellia_128_ecb_info }, + { POLARSSL_CIPHER_CAMELLIA_192_ECB, &camellia_192_ecb_info }, + { POLARSSL_CIPHER_CAMELLIA_256_ECB, &camellia_256_ecb_info }, +#if defined(POLARSSL_CIPHER_MODE_CBC) + { POLARSSL_CIPHER_CAMELLIA_128_CBC, &camellia_128_cbc_info }, + { POLARSSL_CIPHER_CAMELLIA_192_CBC, &camellia_192_cbc_info }, + { POLARSSL_CIPHER_CAMELLIA_256_CBC, &camellia_256_cbc_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CFB) + { POLARSSL_CIPHER_CAMELLIA_128_CFB128, &camellia_128_cfb128_info }, + { POLARSSL_CIPHER_CAMELLIA_192_CFB128, &camellia_192_cfb128_info }, + { POLARSSL_CIPHER_CAMELLIA_256_CFB128, &camellia_256_cfb128_info }, +#endif +#if defined(POLARSSL_CIPHER_MODE_CTR) + { POLARSSL_CIPHER_CAMELLIA_128_CTR, &camellia_128_ctr_info }, + { POLARSSL_CIPHER_CAMELLIA_192_CTR, &camellia_192_ctr_info }, + { POLARSSL_CIPHER_CAMELLIA_256_CTR, &camellia_256_ctr_info }, +#endif +#if defined(POLARSSL_GCM_C) + { POLARSSL_CIPHER_CAMELLIA_128_GCM, &camellia_128_gcm_info }, + { POLARSSL_CIPHER_CAMELLIA_192_GCM, &camellia_192_gcm_info }, + { POLARSSL_CIPHER_CAMELLIA_256_GCM, &camellia_256_gcm_info }, +#endif +#if defined(POLARSSL_CCM_C) + { POLARSSL_CIPHER_CAMELLIA_128_CCM, &camellia_128_ccm_info }, + { POLARSSL_CIPHER_CAMELLIA_192_CCM, &camellia_192_ccm_info }, + { POLARSSL_CIPHER_CAMELLIA_256_CCM, &camellia_256_ccm_info }, +#endif +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) + { POLARSSL_CIPHER_DES_ECB, &des_ecb_info }, + { POLARSSL_CIPHER_DES_EDE_ECB, &des_ede_ecb_info }, + { POLARSSL_CIPHER_DES_EDE3_ECB, &des_ede3_ecb_info }, +#if defined(POLARSSL_CIPHER_MODE_CBC) + { POLARSSL_CIPHER_DES_CBC, &des_cbc_info }, + { POLARSSL_CIPHER_DES_EDE_CBC, &des_ede_cbc_info }, + { POLARSSL_CIPHER_DES_EDE3_CBC, &des_ede3_cbc_info }, +#endif +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) + { POLARSSL_CIPHER_NULL, &null_cipher_info }, +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ + + { (cipher_type_t)0, NULL } +}; + +#define NUM_CIPHERS sizeof cipher_definitions / sizeof cipher_definitions[0] +int supported_ciphers[NUM_CIPHERS]; + +#endif /* POLARSSL_CIPHER_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ctr_drbg.c b/component/common/network/ssl/polarssl-1.3.8/library/ctr_drbg.c new file mode 100644 index 0000000..96ee4f1 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ctr_drbg.c @@ -0,0 +1,549 @@ +/* + * CTR_DRBG implementation based on AES-256 (NIST SP 800-90) + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The NIST SP 800-90 DRBGs are described in the following publucation. + * + * http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_CTR_DRBG_C) + +#include "polarssl/ctr_drbg.h" + +#if defined(POLARSSL_FS_IO) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Non-public function wrapped by ctr_crbg_init(). Necessary to allow NIST + * tests to succeed (which require known length fixed entropy) + */ +int ctr_drbg_init_entropy_len( + ctr_drbg_context *ctx, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len, + size_t entropy_len ) +{ + int ret; + unsigned char key[CTR_DRBG_KEYSIZE]; + + memset( ctx, 0, sizeof(ctr_drbg_context) ); + memset( key, 0, CTR_DRBG_KEYSIZE ); + + aes_init( &ctx->aes_ctx ); + + ctx->f_entropy = f_entropy; + ctx->p_entropy = p_entropy; + + ctx->entropy_len = entropy_len; + ctx->reseed_interval = CTR_DRBG_RESEED_INTERVAL; + + /* + * Initialize with an empty key + */ + aes_setkey_enc( &ctx->aes_ctx, key, CTR_DRBG_KEYBITS ); + + if( ( ret = ctr_drbg_reseed( ctx, custom, len ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +int ctr_drbg_init( ctr_drbg_context *ctx, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len ) +{ + return( ctr_drbg_init_entropy_len( ctx, f_entropy, p_entropy, custom, len, + CTR_DRBG_ENTROPY_LEN ) ); +} + +void ctr_drbg_free( ctr_drbg_context *ctx ) +{ + if( ctx == NULL ) + return; + + aes_free( &ctx->aes_ctx ); + polarssl_zeroize( ctx, sizeof( ctr_drbg_context ) ); +} + +void ctr_drbg_set_prediction_resistance( ctr_drbg_context *ctx, int resistance ) +{ + ctx->prediction_resistance = resistance; +} + +void ctr_drbg_set_entropy_len( ctr_drbg_context *ctx, size_t len ) +{ + ctx->entropy_len = len; +} + +void ctr_drbg_set_reseed_interval( ctr_drbg_context *ctx, int interval ) +{ + ctx->reseed_interval = interval; +} + +static int block_cipher_df( unsigned char *output, + const unsigned char *data, size_t data_len ) +{ + unsigned char buf[CTR_DRBG_MAX_SEED_INPUT + CTR_DRBG_BLOCKSIZE + 16]; + unsigned char tmp[CTR_DRBG_SEEDLEN]; + unsigned char key[CTR_DRBG_KEYSIZE]; + unsigned char chain[CTR_DRBG_BLOCKSIZE]; + unsigned char *p, *iv; + aes_context aes_ctx; + + int i, j; + size_t buf_len, use_len; + + memset( buf, 0, CTR_DRBG_MAX_SEED_INPUT + CTR_DRBG_BLOCKSIZE + 16 ); + aes_init( &aes_ctx ); + + /* + * Construct IV (16 bytes) and S in buffer + * IV = Counter (in 32-bits) padded to 16 with zeroes + * S = Length input string (in 32-bits) || Length of output (in 32-bits) || + * data || 0x80 + * (Total is padded to a multiple of 16-bytes with zeroes) + */ + p = buf + CTR_DRBG_BLOCKSIZE; + *p++ = ( data_len >> 24 ) & 0xff; + *p++ = ( data_len >> 16 ) & 0xff; + *p++ = ( data_len >> 8 ) & 0xff; + *p++ = ( data_len ) & 0xff; + p += 3; + *p++ = CTR_DRBG_SEEDLEN; + memcpy( p, data, data_len ); + p[data_len] = 0x80; + + buf_len = CTR_DRBG_BLOCKSIZE + 8 + data_len + 1; + + for( i = 0; i < CTR_DRBG_KEYSIZE; i++ ) + key[i] = i; + + aes_setkey_enc( &aes_ctx, key, CTR_DRBG_KEYBITS ); + + /* + * Reduce data to POLARSSL_CTR_DRBG_SEEDLEN bytes of data + */ + for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) + { + p = buf; + memset( chain, 0, CTR_DRBG_BLOCKSIZE ); + use_len = buf_len; + + while( use_len > 0 ) + { + for( i = 0; i < CTR_DRBG_BLOCKSIZE; i++ ) + chain[i] ^= p[i]; + p += CTR_DRBG_BLOCKSIZE; + use_len -= ( use_len >= CTR_DRBG_BLOCKSIZE ) ? + CTR_DRBG_BLOCKSIZE : use_len; + + aes_crypt_ecb( &aes_ctx, AES_ENCRYPT, chain, chain ); + } + + memcpy( tmp + j, chain, CTR_DRBG_BLOCKSIZE ); + + /* + * Update IV + */ + buf[3]++; + } + + /* + * Do final encryption with reduced data + */ + aes_setkey_enc( &aes_ctx, tmp, CTR_DRBG_KEYBITS ); + iv = tmp + CTR_DRBG_KEYSIZE; + p = output; + + for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) + { + aes_crypt_ecb( &aes_ctx, AES_ENCRYPT, iv, iv ); + memcpy( p, iv, CTR_DRBG_BLOCKSIZE ); + p += CTR_DRBG_BLOCKSIZE; + } + + aes_free( &aes_ctx ); + + return( 0 ); +} + +static int ctr_drbg_update_internal( ctr_drbg_context *ctx, + const unsigned char data[CTR_DRBG_SEEDLEN] ) +{ + unsigned char tmp[CTR_DRBG_SEEDLEN]; + unsigned char *p = tmp; + int i, j; + + memset( tmp, 0, CTR_DRBG_SEEDLEN ); + + for( j = 0; j < CTR_DRBG_SEEDLEN; j += CTR_DRBG_BLOCKSIZE ) + { + /* + * Increase counter + */ + for( i = CTR_DRBG_BLOCKSIZE; i > 0; i-- ) + if( ++ctx->counter[i - 1] != 0 ) + break; + + /* + * Crypt counter block + */ + aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->counter, p ); + + p += CTR_DRBG_BLOCKSIZE; + } + + for( i = 0; i < CTR_DRBG_SEEDLEN; i++ ) + tmp[i] ^= data[i]; + + /* + * Update key and counter + */ + aes_setkey_enc( &ctx->aes_ctx, tmp, CTR_DRBG_KEYBITS ); + memcpy( ctx->counter, tmp + CTR_DRBG_KEYSIZE, CTR_DRBG_BLOCKSIZE ); + + return( 0 ); +} + +void ctr_drbg_update( ctr_drbg_context *ctx, + const unsigned char *additional, size_t add_len ) +{ + unsigned char add_input[CTR_DRBG_SEEDLEN]; + + if( add_len > 0 ) + { + block_cipher_df( add_input, additional, add_len ); + ctr_drbg_update_internal( ctx, add_input ); + } +} + +int ctr_drbg_reseed( ctr_drbg_context *ctx, + const unsigned char *additional, size_t len ) +{ + unsigned char seed[CTR_DRBG_MAX_SEED_INPUT]; + size_t seedlen = 0; + + if( ctx->entropy_len + len > CTR_DRBG_MAX_SEED_INPUT ) + return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); + + memset( seed, 0, CTR_DRBG_MAX_SEED_INPUT ); + + /* + * Gather entropy_len bytes of entropy to seed state + */ + if( 0 != ctx->f_entropy( ctx->p_entropy, seed, + ctx->entropy_len ) ) + { + return( POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED ); + } + + seedlen += ctx->entropy_len; + + /* + * Add additional data + */ + if( additional && len ) + { + memcpy( seed + seedlen, additional, len ); + seedlen += len; + } + + /* + * Reduce to 384 bits + */ + block_cipher_df( seed, seed, seedlen ); + + /* + * Update state + */ + ctr_drbg_update_internal( ctx, seed ); + ctx->reseed_counter = 1; + + return( 0 ); +} + +int ctr_drbg_random_with_add( void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, size_t add_len ) +{ + int ret = 0; + ctr_drbg_context *ctx = (ctr_drbg_context *) p_rng; + unsigned char add_input[CTR_DRBG_SEEDLEN]; + unsigned char *p = output; + unsigned char tmp[CTR_DRBG_BLOCKSIZE]; + int i; + size_t use_len; + + if( output_len > CTR_DRBG_MAX_REQUEST ) + return( POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG ); + + if( add_len > CTR_DRBG_MAX_INPUT ) + return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); + + memset( add_input, 0, CTR_DRBG_SEEDLEN ); + + if( ctx->reseed_counter > ctx->reseed_interval || + ctx->prediction_resistance ) + { + if( ( ret = ctr_drbg_reseed( ctx, additional, add_len ) ) != 0 ) + return( ret ); + + add_len = 0; + } + + if( add_len > 0 ) + { + block_cipher_df( add_input, additional, add_len ); + ctr_drbg_update_internal( ctx, add_input ); + } + + while( output_len > 0 ) + { + /* + * Increase counter + */ + for( i = CTR_DRBG_BLOCKSIZE; i > 0; i-- ) + if( ++ctx->counter[i - 1] != 0 ) + break; + + /* + * Crypt counter block + */ + aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->counter, tmp ); + + use_len = ( output_len > CTR_DRBG_BLOCKSIZE ) ? CTR_DRBG_BLOCKSIZE : + output_len; + /* + * Copy random block to destination + */ + memcpy( p, tmp, use_len ); + p += use_len; + output_len -= use_len; + } + + ctr_drbg_update_internal( ctx, add_input ); + + ctx->reseed_counter++; + + return( 0 ); +} + +int ctr_drbg_random( void *p_rng, unsigned char *output, size_t output_len ) +{ + return ctr_drbg_random_with_add( p_rng, output, output_len, NULL, 0 ); +} + +#if defined(POLARSSL_FS_IO) +int ctr_drbg_write_seed_file( ctr_drbg_context *ctx, const char *path ) +{ + int ret = POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR; + FILE *f; + unsigned char buf[ CTR_DRBG_MAX_INPUT ]; + + if( ( f = fopen( path, "wb" ) ) == NULL ) + return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); + + if( ( ret = ctr_drbg_random( ctx, buf, CTR_DRBG_MAX_INPUT ) ) != 0 ) + goto exit; + + if( fwrite( buf, 1, CTR_DRBG_MAX_INPUT, f ) != CTR_DRBG_MAX_INPUT ) + { + ret = POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR; + goto exit; + } + + ret = 0; + +exit: + fclose( f ); + return( ret ); +} + +int ctr_drbg_update_seed_file( ctr_drbg_context *ctx, const char *path ) +{ + FILE *f; + size_t n; + unsigned char buf[ CTR_DRBG_MAX_INPUT ]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + n = (size_t) ftell( f ); + fseek( f, 0, SEEK_SET ); + + if( n > CTR_DRBG_MAX_INPUT ) + { + fclose( f ); + return( POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG ); + } + + if( fread( buf, 1, n, f ) != n ) + { + fclose( f ); + return( POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR ); + } + + fclose( f ); + + ctr_drbg_update( ctx, buf, n ); + + return( ctr_drbg_write_seed_file( ctx, path ) ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +static unsigned char entropy_source_pr[96] = + { 0xc1, 0x80, 0x81, 0xa6, 0x5d, 0x44, 0x02, 0x16, + 0x19, 0xb3, 0xf1, 0x80, 0xb1, 0xc9, 0x20, 0x02, + 0x6a, 0x54, 0x6f, 0x0c, 0x70, 0x81, 0x49, 0x8b, + 0x6e, 0xa6, 0x62, 0x52, 0x6d, 0x51, 0xb1, 0xcb, + 0x58, 0x3b, 0xfa, 0xd5, 0x37, 0x5f, 0xfb, 0xc9, + 0xff, 0x46, 0xd2, 0x19, 0xc7, 0x22, 0x3e, 0x95, + 0x45, 0x9d, 0x82, 0xe1, 0xe7, 0x22, 0x9f, 0x63, + 0x31, 0x69, 0xd2, 0x6b, 0x57, 0x47, 0x4f, 0xa3, + 0x37, 0xc9, 0x98, 0x1c, 0x0b, 0xfb, 0x91, 0x31, + 0x4d, 0x55, 0xb9, 0xe9, 0x1c, 0x5a, 0x5e, 0xe4, + 0x93, 0x92, 0xcf, 0xc5, 0x23, 0x12, 0xd5, 0x56, + 0x2c, 0x4a, 0x6e, 0xff, 0xdc, 0x10, 0xd0, 0x68 }; + +static unsigned char entropy_source_nopr[64] = + { 0x5a, 0x19, 0x4d, 0x5e, 0x2b, 0x31, 0x58, 0x14, + 0x54, 0xde, 0xf6, 0x75, 0xfb, 0x79, 0x58, 0xfe, + 0xc7, 0xdb, 0x87, 0x3e, 0x56, 0x89, 0xfc, 0x9d, + 0x03, 0x21, 0x7c, 0x68, 0xd8, 0x03, 0x38, 0x20, + 0xf9, 0xe6, 0x5e, 0x04, 0xd8, 0x56, 0xf3, 0xa9, + 0xc4, 0x4a, 0x4c, 0xbd, 0xc1, 0xd0, 0x08, 0x46, + 0xf5, 0x98, 0x3d, 0x77, 0x1c, 0x1b, 0x13, 0x7e, + 0x4e, 0x0f, 0x9d, 0x8e, 0xf4, 0x09, 0xf9, 0x2e }; + +static const unsigned char nonce_pers_pr[16] = + { 0xd2, 0x54, 0xfc, 0xff, 0x02, 0x1e, 0x69, 0xd2, + 0x29, 0xc9, 0xcf, 0xad, 0x85, 0xfa, 0x48, 0x6c }; + +static const unsigned char nonce_pers_nopr[16] = + { 0x1b, 0x54, 0xb8, 0xff, 0x06, 0x42, 0xbf, 0xf5, + 0x21, 0xf1, 0x5c, 0x1c, 0x0b, 0x66, 0x5f, 0x3f }; + +static const unsigned char result_pr[16] = + { 0x34, 0x01, 0x16, 0x56, 0xb4, 0x29, 0x00, 0x8f, + 0x35, 0x63, 0xec, 0xb5, 0xf2, 0x59, 0x07, 0x23 }; + +static const unsigned char result_nopr[16] = + { 0xa0, 0x54, 0x30, 0x3d, 0x8a, 0x7e, 0xa9, 0x88, + 0x9d, 0x90, 0x3e, 0x07, 0x7c, 0x6f, 0x21, 0x8f }; + +static size_t test_offset; +static int ctr_drbg_self_test_entropy( void *data, unsigned char *buf, + size_t len ) +{ + const unsigned char *p = data; + memcpy( buf, p + test_offset, len ); + test_offset += len; + return( 0 ); +} + +#define CHK( c ) if( (c) != 0 ) \ + { \ + if( verbose != 0 ) \ + polarssl_printf( "failed\n" ); \ + return( 1 ); \ + } + +/* + * Checkup routine + */ +int ctr_drbg_self_test( int verbose ) +{ + ctr_drbg_context ctx; + unsigned char buf[16]; + + /* + * Based on a NIST CTR_DRBG test vector (PR = True) + */ + if( verbose != 0 ) + polarssl_printf( " CTR_DRBG (PR = TRUE) : " ); + + test_offset = 0; + CHK( ctr_drbg_init_entropy_len( &ctx, ctr_drbg_self_test_entropy, + entropy_source_pr, nonce_pers_pr, 16, 32 ) ); + ctr_drbg_set_prediction_resistance( &ctx, CTR_DRBG_PR_ON ); + CHK( ctr_drbg_random( &ctx, buf, CTR_DRBG_BLOCKSIZE ) ); + CHK( ctr_drbg_random( &ctx, buf, CTR_DRBG_BLOCKSIZE ) ); + CHK( memcmp( buf, result_pr, CTR_DRBG_BLOCKSIZE ) ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + /* + * Based on a NIST CTR_DRBG test vector (PR = FALSE) + */ + if( verbose != 0 ) + polarssl_printf( " CTR_DRBG (PR = FALSE): " ); + + test_offset = 0; + CHK( ctr_drbg_init_entropy_len( &ctx, ctr_drbg_self_test_entropy, + entropy_source_nopr, nonce_pers_nopr, 16, 32 ) ); + CHK( ctr_drbg_random( &ctx, buf, 16 ) ); + CHK( ctr_drbg_reseed( &ctx, NULL, 0 ) ); + CHK( ctr_drbg_random( &ctx, buf, 16 ) ); + CHK( memcmp( buf, result_nopr, 16 ) ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_CTR_DRBG_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/debug.c b/component/common/network/ssl/polarssl-1.3.8/library/debug.c new file mode 100644 index 0000000..a81f502 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/debug.c @@ -0,0 +1,351 @@ +/* + * Debugging routines + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_DEBUG_C) + +#include "polarssl/debug.h" + +#include +#include + +#if defined(EFIX64) || defined(EFI32) +#include +#endif + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#if !defined snprintf +#define snprintf _snprintf +#endif + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif +#endif /* _MSC_VER */ + +static int debug_log_mode = POLARSSL_DEBUG_DFL_MODE; +static int debug_threshold = 0; + +void debug_set_log_mode( int log_mode ) +{ + debug_log_mode = log_mode; +} + +void debug_set_threshold( int threshold ) +{ + debug_threshold = threshold; +} + +char *debug_fmt( const char *format, ... ) +{ + va_list argp; + static char str[512]; + int maxlen = sizeof( str ) - 1; + + va_start( argp, format ); + vsnprintf( str, maxlen, format, argp ); + va_end( argp ); + + str[maxlen] = '\0'; + return( str ); +} + +void debug_print_msg( const ssl_context *ssl, int level, + const char *file, int line, const char *text ) +{ + char str[512]; + int maxlen = sizeof( str ) - 1; + + if( ssl->f_dbg == NULL || level > debug_threshold ) + return; + + if( debug_log_mode == POLARSSL_DEBUG_LOG_RAW ) + { + ssl->f_dbg( ssl->p_dbg, level, text ); + return; + } + + snprintf( str, maxlen, "%s(%04d): %s\n", file, line, text ); + str[maxlen] = '\0'; + ssl->f_dbg( ssl->p_dbg, level, str ); +} + +void debug_print_ret( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, int ret ) +{ + char str[512]; + int maxlen = sizeof( str ) - 1; + size_t idx = 0; + + if( ssl->f_dbg == NULL || level > debug_threshold ) + return; + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + snprintf( str + idx, maxlen - idx, "%s() returned %d (-0x%04x)\n", + text, ret, -ret ); + + str[maxlen] = '\0'; + ssl->f_dbg( ssl->p_dbg, level, str ); +} + +void debug_print_buf( const ssl_context *ssl, int level, + const char *file, int line, const char *text, + unsigned char *buf, size_t len ) +{ + char str[512]; + size_t i, maxlen = sizeof( str ) - 1, idx = 0; + + if( ssl->f_dbg == NULL || level > debug_threshold ) + return; + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + snprintf( str + idx, maxlen - idx, "dumping '%s' (%u bytes)\n", + text, (unsigned int) len ); + + str[maxlen] = '\0'; + ssl->f_dbg( ssl->p_dbg, level, str ); + + idx = 0; + for( i = 0; i < len; i++ ) + { + if( i >= 4096 ) + break; + + if( i % 16 == 0 ) + { + if( i > 0 ) + { + snprintf( str + idx, maxlen - idx, "\n" ); + ssl->f_dbg( ssl->p_dbg, level, str ); + idx = 0; + } + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + idx += snprintf( str + idx, maxlen - idx, "%04x: ", + (unsigned int) i ); + + } + + idx += snprintf( str + idx, maxlen - idx, " %02x", + (unsigned int) buf[i] ); + } + + if( len > 0 ) + { + snprintf( str + idx, maxlen - idx, "\n" ); + ssl->f_dbg( ssl->p_dbg, level, str ); + } +} + +#if defined(POLARSSL_ECP_C) +void debug_print_ecp( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const ecp_point *X ) +{ + char str[512]; + int maxlen = sizeof( str ) - 1; + + if( ssl->f_dbg == NULL || level > debug_threshold ) + return; + + snprintf( str, maxlen, "%s(X)", text ); + str[maxlen] = '\0'; + debug_print_mpi( ssl, level, file, line, str, &X->X ); + + snprintf( str, maxlen, "%s(Y)", text ); + str[maxlen] = '\0'; + debug_print_mpi( ssl, level, file, line, str, &X->Y ); +} +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_BIGNUM_C) +void debug_print_mpi( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mpi *X ) +{ + char str[512]; + int j, k, maxlen = sizeof( str ) - 1, zeros = 1; + size_t i, n, idx = 0; + + if( ssl->f_dbg == NULL || X == NULL || level > debug_threshold ) + return; + + for( n = X->n - 1; n > 0; n-- ) + if( X->p[n] != 0 ) + break; + + for( j = ( sizeof(t_uint) << 3 ) - 1; j >= 0; j-- ) + if( ( ( X->p[n] >> j ) & 1 ) != 0 ) + break; + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + snprintf( str + idx, maxlen - idx, "value of '%s' (%d bits) is:\n", + text, (int) ( ( n * ( sizeof(t_uint) << 3 ) ) + j + 1 ) ); + + str[maxlen] = '\0'; + ssl->f_dbg( ssl->p_dbg, level, str ); + + idx = 0; + for( i = n + 1, j = 0; i > 0; i-- ) + { + if( zeros && X->p[i - 1] == 0 ) + continue; + + for( k = sizeof( t_uint ) - 1; k >= 0; k-- ) + { + if( zeros && ( ( X->p[i - 1] >> ( k << 3 ) ) & 0xFF ) == 0 ) + continue; + else + zeros = 0; + + if( j % 16 == 0 ) + { + if( j > 0 ) + { + snprintf( str + idx, maxlen - idx, "\n" ); + ssl->f_dbg( ssl->p_dbg, level, str ); + idx = 0; + } + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + } + + idx += snprintf( str + idx, maxlen - idx, " %02x", (unsigned int) + ( X->p[i - 1] >> ( k << 3 ) ) & 0xFF ); + + j++; + } + + } + + if( zeros == 1 ) + { + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + { + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + } + idx += snprintf( str + idx, maxlen - idx, " 00" ); + } + + snprintf( str + idx, maxlen - idx, "\n" ); + ssl->f_dbg( ssl->p_dbg, level, str ); +} +#endif /* POLARSSL_BIGNUM_C */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) +static void debug_print_pk( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const pk_context *pk ) +{ + size_t i; + pk_debug_item items[POLARSSL_PK_DEBUG_MAX_ITEMS]; + char name[16]; + + memset( items, 0, sizeof( items ) ); + + if( pk_debug( pk, items ) != 0 ) + { + debug_print_msg( ssl, level, file, line, "invalid PK context" ); + return; + } + + for( i = 0; i < POLARSSL_PK_DEBUG_MAX_ITEMS; i++ ) + { + if( items[i].type == POLARSSL_PK_DEBUG_NONE ) + return; + + snprintf( name, sizeof( name ), "%s%s", text, items[i].name ); + name[sizeof( name ) - 1] = '\0'; + + if( items[i].type == POLARSSL_PK_DEBUG_MPI ) + debug_print_mpi( ssl, level, file, line, name, items[i].value ); + else +#if defined(POLARSSL_ECP_C) + if( items[i].type == POLARSSL_PK_DEBUG_ECP ) + debug_print_ecp( ssl, level, file, line, name, items[i].value ); + else +#endif + debug_print_msg( ssl, level, file, line, "should not happen" ); + } +} + +void debug_print_crt( const ssl_context *ssl, int level, + const char *file, int line, + const char *text, const x509_crt *crt ) +{ + char str[1024], prefix[64]; + int i = 0, maxlen = sizeof( prefix ) - 1, idx = 0; + + if( ssl->f_dbg == NULL || crt == NULL || level > debug_threshold ) + return; + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + { + snprintf( prefix, maxlen, "%s(%04d): ", file, line ); + prefix[maxlen] = '\0'; + } + else + prefix[0] = '\0'; + + maxlen = sizeof( str ) - 1; + + while( crt != NULL ) + { + char buf[1024]; + x509_crt_info( buf, sizeof( buf ) - 1, prefix, crt ); + + if( debug_log_mode == POLARSSL_DEBUG_LOG_FULL ) + idx = snprintf( str, maxlen, "%s(%04d): ", file, line ); + + snprintf( str + idx, maxlen - idx, "%s #%d:\n%s", + text, ++i, buf ); + + str[maxlen] = '\0'; + ssl->f_dbg( ssl->p_dbg, level, str ); + + debug_print_pk( ssl, level, file, line, "crt->", &crt->pk ); + + crt = crt->next; + } +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#endif /* POLARSSL_DEBUG_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/des.c b/component/common/network/ssl/polarssl-1.3.8/library/des.c new file mode 100644 index 0000000..bbd7095 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/des.c @@ -0,0 +1,1156 @@ +/* + * FIPS-46-3 compliant Triple-DES implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * DES, on which TDES is based, was originally designed by Horst Feistel + * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). + * + * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif +#ifdef RTL_HW_CRYPTO +#include +#endif +#if defined(POLARSSL_DES_C) + +#include "polarssl/des.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +#if !defined(POLARSSL_DES_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* + * Expanded DES S-boxes + */ +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* + * PC1: left and right halves bit-swap + */ +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* + * Initial Permutation macro + */ +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* + * Final Permutation macro + */ +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* + * DES round macro + */ +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +#define SWAP(a,b) { uint32_t t = a; a = b; b = t; t = 0; } + +void des_init( des_context *ctx ) +{ + memset( ctx, 0, sizeof( des_context ) ); +} + +void des_free( des_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( des_context ) ); +} + +void des3_init( des3_context *ctx ) +{ + memset( ctx, 0, sizeof( des3_context ) ); +} + +void des3_free( des3_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( des3_context ) ); +} + +static const unsigned char odd_parity_table[128] = { 1, 2, 4, 7, 8, + 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, 32, 35, 37, 38, 41, 42, 44, + 47, 49, 50, 52, 55, 56, 59, 61, 62, 64, 67, 69, 70, 73, 74, 76, 79, 81, + 82, 84, 87, 88, 91, 93, 94, 97, 98, 100, 103, 104, 107, 109, 110, 112, + 115, 117, 118, 121, 122, 124, 127, 128, 131, 133, 134, 137, 138, 140, + 143, 145, 146, 148, 151, 152, 155, 157, 158, 161, 162, 164, 167, 168, + 171, 173, 174, 176, 179, 181, 182, 185, 186, 188, 191, 193, 194, 196, + 199, 200, 203, 205, 206, 208, 211, 213, 214, 217, 218, 220, 223, 224, + 227, 229, 230, 233, 234, 236, 239, 241, 242, 244, 247, 248, 251, 253, + 254 }; + +void des_key_set_parity( unsigned char key[DES_KEY_SIZE] ) +{ + int i; + + for( i = 0; i < DES_KEY_SIZE; i++ ) + key[i] = odd_parity_table[key[i] / 2]; +} + +/* + * Check the given key's parity, returns 1 on failure, 0 on SUCCESS + */ +int des_key_check_key_parity( const unsigned char key[DES_KEY_SIZE] ) +{ + int i; + + for( i = 0; i < DES_KEY_SIZE; i++ ) + if( key[i] != odd_parity_table[key[i] / 2] ) + return( 1 ); + + return( 0 ); +} + +/* + * Table of weak and semi-weak keys + * + * Source: http://en.wikipedia.org/wiki/Weak_key + * + * Weak: + * Alternating ones + zeros (0x0101010101010101) + * Alternating 'F' + 'E' (0xFEFEFEFEFEFEFEFE) + * '0xE0E0E0E0F1F1F1F1' + * '0x1F1F1F1F0E0E0E0E' + * + * Semi-weak: + * 0x011F011F010E010E and 0x1F011F010E010E01 + * 0x01E001E001F101F1 and 0xE001E001F101F101 + * 0x01FE01FE01FE01FE and 0xFE01FE01FE01FE01 + * 0x1FE01FE00EF10EF1 and 0xE01FE01FF10EF10E + * 0x1FFE1FFE0EFE0EFE and 0xFE1FFE1FFE0EFE0E + * 0xE0FEE0FEF1FEF1FE and 0xFEE0FEE0FEF1FEF1 + * + */ + +#define WEAK_KEY_COUNT 16 + +static const unsigned char weak_key_table[WEAK_KEY_COUNT][DES_KEY_SIZE] = +{ + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE }, + { 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E }, + { 0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1 }, + + { 0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E }, + { 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01 }, + { 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1 }, + { 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01 }, + { 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE }, + { 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01 }, + { 0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1 }, + { 0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E }, + { 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE }, + { 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E }, + { 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE }, + { 0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1 } +}; + +int des_key_check_weak( const unsigned char key[DES_KEY_SIZE] ) +{ + int i; + + for( i = 0; i < WEAK_KEY_COUNT; i++ ) + if( memcmp( weak_key_table[i], key, DES_KEY_SIZE) == 0 ) + return( 1 ); + + return( 0 ); +} + +static void des_setkey( uint32_t SK[32], const unsigned char key[DES_KEY_SIZE] ) +{ + int i; + uint32_t X, Y, T; + + GET_UINT32_BE( X, key, 0 ); + GET_UINT32_BE( Y, key, 4 ); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} + +/* + * DES key schedule (56-bit, encryption) + */ +int des_setkey_enc( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ) +{ +#ifdef RTL_HW_CRYPTO + memcpy(ctx->enc_key, key, DES_KEY_SIZE); +#endif + des_setkey( ctx->sk, key ); + return( 0 ); +} + +/* + * DES key schedule (56-bit, decryption) + */ +int des_setkey_dec( des_context *ctx, const unsigned char key[DES_KEY_SIZE] ) +{ + int i; +#ifdef RTL_HW_CRYPTO + memcpy(ctx->dec_key, key, DES_KEY_SIZE); +#endif + des_setkey( ctx->sk, key ); + + for( i = 0; i < 16; i += 2 ) + { + SWAP( ctx->sk[i ], ctx->sk[30 - i] ); + SWAP( ctx->sk[i + 1], ctx->sk[31 - i] ); + } + + return( 0 ); +} + +static void des3_set2key( uint32_t esk[96], + uint32_t dsk[96], + const unsigned char key[DES_KEY_SIZE*2] ) +{ + int i; + + des_setkey( esk, key ); + des_setkey( dsk + 32, key + 8 ); + + for( i = 0; i < 32; i += 2 ) + { + dsk[i ] = esk[30 - i]; + dsk[i + 1] = esk[31 - i]; + + esk[i + 32] = dsk[62 - i]; + esk[i + 33] = dsk[63 - i]; + + esk[i + 64] = esk[i ]; + esk[i + 65] = esk[i + 1]; + + dsk[i + 64] = dsk[i ]; + dsk[i + 65] = dsk[i + 1]; + } +} + +/* + * Triple-DES key schedule (112-bit, encryption) + */ +int des3_set2key_enc( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 2] ) +{ + uint32_t sk[96]; +#ifdef RTL_HW_CRYPTO + memcpy(ctx->enc_key, key, DES_KEY_SIZE * 2); + memcpy(ctx->enc_key + DES_KEY_SIZE * 2, key, DES_KEY_SIZE); +#endif + des3_set2key( ctx->sk, sk, key ); + polarssl_zeroize( sk, sizeof( sk ) ); + + return( 0 ); +} + +/* + * Triple-DES key schedule (112-bit, decryption) + */ +int des3_set2key_dec( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 2] ) +{ + uint32_t sk[96]; +#ifdef RTL_HW_CRYPTO + memcpy(ctx->dec_key, key, DES_KEY_SIZE * 2); + memcpy(ctx->dec_key + DES_KEY_SIZE * 2, key, DES_KEY_SIZE); +#endif + des3_set2key( sk, ctx->sk, key ); + polarssl_zeroize( sk, sizeof( sk ) ); + + return( 0 ); +} + +static void des3_set3key( uint32_t esk[96], + uint32_t dsk[96], + const unsigned char key[24] ) +{ + int i; + + des_setkey( esk, key ); + des_setkey( dsk + 32, key + 8 ); + des_setkey( esk + 64, key + 16 ); + + for( i = 0; i < 32; i += 2 ) + { + dsk[i ] = esk[94 - i]; + dsk[i + 1] = esk[95 - i]; + + esk[i + 32] = dsk[62 - i]; + esk[i + 33] = dsk[63 - i]; + + dsk[i + 64] = esk[30 - i]; + dsk[i + 65] = esk[31 - i]; + } +} + +/* + * Triple-DES key schedule (168-bit, encryption) + */ +int des3_set3key_enc( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 3] ) +{ + uint32_t sk[96]; +#ifdef RTL_HW_CRYPTO + memcpy(ctx->enc_key, key, DES_KEY_SIZE * 3); +#endif + des3_set3key( ctx->sk, sk, key ); + polarssl_zeroize( sk, sizeof( sk ) ); + + return( 0 ); +} + +/* + * Triple-DES key schedule (168-bit, decryption) + */ +int des3_set3key_dec( des3_context *ctx, + const unsigned char key[DES_KEY_SIZE * 3] ) +{ + uint32_t sk[96]; +#ifdef RTL_HW_CRYPTO + memcpy(ctx->dec_key, key, DES_KEY_SIZE * 3); +#endif + des3_set3key( sk, ctx->sk, key ); + polarssl_zeroize( sk, sizeof( sk ) ); + + return( 0 ); +} + +/* + * DES-ECB block encryption/decryption + */ +int des_crypt_ecb( des_context *ctx, + const unsigned char input[8], + unsigned char output[8] ) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE( X, input, 0 ); + GET_UINT32_BE( Y, input, 4 ); + + DES_IP( X, Y ); + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( Y, X ); + DES_ROUND( X, Y ); + } + + DES_FP( Y, X ); + + PUT_UINT32_BE( Y, output, 0 ); + PUT_UINT32_BE( X, output, 4 ); + + return( 0 ); + +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * DES-CBC buffer encryption/decryption + */ +int des_crypt_cbc( des_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output ) +{ +#ifdef RTL_HW_CRYPTO + unsigned char key_buf[8 + 4], *key_buf_aligned; + unsigned char iv_buf[8 + 4], *iv_buf_aligned, iv_tmp[8]; + unsigned char *output_buf, *output_buf_aligned; + + if(length % 8) + return(POLARSSL_ERR_DES_INVALID_INPUT_LENGTH); + + if(length > 0) + { + key_buf_aligned = (unsigned char *) (((unsigned int) key_buf + 4) / 4 * 4); + iv_buf_aligned = (unsigned char *) (((unsigned int) iv_buf + 4) / 4 * 4); + output_buf = polarssl_malloc(length + 4); + output_buf_aligned = (unsigned char *) (((unsigned int) output_buf + 4) / 4 * 4); + + memcpy(iv_buf_aligned, iv, 8); + memset(output_buf, 0, length + 4); + + if(mode == DES_DECRYPT) + { + memcpy(key_buf_aligned, ctx->dec_key, DES_KEY_SIZE); + memcpy(iv_tmp, (input + length - 8), 8); + rtl_crypto_des_cbc_init(key_buf_aligned, DES_KEY_SIZE); + rtl_crypto_des_cbc_decrypt(input, length, iv_buf_aligned, 8, output_buf_aligned); + memcpy(iv, iv_tmp, 8); + } + else + { + memcpy(key_buf_aligned, ctx->enc_key, DES_KEY_SIZE); + rtl_crypto_des_cbc_init(key_buf_aligned, DES_KEY_SIZE); + rtl_crypto_des_cbc_encrypt(input, length, iv_buf_aligned, 8, output_buf_aligned); + memcpy(iv, (output_buf_aligned + length - 8), 8); + } + + memcpy(output, output_buf_aligned, length); + polarssl_free(output_buf); + } + + return 0; +#else /* RTL_HW_CRYPTO */ + int i; + unsigned char temp[8]; + + if( length % 8 ) + return( POLARSSL_ERR_DES_INVALID_INPUT_LENGTH ); + + if( mode == DES_ENCRYPT ) + { + while( length > 0 ) + { + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + des_crypt_ecb( ctx, output, output ); + memcpy( iv, output, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + else /* DES_DECRYPT */ + { + while( length > 0 ) + { + memcpy( temp, input, 8 ); + des_crypt_ecb( ctx, input, output ); + + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + + return( 0 ); +#endif +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +/* + * 3DES-ECB block encryption/decryption + */ +int des3_crypt_ecb( des3_context *ctx, + const unsigned char input[8], + unsigned char output[8] ) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + GET_UINT32_BE( X, input, 0 ); + GET_UINT32_BE( Y, input, 4 ); + + DES_IP( X, Y ); + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( Y, X ); + DES_ROUND( X, Y ); + } + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( X, Y ); + DES_ROUND( Y, X ); + } + + for( i = 0; i < 8; i++ ) + { + DES_ROUND( Y, X ); + DES_ROUND( X, Y ); + } + + DES_FP( Y, X ); + + PUT_UINT32_BE( Y, output, 0 ); + PUT_UINT32_BE( X, output, 4 ); + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * 3DES-CBC buffer encryption/decryption + */ +int des3_crypt_cbc( des3_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output ) +{ +#ifdef RTL_HW_CRYPTO + unsigned char key_buf[24 + 4], *key_buf_aligned; + unsigned char iv_buf[8 + 4], *iv_buf_aligned, iv_tmp[8]; + unsigned char *output_buf, *output_buf_aligned; + + if(length % 8) + return(POLARSSL_ERR_DES_INVALID_INPUT_LENGTH); + + if(length > 0) + { + key_buf_aligned = (unsigned char *) (((unsigned int) key_buf + 4) / 4 * 4); + iv_buf_aligned = (unsigned char *) (((unsigned int) iv_buf + 4) / 4 * 4); + output_buf = polarssl_malloc(length + 4); + output_buf_aligned = (unsigned char *) (((unsigned int) output_buf + 4) / 4 * 4); + + memcpy(iv_buf_aligned, iv, 8); + memset(output_buf, 0, length + 4); + + if(mode == DES_DECRYPT) + { + memcpy(key_buf_aligned, ctx->dec_key, DES_KEY_SIZE * 3); + memcpy(iv_tmp, (input + length - 8), 8); + rtl_crypto_3des_cbc_init(key_buf_aligned, DES_KEY_SIZE * 3); + rtl_crypto_3des_cbc_decrypt(input, length, iv_buf_aligned, 8, output_buf_aligned); + memcpy(iv, iv_tmp, 8); + } + else + { + memcpy(key_buf_aligned, ctx->enc_key, DES_KEY_SIZE * 3); + rtl_crypto_3des_cbc_init(key_buf_aligned, DES_KEY_SIZE * 3); + rtl_crypto_3des_cbc_encrypt(input, length, iv_buf_aligned, 8, output_buf_aligned); + memcpy(iv, (output_buf_aligned + length - 8), 8); + } + + memcpy(output, output_buf_aligned, length); + polarssl_free(output_buf); + } + + return 0; +#else /* RTL_HW_CRYPTO */ + int i; + unsigned char temp[8]; + + if( length % 8 ) + return( POLARSSL_ERR_DES_INVALID_INPUT_LENGTH ); + + if( mode == DES_ENCRYPT ) + { + while( length > 0 ) + { + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + des3_crypt_ecb( ctx, output, output ); + memcpy( iv, output, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + else /* DES_DECRYPT */ + { + while( length > 0 ) + { + memcpy( temp, input, 8 ); + des3_crypt_ecb( ctx, input, output ); + + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + + return( 0 ); +#endif +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#endif /* !POLARSSL_DES_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include + +/* + * DES and 3DES test vectors from: + * + * http://csrc.nist.gov/groups/STM/cavp/documents/des/tripledes-vectors.zip + */ +static const unsigned char des3_test_keys[24] = +{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, + 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 +}; + +static const unsigned char des3_test_buf[8] = +{ + 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 +}; + +static const unsigned char des3_test_ecb_dec[3][8] = +{ + { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D }, + { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB }, + { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A } +}; + +static const unsigned char des3_test_ecb_enc[3][8] = +{ + { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B }, + { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 }, + { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 } +}; + +#if defined(POLARSSL_CIPHER_MODE_CBC) +static const unsigned char des3_test_iv[8] = +{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, +}; + +static const unsigned char des3_test_cbc_dec[3][8] = +{ + { 0x12, 0x9F, 0x40, 0xB9, 0xD2, 0x00, 0x56, 0xB3 }, + { 0x47, 0x0E, 0xFC, 0x9A, 0x6B, 0x8E, 0xE3, 0x93 }, + { 0xC5, 0xCE, 0xCF, 0x63, 0xEC, 0xEC, 0x51, 0x4C } +}; + +static const unsigned char des3_test_cbc_enc[3][8] = +{ + { 0x54, 0xF1, 0x5A, 0xF6, 0xEB, 0xE3, 0xA4, 0xB4 }, + { 0x35, 0x76, 0x11, 0x56, 0x5F, 0xA1, 0x8E, 0x4D }, + { 0xCB, 0x19, 0x1F, 0x85, 0xD1, 0xED, 0x84, 0x39 } +}; +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +/* + * Checkup routine + */ +int des_self_test( int verbose ) +{ + int i, j, u, v, ret = 0; + des_context ctx; + des3_context ctx3; + unsigned char buf[8]; +#if defined(POLARSSL_CIPHER_MODE_CBC) + unsigned char prv[8]; + unsigned char iv[8]; +#endif + + des_init( &ctx ); + des3_init( &ctx3 ); + /* + * ECB mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " DES%c-ECB-%3d (%s): ", + ( u == 0 ) ? ' ' : '3', 56 + u * 56, + ( v == DES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( buf, des3_test_buf, 8 ); + + switch( i ) + { + case 0: + des_setkey_dec( &ctx, des3_test_keys ); + break; + + case 1: + des_setkey_enc( &ctx, des3_test_keys ); + break; + + case 2: + des3_set2key_dec( &ctx3, des3_test_keys ); + break; + + case 3: + des3_set2key_enc( &ctx3, des3_test_keys ); + break; + + case 4: + des3_set3key_dec( &ctx3, des3_test_keys ); + break; + + case 5: + des3_set3key_enc( &ctx3, des3_test_keys ); + break; + + default: + return( 1 ); + } + + for( j = 0; j < 10000; j++ ) + { + if( u == 0 ) + des_crypt_ecb( &ctx, buf, buf ); + else + des3_crypt_ecb( &ctx3, buf, buf ); + } + + if( ( v == DES_DECRYPT && + memcmp( buf, des3_test_ecb_dec[u], 8 ) != 0 ) || + ( v != DES_DECRYPT && + memcmp( buf, des3_test_ecb_enc[u], 8 ) != 0 ) ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +#if defined(POLARSSL_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for( i = 0; i < 6; i++ ) + { + u = i >> 1; + v = i & 1; + + if( verbose != 0 ) + polarssl_printf( " DES%c-CBC-%3d (%s): ", + ( u == 0 ) ? ' ' : '3', 56 + u * 56, + ( v == DES_DECRYPT ) ? "dec" : "enc" ); + + memcpy( iv, des3_test_iv, 8 ); + memcpy( prv, des3_test_iv, 8 ); + memcpy( buf, des3_test_buf, 8 ); + + switch( i ) + { + case 0: + des_setkey_dec( &ctx, des3_test_keys ); + break; + + case 1: + des_setkey_enc( &ctx, des3_test_keys ); + break; + + case 2: + des3_set2key_dec( &ctx3, des3_test_keys ); + break; + + case 3: + des3_set2key_enc( &ctx3, des3_test_keys ); + break; + + case 4: + des3_set3key_dec( &ctx3, des3_test_keys ); + break; + + case 5: + des3_set3key_enc( &ctx3, des3_test_keys ); + break; + + default: + return( 1 ); + } + + if( v == DES_DECRYPT ) + { + for( j = 0; j < 10000; j++ ) + { + if( u == 0 ) + des_crypt_cbc( &ctx, v, 8, iv, buf, buf ); + else + des3_crypt_cbc( &ctx3, v, 8, iv, buf, buf ); + } + } + else + { + for( j = 0; j < 10000; j++ ) + { + unsigned char tmp[8]; + + if( u == 0 ) + des_crypt_cbc( &ctx, v, 8, iv, buf, buf ); + else + des3_crypt_cbc( &ctx3, v, 8, iv, buf, buf ); + + memcpy( tmp, prv, 8 ); + memcpy( prv, buf, 8 ); + memcpy( buf, tmp, 8 ); + } + + memcpy( buf, prv, 8 ); + } + + if( ( v == DES_DECRYPT && + memcmp( buf, des3_test_cbc_dec[u], 8 ) != 0 ) || + ( v != DES_DECRYPT && + memcmp( buf, des3_test_cbc_enc[u], 8 ) != 0 ) ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } +#endif /* POLARSSL_CIPHER_MODE_CBC */ + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + des_free( &ctx ); + des3_free( &ctx3 ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_DES_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/dhm.c b/component/common/network/ssl/polarssl-1.3.8/library/dhm.c new file mode 100644 index 0000000..089c11b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/dhm.c @@ -0,0 +1,598 @@ +/* + * Diffie-Hellman-Merkle key exchange + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * Reference: + * + * http://www.cacr.math.uwaterloo.ca/hac/ (chapter 12) + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_DHM_C) + +#include "polarssl/dhm.h" + +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_ASN1_PARSE_C) +#include "polarssl/asn1.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_printf printf +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * helper to validate the mpi size and import it + */ +static int dhm_read_bignum( mpi *X, + unsigned char **p, + const unsigned char *end ) +{ + int ret, n; + + if( end - *p < 2 ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + n = ( (*p)[0] << 8 ) | (*p)[1]; + (*p) += 2; + + if( (int)( end - *p ) < n ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + if( ( ret = mpi_read_binary( X, *p, n ) ) != 0 ) + return( POLARSSL_ERR_DHM_READ_PARAMS_FAILED + ret ); + + (*p) += n; + + return( 0 ); +} + +/* + * Verify sanity of parameter with regards to P + * + * Parameter should be: 2 <= public_param <= P - 2 + * + * For more information on the attack, see: + * http://www.cl.cam.ac.uk/~rja14/Papers/psandqs.pdf + * http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-2643 + */ +static int dhm_check_range( const mpi *param, const mpi *P ) +{ + mpi L, U; + int ret = POLARSSL_ERR_DHM_BAD_INPUT_DATA; + + mpi_init( &L ); mpi_init( &U ); + + MPI_CHK( mpi_lset( &L, 2 ) ); + MPI_CHK( mpi_sub_int( &U, P, 2 ) ); + + if( mpi_cmp_mpi( param, &L ) >= 0 && + mpi_cmp_mpi( param, &U ) <= 0 ) + { + ret = 0; + } + +cleanup: + mpi_free( &L ); mpi_free( &U ); + return( ret ); +} + +void dhm_init( dhm_context *ctx ) +{ + memset( ctx, 0, sizeof( dhm_context ) ); +} + +/* + * Parse the ServerKeyExchange parameters + */ +int dhm_read_params( dhm_context *ctx, + unsigned char **p, + const unsigned char *end ) +{ + int ret; + + if( ( ret = dhm_read_bignum( &ctx->P, p, end ) ) != 0 || + ( ret = dhm_read_bignum( &ctx->G, p, end ) ) != 0 || + ( ret = dhm_read_bignum( &ctx->GY, p, end ) ) != 0 ) + return( ret ); + + if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) + return( ret ); + + ctx->len = mpi_size( &ctx->P ); + + return( 0 ); +} + +/* + * Setup and write the ServerKeyExchange parameters + */ +int dhm_make_params( dhm_context *ctx, int x_size, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret, count = 0; + size_t n1, n2, n3; + unsigned char *p; + + if( mpi_cmp_int( &ctx->P, 0 ) == 0 ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + /* + * Generate X as large as possible ( < P ) + */ + do + { + mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ); + + while( mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) + MPI_CHK( mpi_shift_r( &ctx->X, 1 ) ); + + if( count++ > 10 ) + return( POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED ); + } + while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); + + /* + * Calculate GX = G^X mod P + */ + MPI_CHK( mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, + &ctx->P , &ctx->RP ) ); + + if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) + return( ret ); + + /* + * export P, G, GX + */ +#define DHM_MPI_EXPORT(X,n) \ + MPI_CHK( mpi_write_binary( X, p + 2, n ) ); \ + *p++ = (unsigned char)( n >> 8 ); \ + *p++ = (unsigned char)( n ); p += n; + + n1 = mpi_size( &ctx->P ); + n2 = mpi_size( &ctx->G ); + n3 = mpi_size( &ctx->GX ); + + p = output; + DHM_MPI_EXPORT( &ctx->P , n1 ); + DHM_MPI_EXPORT( &ctx->G , n2 ); + DHM_MPI_EXPORT( &ctx->GX, n3 ); + + *olen = p - output; + + ctx->len = n1; + +cleanup: + + if( ret != 0 ) + return( POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED + ret ); + + return( 0 ); +} + +/* + * Import the peer's public value G^Y + */ +int dhm_read_public( dhm_context *ctx, + const unsigned char *input, size_t ilen ) +{ + int ret; + + if( ctx == NULL || ilen < 1 || ilen > ctx->len ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + if( ( ret = mpi_read_binary( &ctx->GY, input, ilen ) ) != 0 ) + return( POLARSSL_ERR_DHM_READ_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Create own private value X and export G^X + */ +int dhm_make_public( dhm_context *ctx, int x_size, + unsigned char *output, size_t olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret, count = 0; + + if( ctx == NULL || olen < 1 || olen > ctx->len ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + if( mpi_cmp_int( &ctx->P, 0 ) == 0 ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + /* + * generate X and calculate GX = G^X mod P + */ + do + { + mpi_fill_random( &ctx->X, x_size, f_rng, p_rng ); + + while( mpi_cmp_mpi( &ctx->X, &ctx->P ) >= 0 ) + MPI_CHK( mpi_shift_r( &ctx->X, 1 ) ); + + if( count++ > 10 ) + return( POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED ); + } + while( dhm_check_range( &ctx->X, &ctx->P ) != 0 ); + + MPI_CHK( mpi_exp_mod( &ctx->GX, &ctx->G, &ctx->X, + &ctx->P , &ctx->RP ) ); + + if( ( ret = dhm_check_range( &ctx->GX, &ctx->P ) ) != 0 ) + return( ret ); + + MPI_CHK( mpi_write_binary( &ctx->GX, output, olen ) ); + +cleanup: + + if( ret != 0 ) + return( POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +/* + * Use the blinding method and optimisation suggested in section 10 of: + * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, + * DSS, and other systems. In : Advances in Cryptology—CRYPTO’96. Springer + * Berlin Heidelberg, 1996. p. 104-113. + */ +static int dhm_update_blinding( dhm_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret, count; + + /* + * Don't use any blinding the first time a particular X is used, + * but remember it to use blinding next time. + */ + if( mpi_cmp_mpi( &ctx->X, &ctx->pX ) != 0 ) + { + MPI_CHK( mpi_copy( &ctx->pX, &ctx->X ) ); + MPI_CHK( mpi_lset( &ctx->Vi, 1 ) ); + MPI_CHK( mpi_lset( &ctx->Vf, 1 ) ); + + return( 0 ); + } + + /* + * Ok, we need blinding. Can we re-use existing values? + * If yes, just update them by squaring them. + */ + if( mpi_cmp_int( &ctx->Vi, 1 ) != 0 ) + { + MPI_CHK( mpi_mul_mpi( &ctx->Vi, &ctx->Vi, &ctx->Vi ) ); + MPI_CHK( mpi_mod_mpi( &ctx->Vi, &ctx->Vi, &ctx->P ) ); + + MPI_CHK( mpi_mul_mpi( &ctx->Vf, &ctx->Vf, &ctx->Vf ) ); + MPI_CHK( mpi_mod_mpi( &ctx->Vf, &ctx->Vf, &ctx->P ) ); + + return( 0 ); + } + + /* + * We need to generate blinding values from scratch + */ + + /* Vi = random( 2, P-1 ) */ + count = 0; + do + { + mpi_fill_random( &ctx->Vi, mpi_size( &ctx->P ), f_rng, p_rng ); + + while( mpi_cmp_mpi( &ctx->Vi, &ctx->P ) >= 0 ) + MPI_CHK( mpi_shift_r( &ctx->Vi, 1 ) ); + + if( count++ > 10 ) + return( POLARSSL_ERR_MPI_NOT_ACCEPTABLE ); + } + while( mpi_cmp_int( &ctx->Vi, 1 ) <= 0 ); + + /* Vf = Vi^-X mod P */ + MPI_CHK( mpi_inv_mod( &ctx->Vf, &ctx->Vi, &ctx->P ) ); + MPI_CHK( mpi_exp_mod( &ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP ) ); + +cleanup: + return( ret ); +} + +/* + * Derive and export the shared secret (G^Y)^X mod P + */ +int dhm_calc_secret( dhm_context *ctx, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + mpi GYb; + + if( ctx == NULL || *olen < ctx->len ) + return( POLARSSL_ERR_DHM_BAD_INPUT_DATA ); + + if( ( ret = dhm_check_range( &ctx->GY, &ctx->P ) ) != 0 ) + return( ret ); + + mpi_init( &GYb ); + + /* Blind peer's value */ + if( f_rng != NULL ) + { + MPI_CHK( dhm_update_blinding( ctx, f_rng, p_rng ) ); + MPI_CHK( mpi_mul_mpi( &GYb, &ctx->GY, &ctx->Vi ) ); + MPI_CHK( mpi_mod_mpi( &GYb, &GYb, &ctx->P ) ); + } + else + MPI_CHK( mpi_copy( &GYb, &ctx->GY ) ); + + /* Do modular exponentiation */ + MPI_CHK( mpi_exp_mod( &ctx->K, &GYb, &ctx->X, + &ctx->P, &ctx->RP ) ); + + /* Unblind secret value */ + if( f_rng != NULL ) + { + MPI_CHK( mpi_mul_mpi( &ctx->K, &ctx->K, &ctx->Vf ) ); + MPI_CHK( mpi_mod_mpi( &ctx->K, &ctx->K, &ctx->P ) ); + } + + *olen = mpi_size( &ctx->K ); + + MPI_CHK( mpi_write_binary( &ctx->K, output, *olen ) ); + +cleanup: + mpi_free( &GYb ); + + if( ret != 0 ) + return( POLARSSL_ERR_DHM_CALC_SECRET_FAILED + ret ); + + return( 0 ); +} + +/* + * Free the components of a DHM key + */ +void dhm_free( dhm_context *ctx ) +{ + mpi_free( &ctx->pX); mpi_free( &ctx->Vf ); mpi_free( &ctx->Vi ); + mpi_free( &ctx->RP ); mpi_free( &ctx->K ); mpi_free( &ctx->GY ); + mpi_free( &ctx->GX ); mpi_free( &ctx->X ); mpi_free( &ctx->G ); + mpi_free( &ctx->P ); + + polarssl_zeroize( ctx, sizeof( dhm_context ) ); +} + +#if defined(POLARSSL_ASN1_PARSE_C) +/* + * Parse DHM parameters + */ +int dhm_parse_dhm( dhm_context *dhm, const unsigned char *dhmin, + size_t dhminlen ) +{ + int ret; + size_t len; + unsigned char *p, *end; +#if defined(POLARSSL_PEM_PARSE_C) + pem_context pem; + + pem_init( &pem ); + + ret = pem_read_buffer( &pem, + "-----BEGIN DH PARAMETERS-----", + "-----END DH PARAMETERS-----", + dhmin, NULL, 0, &dhminlen ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + dhminlen = pem.buflen; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + goto exit; + + p = ( ret == 0 ) ? pem.buf : (unsigned char *) dhmin; +#else + p = (unsigned char *) dhmin; +#endif /* POLARSSL_PEM_PARSE_C */ + end = p + dhminlen; + + /* + * DHParams ::= SEQUENCE { + * prime INTEGER, -- P + * generator INTEGER, -- g + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + ret = POLARSSL_ERR_DHM_INVALID_FORMAT + ret; + goto exit; + } + + end = p + len; + + if( ( ret = asn1_get_mpi( &p, end, &dhm->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &dhm->G ) ) != 0 ) + { + ret = POLARSSL_ERR_DHM_INVALID_FORMAT + ret; + goto exit; + } + + if( p != end ) + { + ret = POLARSSL_ERR_DHM_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH; + goto exit; + } + + ret = 0; + + dhm->len = mpi_size( &dhm->P ); + +exit: +#if defined(POLARSSL_PEM_PARSE_C) + pem_free( &pem ); +#endif + if( ret != 0 ) + dhm_free( dhm ); + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load all data from a file into a given buffer. + */ +static int load_file( const char *path, unsigned char **buf, size_t *n ) +{ + FILE *f; + long size; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_DHM_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + if( ( size = ftell( f ) ) == -1 ) + { + fclose( f ); + return( POLARSSL_ERR_DHM_FILE_IO_ERROR ); + } + fseek( f, 0, SEEK_SET ); + + *n = (size_t) size; + + if( *n + 1 == 0 || + ( *buf = (unsigned char *) polarssl_malloc( *n + 1 ) ) == NULL ) + { + fclose( f ); + return( POLARSSL_ERR_DHM_MALLOC_FAILED ); + } + + if( fread( *buf, 1, *n, f ) != *n ) + { + fclose( f ); + polarssl_free( *buf ); + return( POLARSSL_ERR_DHM_FILE_IO_ERROR ); + } + + fclose( f ); + + (*buf)[*n] = '\0'; + + return( 0 ); +} + +/* + * Load and parse DHM parameters + */ +int dhm_parse_dhmfile( dhm_context *dhm, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = dhm_parse_dhm( dhm, buf, n ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ +#endif /* POLARSSL_ASN1_PARSE_C */ + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/certs.h" + +/* + * Checkup routine + */ +int dhm_self_test( int verbose ) +{ +#if defined(POLARSSL_CERTS_C) + int ret; + dhm_context dhm; + + dhm_init( &dhm ); + + if( verbose != 0 ) + polarssl_printf( " DHM parameter load: " ); + + if( ( ret = dhm_parse_dhm( &dhm, (const unsigned char *) test_dhm_params, + strlen( test_dhm_params ) ) ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n\n" ); + +exit: + dhm_free( &dhm ); + + return( ret ); +#else + if( verbose != 0 ) + polarssl_printf( " DHM parameter load: skipped\n" ); + + return( 0 ); +#endif /* POLARSSL_CERTS_C */ +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_DHM_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ecdh.c b/component/common/network/ssl/polarssl-1.3.8/library/ecdh.c new file mode 100644 index 0000000..b93d82e --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ecdh.c @@ -0,0 +1,280 @@ +/* + * Elliptic curve Diffie-Hellman + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * RFC 4492 + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ECDH_C) + +#include "polarssl/ecdh.h" + +/* + * Generate public key: simple wrapper around ecp_gen_keypair + */ +int ecdh_gen_public( ecp_group *grp, mpi *d, ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + return ecp_gen_keypair( grp, d, Q, f_rng, p_rng ); +} + +/* + * Compute shared secret (SEC1 3.3.1) + */ +int ecdh_compute_shared( ecp_group *grp, mpi *z, + const ecp_point *Q, const mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + ecp_point P; + + ecp_point_init( &P ); + + /* + * Make sure Q is a valid pubkey before using it + */ + MPI_CHK( ecp_check_pubkey( grp, Q ) ); + + MPI_CHK( ecp_mul( grp, &P, d, Q, f_rng, p_rng ) ); + + if( ecp_is_zero( &P ) ) + { + ret = POLARSSL_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + MPI_CHK( mpi_copy( z, &P.X ) ); + +cleanup: + ecp_point_free( &P ); + + return( ret ); +} + +/* + * Initialize context + */ +void ecdh_init( ecdh_context *ctx ) +{ + memset( ctx, 0, sizeof( ecdh_context ) ); +} + +/* + * Free context + */ +void ecdh_free( ecdh_context *ctx ) +{ + if( ctx == NULL ) + return; + + ecp_group_free( &ctx->grp ); + ecp_point_free( &ctx->Q ); + ecp_point_free( &ctx->Qp ); + ecp_point_free( &ctx->Vi ); + ecp_point_free( &ctx->Vf ); + mpi_free( &ctx->d ); + mpi_free( &ctx->z ); + mpi_free( &ctx->_d ); +} + +/* + * Setup and write the ServerKeyExhange parameters (RFC 4492) + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ +int ecdh_make_params( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t grp_len, pt_len; + + if( ctx == NULL || ctx->grp.pbits == 0 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecdh_gen_public( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) ) + != 0 ) + return( ret ); + + if( ( ret = ecp_tls_write_group( &ctx->grp, &grp_len, buf, blen ) ) + != 0 ) + return( ret ); + + buf += grp_len; + blen -= grp_len; + + if( ( ret = ecp_tls_write_point( &ctx->grp, &ctx->Q, ctx->point_format, + &pt_len, buf, blen ) ) != 0 ) + return( ret ); + + *olen = grp_len + pt_len; + return( 0 ); +} + +/* + * Read the ServerKeyExhange parameters (RFC 4492) + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ +int ecdh_read_params( ecdh_context *ctx, + const unsigned char **buf, const unsigned char *end ) +{ + int ret; + + if( ( ret = ecp_tls_read_group( &ctx->grp, buf, end - *buf ) ) != 0 ) + return( ret ); + + if( ( ret = ecp_tls_read_point( &ctx->grp, &ctx->Qp, buf, end - *buf ) ) + != 0 ) + return( ret ); + + return( 0 ); +} + +/* + * Get parameters from a keypair + */ +int ecdh_get_params( ecdh_context *ctx, const ecp_keypair *key, + ecdh_side side ) +{ + int ret; + + if( ( ret = ecp_group_copy( &ctx->grp, &key->grp ) ) != 0 ) + return( ret ); + + /* If it's not our key, just import the public part as Qp */ + if( side == POLARSSL_ECDH_THEIRS ) + return( ecp_copy( &ctx->Qp, &key->Q ) ); + + /* Our key: import public (as Q) and private parts */ + if( side != POLARSSL_ECDH_OURS ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecp_copy( &ctx->Q, &key->Q ) ) != 0 || + ( ret = mpi_copy( &ctx->d, &key->d ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +/* + * Setup and export the client public value + */ +int ecdh_make_public( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + if( ctx == NULL || ctx->grp.pbits == 0 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecdh_gen_public( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) ) + != 0 ) + return( ret ); + + return ecp_tls_write_point( &ctx->grp, &ctx->Q, ctx->point_format, + olen, buf, blen ); +} + +/* + * Parse and import the client's public value + */ +int ecdh_read_public( ecdh_context *ctx, + const unsigned char *buf, size_t blen ) +{ + int ret; + const unsigned char *p = buf; + + if( ctx == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecp_tls_read_point( &ctx->grp, &ctx->Qp, &p, blen ) ) != 0 ) + return( ret ); + + if( (size_t)( p - buf ) != blen ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + return( 0 ); +} + +/* + * Derive and export the shared secret + */ +int ecdh_calc_secret( ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + if( ctx == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecdh_compute_shared( &ctx->grp, &ctx->z, &ctx->Qp, &ctx->d, + f_rng, p_rng ) ) != 0 ) + { + return( ret ); + } + + if( mpi_size( &ctx->z ) > blen ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + *olen = ctx->grp.pbits / 8 + ( ( ctx->grp.pbits % 8 ) != 0 ); + return mpi_write_binary( &ctx->z, buf, *olen ); +} + + +#if defined(POLARSSL_SELF_TEST) + +/* + * Checkup routine + */ +int ecdh_self_test( int verbose ) +{ + ((void) verbose ); + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_ECDH_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ecdsa.c b/component/common/network/ssl/polarssl-1.3.8/library/ecdsa.c new file mode 100644 index 0000000..5af7f6b --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ecdsa.c @@ -0,0 +1,503 @@ +/* + * Elliptic curve DSA + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ECDSA_C) + +#include "polarssl/ecdsa.h" +#include "polarssl/asn1write.h" + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +#include "polarssl/hmac_drbg.h" +#endif + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +/* + * This a hopefully temporary compatibility function. + * + * Since we can't ensure the caller will pass a valid md_alg before the next + * interface change, try to pick up a decent md by size. + * + * Argument is the minimum size in bytes of the MD output. + */ +static const md_info_t *md_info_by_size( size_t min_size ) +{ + const md_info_t *md_cur, *md_picked = NULL; + const int *md_alg; + + for( md_alg = md_list(); *md_alg != 0; md_alg++ ) + { + if( ( md_cur = md_info_from_type( *md_alg ) ) == NULL || + (size_t) md_cur->size < min_size || + ( md_picked != NULL && md_cur->size > md_picked->size ) ) + continue; + + md_picked = md_cur; + } + + return( md_picked ); +} +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ + +/* + * Derive a suitable integer for group grp from a buffer of length len + * SEC1 4.1.3 step 5 aka SEC1 4.1.4 step 3 + */ +static int derive_mpi( const ecp_group *grp, mpi *x, + const unsigned char *buf, size_t blen ) +{ + int ret; + size_t n_size = ( grp->nbits + 7 ) / 8; + size_t use_size = blen > n_size ? n_size : blen; + + MPI_CHK( mpi_read_binary( x, buf, use_size ) ); + if( use_size * 8 > grp->nbits ) + MPI_CHK( mpi_shift_r( x, use_size * 8 - grp->nbits ) ); + + /* While at it, reduce modulo N */ + if( mpi_cmp_mpi( x, &grp->N ) >= 0 ) + MPI_CHK( mpi_sub_mpi( x, x, &grp->N ) ); + +cleanup: + return( ret ); +} + +/* + * Compute ECDSA signature of a hashed message (SEC1 4.1.3) + * Obviously, compared to SEC1 4.1.3, we skip step 4 (hash message) + */ +int ecdsa_sign( ecp_group *grp, mpi *r, mpi *s, + const mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret, key_tries, sign_tries, blind_tries; + ecp_point R; + mpi k, e, t; + + /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ + if( grp->N.p == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + ecp_point_init( &R ); + mpi_init( &k ); mpi_init( &e ); mpi_init( &t ); + + sign_tries = 0; + do + { + /* + * Steps 1-3: generate a suitable ephemeral keypair + * and set r = xR mod n + */ + key_tries = 0; + do + { + MPI_CHK( ecp_gen_keypair( grp, &k, &R, f_rng, p_rng ) ); + MPI_CHK( mpi_mod_mpi( r, &R.X, &grp->N ) ); + + if( key_tries++ > 10 ) + { + ret = POLARSSL_ERR_ECP_RANDOM_FAILED; + goto cleanup; + } + } + while( mpi_cmp_int( r, 0 ) == 0 ); + + /* + * Step 5: derive MPI from hashed message + */ + MPI_CHK( derive_mpi( grp, &e, buf, blen ) ); + + /* + * Generate a random value to blind inv_mod in next step, + * avoiding a potential timing leak. + */ + blind_tries = 0; + do + { + size_t n_size = ( grp->nbits + 7 ) / 8; + MPI_CHK( mpi_fill_random( &t, n_size, f_rng, p_rng ) ); + MPI_CHK( mpi_shift_r( &t, 8 * n_size - grp->nbits ) ); + + /* See ecp_gen_keypair() */ + if( ++blind_tries > 30 ) + return( POLARSSL_ERR_ECP_RANDOM_FAILED ); + } + while( mpi_cmp_int( &t, 1 ) < 0 || + mpi_cmp_mpi( &t, &grp->N ) >= 0 ); + + /* + * Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n + */ + MPI_CHK( mpi_mul_mpi( s, r, d ) ); + MPI_CHK( mpi_add_mpi( &e, &e, s ) ); + MPI_CHK( mpi_mul_mpi( &e, &e, &t ) ); + MPI_CHK( mpi_mul_mpi( &k, &k, &t ) ); + MPI_CHK( mpi_inv_mod( s, &k, &grp->N ) ); + MPI_CHK( mpi_mul_mpi( s, s, &e ) ); + MPI_CHK( mpi_mod_mpi( s, s, &grp->N ) ); + + if( sign_tries++ > 10 ) + { + ret = POLARSSL_ERR_ECP_RANDOM_FAILED; + goto cleanup; + } + } + while( mpi_cmp_int( s, 0 ) == 0 ); + +cleanup: + ecp_point_free( &R ); + mpi_free( &k ); mpi_free( &e ); mpi_free( &t ); + + return( ret ); +} + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +/* + * Deterministic signature wrapper + */ +int ecdsa_sign_det( ecp_group *grp, mpi *r, mpi *s, + const mpi *d, const unsigned char *buf, size_t blen, + md_type_t md_alg ) +{ + int ret; + hmac_drbg_context rng_ctx; + unsigned char data[2 * POLARSSL_ECP_MAX_BYTES]; + size_t grp_len = ( grp->nbits + 7 ) / 8; + const md_info_t *md_info; + mpi h; + + /* Temporary fallback */ + if( md_alg == POLARSSL_MD_NONE ) + md_info = md_info_by_size( blen ); + else + md_info = md_info_from_type( md_alg ); + + if( md_info == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + mpi_init( &h ); + memset( &rng_ctx, 0, sizeof( hmac_drbg_context ) ); + + /* Use private key and message hash (reduced) to initialize HMAC_DRBG */ + MPI_CHK( mpi_write_binary( d, data, grp_len ) ); + MPI_CHK( derive_mpi( grp, &h, buf, blen ) ); + MPI_CHK( mpi_write_binary( &h, data + grp_len, grp_len ) ); + hmac_drbg_init_buf( &rng_ctx, md_info, data, 2 * grp_len ); + + ret = ecdsa_sign( grp, r, s, d, buf, blen, + hmac_drbg_random, &rng_ctx ); + +cleanup: + hmac_drbg_free( &rng_ctx ); + mpi_free( &h ); + + return( ret ); +} +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ + +/* + * Verify ECDSA signature of hashed message (SEC1 4.1.4) + * Obviously, compared to SEC1 4.1.3, we skip step 2 (hash message) + */ +int ecdsa_verify( ecp_group *grp, + const unsigned char *buf, size_t blen, + const ecp_point *Q, const mpi *r, const mpi *s) +{ + int ret; + mpi e, s_inv, u1, u2; + ecp_point R, P; + + ecp_point_init( &R ); ecp_point_init( &P ); + mpi_init( &e ); mpi_init( &s_inv ); mpi_init( &u1 ); mpi_init( &u2 ); + + /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ + if( grp->N.p == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * Step 1: make sure r and s are in range 1..n-1 + */ + if( mpi_cmp_int( r, 1 ) < 0 || mpi_cmp_mpi( r, &grp->N ) >= 0 || + mpi_cmp_int( s, 1 ) < 0 || mpi_cmp_mpi( s, &grp->N ) >= 0 ) + { + ret = POLARSSL_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + + /* + * Additional precaution: make sure Q is valid + */ + MPI_CHK( ecp_check_pubkey( grp, Q ) ); + + /* + * Step 3: derive MPI from hashed message + */ + MPI_CHK( derive_mpi( grp, &e, buf, blen ) ); + + /* + * Step 4: u1 = e / s mod n, u2 = r / s mod n + */ + MPI_CHK( mpi_inv_mod( &s_inv, s, &grp->N ) ); + + MPI_CHK( mpi_mul_mpi( &u1, &e, &s_inv ) ); + MPI_CHK( mpi_mod_mpi( &u1, &u1, &grp->N ) ); + + MPI_CHK( mpi_mul_mpi( &u2, r, &s_inv ) ); + MPI_CHK( mpi_mod_mpi( &u2, &u2, &grp->N ) ); + + /* + * Step 5: R = u1 G + u2 Q + * + * Since we're not using any secret data, no need to pass a RNG to + * ecp_mul() for countermesures. + */ + MPI_CHK( ecp_mul( grp, &R, &u1, &grp->G, NULL, NULL ) ); + MPI_CHK( ecp_mul( grp, &P, &u2, Q, NULL, NULL ) ); + MPI_CHK( ecp_add( grp, &R, &R, &P ) ); + + if( ecp_is_zero( &R ) ) + { + ret = POLARSSL_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + + /* + * Step 6: convert xR to an integer (no-op) + * Step 7: reduce xR mod n (gives v) + */ + MPI_CHK( mpi_mod_mpi( &R.X, &R.X, &grp->N ) ); + + /* + * Step 8: check if v (that is, R.X) is equal to r + */ + if( mpi_cmp_mpi( &R.X, r ) != 0 ) + { + ret = POLARSSL_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + +cleanup: + ecp_point_free( &R ); ecp_point_free( &P ); + mpi_free( &e ); mpi_free( &s_inv ); mpi_free( &u1 ); mpi_free( &u2 ); + + return( ret ); +} + +/* + * RFC 4492 page 20: + * + * Ecdsa-Sig-Value ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * Size is at most + * 1 (tag) + 1 (len) + 1 (initial 0) + ECP_MAX_BYTES for each of r and s, + * twice that + 1 (tag) + 2 (len) for the sequence + * (assuming ECP_MAX_BYTES is less than 126 for r and s, + * and less than 124 (total len <= 255) for the sequence) + */ +#if POLARSSL_ECP_MAX_BYTES > 124 +#error "POLARSSL_ECP_MAX_BYTES bigger than expected, please fix MAX_SIG_LEN" +#endif +#define MAX_SIG_LEN ( 3 + 2 * ( 2 + POLARSSL_ECP_MAX_BYTES ) ) + +/* + * Convert a signature (given by context) to ASN.1 + */ +static int ecdsa_signature_to_asn1( ecdsa_context *ctx, + unsigned char *sig, size_t *slen ) +{ + int ret; + unsigned char buf[MAX_SIG_LEN]; + unsigned char *p = buf + sizeof( buf ); + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_mpi( &p, buf, &ctx->s ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &p, buf, &ctx->r ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &p, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &p, buf, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ); + + memcpy( sig, p, len ); + *slen = len; + + return( 0 ); +} + +/* + * Compute and write signature + */ +int ecdsa_write_signature( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + + if( ( ret = ecdsa_sign( &ctx->grp, &ctx->r, &ctx->s, &ctx->d, + hash, hlen, f_rng, p_rng ) ) != 0 ) + { + return( ret ); + } + + return( ecdsa_signature_to_asn1( ctx, sig, slen ) ); +} + +#if defined(POLARSSL_ECDSA_DETERMINISTIC) +/* + * Compute and write signature deterministically + */ +int ecdsa_write_signature_det( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t *slen, + md_type_t md_alg ) +{ + int ret; + + if( ( ret = ecdsa_sign_det( &ctx->grp, &ctx->r, &ctx->s, &ctx->d, + hash, hlen, md_alg ) ) != 0 ) + { + return( ret ); + } + + return( ecdsa_signature_to_asn1( ctx, sig, slen ) ); +} +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ + +/* + * Read and check signature + */ +int ecdsa_read_signature( ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen ) +{ + int ret; + unsigned char *p = (unsigned char *) sig; + const unsigned char *end = sig + slen; + size_t len; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA + ret ); + } + + if( p + len != end ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( ret = asn1_get_mpi( &p, end, &ctx->r ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &ctx->s ) ) != 0 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA + ret ); + + if( ( ret = ecdsa_verify( &ctx->grp, hash, hlen, + &ctx->Q, &ctx->r, &ctx->s ) ) != 0 ) + return( ret ); + + if( p != end ) + return( POLARSSL_ERR_ECP_SIG_LEN_MISMATCH ); + + return( 0 ); +} + +/* + * Generate key pair + */ +int ecdsa_genkey( ecdsa_context *ctx, ecp_group_id gid, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + return( ecp_use_known_dp( &ctx->grp, gid ) || + ecp_gen_keypair( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) ); +} + +/* + * Set context from an ecp_keypair + */ +int ecdsa_from_keypair( ecdsa_context *ctx, const ecp_keypair *key ) +{ + int ret; + + if( ( ret = ecp_group_copy( &ctx->grp, &key->grp ) ) != 0 || + ( ret = mpi_copy( &ctx->d, &key->d ) ) != 0 || + ( ret = ecp_copy( &ctx->Q, &key->Q ) ) != 0 ) + { + ecdsa_free( ctx ); + } + + return( ret ); +} + +/* + * Initialize context + */ +void ecdsa_init( ecdsa_context *ctx ) +{ + ecp_group_init( &ctx->grp ); + mpi_init( &ctx->d ); + ecp_point_init( &ctx->Q ); + mpi_init( &ctx->r ); + mpi_init( &ctx->s ); +} + +/* + * Free context + */ +void ecdsa_free( ecdsa_context *ctx ) +{ + ecp_group_free( &ctx->grp ); + mpi_free( &ctx->d ); + ecp_point_free( &ctx->Q ); + mpi_free( &ctx->r ); + mpi_free( &ctx->s ); +} + +#if defined(POLARSSL_SELF_TEST) + +/* + * Checkup routine + */ +int ecdsa_self_test( int verbose ) +{ + ((void) verbose ); + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_ECDSA_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ecp.c b/component/common/network/ssl/polarssl-1.3.8/library/ecp.c new file mode 100644 index 0000000..afa7955 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ecp.c @@ -0,0 +1,2029 @@ +/* + * Elliptic curves over GF(p): generic functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone + * FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf + * RFC 4492 for the related TLS structures and constants + * + * [M255] http://cr.yp.to/ecdh/curve25519-20060209.pdf + * + * [2] CORON, Jean-Sébastien. Resistance against differential power analysis + * for elliptic curve cryptosystems. In : Cryptographic Hardware and + * Embedded Systems. Springer Berlin Heidelberg, 1999. p. 292-302. + * + * + * [3] HEDABOU, Mustapha, PINEL, Pierre, et BÉNÉTEAU, Lucien. A comb method to + * render ECC resistant against Side Channel Attacks. IACR Cryptology + * ePrint Archive, 2004, vol. 2004, p. 342. + * + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ECP_C) + +#include "polarssl/ecp.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if defined(POLARSSL_SELF_TEST) +/* + * Counts of point addition and doubling, and field multiplications. + * Used to test resistance of point multiplication to simple timing attacks. + */ +static unsigned long add_count, dbl_count, mul_count; +#endif + +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_BP256R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_BP384R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_BP512R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) +#define POLARSSL_ECP_SHORT_WEIERSTRASS +#endif + +#if defined(POLARSSL_ECP_DP_M221_ENABLED) || \ + defined(POLARSSL_ECP_DP_M255_ENABLED) || \ + defined(POLARSSL_ECP_DP_M383_ENABLED) || \ + defined(POLARSSL_ECP_DP_M511_ENABLED) +#define POLARSSL_ECP_MONTGOMERY +#endif + +/* + * Curve types: internal for now, might be exposed later + */ +typedef enum +{ + POLARSSL_ECP_TYPE_NONE = 0, + POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS, /* y^2 = x^3 + a x + b */ + POLARSSL_ECP_TYPE_MONTGOMERY, /* y^2 = x^3 + a x^2 + x */ +} ecp_curve_type; + +/* + * List of supported curves: + * - internal ID + * - TLS NamedCurve ID (RFC 4492 sec. 5.1.1, RFC 7071 sec. 2) + * - size in bits + * - readable name + * + * Curves are listed in order: largest curves first, and for a given size, + * fastest curves first. This provides the default order for the SSL module. + */ +static const ecp_curve_info ecp_supported_curves[] = +{ +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) + { POLARSSL_ECP_DP_SECP521R1, 25, 521, "secp521r1" }, +#endif +#if defined(POLARSSL_ECP_DP_BP512R1_ENABLED) + { POLARSSL_ECP_DP_BP512R1, 28, 512, "brainpoolP512r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) + { POLARSSL_ECP_DP_SECP384R1, 24, 384, "secp384r1" }, +#endif +#if defined(POLARSSL_ECP_DP_BP384R1_ENABLED) + { POLARSSL_ECP_DP_BP384R1, 27, 384, "brainpoolP384r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) + { POLARSSL_ECP_DP_SECP256R1, 23, 256, "secp256r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) + { POLARSSL_ECP_DP_SECP256K1, 22, 256, "secp256k1" }, +#endif +#if defined(POLARSSL_ECP_DP_BP256R1_ENABLED) + { POLARSSL_ECP_DP_BP256R1, 26, 256, "brainpoolP256r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) + { POLARSSL_ECP_DP_SECP224R1, 21, 224, "secp224r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) + { POLARSSL_ECP_DP_SECP224K1, 20, 224, "secp224k1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) + { POLARSSL_ECP_DP_SECP192R1, 19, 192, "secp192r1" }, +#endif +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) + { POLARSSL_ECP_DP_SECP192K1, 18, 192, "secp192k1" }, +#endif + { POLARSSL_ECP_DP_NONE, 0, 0, NULL }, +}; + +#define ECP_NB_CURVES sizeof( ecp_supported_curves ) / \ + sizeof( ecp_supported_curves[0] ) + +static ecp_group_id ecp_supported_grp_id[ECP_NB_CURVES]; + +/* + * List of supported curves and associated info + */ +const ecp_curve_info *ecp_curve_list( void ) +{ + return( ecp_supported_curves ); +} + +/* + * List of supported curves, group ID only + */ +const ecp_group_id *ecp_grp_id_list( void ) +{ + static int init_done = 0; + + if( ! init_done ) + { + size_t i = 0; + const ecp_curve_info *curve_info; + + for( curve_info = ecp_curve_list(); + curve_info->grp_id != POLARSSL_ECP_DP_NONE; + curve_info++ ) + { + ecp_supported_grp_id[i++] = curve_info->grp_id; + } + ecp_supported_grp_id[i] = POLARSSL_ECP_DP_NONE; + + init_done = 1; + } + + return( ecp_supported_grp_id ); +} + +/* + * Get the curve info for the internal identifier + */ +const ecp_curve_info *ecp_curve_info_from_grp_id( ecp_group_id grp_id ) +{ + const ecp_curve_info *curve_info; + + for( curve_info = ecp_curve_list(); + curve_info->grp_id != POLARSSL_ECP_DP_NONE; + curve_info++ ) + { + if( curve_info->grp_id == grp_id ) + return( curve_info ); + } + + return( NULL ); +} + +/* + * Get the curve info from the TLS identifier + */ +const ecp_curve_info *ecp_curve_info_from_tls_id( uint16_t tls_id ) +{ + const ecp_curve_info *curve_info; + + for( curve_info = ecp_curve_list(); + curve_info->grp_id != POLARSSL_ECP_DP_NONE; + curve_info++ ) + { + if( curve_info->tls_id == tls_id ) + return( curve_info ); + } + + return( NULL ); +} + +/* + * Get the curve info from the name + */ +const ecp_curve_info *ecp_curve_info_from_name( const char *name ) +{ + const ecp_curve_info *curve_info; + + for( curve_info = ecp_curve_list(); + curve_info->grp_id != POLARSSL_ECP_DP_NONE; + curve_info++ ) + { + if( strcasecmp( curve_info->name, name ) == 0 ) + return( curve_info ); + } + + return( NULL ); +} + +/* + * Get the type of a curve + */ +static inline ecp_curve_type ecp_get_type( const ecp_group *grp ) +{ + if( grp->G.X.p == NULL ) + return( POLARSSL_ECP_TYPE_NONE ); + + if( grp->G.Y.p == NULL ) + return( POLARSSL_ECP_TYPE_MONTGOMERY ); + else + return( POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ); +} + +/* + * Initialize (the components of) a point + */ +void ecp_point_init( ecp_point *pt ) +{ + if( pt == NULL ) + return; + + mpi_init( &pt->X ); + mpi_init( &pt->Y ); + mpi_init( &pt->Z ); +} + +/* + * Initialize (the components of) a group + */ +void ecp_group_init( ecp_group *grp ) +{ + if( grp == NULL ) + return; + + memset( grp, 0, sizeof( ecp_group ) ); +} + +/* + * Initialize (the components of) a key pair + */ +void ecp_keypair_init( ecp_keypair *key ) +{ + if( key == NULL ) + return; + + ecp_group_init( &key->grp ); + mpi_init( &key->d ); + ecp_point_init( &key->Q ); +} + +/* + * Unallocate (the components of) a point + */ +void ecp_point_free( ecp_point *pt ) +{ + if( pt == NULL ) + return; + + mpi_free( &( pt->X ) ); + mpi_free( &( pt->Y ) ); + mpi_free( &( pt->Z ) ); +} + +/* + * Unallocate (the components of) a group + */ +void ecp_group_free( ecp_group *grp ) +{ + size_t i; + + if( grp == NULL ) + return; + + if( grp->h != 1 ) + { + mpi_free( &grp->P ); + mpi_free( &grp->A ); + mpi_free( &grp->B ); + ecp_point_free( &grp->G ); + mpi_free( &grp->N ); + } + + if( grp->T != NULL ) + { + for( i = 0; i < grp->T_size; i++ ) + ecp_point_free( &grp->T[i] ); + polarssl_free( grp->T ); + } + + polarssl_zeroize( grp, sizeof( ecp_group ) ); +} + +/* + * Unallocate (the components of) a key pair + */ +void ecp_keypair_free( ecp_keypair *key ) +{ + if( key == NULL ) + return; + + ecp_group_free( &key->grp ); + mpi_free( &key->d ); + ecp_point_free( &key->Q ); +} + +/* + * Copy the contents of a point + */ +int ecp_copy( ecp_point *P, const ecp_point *Q ) +{ + int ret; + + MPI_CHK( mpi_copy( &P->X, &Q->X ) ); + MPI_CHK( mpi_copy( &P->Y, &Q->Y ) ); + MPI_CHK( mpi_copy( &P->Z, &Q->Z ) ); + +cleanup: + return( ret ); +} + +/* + * Copy the contents of a group object + */ +int ecp_group_copy( ecp_group *dst, const ecp_group *src ) +{ + return ecp_use_known_dp( dst, src->id ); +} + +/* + * Set point to zero + */ +int ecp_set_zero( ecp_point *pt ) +{ + int ret; + + MPI_CHK( mpi_lset( &pt->X , 1 ) ); + MPI_CHK( mpi_lset( &pt->Y , 1 ) ); + MPI_CHK( mpi_lset( &pt->Z , 0 ) ); + +cleanup: + return( ret ); +} + +/* + * Tell if a point is zero + */ +int ecp_is_zero( ecp_point *pt ) +{ + return( mpi_cmp_int( &pt->Z, 0 ) == 0 ); +} + +/* + * Import a non-zero point from ASCII strings + */ +int ecp_point_read_string( ecp_point *P, int radix, + const char *x, const char *y ) +{ + int ret; + + MPI_CHK( mpi_read_string( &P->X, radix, x ) ); + MPI_CHK( mpi_read_string( &P->Y, radix, y ) ); + MPI_CHK( mpi_lset( &P->Z, 1 ) ); + +cleanup: + return( ret ); +} + +/* + * Export a point into unsigned binary data (SEC1 2.3.3) + */ +int ecp_point_write_binary( const ecp_group *grp, const ecp_point *P, + int format, size_t *olen, + unsigned char *buf, size_t buflen ) +{ + int ret = 0; + size_t plen; + + if( format != POLARSSL_ECP_PF_UNCOMPRESSED && + format != POLARSSL_ECP_PF_COMPRESSED ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * Common case: P == 0 + */ + if( mpi_cmp_int( &P->Z, 0 ) == 0 ) + { + if( buflen < 1 ) + return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL ); + + buf[0] = 0x00; + *olen = 1; + + return( 0 ); + } + + plen = mpi_size( &grp->P ); + + if( format == POLARSSL_ECP_PF_UNCOMPRESSED ) + { + *olen = 2 * plen + 1; + + if( buflen < *olen ) + return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL ); + + buf[0] = 0x04; + MPI_CHK( mpi_write_binary( &P->X, buf + 1, plen ) ); + MPI_CHK( mpi_write_binary( &P->Y, buf + 1 + plen, plen ) ); + } + else if( format == POLARSSL_ECP_PF_COMPRESSED ) + { + *olen = plen + 1; + + if( buflen < *olen ) + return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL ); + + buf[0] = 0x02 + mpi_get_bit( &P->Y, 0 ); + MPI_CHK( mpi_write_binary( &P->X, buf + 1, plen ) ); + } + +cleanup: + return( ret ); +} + +/* + * Import a point from unsigned binary data (SEC1 2.3.4) + */ +int ecp_point_read_binary( const ecp_group *grp, ecp_point *pt, + const unsigned char *buf, size_t ilen ) +{ + int ret; + size_t plen; + + if ( ilen < 1 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( buf[0] == 0x00 ) + { + if( ilen == 1 ) + return( ecp_set_zero( pt ) ); + else + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + } + + plen = mpi_size( &grp->P ); + + if( buf[0] != 0x04 ) + return( POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ); + + if( ilen != 2 * plen + 1 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + MPI_CHK( mpi_read_binary( &pt->X, buf + 1, plen ) ); + MPI_CHK( mpi_read_binary( &pt->Y, buf + 1 + plen, plen ) ); + MPI_CHK( mpi_lset( &pt->Z, 1 ) ); + +cleanup: + return( ret ); +} + +/* + * Import a point from a TLS ECPoint record (RFC 4492) + * struct { + * opaque point <1..2^8-1>; + * } ECPoint; + */ +int ecp_tls_read_point( const ecp_group *grp, ecp_point *pt, + const unsigned char **buf, size_t buf_len ) +{ + unsigned char data_len; + const unsigned char *buf_start; + + /* + * We must have at least two bytes (1 for length, at least one for data) + */ + if( buf_len < 2 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + data_len = *(*buf)++; + if( data_len < 1 || data_len > buf_len - 1 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * Save buffer start for read_binary and update buf + */ + buf_start = *buf; + *buf += data_len; + + return ecp_point_read_binary( grp, pt, buf_start, data_len ); +} + +/* + * Export a point as a TLS ECPoint record (RFC 4492) + * struct { + * opaque point <1..2^8-1>; + * } ECPoint; + */ +int ecp_tls_write_point( const ecp_group *grp, const ecp_point *pt, + int format, size_t *olen, + unsigned char *buf, size_t blen ) +{ + int ret; + + /* + * buffer length must be at least one, for our length byte + */ + if( blen < 1 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecp_point_write_binary( grp, pt, format, + olen, buf + 1, blen - 1) ) != 0 ) + return( ret ); + + /* + * write length to the first byte and update total length + */ + buf[0] = (unsigned char) *olen; + ++*olen; + + return( 0 ); +} + +/* + * Import an ECP group from ASCII strings, case A == -3 + */ +int ecp_group_read_string( ecp_group *grp, int radix, + const char *p, const char *b, + const char *gx, const char *gy, const char *n) +{ + int ret; + + MPI_CHK( mpi_read_string( &grp->P, radix, p ) ); + MPI_CHK( mpi_read_string( &grp->B, radix, b ) ); + MPI_CHK( ecp_point_read_string( &grp->G, radix, gx, gy ) ); + MPI_CHK( mpi_read_string( &grp->N, radix, n ) ); + + grp->pbits = mpi_msb( &grp->P ); + grp->nbits = mpi_msb( &grp->N ); + +cleanup: + if( ret != 0 ) + ecp_group_free( grp ); + + return( ret ); +} + +/* + * Set a group from an ECParameters record (RFC 4492) + */ +int ecp_tls_read_group( ecp_group *grp, const unsigned char **buf, size_t len ) +{ + uint16_t tls_id; + const ecp_curve_info *curve_info; + + /* + * We expect at least three bytes (see below) + */ + if( len < 3 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * First byte is curve_type; only named_curve is handled + */ + if( *(*buf)++ != POLARSSL_ECP_TLS_NAMED_CURVE ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * Next two bytes are the namedcurve value + */ + tls_id = *(*buf)++; + tls_id <<= 8; + tls_id |= *(*buf)++; + + if( ( curve_info = ecp_curve_info_from_tls_id( tls_id ) ) == NULL ) + return( POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ); + + return ecp_use_known_dp( grp, curve_info->grp_id ); +} + +/* + * Write the ECParameters record corresponding to a group (RFC 4492) + */ +int ecp_tls_write_group( const ecp_group *grp, size_t *olen, + unsigned char *buf, size_t blen ) +{ + const ecp_curve_info *curve_info; + + if( ( curve_info = ecp_curve_info_from_grp_id( grp->id ) ) == NULL ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * We are going to write 3 bytes (see below) + */ + *olen = 3; + if( blen < *olen ) + return( POLARSSL_ERR_ECP_BUFFER_TOO_SMALL ); + + /* + * First byte is curve_type, always named_curve + */ + *buf++ = POLARSSL_ECP_TLS_NAMED_CURVE; + + /* + * Next two bytes are the namedcurve value + */ + buf[0] = curve_info->tls_id >> 8; + buf[1] = curve_info->tls_id & 0xFF; + + return( 0 ); +} + +/* + * Wrapper around fast quasi-modp functions, with fall-back to mpi_mod_mpi. + * See the documentation of struct ecp_group. + * + * This function is in the critial loop for ecp_mul, so pay attention to perf. + */ +static int ecp_modp( mpi *N, const ecp_group *grp ) +{ + int ret; + + if( grp->modp == NULL ) + return( mpi_mod_mpi( N, N, &grp->P ) ); + + /* N->s < 0 is a much faster test, which fails only if N is 0 */ + if( ( N->s < 0 && mpi_cmp_int( N, 0 ) != 0 ) || + mpi_msb( N ) > 2 * grp->pbits ) + { + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + } + + MPI_CHK( grp->modp( N ) ); + + /* N->s < 0 is a much faster test, which fails only if N is 0 */ + while( N->s < 0 && mpi_cmp_int( N, 0 ) != 0 ) + MPI_CHK( mpi_add_mpi( N, N, &grp->P ) ); + + while( mpi_cmp_mpi( N, &grp->P ) >= 0 ) + /* we known P, N and the result are positive */ + MPI_CHK( mpi_sub_abs( N, N, &grp->P ) ); + +cleanup: + return( ret ); +} + +/* + * Fast mod-p functions expect their argument to be in the 0..p^2 range. + * + * In order to guarantee that, we need to ensure that operands of + * mpi_mul_mpi are in the 0..p range. So, after each operation we will + * bring the result back to this range. + * + * The following macros are shortcuts for doing that. + */ + +/* + * Reduce a mpi mod p in-place, general case, to use after mpi_mul_mpi + */ +#if defined(POLARSSL_SELF_TEST) +#define INC_MUL_COUNT mul_count++; +#else +#define INC_MUL_COUNT +#endif + +#define MOD_MUL( N ) do { MPI_CHK( ecp_modp( &N, grp ) ); INC_MUL_COUNT } \ + while( 0 ) + +/* + * Reduce a mpi mod p in-place, to use after mpi_sub_mpi + * N->s < 0 is a very fast test, which fails only if N is 0 + */ +#define MOD_SUB( N ) \ + while( N.s < 0 && mpi_cmp_int( &N, 0 ) != 0 ) \ + MPI_CHK( mpi_add_mpi( &N, &N, &grp->P ) ) + +/* + * Reduce a mpi mod p in-place, to use after mpi_add_mpi and mpi_mul_int. + * We known P, N and the result are positive, so sub_abs is correct, and + * a bit faster. + */ +#define MOD_ADD( N ) \ + while( mpi_cmp_mpi( &N, &grp->P ) >= 0 ) \ + MPI_CHK( mpi_sub_abs( &N, &N, &grp->P ) ) + +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) +/* + * For curves in short Weierstrass form, we do all the internal operations in + * Jacobian coordinates. + * + * For multiplication, we'll use a comb method with coutermeasueres against + * SPA, hence timing attacks. + */ + +/* + * Normalize jacobian coordinates so that Z == 0 || Z == 1 (GECC 3.2.1) + * Cost: 1N := 1I + 3M + 1S + */ +static int ecp_normalize_jac( const ecp_group *grp, ecp_point *pt ) +{ + int ret; + mpi Zi, ZZi; + + if( mpi_cmp_int( &pt->Z, 0 ) == 0 ) + return( 0 ); + + mpi_init( &Zi ); mpi_init( &ZZi ); + + /* + * X = X / Z^2 mod p + */ + MPI_CHK( mpi_inv_mod( &Zi, &pt->Z, &grp->P ) ); + MPI_CHK( mpi_mul_mpi( &ZZi, &Zi, &Zi ) ); MOD_MUL( ZZi ); + MPI_CHK( mpi_mul_mpi( &pt->X, &pt->X, &ZZi ) ); MOD_MUL( pt->X ); + + /* + * Y = Y / Z^3 mod p + */ + MPI_CHK( mpi_mul_mpi( &pt->Y, &pt->Y, &ZZi ) ); MOD_MUL( pt->Y ); + MPI_CHK( mpi_mul_mpi( &pt->Y, &pt->Y, &Zi ) ); MOD_MUL( pt->Y ); + + /* + * Z = 1 + */ + MPI_CHK( mpi_lset( &pt->Z, 1 ) ); + +cleanup: + + mpi_free( &Zi ); mpi_free( &ZZi ); + + return( ret ); +} + +/* + * Normalize jacobian coordinates of an array of (pointers to) points, + * using Montgomery's trick to perform only one inversion mod P. + * (See for example Cohen's "A Course in Computational Algebraic Number + * Theory", Algorithm 10.3.4.) + * + * Warning: fails (returning an error) if one of the points is zero! + * This should never happen, see choice of w in ecp_mul_comb(). + * + * Cost: 1N(t) := 1I + (6t - 3)M + 1S + */ +static int ecp_normalize_jac_many( const ecp_group *grp, + ecp_point *T[], size_t t_len ) +{ + int ret; + size_t i; + mpi *c, u, Zi, ZZi; + + if( t_len < 2 ) + return( ecp_normalize_jac( grp, *T ) ); + + if( ( c = (mpi *) polarssl_malloc( t_len * sizeof( mpi ) ) ) == NULL ) + return( POLARSSL_ERR_ECP_MALLOC_FAILED ); + + mpi_init( &u ); mpi_init( &Zi ); mpi_init( &ZZi ); + for( i = 0; i < t_len; i++ ) + mpi_init( &c[i] ); + + /* + * c[i] = Z_0 * ... * Z_i + */ + MPI_CHK( mpi_copy( &c[0], &T[0]->Z ) ); + for( i = 1; i < t_len; i++ ) + { + MPI_CHK( mpi_mul_mpi( &c[i], &c[i-1], &T[i]->Z ) ); + MOD_MUL( c[i] ); + } + + /* + * u = 1 / (Z_0 * ... * Z_n) mod P + */ + MPI_CHK( mpi_inv_mod( &u, &c[t_len-1], &grp->P ) ); + + for( i = t_len - 1; ; i-- ) + { + /* + * Zi = 1 / Z_i mod p + * u = 1 / (Z_0 * ... * Z_i) mod P + */ + if( i == 0 ) { + MPI_CHK( mpi_copy( &Zi, &u ) ); + } + else + { + MPI_CHK( mpi_mul_mpi( &Zi, &u, &c[i-1] ) ); MOD_MUL( Zi ); + MPI_CHK( mpi_mul_mpi( &u, &u, &T[i]->Z ) ); MOD_MUL( u ); + } + + /* + * proceed as in normalize() + */ + MPI_CHK( mpi_mul_mpi( &ZZi, &Zi, &Zi ) ); MOD_MUL( ZZi ); + MPI_CHK( mpi_mul_mpi( &T[i]->X, &T[i]->X, &ZZi ) ); MOD_MUL( T[i]->X ); + MPI_CHK( mpi_mul_mpi( &T[i]->Y, &T[i]->Y, &ZZi ) ); MOD_MUL( T[i]->Y ); + MPI_CHK( mpi_mul_mpi( &T[i]->Y, &T[i]->Y, &Zi ) ); MOD_MUL( T[i]->Y ); + + /* + * Post-precessing: reclaim some memory by shrinking coordinates + * - not storing Z (always 1) + * - shrinking other coordinates, but still keeping the same number of + * limbs as P, as otherwise it will too likely be regrown too fast. + */ + MPI_CHK( mpi_shrink( &T[i]->X, grp->P.n ) ); + MPI_CHK( mpi_shrink( &T[i]->Y, grp->P.n ) ); + mpi_free( &T[i]->Z ); + + if( i == 0 ) + break; + } + +cleanup: + + mpi_free( &u ); mpi_free( &Zi ); mpi_free( &ZZi ); + for( i = 0; i < t_len; i++ ) + mpi_free( &c[i] ); + polarssl_free( c ); + + return( ret ); +} + +/* + * Conditional point inversion: Q -> -Q = (Q.X, -Q.Y, Q.Z) without leak. + * "inv" must be 0 (don't invert) or 1 (invert) or the result will be invalid + */ +static int ecp_safe_invert_jac( const ecp_group *grp, + ecp_point *Q, + unsigned char inv ) +{ + int ret; + unsigned char nonzero; + mpi mQY; + + mpi_init( &mQY ); + + /* Use the fact that -Q.Y mod P = P - Q.Y unless Q.Y == 0 */ + MPI_CHK( mpi_sub_mpi( &mQY, &grp->P, &Q->Y ) ); + nonzero = mpi_cmp_int( &Q->Y, 0 ) != 0; + MPI_CHK( mpi_safe_cond_assign( &Q->Y, &mQY, inv & nonzero ) ); + +cleanup: + mpi_free( &mQY ); + + return( ret ); +} + +/* + * Point doubling R = 2 P, Jacobian coordinates + * + * http://www.hyperelliptic.org/EFD/g1p/auto-code/shortw/jacobian/doubling/dbl-2007-bl.op3 + * with heavy variable renaming, some reordering and one minor modification + * (a = 2 * b, c = d - 2a replaced with c = d, c = c - b, c = c - b) + * in order to use a lot less intermediate variables (6 vs 25). + * + * Cost: 1D := 2M + 8S + */ +static int ecp_double_jac( const ecp_group *grp, ecp_point *R, + const ecp_point *P ) +{ + int ret; + mpi T1, T2, T3, X3, Y3, Z3; + +#if defined(POLARSSL_SELF_TEST) + dbl_count++; +#endif + + mpi_init( &T1 ); mpi_init( &T2 ); mpi_init( &T3 ); + mpi_init( &X3 ); mpi_init( &Y3 ); mpi_init( &Z3 ); + + MPI_CHK( mpi_mul_mpi( &T3, &P->X, &P->X ) ); MOD_MUL( T3 ); + MPI_CHK( mpi_mul_mpi( &T2, &P->Y, &P->Y ) ); MOD_MUL( T2 ); + MPI_CHK( mpi_mul_mpi( &Y3, &T2, &T2 ) ); MOD_MUL( Y3 ); + MPI_CHK( mpi_add_mpi( &X3, &P->X, &T2 ) ); MOD_ADD( X3 ); + MPI_CHK( mpi_mul_mpi( &X3, &X3, &X3 ) ); MOD_MUL( X3 ); + MPI_CHK( mpi_sub_mpi( &X3, &X3, &Y3 ) ); MOD_SUB( X3 ); + MPI_CHK( mpi_sub_mpi( &X3, &X3, &T3 ) ); MOD_SUB( X3 ); + MPI_CHK( mpi_mul_int( &T1, &X3, 2 ) ); MOD_ADD( T1 ); + MPI_CHK( mpi_mul_mpi( &Z3, &P->Z, &P->Z ) ); MOD_MUL( Z3 ); + MPI_CHK( mpi_mul_mpi( &X3, &Z3, &Z3 ) ); MOD_MUL( X3 ); + MPI_CHK( mpi_mul_int( &T3, &T3, 3 ) ); MOD_ADD( T3 ); + + /* Special case for A = -3 */ + if( grp->A.p == NULL ) + { + MPI_CHK( mpi_mul_int( &X3, &X3, 3 ) ); + X3.s = -1; /* mpi_mul_int doesn't handle negative numbers */ + MOD_SUB( X3 ); + } + else + MPI_CHK( mpi_mul_mpi( &X3, &X3, &grp->A ) ); MOD_MUL( X3 ); + + MPI_CHK( mpi_add_mpi( &T3, &T3, &X3 ) ); MOD_ADD( T3 ); + MPI_CHK( mpi_mul_mpi( &X3, &T3, &T3 ) ); MOD_MUL( X3 ); + MPI_CHK( mpi_sub_mpi( &X3, &X3, &T1 ) ); MOD_SUB( X3 ); + MPI_CHK( mpi_sub_mpi( &X3, &X3, &T1 ) ); MOD_SUB( X3 ); + MPI_CHK( mpi_sub_mpi( &T1, &T1, &X3 ) ); MOD_SUB( T1 ); + MPI_CHK( mpi_mul_mpi( &T1, &T3, &T1 ) ); MOD_MUL( T1 ); + MPI_CHK( mpi_mul_int( &T3, &Y3, 8 ) ); MOD_ADD( T3 ); + MPI_CHK( mpi_sub_mpi( &Y3, &T1, &T3 ) ); MOD_SUB( Y3 ); + MPI_CHK( mpi_add_mpi( &T1, &P->Y, &P->Z ) ); MOD_ADD( T1 ); + MPI_CHK( mpi_mul_mpi( &T1, &T1, &T1 ) ); MOD_MUL( T1 ); + MPI_CHK( mpi_sub_mpi( &T1, &T1, &T2 ) ); MOD_SUB( T1 ); + MPI_CHK( mpi_sub_mpi( &Z3, &T1, &Z3 ) ); MOD_SUB( Z3 ); + + MPI_CHK( mpi_copy( &R->X, &X3 ) ); + MPI_CHK( mpi_copy( &R->Y, &Y3 ) ); + MPI_CHK( mpi_copy( &R->Z, &Z3 ) ); + +cleanup: + mpi_free( &T1 ); mpi_free( &T2 ); mpi_free( &T3 ); + mpi_free( &X3 ); mpi_free( &Y3 ); mpi_free( &Z3 ); + + return( ret ); +} + +/* + * Addition: R = P + Q, mixed affine-Jacobian coordinates (GECC 3.22) + * + * The coordinates of Q must be normalized (= affine), + * but those of P don't need to. R is not normalized. + * + * Special cases: (1) P or Q is zero, (2) R is zero, (3) P == Q. + * None of these cases can happen as intermediate step in ecp_mul_comb(): + * - at each step, P, Q and R are multiples of the base point, the factor + * being less than its order, so none of them is zero; + * - Q is an odd multiple of the base point, P an even multiple, + * due to the choice of precomputed points in the modified comb method. + * So branches for these cases do not leak secret information. + * + * We accept Q->Z being unset (saving memory in tables) as meaning 1. + * + * Cost: 1A := 8M + 3S + */ +static int ecp_add_mixed( const ecp_group *grp, ecp_point *R, + const ecp_point *P, const ecp_point *Q ) +{ + int ret; + mpi T1, T2, T3, T4, X, Y, Z; + +#if defined(POLARSSL_SELF_TEST) + add_count++; +#endif + + /* + * Trivial cases: P == 0 or Q == 0 (case 1) + */ + if( mpi_cmp_int( &P->Z, 0 ) == 0 ) + return( ecp_copy( R, Q ) ); + + if( Q->Z.p != NULL && mpi_cmp_int( &Q->Z, 0 ) == 0 ) + return( ecp_copy( R, P ) ); + + /* + * Make sure Q coordinates are normalized + */ + if( Q->Z.p != NULL && mpi_cmp_int( &Q->Z, 1 ) != 0 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + mpi_init( &T1 ); mpi_init( &T2 ); mpi_init( &T3 ); mpi_init( &T4 ); + mpi_init( &X ); mpi_init( &Y ); mpi_init( &Z ); + + MPI_CHK( mpi_mul_mpi( &T1, &P->Z, &P->Z ) ); MOD_MUL( T1 ); + MPI_CHK( mpi_mul_mpi( &T2, &T1, &P->Z ) ); MOD_MUL( T2 ); + MPI_CHK( mpi_mul_mpi( &T1, &T1, &Q->X ) ); MOD_MUL( T1 ); + MPI_CHK( mpi_mul_mpi( &T2, &T2, &Q->Y ) ); MOD_MUL( T2 ); + MPI_CHK( mpi_sub_mpi( &T1, &T1, &P->X ) ); MOD_SUB( T1 ); + MPI_CHK( mpi_sub_mpi( &T2, &T2, &P->Y ) ); MOD_SUB( T2 ); + + /* Special cases (2) and (3) */ + if( mpi_cmp_int( &T1, 0 ) == 0 ) + { + if( mpi_cmp_int( &T2, 0 ) == 0 ) + { + ret = ecp_double_jac( grp, R, P ); + goto cleanup; + } + else + { + ret = ecp_set_zero( R ); + goto cleanup; + } + } + + MPI_CHK( mpi_mul_mpi( &Z, &P->Z, &T1 ) ); MOD_MUL( Z ); + MPI_CHK( mpi_mul_mpi( &T3, &T1, &T1 ) ); MOD_MUL( T3 ); + MPI_CHK( mpi_mul_mpi( &T4, &T3, &T1 ) ); MOD_MUL( T4 ); + MPI_CHK( mpi_mul_mpi( &T3, &T3, &P->X ) ); MOD_MUL( T3 ); + MPI_CHK( mpi_mul_int( &T1, &T3, 2 ) ); MOD_ADD( T1 ); + MPI_CHK( mpi_mul_mpi( &X, &T2, &T2 ) ); MOD_MUL( X ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T1 ) ); MOD_SUB( X ); + MPI_CHK( mpi_sub_mpi( &X, &X, &T4 ) ); MOD_SUB( X ); + MPI_CHK( mpi_sub_mpi( &T3, &T3, &X ) ); MOD_SUB( T3 ); + MPI_CHK( mpi_mul_mpi( &T3, &T3, &T2 ) ); MOD_MUL( T3 ); + MPI_CHK( mpi_mul_mpi( &T4, &T4, &P->Y ) ); MOD_MUL( T4 ); + MPI_CHK( mpi_sub_mpi( &Y, &T3, &T4 ) ); MOD_SUB( Y ); + + MPI_CHK( mpi_copy( &R->X, &X ) ); + MPI_CHK( mpi_copy( &R->Y, &Y ) ); + MPI_CHK( mpi_copy( &R->Z, &Z ) ); + +cleanup: + + mpi_free( &T1 ); mpi_free( &T2 ); mpi_free( &T3 ); mpi_free( &T4 ); + mpi_free( &X ); mpi_free( &Y ); mpi_free( &Z ); + + return( ret ); +} + +/* + * Addition: R = P + Q, result's coordinates normalized + */ +int ecp_add( const ecp_group *grp, ecp_point *R, + const ecp_point *P, const ecp_point *Q ) +{ + int ret; + + if( ecp_get_type( grp ) != POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + return( POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ); + + MPI_CHK( ecp_add_mixed( grp, R, P, Q ) ); + MPI_CHK( ecp_normalize_jac( grp, R ) ); + +cleanup: + return( ret ); +} + +/* + * Subtraction: R = P - Q, result's coordinates normalized + */ +int ecp_sub( const ecp_group *grp, ecp_point *R, + const ecp_point *P, const ecp_point *Q ) +{ + int ret; + ecp_point mQ; + + ecp_point_init( &mQ ); + + if( ecp_get_type( grp ) != POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + return( POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ); + + /* mQ = - Q */ + MPI_CHK( ecp_copy( &mQ, Q ) ); + if( mpi_cmp_int( &mQ.Y, 0 ) != 0 ) + MPI_CHK( mpi_sub_mpi( &mQ.Y, &grp->P, &mQ.Y ) ); + + MPI_CHK( ecp_add_mixed( grp, R, P, &mQ ) ); + MPI_CHK( ecp_normalize_jac( grp, R ) ); + +cleanup: + ecp_point_free( &mQ ); + + return( ret ); +} + +/* + * Randomize jacobian coordinates: + * (X, Y, Z) -> (l^2 X, l^3 Y, l Z) for random l + * This is sort of the reverse operation of ecp_normalize_jac(). + * + * This countermeasure was first suggested in [2]. + */ +static int ecp_randomize_jac( const ecp_group *grp, ecp_point *pt, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + mpi l, ll; + size_t p_size = ( grp->pbits + 7 ) / 8; + int count = 0; + + mpi_init( &l ); mpi_init( &ll ); + + /* Generate l such that 1 < l < p */ + do + { + mpi_fill_random( &l, p_size, f_rng, p_rng ); + + while( mpi_cmp_mpi( &l, &grp->P ) >= 0 ) + MPI_CHK( mpi_shift_r( &l, 1 ) ); + + if( count++ > 10 ) + return( POLARSSL_ERR_ECP_RANDOM_FAILED ); + } + while( mpi_cmp_int( &l, 1 ) <= 0 ); + + /* Z = l * Z */ + MPI_CHK( mpi_mul_mpi( &pt->Z, &pt->Z, &l ) ); MOD_MUL( pt->Z ); + + /* X = l^2 * X */ + MPI_CHK( mpi_mul_mpi( &ll, &l, &l ) ); MOD_MUL( ll ); + MPI_CHK( mpi_mul_mpi( &pt->X, &pt->X, &ll ) ); MOD_MUL( pt->X ); + + /* Y = l^3 * Y */ + MPI_CHK( mpi_mul_mpi( &ll, &ll, &l ) ); MOD_MUL( ll ); + MPI_CHK( mpi_mul_mpi( &pt->Y, &pt->Y, &ll ) ); MOD_MUL( pt->Y ); + +cleanup: + mpi_free( &l ); mpi_free( &ll ); + + return( ret ); +} + +/* + * Check and define parameters used by the comb method (see below for details) + */ +#if POLARSSL_ECP_WINDOW_SIZE < 2 || POLARSSL_ECP_WINDOW_SIZE > 7 +#error "POLARSSL_ECP_WINDOW_SIZE out of bounds" +#endif + +/* d = ceil( n / w ) */ +#define COMB_MAX_D ( POLARSSL_ECP_MAX_BITS + 1 ) / 2 + +/* number of precomputed points */ +#define COMB_MAX_PRE ( 1 << ( POLARSSL_ECP_WINDOW_SIZE - 1 ) ) + +/* + * Compute the representation of m that will be used with our comb method. + * + * The basic comb method is described in GECC 3.44 for example. We use a + * modified version that provides resistance to SPA by avoiding zero + * digits in the representation as in [3]. We modify the method further by + * requiring that all K_i be odd, which has the small cost that our + * representation uses one more K_i, due to carries. + * + * Also, for the sake of compactness, only the seven low-order bits of x[i] + * are used to represent K_i, and the msb of x[i] encodes the the sign (s_i in + * the paper): it is set if and only if if s_i == -1; + * + * Calling conventions: + * - x is an array of size d + 1 + * - w is the size, ie number of teeth, of the comb, and must be between + * 2 and 7 (in practice, between 2 and POLARSSL_ECP_WINDOW_SIZE) + * - m is the MPI, expected to be odd and such that bitlength(m) <= w * d + * (the result will be incorrect if these assumptions are not satisfied) + */ +static void ecp_comb_fixed( unsigned char x[], size_t d, + unsigned char w, const mpi *m ) +{ + size_t i, j; + unsigned char c, cc, adjust; + + memset( x, 0, d+1 ); + + /* First get the classical comb values (except for x_d = 0) */ + for( i = 0; i < d; i++ ) + for( j = 0; j < w; j++ ) + x[i] |= mpi_get_bit( m, i + d * j ) << j; + + /* Now make sure x_1 .. x_d are odd */ + c = 0; + for( i = 1; i <= d; i++ ) + { + /* Add carry and update it */ + cc = x[i] & c; + x[i] = x[i] ^ c; + c = cc; + + /* Adjust if needed, avoiding branches */ + adjust = 1 - ( x[i] & 0x01 ); + c |= x[i] & ( x[i-1] * adjust ); + x[i] = x[i] ^ ( x[i-1] * adjust ); + x[i-1] |= adjust << 7; + } +} + +/* + * Precompute points for the comb method + * + * If i = i_{w-1} ... i_1 is the binary representation of i, then + * T[i] = i_{w-1} 2^{(w-1)d} P + ... + i_1 2^d P + P + * + * T must be able to hold 2^{w - 1} elements + * + * Cost: d(w-1) D + (2^{w-1} - 1) A + 1 N(w-1) + 1 N(2^{w-1} - 1) + */ +static int ecp_precompute_comb( const ecp_group *grp, + ecp_point T[], const ecp_point *P, + unsigned char w, size_t d ) +{ + int ret; + unsigned char i, k; + size_t j; + ecp_point *cur, *TT[COMB_MAX_PRE - 1]; + + /* + * Set T[0] = P and + * T[2^{l-1}] = 2^{dl} P for l = 1 .. w-1 (this is not the final value) + */ + MPI_CHK( ecp_copy( &T[0], P ) ); + + k = 0; + for( i = 1; i < ( 1U << ( w - 1 ) ); i <<= 1 ) + { + cur = T + i; + MPI_CHK( ecp_copy( cur, T + ( i >> 1 ) ) ); + for( j = 0; j < d; j++ ) + MPI_CHK( ecp_double_jac( grp, cur, cur ) ); + + TT[k++] = cur; + } + + MPI_CHK( ecp_normalize_jac_many( grp, TT, k ) ); + + /* + * Compute the remaining ones using the minimal number of additions + * Be careful to update T[2^l] only after using it! + */ + k = 0; + for( i = 1; i < ( 1U << ( w - 1 ) ); i <<= 1 ) + { + j = i; + while( j-- ) + { + MPI_CHK( ecp_add_mixed( grp, &T[i + j], &T[j], &T[i] ) ); + TT[k++] = &T[i + j]; + } + } + + MPI_CHK( ecp_normalize_jac_many( grp, TT, k ) ); + +cleanup: + return( ret ); +} + +/* + * Select precomputed point: R = sign(i) * T[ abs(i) / 2 ] + */ +static int ecp_select_comb( const ecp_group *grp, ecp_point *R, + const ecp_point T[], unsigned char t_len, + unsigned char i ) +{ + int ret; + unsigned char ii, j; + + /* Ignore the "sign" bit and scale down */ + ii = ( i & 0x7Fu ) >> 1; + + /* Read the whole table to thwart cache-based timing attacks */ + for( j = 0; j < t_len; j++ ) + { + MPI_CHK( mpi_safe_cond_assign( &R->X, &T[j].X, j == ii ) ); + MPI_CHK( mpi_safe_cond_assign( &R->Y, &T[j].Y, j == ii ) ); + } + + /* Safely invert result if i is "negative" */ + MPI_CHK( ecp_safe_invert_jac( grp, R, i >> 7 ) ); + +cleanup: + return( ret ); +} + +/* + * Core multiplication algorithm for the (modified) comb method. + * This part is actually common with the basic comb method (GECC 3.44) + * + * Cost: d A + d D + 1 R + */ +static int ecp_mul_comb_core( const ecp_group *grp, ecp_point *R, + const ecp_point T[], unsigned char t_len, + const unsigned char x[], size_t d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + ecp_point Txi; + size_t i; + + ecp_point_init( &Txi ); + + /* Start with a non-zero point and randomize its coordinates */ + i = d; + MPI_CHK( ecp_select_comb( grp, R, T, t_len, x[i] ) ); + MPI_CHK( mpi_lset( &R->Z, 1 ) ); + if( f_rng != 0 ) + MPI_CHK( ecp_randomize_jac( grp, R, f_rng, p_rng ) ); + + while( i-- != 0 ) + { + MPI_CHK( ecp_double_jac( grp, R, R ) ); + MPI_CHK( ecp_select_comb( grp, &Txi, T, t_len, x[i] ) ); + MPI_CHK( ecp_add_mixed( grp, R, R, &Txi ) ); + } + +cleanup: + ecp_point_free( &Txi ); + + return( ret ); +} + +/* + * Multiplication using the comb method, + * for curves in short Weierstrass form + */ +static int ecp_mul_comb( ecp_group *grp, ecp_point *R, + const mpi *m, const ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + unsigned char w, m_is_odd, p_eq_g, pre_len, i; + size_t d; + unsigned char k[COMB_MAX_D + 1]; + ecp_point *T; + mpi M, mm; + + mpi_init( &M ); + mpi_init( &mm ); + + /* we need N to be odd to trnaform m in an odd number, check now */ + if( mpi_get_bit( &grp->N, 0 ) != 1 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + /* + * Minimize the number of multiplications, that is minimize + * 10 * d * w + 18 * 2^(w-1) + 11 * d + 7 * w, with d = ceil( nbits / w ) + * (see costs of the various parts, with 1S = 1M) + */ + w = grp->nbits >= 384 ? 5 : 4; + + /* + * If P == G, pre-compute a bit more, since this may be re-used later. + * Just adding one avoids upping the cost of the first mul too much, + * and the memory cost too. + */ +#if POLARSSL_ECP_FIXED_POINT_OPTIM == 1 + p_eq_g = ( mpi_cmp_mpi( &P->Y, &grp->G.Y ) == 0 && + mpi_cmp_mpi( &P->X, &grp->G.X ) == 0 ); + if( p_eq_g ) + w++; +#else + p_eq_g = 0; +#endif + + /* + * Make sure w is within bounds. + * (The last test is useful only for very small curves in the test suite.) + */ + if( w > POLARSSL_ECP_WINDOW_SIZE ) + w = POLARSSL_ECP_WINDOW_SIZE; + if( w >= grp->nbits ) + w = 2; + + /* Other sizes that depend on w */ + pre_len = 1U << ( w - 1 ); + d = ( grp->nbits + w - 1 ) / w; + + /* + * Prepare precomputed points: if P == G we want to + * use grp->T if already initialized, or initialize it. + */ + T = p_eq_g ? grp->T : NULL; + + if( T == NULL ) + { + T = (ecp_point *) polarssl_malloc( pre_len * sizeof( ecp_point ) ); + if( T == NULL ) + { + ret = POLARSSL_ERR_ECP_MALLOC_FAILED; + goto cleanup; + } + + for( i = 0; i < pre_len; i++ ) + ecp_point_init( &T[i] ); + + MPI_CHK( ecp_precompute_comb( grp, T, P, w, d ) ); + + if( p_eq_g ) + { + grp->T = T; + grp->T_size = pre_len; + } + } + + /* + * Make sure M is odd (M = m or M = N - m, since N is odd) + * using the fact that m * P = - (N - m) * P + */ + m_is_odd = ( mpi_get_bit( m, 0 ) == 1 ); + MPI_CHK( mpi_copy( &M, m ) ); + MPI_CHK( mpi_sub_mpi( &mm, &grp->N, m ) ); + MPI_CHK( mpi_safe_cond_assign( &M, &mm, ! m_is_odd ) ); + + /* + * Go for comb multiplication, R = M * P + */ + ecp_comb_fixed( k, d, w, &M ); + MPI_CHK( ecp_mul_comb_core( grp, R, T, pre_len, k, d, f_rng, p_rng ) ); + + /* + * Now get m * P from M * P and normalize it + */ + MPI_CHK( ecp_safe_invert_jac( grp, R, ! m_is_odd ) ); + MPI_CHK( ecp_normalize_jac( grp, R ) ); + +cleanup: + + if( T != NULL && ! p_eq_g ) + { + for( i = 0; i < pre_len; i++ ) + ecp_point_free( &T[i] ); + polarssl_free( T ); + } + + mpi_free( &M ); + mpi_free( &mm ); + + if( ret != 0 ) + ecp_point_free( R ); + + return( ret ); +} + +#endif /* POLARSSL_ECP_SHORT_WEIERSTRASS */ + +#if defined(POLARSSL_ECP_MONTGOMERY) +/* + * For Montgomery curves, we do all the internal arithmetic in projective + * coordinates. Import/export of points uses only the x coordinates, which is + * internaly represented as X / Z. + * + * For scalar multiplication, we'll use a Montgomery ladder. + */ + +/* + * Normalize Montgomery x/z coordinates: X = X/Z, Z = 1 + * Cost: 1M + 1I + */ +static int ecp_normalize_mxz( const ecp_group *grp, ecp_point *P ) +{ + int ret; + + MPI_CHK( mpi_inv_mod( &P->Z, &P->Z, &grp->P ) ); + MPI_CHK( mpi_mul_mpi( &P->X, &P->X, &P->Z ) ); MOD_MUL( P->X ); + MPI_CHK( mpi_lset( &P->Z, 1 ) ); + +cleanup: + return( ret ); +} + +/* + * Randomize projective x/z coordinates: + * (X, Z) -> (l X, l Z) for random l + * This is sort of the reverse operation of ecp_normalize_mxz(). + * + * This countermeasure was first suggested in [2]. + * Cost: 2M + */ +static int ecp_randomize_mxz( const ecp_group *grp, ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + mpi l; + size_t p_size = ( grp->pbits + 7 ) / 8; + int count = 0; + + mpi_init( &l ); + + /* Generate l such that 1 < l < p */ + do + { + mpi_fill_random( &l, p_size, f_rng, p_rng ); + + while( mpi_cmp_mpi( &l, &grp->P ) >= 0 ) + MPI_CHK( mpi_shift_r( &l, 1 ) ); + + if( count++ > 10 ) + return( POLARSSL_ERR_ECP_RANDOM_FAILED ); + } + while( mpi_cmp_int( &l, 1 ) <= 0 ); + + MPI_CHK( mpi_mul_mpi( &P->X, &P->X, &l ) ); MOD_MUL( P->X ); + MPI_CHK( mpi_mul_mpi( &P->Z, &P->Z, &l ) ); MOD_MUL( P->Z ); + +cleanup: + mpi_free( &l ); + + return( ret ); +} + +/* + * Double-and-add: R = 2P, S = P + Q, with d = X(P - Q), + * for Montgomery curves in x/z coordinates. + * + * http://www.hyperelliptic.org/EFD/g1p/auto-code/montgom/xz/ladder/mladd-1987-m.op3 + * with + * d = X1 + * P = (X2, Z2) + * Q = (X3, Z3) + * R = (X4, Z4) + * S = (X5, Z5) + * and eliminating temporary variables tO, ..., t4. + * + * Cost: 5M + 4S + */ +static int ecp_double_add_mxz( const ecp_group *grp, + ecp_point *R, ecp_point *S, + const ecp_point *P, const ecp_point *Q, + const mpi *d ) +{ + int ret; + mpi A, AA, B, BB, E, C, D, DA, CB; + + mpi_init( &A ); mpi_init( &AA ); mpi_init( &B ); + mpi_init( &BB ); mpi_init( &E ); mpi_init( &C ); + mpi_init( &D ); mpi_init( &DA ); mpi_init( &CB ); + + MPI_CHK( mpi_add_mpi( &A, &P->X, &P->Z ) ); MOD_ADD( A ); + MPI_CHK( mpi_mul_mpi( &AA, &A, &A ) ); MOD_MUL( AA ); + MPI_CHK( mpi_sub_mpi( &B, &P->X, &P->Z ) ); MOD_SUB( B ); + MPI_CHK( mpi_mul_mpi( &BB, &B, &B ) ); MOD_MUL( BB ); + MPI_CHK( mpi_sub_mpi( &E, &AA, &BB ) ); MOD_SUB( E ); + MPI_CHK( mpi_add_mpi( &C, &Q->X, &Q->Z ) ); MOD_ADD( C ); + MPI_CHK( mpi_sub_mpi( &D, &Q->X, &Q->Z ) ); MOD_SUB( D ); + MPI_CHK( mpi_mul_mpi( &DA, &D, &A ) ); MOD_MUL( DA ); + MPI_CHK( mpi_mul_mpi( &CB, &C, &B ) ); MOD_MUL( CB ); + MPI_CHK( mpi_add_mpi( &S->X, &DA, &CB ) ); MOD_MUL( S->X ); + MPI_CHK( mpi_mul_mpi( &S->X, &S->X, &S->X ) ); MOD_MUL( S->X ); + MPI_CHK( mpi_sub_mpi( &S->Z, &DA, &CB ) ); MOD_SUB( S->Z ); + MPI_CHK( mpi_mul_mpi( &S->Z, &S->Z, &S->Z ) ); MOD_MUL( S->Z ); + MPI_CHK( mpi_mul_mpi( &S->Z, d, &S->Z ) ); MOD_MUL( S->Z ); + MPI_CHK( mpi_mul_mpi( &R->X, &AA, &BB ) ); MOD_MUL( R->X ); + MPI_CHK( mpi_mul_mpi( &R->Z, &grp->A, &E ) ); MOD_MUL( R->Z ); + MPI_CHK( mpi_add_mpi( &R->Z, &BB, &R->Z ) ); MOD_ADD( R->Z ); + MPI_CHK( mpi_mul_mpi( &R->Z, &E, &R->Z ) ); MOD_MUL( R->Z ); + +cleanup: + mpi_free( &A ); mpi_free( &AA ); mpi_free( &B ); + mpi_free( &BB ); mpi_free( &E ); mpi_free( &C ); + mpi_free( &D ); mpi_free( &DA ); mpi_free( &CB ); + + return( ret ); +} + +/* + * Multiplication with Montgomery ladder in x/z coordinates, + * for curves in Montgomery form + */ +static int ecp_mul_mxz( ecp_group *grp, ecp_point *R, + const mpi *m, const ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t i; + unsigned char b; + ecp_point RP; + mpi PX; + + ecp_point_init( &RP ); mpi_init( &PX ); + + /* Save PX and read from P before writing to R, in case P == R */ + MPI_CHK( mpi_copy( &PX, &P->X ) ); + MPI_CHK( ecp_copy( &RP, P ) ); + + /* Set R to zero in modified x/z coordinates */ + MPI_CHK( mpi_lset( &R->X, 1 ) ); + MPI_CHK( mpi_lset( &R->Z, 0 ) ); + mpi_free( &R->Y ); + + /* RP.X might be sligtly larger than P, so reduce it */ + MOD_ADD( RP.X ); + + /* Randomize coordinates of the starting point */ + if( f_rng != NULL ) + MPI_CHK( ecp_randomize_mxz( grp, &RP, f_rng, p_rng ) ); + + /* Loop invariant: R = result so far, RP = R + P */ + i = mpi_msb( m ); /* one past the (zero-based) most significant bit */ + while( i-- > 0 ) + { + b = mpi_get_bit( m, i ); + /* + * if (b) R = 2R + P else R = 2R, + * which is: + * if (b) double_add( RP, R, RP, R ) + * else double_add( R, RP, R, RP ) + * but using safe conditional swaps to avoid leaks + */ + MPI_CHK( mpi_safe_cond_swap( &R->X, &RP.X, b ) ); + MPI_CHK( mpi_safe_cond_swap( &R->Z, &RP.Z, b ) ); + MPI_CHK( ecp_double_add_mxz( grp, R, &RP, R, &RP, &PX ) ); + MPI_CHK( mpi_safe_cond_swap( &R->X, &RP.X, b ) ); + MPI_CHK( mpi_safe_cond_swap( &R->Z, &RP.Z, b ) ); + } + + MPI_CHK( ecp_normalize_mxz( grp, R ) ); + +cleanup: + ecp_point_free( &RP ); mpi_free( &PX ); + + return( ret ); +} + +#endif /* POLARSSL_ECP_MONTGOMERY */ + +/* + * Multiplication R = m * P + */ +int ecp_mul( ecp_group *grp, ecp_point *R, + const mpi *m, const ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + + /* Common sanity checks */ + if( mpi_cmp_int( &P->Z, 1 ) != 0 ) + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = ecp_check_privkey( grp, m ) ) != 0 || + ( ret = ecp_check_pubkey( grp, P ) ) != 0 ) + return( ret ); + +#if defined(POLARSSL_ECP_MONTGOMERY) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_MONTGOMERY ) + return( ecp_mul_mxz( grp, R, m, P, f_rng, p_rng ) ); +#endif +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + return( ecp_mul_comb( grp, R, m, P, f_rng, p_rng ) ); +#endif + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); +} + +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) +/* + * Check that an affine point is valid as a public key, + * short weierstrass curves (SEC1 3.2.3.1) + */ +static int ecp_check_pubkey_sw( const ecp_group *grp, const ecp_point *pt ) +{ + int ret; + mpi YY, RHS; + + /* pt coordinates must be normalized for our checks */ + if( mpi_cmp_int( &pt->X, 0 ) < 0 || + mpi_cmp_int( &pt->Y, 0 ) < 0 || + mpi_cmp_mpi( &pt->X, &grp->P ) >= 0 || + mpi_cmp_mpi( &pt->Y, &grp->P ) >= 0 ) + return( POLARSSL_ERR_ECP_INVALID_KEY ); + + mpi_init( &YY ); mpi_init( &RHS ); + + /* + * YY = Y^2 + * RHS = X (X^2 + A) + B = X^3 + A X + B + */ + MPI_CHK( mpi_mul_mpi( &YY, &pt->Y, &pt->Y ) ); MOD_MUL( YY ); + MPI_CHK( mpi_mul_mpi( &RHS, &pt->X, &pt->X ) ); MOD_MUL( RHS ); + + /* Special case for A = -3 */ + if( grp->A.p == NULL ) + { + MPI_CHK( mpi_sub_int( &RHS, &RHS, 3 ) ); MOD_SUB( RHS ); + } + else + { + MPI_CHK( mpi_add_mpi( &RHS, &RHS, &grp->A ) ); MOD_ADD( RHS ); + } + + MPI_CHK( mpi_mul_mpi( &RHS, &RHS, &pt->X ) ); MOD_MUL( RHS ); + MPI_CHK( mpi_add_mpi( &RHS, &RHS, &grp->B ) ); MOD_ADD( RHS ); + + if( mpi_cmp_mpi( &YY, &RHS ) != 0 ) + ret = POLARSSL_ERR_ECP_INVALID_KEY; + +cleanup: + + mpi_free( &YY ); mpi_free( &RHS ); + + return( ret ); +} +#endif /* POLARSSL_ECP_SHORT_WEIERSTRASS */ + + +#if defined(POLARSSL_ECP_MONTGOMERY) +/* + * Check validity of a public key for Montgomery curves with x-only schemes + */ +static int ecp_check_pubkey_mx( const ecp_group *grp, const ecp_point *pt ) +{ + /* [M255 p. 5] Just check X is the correct number of bytes */ + if( mpi_size( &pt->X ) > ( grp->nbits + 7 ) / 8 ) + return( POLARSSL_ERR_ECP_INVALID_KEY ); + + return( 0 ); +} +#endif /* POLARSSL_ECP_MONTGOMERY */ + +/* + * Check that a point is valid as a public key + */ +int ecp_check_pubkey( const ecp_group *grp, const ecp_point *pt ) +{ + /* Must use affine coordinates */ + if( mpi_cmp_int( &pt->Z, 1 ) != 0 ) + return( POLARSSL_ERR_ECP_INVALID_KEY ); + +#if defined(POLARSSL_ECP_MONTGOMERY) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_MONTGOMERY ) + return( ecp_check_pubkey_mx( grp, pt ) ); +#endif +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + return( ecp_check_pubkey_sw( grp, pt ) ); +#endif + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); +} + +/* + * Check that an mpi is valid as a private key + */ +int ecp_check_privkey( const ecp_group *grp, const mpi *d ) +{ +#if defined(POLARSSL_ECP_MONTGOMERY) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_MONTGOMERY ) + { + /* see [M255] page 5 */ + if( mpi_get_bit( d, 0 ) != 0 || + mpi_get_bit( d, 1 ) != 0 || + mpi_get_bit( d, 2 ) != 0 || + mpi_msb( d ) - 1 != grp->nbits ) /* mpi_msb is one-based! */ + return( POLARSSL_ERR_ECP_INVALID_KEY ); + else + return( 0 ); + } +#endif /* POLARSSL_ECP_MONTGOMERY */ +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + { + /* see SEC1 3.2 */ + if( mpi_cmp_int( d, 1 ) < 0 || + mpi_cmp_mpi( d, &grp->N ) >= 0 ) + return( POLARSSL_ERR_ECP_INVALID_KEY ); + else + return( 0 ); + } +#endif /* POLARSSL_ECP_SHORT_WEIERSTRASS */ + + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); +} + +/* + * Generate a keypair + */ +int ecp_gen_keypair( ecp_group *grp, mpi *d, ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + size_t n_size = ( grp->nbits + 7 ) / 8; + +#if defined(POLARSSL_ECP_MONTGOMERY) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_MONTGOMERY ) + { + /* [M225] page 5 */ + size_t b; + + MPI_CHK( mpi_fill_random( d, n_size, f_rng, p_rng ) ); + + /* Make sure the most significant bit is nbits */ + b = mpi_msb( d ) - 1; /* mpi_msb is one-based */ + if( b > grp->nbits ) + MPI_CHK( mpi_shift_r( d, b - grp->nbits ) ); + else + MPI_CHK( mpi_set_bit( d, grp->nbits, 1 ) ); + + /* Make sure the last three bits are unset */ + MPI_CHK( mpi_set_bit( d, 0, 0 ) ); + MPI_CHK( mpi_set_bit( d, 1, 0 ) ); + MPI_CHK( mpi_set_bit( d, 2, 0 ) ); + } + else +#endif /* POLARSSL_ECP_MONTGOMERY */ +#if defined(POLARSSL_ECP_SHORT_WEIERSTRASS) + if( ecp_get_type( grp ) == POLARSSL_ECP_TYPE_SHORT_WEIERSTRASS ) + { + /* SEC1 3.2.1: Generate d such that 1 <= n < N */ + int count = 0; + unsigned char rnd[POLARSSL_ECP_MAX_BYTES]; + + /* + * Match the procedure given in RFC 6979 (deterministic ECDSA): + * - use the same byte ordering; + * - keep the leftmost nbits bits of the generated octet string; + * - try until result is in the desired range. + * This also avoids any biais, which is especially important for ECDSA. + */ + do + { + MPI_CHK( f_rng( p_rng, rnd, n_size ) ); + MPI_CHK( mpi_read_binary( d, rnd, n_size ) ); + MPI_CHK( mpi_shift_r( d, 8 * n_size - grp->nbits ) ); + + /* + * Each try has at worst a probability 1/2 of failing (the msb has + * a probability 1/2 of being 0, and then the result will be < N), + * so after 30 tries failure probability is a most 2**(-30). + * + * For most curves, 1 try is enough with overwhelming probability, + * since N starts with a lot of 1s in binary, but some curves + * such as secp224k1 are actually very close to the worst case. + */ + if( ++count > 30 ) + return( POLARSSL_ERR_ECP_RANDOM_FAILED ); + } + while( mpi_cmp_int( d, 1 ) < 0 || + mpi_cmp_mpi( d, &grp->N ) >= 0 ); + } + else +#endif /* POLARSSL_ECP_SHORT_WEIERSTRASS */ + return( POLARSSL_ERR_ECP_BAD_INPUT_DATA ); + +cleanup: + if( ret != 0 ) + return( ret ); + + return( ecp_mul( grp, Q, d, &grp->G, f_rng, p_rng ) ); +} + +/* + * Generate a keypair, prettier wrapper + */ +int ecp_gen_key( ecp_group_id grp_id, ecp_keypair *key, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + + if( ( ret = ecp_use_known_dp( &key->grp, grp_id ) ) != 0 ) + return( ret ); + + return( ecp_gen_keypair( &key->grp, &key->d, &key->Q, f_rng, p_rng ) ); +} + +#if defined(POLARSSL_SELF_TEST) + +/* + * Checkup routine + */ +int ecp_self_test( int verbose ) +{ + int ret; + size_t i; + ecp_group grp; + ecp_point R, P; + mpi m; + unsigned long add_c_prev, dbl_c_prev, mul_c_prev; + /* exponents especially adapted for secp192r1 */ + const char *exponents[] = + { + "000000000000000000000000000000000000000000000001", /* one */ + "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22830", /* N - 1 */ + "5EA6F389A38B8BC81E767753B15AA5569E1782E30ABE7D25", /* random */ + "400000000000000000000000000000000000000000000000", /* one and zeros */ + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", /* all ones */ + "555555555555555555555555555555555555555555555555", /* 101010... */ + }; + + ecp_group_init( &grp ); + ecp_point_init( &R ); + ecp_point_init( &P ); + mpi_init( &m ); + + /* Use secp192r1 if available, or any available curve */ +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) + MPI_CHK( ecp_use_known_dp( &grp, POLARSSL_ECP_DP_SECP192R1 ) ); +#else + MPI_CHK( ecp_use_known_dp( &grp, ecp_curve_list()->grp_id ) ); +#endif + + if( verbose != 0 ) + polarssl_printf( " ECP test #1 (constant op_count, base point G): " ); + + /* Do a dummy multiplication first to trigger precomputation */ + MPI_CHK( mpi_lset( &m, 2 ) ); + MPI_CHK( ecp_mul( &grp, &P, &m, &grp.G, NULL, NULL ) ); + + add_count = 0; + dbl_count = 0; + mul_count = 0; + MPI_CHK( mpi_read_string( &m, 16, exponents[0] ) ); + MPI_CHK( ecp_mul( &grp, &R, &m, &grp.G, NULL, NULL ) ); + + for( i = 1; i < sizeof( exponents ) / sizeof( exponents[0] ); i++ ) + { + add_c_prev = add_count; + dbl_c_prev = dbl_count; + mul_c_prev = mul_count; + add_count = 0; + dbl_count = 0; + mul_count = 0; + + MPI_CHK( mpi_read_string( &m, 16, exponents[i] ) ); + MPI_CHK( ecp_mul( &grp, &R, &m, &grp.G, NULL, NULL ) ); + + if( add_count != add_c_prev || + dbl_count != dbl_c_prev || + mul_count != mul_c_prev ) + { + if( verbose != 0 ) + polarssl_printf( "failed (%u)\n", (unsigned int) i ); + + ret = 1; + goto cleanup; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " ECP test #2 (constant op_count, other point): " ); + /* We computed P = 2G last time, use it */ + + add_count = 0; + dbl_count = 0; + mul_count = 0; + MPI_CHK( mpi_read_string( &m, 16, exponents[0] ) ); + MPI_CHK( ecp_mul( &grp, &R, &m, &P, NULL, NULL ) ); + + for( i = 1; i < sizeof( exponents ) / sizeof( exponents[0] ); i++ ) + { + add_c_prev = add_count; + dbl_c_prev = dbl_count; + mul_c_prev = mul_count; + add_count = 0; + dbl_count = 0; + mul_count = 0; + + MPI_CHK( mpi_read_string( &m, 16, exponents[i] ) ); + MPI_CHK( ecp_mul( &grp, &R, &m, &P, NULL, NULL ) ); + + if( add_count != add_c_prev || + dbl_count != dbl_c_prev || + mul_count != mul_c_prev ) + { + if( verbose != 0 ) + polarssl_printf( "failed (%u)\n", (unsigned int) i ); + + ret = 1; + goto cleanup; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + +cleanup: + + if( ret < 0 && verbose != 0 ) + polarssl_printf( "Unexpected error, return code = %08X\n", ret ); + + ecp_group_free( &grp ); + ecp_point_free( &R ); + ecp_point_free( &P ); + mpi_free( &m ); + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_ECP_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ecp_curves.c b/component/common/network/ssl/polarssl-1.3.8/library/ecp_curves.c new file mode 100644 index 0000000..4c0018c --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ecp_curves.c @@ -0,0 +1,1380 @@ +/* + * Elliptic curves over GF(p): curve-specific data and functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ECP_C) + +#include "polarssl/ecp.h" + +#if defined(_MSC_VER) && !defined(inline) +#define inline _inline +#else +#if defined(__ARMCC_VERSION) && !defined(inline) +#define inline __inline +#endif /* __ARMCC_VERSION */ +#endif /*_MSC_VER */ + +/* + * Conversion macros for embedded constants: + * build lists of t_uint's from lists of unsigned char's grouped by 8, 4 or 2 + */ +#if defined(POLARSSL_HAVE_INT8) + +#define BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h ) \ + a, b, c, d, e, f, g, h + +#define BYTES_TO_T_UINT_4( a, b, c, d ) \ + a, b, c, d + +#define BYTES_TO_T_UINT_2( a, b ) \ + a, b + +#elif defined(POLARSSL_HAVE_INT16) + +#define BYTES_TO_T_UINT_2( a, b ) \ + ( (t_uint) a << 0 ) | \ + ( (t_uint) b << 8 ) + +#define BYTES_TO_T_UINT_4( a, b, c, d ) \ + BYTES_TO_T_UINT_2( a, b ), \ + BYTES_TO_T_UINT_2( c, d ) + +#define BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h ) \ + BYTES_TO_T_UINT_2( a, b ), \ + BYTES_TO_T_UINT_2( c, d ), \ + BYTES_TO_T_UINT_2( e, f ), \ + BYTES_TO_T_UINT_2( g, h ) + +#elif defined(POLARSSL_HAVE_INT32) + +#define BYTES_TO_T_UINT_4( a, b, c, d ) \ + ( (t_uint) a << 0 ) | \ + ( (t_uint) b << 8 ) | \ + ( (t_uint) c << 16 ) | \ + ( (t_uint) d << 24 ) + +#define BYTES_TO_T_UINT_2( a, b ) \ + BYTES_TO_T_UINT_4( a, b, 0, 0 ) + +#define BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h ) \ + BYTES_TO_T_UINT_4( a, b, c, d ), \ + BYTES_TO_T_UINT_4( e, f, g, h ) + +#else /* 64-bits */ + +#define BYTES_TO_T_UINT_8( a, b, c, d, e, f, g, h ) \ + ( (t_uint) a << 0 ) | \ + ( (t_uint) b << 8 ) | \ + ( (t_uint) c << 16 ) | \ + ( (t_uint) d << 24 ) | \ + ( (t_uint) e << 32 ) | \ + ( (t_uint) f << 40 ) | \ + ( (t_uint) g << 48 ) | \ + ( (t_uint) h << 56 ) + +#define BYTES_TO_T_UINT_4( a, b, c, d ) \ + BYTES_TO_T_UINT_8( a, b, c, d, 0, 0, 0, 0 ) + +#define BYTES_TO_T_UINT_2( a, b ) \ + BYTES_TO_T_UINT_8( a, b, 0, 0, 0, 0, 0, 0 ) + +#endif /* bits in t_uint */ + +/* + * Note: the constants are in little-endian order + * to be directly usable in MPIs + */ + +/* + * Domain parameters for secp192r1 + */ +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) +static const t_uint secp192r1_p[] = { + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp192r1_b[] = { + BYTES_TO_T_UINT_8( 0xB1, 0xB9, 0x46, 0xC1, 0xEC, 0xDE, 0xB8, 0xFE ), + BYTES_TO_T_UINT_8( 0x49, 0x30, 0x24, 0x72, 0xAB, 0xE9, 0xA7, 0x0F ), + BYTES_TO_T_UINT_8( 0xE7, 0x80, 0x9C, 0xE5, 0x19, 0x05, 0x21, 0x64 ), +}; +static const t_uint secp192r1_gx[] = { + BYTES_TO_T_UINT_8( 0x12, 0x10, 0xFF, 0x82, 0xFD, 0x0A, 0xFF, 0xF4 ), + BYTES_TO_T_UINT_8( 0x00, 0x88, 0xA1, 0x43, 0xEB, 0x20, 0xBF, 0x7C ), + BYTES_TO_T_UINT_8( 0xF6, 0x90, 0x30, 0xB0, 0x0E, 0xA8, 0x8D, 0x18 ), +}; +static const t_uint secp192r1_gy[] = { + BYTES_TO_T_UINT_8( 0x11, 0x48, 0x79, 0x1E, 0xA1, 0x77, 0xF9, 0x73 ), + BYTES_TO_T_UINT_8( 0xD5, 0xCD, 0x24, 0x6B, 0xED, 0x11, 0x10, 0x63 ), + BYTES_TO_T_UINT_8( 0x78, 0xDA, 0xC8, 0xFF, 0x95, 0x2B, 0x19, 0x07 ), +}; +static const t_uint secp192r1_n[] = { + BYTES_TO_T_UINT_8( 0x31, 0x28, 0xD2, 0xB4, 0xB1, 0xC9, 0x6B, 0x14 ), + BYTES_TO_T_UINT_8( 0x36, 0xF8, 0xDE, 0x99, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP192R1_ENABLED */ + +/* + * Domain parameters for secp224r1 + */ +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) +static const t_uint secp224r1_p[] = { + BYTES_TO_T_UINT_8( 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ), +}; +static const t_uint secp224r1_b[] = { + BYTES_TO_T_UINT_8( 0xB4, 0xFF, 0x55, 0x23, 0x43, 0x39, 0x0B, 0x27 ), + BYTES_TO_T_UINT_8( 0xBA, 0xD8, 0xBF, 0xD7, 0xB7, 0xB0, 0x44, 0x50 ), + BYTES_TO_T_UINT_8( 0x56, 0x32, 0x41, 0xF5, 0xAB, 0xB3, 0x04, 0x0C ), + BYTES_TO_T_UINT_4( 0x85, 0x0A, 0x05, 0xB4 ), +}; +static const t_uint secp224r1_gx[] = { + BYTES_TO_T_UINT_8( 0x21, 0x1D, 0x5C, 0x11, 0xD6, 0x80, 0x32, 0x34 ), + BYTES_TO_T_UINT_8( 0x22, 0x11, 0xC2, 0x56, 0xD3, 0xC1, 0x03, 0x4A ), + BYTES_TO_T_UINT_8( 0xB9, 0x90, 0x13, 0x32, 0x7F, 0xBF, 0xB4, 0x6B ), + BYTES_TO_T_UINT_4( 0xBD, 0x0C, 0x0E, 0xB7 ), +}; +static const t_uint secp224r1_gy[] = { + BYTES_TO_T_UINT_8( 0x34, 0x7E, 0x00, 0x85, 0x99, 0x81, 0xD5, 0x44 ), + BYTES_TO_T_UINT_8( 0x64, 0x47, 0x07, 0x5A, 0xA0, 0x75, 0x43, 0xCD ), + BYTES_TO_T_UINT_8( 0xE6, 0xDF, 0x22, 0x4C, 0xFB, 0x23, 0xF7, 0xB5 ), + BYTES_TO_T_UINT_4( 0x88, 0x63, 0x37, 0xBD ), +}; +static const t_uint secp224r1_n[] = { + BYTES_TO_T_UINT_8( 0x3D, 0x2A, 0x5C, 0x5C, 0x45, 0x29, 0xDD, 0x13 ), + BYTES_TO_T_UINT_8( 0x3E, 0xF0, 0xB8, 0xE0, 0xA2, 0x16, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_4( 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP224R1_ENABLED */ + +/* + * Domain parameters for secp256r1 + */ +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) +static const t_uint secp256r1_p[] = { + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), + BYTES_TO_T_UINT_8( 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp256r1_b[] = { + BYTES_TO_T_UINT_8( 0x4B, 0x60, 0xD2, 0x27, 0x3E, 0x3C, 0xCE, 0x3B ), + BYTES_TO_T_UINT_8( 0xF6, 0xB0, 0x53, 0xCC, 0xB0, 0x06, 0x1D, 0x65 ), + BYTES_TO_T_UINT_8( 0xBC, 0x86, 0x98, 0x76, 0x55, 0xBD, 0xEB, 0xB3 ), + BYTES_TO_T_UINT_8( 0xE7, 0x93, 0x3A, 0xAA, 0xD8, 0x35, 0xC6, 0x5A ), +}; +static const t_uint secp256r1_gx[] = { + BYTES_TO_T_UINT_8( 0x96, 0xC2, 0x98, 0xD8, 0x45, 0x39, 0xA1, 0xF4 ), + BYTES_TO_T_UINT_8( 0xA0, 0x33, 0xEB, 0x2D, 0x81, 0x7D, 0x03, 0x77 ), + BYTES_TO_T_UINT_8( 0xF2, 0x40, 0xA4, 0x63, 0xE5, 0xE6, 0xBC, 0xF8 ), + BYTES_TO_T_UINT_8( 0x47, 0x42, 0x2C, 0xE1, 0xF2, 0xD1, 0x17, 0x6B ), +}; +static const t_uint secp256r1_gy[] = { + BYTES_TO_T_UINT_8( 0xF5, 0x51, 0xBF, 0x37, 0x68, 0x40, 0xB6, 0xCB ), + BYTES_TO_T_UINT_8( 0xCE, 0x5E, 0x31, 0x6B, 0x57, 0x33, 0xCE, 0x2B ), + BYTES_TO_T_UINT_8( 0x16, 0x9E, 0x0F, 0x7C, 0x4A, 0xEB, 0xE7, 0x8E ), + BYTES_TO_T_UINT_8( 0x9B, 0x7F, 0x1A, 0xFE, 0xE2, 0x42, 0xE3, 0x4F ), +}; +static const t_uint secp256r1_n[] = { + BYTES_TO_T_UINT_8( 0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3 ), + BYTES_TO_T_UINT_8( 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP256R1_ENABLED */ + +/* + * Domain parameters for secp384r1 + */ +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) +static const t_uint secp384r1_p[] = { + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp384r1_b[] = { + BYTES_TO_T_UINT_8( 0xEF, 0x2A, 0xEC, 0xD3, 0xED, 0xC8, 0x85, 0x2A ), + BYTES_TO_T_UINT_8( 0x9D, 0xD1, 0x2E, 0x8A, 0x8D, 0x39, 0x56, 0xC6 ), + BYTES_TO_T_UINT_8( 0x5A, 0x87, 0x13, 0x50, 0x8F, 0x08, 0x14, 0x03 ), + BYTES_TO_T_UINT_8( 0x12, 0x41, 0x81, 0xFE, 0x6E, 0x9C, 0x1D, 0x18 ), + BYTES_TO_T_UINT_8( 0x19, 0x2D, 0xF8, 0xE3, 0x6B, 0x05, 0x8E, 0x98 ), + BYTES_TO_T_UINT_8( 0xE4, 0xE7, 0x3E, 0xE2, 0xA7, 0x2F, 0x31, 0xB3 ), +}; +static const t_uint secp384r1_gx[] = { + BYTES_TO_T_UINT_8( 0xB7, 0x0A, 0x76, 0x72, 0x38, 0x5E, 0x54, 0x3A ), + BYTES_TO_T_UINT_8( 0x6C, 0x29, 0x55, 0xBF, 0x5D, 0xF2, 0x02, 0x55 ), + BYTES_TO_T_UINT_8( 0x38, 0x2A, 0x54, 0x82, 0xE0, 0x41, 0xF7, 0x59 ), + BYTES_TO_T_UINT_8( 0x98, 0x9B, 0xA7, 0x8B, 0x62, 0x3B, 0x1D, 0x6E ), + BYTES_TO_T_UINT_8( 0x74, 0xAD, 0x20, 0xF3, 0x1E, 0xC7, 0xB1, 0x8E ), + BYTES_TO_T_UINT_8( 0x37, 0x05, 0x8B, 0xBE, 0x22, 0xCA, 0x87, 0xAA ), +}; +static const t_uint secp384r1_gy[] = { + BYTES_TO_T_UINT_8( 0x5F, 0x0E, 0xEA, 0x90, 0x7C, 0x1D, 0x43, 0x7A ), + BYTES_TO_T_UINT_8( 0x9D, 0x81, 0x7E, 0x1D, 0xCE, 0xB1, 0x60, 0x0A ), + BYTES_TO_T_UINT_8( 0xC0, 0xB8, 0xF0, 0xB5, 0x13, 0x31, 0xDA, 0xE9 ), + BYTES_TO_T_UINT_8( 0x7C, 0x14, 0x9A, 0x28, 0xBD, 0x1D, 0xF4, 0xF8 ), + BYTES_TO_T_UINT_8( 0x29, 0xDC, 0x92, 0x92, 0xBF, 0x98, 0x9E, 0x5D ), + BYTES_TO_T_UINT_8( 0x6F, 0x2C, 0x26, 0x96, 0x4A, 0xDE, 0x17, 0x36 ), +}; +static const t_uint secp384r1_n[] = { + BYTES_TO_T_UINT_8( 0x73, 0x29, 0xC5, 0xCC, 0x6A, 0x19, 0xEC, 0xEC ), + BYTES_TO_T_UINT_8( 0x7A, 0xA7, 0xB0, 0x48, 0xB2, 0x0D, 0x1A, 0x58 ), + BYTES_TO_T_UINT_8( 0xDF, 0x2D, 0x37, 0xF4, 0x81, 0x4D, 0x63, 0xC7 ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP384R1_ENABLED */ + +/* + * Domain parameters for secp521r1 + */ +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) +static const t_uint secp521r1_p[] = { + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_2( 0xFF, 0x01 ), +}; +static const t_uint secp521r1_b[] = { + BYTES_TO_T_UINT_8( 0x00, 0x3F, 0x50, 0x6B, 0xD4, 0x1F, 0x45, 0xEF ), + BYTES_TO_T_UINT_8( 0xF1, 0x34, 0x2C, 0x3D, 0x88, 0xDF, 0x73, 0x35 ), + BYTES_TO_T_UINT_8( 0x07, 0xBF, 0xB1, 0x3B, 0xBD, 0xC0, 0x52, 0x16 ), + BYTES_TO_T_UINT_8( 0x7B, 0x93, 0x7E, 0xEC, 0x51, 0x39, 0x19, 0x56 ), + BYTES_TO_T_UINT_8( 0xE1, 0x09, 0xF1, 0x8E, 0x91, 0x89, 0xB4, 0xB8 ), + BYTES_TO_T_UINT_8( 0xF3, 0x15, 0xB3, 0x99, 0x5B, 0x72, 0xDA, 0xA2 ), + BYTES_TO_T_UINT_8( 0xEE, 0x40, 0x85, 0xB6, 0xA0, 0x21, 0x9A, 0x92 ), + BYTES_TO_T_UINT_8( 0x1F, 0x9A, 0x1C, 0x8E, 0x61, 0xB9, 0x3E, 0x95 ), + BYTES_TO_T_UINT_2( 0x51, 0x00 ), +}; +static const t_uint secp521r1_gx[] = { + BYTES_TO_T_UINT_8( 0x66, 0xBD, 0xE5, 0xC2, 0x31, 0x7E, 0x7E, 0xF9 ), + BYTES_TO_T_UINT_8( 0x9B, 0x42, 0x6A, 0x85, 0xC1, 0xB3, 0x48, 0x33 ), + BYTES_TO_T_UINT_8( 0xDE, 0xA8, 0xFF, 0xA2, 0x27, 0xC1, 0x1D, 0xFE ), + BYTES_TO_T_UINT_8( 0x28, 0x59, 0xE7, 0xEF, 0x77, 0x5E, 0x4B, 0xA1 ), + BYTES_TO_T_UINT_8( 0xBA, 0x3D, 0x4D, 0x6B, 0x60, 0xAF, 0x28, 0xF8 ), + BYTES_TO_T_UINT_8( 0x21, 0xB5, 0x3F, 0x05, 0x39, 0x81, 0x64, 0x9C ), + BYTES_TO_T_UINT_8( 0x42, 0xB4, 0x95, 0x23, 0x66, 0xCB, 0x3E, 0x9E ), + BYTES_TO_T_UINT_8( 0xCD, 0xE9, 0x04, 0x04, 0xB7, 0x06, 0x8E, 0x85 ), + BYTES_TO_T_UINT_2( 0xC6, 0x00 ), +}; +static const t_uint secp521r1_gy[] = { + BYTES_TO_T_UINT_8( 0x50, 0x66, 0xD1, 0x9F, 0x76, 0x94, 0xBE, 0x88 ), + BYTES_TO_T_UINT_8( 0x40, 0xC2, 0x72, 0xA2, 0x86, 0x70, 0x3C, 0x35 ), + BYTES_TO_T_UINT_8( 0x61, 0x07, 0xAD, 0x3F, 0x01, 0xB9, 0x50, 0xC5 ), + BYTES_TO_T_UINT_8( 0x40, 0x26, 0xF4, 0x5E, 0x99, 0x72, 0xEE, 0x97 ), + BYTES_TO_T_UINT_8( 0x2C, 0x66, 0x3E, 0x27, 0x17, 0xBD, 0xAF, 0x17 ), + BYTES_TO_T_UINT_8( 0x68, 0x44, 0x9B, 0x57, 0x49, 0x44, 0xF5, 0x98 ), + BYTES_TO_T_UINT_8( 0xD9, 0x1B, 0x7D, 0x2C, 0xB4, 0x5F, 0x8A, 0x5C ), + BYTES_TO_T_UINT_8( 0x04, 0xC0, 0x3B, 0x9A, 0x78, 0x6A, 0x29, 0x39 ), + BYTES_TO_T_UINT_2( 0x18, 0x01 ), +}; +static const t_uint secp521r1_n[] = { + BYTES_TO_T_UINT_8( 0x09, 0x64, 0x38, 0x91, 0x1E, 0xB7, 0x6F, 0xBB ), + BYTES_TO_T_UINT_8( 0xAE, 0x47, 0x9C, 0x89, 0xB8, 0xC9, 0xB5, 0x3B ), + BYTES_TO_T_UINT_8( 0xD0, 0xA5, 0x09, 0xF7, 0x48, 0x01, 0xCC, 0x7F ), + BYTES_TO_T_UINT_8( 0x6B, 0x96, 0x2F, 0xBF, 0x83, 0x87, 0x86, 0x51 ), + BYTES_TO_T_UINT_8( 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_2( 0xFF, 0x01 ), +}; +#endif /* POLARSSL_ECP_DP_SECP521R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) +static const t_uint secp192k1_p[] = { + BYTES_TO_T_UINT_8( 0x37, 0xEE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp192k1_a[] = { + BYTES_TO_T_UINT_2( 0x00, 0x00 ), +}; +static const t_uint secp192k1_b[] = { + BYTES_TO_T_UINT_2( 0x03, 0x00 ), +}; +static const t_uint secp192k1_gx[] = { + BYTES_TO_T_UINT_8( 0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D ), + BYTES_TO_T_UINT_8( 0x34, 0xF4, 0xB7, 0x80, 0x02, 0x7D, 0xB0, 0x26 ), + BYTES_TO_T_UINT_8( 0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB ), +}; +static const t_uint secp192k1_gy[] = { + BYTES_TO_T_UINT_8( 0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40 ), + BYTES_TO_T_UINT_8( 0x34, 0x86, 0xBE, 0x15, 0xD0, 0x63, 0x41, 0x84 ), + BYTES_TO_T_UINT_8( 0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B ), +}; +static const t_uint secp192k1_n[] = { + BYTES_TO_T_UINT_8( 0x8D, 0xFD, 0xDE, 0x74, 0x6A, 0x46, 0x69, 0x0F ), + BYTES_TO_T_UINT_8( 0x17, 0xFC, 0xF2, 0x26, 0xFE, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP192K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) +static const t_uint secp224k1_p[] = { + BYTES_TO_T_UINT_8( 0x6D, 0xE5, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_4( 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp224k1_a[] = { + BYTES_TO_T_UINT_2( 0x00, 0x00 ), +}; +static const t_uint secp224k1_b[] = { + BYTES_TO_T_UINT_2( 0x05, 0x00 ), +}; +static const t_uint secp224k1_gx[] = { + BYTES_TO_T_UINT_8( 0x5C, 0xA4, 0xB7, 0xB6, 0x0E, 0x65, 0x7E, 0x0F ), + BYTES_TO_T_UINT_8( 0xA9, 0x75, 0x70, 0xE4, 0xE9, 0x67, 0xA4, 0x69 ), + BYTES_TO_T_UINT_8( 0xA1, 0x28, 0xFC, 0x30, 0xDF, 0x99, 0xF0, 0x4D ), + BYTES_TO_T_UINT_4( 0x33, 0x5B, 0x45, 0xA1 ), +}; +static const t_uint secp224k1_gy[] = { + BYTES_TO_T_UINT_8( 0xA5, 0x61, 0x6D, 0x55, 0xDB, 0x4B, 0xCA, 0xE2 ), + BYTES_TO_T_UINT_8( 0x59, 0xBD, 0xB0, 0xC0, 0xF7, 0x19, 0xE3, 0xF7 ), + BYTES_TO_T_UINT_8( 0xD6, 0xFB, 0xCA, 0x82, 0x42, 0x34, 0xBA, 0x7F ), + BYTES_TO_T_UINT_4( 0xED, 0x9F, 0x08, 0x7E ), +}; +static const t_uint secp224k1_n[] = { + BYTES_TO_T_UINT_8( 0xF7, 0xB1, 0x9F, 0x76, 0x71, 0xA9, 0xF0, 0xCA ), + BYTES_TO_T_UINT_8( 0x84, 0x61, 0xEC, 0xD2, 0xE8, 0xDC, 0x01, 0x00 ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ), + BYTES_TO_T_UINT_8( 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 ), +}; +#endif /* POLARSSL_ECP_DP_SECP224K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) +static const t_uint secp256k1_p[] = { + BYTES_TO_T_UINT_8( 0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +static const t_uint secp256k1_a[] = { + BYTES_TO_T_UINT_2( 0x00, 0x00 ), +}; +static const t_uint secp256k1_b[] = { + BYTES_TO_T_UINT_2( 0x07, 0x00 ), +}; +static const t_uint secp256k1_gx[] = { + BYTES_TO_T_UINT_8( 0x98, 0x17, 0xF8, 0x16, 0x5B, 0x81, 0xF2, 0x59 ), + BYTES_TO_T_UINT_8( 0xD9, 0x28, 0xCE, 0x2D, 0xDB, 0xFC, 0x9B, 0x02 ), + BYTES_TO_T_UINT_8( 0x07, 0x0B, 0x87, 0xCE, 0x95, 0x62, 0xA0, 0x55 ), + BYTES_TO_T_UINT_8( 0xAC, 0xBB, 0xDC, 0xF9, 0x7E, 0x66, 0xBE, 0x79 ), +}; +static const t_uint secp256k1_gy[] = { + BYTES_TO_T_UINT_8( 0xB8, 0xD4, 0x10, 0xFB, 0x8F, 0xD0, 0x47, 0x9C ), + BYTES_TO_T_UINT_8( 0x19, 0x54, 0x85, 0xA6, 0x48, 0xB4, 0x17, 0xFD ), + BYTES_TO_T_UINT_8( 0xA8, 0x08, 0x11, 0x0E, 0xFC, 0xFB, 0xA4, 0x5D ), + BYTES_TO_T_UINT_8( 0x65, 0xC4, 0xA3, 0x26, 0x77, 0xDA, 0x3A, 0x48 ), +}; +static const t_uint secp256k1_n[] = { + BYTES_TO_T_UINT_8( 0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF ), + BYTES_TO_T_UINT_8( 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA ), + BYTES_TO_T_UINT_8( 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), + BYTES_TO_T_UINT_8( 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ), +}; +#endif /* POLARSSL_ECP_DP_SECP256K1_ENABLED */ + +/* + * Domain parameters for brainpoolP256r1 (RFC 5639 3.4) + */ +#if defined(POLARSSL_ECP_DP_BP256R1_ENABLED) +static const t_uint brainpoolP256r1_p[] = { + BYTES_TO_T_UINT_8( 0x77, 0x53, 0x6E, 0x1F, 0x1D, 0x48, 0x13, 0x20 ), + BYTES_TO_T_UINT_8( 0x28, 0x20, 0x26, 0xD5, 0x23, 0xF6, 0x3B, 0x6E ), + BYTES_TO_T_UINT_8( 0x72, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E ), + BYTES_TO_T_UINT_8( 0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9 ), +}; +static const t_uint brainpoolP256r1_a[] = { + BYTES_TO_T_UINT_8( 0xD9, 0xB5, 0x30, 0xF3, 0x44, 0x4B, 0x4A, 0xE9 ), + BYTES_TO_T_UINT_8( 0x6C, 0x5C, 0xDC, 0x26, 0xC1, 0x55, 0x80, 0xFB ), + BYTES_TO_T_UINT_8( 0xE7, 0xFF, 0x7A, 0x41, 0x30, 0x75, 0xF6, 0xEE ), + BYTES_TO_T_UINT_8( 0x57, 0x30, 0x2C, 0xFC, 0x75, 0x09, 0x5A, 0x7D ), +}; +static const t_uint brainpoolP256r1_b[] = { + BYTES_TO_T_UINT_8( 0xB6, 0x07, 0x8C, 0xFF, 0x18, 0xDC, 0xCC, 0x6B ), + BYTES_TO_T_UINT_8( 0xCE, 0xE1, 0xF7, 0x5C, 0x29, 0x16, 0x84, 0x95 ), + BYTES_TO_T_UINT_8( 0xBF, 0x7C, 0xD7, 0xBB, 0xD9, 0xB5, 0x30, 0xF3 ), + BYTES_TO_T_UINT_8( 0x44, 0x4B, 0x4A, 0xE9, 0x6C, 0x5C, 0xDC, 0x26 ), +}; +static const t_uint brainpoolP256r1_gx[] = { + BYTES_TO_T_UINT_8( 0x62, 0x32, 0xCE, 0x9A, 0xBD, 0x53, 0x44, 0x3A ), + BYTES_TO_T_UINT_8( 0xC2, 0x23, 0xBD, 0xE3, 0xE1, 0x27, 0xDE, 0xB9 ), + BYTES_TO_T_UINT_8( 0xAF, 0xB7, 0x81, 0xFC, 0x2F, 0x48, 0x4B, 0x2C ), + BYTES_TO_T_UINT_8( 0xCB, 0x57, 0x7E, 0xCB, 0xB9, 0xAE, 0xD2, 0x8B ), +}; +static const t_uint brainpoolP256r1_gy[] = { + BYTES_TO_T_UINT_8( 0x97, 0x69, 0x04, 0x2F, 0xC7, 0x54, 0x1D, 0x5C ), + BYTES_TO_T_UINT_8( 0x54, 0x8E, 0xED, 0x2D, 0x13, 0x45, 0x77, 0xC2 ), + BYTES_TO_T_UINT_8( 0xC9, 0x1D, 0x61, 0x14, 0x1A, 0x46, 0xF8, 0x97 ), + BYTES_TO_T_UINT_8( 0xFD, 0xC4, 0xDA, 0xC3, 0x35, 0xF8, 0x7E, 0x54 ), +}; +static const t_uint brainpoolP256r1_n[] = { + BYTES_TO_T_UINT_8( 0xA7, 0x56, 0x48, 0x97, 0x82, 0x0E, 0x1E, 0x90 ), + BYTES_TO_T_UINT_8( 0xF7, 0xA6, 0x61, 0xB5, 0xA3, 0x7A, 0x39, 0x8C ), + BYTES_TO_T_UINT_8( 0x71, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E ), + BYTES_TO_T_UINT_8( 0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9 ), +}; +#endif /* POLARSSL_ECP_DP_BP256R1_ENABLED */ + +/* + * Domain parameters for brainpoolP384r1 (RFC 5639 3.6) + */ +#if defined(POLARSSL_ECP_DP_BP384R1_ENABLED) +static const t_uint brainpoolP384r1_p[] = { + BYTES_TO_T_UINT_8( 0x53, 0xEC, 0x07, 0x31, 0x13, 0x00, 0x47, 0x87 ), + BYTES_TO_T_UINT_8( 0x71, 0x1A, 0x1D, 0x90, 0x29, 0xA7, 0xD3, 0xAC ), + BYTES_TO_T_UINT_8( 0x23, 0x11, 0xB7, 0x7F, 0x19, 0xDA, 0xB1, 0x12 ), + BYTES_TO_T_UINT_8( 0xB4, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15 ), + BYTES_TO_T_UINT_8( 0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F ), + BYTES_TO_T_UINT_8( 0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C ), +}; +static const t_uint brainpoolP384r1_a[] = { + BYTES_TO_T_UINT_8( 0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04 ), + BYTES_TO_T_UINT_8( 0xEB, 0xD4, 0x3A, 0x50, 0x4A, 0x81, 0xA5, 0x8A ), + BYTES_TO_T_UINT_8( 0x0F, 0xF9, 0x91, 0xBA, 0xEF, 0x65, 0x91, 0x13 ), + BYTES_TO_T_UINT_8( 0x87, 0x27, 0xB2, 0x4F, 0x8E, 0xA2, 0xBE, 0xC2 ), + BYTES_TO_T_UINT_8( 0xA0, 0xAF, 0x05, 0xCE, 0x0A, 0x08, 0x72, 0x3C ), + BYTES_TO_T_UINT_8( 0x0C, 0x15, 0x8C, 0x3D, 0xC6, 0x82, 0xC3, 0x7B ), +}; +static const t_uint brainpoolP384r1_b[] = { + BYTES_TO_T_UINT_8( 0x11, 0x4C, 0x50, 0xFA, 0x96, 0x86, 0xB7, 0x3A ), + BYTES_TO_T_UINT_8( 0x94, 0xC9, 0xDB, 0x95, 0x02, 0x39, 0xB4, 0x7C ), + BYTES_TO_T_UINT_8( 0xD5, 0x62, 0xEB, 0x3E, 0xA5, 0x0E, 0x88, 0x2E ), + BYTES_TO_T_UINT_8( 0xA6, 0xD2, 0xDC, 0x07, 0xE1, 0x7D, 0xB7, 0x2F ), + BYTES_TO_T_UINT_8( 0x7C, 0x44, 0xF0, 0x16, 0x54, 0xB5, 0x39, 0x8B ), + BYTES_TO_T_UINT_8( 0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04 ), +}; +static const t_uint brainpoolP384r1_gx[] = { + BYTES_TO_T_UINT_8( 0x1E, 0xAF, 0xD4, 0x47, 0xE2, 0xB2, 0x87, 0xEF ), + BYTES_TO_T_UINT_8( 0xAA, 0x46, 0xD6, 0x36, 0x34, 0xE0, 0x26, 0xE8 ), + BYTES_TO_T_UINT_8( 0xE8, 0x10, 0xBD, 0x0C, 0xFE, 0xCA, 0x7F, 0xDB ), + BYTES_TO_T_UINT_8( 0xE3, 0x4F, 0xF1, 0x7E, 0xE7, 0xA3, 0x47, 0x88 ), + BYTES_TO_T_UINT_8( 0x6B, 0x3F, 0xC1, 0xB7, 0x81, 0x3A, 0xA6, 0xA2 ), + BYTES_TO_T_UINT_8( 0xFF, 0x45, 0xCF, 0x68, 0xF0, 0x64, 0x1C, 0x1D ), +}; +static const t_uint brainpoolP384r1_gy[] = { + BYTES_TO_T_UINT_8( 0x15, 0x53, 0x3C, 0x26, 0x41, 0x03, 0x82, 0x42 ), + BYTES_TO_T_UINT_8( 0x11, 0x81, 0x91, 0x77, 0x21, 0x46, 0x46, 0x0E ), + BYTES_TO_T_UINT_8( 0x28, 0x29, 0x91, 0xF9, 0x4F, 0x05, 0x9C, 0xE1 ), + BYTES_TO_T_UINT_8( 0x64, 0x58, 0xEC, 0xFE, 0x29, 0x0B, 0xB7, 0x62 ), + BYTES_TO_T_UINT_8( 0x52, 0xD5, 0xCF, 0x95, 0x8E, 0xEB, 0xB1, 0x5C ), + BYTES_TO_T_UINT_8( 0xA4, 0xC2, 0xF9, 0x20, 0x75, 0x1D, 0xBE, 0x8A ), +}; +static const t_uint brainpoolP384r1_n[] = { + BYTES_TO_T_UINT_8( 0x65, 0x65, 0x04, 0xE9, 0x02, 0x32, 0x88, 0x3B ), + BYTES_TO_T_UINT_8( 0x10, 0xC3, 0x7F, 0x6B, 0xAF, 0xB6, 0x3A, 0xCF ), + BYTES_TO_T_UINT_8( 0xA7, 0x25, 0x04, 0xAC, 0x6C, 0x6E, 0x16, 0x1F ), + BYTES_TO_T_UINT_8( 0xB3, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15 ), + BYTES_TO_T_UINT_8( 0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F ), + BYTES_TO_T_UINT_8( 0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C ), +}; +#endif /* POLARSSL_ECP_DP_BP384R1_ENABLED */ + +/* + * Domain parameters for brainpoolP512r1 (RFC 5639 3.7) + */ +#if defined(POLARSSL_ECP_DP_BP512R1_ENABLED) +static const t_uint brainpoolP512r1_p[] = { + BYTES_TO_T_UINT_8( 0xF3, 0x48, 0x3A, 0x58, 0x56, 0x60, 0xAA, 0x28 ), + BYTES_TO_T_UINT_8( 0x85, 0xC6, 0x82, 0x2D, 0x2F, 0xFF, 0x81, 0x28 ), + BYTES_TO_T_UINT_8( 0xE6, 0x80, 0xA3, 0xE6, 0x2A, 0xA1, 0xCD, 0xAE ), + BYTES_TO_T_UINT_8( 0x42, 0x68, 0xC6, 0x9B, 0x00, 0x9B, 0x4D, 0x7D ), + BYTES_TO_T_UINT_8( 0x71, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6 ), + BYTES_TO_T_UINT_8( 0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB ), + BYTES_TO_T_UINT_8( 0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F ), + BYTES_TO_T_UINT_8( 0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA ), +}; +static const t_uint brainpoolP512r1_a[] = { + BYTES_TO_T_UINT_8( 0xCA, 0x94, 0xFC, 0x77, 0x4D, 0xAC, 0xC1, 0xE7 ), + BYTES_TO_T_UINT_8( 0xB9, 0xC7, 0xF2, 0x2B, 0xA7, 0x17, 0x11, 0x7F ), + BYTES_TO_T_UINT_8( 0xB5, 0xC8, 0x9A, 0x8B, 0xC9, 0xF1, 0x2E, 0x0A ), + BYTES_TO_T_UINT_8( 0xA1, 0x3A, 0x25, 0xA8, 0x5A, 0x5D, 0xED, 0x2D ), + BYTES_TO_T_UINT_8( 0xBC, 0x63, 0x98, 0xEA, 0xCA, 0x41, 0x34, 0xA8 ), + BYTES_TO_T_UINT_8( 0x10, 0x16, 0xF9, 0x3D, 0x8D, 0xDD, 0xCB, 0x94 ), + BYTES_TO_T_UINT_8( 0xC5, 0x4C, 0x23, 0xAC, 0x45, 0x71, 0x32, 0xE2 ), + BYTES_TO_T_UINT_8( 0x89, 0x3B, 0x60, 0x8B, 0x31, 0xA3, 0x30, 0x78 ), +}; +static const t_uint brainpoolP512r1_b[] = { + BYTES_TO_T_UINT_8( 0x23, 0xF7, 0x16, 0x80, 0x63, 0xBD, 0x09, 0x28 ), + BYTES_TO_T_UINT_8( 0xDD, 0xE5, 0xBA, 0x5E, 0xB7, 0x50, 0x40, 0x98 ), + BYTES_TO_T_UINT_8( 0x67, 0x3E, 0x08, 0xDC, 0xCA, 0x94, 0xFC, 0x77 ), + BYTES_TO_T_UINT_8( 0x4D, 0xAC, 0xC1, 0xE7, 0xB9, 0xC7, 0xF2, 0x2B ), + BYTES_TO_T_UINT_8( 0xA7, 0x17, 0x11, 0x7F, 0xB5, 0xC8, 0x9A, 0x8B ), + BYTES_TO_T_UINT_8( 0xC9, 0xF1, 0x2E, 0x0A, 0xA1, 0x3A, 0x25, 0xA8 ), + BYTES_TO_T_UINT_8( 0x5A, 0x5D, 0xED, 0x2D, 0xBC, 0x63, 0x98, 0xEA ), + BYTES_TO_T_UINT_8( 0xCA, 0x41, 0x34, 0xA8, 0x10, 0x16, 0xF9, 0x3D ), +}; +static const t_uint brainpoolP512r1_gx[] = { + BYTES_TO_T_UINT_8( 0x22, 0xF8, 0xB9, 0xBC, 0x09, 0x22, 0x35, 0x8B ), + BYTES_TO_T_UINT_8( 0x68, 0x5E, 0x6A, 0x40, 0x47, 0x50, 0x6D, 0x7C ), + BYTES_TO_T_UINT_8( 0x5F, 0x7D, 0xB9, 0x93, 0x7B, 0x68, 0xD1, 0x50 ), + BYTES_TO_T_UINT_8( 0x8D, 0xD4, 0xD0, 0xE2, 0x78, 0x1F, 0x3B, 0xFF ), + BYTES_TO_T_UINT_8( 0x8E, 0x09, 0xD0, 0xF4, 0xEE, 0x62, 0x3B, 0xB4 ), + BYTES_TO_T_UINT_8( 0xC1, 0x16, 0xD9, 0xB5, 0x70, 0x9F, 0xED, 0x85 ), + BYTES_TO_T_UINT_8( 0x93, 0x6A, 0x4C, 0x9C, 0x2E, 0x32, 0x21, 0x5A ), + BYTES_TO_T_UINT_8( 0x64, 0xD9, 0x2E, 0xD8, 0xBD, 0xE4, 0xAE, 0x81 ), +}; +static const t_uint brainpoolP512r1_gy[] = { + BYTES_TO_T_UINT_8( 0x92, 0x08, 0xD8, 0x3A, 0x0F, 0x1E, 0xCD, 0x78 ), + BYTES_TO_T_UINT_8( 0x06, 0x54, 0xF0, 0xA8, 0x2F, 0x2B, 0xCA, 0xD1 ), + BYTES_TO_T_UINT_8( 0xAE, 0x63, 0x27, 0x8A, 0xD8, 0x4B, 0xCA, 0x5B ), + BYTES_TO_T_UINT_8( 0x5E, 0x48, 0x5F, 0x4A, 0x49, 0xDE, 0xDC, 0xB2 ), + BYTES_TO_T_UINT_8( 0x11, 0x81, 0x1F, 0x88, 0x5B, 0xC5, 0x00, 0xA0 ), + BYTES_TO_T_UINT_8( 0x1A, 0x7B, 0xA5, 0x24, 0x00, 0xF7, 0x09, 0xF2 ), + BYTES_TO_T_UINT_8( 0xFD, 0x22, 0x78, 0xCF, 0xA9, 0xBF, 0xEA, 0xC0 ), + BYTES_TO_T_UINT_8( 0xEC, 0x32, 0x63, 0x56, 0x5D, 0x38, 0xDE, 0x7D ), +}; +static const t_uint brainpoolP512r1_n[] = { + BYTES_TO_T_UINT_8( 0x69, 0x00, 0xA9, 0x9C, 0x82, 0x96, 0x87, 0xB5 ), + BYTES_TO_T_UINT_8( 0xDD, 0xDA, 0x5D, 0x08, 0x81, 0xD3, 0xB1, 0x1D ), + BYTES_TO_T_UINT_8( 0x47, 0x10, 0xAC, 0x7F, 0x19, 0x61, 0x86, 0x41 ), + BYTES_TO_T_UINT_8( 0x19, 0x26, 0xA9, 0x4C, 0x41, 0x5C, 0x3E, 0x55 ), + BYTES_TO_T_UINT_8( 0x70, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6 ), + BYTES_TO_T_UINT_8( 0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB ), + BYTES_TO_T_UINT_8( 0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F ), + BYTES_TO_T_UINT_8( 0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA ), +}; +#endif /* POLARSSL_ECP_DP_BP512R1_ENABLED */ + +/* + * Create an MPI from embedded constants + * (assumes len is an exact multiple of sizeof t_uint) + */ +static inline void ecp_mpi_load( mpi *X, const t_uint *p, size_t len ) +{ + X->s = 1; + X->n = len / sizeof( t_uint ); + X->p = (t_uint *) p; +} + +/* + * Set an MPI to static value 1 + */ +static inline void ecp_mpi_set1( mpi *X ) +{ + static t_uint one[] = { 1 }; + X->s = 1; + X->n = 1; + X->p = one; +} + +/* + * Make group available from embedded constants + */ +static int ecp_group_load( ecp_group *grp, + const t_uint *p, size_t plen, + const t_uint *a, size_t alen, + const t_uint *b, size_t blen, + const t_uint *gx, size_t gxlen, + const t_uint *gy, size_t gylen, + const t_uint *n, size_t nlen) +{ + ecp_mpi_load( &grp->P, p, plen ); + if( a != NULL ) + ecp_mpi_load( &grp->A, a, alen ); + ecp_mpi_load( &grp->B, b, blen ); + ecp_mpi_load( &grp->N, n, nlen ); + + ecp_mpi_load( &grp->G.X, gx, gxlen ); + ecp_mpi_load( &grp->G.Y, gy, gylen ); + ecp_mpi_set1( &grp->G.Z ); + + grp->pbits = mpi_msb( &grp->P ); + grp->nbits = mpi_msb( &grp->N ); + + grp->h = 1; + + return( 0 ); +} + +#if defined(POLARSSL_ECP_NIST_OPTIM) +/* Forward declarations */ +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) +static int ecp_mod_p192( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) +static int ecp_mod_p224( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) +static int ecp_mod_p256( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) +static int ecp_mod_p384( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) +static int ecp_mod_p521( mpi * ); +#endif + +#define NIST_MODP( P ) grp->modp = ecp_mod_ ## P; +#else +#define NIST_MODP( P ) +#endif /* POLARSSL_ECP_NIST_OPTIM */ + +/* Additional forward declarations */ +#if defined(POLARSSL_ECP_DP_M255_ENABLED) +static int ecp_mod_p255( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) +static int ecp_mod_p192k1( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) +static int ecp_mod_p224k1( mpi * ); +#endif +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) +static int ecp_mod_p256k1( mpi * ); +#endif + +#define LOAD_GROUP_A( G ) ecp_group_load( grp, \ + G ## _p, sizeof( G ## _p ), \ + G ## _a, sizeof( G ## _a ), \ + G ## _b, sizeof( G ## _b ), \ + G ## _gx, sizeof( G ## _gx ), \ + G ## _gy, sizeof( G ## _gy ), \ + G ## _n, sizeof( G ## _n ) ) + +#define LOAD_GROUP( G ) ecp_group_load( grp, \ + G ## _p, sizeof( G ## _p ), \ + NULL, 0, \ + G ## _b, sizeof( G ## _b ), \ + G ## _gx, sizeof( G ## _gx ), \ + G ## _gy, sizeof( G ## _gy ), \ + G ## _n, sizeof( G ## _n ) ) + +#if defined(POLARSSL_ECP_DP_M255_ENABLED) +/* + * Specialized function for creating the Curve25519 group + */ +static int ecp_use_curve25519( ecp_group *grp ) +{ + int ret; + + /* Actually ( A + 2 ) / 4 */ + MPI_CHK( mpi_read_string( &grp->A, 16, "01DB42" ) ); + + /* P = 2^255 - 19 */ + MPI_CHK( mpi_lset( &grp->P, 1 ) ); + MPI_CHK( mpi_shift_l( &grp->P, 255 ) ); + MPI_CHK( mpi_sub_int( &grp->P, &grp->P, 19 ) ); + grp->pbits = mpi_msb( &grp->P ); + + /* Y intentionaly not set, since we use x/z coordinates. + * This is used as a marker to identify Montgomery curves! */ + MPI_CHK( mpi_lset( &grp->G.X, 9 ) ); + MPI_CHK( mpi_lset( &grp->G.Z, 1 ) ); + mpi_free( &grp->G.Y ); + + /* Actually, the required msb for private keys */ + grp->nbits = 254; + +cleanup: + if( ret != 0 ) + ecp_group_free( grp ); + + return( ret ); +} +#endif /* POLARSSL_ECP_DP_M255_ENABLED */ + +/* + * Set a group using well-known domain parameters + */ +int ecp_use_known_dp( ecp_group *grp, ecp_group_id id ) +{ + ecp_group_free( grp ); + + grp->id = id; + + switch( id ) + { +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) + case POLARSSL_ECP_DP_SECP192R1: + NIST_MODP( p192 ); + return( LOAD_GROUP( secp192r1 ) ); +#endif /* POLARSSL_ECP_DP_SECP192R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) + case POLARSSL_ECP_DP_SECP224R1: + NIST_MODP( p224 ); + return( LOAD_GROUP( secp224r1 ) ); +#endif /* POLARSSL_ECP_DP_SECP224R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) + case POLARSSL_ECP_DP_SECP256R1: + NIST_MODP( p256 ); + return( LOAD_GROUP( secp256r1 ) ); +#endif /* POLARSSL_ECP_DP_SECP256R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) + case POLARSSL_ECP_DP_SECP384R1: + NIST_MODP( p384 ); + return( LOAD_GROUP( secp384r1 ) ); +#endif /* POLARSSL_ECP_DP_SECP384R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) + case POLARSSL_ECP_DP_SECP521R1: + NIST_MODP( p521 ); + return( LOAD_GROUP( secp521r1 ) ); +#endif /* POLARSSL_ECP_DP_SECP521R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) + case POLARSSL_ECP_DP_SECP192K1: + grp->modp = ecp_mod_p192k1; + return( LOAD_GROUP_A( secp192k1 ) ); +#endif /* POLARSSL_ECP_DP_SECP192K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) + case POLARSSL_ECP_DP_SECP224K1: + grp->modp = ecp_mod_p224k1; + return( LOAD_GROUP_A( secp224k1 ) ); +#endif /* POLARSSL_ECP_DP_SECP224K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) + case POLARSSL_ECP_DP_SECP256K1: + grp->modp = ecp_mod_p256k1; + return( LOAD_GROUP_A( secp256k1 ) ); +#endif /* POLARSSL_ECP_DP_SECP256K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_BP256R1_ENABLED) + case POLARSSL_ECP_DP_BP256R1: + return( LOAD_GROUP_A( brainpoolP256r1 ) ); +#endif /* POLARSSL_ECP_DP_BP256R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_BP384R1_ENABLED) + case POLARSSL_ECP_DP_BP384R1: + return( LOAD_GROUP_A( brainpoolP384r1 ) ); +#endif /* POLARSSL_ECP_DP_BP384R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_BP512R1_ENABLED) + case POLARSSL_ECP_DP_BP512R1: + return( LOAD_GROUP_A( brainpoolP512r1 ) ); +#endif /* POLARSSL_ECP_DP_BP512R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_M255_ENABLED) + case POLARSSL_ECP_DP_M255: + grp->modp = ecp_mod_p255; + return( ecp_use_curve25519( grp ) ); +#endif /* POLARSSL_ECP_DP_M255_ENABLED */ + + default: + ecp_group_free( grp ); + return( POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ); + } +} + +#if defined(POLARSSL_ECP_NIST_OPTIM) +/* + * Fast reduction modulo the primes used by the NIST curves. + * + * These functions are critical for speed, but not needed for correct + * operations. So, we make the choice to heavily rely on the internals of our + * bignum library, which creates a tight coupling between these functions and + * our MPI implementation. However, the coupling between the ECP module and + * MPI remains loose, since these functions can be deactivated at will. + */ + +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) +/* + * Compared to the way things are presented in FIPS 186-3 D.2, + * we proceed in columns, from right (least significant chunk) to left, + * adding chunks to N in place, and keeping a carry for the next chunk. + * This avoids moving things around in memory, and uselessly adding zeros, + * compared to the more straightforward, line-oriented approach. + * + * For this prime we need to handle data in chunks of 64 bits. + * Since this is always a multiple of our basic t_uint, we can + * use a t_uint * to designate such a chunk, and small loops to handle it. + */ + +/* Add 64-bit chunks (dst += src) and update carry */ +static inline void add64( t_uint *dst, t_uint *src, t_uint *carry ) +{ + unsigned char i; + t_uint c = 0; + for( i = 0; i < 8 / sizeof( t_uint ); i++, dst++, src++ ) + { + *dst += c; c = ( *dst < c ); + *dst += *src; c += ( *dst < *src ); + } + *carry += c; +} + +/* Add carry to a 64-bit chunk and update carry */ +static inline void carry64( t_uint *dst, t_uint *carry ) +{ + unsigned char i; + for( i = 0; i < 8 / sizeof( t_uint ); i++, dst++ ) + { + *dst += *carry; + *carry = ( *dst < *carry ); + } +} + +#define WIDTH 8 / sizeof( t_uint ) +#define A( i ) N->p + i * WIDTH +#define ADD( i ) add64( p, A( i ), &c ) +#define NEXT p += WIDTH; carry64( p, &c ) +#define LAST p += WIDTH; *p = c; while( ++p < end ) *p = 0 + +/* + * Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1) + */ +static int ecp_mod_p192( mpi *N ) +{ + int ret; + t_uint c = 0; + t_uint *p, *end; + + /* Make sure we have enough blocks so that A(5) is legal */ + MPI_CHK( mpi_grow( N, 6 * WIDTH ) ); + + p = N->p; + end = p + N->n; + + ADD( 3 ); ADD( 5 ); NEXT; // A0 += A3 + A5 + ADD( 3 ); ADD( 4 ); ADD( 5 ); NEXT; // A1 += A3 + A4 + A5 + ADD( 4 ); ADD( 5 ); LAST; // A2 += A4 + A5 + +cleanup: + return( ret ); +} + +#undef WIDTH +#undef A +#undef ADD +#undef NEXT +#undef LAST +#endif /* POLARSSL_ECP_DP_SECP192R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) +/* + * The reader is advised to first understand ecp_mod_p192() since the same + * general structure is used here, but with additional complications: + * (1) chunks of 32 bits, and (2) subtractions. + */ + +/* + * For these primes, we need to handle data in chunks of 32 bits. + * This makes it more complicated if we use 64 bits limbs in MPI, + * which prevents us from using a uniform access method as for p192. + * + * So, we define a mini abstraction layer to access 32 bit chunks, + * load them in 'cur' for work, and store them back from 'cur' when done. + * + * While at it, also define the size of N in terms of 32-bit chunks. + */ +#define LOAD32 cur = A( i ); + +#if defined(POLARSSL_HAVE_INT8) /* 8 bit */ + +#define MAX32 N->n / 4 +#define A( j ) (uint32_t)( N->p[4*j+0] ) | \ + ( N->p[4*j+1] << 8 ) | \ + ( N->p[4*j+2] << 16 ) | \ + ( N->p[4*j+3] << 24 ) +#define STORE32 N->p[4*i+0] = (t_uint)( cur ); \ + N->p[4*i+1] = (t_uint)( cur >> 8 ); \ + N->p[4*i+2] = (t_uint)( cur >> 16 ); \ + N->p[4*i+3] = (t_uint)( cur >> 24 ); + +#elif defined(POLARSSL_HAVE_INT16) /* 16 bit */ + +#define MAX32 N->n / 2 +#define A( j ) (uint32_t)( N->p[2*j] ) | ( N->p[2*j+1] << 16 ) +#define STORE32 N->p[2*i+0] = (t_uint)( cur ); \ + N->p[2*i+1] = (t_uint)( cur >> 16 ); + +#elif defined(POLARSSL_HAVE_INT32) /* 32 bit */ + +#define MAX32 N->n +#define A( j ) N->p[j] +#define STORE32 N->p[i] = cur; + +#else /* 64-bit */ + +#define MAX32 N->n * 2 +#define A( j ) j % 2 ? (uint32_t)( N->p[j/2] >> 32 ) : (uint32_t)( N->p[j/2] ) +#define STORE32 \ + if( i % 2 ) { \ + N->p[i/2] &= 0x00000000FFFFFFFF; \ + N->p[i/2] |= ((t_uint) cur) << 32; \ + } else { \ + N->p[i/2] &= 0xFFFFFFFF00000000; \ + N->p[i/2] |= (t_uint) cur; \ + } + +#endif /* sizeof( t_uint ) */ + +/* + * Helpers for addition and subtraction of chunks, with signed carry. + */ +static inline void add32( uint32_t *dst, uint32_t src, signed char *carry ) +{ + *dst += src; + *carry += ( *dst < src ); +} + +static inline void sub32( uint32_t *dst, uint32_t src, signed char *carry ) +{ + *carry -= ( *dst < src ); + *dst -= src; +} + +#define ADD( j ) add32( &cur, A( j ), &c ); +#define SUB( j ) sub32( &cur, A( j ), &c ); + +/* + * Helpers for the main 'loop' + * (see fix_negative for the motivation of C) + */ +#define INIT( b ) \ + int ret; \ + signed char c = 0, cc; \ + uint32_t cur; \ + size_t i = 0, bits = b; \ + mpi C; \ + t_uint Cp[ b / 8 / sizeof( t_uint) + 1 ]; \ + \ + C.s = 1; \ + C.n = b / 8 / sizeof( t_uint) + 1; \ + C.p = Cp; \ + memset( Cp, 0, C.n * sizeof( t_uint ) ); \ + \ + MPI_CHK( mpi_grow( N, b * 2 / 8 / sizeof( t_uint ) ) ); \ + LOAD32; + +#define NEXT \ + STORE32; i++; LOAD32; \ + cc = c; c = 0; \ + if( cc < 0 ) \ + sub32( &cur, -cc, &c ); \ + else \ + add32( &cur, cc, &c ); \ + +#define LAST \ + STORE32; i++; \ + cur = c > 0 ? c : 0; STORE32; \ + cur = 0; while( ++i < MAX32 ) { STORE32; } \ + if( c < 0 ) fix_negative( N, c, &C, bits ); + +/* + * If the result is negative, we get it in the form + * c * 2^(bits + 32) + N, with c negative and N positive shorter than 'bits' + */ +static inline int fix_negative( mpi *N, signed char c, mpi *C, size_t bits ) +{ + int ret; + + /* C = - c * 2^(bits + 32) */ +#if !defined(POLARSSL_HAVE_INT64) + ((void) bits); +#else + if( bits == 224 ) + C->p[ C->n - 1 ] = ((t_uint) -c) << 32; + else +#endif + C->p[ C->n - 1 ] = (t_uint) -c; + + /* N = - ( C - N ) */ + MPI_CHK( mpi_sub_abs( N, C, N ) ); + N->s = -1; + +cleanup: + + return( ret ); +} + +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) +/* + * Fast quasi-reduction modulo p224 (FIPS 186-3 D.2.2) + */ +static int ecp_mod_p224( mpi *N ) +{ + INIT( 224 ); + + SUB( 7 ); SUB( 11 ); NEXT; // A0 += -A7 - A11 + SUB( 8 ); SUB( 12 ); NEXT; // A1 += -A8 - A12 + SUB( 9 ); SUB( 13 ); NEXT; // A2 += -A9 - A13 + SUB( 10 ); ADD( 7 ); ADD( 11 ); NEXT; // A3 += -A10 + A7 + A11 + SUB( 11 ); ADD( 8 ); ADD( 12 ); NEXT; // A4 += -A11 + A8 + A12 + SUB( 12 ); ADD( 9 ); ADD( 13 ); NEXT; // A5 += -A12 + A9 + A13 + SUB( 13 ); ADD( 10 ); LAST; // A6 += -A13 + A10 + +cleanup: + return( ret ); +} +#endif /* POLARSSL_ECP_DP_SECP224R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) +/* + * Fast quasi-reduction modulo p256 (FIPS 186-3 D.2.3) + */ +static int ecp_mod_p256( mpi *N ) +{ + INIT( 256 ); + + ADD( 8 ); ADD( 9 ); + SUB( 11 ); SUB( 12 ); SUB( 13 ); SUB( 14 ); NEXT; // A0 + + ADD( 9 ); ADD( 10 ); + SUB( 12 ); SUB( 13 ); SUB( 14 ); SUB( 15 ); NEXT; // A1 + + ADD( 10 ); ADD( 11 ); + SUB( 13 ); SUB( 14 ); SUB( 15 ); NEXT; // A2 + + ADD( 11 ); ADD( 11 ); ADD( 12 ); ADD( 12 ); ADD( 13 ); + SUB( 15 ); SUB( 8 ); SUB( 9 ); NEXT; // A3 + + ADD( 12 ); ADD( 12 ); ADD( 13 ); ADD( 13 ); ADD( 14 ); + SUB( 9 ); SUB( 10 ); NEXT; // A4 + + ADD( 13 ); ADD( 13 ); ADD( 14 ); ADD( 14 ); ADD( 15 ); + SUB( 10 ); SUB( 11 ); NEXT; // A5 + + ADD( 14 ); ADD( 14 ); ADD( 15 ); ADD( 15 ); ADD( 14 ); ADD( 13 ); + SUB( 8 ); SUB( 9 ); NEXT; // A6 + + ADD( 15 ); ADD( 15 ); ADD( 15 ); ADD( 8 ); + SUB( 10 ); SUB( 11 ); SUB( 12 ); SUB( 13 ); LAST; // A7 + +cleanup: + return( ret ); +} +#endif /* POLARSSL_ECP_DP_SECP256R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) +/* + * Fast quasi-reduction modulo p384 (FIPS 186-3 D.2.4) + */ +static int ecp_mod_p384( mpi *N ) +{ + INIT( 384 ); + + ADD( 12 ); ADD( 21 ); ADD( 20 ); + SUB( 23 ); NEXT; // A0 + + ADD( 13 ); ADD( 22 ); ADD( 23 ); + SUB( 12 ); SUB( 20 ); NEXT; // A2 + + ADD( 14 ); ADD( 23 ); + SUB( 13 ); SUB( 21 ); NEXT; // A2 + + ADD( 15 ); ADD( 12 ); ADD( 20 ); ADD( 21 ); + SUB( 14 ); SUB( 22 ); SUB( 23 ); NEXT; // A3 + + ADD( 21 ); ADD( 21 ); ADD( 16 ); ADD( 13 ); ADD( 12 ); ADD( 20 ); ADD( 22 ); + SUB( 15 ); SUB( 23 ); SUB( 23 ); NEXT; // A4 + + ADD( 22 ); ADD( 22 ); ADD( 17 ); ADD( 14 ); ADD( 13 ); ADD( 21 ); ADD( 23 ); + SUB( 16 ); NEXT; // A5 + + ADD( 23 ); ADD( 23 ); ADD( 18 ); ADD( 15 ); ADD( 14 ); ADD( 22 ); + SUB( 17 ); NEXT; // A6 + + ADD( 19 ); ADD( 16 ); ADD( 15 ); ADD( 23 ); + SUB( 18 ); NEXT; // A7 + + ADD( 20 ); ADD( 17 ); ADD( 16 ); + SUB( 19 ); NEXT; // A8 + + ADD( 21 ); ADD( 18 ); ADD( 17 ); + SUB( 20 ); NEXT; // A9 + + ADD( 22 ); ADD( 19 ); ADD( 18 ); + SUB( 21 ); NEXT; // A10 + + ADD( 23 ); ADD( 20 ); ADD( 19 ); + SUB( 22 ); LAST; // A11 + +cleanup: + return( ret ); +} +#endif /* POLARSSL_ECP_DP_SECP384R1_ENABLED */ + +#undef A +#undef LOAD32 +#undef STORE32 +#undef MAX32 +#undef INIT +#undef NEXT +#undef LAST + +#endif /* POLARSSL_ECP_DP_SECP224R1_ENABLED || + POLARSSL_ECP_DP_SECP256R1_ENABLED || + POLARSSL_ECP_DP_SECP384R1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) +/* + * Here we have an actual Mersenne prime, so things are more straightforward. + * However, chunks are aligned on a 'weird' boundary (521 bits). + */ + +/* Size of p521 in terms of t_uint */ +#define P521_WIDTH ( 521 / 8 / sizeof( t_uint ) + 1 ) + +/* Bits to keep in the most significant t_uint */ +#if defined(POLARSSL_HAVE_INT8) +#define P521_MASK 0x01 +#else +#define P521_MASK 0x01FF +#endif + +/* + * Fast quasi-reduction modulo p521 (FIPS 186-3 D.2.5) + * Write N as A1 + 2^521 A0, return A0 + A1 + */ +static int ecp_mod_p521( mpi *N ) +{ + int ret; + size_t i; + mpi M; + t_uint Mp[P521_WIDTH + 1]; + /* Worst case for the size of M is when t_uint is 16 bits: + * we need to hold bits 513 to 1056, which is 34 limbs, that is + * P521_WIDTH + 1. Otherwise P521_WIDTH is enough. */ + + if( N->n < P521_WIDTH ) + return( 0 ); + + /* M = A1 */ + M.s = 1; + M.n = N->n - ( P521_WIDTH - 1 ); + if( M.n > P521_WIDTH + 1 ) + M.n = P521_WIDTH + 1; + M.p = Mp; + memcpy( Mp, N->p + P521_WIDTH - 1, M.n * sizeof( t_uint ) ); + MPI_CHK( mpi_shift_r( &M, 521 % ( 8 * sizeof( t_uint ) ) ) ); + + /* N = A0 */ + N->p[P521_WIDTH - 1] &= P521_MASK; + for( i = P521_WIDTH; i < N->n; i++ ) + N->p[i] = 0; + + /* N = A0 + A1 */ + MPI_CHK( mpi_add_abs( N, N, &M ) ); + +cleanup: + return( ret ); +} + +#undef P521_WIDTH +#undef P521_MASK +#endif /* POLARSSL_ECP_DP_SECP521R1_ENABLED */ + +#endif /* POLARSSL_ECP_NIST_OPTIM */ + +#if defined(POLARSSL_ECP_DP_M255_ENABLED) + +/* Size of p255 in terms of t_uint */ +#define P255_WIDTH ( 255 / 8 / sizeof( t_uint ) + 1 ) + +/* + * Fast quasi-reduction modulo p255 = 2^255 - 19 + * Write N as A0 + 2^255 A1, return A0 + 19 * A1 + */ +static int ecp_mod_p255( mpi *N ) +{ + int ret; + size_t i; + mpi M; + t_uint Mp[P255_WIDTH + 2]; + + if( N->n < P255_WIDTH ) + return( 0 ); + + /* M = A1 */ + M.s = 1; + M.n = N->n - ( P255_WIDTH - 1 ); + if( M.n > P255_WIDTH + 1 ) + M.n = P255_WIDTH + 1; + M.p = Mp; + memset( Mp, 0, sizeof Mp ); + memcpy( Mp, N->p + P255_WIDTH - 1, M.n * sizeof( t_uint ) ); + MPI_CHK( mpi_shift_r( &M, 255 % ( 8 * sizeof( t_uint ) ) ) ); + M.n++; /* Make room for multiplication by 19 */ + + /* N = A0 */ + MPI_CHK( mpi_set_bit( N, 255, 0 ) ); + for( i = P255_WIDTH; i < N->n; i++ ) + N->p[i] = 0; + + /* N = A0 + 19 * A1 */ + MPI_CHK( mpi_mul_int( &M, &M, 19 ) ); + MPI_CHK( mpi_add_abs( N, N, &M ) ); + +cleanup: + return( ret ); +} +#endif /* POLARSSL_ECP_DP_M255_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) || \ + defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) +/* + * Fast quasi-reduction modulo P = 2^s - R, + * with R about 33 bits, used by the Koblitz curves. + * + * Write N as A0 + 2^224 A1, return A0 + R * A1. + * Actually do two passes, since R is big. + */ +#define P_KOBLITZ_MAX ( 256 / 8 / sizeof( t_uint ) ) // Max limbs in P +#define P_KOBLITZ_R ( 8 / sizeof( t_uint ) ) // Limbs in R +static inline int ecp_mod_koblitz( mpi *N, t_uint *Rp, size_t p_limbs, + size_t adjust, size_t shift, t_uint mask ) +{ + int ret; + size_t i; + mpi M, R; + t_uint Mp[P_KOBLITZ_MAX + P_KOBLITZ_R]; + + if( N->n < p_limbs ) + return( 0 ); + + /* Init R */ + R.s = 1; + R.p = Rp; + R.n = P_KOBLITZ_R; + + /* Common setup for M */ + M.s = 1; + M.p = Mp; + + /* M = A1 */ + M.n = N->n - ( p_limbs - adjust ); + if( M.n > p_limbs + adjust ) + M.n = p_limbs + adjust; + memset( Mp, 0, sizeof Mp ); + memcpy( Mp, N->p + p_limbs - adjust, M.n * sizeof( t_uint ) ); + if( shift != 0 ) + MPI_CHK( mpi_shift_r( &M, shift ) ); + M.n += R.n - adjust; /* Make room for multiplication by R */ + + /* N = A0 */ + if( mask != 0 ) + N->p[p_limbs - 1] &= mask; + for( i = p_limbs; i < N->n; i++ ) + N->p[i] = 0; + + /* N = A0 + R * A1 */ + MPI_CHK( mpi_mul_mpi( &M, &M, &R ) ); + MPI_CHK( mpi_add_abs( N, N, &M ) ); + + /* Second pass */ + + /* M = A1 */ + M.n = N->n - ( p_limbs - adjust ); + if( M.n > p_limbs + adjust ) + M.n = p_limbs + adjust; + memset( Mp, 0, sizeof Mp ); + memcpy( Mp, N->p + p_limbs - adjust, M.n * sizeof( t_uint ) ); + if( shift != 0 ) + MPI_CHK( mpi_shift_r( &M, shift ) ); + M.n += R.n - adjust; /* Make room for multiplication by R */ + + /* N = A0 */ + if( mask != 0 ) + N->p[p_limbs - 1] &= mask; + for( i = p_limbs; i < N->n; i++ ) + N->p[i] = 0; + + /* N = A0 + R * A1 */ + MPI_CHK( mpi_mul_mpi( &M, &M, &R ) ); + MPI_CHK( mpi_add_abs( N, N, &M ) ); + +cleanup: + return( ret ); +} +#endif /* POLARSSL_ECP_DP_SECP192K1_ENABLED) || + POLARSSL_ECP_DP_SECP224K1_ENABLED) || + POLARSSL_ECP_DP_SECP256K1_ENABLED) */ + +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) +/* + * Fast quasi-reduction modulo p192k1 = 2^192 - R, + * with R = 2^32 + 2^12 + 2^8 + 2^7 + 2^6 + 2^3 + 1 = 0x0100001119 + */ +static int ecp_mod_p192k1( mpi *N ) +{ + static t_uint Rp[] = { + BYTES_TO_T_UINT_8( 0xC9, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 ) }; + + return( ecp_mod_koblitz( N, Rp, 192 / 8 / sizeof( t_uint ), 0, 0, 0 ) ); +} +#endif /* POLARSSL_ECP_DP_SECP192K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) +/* + * Fast quasi-reduction modulo p224k1 = 2^224 - R, + * with R = 2^32 + 2^12 + 2^11 + 2^9 + 2^7 + 2^4 + 2 + 1 = 0x0100001A93 + */ +static int ecp_mod_p224k1( mpi *N ) +{ + static t_uint Rp[] = { + BYTES_TO_T_UINT_8( 0x93, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 ) }; + +#if defined(POLARSSL_HAVE_INT64) + return( ecp_mod_koblitz( N, Rp, 4, 1, 32, 0xFFFFFFFF ) ); +#else + return( ecp_mod_koblitz( N, Rp, 224 / 8 / sizeof( t_uint ), 0, 0, 0 ) ); +#endif +} + +#endif /* POLARSSL_ECP_DP_SECP224K1_ENABLED */ + +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) +/* + * Fast quasi-reduction modulo p256k1 = 2^256 - R, + * with R = 2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1 = 0x01000003D1 + */ +static int ecp_mod_p256k1( mpi *N ) +{ + static t_uint Rp[] = { + BYTES_TO_T_UINT_8( 0xD1, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 ) }; + return( ecp_mod_koblitz( N, Rp, 256 / 8 / sizeof( t_uint ), 0, 0, 0 ) ); +} +#endif /* POLARSSL_ECP_DP_SECP256K1_ENABLED */ + +#endif /* POLARSSL_ECP_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/entropy.c b/component/common/network/ssl/polarssl-1.3.8/library/entropy.c new file mode 100644 index 0000000..bc7fb0f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/entropy.c @@ -0,0 +1,477 @@ +/* + * Entropy accumulator implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ENTROPY_C) + +#include "polarssl/entropy.h" +#include "polarssl/entropy_poll.h" + +#if defined(POLARSSL_FS_IO) +#include +#endif + +#if defined(POLARSSL_HAVEGE_C) +#include "polarssl/havege.h" +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#define ENTROPY_MAX_LOOP 256 /**< Maximum amount to loop before error */ + +void entropy_init( entropy_context *ctx ) +{ + memset( ctx, 0, sizeof(entropy_context) ); + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_init( &ctx->mutex ); +#endif + +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) + sha512_starts( &ctx->accumulator, 0 ); +#else + sha256_starts( &ctx->accumulator, 0 ); +#endif +#if defined(POLARSSL_HAVEGE_C) + havege_init( &ctx->havege_data ); +#endif + +#if !defined(POLARSSL_NO_DEFAULT_ENTROPY_SOURCES) +#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) + entropy_add_source( ctx, platform_entropy_poll, NULL, + ENTROPY_MIN_PLATFORM ); +#endif +#if defined(POLARSSL_TIMING_C) + entropy_add_source( ctx, hardclock_poll, NULL, ENTROPY_MIN_HARDCLOCK ); +#endif +#if defined(POLARSSL_HAVEGE_C) + entropy_add_source( ctx, havege_poll, &ctx->havege_data, + ENTROPY_MIN_HAVEGE ); +#endif +#endif /* POLARSSL_NO_DEFAULT_ENTROPY_SOURCES */ +} + +void entropy_free( entropy_context *ctx ) +{ +#if defined(POLARSSL_HAVEGE_C) + havege_free( &ctx->havege_data ); +#endif + polarssl_zeroize( ctx, sizeof( entropy_context ) ); +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_free( &ctx->mutex ); +#endif +} + +int entropy_add_source( entropy_context *ctx, + f_source_ptr f_source, void *p_source, + size_t threshold ) +{ + int index, ret = 0; + +#if defined(POLARSSL_THREADING_C) + if( ( ret = polarssl_mutex_lock( &ctx->mutex ) ) != 0 ) + return( ret ); +#endif + + index = ctx->source_count; + if( index >= ENTROPY_MAX_SOURCES ) + { + ret = POLARSSL_ERR_ENTROPY_MAX_SOURCES; + goto exit; + } + + ctx->source[index].f_source = f_source; + ctx->source[index].p_source = p_source; + ctx->source[index].threshold = threshold; + + ctx->source_count++; + +exit: +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &ctx->mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); +#endif + + return( ret ); +} + +/* + * Entropy accumulator update + */ +static int entropy_update( entropy_context *ctx, unsigned char source_id, + const unsigned char *data, size_t len ) +{ + unsigned char header[2]; + unsigned char tmp[ENTROPY_BLOCK_SIZE]; + size_t use_len = len; + const unsigned char *p = data; + + if( use_len > ENTROPY_BLOCK_SIZE ) + { +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) + sha512( data, len, tmp, 0 ); +#else + sha256( data, len, tmp, 0 ); +#endif + p = tmp; + use_len = ENTROPY_BLOCK_SIZE; + } + + header[0] = source_id; + header[1] = use_len & 0xFF; + +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) + sha512_update( &ctx->accumulator, header, 2 ); + sha512_update( &ctx->accumulator, p, use_len ); +#else + sha256_update( &ctx->accumulator, header, 2 ); + sha256_update( &ctx->accumulator, p, use_len ); +#endif + + return( 0 ); +} + +int entropy_update_manual( entropy_context *ctx, + const unsigned char *data, size_t len ) +{ + int ret; + +#if defined(POLARSSL_THREADING_C) + if( ( ret = polarssl_mutex_lock( &ctx->mutex ) ) != 0 ) + return( ret ); +#endif + + ret = entropy_update( ctx, ENTROPY_SOURCE_MANUAL, data, len ); + +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &ctx->mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); +#endif + + return( ret ); +} + +/* + * Run through the different sources to add entropy to our accumulator + */ +static int entropy_gather_internal( entropy_context *ctx ) +{ + int ret, i; + unsigned char buf[ENTROPY_MAX_GATHER]; + size_t olen; + + if( ctx->source_count == 0 ) + return( POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED ); + + /* + * Run through our entropy sources + */ + for( i = 0; i < ctx->source_count; i++ ) + { + olen = 0; + if( ( ret = ctx->source[i].f_source( ctx->source[i].p_source, + buf, ENTROPY_MAX_GATHER, &olen ) ) != 0 ) + { + return( ret ); + } + + /* + * Add if we actually gathered something + */ + if( olen > 0 ) + { + entropy_update( ctx, (unsigned char) i, buf, olen ); + ctx->source[i].size += olen; + } + } + + return( 0 ); +} + +/* + * Thread-safe wrapper for entropy_gather_internal() + */ +int entropy_gather( entropy_context *ctx ) +{ + int ret; + +#if defined(POLARSSL_THREADING_C) + if( ( ret = polarssl_mutex_lock( &ctx->mutex ) ) != 0 ) + return( ret ); +#endif + + ret = entropy_gather_internal( ctx ); + +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &ctx->mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); +#endif + + return( ret ); +} + +int entropy_func( void *data, unsigned char *output, size_t len ) +{ + int ret, count = 0, i, reached; + entropy_context *ctx = (entropy_context *) data; + unsigned char buf[ENTROPY_BLOCK_SIZE]; + + if( len > ENTROPY_BLOCK_SIZE ) + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + +#if defined(POLARSSL_THREADING_C) + if( ( ret = polarssl_mutex_lock( &ctx->mutex ) ) != 0 ) + return( ret ); +#endif + + /* + * Always gather extra entropy before a call + */ + do + { + if( count++ > ENTROPY_MAX_LOOP ) + { + ret = POLARSSL_ERR_ENTROPY_SOURCE_FAILED; + goto exit; + } + + if( ( ret = entropy_gather_internal( ctx ) ) != 0 ) + goto exit; + + reached = 0; + + for( i = 0; i < ctx->source_count; i++ ) + if( ctx->source[i].size >= ctx->source[i].threshold ) + reached++; + } + while( reached != ctx->source_count ); + + memset( buf, 0, ENTROPY_BLOCK_SIZE ); + +#if defined(POLARSSL_ENTROPY_SHA512_ACCUMULATOR) + sha512_finish( &ctx->accumulator, buf ); + + /* + * Reset accumulator and counters and recycle existing entropy + */ + memset( &ctx->accumulator, 0, sizeof( sha512_context ) ); + sha512_starts( &ctx->accumulator, 0 ); + sha512_update( &ctx->accumulator, buf, ENTROPY_BLOCK_SIZE ); + + /* + * Perform second SHA-512 on entropy + */ + sha512( buf, ENTROPY_BLOCK_SIZE, buf, 0 ); +#else /* POLARSSL_ENTROPY_SHA512_ACCUMULATOR */ + sha256_finish( &ctx->accumulator, buf ); + + /* + * Reset accumulator and counters and recycle existing entropy + */ + memset( &ctx->accumulator, 0, sizeof( sha256_context ) ); + sha256_starts( &ctx->accumulator, 0 ); + sha256_update( &ctx->accumulator, buf, ENTROPY_BLOCK_SIZE ); + + /* + * Perform second SHA-256 on entropy + */ + sha256( buf, ENTROPY_BLOCK_SIZE, buf, 0 ); +#endif /* POLARSSL_ENTROPY_SHA512_ACCUMULATOR */ + + for( i = 0; i < ctx->source_count; i++ ) + ctx->source[i].size = 0; + + memcpy( output, buf, len ); + + ret = 0; + +exit: +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &ctx->mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); +#endif + + return( ret ); +} + +#if defined(POLARSSL_FS_IO) +int entropy_write_seed_file( entropy_context *ctx, const char *path ) +{ + int ret = POLARSSL_ERR_ENTROPY_FILE_IO_ERROR; + FILE *f; + unsigned char buf[ENTROPY_BLOCK_SIZE]; + + if( ( f = fopen( path, "wb" ) ) == NULL ) + return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR ); + + if( ( ret = entropy_func( ctx, buf, ENTROPY_BLOCK_SIZE ) ) != 0 ) + goto exit; + + if( fwrite( buf, 1, ENTROPY_BLOCK_SIZE, f ) != ENTROPY_BLOCK_SIZE ) + { + ret = POLARSSL_ERR_ENTROPY_FILE_IO_ERROR; + goto exit; + } + + ret = 0; + +exit: + fclose( f ); + return( ret ); +} + +int entropy_update_seed_file( entropy_context *ctx, const char *path ) +{ + FILE *f; + size_t n; + unsigned char buf[ ENTROPY_MAX_SEED_SIZE ]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + n = (size_t) ftell( f ); + fseek( f, 0, SEEK_SET ); + + if( n > ENTROPY_MAX_SEED_SIZE ) + n = ENTROPY_MAX_SEED_SIZE; + + if( fread( buf, 1, n, f ) != n ) + { + fclose( f ); + return( POLARSSL_ERR_ENTROPY_FILE_IO_ERROR ); + } + + fclose( f ); + + entropy_update_manual( ctx, buf, n ); + + return( entropy_write_seed_file( ctx, path ) ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(POLARSSL_SELF_TEST) + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_printf printf +#endif + +/* + * Dummy source function + */ +static int entropy_dummy_source( void *data, unsigned char *output, + size_t len, size_t *olen ) +{ + ((void) data); + + memset( output, 0x2a, len ); + *olen = len; + + return( 0 ); +} + +/* + * The actual entropy quality is hard to test, but we can at least + * test that the functions don't cause errors and write the correct + * amount of data to buffers. + */ +int entropy_self_test( int verbose ) +{ + int ret = 0; + entropy_context ctx; + unsigned char buf[ENTROPY_BLOCK_SIZE] = { 0 }; + unsigned char acc[ENTROPY_BLOCK_SIZE] = { 0 }; + size_t i, j; + + if( verbose != 0 ) + polarssl_printf( " ENTROPY test: " ); + + entropy_init( &ctx ); + + ret = entropy_add_source( &ctx, entropy_dummy_source, NULL, 16 ); + if( ret != 0 ) + goto cleanup; + + if( ( ret = entropy_gather( &ctx ) ) != 0 ) + goto cleanup; + + if( ( ret = entropy_update_manual( &ctx, buf, sizeof buf ) ) != 0 ) + goto cleanup; + + /* + * To test that entropy_func writes correct number of bytes: + * - use the whole buffer and rely on ASan to detect overruns + * - collect entropy 8 times and OR the result in an accumulator: + * any byte should then be 0 with probably 2^(-64), so requiring + * each of the 32 or 64 bytes to be non-zero has a false failure rate + * of at most 2^(-58) which is acceptable. + */ + for( i = 0; i < 8; i++ ) + { + if( ( ret = entropy_func( &ctx, buf, sizeof( buf ) ) ) != 0 ) + goto cleanup; + + for( j = 0; j < sizeof( buf ); j++ ) + acc[j] |= buf[j]; + } + + for( j = 0; j < sizeof( buf ); j++ ) + { + if( acc[j] == 0 ) + { + ret = 1; + goto cleanup; + } + } + +cleanup: + entropy_free( &ctx ); + + if( verbose != 0 ) + { + if( ret != 0 ) + polarssl_printf( "failed\n" ); + else + polarssl_printf( "passed\n" ); + + polarssl_printf( "\n" ); + } + + return( ret != 0 ); +} +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_ENTROPY_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/entropy_poll.c b/component/common/network/ssl/polarssl-1.3.8/library/entropy_poll.c new file mode 100644 index 0000000..9ca9e95 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/entropy_poll.c @@ -0,0 +1,140 @@ +/* + * Platform-specific and custom entropy polling functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ENTROPY_C) + +#include "polarssl/entropy.h" +#include "polarssl/entropy_poll.h" + +#if defined(POLARSSL_TIMING_C) +#include "polarssl/timing.h" +#endif +#if defined(POLARSSL_HAVEGE_C) +#include "polarssl/havege.h" +#endif + +#if !defined(POLARSSL_NO_PLATFORM_ENTROPY) +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0400 +#endif +#include +#include + +int platform_entropy_poll( void *data, unsigned char *output, size_t len, + size_t *olen ) +{ + HCRYPTPROV provider; + ((void) data); + *olen = 0; + + if( CryptAcquireContext( &provider, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE ) + { + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + } + + if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE ) + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + + CryptReleaseContext( provider, 0 ); + *olen = len; + + return( 0 ); +} +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +#include + +int platform_entropy_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + FILE *file; + size_t ret; + ((void) data); + + *olen = 0; + + file = fopen( "/dev/urandom", "rb" ); + if( file == NULL ) + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + + ret = fread( output, 1, len, file ); + if( ret != len ) + { + fclose( file ); + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + } + + fclose( file ); + *olen = len; + + return( 0 ); +} +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +#endif /* !POLARSSL_NO_PLATFORM_ENTROPY */ + +#if defined(POLARSSL_TIMING_C) +int hardclock_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + unsigned long timer = hardclock(); + ((void) data); + *olen = 0; + + if( len < sizeof(unsigned long) ) + return( 0 ); + + memcpy( output, &timer, sizeof(unsigned long) ); + *olen = sizeof(unsigned long); + + return( 0 ); +} +#endif /* POLARSSL_TIMING_C */ + +#if defined(POLARSSL_HAVEGE_C) +int havege_poll( void *data, + unsigned char *output, size_t len, size_t *olen ) +{ + havege_state *hs = (havege_state *) data; + *olen = 0; + + if( havege_random( hs, output, len ) != 0 ) + return( POLARSSL_ERR_ENTROPY_SOURCE_FAILED ); + + *olen = len; + + return( 0 ); +} +#endif /* POLARSSL_HAVEGE_C */ + +#endif /* POLARSSL_ENTROPY_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/error.c b/component/common/network/ssl/polarssl-1.3.8/library/error.c new file mode 100644 index 0000000..22234cf --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/error.c @@ -0,0 +1,769 @@ +/* + * Error message information + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_ERROR_C) || defined(POLARSSL_ERROR_STRERROR_DUMMY) +#include "polarssl/error.h" +#endif + +#if defined(POLARSSL_ERROR_C) + +#if defined(POLARSSL_AES_C) +#include "polarssl/aes.h" +#endif + +#if defined(POLARSSL_BASE64_C) +#include "polarssl/base64.h" +#endif + +#if defined(POLARSSL_BIGNUM_C) +#include "polarssl/bignum.h" +#endif + +#if defined(POLARSSL_BLOWFISH_C) +#include "polarssl/blowfish.h" +#endif + +#if defined(POLARSSL_CAMELLIA_C) +#include "polarssl/camellia.h" +#endif + +#if defined(POLARSSL_CCM_C) +#include "polarssl/ccm.h" +#endif + +#if defined(POLARSSL_CIPHER_C) +#include "polarssl/cipher.h" +#endif + +#if defined(POLARSSL_CTR_DRBG_C) +#include "polarssl/ctr_drbg.h" +#endif + +#if defined(POLARSSL_DES_C) +#include "polarssl/des.h" +#endif + +#if defined(POLARSSL_DHM_C) +#include "polarssl/dhm.h" +#endif + +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif + +#if defined(POLARSSL_ENTROPY_C) +#include "polarssl/entropy.h" +#endif + +#if defined(POLARSSL_GCM_C) +#include "polarssl/gcm.h" +#endif + +#if defined(POLARSSL_HMAC_DRBG_C) +#include "polarssl/hmac_drbg.h" +#endif + +#if defined(POLARSSL_MD_C) +#include "polarssl/md.h" +#endif + +#if defined(POLARSSL_MD2_C) +#include "polarssl/md2.h" +#endif + +#if defined(POLARSSL_MD4_C) +#include "polarssl/md4.h" +#endif + +#if defined(POLARSSL_MD5_C) +#include "polarssl/md5.h" +#endif + +#if defined(POLARSSL_NET_C) +#include "polarssl/net.h" +#endif + +#if defined(POLARSSL_OID_C) +#include "polarssl/oid.h" +#endif + +#if defined(POLARSSL_PADLOCK_C) +#include "polarssl/padlock.h" +#endif + +#if defined(POLARSSL_PBKDF2_C) +#include "polarssl/pbkdf2.h" +#endif + +#if defined(POLARSSL_PEM_PARSE_C) || defined(POLARSSL_PEM_WRITE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PK_C) +#include "polarssl/pk.h" +#endif + +#if defined(POLARSSL_PKCS12_C) +#include "polarssl/pkcs12.h" +#endif + +#if defined(POLARSSL_PKCS5_C) +#include "polarssl/pkcs5.h" +#endif + +#if defined(POLARSSL_RIPEMD160_C) +#include "polarssl/ripemd160.h" +#endif + +#if defined(POLARSSL_RSA_C) +#include "polarssl/rsa.h" +#endif + +#if defined(POLARSSL_SHA1_C) +#include "polarssl/sha1.h" +#endif + +#if defined(POLARSSL_SHA256_C) +#include "polarssl/sha256.h" +#endif + +#if defined(POLARSSL_SHA512_C) +#include "polarssl/sha512.h" +#endif + +#if defined(POLARSSL_SSL_TLS_C) +#include "polarssl/ssl.h" +#endif + +#if defined(POLARSSL_THREADING_C) +#include "polarssl/threading.h" +#endif + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +#include "polarssl/x509.h" +#endif + +#if defined(POLARSSL_XTEA_C) +#include "polarssl/xtea.h" +#endif + + +#include + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#define snprintf _snprintf +#endif + +void polarssl_strerror( int ret, char *buf, size_t buflen ) +{ + size_t len; + int use_ret; + + if( buflen == 0 ) + return; + + memset( buf, 0x00, buflen ); + /* Reduce buflen to make sure MSVC _snprintf() ends with \0 as well */ + buflen -= 1; + + if( ret < 0 ) + ret = -ret; + + if( ret & 0xFF80 ) + { + use_ret = ret & 0xFF80; + + // High level error codes + // + // BEGIN generated code +#if defined(POLARSSL_CIPHER_C) + if( use_ret == -(POLARSSL_ERR_CIPHER_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "CIPHER - The selected feature is not available" ); + if( use_ret == -(POLARSSL_ERR_CIPHER_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "CIPHER - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_CIPHER_ALLOC_FAILED) ) + snprintf( buf, buflen, "CIPHER - Failed to allocate memory" ); + if( use_ret == -(POLARSSL_ERR_CIPHER_INVALID_PADDING) ) + snprintf( buf, buflen, "CIPHER - Input data contains invalid padding and is rejected" ); + if( use_ret == -(POLARSSL_ERR_CIPHER_FULL_BLOCK_EXPECTED) ) + snprintf( buf, buflen, "CIPHER - Decryption of block requires a full block" ); + if( use_ret == -(POLARSSL_ERR_CIPHER_AUTH_FAILED) ) + snprintf( buf, buflen, "CIPHER - Authentication failed (for AEAD modes)" ); +#endif /* POLARSSL_CIPHER_C */ + +#if defined(POLARSSL_DHM_C) + if( use_ret == -(POLARSSL_ERR_DHM_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "DHM - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_DHM_READ_PARAMS_FAILED) ) + snprintf( buf, buflen, "DHM - Reading of the DHM parameters failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_MAKE_PARAMS_FAILED) ) + snprintf( buf, buflen, "DHM - Making of the DHM parameters failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_READ_PUBLIC_FAILED) ) + snprintf( buf, buflen, "DHM - Reading of the public values failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_MAKE_PUBLIC_FAILED) ) + snprintf( buf, buflen, "DHM - Making of the public value failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_CALC_SECRET_FAILED) ) + snprintf( buf, buflen, "DHM - Calculation of the DHM secret failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_INVALID_FORMAT) ) + snprintf( buf, buflen, "DHM - The ASN.1 data is not formatted correctly" ); + if( use_ret == -(POLARSSL_ERR_DHM_MALLOC_FAILED) ) + snprintf( buf, buflen, "DHM - Allocation of memory failed" ); + if( use_ret == -(POLARSSL_ERR_DHM_FILE_IO_ERROR) ) + snprintf( buf, buflen, "DHM - Read/write of file failed" ); +#endif /* POLARSSL_DHM_C */ + +#if defined(POLARSSL_ECP_C) + if( use_ret == -(POLARSSL_ERR_ECP_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "ECP - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_ECP_BUFFER_TOO_SMALL) ) + snprintf( buf, buflen, "ECP - The buffer is too small to write to" ); + if( use_ret == -(POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "ECP - Requested curve not available" ); + if( use_ret == -(POLARSSL_ERR_ECP_VERIFY_FAILED) ) + snprintf( buf, buflen, "ECP - The signature is not valid" ); + if( use_ret == -(POLARSSL_ERR_ECP_MALLOC_FAILED) ) + snprintf( buf, buflen, "ECP - Memory allocation failed" ); + if( use_ret == -(POLARSSL_ERR_ECP_RANDOM_FAILED) ) + snprintf( buf, buflen, "ECP - Generation of random value, such as (ephemeral) key, failed" ); + if( use_ret == -(POLARSSL_ERR_ECP_INVALID_KEY) ) + snprintf( buf, buflen, "ECP - Invalid private or public key" ); + if( use_ret == -(POLARSSL_ERR_ECP_SIG_LEN_MISMATCH) ) + snprintf( buf, buflen, "ECP - Signature is valid but shorter than the user-supplied length" ); +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_MD_C) + if( use_ret == -(POLARSSL_ERR_MD_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "MD - The selected feature is not available" ); + if( use_ret == -(POLARSSL_ERR_MD_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "MD - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_MD_ALLOC_FAILED) ) + snprintf( buf, buflen, "MD - Failed to allocate memory" ); + if( use_ret == -(POLARSSL_ERR_MD_FILE_IO_ERROR) ) + snprintf( buf, buflen, "MD - Opening or reading of file failed" ); +#endif /* POLARSSL_MD_C */ + +#if defined(POLARSSL_PEM_PARSE_C) || defined(POLARSSL_PEM_WRITE_C) + if( use_ret == -(POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT) ) + snprintf( buf, buflen, "PEM - No PEM header or footer found" ); + if( use_ret == -(POLARSSL_ERR_PEM_INVALID_DATA) ) + snprintf( buf, buflen, "PEM - PEM string is not as expected" ); + if( use_ret == -(POLARSSL_ERR_PEM_MALLOC_FAILED) ) + snprintf( buf, buflen, "PEM - Failed to allocate memory" ); + if( use_ret == -(POLARSSL_ERR_PEM_INVALID_ENC_IV) ) + snprintf( buf, buflen, "PEM - RSA IV is not in hex-format" ); + if( use_ret == -(POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG) ) + snprintf( buf, buflen, "PEM - Unsupported key encryption algorithm" ); + if( use_ret == -(POLARSSL_ERR_PEM_PASSWORD_REQUIRED) ) + snprintf( buf, buflen, "PEM - Private key password can't be empty" ); + if( use_ret == -(POLARSSL_ERR_PEM_PASSWORD_MISMATCH) ) + snprintf( buf, buflen, "PEM - Given private key password does not allow for correct decryption" ); + if( use_ret == -(POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "PEM - Unavailable feature, e.g. hashing/encryption combination" ); + if( use_ret == -(POLARSSL_ERR_PEM_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "PEM - Bad input parameters to function" ); +#endif /* POLARSSL_PEM_PARSE_C || POLARSSL_PEM_WRITE_C */ + +#if defined(POLARSSL_PK_C) + if( use_ret == -(POLARSSL_ERR_PK_MALLOC_FAILED) ) + snprintf( buf, buflen, "PK - Memory alloation failed" ); + if( use_ret == -(POLARSSL_ERR_PK_TYPE_MISMATCH) ) + snprintf( buf, buflen, "PK - Type mismatch, eg attempt to encrypt with an ECDSA key" ); + if( use_ret == -(POLARSSL_ERR_PK_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "PK - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_PK_FILE_IO_ERROR) ) + snprintf( buf, buflen, "PK - Read/write of file failed" ); + if( use_ret == -(POLARSSL_ERR_PK_KEY_INVALID_VERSION) ) + snprintf( buf, buflen, "PK - Unsupported key version" ); + if( use_ret == -(POLARSSL_ERR_PK_KEY_INVALID_FORMAT) ) + snprintf( buf, buflen, "PK - Invalid key tag or value" ); + if( use_ret == -(POLARSSL_ERR_PK_UNKNOWN_PK_ALG) ) + snprintf( buf, buflen, "PK - Key algorithm is unsupported (only RSA and EC are supported)" ); + if( use_ret == -(POLARSSL_ERR_PK_PASSWORD_REQUIRED) ) + snprintf( buf, buflen, "PK - Private key password can't be empty" ); + if( use_ret == -(POLARSSL_ERR_PK_PASSWORD_MISMATCH) ) + snprintf( buf, buflen, "PK - Given private key password does not allow for correct decryption" ); + if( use_ret == -(POLARSSL_ERR_PK_INVALID_PUBKEY) ) + snprintf( buf, buflen, "PK - The pubkey tag or value is invalid (only RSA and EC are supported)" ); + if( use_ret == -(POLARSSL_ERR_PK_INVALID_ALG) ) + snprintf( buf, buflen, "PK - The algorithm tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_PK_UNKNOWN_NAMED_CURVE) ) + snprintf( buf, buflen, "PK - Elliptic curve is unsupported (only NIST curves are supported)" ); + if( use_ret == -(POLARSSL_ERR_PK_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "PK - Unavailable feature, e.g. RSA disabled for RSA key" ); + if( use_ret == -(POLARSSL_ERR_PK_SIG_LEN_MISMATCH) ) + snprintf( buf, buflen, "PK - The signature is valid but its length is less than expected" ); +#endif /* POLARSSL_PK_C */ + +#if defined(POLARSSL_PKCS12_C) + if( use_ret == -(POLARSSL_ERR_PKCS12_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "PKCS12 - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "PKCS12 - Feature not available, e.g. unsupported encryption scheme" ); + if( use_ret == -(POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT) ) + snprintf( buf, buflen, "PKCS12 - PBE ASN.1 data not as expected" ); + if( use_ret == -(POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH) ) + snprintf( buf, buflen, "PKCS12 - Given private key password does not allow for correct decryption" ); +#endif /* POLARSSL_PKCS12_C */ + +#if defined(POLARSSL_PKCS5_C) + if( use_ret == -(POLARSSL_ERR_PKCS5_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "PKCS5 - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_PKCS5_INVALID_FORMAT) ) + snprintf( buf, buflen, "PKCS5 - Unexpected ASN.1 data" ); + if( use_ret == -(POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "PKCS5 - Requested encryption or digest alg not available" ); + if( use_ret == -(POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH) ) + snprintf( buf, buflen, "PKCS5 - Given private key password does not allow for correct decryption" ); +#endif /* POLARSSL_PKCS5_C */ + +#if defined(POLARSSL_RSA_C) + if( use_ret == -(POLARSSL_ERR_RSA_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "RSA - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_RSA_INVALID_PADDING) ) + snprintf( buf, buflen, "RSA - Input data contains invalid padding and is rejected" ); + if( use_ret == -(POLARSSL_ERR_RSA_KEY_GEN_FAILED) ) + snprintf( buf, buflen, "RSA - Something failed during generation of a key" ); + if( use_ret == -(POLARSSL_ERR_RSA_KEY_CHECK_FAILED) ) + snprintf( buf, buflen, "RSA - Key failed to pass the libraries validity check" ); + if( use_ret == -(POLARSSL_ERR_RSA_PUBLIC_FAILED) ) + snprintf( buf, buflen, "RSA - The public key operation failed" ); + if( use_ret == -(POLARSSL_ERR_RSA_PRIVATE_FAILED) ) + snprintf( buf, buflen, "RSA - The private key operation failed" ); + if( use_ret == -(POLARSSL_ERR_RSA_VERIFY_FAILED) ) + snprintf( buf, buflen, "RSA - The PKCS#1 verification failed" ); + if( use_ret == -(POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE) ) + snprintf( buf, buflen, "RSA - The output buffer for decryption is not large enough" ); + if( use_ret == -(POLARSSL_ERR_RSA_RNG_FAILED) ) + snprintf( buf, buflen, "RSA - The random generator failed to generate non-zeros" ); +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_SSL_TLS_C) + if( use_ret == -(POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "SSL - The requested feature is not available" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "SSL - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_SSL_INVALID_MAC) ) + snprintf( buf, buflen, "SSL - Verification of the message MAC failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_INVALID_RECORD) ) + snprintf( buf, buflen, "SSL - An invalid SSL record was received" ); + if( use_ret == -(POLARSSL_ERR_SSL_CONN_EOF) ) + snprintf( buf, buflen, "SSL - The connection indicated an EOF" ); + if( use_ret == -(POLARSSL_ERR_SSL_UNKNOWN_CIPHER) ) + snprintf( buf, buflen, "SSL - An unknown cipher was received" ); + if( use_ret == -(POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN) ) + snprintf( buf, buflen, "SSL - The server has no ciphersuites in common with the client" ); + if( use_ret == -(POLARSSL_ERR_SSL_NO_RNG) ) + snprintf( buf, buflen, "SSL - No RNG was provided to the SSL module" ); + if( use_ret == -(POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE) ) + snprintf( buf, buflen, "SSL - No client certification received from the client, but required by the authentication mode" ); + if( use_ret == -(POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE) ) + snprintf( buf, buflen, "SSL - DESCRIPTION MISSING" ); + if( use_ret == -(POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED) ) + snprintf( buf, buflen, "SSL - The own certificate is not set, but needed by the server" ); + if( use_ret == -(POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED) ) + snprintf( buf, buflen, "SSL - The own private key or pre-shared key is not set, but needed" ); + if( use_ret == -(POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED) ) + snprintf( buf, buflen, "SSL - No CA Chain is set, but required to operate" ); + if( use_ret == -(POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE) ) + snprintf( buf, buflen, "SSL - An unexpected message was received from our peer" ); + if( use_ret == -(POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE) ) + { + snprintf( buf, buflen, "SSL - A fatal alert message was received from our peer" ); + return; + } + if( use_ret == -(POLARSSL_ERR_SSL_PEER_VERIFY_FAILED) ) + snprintf( buf, buflen, "SSL - Verification of our peer failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) ) + snprintf( buf, buflen, "SSL - The peer notified us that the connection is going to be closed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO) ) + snprintf( buf, buflen, "SSL - Processing of the ClientHello handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO) ) + snprintf( buf, buflen, "SSL - Processing of the ServerHello handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE) ) + snprintf( buf, buflen, "SSL - Processing of the Certificate handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST) ) + snprintf( buf, buflen, "SSL - Processing of the CertificateRequest handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE) ) + snprintf( buf, buflen, "SSL - Processing of the ServerKeyExchange handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE) ) + snprintf( buf, buflen, "SSL - Processing of the ServerHelloDone handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE) ) + snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP) ) + snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Read Public" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS) ) + snprintf( buf, buflen, "SSL - Processing of the ClientKeyExchange handshake message failed in DHM / ECDH Calculate Secret" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY) ) + snprintf( buf, buflen, "SSL - Processing of the CertificateVerify handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC) ) + snprintf( buf, buflen, "SSL - Processing of the ChangeCipherSpec handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_FINISHED) ) + snprintf( buf, buflen, "SSL - Processing of the Finished handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_MALLOC_FAILED) ) + snprintf( buf, buflen, "SSL - Memory allocation failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_HW_ACCEL_FAILED) ) + snprintf( buf, buflen, "SSL - Hardware acceleration function returned with error" ); + if( use_ret == -(POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH) ) + snprintf( buf, buflen, "SSL - Hardware acceleration function skipped / left alone data" ); + if( use_ret == -(POLARSSL_ERR_SSL_COMPRESSION_FAILED) ) + snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) ) + snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" ); + if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET) ) + snprintf( buf, buflen, "SSL - Processing of the NewSessionTicket handshake message failed" ); + if( use_ret == -(POLARSSL_ERR_SSL_SESSION_TICKET_EXPIRED) ) + snprintf( buf, buflen, "SSL - Session ticket has expired" ); + if( use_ret == -(POLARSSL_ERR_SSL_PK_TYPE_MISMATCH) ) + snprintf( buf, buflen, "SSL - Public key type mismatch (eg, asked for RSA key exchange and presented EC key)" ); + if( use_ret == -(POLARSSL_ERR_SSL_UNKNOWN_IDENTITY) ) + snprintf( buf, buflen, "SSL - Unknown identity received (eg, PSK identity)" ); + if( use_ret == -(POLARSSL_ERR_SSL_INTERNAL_ERROR) ) + snprintf( buf, buflen, "SSL - Internal error (eg, unexpected failure in lower-level module)" ); + if( use_ret == -(POLARSSL_ERR_SSL_COUNTER_WRAPPING) ) + snprintf( buf, buflen, "SSL - A counter would wrap (eg, too many messages exchanged)" ); +#endif /* POLARSSL_SSL_TLS_C */ + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) + if( use_ret == -(POLARSSL_ERR_X509_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "X509 - Unavailable feature, e.g. RSA hashing/encryption combination" ); + if( use_ret == -(POLARSSL_ERR_X509_UNKNOWN_OID) ) + snprintf( buf, buflen, "X509 - Requested OID is unknown" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_FORMAT) ) + snprintf( buf, buflen, "X509 - The CRT/CRL/CSR format is invalid, e.g. different type expected" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_VERSION) ) + snprintf( buf, buflen, "X509 - The CRT/CRL/CSR version element is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_SERIAL) ) + snprintf( buf, buflen, "X509 - The serial tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_ALG) ) + snprintf( buf, buflen, "X509 - The algorithm tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_NAME) ) + snprintf( buf, buflen, "X509 - The name tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_DATE) ) + snprintf( buf, buflen, "X509 - The date tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_SIGNATURE) ) + snprintf( buf, buflen, "X509 - The signature tag or value invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_INVALID_EXTENSIONS) ) + snprintf( buf, buflen, "X509 - The extension tag or value is invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_UNKNOWN_VERSION) ) + snprintf( buf, buflen, "X509 - CRT/CRL/CSR has an unsupported version number" ); + if( use_ret == -(POLARSSL_ERR_X509_UNKNOWN_SIG_ALG) ) + snprintf( buf, buflen, "X509 - Signature algorithm (oid) is unsupported" ); + if( use_ret == -(POLARSSL_ERR_X509_SIG_MISMATCH) ) + snprintf( buf, buflen, "X509 - Signature algorithms do not match. (see \\c ::x509_crt sig_oid)" ); + if( use_ret == -(POLARSSL_ERR_X509_CERT_VERIFY_FAILED) ) + snprintf( buf, buflen, "X509 - Certificate verification failed, e.g. CRL, CA or signature check failed" ); + if( use_ret == -(POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT) ) + snprintf( buf, buflen, "X509 - Format not recognized as DER or PEM" ); + if( use_ret == -(POLARSSL_ERR_X509_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "X509 - Input invalid" ); + if( use_ret == -(POLARSSL_ERR_X509_MALLOC_FAILED) ) + snprintf( buf, buflen, "X509 - Allocation of memory failed" ); + if( use_ret == -(POLARSSL_ERR_X509_FILE_IO_ERROR) ) + snprintf( buf, buflen, "X509 - Read/write of file failed" ); +#endif /* POLARSSL_X509_USE,X509_CREATE_C */ + // END generated code + + if( strlen( buf ) == 0 ) + snprintf( buf, buflen, "UNKNOWN ERROR CODE (%04X)", use_ret ); + } + + use_ret = ret & ~0xFF80; + + if( use_ret == 0 ) + return; + + // If high level code is present, make a concatenation between both + // error strings. + // + len = strlen( buf ); + + if( len > 0 ) + { + if( buflen - len < 5 ) + return; + + snprintf( buf + len, buflen - len, " : " ); + + buf += len + 3; + buflen -= len + 3; + } + + // Low level error codes + // + // BEGIN generated code +#if defined(POLARSSL_AES_C) + if( use_ret == -(POLARSSL_ERR_AES_INVALID_KEY_LENGTH) ) + snprintf( buf, buflen, "AES - Invalid key length" ); + if( use_ret == -(POLARSSL_ERR_AES_INVALID_INPUT_LENGTH) ) + snprintf( buf, buflen, "AES - Invalid data input length" ); +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_ASN1_PARSE_C) + if( use_ret == -(POLARSSL_ERR_ASN1_OUT_OF_DATA) ) + snprintf( buf, buflen, "ASN1 - Out of data when parsing an ASN1 data structure" ); + if( use_ret == -(POLARSSL_ERR_ASN1_UNEXPECTED_TAG) ) + snprintf( buf, buflen, "ASN1 - ASN1 tag was of an unexpected value" ); + if( use_ret == -(POLARSSL_ERR_ASN1_INVALID_LENGTH) ) + snprintf( buf, buflen, "ASN1 - Error when trying to determine the length or invalid length" ); + if( use_ret == -(POLARSSL_ERR_ASN1_LENGTH_MISMATCH) ) + snprintf( buf, buflen, "ASN1 - Actual length differs from expected length" ); + if( use_ret == -(POLARSSL_ERR_ASN1_INVALID_DATA) ) + snprintf( buf, buflen, "ASN1 - Data is invalid. (not used)" ); + if( use_ret == -(POLARSSL_ERR_ASN1_MALLOC_FAILED) ) + snprintf( buf, buflen, "ASN1 - Memory allocation failed" ); + if( use_ret == -(POLARSSL_ERR_ASN1_BUF_TOO_SMALL) ) + snprintf( buf, buflen, "ASN1 - Buffer too small when writing ASN.1 data structure" ); +#endif /* POLARSSL_ASN1_PARSE_C */ + +#if defined(POLARSSL_BASE64_C) + if( use_ret == -(POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL) ) + snprintf( buf, buflen, "BASE64 - Output buffer too small" ); + if( use_ret == -(POLARSSL_ERR_BASE64_INVALID_CHARACTER) ) + snprintf( buf, buflen, "BASE64 - Invalid character in input" ); +#endif /* POLARSSL_BASE64_C */ + +#if defined(POLARSSL_BIGNUM_C) + if( use_ret == -(POLARSSL_ERR_MPI_FILE_IO_ERROR) ) + snprintf( buf, buflen, "BIGNUM - An error occurred while reading from or writing to a file" ); + if( use_ret == -(POLARSSL_ERR_MPI_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "BIGNUM - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_MPI_INVALID_CHARACTER) ) + snprintf( buf, buflen, "BIGNUM - There is an invalid character in the digit string" ); + if( use_ret == -(POLARSSL_ERR_MPI_BUFFER_TOO_SMALL) ) + snprintf( buf, buflen, "BIGNUM - The buffer is too small to write to" ); + if( use_ret == -(POLARSSL_ERR_MPI_NEGATIVE_VALUE) ) + snprintf( buf, buflen, "BIGNUM - The input arguments are negative or result in illegal output" ); + if( use_ret == -(POLARSSL_ERR_MPI_DIVISION_BY_ZERO) ) + snprintf( buf, buflen, "BIGNUM - The input argument for division is zero, which is not allowed" ); + if( use_ret == -(POLARSSL_ERR_MPI_NOT_ACCEPTABLE) ) + snprintf( buf, buflen, "BIGNUM - The input arguments are not acceptable" ); + if( use_ret == -(POLARSSL_ERR_MPI_MALLOC_FAILED) ) + snprintf( buf, buflen, "BIGNUM - Memory allocation failed" ); +#endif /* POLARSSL_BIGNUM_C */ + +#if defined(POLARSSL_BLOWFISH_C) + if( use_ret == -(POLARSSL_ERR_BLOWFISH_INVALID_KEY_LENGTH) ) + snprintf( buf, buflen, "BLOWFISH - Invalid key length" ); + if( use_ret == -(POLARSSL_ERR_BLOWFISH_INVALID_INPUT_LENGTH) ) + snprintf( buf, buflen, "BLOWFISH - Invalid data input length" ); +#endif /* POLARSSL_BLOWFISH_C */ + +#if defined(POLARSSL_CAMELLIA_C) + if( use_ret == -(POLARSSL_ERR_CAMELLIA_INVALID_KEY_LENGTH) ) + snprintf( buf, buflen, "CAMELLIA - Invalid key length" ); + if( use_ret == -(POLARSSL_ERR_CAMELLIA_INVALID_INPUT_LENGTH) ) + snprintf( buf, buflen, "CAMELLIA - Invalid data input length" ); +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_CCM_C) + if( use_ret == -(POLARSSL_ERR_CCM_BAD_INPUT) ) + snprintf( buf, buflen, "CCM - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_CCM_AUTH_FAILED) ) + snprintf( buf, buflen, "CCM - Authenticated decryption failed" ); +#endif /* POLARSSL_CCM_C */ + +#if defined(POLARSSL_CTR_DRBG_C) + if( use_ret == -(POLARSSL_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED) ) + snprintf( buf, buflen, "CTR_DRBG - The entropy source failed" ); + if( use_ret == -(POLARSSL_ERR_CTR_DRBG_REQUEST_TOO_BIG) ) + snprintf( buf, buflen, "CTR_DRBG - Too many random requested in single call" ); + if( use_ret == -(POLARSSL_ERR_CTR_DRBG_INPUT_TOO_BIG) ) + snprintf( buf, buflen, "CTR_DRBG - Input too large (Entropy + additional)" ); + if( use_ret == -(POLARSSL_ERR_CTR_DRBG_FILE_IO_ERROR) ) + snprintf( buf, buflen, "CTR_DRBG - Read/write error in file" ); +#endif /* POLARSSL_CTR_DRBG_C */ + +#if defined(POLARSSL_DES_C) + if( use_ret == -(POLARSSL_ERR_DES_INVALID_INPUT_LENGTH) ) + snprintf( buf, buflen, "DES - The data input has an invalid length" ); +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ENTROPY_C) + if( use_ret == -(POLARSSL_ERR_ENTROPY_SOURCE_FAILED) ) + snprintf( buf, buflen, "ENTROPY - Critical entropy source failure" ); + if( use_ret == -(POLARSSL_ERR_ENTROPY_MAX_SOURCES) ) + snprintf( buf, buflen, "ENTROPY - No more sources can be added" ); + if( use_ret == -(POLARSSL_ERR_ENTROPY_NO_SOURCES_DEFINED) ) + snprintf( buf, buflen, "ENTROPY - No sources have been added to poll" ); + if( use_ret == -(POLARSSL_ERR_ENTROPY_FILE_IO_ERROR) ) + snprintf( buf, buflen, "ENTROPY - Read/write error in file" ); +#endif /* POLARSSL_ENTROPY_C */ + +#if defined(POLARSSL_GCM_C) + if( use_ret == -(POLARSSL_ERR_GCM_AUTH_FAILED) ) + snprintf( buf, buflen, "GCM - Authenticated decryption failed" ); + if( use_ret == -(POLARSSL_ERR_GCM_BAD_INPUT) ) + snprintf( buf, buflen, "GCM - Bad input parameters to function" ); +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_HMAC_DRBG_C) + if( use_ret == -(POLARSSL_ERR_HMAC_DRBG_REQUEST_TOO_BIG) ) + snprintf( buf, buflen, "HMAC_DRBG - Too many random requested in single call" ); + if( use_ret == -(POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG) ) + snprintf( buf, buflen, "HMAC_DRBG - Input too large (Entropy + additional)" ); + if( use_ret == -(POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR) ) + snprintf( buf, buflen, "HMAC_DRBG - Read/write error in file" ); + if( use_ret == -(POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED) ) + snprintf( buf, buflen, "HMAC_DRBG - The entropy source failed" ); +#endif /* POLARSSL_HMAC_DRBG_C */ + +#if defined(POLARSSL_MD2_C) + if( use_ret == -(POLARSSL_ERR_MD2_FILE_IO_ERROR) ) + snprintf( buf, buflen, "MD2 - Read/write error in file" ); +#endif /* POLARSSL_MD2_C */ + +#if defined(POLARSSL_MD4_C) + if( use_ret == -(POLARSSL_ERR_MD4_FILE_IO_ERROR) ) + snprintf( buf, buflen, "MD4 - Read/write error in file" ); +#endif /* POLARSSL_MD4_C */ + +#if defined(POLARSSL_MD5_C) + if( use_ret == -(POLARSSL_ERR_MD5_FILE_IO_ERROR) ) + snprintf( buf, buflen, "MD5 - Read/write error in file" ); +#endif /* POLARSSL_MD5_C */ + +#if defined(POLARSSL_NET_C) + if( use_ret == -(POLARSSL_ERR_NET_UNKNOWN_HOST) ) + snprintf( buf, buflen, "NET - Failed to get an IP address for the given hostname" ); + if( use_ret == -(POLARSSL_ERR_NET_SOCKET_FAILED) ) + snprintf( buf, buflen, "NET - Failed to open a socket" ); + if( use_ret == -(POLARSSL_ERR_NET_CONNECT_FAILED) ) + snprintf( buf, buflen, "NET - The connection to the given server / port failed" ); + if( use_ret == -(POLARSSL_ERR_NET_BIND_FAILED) ) + snprintf( buf, buflen, "NET - Binding of the socket failed" ); + if( use_ret == -(POLARSSL_ERR_NET_LISTEN_FAILED) ) + snprintf( buf, buflen, "NET - Could not listen on the socket" ); + if( use_ret == -(POLARSSL_ERR_NET_ACCEPT_FAILED) ) + snprintf( buf, buflen, "NET - Could not accept the incoming connection" ); + if( use_ret == -(POLARSSL_ERR_NET_RECV_FAILED) ) + snprintf( buf, buflen, "NET - Reading information from the socket failed" ); + if( use_ret == -(POLARSSL_ERR_NET_SEND_FAILED) ) + snprintf( buf, buflen, "NET - Sending information through the socket failed" ); + if( use_ret == -(POLARSSL_ERR_NET_CONN_RESET) ) + snprintf( buf, buflen, "NET - Connection was reset by peer" ); + if( use_ret == -(POLARSSL_ERR_NET_WANT_READ) ) + snprintf( buf, buflen, "NET - Connection requires a read call" ); + if( use_ret == -(POLARSSL_ERR_NET_WANT_WRITE) ) + snprintf( buf, buflen, "NET - Connection requires a write call" ); +#endif /* POLARSSL_NET_C */ + +#if defined(POLARSSL_OID_C) + if( use_ret == -(POLARSSL_ERR_OID_NOT_FOUND) ) + snprintf( buf, buflen, "OID - OID is not found" ); + if( use_ret == -(POLARSSL_ERR_OID_BUF_TOO_SMALL) ) + snprintf( buf, buflen, "OID - output buffer is too small" ); +#endif /* POLARSSL_OID_C */ + +#if defined(POLARSSL_PADLOCK_C) + if( use_ret == -(POLARSSL_ERR_PADLOCK_DATA_MISALIGNED) ) + snprintf( buf, buflen, "PADLOCK - Input data should be aligned" ); +#endif /* POLARSSL_PADLOCK_C */ + +#if defined(POLARSSL_PBKDF2_C) + if( use_ret == -(POLARSSL_ERR_PBKDF2_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "PBKDF2 - Bad input parameters to function" ); +#endif /* POLARSSL_PBKDF2_C */ + +#if defined(POLARSSL_RIPEMD160_C) + if( use_ret == -(POLARSSL_ERR_RIPEMD160_FILE_IO_ERROR) ) + snprintf( buf, buflen, "RIPEMD160 - Read/write error in file" ); +#endif /* POLARSSL_RIPEMD160_C */ + +#if defined(POLARSSL_SHA1_C) + if( use_ret == -(POLARSSL_ERR_SHA1_FILE_IO_ERROR) ) + snprintf( buf, buflen, "SHA1 - Read/write error in file" ); +#endif /* POLARSSL_SHA1_C */ + +#if defined(POLARSSL_SHA256_C) + if( use_ret == -(POLARSSL_ERR_SHA256_FILE_IO_ERROR) ) + snprintf( buf, buflen, "SHA256 - Read/write error in file" ); +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + if( use_ret == -(POLARSSL_ERR_SHA512_FILE_IO_ERROR) ) + snprintf( buf, buflen, "SHA512 - Read/write error in file" ); +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_THREADING_C) + if( use_ret == -(POLARSSL_ERR_THREADING_FEATURE_UNAVAILABLE) ) + snprintf( buf, buflen, "THREADING - The selected feature is not available" ); + if( use_ret == -(POLARSSL_ERR_THREADING_BAD_INPUT_DATA) ) + snprintf( buf, buflen, "THREADING - Bad input parameters to function" ); + if( use_ret == -(POLARSSL_ERR_THREADING_MUTEX_ERROR) ) + snprintf( buf, buflen, "THREADING - Locking / unlocking / free failed with error code" ); +#endif /* POLARSSL_THREADING_C */ + +#if defined(POLARSSL_XTEA_C) + if( use_ret == -(POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH) ) + snprintf( buf, buflen, "XTEA - The data input has an invalid length" ); +#endif /* POLARSSL_XTEA_C */ + // END generated code + + if( strlen( buf ) != 0 ) + return; + + snprintf( buf, buflen, "UNKNOWN ERROR CODE (%04X)", use_ret ); +} + +#if defined(POLARSSL_ERROR_STRERROR_BC) +void error_strerror( int ret, char *buf, size_t buflen ) +{ + polarssl_strerror( ret, buf, buflen ); +} +#endif /* POLARSSL_ERROR_STRERROR_BC */ + +#else /* POLARSSL_ERROR_C */ + +#if defined(POLARSSL_ERROR_STRERROR_DUMMY) + +#include + +/* + * Provide an non-function in case POLARSSL_ERROR_C is not defined + */ +void polarssl_strerror( int ret, char *buf, size_t buflen ) +{ + ((void) ret); + + if( buflen > 0 ) + buf[0] = '\0'; +} + +#if defined(POLARSSL_ERROR_STRERROR_BC) +void error_strerror( int ret, char *buf, size_t buflen ) +{ + polarssl_strerror( ret, buf, buflen ); +} +#endif /* POLARSSL_ERROR_STRERROR_BC */ +#endif /* POLARSSL_ERROR_STRERROR_DUMMY */ + +#endif /* POLARSSL_ERROR_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/gcm.c b/component/common/network/ssl/polarssl-1.3.8/library/gcm.c new file mode 100644 index 0000000..77b1e0f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/gcm.c @@ -0,0 +1,948 @@ +/* + * NIST SP800-38D compliant GCM implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf + * + * See also: + * [MGV] http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf + * + * We use the algorithm described as Shoup's method with 4-bit tables in + * [MGV] 4.1, pp. 12-13, to enhance speed without using too much memory. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_GCM_C) + +#include "polarssl/gcm.h" + +#if defined(POLARSSL_AESNI_C) +#include "polarssl/aesni.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Precompute small multiples of H, that is set + * HH[i] || HL[i] = H times i, + * where i is seen as a field element as in [MGV], ie high-order bits + * correspond to low powers of P. The result is stored in the same way, that + * is the high-order bit of HH corresponds to P^0 and the low-order bit of HL + * corresponds to P^127. + */ +static int gcm_gen_table( gcm_context *ctx ) +{ + int ret, i, j; + uint64_t hi, lo; + uint64_t vl, vh; + unsigned char h[16]; + size_t olen = 0; + + memset( h, 0, 16 ); + if( ( ret = cipher_update( &ctx->cipher_ctx, h, 16, h, &olen ) ) != 0 ) + return( ret ); + + /* pack h as two 64-bits ints, big-endian */ + GET_UINT32_BE( hi, h, 0 ); + GET_UINT32_BE( lo, h, 4 ); + vh = (uint64_t) hi << 32 | lo; + + GET_UINT32_BE( hi, h, 8 ); + GET_UINT32_BE( lo, h, 12 ); + vl = (uint64_t) hi << 32 | lo; + + /* 8 = 1000 corresponds to 1 in GF(2^128) */ + ctx->HL[8] = vl; + ctx->HH[8] = vh; + +#if defined(POLARSSL_AESNI_C) && defined(POLARSSL_HAVE_X86_64) + /* With CLMUL support, we need only h, not the rest of the table */ + if( aesni_supports( POLARSSL_AESNI_CLMUL ) ) + return( 0 ); +#endif + + /* 0 corresponds to 0 in GF(2^128) */ + ctx->HH[0] = 0; + ctx->HL[0] = 0; + + for( i = 4; i > 0; i >>= 1 ) + { + uint32_t T = ( vl & 1 ) * 0xe1000000U; + vl = ( vh << 63 ) | ( vl >> 1 ); + vh = ( vh >> 1 ) ^ ( (uint64_t) T << 32); + + ctx->HL[i] = vl; + ctx->HH[i] = vh; + } + + for( i = 2; i < 16; i <<= 1 ) + { + uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; + vh = *HiH; + vl = *HiL; + for( j = 1; j < i; j++ ) + { + HiH[j] = vh ^ ctx->HH[j]; + HiL[j] = vl ^ ctx->HL[j]; + } + } + + return( 0 ); +} + +int gcm_init( gcm_context *ctx, cipher_id_t cipher, const unsigned char *key, + unsigned int keysize ) +{ + int ret; + const cipher_info_t *cipher_info; + + memset( ctx, 0, sizeof(gcm_context) ); + + cipher_init( &ctx->cipher_ctx ); + + cipher_info = cipher_info_from_values( cipher, keysize, POLARSSL_MODE_ECB ); + if( cipher_info == NULL ) + return( POLARSSL_ERR_GCM_BAD_INPUT ); + + if( cipher_info->block_size != 16 ) + return( POLARSSL_ERR_GCM_BAD_INPUT ); + + if( ( ret = cipher_init_ctx( &ctx->cipher_ctx, cipher_info ) ) != 0 ) + return( ret ); + + if( ( ret = cipher_setkey( &ctx->cipher_ctx, key, keysize, + POLARSSL_ENCRYPT ) ) != 0 ) + { + return( ret ); + } + + if( ( ret = gcm_gen_table( ctx ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +/* + * Shoup's method for multiplication use this table with + * last4[x] = x times P^128 + * where x and last4[x] are seen as elements of GF(2^128) as in [MGV] + */ +static const uint64_t last4[16] = +{ + 0x0000, 0x1c20, 0x3840, 0x2460, + 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, + 0x9180, 0x8da0, 0xa9c0, 0xb5e0 +}; + +/* + * Sets output to x times H using the precomputed tables. + * x and output are seen as elements of GF(2^128) as in [MGV]. + */ +static void gcm_mult( gcm_context *ctx, const unsigned char x[16], + unsigned char output[16] ) +{ + int i = 0; + unsigned char lo, hi, rem; + uint64_t zh, zl; + +#if defined(POLARSSL_AESNI_C) && defined(POLARSSL_HAVE_X86_64) + if( aesni_supports( POLARSSL_AESNI_CLMUL ) ) { + unsigned char h[16]; + + PUT_UINT32_BE( ctx->HH[8] >> 32, h, 0 ); + PUT_UINT32_BE( ctx->HH[8], h, 4 ); + PUT_UINT32_BE( ctx->HL[8] >> 32, h, 8 ); + PUT_UINT32_BE( ctx->HL[8], h, 12 ); + + aesni_gcm_mult( output, x, h ); + return; + } +#endif /* POLARSSL_AESNI_C && POLARSSL_HAVE_X86_64 */ + + lo = x[15] & 0xf; + hi = x[15] >> 4; + + zh = ctx->HH[lo]; + zl = ctx->HL[lo]; + + for( i = 15; i >= 0; i-- ) + { + lo = x[i] & 0xf; + hi = x[i] >> 4; + + if( i != 15 ) + { + rem = (unsigned char) zl & 0xf; + zl = ( zh << 60 ) | ( zl >> 4 ); + zh = ( zh >> 4 ); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[lo]; + zl ^= ctx->HL[lo]; + + } + + rem = (unsigned char) zl & 0xf; + zl = ( zh << 60 ) | ( zl >> 4 ); + zh = ( zh >> 4 ); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[hi]; + zl ^= ctx->HL[hi]; + } + + PUT_UINT32_BE( zh >> 32, output, 0 ); + PUT_UINT32_BE( zh, output, 4 ); + PUT_UINT32_BE( zl >> 32, output, 8 ); + PUT_UINT32_BE( zl, output, 12 ); +} + +int gcm_starts( gcm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len ) +{ + int ret; + unsigned char work_buf[16]; + size_t i; + const unsigned char *p; + size_t use_len, olen = 0; + + /* IV and AD are limited to 2^64 bits, so 2^61 bytes */ + if( ( (uint64_t) iv_len ) >> 61 != 0 || + ( (uint64_t) add_len ) >> 61 != 0 ) + { + return( POLARSSL_ERR_GCM_BAD_INPUT ); + } + + memset( ctx->y, 0x00, sizeof(ctx->y) ); + memset( ctx->buf, 0x00, sizeof(ctx->buf) ); + + ctx->mode = mode; + ctx->len = 0; + ctx->add_len = 0; + + if( iv_len == 12 ) + { + memcpy( ctx->y, iv, iv_len ); + ctx->y[15] = 1; + } + else + { + memset( work_buf, 0x00, 16 ); + PUT_UINT32_BE( iv_len * 8, work_buf, 12 ); + + p = iv; + while( iv_len > 0 ) + { + use_len = ( iv_len < 16 ) ? iv_len : 16; + + for( i = 0; i < use_len; i++ ) + ctx->y[i] ^= p[i]; + + gcm_mult( ctx, ctx->y, ctx->y ); + + iv_len -= use_len; + p += use_len; + } + + for( i = 0; i < 16; i++ ) + ctx->y[i] ^= work_buf[i]; + + gcm_mult( ctx, ctx->y, ctx->y ); + } + + if( ( ret = cipher_update( &ctx->cipher_ctx, ctx->y, 16, ctx->base_ectr, + &olen ) ) != 0 ) + { + return( ret ); + } + + ctx->add_len = add_len; + p = add; + while( add_len > 0 ) + { + use_len = ( add_len < 16 ) ? add_len : 16; + + for( i = 0; i < use_len; i++ ) + ctx->buf[i] ^= p[i]; + + gcm_mult( ctx, ctx->buf, ctx->buf ); + + add_len -= use_len; + p += use_len; + } + + return( 0 ); +} + +int gcm_update( gcm_context *ctx, + size_t length, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + unsigned char ectr[16]; + size_t i; + const unsigned char *p; + unsigned char *out_p = output; + size_t use_len, olen = 0; + + if( output > input && (size_t) ( output - input ) < length ) + return( POLARSSL_ERR_GCM_BAD_INPUT ); + + /* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes + * Also check for possible overflow */ + if( ctx->len + length < ctx->len || + (uint64_t) ctx->len + length > 0x03FFFFE0llu ) + { + return( POLARSSL_ERR_GCM_BAD_INPUT ); + } + + ctx->len += length; + + p = input; + while( length > 0 ) + { + use_len = ( length < 16 ) ? length : 16; + + for( i = 16; i > 12; i-- ) + if( ++ctx->y[i - 1] != 0 ) + break; + + if( ( ret = cipher_update( &ctx->cipher_ctx, ctx->y, 16, ectr, + &olen ) ) != 0 ) + { + return( ret ); + } + + for( i = 0; i < use_len; i++ ) + { + if( ctx->mode == GCM_DECRYPT ) + ctx->buf[i] ^= p[i]; + out_p[i] = ectr[i] ^ p[i]; + if( ctx->mode == GCM_ENCRYPT ) + ctx->buf[i] ^= out_p[i]; + } + + gcm_mult( ctx, ctx->buf, ctx->buf ); + + length -= use_len; + p += use_len; + out_p += use_len; + } + + return( 0 ); +} + +int gcm_finish( gcm_context *ctx, + unsigned char *tag, + size_t tag_len ) +{ + unsigned char work_buf[16]; + size_t i; + uint64_t orig_len = ctx->len * 8; + uint64_t orig_add_len = ctx->add_len * 8; + + if( tag_len > 16 || tag_len < 4 ) + return( POLARSSL_ERR_GCM_BAD_INPUT ); + + if( tag_len != 0 ) + memcpy( tag, ctx->base_ectr, tag_len ); + + if( orig_len || orig_add_len ) + { + memset( work_buf, 0x00, 16 ); + + PUT_UINT32_BE( ( orig_add_len >> 32 ), work_buf, 0 ); + PUT_UINT32_BE( ( orig_add_len ), work_buf, 4 ); + PUT_UINT32_BE( ( orig_len >> 32 ), work_buf, 8 ); + PUT_UINT32_BE( ( orig_len ), work_buf, 12 ); + + for( i = 0; i < 16; i++ ) + ctx->buf[i] ^= work_buf[i]; + + gcm_mult( ctx, ctx->buf, ctx->buf ); + + for( i = 0; i < tag_len; i++ ) + tag[i] ^= ctx->buf[i]; + } + + return( 0 ); +} + +int gcm_crypt_and_tag( gcm_context *ctx, + int mode, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *input, + unsigned char *output, + size_t tag_len, + unsigned char *tag ) +{ + int ret; + + if( ( ret = gcm_starts( ctx, mode, iv, iv_len, add, add_len ) ) != 0 ) + return( ret ); + + if( ( ret = gcm_update( ctx, length, input, output ) ) != 0 ) + return( ret ); + + if( ( ret = gcm_finish( ctx, tag, tag_len ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +int gcm_auth_decrypt( gcm_context *ctx, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *tag, + size_t tag_len, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + unsigned char check_tag[16]; + size_t i; + int diff; + + if( ( ret = gcm_crypt_and_tag( ctx, GCM_DECRYPT, length, + iv, iv_len, add, add_len, + input, output, tag_len, check_tag ) ) != 0 ) + { + return( ret ); + } + + /* Check tag in "constant-time" */ + for( diff = 0, i = 0; i < tag_len; i++ ) + diff |= tag[i] ^ check_tag[i]; + + if( diff != 0 ) + { + polarssl_zeroize( output, length ); + return( POLARSSL_ERR_GCM_AUTH_FAILED ); + } + + return( 0 ); +} + +void gcm_free( gcm_context *ctx ) +{ + cipher_free( &ctx->cipher_ctx ); + polarssl_zeroize( ctx, sizeof( gcm_context ) ); +} + +#if defined(POLARSSL_SELF_TEST) && defined(POLARSSL_AES_C) + +#include + +/* + * AES-GCM test vectors from: + * + * http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip + */ +#define MAX_TESTS 6 + +int key_index[MAX_TESTS] = + { 0, 0, 1, 1, 1, 1 }; + +unsigned char key[MAX_TESTS][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }, +}; + +size_t iv_len[MAX_TESTS] = + { 12, 12, 12, 12, 8, 60 }; + +int iv_index[MAX_TESTS] = + { 0, 0, 1, 1, 1, 2 }; + +unsigned char iv[MAX_TESTS][64] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 }, + { 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, + 0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, + 0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, + 0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28, + 0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39, + 0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54, + 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57, + 0xa6, 0x37, 0xb3, 0x9b }, +}; + +size_t add_len[MAX_TESTS] = + { 0, 0, 0, 20, 20, 20 }; + +int add_index[MAX_TESTS] = + { 0, 0, 0, 1, 1, 1 }; + +unsigned char additional[MAX_TESTS][64] = +{ + { 0x00 }, + { 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xad, 0xda, 0xd2 }, +}; + +size_t pt_len[MAX_TESTS] = + { 0, 16, 64, 60, 60, 60 }; + +int pt_index[MAX_TESTS] = + { 0, 0, 1, 1, 1, 1 }; + +unsigned char pt[MAX_TESTS][64] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }, +}; + +unsigned char ct[MAX_TESTS * 3][64] = +{ + { 0x00 }, + { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, + 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }, + { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }, + { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91 }, + { 0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a, + 0x77, 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55, + 0x69, 0x9b, 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8, + 0x37, 0x66, 0xe5, 0xf9, 0x7b, 0x6c, 0x74, 0x23, + 0x73, 0x80, 0x69, 0x00, 0xe4, 0x9f, 0x24, 0xb2, + 0x2b, 0x09, 0x75, 0x44, 0xd4, 0x89, 0x6b, 0x42, + 0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac, 0x0f, 0x07, + 0xc2, 0x3f, 0x45, 0x98 }, + { 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, + 0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94, + 0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, + 0xba, 0x26, 0x2a, 0x3c, 0xca, 0x7e, 0x2c, 0xa7, + 0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90, + 0xcc, 0xdc, 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f, + 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03, + 0x4c, 0x34, 0xae, 0xe5 }, + { 0x00 }, + { 0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, + 0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00 }, + { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2, 0x56 }, + { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10 }, + { 0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54, + 0xed, 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8, + 0xc5, 0x66, 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f, + 0x83, 0x47, 0x28, 0x0f, 0xc4, 0x50, 0x70, 0x57, + 0xfd, 0xdc, 0x29, 0xdf, 0x9a, 0x47, 0x1f, 0x75, + 0xc6, 0x65, 0x41, 0xd4, 0xd4, 0xda, 0xd1, 0xc9, + 0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b, 0x47, 0x3f, + 0xa0, 0xf0, 0x62, 0xf7 }, + { 0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c, + 0x48, 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff, + 0x1d, 0xe9, 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef, + 0x6e, 0xf7, 0xb7, 0x98, 0x28, 0x66, 0x6e, 0x45, + 0x81, 0xe7, 0x90, 0x12, 0xaf, 0x34, 0xdd, 0xd9, + 0xe2, 0xf0, 0x37, 0x58, 0x9b, 0x29, 0x2d, 0xb3, + 0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa, 0x22, 0xe7, + 0xe9, 0xb7, 0x37, 0x3b }, + { 0x00 }, + { 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, + 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18 }, + { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad }, + { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62 }, + { 0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32, + 0xae, 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb, + 0xaf, 0x1a, 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa, + 0xc5, 0x2f, 0xf7, 0xd7, 0x9b, 0xba, 0x9d, 0xe0, + 0xfe, 0xb5, 0x82, 0xd3, 0x39, 0x34, 0xa4, 0xf0, + 0x95, 0x4c, 0xc2, 0x36, 0x3b, 0xc7, 0x3f, 0x78, + 0x62, 0xac, 0x43, 0x0e, 0x64, 0xab, 0xe4, 0x99, + 0xf4, 0x7c, 0x9b, 0x1f }, + { 0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1, + 0xf7, 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20, + 0xee, 0xb2, 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19, + 0xa0, 0x58, 0xab, 0x4f, 0x6f, 0x74, 0x6b, 0xf4, + 0x0f, 0xc0, 0xc3, 0xb7, 0x80, 0xf2, 0x44, 0x45, + 0x2d, 0xa3, 0xeb, 0xf1, 0xc5, 0xd8, 0x2c, 0xde, + 0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e, 0xf8, 0x2e, + 0x44, 0xae, 0x7e, 0x3f }, +}; + +unsigned char tag[MAX_TESTS * 3][16] = +{ + { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, + 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a }, + { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, + 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }, + { 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, + 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }, + { 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, + 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 }, + { 0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85, + 0x56, 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb }, + { 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, + 0x46, 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50 }, + { 0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b, + 0xa0, 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35 }, + { 0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab, + 0x8e, 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb }, + { 0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf, + 0xb1, 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14 }, + { 0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f, + 0x37, 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c }, + { 0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24, + 0x09, 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8 }, + { 0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb, + 0xb8, 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9 }, + { 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, + 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b }, + { 0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, + 0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19 }, + { 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, + 0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c }, + { 0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68, + 0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b }, + { 0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4, + 0x5e, 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2 }, + { 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, + 0xc8, 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a }, +}; + +int gcm_self_test( int verbose ) +{ + gcm_context ctx; + unsigned char buf[64]; + unsigned char tag_buf[16]; + int i, j, ret; + cipher_id_t cipher = POLARSSL_CIPHER_ID_AES; + + for( j = 0; j < 3; j++ ) + { + int key_len = 128 + 64 * j; + + for( i = 0; i < MAX_TESTS; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " AES-GCM-%3d #%d (%s): ", + key_len, i, "enc" ); + + gcm_init( &ctx, cipher, key[key_index[i]], key_len ); + + ret = gcm_crypt_and_tag( &ctx, GCM_ENCRYPT, + pt_len[i], + iv[iv_index[i]], iv_len[i], + additional[add_index[i]], add_len[i], + pt[pt_index[i]], buf, 16, tag_buf ); + + if( ret != 0 || + memcmp( buf, ct[j * 6 + i], pt_len[i] ) != 0 || + memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + gcm_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " AES-GCM-%3d #%d (%s): ", + key_len, i, "dec" ); + + gcm_init( &ctx, cipher, key[key_index[i]], key_len ); + + ret = gcm_crypt_and_tag( &ctx, GCM_DECRYPT, + pt_len[i], + iv[iv_index[i]], iv_len[i], + additional[add_index[i]], add_len[i], + ct[j * 6 + i], buf, 16, tag_buf ); + + if( ret != 0 || + memcmp( buf, pt[pt_index[i]], pt_len[i] ) != 0 || + memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + gcm_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " AES-GCM-%3d #%d split (%s): ", + key_len, i, "enc" ); + + gcm_init( &ctx, cipher, key[key_index[i]], key_len ); + + ret = gcm_starts( &ctx, GCM_ENCRYPT, + iv[iv_index[i]], iv_len[i], + additional[add_index[i]], add_len[i] ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( pt_len[i] > 32 ) + { + size_t rest_len = pt_len[i] - 32; + ret = gcm_update( &ctx, 32, pt[pt_index[i]], buf ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + ret = gcm_update( &ctx, rest_len, pt[pt_index[i]] + 32, + buf + 32 ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + else + { + ret = gcm_update( &ctx, pt_len[i], pt[pt_index[i]], buf ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + ret = gcm_finish( &ctx, tag_buf, 16 ); + if( ret != 0 || + memcmp( buf, ct[j * 6 + i], pt_len[i] ) != 0 || + memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + gcm_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " AES-GCM-%3d #%d split (%s): ", + key_len, i, "dec" ); + + gcm_init( &ctx, cipher, key[key_index[i]], key_len ); + + ret = gcm_starts( &ctx, GCM_DECRYPT, + iv[iv_index[i]], iv_len[i], + additional[add_index[i]], add_len[i] ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( pt_len[i] > 32 ) + { + size_t rest_len = pt_len[i] - 32; + ret = gcm_update( &ctx, 32, ct[j * 6 + i], buf ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + ret = gcm_update( &ctx, rest_len, ct[j * 6 + i] + 32, + buf + 32 ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + else + { + ret = gcm_update( &ctx, pt_len[i], ct[j * 6 + i], buf ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + ret = gcm_finish( &ctx, tag_buf, 16 ); + if( ret != 0 || + memcmp( buf, pt[pt_index[i]], pt_len[i] ) != 0 || + memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + gcm_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + } + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + + + +#endif /* POLARSSL_SELF_TEST && POLARSSL_AES_C */ + +#endif /* POLARSSL_GCM_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/havege.c b/component/common/network/ssl/polarssl-1.3.8/library/havege.c new file mode 100644 index 0000000..3acd5bc --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/havege.c @@ -0,0 +1,247 @@ +/** + * \brief HAVEGE: HArdware Volatile Entropy Gathering and Expansion + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The HAVEGE RNG was designed by Andre Seznec in 2002. + * + * http://www.irisa.fr/caps/projects/hipsor/publi.php + * + * Contact: seznec(at)irisa_dot_fr - orocheco(at)irisa_dot_fr + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_HAVEGE_C) + +#include "polarssl/havege.h" +#include "polarssl/timing.h" + +#include + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* ------------------------------------------------------------------------ + * On average, one iteration accesses two 8-word blocks in the havege WALK + * table, and generates 16 words in the RES array. + * + * The data read in the WALK table is updated and permuted after each use. + * The result of the hardware clock counter read is used for this update. + * + * 25 conditional tests are present. The conditional tests are grouped in + * two nested groups of 12 conditional tests and 1 test that controls the + * permutation; on average, there should be 6 tests executed and 3 of them + * should be mispredicted. + * ------------------------------------------------------------------------ + */ + +#define SWAP(X,Y) { int *T = X; X = Y; Y = T; } + +#define TST1_ENTER if( PTEST & 1 ) { PTEST ^= 3; PTEST >>= 1; +#define TST2_ENTER if( PTEST & 1 ) { PTEST ^= 3; PTEST >>= 1; + +#define TST1_LEAVE U1++; } +#define TST2_LEAVE U2++; } + +#define ONE_ITERATION \ + \ + PTEST = PT1 >> 20; \ + \ + TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ + TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ + TST1_ENTER TST1_ENTER TST1_ENTER TST1_ENTER \ + \ + TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ + TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ + TST1_LEAVE TST1_LEAVE TST1_LEAVE TST1_LEAVE \ + \ + PTX = (PT1 >> 18) & 7; \ + PT1 &= 0x1FFF; \ + PT2 &= 0x1FFF; \ + CLK = (int) hardclock(); \ + \ + i = 0; \ + A = &WALK[PT1 ]; RES[i++] ^= *A; \ + B = &WALK[PT2 ]; RES[i++] ^= *B; \ + C = &WALK[PT1 ^ 1]; RES[i++] ^= *C; \ + D = &WALK[PT2 ^ 4]; RES[i++] ^= *D; \ + \ + IN = (*A >> (1)) ^ (*A << (31)) ^ CLK; \ + *A = (*B >> (2)) ^ (*B << (30)) ^ CLK; \ + *B = IN ^ U1; \ + *C = (*C >> (3)) ^ (*C << (29)) ^ CLK; \ + *D = (*D >> (4)) ^ (*D << (28)) ^ CLK; \ + \ + A = &WALK[PT1 ^ 2]; RES[i++] ^= *A; \ + B = &WALK[PT2 ^ 2]; RES[i++] ^= *B; \ + C = &WALK[PT1 ^ 3]; RES[i++] ^= *C; \ + D = &WALK[PT2 ^ 6]; RES[i++] ^= *D; \ + \ + if( PTEST & 1 ) SWAP( A, C ); \ + \ + IN = (*A >> (5)) ^ (*A << (27)) ^ CLK; \ + *A = (*B >> (6)) ^ (*B << (26)) ^ CLK; \ + *B = IN; CLK = (int) hardclock(); \ + *C = (*C >> (7)) ^ (*C << (25)) ^ CLK; \ + *D = (*D >> (8)) ^ (*D << (24)) ^ CLK; \ + \ + A = &WALK[PT1 ^ 4]; \ + B = &WALK[PT2 ^ 1]; \ + \ + PTEST = PT2 >> 1; \ + \ + PT2 = (RES[(i - 8) ^ PTY] ^ WALK[PT2 ^ PTY ^ 7]); \ + PT2 = ((PT2 & 0x1FFF) & (~8)) ^ ((PT1 ^ 8) & 0x8); \ + PTY = (PT2 >> 10) & 7; \ + \ + TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ + TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ + TST2_ENTER TST2_ENTER TST2_ENTER TST2_ENTER \ + \ + TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ + TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ + TST2_LEAVE TST2_LEAVE TST2_LEAVE TST2_LEAVE \ + \ + C = &WALK[PT1 ^ 5]; \ + D = &WALK[PT2 ^ 5]; \ + \ + RES[i++] ^= *A; \ + RES[i++] ^= *B; \ + RES[i++] ^= *C; \ + RES[i++] ^= *D; \ + \ + IN = (*A >> ( 9)) ^ (*A << (23)) ^ CLK; \ + *A = (*B >> (10)) ^ (*B << (22)) ^ CLK; \ + *B = IN ^ U2; \ + *C = (*C >> (11)) ^ (*C << (21)) ^ CLK; \ + *D = (*D >> (12)) ^ (*D << (20)) ^ CLK; \ + \ + A = &WALK[PT1 ^ 6]; RES[i++] ^= *A; \ + B = &WALK[PT2 ^ 3]; RES[i++] ^= *B; \ + C = &WALK[PT1 ^ 7]; RES[i++] ^= *C; \ + D = &WALK[PT2 ^ 7]; RES[i++] ^= *D; \ + \ + IN = (*A >> (13)) ^ (*A << (19)) ^ CLK; \ + *A = (*B >> (14)) ^ (*B << (18)) ^ CLK; \ + *B = IN; \ + *C = (*C >> (15)) ^ (*C << (17)) ^ CLK; \ + *D = (*D >> (16)) ^ (*D << (16)) ^ CLK; \ + \ + PT1 = ( RES[( i - 8 ) ^ PTX] ^ \ + WALK[PT1 ^ PTX ^ 7] ) & (~1); \ + PT1 ^= (PT2 ^ 0x10) & 0x10; \ + \ + for( n++, i = 0; i < 16; i++ ) \ + hs->pool[n % COLLECT_SIZE] ^= RES[i]; + +/* + * Entropy gathering function + */ +static void havege_fill( havege_state *hs ) +{ + int i, n = 0; + int U1, U2, *A, *B, *C, *D; + int PT1, PT2, *WALK, RES[16]; + int PTX, PTY, CLK, PTEST, IN; + + WALK = hs->WALK; + PT1 = hs->PT1; + PT2 = hs->PT2; + + PTX = U1 = 0; + PTY = U2 = 0; + + memset( RES, 0, sizeof( RES ) ); + + while( n < COLLECT_SIZE * 4 ) + { + ONE_ITERATION + ONE_ITERATION + ONE_ITERATION + ONE_ITERATION + } + + hs->PT1 = PT1; + hs->PT2 = PT2; + + hs->offset[0] = 0; + hs->offset[1] = COLLECT_SIZE / 2; +} + +/* + * HAVEGE initialization + */ +void havege_init( havege_state *hs ) +{ + memset( hs, 0, sizeof( havege_state ) ); + + havege_fill( hs ); +} + +void havege_free( havege_state *hs ) +{ + if( hs == NULL ) + return; + + polarssl_zeroize( hs, sizeof( havege_state ) ); +} + +/* + * HAVEGE rand function + */ +int havege_random( void *p_rng, unsigned char *buf, size_t len ) +{ + int val; + size_t use_len; + havege_state *hs = (havege_state *) p_rng; + unsigned char *p = buf; + + while( len > 0 ) + { + use_len = len; + if( use_len > sizeof(int) ) + use_len = sizeof(int); + + if( hs->offset[1] >= COLLECT_SIZE ) + havege_fill( hs ); + + val = hs->pool[hs->offset[0]++]; + val ^= hs->pool[hs->offset[1]++]; + + memcpy( p, &val, use_len ); + + len -= use_len; + p += use_len; + } + + return( 0 ); +} + +#endif /* POLARSSL_HAVEGE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/hmac_drbg.c b/component/common/network/ssl/polarssl-1.3.8/library/hmac_drbg.c new file mode 100644 index 0000000..d691be1 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/hmac_drbg.c @@ -0,0 +1,502 @@ +/* + * HMAC_DRBG implementation (NIST SP 800-90) + * + * Copyright (C) 2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * The NIST SP 800-90A DRBGs are described in the following publication. + * http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + * References below are based on rev. 1 (January 2012). + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_HMAC_DRBG_C) + +#include "polarssl/hmac_drbg.h" + +#if defined(POLARSSL_FS_IO) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * HMAC_DRBG update, using optional additional data (10.1.2.2) + */ +void hmac_drbg_update( hmac_drbg_context *ctx, + const unsigned char *additional, size_t add_len ) +{ + size_t md_len = ctx->md_ctx.md_info->size; + unsigned char rounds = ( additional != NULL && add_len != 0 ) ? 2 : 1; + unsigned char sep[1]; + unsigned char K[POLARSSL_MD_MAX_SIZE]; + + for( sep[0] = 0; sep[0] < rounds; sep[0]++ ) + { + /* Step 1 or 4 */ + md_hmac_reset( &ctx->md_ctx ); + md_hmac_update( &ctx->md_ctx, ctx->V, md_len ); + md_hmac_update( &ctx->md_ctx, sep, 1 ); + if( rounds == 2 ) + md_hmac_update( &ctx->md_ctx, additional, add_len ); + md_hmac_finish( &ctx->md_ctx, K ); + + /* Step 2 or 5 */ + md_hmac_starts( &ctx->md_ctx, K, md_len ); + md_hmac_update( &ctx->md_ctx, ctx->V, md_len ); + md_hmac_finish( &ctx->md_ctx, ctx->V ); + } +} + +/* + * Simplified HMAC_DRBG initialisation (for use with deterministic ECDSA) + */ +int hmac_drbg_init_buf( hmac_drbg_context *ctx, + const md_info_t * md_info, + const unsigned char *data, size_t data_len ) +{ + int ret; + + memset( ctx, 0, sizeof( hmac_drbg_context ) ); + + md_init( &ctx->md_ctx ); + + if( ( ret = md_init_ctx( &ctx->md_ctx, md_info ) ) != 0 ) + return( ret ); + + /* + * Set initial working state. + * Use the V memory location, which is currently all 0, to initialize the + * MD context with an all-zero key. Then set V to its initial value. + */ + md_hmac_starts( &ctx->md_ctx, ctx->V, md_info->size ); + memset( ctx->V, 0x01, md_info->size ); + + hmac_drbg_update( ctx, data, data_len ); + + return( 0 ); +} + +/* + * HMAC_DRBG reseeding: 10.1.2.4 (arabic) + 9.2 (Roman) + */ +int hmac_drbg_reseed( hmac_drbg_context *ctx, + const unsigned char *additional, size_t len ) +{ + unsigned char seed[POLARSSL_HMAC_DRBG_MAX_SEED_INPUT]; + size_t seedlen; + + /* III. Check input length */ + if( len > POLARSSL_HMAC_DRBG_MAX_INPUT || + ctx->entropy_len + len > POLARSSL_HMAC_DRBG_MAX_SEED_INPUT ) + { + return( POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG ); + } + + memset( seed, 0, POLARSSL_HMAC_DRBG_MAX_SEED_INPUT ); + + /* IV. Gather entropy_len bytes of entropy for the seed */ + if( ctx->f_entropy( ctx->p_entropy, seed, ctx->entropy_len ) != 0 ) + return( POLARSSL_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED ); + + seedlen = ctx->entropy_len; + + /* 1. Concatenate entropy and additional data if any */ + if( additional != NULL && len != 0 ) + { + memcpy( seed + seedlen, additional, len ); + seedlen += len; + } + + /* 2. Update state */ + hmac_drbg_update( ctx, seed, seedlen ); + + /* 3. Reset reseed_counter */ + ctx->reseed_counter = 1; + + /* 4. Done */ + return( 0 ); +} + +/* + * HMAC_DRBG initialisation (10.1.2.3 + 9.1) + */ +int hmac_drbg_init( hmac_drbg_context *ctx, + const md_info_t * md_info, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len ) +{ + int ret; + size_t entropy_len; + + memset( ctx, 0, sizeof( hmac_drbg_context ) ); + + md_init( &ctx->md_ctx ); + + if( ( ret = md_init_ctx( &ctx->md_ctx, md_info ) ) != 0 ) + return( ret ); + + /* + * Set initial working state. + * Use the V memory location, which is currently all 0, to initialize the + * MD context with an all-zero key. Then set V to its initial value. + */ + md_hmac_starts( &ctx->md_ctx, ctx->V, md_info->size ); + memset( ctx->V, 0x01, md_info->size ); + + ctx->f_entropy = f_entropy; + ctx->p_entropy = p_entropy; + + ctx->reseed_interval = POLARSSL_HMAC_DRBG_RESEED_INTERVAL; + + /* + * See SP800-57 5.6.1 (p. 65-66) for the security strength provided by + * each hash function, then according to SP800-90A rev1 10.1 table 2, + * min_entropy_len (in bits) is security_strength. + * + * (This also matches the sizes used in the NIST test vectors.) + */ + entropy_len = md_info->size <= 20 ? 16 : /* 160-bits hash -> 128 bits */ + md_info->size <= 28 ? 24 : /* 224-bits hash -> 192 bits */ + 32; /* better (256+) -> 256 bits */ + + /* + * For initialisation, use more entropy to emulate a nonce + * (Again, matches test vectors.) + */ + ctx->entropy_len = entropy_len * 3 / 2; + + if( ( ret = hmac_drbg_reseed( ctx, custom, len ) ) != 0 ) + return( ret ); + + ctx->entropy_len = entropy_len; + + return( 0 ); +} + +/* + * Set prediction resistance + */ +void hmac_drbg_set_prediction_resistance( hmac_drbg_context *ctx, + int resistance ) +{ + ctx->prediction_resistance = resistance; +} + +/* + * Set entropy length grabbed for reseeds + */ +void hmac_drbg_set_entropy_len( hmac_drbg_context *ctx, size_t len ) +{ + ctx->entropy_len = len; +} + +/* + * Set reseed interval + */ +void hmac_drbg_set_reseed_interval( hmac_drbg_context *ctx, int interval ) +{ + ctx->reseed_interval = interval; +} + +/* + * HMAC_DRBG random function with optional additional data: + * 10.1.2.5 (arabic) + 9.3 (Roman) + */ +int hmac_drbg_random_with_add( void *p_rng, + unsigned char *output, size_t out_len, + const unsigned char *additional, size_t add_len ) +{ + int ret; + hmac_drbg_context *ctx = (hmac_drbg_context *) p_rng; + size_t md_len = md_get_size( ctx->md_ctx.md_info ); + size_t left = out_len; + unsigned char *out = output; + + /* II. Check request length */ + if( out_len > POLARSSL_HMAC_DRBG_MAX_REQUEST ) + return( POLARSSL_ERR_HMAC_DRBG_REQUEST_TOO_BIG ); + + /* III. Check input length */ + if( add_len > POLARSSL_HMAC_DRBG_MAX_INPUT ) + return( POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG ); + + /* 1. (aka VII and IX) Check reseed counter and PR */ + if( ctx->f_entropy != NULL && /* For no-reseeding instances */ + ( ctx->prediction_resistance == POLARSSL_HMAC_DRBG_PR_ON || + ctx->reseed_counter > ctx->reseed_interval ) ) + { + if( ( ret = hmac_drbg_reseed( ctx, additional, add_len ) ) != 0 ) + return( ret ); + + add_len = 0; /* VII.4 */ + } + + /* 2. Use additional data if any */ + if( additional != NULL && add_len != 0 ) + hmac_drbg_update( ctx, additional, add_len ); + + /* 3, 4, 5. Generate bytes */ + while( left != 0 ) + { + size_t use_len = left > md_len ? md_len : left; + + md_hmac_reset( &ctx->md_ctx ); + md_hmac_update( &ctx->md_ctx, ctx->V, md_len ); + md_hmac_finish( &ctx->md_ctx, ctx->V ); + + memcpy( out, ctx->V, use_len ); + out += use_len; + left -= use_len; + } + + /* 6. Update */ + hmac_drbg_update( ctx, additional, add_len ); + + /* 7. Update reseed counter */ + ctx->reseed_counter++; + + /* 8. Done */ + return( 0 ); +} + +/* + * HMAC_DRBG random function + */ +int hmac_drbg_random( void *p_rng, unsigned char *output, size_t out_len ) +{ + return( hmac_drbg_random_with_add( p_rng, output, out_len, NULL, 0 ) ); +} + +/* + * Free an HMAC_DRBG context + */ +void hmac_drbg_free( hmac_drbg_context *ctx ) +{ + if( ctx == NULL ) + return; + + md_free_ctx( &ctx->md_ctx ); + + polarssl_zeroize( ctx, sizeof( hmac_drbg_context ) ); +} + +#if defined(POLARSSL_FS_IO) +int hmac_drbg_write_seed_file( hmac_drbg_context *ctx, const char *path ) +{ + int ret; + FILE *f; + unsigned char buf[ POLARSSL_HMAC_DRBG_MAX_INPUT ]; + + if( ( f = fopen( path, "wb" ) ) == NULL ) + return( POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR ); + + if( ( ret = hmac_drbg_random( ctx, buf, sizeof( buf ) ) ) != 0 ) + goto exit; + + if( fwrite( buf, 1, sizeof( buf ), f ) != sizeof( buf ) ) + { + ret = POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR; + goto exit; + } + + ret = 0; + +exit: + fclose( f ); + return( ret ); +} + +int hmac_drbg_update_seed_file( hmac_drbg_context *ctx, const char *path ) +{ + FILE *f; + size_t n; + unsigned char buf[ POLARSSL_HMAC_DRBG_MAX_INPUT ]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + n = (size_t) ftell( f ); + fseek( f, 0, SEEK_SET ); + + if( n > POLARSSL_HMAC_DRBG_MAX_INPUT ) + { + fclose( f ); + return( POLARSSL_ERR_HMAC_DRBG_INPUT_TOO_BIG ); + } + + if( fread( buf, 1, n, f ) != n ) + { + fclose( f ); + return( POLARSSL_ERR_HMAC_DRBG_FILE_IO_ERROR ); + } + + fclose( f ); + + hmac_drbg_update( ctx, buf, n ); + + return( hmac_drbg_write_seed_file( ctx, path ) ); +} +#endif /* POLARSSL_FS_IO */ + + +#if defined(POLARSSL_SELF_TEST) + +#include + +#if !defined(POLARSSL_SHA1_C) +/* Dummy checkup routine */ +int hmac_drbg_self_test( int verbose ) +{ + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} +#else + +#define OUTPUT_LEN 80 + +/* From a NIST PR=true test vector */ +static unsigned char entropy_pr[] = { + 0xa0, 0xc9, 0xab, 0x58, 0xf1, 0xe2, 0xe5, 0xa4, 0xde, 0x3e, 0xbd, 0x4f, + 0xf7, 0x3e, 0x9c, 0x5b, 0x64, 0xef, 0xd8, 0xca, 0x02, 0x8c, 0xf8, 0x11, + 0x48, 0xa5, 0x84, 0xfe, 0x69, 0xab, 0x5a, 0xee, 0x42, 0xaa, 0x4d, 0x42, + 0x17, 0x60, 0x99, 0xd4, 0x5e, 0x13, 0x97, 0xdc, 0x40, 0x4d, 0x86, 0xa3, + 0x7b, 0xf5, 0x59, 0x54, 0x75, 0x69, 0x51, 0xe4 }; +static const unsigned char result_pr[OUTPUT_LEN] = { + 0x9a, 0x00, 0xa2, 0xd0, 0x0e, 0xd5, 0x9b, 0xfe, 0x31, 0xec, 0xb1, 0x39, + 0x9b, 0x60, 0x81, 0x48, 0xd1, 0x96, 0x9d, 0x25, 0x0d, 0x3c, 0x1e, 0x94, + 0x10, 0x10, 0x98, 0x12, 0x93, 0x25, 0xca, 0xb8, 0xfc, 0xcc, 0x2d, 0x54, + 0x73, 0x19, 0x70, 0xc0, 0x10, 0x7a, 0xa4, 0x89, 0x25, 0x19, 0x95, 0x5e, + 0x4b, 0xc6, 0x00, 0x1d, 0x7f, 0x4e, 0x6a, 0x2b, 0xf8, 0xa3, 0x01, 0xab, + 0x46, 0x05, 0x5c, 0x09, 0xa6, 0x71, 0x88, 0xf1, 0xa7, 0x40, 0xee, 0xf3, + 0xe1, 0x5c, 0x02, 0x9b, 0x44, 0xaf, 0x03, 0x44 }; + +/* From a NIST PR=false test vector */ +static unsigned char entropy_nopr[] = { + 0x79, 0x34, 0x9b, 0xbf, 0x7c, 0xdd, 0xa5, 0x79, 0x95, 0x57, 0x86, 0x66, + 0x21, 0xc9, 0x13, 0x83, 0x11, 0x46, 0x73, 0x3a, 0xbf, 0x8c, 0x35, 0xc8, + 0xc7, 0x21, 0x5b, 0x5b, 0x96, 0xc4, 0x8e, 0x9b, 0x33, 0x8c, 0x74, 0xe3, + 0xe9, 0x9d, 0xfe, 0xdf }; +static const unsigned char result_nopr[OUTPUT_LEN] = { + 0xc6, 0xa1, 0x6a, 0xb8, 0xd4, 0x20, 0x70, 0x6f, 0x0f, 0x34, 0xab, 0x7f, + 0xec, 0x5a, 0xdc, 0xa9, 0xd8, 0xca, 0x3a, 0x13, 0x3e, 0x15, 0x9c, 0xa6, + 0xac, 0x43, 0xc6, 0xf8, 0xa2, 0xbe, 0x22, 0x83, 0x4a, 0x4c, 0x0a, 0x0a, + 0xff, 0xb1, 0x0d, 0x71, 0x94, 0xf1, 0xc1, 0xa5, 0xcf, 0x73, 0x22, 0xec, + 0x1a, 0xe0, 0x96, 0x4e, 0xd4, 0xbf, 0x12, 0x27, 0x46, 0xe0, 0x87, 0xfd, + 0xb5, 0xb3, 0xe9, 0x1b, 0x34, 0x93, 0xd5, 0xbb, 0x98, 0xfa, 0xed, 0x49, + 0xe8, 0x5f, 0x13, 0x0f, 0xc8, 0xa4, 0x59, 0xb7 }; + +/* "Entropy" from buffer */ +static size_t test_offset; +static int hmac_drbg_self_test_entropy( void *data, + unsigned char *buf, size_t len ) +{ + const unsigned char *p = data; + memcpy( buf, p + test_offset, len ); + test_offset += len; + return( 0 ); +} + +#define CHK( c ) if( (c) != 0 ) \ + { \ + if( verbose != 0 ) \ + polarssl_printf( "failed\n" ); \ + return( 1 ); \ + } + +/* + * Checkup routine for HMAC_DRBG with SHA-1 + */ +int hmac_drbg_self_test( int verbose ) +{ + hmac_drbg_context ctx; + unsigned char buf[OUTPUT_LEN]; + const md_info_t *md_info = md_info_from_type( POLARSSL_MD_SHA1 ); + + /* + * PR = True + */ + if( verbose != 0 ) + polarssl_printf( " HMAC_DRBG (PR = True) : " ); + + test_offset = 0; + CHK( hmac_drbg_init( &ctx, md_info, + hmac_drbg_self_test_entropy, entropy_pr, + NULL, 0 ) ); + hmac_drbg_set_prediction_resistance( &ctx, POLARSSL_HMAC_DRBG_PR_ON ); + CHK( hmac_drbg_random( &ctx, buf, OUTPUT_LEN ) ); + CHK( hmac_drbg_random( &ctx, buf, OUTPUT_LEN ) ); + CHK( memcmp( buf, result_pr, OUTPUT_LEN ) ); + hmac_drbg_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + /* + * PR = False + */ + if( verbose != 0 ) + polarssl_printf( " HMAC_DRBG (PR = False) : " ); + + test_offset = 0; + CHK( hmac_drbg_init( &ctx, md_info, + hmac_drbg_self_test_entropy, entropy_nopr, + NULL, 0 ) ); + CHK( hmac_drbg_reseed( &ctx, NULL, 0 ) ); + CHK( hmac_drbg_random( &ctx, buf, OUTPUT_LEN ) ); + CHK( hmac_drbg_random( &ctx, buf, OUTPUT_LEN ) ); + CHK( memcmp( buf, result_nopr, OUTPUT_LEN ) ); + hmac_drbg_free( &ctx ); + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_HMAC_DRBG_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/md.c b/component/common/network/ssl/polarssl-1.3.8/library/md.c new file mode 100644 index 0000000..7f9c5dc --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/md.c @@ -0,0 +1,341 @@ +/** + * \file md.c + * + * \brief Generic message digest wrapper for PolarSSL + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MD_C) + +#include "polarssl/md.h" +#include "polarssl/md_wrap.h" + +#include + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +static const int supported_digests[] = { + +#if defined(POLARSSL_SHA512_C) + POLARSSL_MD_SHA384, + POLARSSL_MD_SHA512, +#endif + +#if defined(POLARSSL_SHA256_C) + POLARSSL_MD_SHA224, + POLARSSL_MD_SHA256, +#endif + +#if defined(POLARSSL_SHA1_C) + POLARSSL_MD_SHA1, +#endif + +#if defined(POLARSSL_RIPEMD160_C) + POLARSSL_MD_RIPEMD160, +#endif + +#if defined(POLARSSL_MD5_C) + POLARSSL_MD_MD5, +#endif + +#if defined(POLARSSL_MD4_C) + POLARSSL_MD_MD4, +#endif + +#if defined(POLARSSL_MD2_C) + POLARSSL_MD_MD2, +#endif + + POLARSSL_MD_NONE +}; + +const int *md_list( void ) +{ + return( supported_digests ); +} + +const md_info_t *md_info_from_string( const char *md_name ) +{ + if( NULL == md_name ) + return( NULL ); + + /* Get the appropriate digest information */ +#if defined(POLARSSL_MD2_C) + if( !strcasecmp( "MD2", md_name ) ) + return md_info_from_type( POLARSSL_MD_MD2 ); +#endif +#if defined(POLARSSL_MD4_C) + if( !strcasecmp( "MD4", md_name ) ) + return md_info_from_type( POLARSSL_MD_MD4 ); +#endif +#if defined(POLARSSL_MD5_C) + if( !strcasecmp( "MD5", md_name ) ) + return md_info_from_type( POLARSSL_MD_MD5 ); +#endif +#if defined(POLARSSL_RIPEMD160_C) + if( !strcasecmp( "RIPEMD160", md_name ) ) + return md_info_from_type( POLARSSL_MD_RIPEMD160 ); +#endif +#if defined(POLARSSL_SHA1_C) + if( !strcasecmp( "SHA1", md_name ) || !strcasecmp( "SHA", md_name ) ) + return md_info_from_type( POLARSSL_MD_SHA1 ); +#endif +#if defined(POLARSSL_SHA256_C) + if( !strcasecmp( "SHA224", md_name ) ) + return md_info_from_type( POLARSSL_MD_SHA224 ); + if( !strcasecmp( "SHA256", md_name ) ) + return md_info_from_type( POLARSSL_MD_SHA256 ); +#endif +#if defined(POLARSSL_SHA512_C) + if( !strcasecmp( "SHA384", md_name ) ) + return md_info_from_type( POLARSSL_MD_SHA384 ); + if( !strcasecmp( "SHA512", md_name ) ) + return md_info_from_type( POLARSSL_MD_SHA512 ); +#endif + return( NULL ); +} + +const md_info_t *md_info_from_type( md_type_t md_type ) +{ + switch( md_type ) + { +#if defined(POLARSSL_MD2_C) + case POLARSSL_MD_MD2: + return( &md2_info ); +#endif +#if defined(POLARSSL_MD4_C) + case POLARSSL_MD_MD4: + return( &md4_info ); +#endif +#if defined(POLARSSL_MD5_C) + case POLARSSL_MD_MD5: + return( &md5_info ); +#endif +#if defined(POLARSSL_RIPEMD160_C) + case POLARSSL_MD_RIPEMD160: + return( &ripemd160_info ); +#endif +#if defined(POLARSSL_SHA1_C) + case POLARSSL_MD_SHA1: + return( &sha1_info ); +#endif +#if defined(POLARSSL_SHA256_C) + case POLARSSL_MD_SHA224: + return( &sha224_info ); + case POLARSSL_MD_SHA256: + return( &sha256_info ); +#endif +#if defined(POLARSSL_SHA512_C) + case POLARSSL_MD_SHA384: + return( &sha384_info ); + case POLARSSL_MD_SHA512: + return( &sha512_info ); +#endif + default: + return( NULL ); + } +} + +void md_init( md_context_t *ctx ) +{ + memset( ctx, 0, sizeof( md_context_t ) ); +} + +void md_free( md_context_t *ctx ) +{ + if( ctx == NULL ) + return; + + if( ctx->md_ctx ) + ctx->md_info->ctx_free_func( ctx->md_ctx ); + + polarssl_zeroize( ctx, sizeof( md_context_t ) ); +} + +int md_init_ctx( md_context_t *ctx, const md_info_t *md_info ) +{ + if( md_info == NULL || ctx == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + memset( ctx, 0, sizeof( md_context_t ) ); + + if( ( ctx->md_ctx = md_info->ctx_alloc_func() ) == NULL ) + return( POLARSSL_ERR_MD_ALLOC_FAILED ); + + ctx->md_info = md_info; + + md_info->starts_func( ctx->md_ctx ); + + return( 0 ); +} + +int md_free_ctx( md_context_t *ctx ) +{ + md_free( ctx ); + + return( 0 ); +} + +int md_starts( md_context_t *ctx ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->starts_func( ctx->md_ctx ); + + return( 0 ); +} + +int md_update( md_context_t *ctx, const unsigned char *input, size_t ilen ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->update_func( ctx->md_ctx, input, ilen ); + + return( 0 ); +} + +int md_finish( md_context_t *ctx, unsigned char *output ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->finish_func( ctx->md_ctx, output ); + + return( 0 ); +} + +int md( const md_info_t *md_info, const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + if( md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + md_info->digest_func( input, ilen, output ); + + return( 0 ); +} + +int md_file( const md_info_t *md_info, const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + int ret; +#endif + + if( md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + +#if defined(POLARSSL_FS_IO) + ret = md_info->file_func( path, output ); + if( ret != 0 ) + return( POLARSSL_ERR_MD_FILE_IO_ERROR + ret ); + + return( ret ); +#else + ((void) path); + ((void) output); + + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_FS_IO */ +} + +int md_hmac_starts( md_context_t *ctx, const unsigned char *key, size_t keylen ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->hmac_starts_func( ctx->md_ctx, key, keylen ); + + return( 0 ); +} + +int md_hmac_update( md_context_t *ctx, const unsigned char *input, size_t ilen ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->hmac_update_func( ctx->md_ctx, input, ilen ); + + return( 0 ); +} + +int md_hmac_finish( md_context_t *ctx, unsigned char *output ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->hmac_finish_func( ctx->md_ctx, output ); + + return( 0 ); +} + +int md_hmac_reset( md_context_t *ctx ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->hmac_reset_func( ctx->md_ctx ); + + return( 0 ); +} + +int md_hmac( const md_info_t *md_info, const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + if( md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + md_info->hmac_func( key, keylen, input, ilen, output ); + + return( 0 ); +} + +int md_process( md_context_t *ctx, const unsigned char *data ) +{ + if( ctx == NULL || ctx->md_info == NULL ) + return( POLARSSL_ERR_MD_BAD_INPUT_DATA ); + + ctx->md_info->process_func( ctx->md_ctx, data ); + + return( 0 ); +} + +#endif /* POLARSSL_MD_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/md2.c b/component/common/network/ssl/polarssl-1.3.8/library/md2.c new file mode 100644 index 0000000..45bce37 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/md2.c @@ -0,0 +1,398 @@ +/* + * RFC 1115/1319 compliant MD2 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The MD2 algorithm was designed by Ron Rivest in 1989. + * + * http://www.ietf.org/rfc/rfc1115.txt + * http://www.ietf.org/rfc/rfc1319.txt + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MD2_C) + +#include "polarssl/md2.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_MD2_ALT) + +static const unsigned char 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 +}; + +void md2_init( md2_context *ctx ) +{ + memset( ctx, 0, sizeof( md2_context ) ); +} + +void md2_free( md2_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( md2_context ) ); +} + +/* + * MD2 context setup + */ +void md2_starts( md2_context *ctx ) +{ + memset( ctx->cksum, 0, 16 ); + memset( ctx->state, 0, 46 ); + memset( ctx->buffer, 0, 16 ); + ctx->left = 0; +} + +void md2_process( md2_context *ctx ) +{ + int i, j; + unsigned char t = 0; + + for( i = 0; i < 16; i++ ) + { + ctx->state[i + 16] = ctx->buffer[i]; + ctx->state[i + 32] = + (unsigned char)( ctx->buffer[i] ^ ctx->state[i]); + } + + for( i = 0; i < 18; i++ ) + { + for( j = 0; j < 48; j++ ) + { + ctx->state[j] = (unsigned char) + ( ctx->state[j] ^ PI_SUBST[t] ); + t = ctx->state[j]; + } + + t = (unsigned char)( t + i ); + } + + t = ctx->cksum[15]; + + for( i = 0; i < 16; i++ ) + { + ctx->cksum[i] = (unsigned char) + ( ctx->cksum[i] ^ PI_SUBST[ctx->buffer[i] ^ t] ); + t = ctx->cksum[i]; + } +} + +/* + * MD2 process buffer + */ +void md2_update( md2_context *ctx, const unsigned char *input, size_t ilen ) +{ + size_t fill; + + while( ilen > 0 ) + { + if( ctx->left + ilen > 16 ) + fill = 16 - ctx->left; + else + fill = ilen; + + 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 + */ +void md2_finish( md2_context *ctx, unsigned char output[16] ) +{ + size_t i; + unsigned char x; + + x = (unsigned char)( 16 - ctx->left ); + + for( i = ctx->left; i < 16; i++ ) + ctx->buffer[i] = x; + + md2_process( ctx ); + + memcpy( ctx->buffer, ctx->cksum, 16 ); + md2_process( ctx ); + + memcpy( output, ctx->state, 16 ); +} + +#endif /* !POLARSSL_MD2_ALT */ + +/* + * output = MD2( input buffer ) + */ +void md2( const unsigned char *input, size_t ilen, unsigned char output[16] ) +{ + md2_context ctx; + + md2_init( &ctx ); + md2_starts( &ctx ); + md2_update( &ctx, input, ilen ); + md2_finish( &ctx, output ); + md2_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = MD2( file contents ) + */ +int md2_file( const char *path, unsigned char output[16] ) +{ + FILE *f; + size_t n; + md2_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_MD2_FILE_IO_ERROR ); + + md2_init( &ctx ); + md2_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + md2_update( &ctx, buf, n ); + + md2_finish( &ctx, output ); + md2_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_MD2_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * MD2 HMAC context setup + */ +void md2_hmac_starts( md2_context *ctx, const unsigned char *key, + size_t keylen ) +{ + size_t i; + unsigned char sum[16]; + + if( keylen > 16 ) + { + md2( key, keylen, sum ); + keylen = 16; + key = sum; + } + + memset( ctx->ipad, 0x36, 16 ); + memset( ctx->opad, 0x5C, 16 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + md2_starts( ctx ); + md2_update( ctx, ctx->ipad, 16 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * MD2 HMAC process buffer + */ +void md2_hmac_update( md2_context *ctx, const unsigned char *input, + size_t ilen ) +{ + md2_update( ctx, input, ilen ); +} + +/* + * MD2 HMAC final digest + */ +void md2_hmac_finish( md2_context *ctx, unsigned char output[16] ) +{ + unsigned char tmpbuf[16]; + + md2_finish( ctx, tmpbuf ); + md2_starts( ctx ); + md2_update( ctx, ctx->opad, 16 ); + md2_update( ctx, tmpbuf, 16 ); + md2_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * MD2 HMAC context reset + */ +void md2_hmac_reset( md2_context *ctx ) +{ + md2_starts( ctx ); + md2_update( ctx, ctx->ipad, 16 ); +} + +/* + * output = HMAC-MD2( hmac key, input buffer ) + */ +void md2_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ) +{ + md2_context ctx; + + md2_init( &ctx ); + md2_hmac_starts( &ctx, key, keylen ); + md2_hmac_update( &ctx, input, ilen ); + md2_hmac_finish( &ctx, output ); + md2_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) + +/* + * RFC 1319 test vectors + */ +static const char md2_test_str[7][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012" \ + "345678901234567890" } +}; + +static const unsigned char md2_test_sum[7][16] = +{ + { 0x83, 0x50, 0xE5, 0xA3, 0xE2, 0x4C, 0x15, 0x3D, + 0xF2, 0x27, 0x5C, 0x9F, 0x80, 0x69, 0x27, 0x73 }, + { 0x32, 0xEC, 0x01, 0xEC, 0x4A, 0x6D, 0xAC, 0x72, + 0xC0, 0xAB, 0x96, 0xFB, 0x34, 0xC0, 0xB5, 0xD1 }, + { 0xDA, 0x85, 0x3B, 0x0D, 0x3F, 0x88, 0xD9, 0x9B, + 0x30, 0x28, 0x3A, 0x69, 0xE6, 0xDE, 0xD6, 0xBB }, + { 0xAB, 0x4F, 0x49, 0x6B, 0xFB, 0x2A, 0x53, 0x0B, + 0x21, 0x9F, 0xF3, 0x30, 0x31, 0xFE, 0x06, 0xB0 }, + { 0x4E, 0x8D, 0xDF, 0xF3, 0x65, 0x02, 0x92, 0xAB, + 0x5A, 0x41, 0x08, 0xC3, 0xAA, 0x47, 0x94, 0x0B }, + { 0xDA, 0x33, 0xDE, 0xF2, 0xA4, 0x2D, 0xF1, 0x39, + 0x75, 0x35, 0x28, 0x46, 0xC3, 0x03, 0x38, 0xCD }, + { 0xD5, 0x97, 0x6F, 0x79, 0xD8, 0x3D, 0x3A, 0x0D, + 0xC9, 0x80, 0x6C, 0x3C, 0x66, 0xF3, 0xEF, 0xD8 } +}; + +/* + * Checkup routine + */ +int md2_self_test( int verbose ) +{ + int i; + unsigned char md2sum[16]; + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " MD2 test #%d: ", i + 1 ); + + md2( (unsigned char *) md2_test_str[i], + strlen( md2_test_str[i] ), md2sum ); + + if( memcmp( md2sum, md2_test_sum[i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_MD2_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/md4.c b/component/common/network/ssl/polarssl-1.3.8/library/md4.c new file mode 100644 index 0000000..f6b71d5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/md4.c @@ -0,0 +1,494 @@ +/* + * RFC 1186/1320 compliant MD4 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The MD4 algorithm was designed by Ron Rivest in 1990. + * + * http://www.ietf.org/rfc/rfc1186.txt + * http://www.ietf.org/rfc/rfc1320.txt + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MD4_C) + +#include "polarssl/md4.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_MD4_ALT) + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +void md4_init( md4_context *ctx ) +{ + memset( ctx, 0, sizeof( md4_context ) ); +} + +void md4_free( md4_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( md4_context ) ); +} + +/* + * MD4 context setup + */ +void md4_starts( md4_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md4_process( md4_context *ctx, const unsigned char data[64] ) +{ + uint32_t X[16], A, B, C, D; + + GET_UINT32_LE( X[ 0], data, 0 ); + GET_UINT32_LE( X[ 1], data, 4 ); + GET_UINT32_LE( X[ 2], data, 8 ); + GET_UINT32_LE( X[ 3], data, 12 ); + GET_UINT32_LE( X[ 4], data, 16 ); + GET_UINT32_LE( X[ 5], data, 20 ); + GET_UINT32_LE( X[ 6], data, 24 ); + GET_UINT32_LE( X[ 7], data, 28 ); + GET_UINT32_LE( X[ 8], data, 32 ); + GET_UINT32_LE( X[ 9], data, 36 ); + GET_UINT32_LE( X[10], data, 40 ); + GET_UINT32_LE( X[11], data, 44 ); + GET_UINT32_LE( X[12], data, 48 ); + GET_UINT32_LE( X[13], data, 52 ); + GET_UINT32_LE( X[14], data, 56 ); + GET_UINT32_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x, y, z) ((x & y) | ((~x) & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 1], 7 ); + P( C, D, A, B, X[ 2], 11 ); + P( B, C, D, A, X[ 3], 19 ); + P( A, B, C, D, X[ 4], 3 ); + P( D, A, B, C, X[ 5], 7 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[ 7], 19 ); + P( A, B, C, D, X[ 8], 3 ); + P( D, A, B, C, X[ 9], 7 ); + P( C, D, A, B, X[10], 11 ); + P( B, C, D, A, X[11], 19 ); + P( A, B, C, D, X[12], 3 ); + P( D, A, B, C, X[13], 7 ); + P( C, D, A, B, X[14], 11 ); + P( B, C, D, A, X[15], 19 ); + +#undef P +#undef F + +#define F(x,y,z) ((x & y) | (x & z) | (y & z)) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x5A827999; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 4], 5 ); + P( C, D, A, B, X[ 8], 9 ); + P( B, C, D, A, X[12], 13 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 5], 5 ); + P( C, D, A, B, X[ 9], 9 ); + P( B, C, D, A, X[13], 13 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[ 6], 5 ); + P( C, D, A, B, X[10], 9 ); + P( B, C, D, A, X[14], 13 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[ 7], 5 ); + P( C, D, A, B, X[11], 9 ); + P( B, C, D, A, X[15], 13 ); + +#undef P +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define P(a,b,c,d,x,s) { a += F(b,c,d) + x + 0x6ED9EBA1; a = S(a,s); } + + P( A, B, C, D, X[ 0], 3 ); + P( D, A, B, C, X[ 8], 9 ); + P( C, D, A, B, X[ 4], 11 ); + P( B, C, D, A, X[12], 15 ); + P( A, B, C, D, X[ 2], 3 ); + P( D, A, B, C, X[10], 9 ); + P( C, D, A, B, X[ 6], 11 ); + P( B, C, D, A, X[14], 15 ); + P( A, B, C, D, X[ 1], 3 ); + P( D, A, B, C, X[ 9], 9 ); + P( C, D, A, B, X[ 5], 11 ); + P( B, C, D, A, X[13], 15 ); + P( A, B, C, D, X[ 3], 3 ); + P( D, A, B, C, X[11], 9 ); + P( C, D, A, B, X[ 7], 11 ); + P( B, C, D, A, X[15], 15 ); + +#undef F +#undef P + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD4 process buffer + */ +void md4_update( md4_context *ctx, const unsigned char *input, size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + md4_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md4_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, ilen ); + } +} + +static const unsigned char md4_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 +}; + +/* + * MD4 final digest + */ +void md4_finish( md4_context *ctx, unsigned char output[16] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, msglen, 0 ); + PUT_UINT32_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md4_update( ctx, (unsigned char *) md4_padding, padn ); + md4_update( ctx, msglen, 8 ); + + PUT_UINT32_LE( ctx->state[0], output, 0 ); + PUT_UINT32_LE( ctx->state[1], output, 4 ); + PUT_UINT32_LE( ctx->state[2], output, 8 ); + PUT_UINT32_LE( ctx->state[3], output, 12 ); +} + +#endif /* !POLARSSL_MD4_ALT */ + +/* + * output = MD4( input buffer ) + */ +void md4( const unsigned char *input, size_t ilen, unsigned char output[16] ) +{ + md4_context ctx; + + md4_init( &ctx ); + md4_starts( &ctx ); + md4_update( &ctx, input, ilen ); + md4_finish( &ctx, output ); + md4_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = MD4( file contents ) + */ +int md4_file( const char *path, unsigned char output[16] ) +{ + FILE *f; + size_t n; + md4_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_MD4_FILE_IO_ERROR ); + + md4_init( &ctx ); + md4_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + md4_update( &ctx, buf, n ); + + md4_finish( &ctx, output ); + md4_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_MD4_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * MD4 HMAC context setup + */ +void md4_hmac_starts( md4_context *ctx, const unsigned char *key, + size_t keylen ) +{ + size_t i; + unsigned char sum[16]; + + if( keylen > 64 ) + { + md4( key, keylen, sum ); + keylen = 16; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + md4_starts( ctx ); + md4_update( ctx, ctx->ipad, 64 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * MD4 HMAC process buffer + */ +void md4_hmac_update( md4_context *ctx, const unsigned char *input, + size_t ilen ) +{ + md4_update( ctx, input, ilen ); +} + +/* + * MD4 HMAC final digest + */ +void md4_hmac_finish( md4_context *ctx, unsigned char output[16] ) +{ + unsigned char tmpbuf[16]; + + md4_finish( ctx, tmpbuf ); + md4_starts( ctx ); + md4_update( ctx, ctx->opad, 64 ); + md4_update( ctx, tmpbuf, 16 ); + md4_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * MD4 HMAC context reset + */ +void md4_hmac_reset( md4_context *ctx ) +{ + md4_starts( ctx ); + md4_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-MD4( hmac key, input buffer ) + */ +void md4_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ) +{ + md4_context ctx; + + md4_init( &ctx ); + md4_hmac_starts( &ctx, key, keylen ); + md4_hmac_update( &ctx, input, ilen ); + md4_hmac_finish( &ctx, output ); + md4_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) + +/* + * RFC 1320 test vectors + */ +static const char md4_test_str[7][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012" \ + "345678901234567890" } +}; + +static const unsigned char md4_test_sum[7][16] = +{ + { 0x31, 0xD6, 0xCF, 0xE0, 0xD1, 0x6A, 0xE9, 0x31, + 0xB7, 0x3C, 0x59, 0xD7, 0xE0, 0xC0, 0x89, 0xC0 }, + { 0xBD, 0xE5, 0x2C, 0xB3, 0x1D, 0xE3, 0x3E, 0x46, + 0x24, 0x5E, 0x05, 0xFB, 0xDB, 0xD6, 0xFB, 0x24 }, + { 0xA4, 0x48, 0x01, 0x7A, 0xAF, 0x21, 0xD8, 0x52, + 0x5F, 0xC1, 0x0A, 0xE8, 0x7A, 0xA6, 0x72, 0x9D }, + { 0xD9, 0x13, 0x0A, 0x81, 0x64, 0x54, 0x9F, 0xE8, + 0x18, 0x87, 0x48, 0x06, 0xE1, 0xC7, 0x01, 0x4B }, + { 0xD7, 0x9E, 0x1C, 0x30, 0x8A, 0xA5, 0xBB, 0xCD, + 0xEE, 0xA8, 0xED, 0x63, 0xDF, 0x41, 0x2D, 0xA9 }, + { 0x04, 0x3F, 0x85, 0x82, 0xF2, 0x41, 0xDB, 0x35, + 0x1C, 0xE6, 0x27, 0xE1, 0x53, 0xE7, 0xF0, 0xE4 }, + { 0xE3, 0x3B, 0x4D, 0xDC, 0x9C, 0x38, 0xF2, 0x19, + 0x9C, 0x3E, 0x7B, 0x16, 0x4F, 0xCC, 0x05, 0x36 } +}; + +/* + * Checkup routine + */ +int md4_self_test( int verbose ) +{ + int i; + unsigned char md4sum[16]; + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " MD4 test #%d: ", i + 1 ); + + md4( (unsigned char *) md4_test_str[i], + strlen( md4_test_str[i] ), md4sum ); + + if( memcmp( md4sum, md4_test_sum[i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_MD4_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/md5.c b/component/common/network/ssl/polarssl-1.3.8/library/md5.c new file mode 100644 index 0000000..89354bc --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/md5.c @@ -0,0 +1,615 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MD5_C) + +#include "polarssl/md5.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_MD5_ALT) + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +void md5_init( md5_context *ctx ) +{ + memset( ctx, 0, sizeof( md5_context ) ); +} + +void md5_free( md5_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( md5_context ) ); +} + +/* + * MD5 context setup + */ +void md5_starts( md5_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; +} + +void md5_process( md5_context *ctx, const unsigned char data[64] ) +{ + uint32_t X[16], A, B, C, D; + + GET_UINT32_LE( X[ 0], data, 0 ); + GET_UINT32_LE( X[ 1], data, 4 ); + GET_UINT32_LE( X[ 2], data, 8 ); + GET_UINT32_LE( X[ 3], data, 12 ); + GET_UINT32_LE( X[ 4], data, 16 ); + GET_UINT32_LE( X[ 5], data, 20 ); + GET_UINT32_LE( X[ 6], data, 24 ); + GET_UINT32_LE( X[ 7], data, 28 ); + GET_UINT32_LE( X[ 8], data, 32 ); + GET_UINT32_LE( X[ 9], data, 36 ); + GET_UINT32_LE( X[10], data, 40 ); + GET_UINT32_LE( X[11], data, 44 ); + GET_UINT32_LE( X[12], data, 48 ); + GET_UINT32_LE( X[13], data, 52 ); + GET_UINT32_LE( X[14], data, 56 ); + GET_UINT32_LE( X[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define P(a,b,c,d,k,s,t) \ +{ \ + a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) + + P( A, B, C, D, 0, 7, 0xD76AA478 ); + P( D, A, B, C, 1, 12, 0xE8C7B756 ); + P( C, D, A, B, 2, 17, 0x242070DB ); + P( B, C, D, A, 3, 22, 0xC1BDCEEE ); + P( A, B, C, D, 4, 7, 0xF57C0FAF ); + P( D, A, B, C, 5, 12, 0x4787C62A ); + P( C, D, A, B, 6, 17, 0xA8304613 ); + P( B, C, D, A, 7, 22, 0xFD469501 ); + P( A, B, C, D, 8, 7, 0x698098D8 ); + P( D, A, B, C, 9, 12, 0x8B44F7AF ); + P( C, D, A, B, 10, 17, 0xFFFF5BB1 ); + P( B, C, D, A, 11, 22, 0x895CD7BE ); + P( A, B, C, D, 12, 7, 0x6B901122 ); + P( D, A, B, C, 13, 12, 0xFD987193 ); + P( C, D, A, B, 14, 17, 0xA679438E ); + P( B, C, D, A, 15, 22, 0x49B40821 ); + +#undef F + +#define F(x,y,z) (y ^ (z & (x ^ y))) + + P( A, B, C, D, 1, 5, 0xF61E2562 ); + P( D, A, B, C, 6, 9, 0xC040B340 ); + P( C, D, A, B, 11, 14, 0x265E5A51 ); + P( B, C, D, A, 0, 20, 0xE9B6C7AA ); + P( A, B, C, D, 5, 5, 0xD62F105D ); + P( D, A, B, C, 10, 9, 0x02441453 ); + P( C, D, A, B, 15, 14, 0xD8A1E681 ); + P( B, C, D, A, 4, 20, 0xE7D3FBC8 ); + P( A, B, C, D, 9, 5, 0x21E1CDE6 ); + P( D, A, B, C, 14, 9, 0xC33707D6 ); + P( C, D, A, B, 3, 14, 0xF4D50D87 ); + P( B, C, D, A, 8, 20, 0x455A14ED ); + P( A, B, C, D, 13, 5, 0xA9E3E905 ); + P( D, A, B, C, 2, 9, 0xFCEFA3F8 ); + P( C, D, A, B, 7, 14, 0x676F02D9 ); + P( B, C, D, A, 12, 20, 0x8D2A4C8A ); + +#undef F + +#define F(x,y,z) (x ^ y ^ z) + + P( A, B, C, D, 5, 4, 0xFFFA3942 ); + P( D, A, B, C, 8, 11, 0x8771F681 ); + P( C, D, A, B, 11, 16, 0x6D9D6122 ); + P( B, C, D, A, 14, 23, 0xFDE5380C ); + P( A, B, C, D, 1, 4, 0xA4BEEA44 ); + P( D, A, B, C, 4, 11, 0x4BDECFA9 ); + P( C, D, A, B, 7, 16, 0xF6BB4B60 ); + P( B, C, D, A, 10, 23, 0xBEBFBC70 ); + P( A, B, C, D, 13, 4, 0x289B7EC6 ); + P( D, A, B, C, 0, 11, 0xEAA127FA ); + P( C, D, A, B, 3, 16, 0xD4EF3085 ); + P( B, C, D, A, 6, 23, 0x04881D05 ); + P( A, B, C, D, 9, 4, 0xD9D4D039 ); + P( D, A, B, C, 12, 11, 0xE6DB99E5 ); + P( C, D, A, B, 15, 16, 0x1FA27CF8 ); + P( B, C, D, A, 2, 23, 0xC4AC5665 ); + +#undef F + +#define F(x,y,z) (y ^ (x | ~z)) + + P( A, B, C, D, 0, 6, 0xF4292244 ); + P( D, A, B, C, 7, 10, 0x432AFF97 ); + P( C, D, A, B, 14, 15, 0xAB9423A7 ); + P( B, C, D, A, 5, 21, 0xFC93A039 ); + P( A, B, C, D, 12, 6, 0x655B59C3 ); + P( D, A, B, C, 3, 10, 0x8F0CCC92 ); + P( C, D, A, B, 10, 15, 0xFFEFF47D ); + P( B, C, D, A, 1, 21, 0x85845DD1 ); + P( A, B, C, D, 8, 6, 0x6FA87E4F ); + P( D, A, B, C, 15, 10, 0xFE2CE6E0 ); + P( C, D, A, B, 6, 15, 0xA3014314 ); + P( B, C, D, A, 13, 21, 0x4E0811A1 ); + P( A, B, C, D, 4, 6, 0xF7537E82 ); + P( D, A, B, C, 11, 10, 0xBD3AF235 ); + P( C, D, A, B, 2, 15, 0x2AD7D2BB ); + P( B, C, D, A, 9, 21, 0xEB86D391 ); + +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; +} + +/* + * MD5 process buffer + */ +void md5_update( md5_context *ctx, const unsigned char *input, size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + md5_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + md5_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), input, ilen ); + } +} + +static const unsigned char md5_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 +}; + +/* + * MD5 final digest + */ +void md5_finish( md5_context *ctx, unsigned char output[16] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, msglen, 0 ); + PUT_UINT32_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + md5_update( ctx, md5_padding, padn ); + md5_update( ctx, msglen, 8 ); + + PUT_UINT32_LE( ctx->state[0], output, 0 ); + PUT_UINT32_LE( ctx->state[1], output, 4 ); + PUT_UINT32_LE( ctx->state[2], output, 8 ); + PUT_UINT32_LE( ctx->state[3], output, 12 ); +} + +#endif /* !POLARSSL_MD5_ALT */ + +/* + * output = MD5( input buffer ) + */ +void md5( const unsigned char *input, size_t ilen, unsigned char output[16] ) +{ + md5_context ctx; + + md5_init( &ctx ); + md5_starts( &ctx ); + md5_update( &ctx, input, ilen ); + md5_finish( &ctx, output ); + md5_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = MD5( file contents ) + */ +int md5_file( const char *path, unsigned char output[16] ) +{ + FILE *f; + size_t n; + md5_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_MD5_FILE_IO_ERROR ); + + md5_init( &ctx ); + md5_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + md5_update( &ctx, buf, n ); + + md5_finish( &ctx, output ); + md5_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_MD5_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * MD5 HMAC context setup + */ +void md5_hmac_starts( md5_context *ctx, const unsigned char *key, + size_t keylen ) +{ + size_t i; + unsigned char sum[16]; + + if( keylen > 64 ) + { + md5( key, keylen, sum ); + keylen = 16; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + md5_starts( ctx ); + md5_update( ctx, ctx->ipad, 64 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * MD5 HMAC process buffer + */ +void md5_hmac_update( md5_context *ctx, const unsigned char *input, + size_t ilen ) +{ + md5_update( ctx, input, ilen ); +} + +/* + * MD5 HMAC final digest + */ +void md5_hmac_finish( md5_context *ctx, unsigned char output[16] ) +{ + unsigned char tmpbuf[16]; + + md5_finish( ctx, tmpbuf ); + md5_starts( ctx ); + md5_update( ctx, ctx->opad, 64 ); + md5_update( ctx, tmpbuf, 16 ); + md5_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * MD5 HMAC context reset + */ +void md5_hmac_reset( md5_context *ctx ) +{ + md5_starts( ctx ); + md5_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-MD5( hmac key, input buffer ) + */ +void md5_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[16] ) +{ + md5_context ctx; + + md5_init( &ctx ); + md5_hmac_starts( &ctx, key, keylen ); + md5_hmac_update( &ctx, input, ilen ); + md5_hmac_finish( &ctx, output ); + md5_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) +/* + * RFC 1321 test vectors + */ +static unsigned char md5_test_buf[7][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012" \ + "345678901234567890" } +}; + +static const int md5_test_buflen[7] = +{ + 0, 1, 3, 14, 26, 62, 80 +}; + +static const unsigned char md5_test_sum[7][16] = +{ + { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, + 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E }, + { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, + 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 }, + { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, + 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }, + { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, + 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 }, + { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, + 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B }, + { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, + 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F }, + { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, + 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A } +}; + +/* + * RFC 2202 test vectors + */ +static unsigned char md5_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 80 times */ + { "" } +}; + +static const int md5_hmac_test_keylen[7] = +{ + 16, 4, 16, 25, 16, 80, 80 +}; + +static unsigned char md5_hmac_test_buf[7][74] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "Test Using Larger Than Block-Size Key and Larger" + " Than One Block-Size Data" } +}; + +static const int md5_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 73 +}; + +static const unsigned char md5_hmac_test_sum[7][16] = +{ + { 0x92, 0x94, 0x72, 0x7A, 0x36, 0x38, 0xBB, 0x1C, + 0x13, 0xF4, 0x8E, 0xF8, 0x15, 0x8B, 0xFC, 0x9D }, + { 0x75, 0x0C, 0x78, 0x3E, 0x6A, 0xB0, 0xB5, 0x03, + 0xEA, 0xA8, 0x6E, 0x31, 0x0A, 0x5D, 0xB7, 0x38 }, + { 0x56, 0xBE, 0x34, 0x52, 0x1D, 0x14, 0x4C, 0x88, + 0xDB, 0xB8, 0xC7, 0x33, 0xF0, 0xE8, 0xB3, 0xF6 }, + { 0x69, 0x7E, 0xAF, 0x0A, 0xCA, 0x3A, 0x3A, 0xEA, + 0x3A, 0x75, 0x16, 0x47, 0x46, 0xFF, 0xAA, 0x79 }, + { 0x56, 0x46, 0x1E, 0xF2, 0x34, 0x2E, 0xDC, 0x00, + 0xF9, 0xBA, 0xB9, 0x95 }, + { 0x6B, 0x1A, 0xB7, 0xFE, 0x4B, 0xD7, 0xBF, 0x8F, + 0x0B, 0x62, 0xE6, 0xCE, 0x61, 0xB9, 0xD0, 0xCD }, + { 0x6F, 0x63, 0x0F, 0xAD, 0x67, 0xCD, 0xA0, 0xEE, + 0x1F, 0xB1, 0xF5, 0x62, 0xDB, 0x3A, 0xA5, 0x3E } +}; + +/* + * Checkup routine + */ +int md5_self_test( int verbose ) +{ + int i, buflen; + unsigned char buf[1024]; + unsigned char md5sum[16]; + md5_context ctx; + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " MD5 test #%d: ", i + 1 ); + + md5( md5_test_buf[i], md5_test_buflen[i], md5sum ); + + if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " HMAC-MD5 test #%d: ", i + 1 ); + + if( i == 5 || i == 6 ) + { + memset( buf, '\xAA', buflen = 80 ); + md5_hmac_starts( &ctx, buf, buflen ); + } + else + md5_hmac_starts( &ctx, md5_hmac_test_key[i], + md5_hmac_test_keylen[i] ); + + md5_hmac_update( &ctx, md5_hmac_test_buf[i], + md5_hmac_test_buflen[i] ); + + md5_hmac_finish( &ctx, md5sum ); + + buflen = ( i == 4 ) ? 12 : 16; + + if( memcmp( md5sum, md5_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_MD5_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/md_wrap.c b/component/common/network/ssl/polarssl-1.3.8/library/md_wrap.c new file mode 100644 index 0000000..de701d3 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/md_wrap.c @@ -0,0 +1,955 @@ +/** + * \file md_wrap.c + + * \brief Generic message digest wrapper for PolarSSL + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MD_C) + +#include "polarssl/md_wrap.h" + +#if defined(POLARSSL_MD2_C) +#include "polarssl/md2.h" +#endif + +#if defined(POLARSSL_MD4_C) +#include "polarssl/md4.h" +#endif + +#if defined(POLARSSL_MD5_C) +#include "polarssl/md5.h" +#endif + +#if defined(POLARSSL_RIPEMD160_C) +#include "polarssl/ripemd160.h" +#endif + +#if defined(POLARSSL_SHA1_C) +#include "polarssl/sha1.h" +#endif + +#if defined(POLARSSL_SHA256_C) +#include "polarssl/sha256.h" +#endif + +#if defined(POLARSSL_SHA512_C) +#include "polarssl/sha512.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if defined(POLARSSL_MD2_C) + +static void md2_starts_wrap( void *ctx ) +{ + md2_starts( (md2_context *) ctx ); +} + +static void md2_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md2_update( (md2_context *) ctx, input, ilen ); +} + +static void md2_finish_wrap( void *ctx, unsigned char *output ) +{ + md2_finish( (md2_context *) ctx, output ); +} + +static int md2_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return md2_file( path, output ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void md2_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + md2_hmac_starts( (md2_context *) ctx, key, keylen ); +} + +static void md2_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md2_hmac_update( (md2_context *) ctx, input, ilen ); +} + +static void md2_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + md2_hmac_finish( (md2_context *) ctx, output ); +} + +static void md2_hmac_reset_wrap( void *ctx ) +{ + md2_hmac_reset( (md2_context *) ctx ); +} + +static void * md2_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( md2_context ) ); +} + +static void md2_ctx_free( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( md2_context ) ); + polarssl_free( ctx ); +} + +static void md2_process_wrap( void *ctx, const unsigned char *data ) +{ + ((void) data); + + md2_process( (md2_context *) ctx ); +} + +const md_info_t md2_info = { + POLARSSL_MD_MD2, + "MD2", + 16, + md2_starts_wrap, + md2_update_wrap, + md2_finish_wrap, + md2, + md2_file_wrap, + md2_hmac_starts_wrap, + md2_hmac_update_wrap, + md2_hmac_finish_wrap, + md2_hmac_reset_wrap, + md2_hmac, + md2_ctx_alloc, + md2_ctx_free, + md2_process_wrap, +}; + +#endif /* POLARSSL_MD2_C */ + +#if defined(POLARSSL_MD4_C) + +static void md4_starts_wrap( void *ctx ) +{ + md4_starts( (md4_context *) ctx ); +} + +static void md4_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md4_update( (md4_context *) ctx, input, ilen ); +} + +static void md4_finish_wrap( void *ctx, unsigned char *output ) +{ + md4_finish( (md4_context *) ctx, output ); +} + +static int md4_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return md4_file( path, output ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void md4_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + md4_hmac_starts( (md4_context *) ctx, key, keylen ); +} + +static void md4_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md4_hmac_update( (md4_context *) ctx, input, ilen ); +} + +static void md4_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + md4_hmac_finish( (md4_context *) ctx, output ); +} + +static void md4_hmac_reset_wrap( void *ctx ) +{ + md4_hmac_reset( (md4_context *) ctx ); +} + +static void *md4_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( md4_context ) ); +} + +static void md4_ctx_free( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( md4_context ) ); + polarssl_free( ctx ); +} + +static void md4_process_wrap( void *ctx, const unsigned char *data ) +{ + md4_process( (md4_context *) ctx, data ); +} + +const md_info_t md4_info = { + POLARSSL_MD_MD4, + "MD4", + 16, + md4_starts_wrap, + md4_update_wrap, + md4_finish_wrap, + md4, + md4_file_wrap, + md4_hmac_starts_wrap, + md4_hmac_update_wrap, + md4_hmac_finish_wrap, + md4_hmac_reset_wrap, + md4_hmac, + md4_ctx_alloc, + md4_ctx_free, + md4_process_wrap, +}; + +#endif /* POLARSSL_MD4_C */ + +#if defined(POLARSSL_MD5_C) + +static void md5_starts_wrap( void *ctx ) +{ + md5_starts( (md5_context *) ctx ); +} + +static void md5_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md5_update( (md5_context *) ctx, input, ilen ); +} + +static void md5_finish_wrap( void *ctx, unsigned char *output ) +{ + md5_finish( (md5_context *) ctx, output ); +} + +static int md5_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return md5_file( path, output ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void md5_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + md5_hmac_starts( (md5_context *) ctx, key, keylen ); +} + +static void md5_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + md5_hmac_update( (md5_context *) ctx, input, ilen ); +} + +static void md5_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + md5_hmac_finish( (md5_context *) ctx, output ); +} + +static void md5_hmac_reset_wrap( void *ctx ) +{ + md5_hmac_reset( (md5_context *) ctx ); +} + +static void * md5_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( md5_context ) ); +} + +static void md5_ctx_free( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( md5_context ) ); + polarssl_free( ctx ); +} + +static void md5_process_wrap( void *ctx, const unsigned char *data ) +{ + md5_process( (md5_context *) ctx, data ); +} + +const md_info_t md5_info = { + POLARSSL_MD_MD5, + "MD5", + 16, + md5_starts_wrap, + md5_update_wrap, + md5_finish_wrap, + md5, + md5_file_wrap, + md5_hmac_starts_wrap, + md5_hmac_update_wrap, + md5_hmac_finish_wrap, + md5_hmac_reset_wrap, + md5_hmac, + md5_ctx_alloc, + md5_ctx_free, + md5_process_wrap, +}; + +#endif /* POLARSSL_MD5_C */ + +#if defined(POLARSSL_RIPEMD160_C) + +static void ripemd160_starts_wrap( void *ctx ) +{ + ripemd160_starts( (ripemd160_context *) ctx ); +} + +static void ripemd160_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + ripemd160_update( (ripemd160_context *) ctx, input, ilen ); +} + +static void ripemd160_finish_wrap( void *ctx, unsigned char *output ) +{ + ripemd160_finish( (ripemd160_context *) ctx, output ); +} + +static int ripemd160_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return ripemd160_file( path, output ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void ripemd160_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + ripemd160_hmac_starts( (ripemd160_context *) ctx, key, keylen ); +} + +static void ripemd160_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + ripemd160_hmac_update( (ripemd160_context *) ctx, input, ilen ); +} + +static void ripemd160_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + ripemd160_hmac_finish( (ripemd160_context *) ctx, output ); +} + +static void ripemd160_hmac_reset_wrap( void *ctx ) +{ + ripemd160_hmac_reset( (ripemd160_context *) ctx ); +} + +static void * ripemd160_ctx_alloc( void ) +{ + ripemd160_context *ctx; + ctx = (ripemd160_context *) polarssl_malloc( sizeof( ripemd160_context ) ); + + if( ctx == NULL ) + return( NULL ); + + ripemd160_init( ctx ); + + return( ctx ); +} + +static void ripemd160_ctx_free( void *ctx ) +{ + ripemd160_free( (ripemd160_context *) ctx ); + polarssl_free( ctx ); +} + +static void ripemd160_process_wrap( void *ctx, const unsigned char *data ) +{ + ripemd160_process( (ripemd160_context *) ctx, data ); +} + +const md_info_t ripemd160_info = { + POLARSSL_MD_RIPEMD160, + "RIPEMD160", + 20, + ripemd160_starts_wrap, + ripemd160_update_wrap, + ripemd160_finish_wrap, + ripemd160, + ripemd160_file_wrap, + ripemd160_hmac_starts_wrap, + ripemd160_hmac_update_wrap, + ripemd160_hmac_finish_wrap, + ripemd160_hmac_reset_wrap, + ripemd160_hmac, + ripemd160_ctx_alloc, + ripemd160_ctx_free, + ripemd160_process_wrap, +}; + +#endif /* POLARSSL_RIPEMD160_C */ + +#if defined(POLARSSL_SHA1_C) + +static void sha1_starts_wrap( void *ctx ) +{ + sha1_starts( (sha1_context *) ctx ); +} + +static void sha1_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha1_update( (sha1_context *) ctx, input, ilen ); +} + +static void sha1_finish_wrap( void *ctx, unsigned char *output ) +{ + sha1_finish( (sha1_context *) ctx, output ); +} + +static int sha1_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return sha1_file( path, output ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void sha1_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + sha1_hmac_starts( (sha1_context *) ctx, key, keylen ); +} + +static void sha1_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha1_hmac_update( (sha1_context *) ctx, input, ilen ); +} + +static void sha1_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + sha1_hmac_finish( (sha1_context *) ctx, output ); +} + +static void sha1_hmac_reset_wrap( void *ctx ) +{ + sha1_hmac_reset( (sha1_context *) ctx ); +} + +static void * sha1_ctx_alloc( void ) +{ + sha1_context *ctx; + ctx = (sha1_context *) polarssl_malloc( sizeof( sha1_context ) ); + + if( ctx == NULL ) + return( NULL ); + + sha1_init( ctx ); + + return( ctx ); +} + +static void sha1_ctx_free( void *ctx ) +{ + sha1_free( (sha1_context *) ctx ); + polarssl_free( ctx ); +} + +static void sha1_process_wrap( void *ctx, const unsigned char *data ) +{ + sha1_process( (sha1_context *) ctx, data ); +} + +const md_info_t sha1_info = { + POLARSSL_MD_SHA1, + "SHA1", + 20, + sha1_starts_wrap, + sha1_update_wrap, + sha1_finish_wrap, + sha1, + sha1_file_wrap, + sha1_hmac_starts_wrap, + sha1_hmac_update_wrap, + sha1_hmac_finish_wrap, + sha1_hmac_reset_wrap, + sha1_hmac, + sha1_ctx_alloc, + sha1_ctx_free, + sha1_process_wrap, +}; + +#endif /* POLARSSL_SHA1_C */ + +/* + * Wrappers for generic message digests + */ +#if defined(POLARSSL_SHA256_C) + +static void sha224_starts_wrap( void *ctx ) +{ + sha256_starts( (sha256_context *) ctx, 1 ); +} + +static void sha224_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha256_update( (sha256_context *) ctx, input, ilen ); +} + +static void sha224_finish_wrap( void *ctx, unsigned char *output ) +{ + sha256_finish( (sha256_context *) ctx, output ); +} + +static void sha224_wrap( const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha256( input, ilen, output, 1 ); +} + +static int sha224_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return sha256_file( path, output, 1 ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void sha224_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + sha256_hmac_starts( (sha256_context *) ctx, key, keylen, 1 ); +} + +static void sha224_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha256_hmac_update( (sha256_context *) ctx, input, ilen ); +} + +static void sha224_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + sha256_hmac_finish( (sha256_context *) ctx, output ); +} + +static void sha224_hmac_reset_wrap( void *ctx ) +{ + sha256_hmac_reset( (sha256_context *) ctx ); +} + +static void sha224_hmac_wrap( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha256_hmac( key, keylen, input, ilen, output, 1 ); +} + +static void * sha224_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( sha256_context ) ); +} + +static void sha224_ctx_free( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( sha256_context ) ); + polarssl_free( ctx ); +} + +static void sha224_process_wrap( void *ctx, const unsigned char *data ) +{ + sha256_process( (sha256_context *) ctx, data ); +} + +const md_info_t sha224_info = { + POLARSSL_MD_SHA224, + "SHA224", + 28, + sha224_starts_wrap, + sha224_update_wrap, + sha224_finish_wrap, + sha224_wrap, + sha224_file_wrap, + sha224_hmac_starts_wrap, + sha224_hmac_update_wrap, + sha224_hmac_finish_wrap, + sha224_hmac_reset_wrap, + sha224_hmac_wrap, + sha224_ctx_alloc, + sha224_ctx_free, + sha224_process_wrap, +}; + +static void sha256_starts_wrap( void *ctx ) +{ + sha256_starts( (sha256_context *) ctx, 0 ); +} + +static void sha256_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha256_update( (sha256_context *) ctx, input, ilen ); +} + +static void sha256_finish_wrap( void *ctx, unsigned char *output ) +{ + sha256_finish( (sha256_context *) ctx, output ); +} + +static void sha256_wrap( const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha256( input, ilen, output, 0 ); +} + +static int sha256_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return sha256_file( path, output, 0 ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void sha256_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + sha256_hmac_starts( (sha256_context *) ctx, key, keylen, 0 ); +} + +static void sha256_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha256_hmac_update( (sha256_context *) ctx, input, ilen ); +} + +static void sha256_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + sha256_hmac_finish( (sha256_context *) ctx, output ); +} + +static void sha256_hmac_reset_wrap( void *ctx ) +{ + sha256_hmac_reset( (sha256_context *) ctx ); +} + +static void sha256_hmac_wrap( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha256_hmac( key, keylen, input, ilen, output, 0 ); +} + +static void * sha256_ctx_alloc( void ) +{ + sha256_context *ctx; + ctx = (sha256_context *) polarssl_malloc( sizeof( sha256_context ) ); + + if( ctx == NULL ) + return( NULL ); + + sha256_init( ctx ); + + return( ctx ); +} + +static void sha256_ctx_free( void *ctx ) +{ + sha256_free( (sha256_context *) ctx ); + polarssl_free( ctx ); +} + +static void sha256_process_wrap( void *ctx, const unsigned char *data ) +{ + sha256_process( (sha256_context *) ctx, data ); +} + +const md_info_t sha256_info = { + POLARSSL_MD_SHA256, + "SHA256", + 32, + sha256_starts_wrap, + sha256_update_wrap, + sha256_finish_wrap, + sha256_wrap, + sha256_file_wrap, + sha256_hmac_starts_wrap, + sha256_hmac_update_wrap, + sha256_hmac_finish_wrap, + sha256_hmac_reset_wrap, + sha256_hmac_wrap, + sha256_ctx_alloc, + sha256_ctx_free, + sha256_process_wrap, +}; + +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + +static void sha384_starts_wrap( void *ctx ) +{ + sha512_starts( (sha512_context *) ctx, 1 ); +} + +static void sha384_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha512_update( (sha512_context *) ctx, input, ilen ); +} + +static void sha384_finish_wrap( void *ctx, unsigned char *output ) +{ + sha512_finish( (sha512_context *) ctx, output ); +} + +static void sha384_wrap( const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha512( input, ilen, output, 1 ); +} + +static int sha384_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return sha512_file( path, output, 1 ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void sha384_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + sha512_hmac_starts( (sha512_context *) ctx, key, keylen, 1 ); +} + +static void sha384_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha512_hmac_update( (sha512_context *) ctx, input, ilen ); +} + +static void sha384_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + sha512_hmac_finish( (sha512_context *) ctx, output ); +} + +static void sha384_hmac_reset_wrap( void *ctx ) +{ + sha512_hmac_reset( (sha512_context *) ctx ); +} + +static void sha384_hmac_wrap( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha512_hmac( key, keylen, input, ilen, output, 1 ); +} + +static void * sha384_ctx_alloc( void ) +{ + return polarssl_malloc( sizeof( sha512_context ) ); +} + +static void sha384_ctx_free( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( sha512_context ) ); + polarssl_free( ctx ); +} + +static void sha384_process_wrap( void *ctx, const unsigned char *data ) +{ + sha512_process( (sha512_context *) ctx, data ); +} + +const md_info_t sha384_info = { + POLARSSL_MD_SHA384, + "SHA384", + 48, + sha384_starts_wrap, + sha384_update_wrap, + sha384_finish_wrap, + sha384_wrap, + sha384_file_wrap, + sha384_hmac_starts_wrap, + sha384_hmac_update_wrap, + sha384_hmac_finish_wrap, + sha384_hmac_reset_wrap, + sha384_hmac_wrap, + sha384_ctx_alloc, + sha384_ctx_free, + sha384_process_wrap, +}; + +static void sha512_starts_wrap( void *ctx ) +{ + sha512_starts( (sha512_context *) ctx, 0 ); +} + +static void sha512_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha512_update( (sha512_context *) ctx, input, ilen ); +} + +static void sha512_finish_wrap( void *ctx, unsigned char *output ) +{ + sha512_finish( (sha512_context *) ctx, output ); +} + +static void sha512_wrap( const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha512( input, ilen, output, 0 ); +} + +static int sha512_file_wrap( const char *path, unsigned char *output ) +{ +#if defined(POLARSSL_FS_IO) + return sha512_file( path, output, 0 ); +#else + ((void) path); + ((void) output); + return( POLARSSL_ERR_MD_FEATURE_UNAVAILABLE ); +#endif +} + +static void sha512_hmac_starts_wrap( void *ctx, const unsigned char *key, + size_t keylen ) +{ + sha512_hmac_starts( (sha512_context *) ctx, key, keylen, 0 ); +} + +static void sha512_hmac_update_wrap( void *ctx, const unsigned char *input, + size_t ilen ) +{ + sha512_hmac_update( (sha512_context *) ctx, input, ilen ); +} + +static void sha512_hmac_finish_wrap( void *ctx, unsigned char *output ) +{ + sha512_hmac_finish( (sha512_context *) ctx, output ); +} + +static void sha512_hmac_reset_wrap( void *ctx ) +{ + sha512_hmac_reset( (sha512_context *) ctx ); +} + +static void sha512_hmac_wrap( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output ) +{ + sha512_hmac( key, keylen, input, ilen, output, 0 ); +} + +static void * sha512_ctx_alloc( void ) +{ + sha512_context *ctx; + ctx = (sha512_context *) polarssl_malloc( sizeof( sha512_context ) ); + + if( ctx == NULL ) + return( NULL ); + + sha512_init( ctx ); + + return( ctx ); +} + +static void sha512_ctx_free( void *ctx ) +{ + sha512_free( (sha512_context *) ctx ); + polarssl_free( ctx ); +} + +static void sha512_process_wrap( void *ctx, const unsigned char *data ) +{ + sha512_process( (sha512_context *) ctx, data ); +} + +const md_info_t sha512_info = { + POLARSSL_MD_SHA512, + "SHA512", + 64, + sha512_starts_wrap, + sha512_update_wrap, + sha512_finish_wrap, + sha512_wrap, + sha512_file_wrap, + sha512_hmac_starts_wrap, + sha512_hmac_update_wrap, + sha512_hmac_finish_wrap, + sha512_hmac_reset_wrap, + sha512_hmac_wrap, + sha512_ctx_alloc, + sha512_ctx_free, + sha512_process_wrap, +}; + +#endif /* POLARSSL_SHA512_C */ + +#endif /* POLARSSL_MD_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/memory_buffer_alloc.c b/component/common/network/ssl/polarssl-1.3.8/library/memory_buffer_alloc.c new file mode 100644 index 0000000..7710ba5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/memory_buffer_alloc.c @@ -0,0 +1,589 @@ +/* + * Buffer-based memory allocator + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C) + +#include "polarssl/memory_buffer_alloc.h" + +#include + +#if defined(POLARSSL_MEMORY_DEBUG) +#include +#if defined(POLARSSL_MEMORY_BACKTRACE) +#include +#endif +#endif + +#if defined(POLARSSL_THREADING_C) +#include "polarssl/threading.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_fprintf fprintf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#define MAGIC1 0xFF00AA55 +#define MAGIC2 0xEE119966 +#define MAX_BT 20 + +typedef struct _memory_header memory_header; +struct _memory_header +{ + size_t magic1; + size_t size; + size_t alloc; + memory_header *prev; + memory_header *next; + memory_header *prev_free; + memory_header *next_free; +#if defined(POLARSSL_MEMORY_BACKTRACE) + char **trace; + size_t trace_count; +#endif + size_t magic2; +}; + +typedef struct +{ + unsigned char *buf; + size_t len; + memory_header *first; + memory_header *first_free; + size_t current_alloc_size; + int verify; +#if defined(POLARSSL_MEMORY_DEBUG) + size_t malloc_count; + size_t free_count; + size_t total_used; + size_t maximum_used; + size_t header_count; + size_t maximum_header_count; +#endif +#if defined(POLARSSL_THREADING_C) + threading_mutex_t mutex; +#endif +} +buffer_alloc_ctx; + +static buffer_alloc_ctx heap; + +#if defined(POLARSSL_MEMORY_DEBUG) +static void debug_header( memory_header *hdr ) +{ +#if defined(POLARSSL_MEMORY_BACKTRACE) + size_t i; +#endif + + polarssl_fprintf( stderr, "HDR: PTR(%10u), PREV(%10u), NEXT(%10u), " + "ALLOC(%u), SIZE(%10u)\n", + (size_t) hdr, (size_t) hdr->prev, (size_t) hdr->next, + hdr->alloc, hdr->size ); + polarssl_fprintf( stderr, " FPREV(%10u), FNEXT(%10u)\n", + (size_t) hdr->prev_free, (size_t) hdr->next_free ); + +#if defined(POLARSSL_MEMORY_BACKTRACE) + polarssl_fprintf( stderr, "TRACE: \n" ); + for( i = 0; i < hdr->trace_count; i++ ) + polarssl_fprintf( stderr, "%s\n", hdr->trace[i] ); + polarssl_fprintf( stderr, "\n" ); +#endif +} + +static void debug_chain() +{ + memory_header *cur = heap.first; + + polarssl_fprintf( stderr, "\nBlock list\n" ); + while( cur != NULL ) + { + debug_header( cur ); + cur = cur->next; + } + + polarssl_fprintf( stderr, "Free list\n" ); + cur = heap.first_free; + + while( cur != NULL ) + { + debug_header( cur ); + cur = cur->next_free; + } +} +#endif /* POLARSSL_MEMORY_DEBUG */ + +static int verify_header( memory_header *hdr ) +{ + if( hdr->magic1 != MAGIC1 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: MAGIC1 mismatch\n" ); +#endif + return( 1 ); + } + + if( hdr->magic2 != MAGIC2 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: MAGIC2 mismatch\n" ); +#endif + return( 1 ); + } + + if( hdr->alloc > 1 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: alloc has illegal value\n" ); +#endif + return( 1 ); + } + + if( hdr->prev != NULL && hdr->prev == hdr->next ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: prev == next\n" ); +#endif + return( 1 ); + } + + if( hdr->prev_free != NULL && hdr->prev_free == hdr->next_free ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: prev_free == next_free\n" ); +#endif + return( 1 ); + } + + return( 0 ); +} + +static int verify_chain() +{ + memory_header *prv = heap.first, *cur = heap.first->next; + + if( verify_header( heap.first ) != 0 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: verification of first header " + "failed\n" ); +#endif + return( 1 ); + } + + if( heap.first->prev != NULL ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: verification failed: " + "first->prev != NULL\n" ); +#endif + return( 1 ); + } + + while( cur != NULL ) + { + if( verify_header( cur ) != 0 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: verification of header " + "failed\n" ); +#endif + return( 1 ); + } + + if( cur->prev != prv ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: verification failed: " + "cur->prev != prv\n" ); +#endif + return( 1 ); + } + + prv = cur; + cur = cur->next; + } + + return( 0 ); +} + +static void *buffer_alloc_malloc( size_t len ) +{ + memory_header *new, *cur = heap.first_free; + unsigned char *p; +#if defined(POLARSSL_MEMORY_BACKTRACE) + void *trace_buffer[MAX_BT]; + size_t trace_cnt; +#endif + + if( heap.buf == NULL || heap.first == NULL ) + return( NULL ); + + if( len % POLARSSL_MEMORY_ALIGN_MULTIPLE ) + { + len -= len % POLARSSL_MEMORY_ALIGN_MULTIPLE; + len += POLARSSL_MEMORY_ALIGN_MULTIPLE; + } + + // Find block that fits + // + while( cur != NULL ) + { + if( cur->size >= len ) + break; + + cur = cur->next_free; + } + + if( cur == NULL ) + return( NULL ); + + if( cur->alloc != 0 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: block in free_list but allocated " + "data\n" ); +#endif + exit( 1 ); + } + +#if defined(POLARSSL_MEMORY_DEBUG) + heap.malloc_count++; +#endif + + // Found location, split block if > memory_header + 4 room left + // + if( cur->size - len < sizeof(memory_header) + + POLARSSL_MEMORY_ALIGN_MULTIPLE ) + { + cur->alloc = 1; + + // Remove from free_list + // + if( cur->prev_free != NULL ) + cur->prev_free->next_free = cur->next_free; + else + heap.first_free = cur->next_free; + + if( cur->next_free != NULL ) + cur->next_free->prev_free = cur->prev_free; + + cur->prev_free = NULL; + cur->next_free = NULL; + +#if defined(POLARSSL_MEMORY_DEBUG) + heap.total_used += cur->size; + if( heap.total_used > heap.maximum_used ) + heap.maximum_used = heap.total_used; +#endif +#if defined(POLARSSL_MEMORY_BACKTRACE) + trace_cnt = backtrace( trace_buffer, MAX_BT ); + cur->trace = backtrace_symbols( trace_buffer, trace_cnt ); + cur->trace_count = trace_cnt; +#endif + + if( ( heap.verify & MEMORY_VERIFY_ALLOC ) && verify_chain() != 0 ) + exit( 1 ); + + return( ( (unsigned char *) cur ) + sizeof(memory_header) ); + } + + p = ( (unsigned char *) cur ) + sizeof(memory_header) + len; + new = (memory_header *) p; + + new->size = cur->size - len - sizeof(memory_header); + new->alloc = 0; + new->prev = cur; + new->next = cur->next; +#if defined(POLARSSL_MEMORY_BACKTRACE) + new->trace = NULL; + new->trace_count = 0; +#endif + new->magic1 = MAGIC1; + new->magic2 = MAGIC2; + + if( new->next != NULL ) + new->next->prev = new; + + // Replace cur with new in free_list + // + new->prev_free = cur->prev_free; + new->next_free = cur->next_free; + if( new->prev_free != NULL ) + new->prev_free->next_free = new; + else + heap.first_free = new; + + if( new->next_free != NULL ) + new->next_free->prev_free = new; + + cur->alloc = 1; + cur->size = len; + cur->next = new; + cur->prev_free = NULL; + cur->next_free = NULL; + +#if defined(POLARSSL_MEMORY_DEBUG) + heap.header_count++; + if( heap.header_count > heap.maximum_header_count ) + heap.maximum_header_count = heap.header_count; + heap.total_used += cur->size; + if( heap.total_used > heap.maximum_used ) + heap.maximum_used = heap.total_used; +#endif +#if defined(POLARSSL_MEMORY_BACKTRACE) + trace_cnt = backtrace( trace_buffer, MAX_BT ); + cur->trace = backtrace_symbols( trace_buffer, trace_cnt ); + cur->trace_count = trace_cnt; +#endif + + if( ( heap.verify & MEMORY_VERIFY_ALLOC ) && verify_chain() != 0 ) + exit( 1 ); + + return( ( (unsigned char *) cur ) + sizeof(memory_header) ); +} + +static void buffer_alloc_free( void *ptr ) +{ + memory_header *hdr, *old = NULL; + unsigned char *p = (unsigned char *) ptr; + + if( ptr == NULL || heap.buf == NULL || heap.first == NULL ) + return; + + if( p < heap.buf || p > heap.buf + heap.len ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: polarssl_free() outside of managed " + "space\n" ); +#endif + exit( 1 ); + } + + p -= sizeof(memory_header); + hdr = (memory_header *) p; + + if( verify_header( hdr ) != 0 ) + exit( 1 ); + + if( hdr->alloc != 1 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + polarssl_fprintf( stderr, "FATAL: polarssl_free() on unallocated " + "data\n" ); +#endif + exit( 1 ); + } + + hdr->alloc = 0; + +#if defined(POLARSSL_MEMORY_DEBUG) + heap.free_count++; + heap.total_used -= hdr->size; +#endif + + // Regroup with block before + // + if( hdr->prev != NULL && hdr->prev->alloc == 0 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + heap.header_count--; +#endif + hdr->prev->size += sizeof(memory_header) + hdr->size; + hdr->prev->next = hdr->next; + old = hdr; + hdr = hdr->prev; + + if( hdr->next != NULL ) + hdr->next->prev = hdr; + +#if defined(POLARSSL_MEMORY_BACKTRACE) + free( old->trace ); +#endif + memset( old, 0, sizeof(memory_header) ); + } + + // Regroup with block after + // + if( hdr->next != NULL && hdr->next->alloc == 0 ) + { +#if defined(POLARSSL_MEMORY_DEBUG) + heap.header_count--; +#endif + hdr->size += sizeof(memory_header) + hdr->next->size; + old = hdr->next; + hdr->next = hdr->next->next; + + if( hdr->prev_free != NULL || hdr->next_free != NULL ) + { + if( hdr->prev_free != NULL ) + hdr->prev_free->next_free = hdr->next_free; + else + heap.first_free = hdr->next_free; + + if( hdr->next_free != NULL ) + hdr->next_free->prev_free = hdr->prev_free; + } + + hdr->prev_free = old->prev_free; + hdr->next_free = old->next_free; + + if( hdr->prev_free != NULL ) + hdr->prev_free->next_free = hdr; + else + heap.first_free = hdr; + + if( hdr->next_free != NULL ) + hdr->next_free->prev_free = hdr; + + if( hdr->next != NULL ) + hdr->next->prev = hdr; + +#if defined(POLARSSL_MEMORY_BACKTRACE) + free( old->trace ); +#endif + memset( old, 0, sizeof(memory_header) ); + } + + // Prepend to free_list if we have not merged + // (Does not have to stay in same order as prev / next list) + // + if( old == NULL ) + { + hdr->next_free = heap.first_free; + heap.first_free->prev_free = hdr; + heap.first_free = hdr; + } + +#if defined(POLARSSL_MEMORY_BACKTRACE) + hdr->trace = NULL; + hdr->trace_count = 0; +#endif + + if( ( heap.verify & MEMORY_VERIFY_FREE ) && verify_chain() != 0 ) + exit( 1 ); +} + +void memory_buffer_set_verify( int verify ) +{ + heap.verify = verify; +} + +int memory_buffer_alloc_verify() +{ + return verify_chain(); +} + +#if defined(POLARSSL_MEMORY_DEBUG) +void memory_buffer_alloc_status() +{ + polarssl_fprintf( stderr, + "Current use: %u blocks / %u bytes, max: %u blocks / " + "%u bytes (total %u bytes), malloc / free: %u / %u\n", + heap.header_count, heap.total_used, + heap.maximum_header_count, heap.maximum_used, + heap.maximum_header_count * sizeof( memory_header ) + + heap.maximum_used, + heap.malloc_count, heap.free_count ); + + if( heap.first->next == NULL ) + polarssl_fprintf( stderr, "All memory de-allocated in stack buffer\n" ); + else + { + polarssl_fprintf( stderr, "Memory currently allocated:\n" ); + debug_chain(); + } +} +#endif /* POLARSSL_MEMORY_DEBUG */ + +#if defined(POLARSSL_THREADING_C) +static void *buffer_alloc_malloc_mutexed( size_t len ) +{ + void *buf; + polarssl_mutex_lock( &heap.mutex ); + buf = buffer_alloc_malloc( len ); + polarssl_mutex_unlock( &heap.mutex ); + return( buf ); +} + +static void buffer_alloc_free_mutexed( void *ptr ) +{ + polarssl_mutex_lock( &heap.mutex ); + buffer_alloc_free( ptr ); + polarssl_mutex_unlock( &heap.mutex ); +} +#endif /* POLARSSL_THREADING_C */ + +int memory_buffer_alloc_init( unsigned char *buf, size_t len ) +{ + memset( &heap, 0, sizeof(buffer_alloc_ctx) ); + memset( buf, 0, len ); + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_init( &heap.mutex ); + platform_set_malloc_free( buffer_alloc_malloc_mutexed, + buffer_alloc_free_mutexed ); +#else + platform_set_malloc_free( buffer_alloc_malloc, buffer_alloc_free ); +#endif + + if( (size_t) buf % POLARSSL_MEMORY_ALIGN_MULTIPLE ) + { + buf += POLARSSL_MEMORY_ALIGN_MULTIPLE + - (size_t) buf % POLARSSL_MEMORY_ALIGN_MULTIPLE; + len -= (size_t) buf % POLARSSL_MEMORY_ALIGN_MULTIPLE; + } + + heap.buf = buf; + heap.len = len; + + heap.first = (memory_header *) buf; + heap.first->size = len - sizeof(memory_header); + heap.first->magic1 = MAGIC1; + heap.first->magic2 = MAGIC2; + heap.first_free = heap.first; + return( 0 ); +} + +void memory_buffer_alloc_free() +{ +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_free( &heap.mutex ); +#endif + polarssl_zeroize( &heap, sizeof(buffer_alloc_ctx) ); +} + +#endif /* POLARSSL_MEMORY_BUFFER_ALLOC_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/net.c b/component/common/network/ssl/polarssl-1.3.8/library/net.c new file mode 100644 index 0000000..9971758 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/net.c @@ -0,0 +1,599 @@ +/* + * TCP networking functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_NET_C) + +#include "polarssl/net.h" + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + +#if defined(POLARSSL_HAVE_IPV6) +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +/* Enables getaddrinfo() & Co */ +#define _WIN32_WINNT 0x0501 +#include +#endif + +#include +#include + +#if defined(_MSC_VER) +#if defined(_WIN32_WCE) +#pragma comment( lib, "ws2.lib" ) +#else +#pragma comment( lib, "ws2_32.lib" ) +#endif +#endif /* _MSC_VER */ + +#define read(fd,buf,len) recv(fd,(char*)buf,(int) len,0) +#define write(fd,buf,len) send(fd,(char*)buf,(int) len,0) +#define close(fd) closesocket(fd) + +static int wsa_init_done = 0; + +#elif defined(__ICCARM__) || defined(__CC_ARM) + +#include "lwip/sockets.h" +#include "lwip/inet.h" +#if LWIP_DNS +#include "lwip/netdb.h" +#endif +#include + +#else /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +#include +#include +#include +#include +#if defined(POLARSSL_HAVE_TIME) +#include +#endif +#include +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || \ + defined(__DragonFly__) +#include +#elif defined(__APPLE__) || defined(HAVE_MACHINE_ENDIAN_H) || \ + defined(EFIX64) || defined(EFI32) +#include +#elif defined(sun) +#include +#elif defined(_AIX) || defined(HAVE_ARPA_NAMESER_COMPAT_H) +#include +#else +#include +#endif + +#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +#include +#include + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#define snprintf _snprintf +#endif + +#if defined(POLARSSL_HAVE_TIME) +#include +#endif + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +/* + * htons() is not always available. + * By default go for LITTLE_ENDIAN variant. Otherwise hope for _BYTE_ORDER and + * __BIG_ENDIAN to help determine endianness. + */ +#if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN +#define POLARSSL_HTONS(n) (n) +#define POLARSSL_HTONL(n) (n) +#else +#define POLARSSL_HTONS(n) ((((unsigned short)(n) & 0xFF ) << 8 ) | \ + (((unsigned short)(n) & 0xFF00 ) >> 8 )) +#define POLARSSL_HTONL(n) ((((unsigned long )(n) & 0xFF ) << 24) | \ + (((unsigned long )(n) & 0xFF00 ) << 8 ) | \ + (((unsigned long )(n) & 0xFF0000 ) >> 8 ) | \ + (((unsigned long )(n) & 0xFF000000) >> 24)) +#endif + +unsigned short net_htons( unsigned short n ); +unsigned long net_htonl( unsigned long n ); +#define net_htons(n) POLARSSL_HTONS(n) +#define net_htonl(n) POLARSSL_HTONL(n) + +/* + * Prepare for using the sockets interface + */ +static int net_prepare( void ) +{ +#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ + !defined(EFI32) + WSADATA wsaData; + + if( wsa_init_done == 0 ) + { + if( WSAStartup( MAKEWORD(2,0), &wsaData ) != 0 ) + return( POLARSSL_ERR_NET_SOCKET_FAILED ); + + wsa_init_done = 1; + } +#else +#if !defined(EFIX64) && !defined(EFI32) && !defined(__ICCARM__) && !defined(__CC_ARM) + signal( SIGPIPE, SIG_IGN ); +#endif +#endif + return( 0 ); +} + +/* + * Initiate a TCP connection with host:port + */ +int net_connect( int *fd, const char *host, int port ) +{ +#if defined(POLARSSL_HAVE_IPV6) + int ret; + struct addrinfo hints, *addr_list, *cur; + char port_str[6]; + + if( ( ret = net_prepare() ) != 0 ) + return( ret ); + + /* getaddrinfo expects port as a string */ + memset( port_str, 0, sizeof( port_str ) ); + snprintf( port_str, sizeof( port_str ), "%d", port ); + + /* Do name resolution with both IPv6 and IPv4, but only TCP */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if( getaddrinfo( host, port_str, &hints, &addr_list ) != 0 ) + return( POLARSSL_ERR_NET_UNKNOWN_HOST ); + + /* Try the sockaddrs until a connection succeeds */ + ret = POLARSSL_ERR_NET_UNKNOWN_HOST; + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) + { + *fd = (int) socket( cur->ai_family, cur->ai_socktype, + cur->ai_protocol ); + if( *fd < 0 ) + { + ret = POLARSSL_ERR_NET_SOCKET_FAILED; + continue; + } + + if( connect( *fd, cur->ai_addr, cur->ai_addrlen ) == 0 ) + { + ret = 0; + break; + } + + close( *fd ); + ret = POLARSSL_ERR_NET_CONNECT_FAILED; + } + + freeaddrinfo( addr_list ); + + return( ret ); + +#else + /* Legacy IPv4-only version */ + + int ret; + struct sockaddr_in server_addr; +#if LWIP_DNS + struct hostent *server_host; +#endif + if( ( ret = net_prepare() ) != 0 ) + return( ret ); +#if LWIP_DNS + if( ( server_host = gethostbyname( host ) ) == NULL ) + return( POLARSSL_ERR_NET_UNKNOWN_HOST ); + + if( ( *fd = (int) socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) + return( POLARSSL_ERR_NET_SOCKET_FAILED ); + + memcpy( (void *) &server_addr.sin_addr, + (void *) server_host->h_addr, + server_host->h_length ); +#else + if( ( *fd = (int) socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) + return( POLARSSL_ERR_NET_SOCKET_FAILED ); + + server_addr.sin_len = sizeof(server_addr); + server_addr.sin_addr.s_addr = inet_addr(host); +#endif + + server_addr.sin_family = AF_INET; + server_addr.sin_port = net_htons( port ); + + if( connect( *fd, (struct sockaddr *) &server_addr, + sizeof( server_addr ) ) < 0 ) + { + close( *fd ); + return( POLARSSL_ERR_NET_CONNECT_FAILED ); + } + + return( 0 ); +#endif /* POLARSSL_HAVE_IPV6 */ +} + +/* + * Create a listening socket on bind_ip:port + */ +int net_bind( int *fd, const char *bind_ip, int port ) +{ +#if defined(POLARSSL_HAVE_IPV6) + int n, ret; + struct addrinfo hints, *addr_list, *cur; + char port_str[6]; + + if( ( ret = net_prepare() ) != 0 ) + return( ret ); + + /* getaddrinfo expects port as a string */ + memset( port_str, 0, sizeof( port_str ) ); + snprintf( port_str, sizeof( port_str ), "%d", port ); + + /* Bind to IPv6 and/or IPv4, but only in TCP */ + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if( bind_ip == NULL ) + hints.ai_flags = AI_PASSIVE; + + if( getaddrinfo( bind_ip, port_str, &hints, &addr_list ) != 0 ) + return( POLARSSL_ERR_NET_UNKNOWN_HOST ); + + /* Try the sockaddrs until a binding succeeds */ + ret = POLARSSL_ERR_NET_UNKNOWN_HOST; + for( cur = addr_list; cur != NULL; cur = cur->ai_next ) + { + *fd = (int) socket( cur->ai_family, cur->ai_socktype, + cur->ai_protocol ); + if( *fd < 0 ) + { + ret = POLARSSL_ERR_NET_SOCKET_FAILED; + continue; + } + + n = 1; + if( setsockopt( *fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &n, sizeof( n ) ) != 0 ) + { + close( *fd ); + ret = POLARSSL_ERR_NET_SOCKET_FAILED; + continue; + } + + if( bind( *fd, cur->ai_addr, cur->ai_addrlen ) != 0 ) + { + close( *fd ); + ret = POLARSSL_ERR_NET_BIND_FAILED; + continue; + } + + if( listen( *fd, POLARSSL_NET_LISTEN_BACKLOG ) != 0 ) + { + close( *fd ); + ret = POLARSSL_ERR_NET_LISTEN_FAILED; + continue; + } + + /* I we ever get there, it's a success */ + ret = 0; + break; + } + + freeaddrinfo( addr_list ); + + return( ret ); + +#else + /* Legacy IPv4-only version */ + + int ret, n, c[4]; + struct sockaddr_in server_addr; + + if( ( ret = net_prepare() ) != 0 ) + return( ret ); + + if( ( *fd = (int) socket( AF_INET, SOCK_STREAM, IPPROTO_IP ) ) < 0 ) + return( POLARSSL_ERR_NET_SOCKET_FAILED ); + + n = 1; + setsockopt( *fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &n, sizeof( n ) ); + + server_addr.sin_addr.s_addr = net_htonl( INADDR_ANY ); + server_addr.sin_family = AF_INET; + server_addr.sin_port = net_htons( port ); + + if( bind_ip != NULL ) + { + memset( c, 0, sizeof( c ) ); + sscanf( bind_ip, "%d.%d.%d.%d", &c[0], &c[1], &c[2], &c[3] ); + + for( n = 0; n < 4; n++ ) + if( c[n] < 0 || c[n] > 255 ) + break; + + if( n == 4 ) + server_addr.sin_addr.s_addr = net_htonl( + ( (uint32_t) c[0] << 24 ) | + ( (uint32_t) c[1] << 16 ) | + ( (uint32_t) c[2] << 8 ) | + ( (uint32_t) c[3] ) ); + } + + if( bind( *fd, (struct sockaddr *) &server_addr, + sizeof( server_addr ) ) < 0 ) + { + close( *fd ); + return( POLARSSL_ERR_NET_BIND_FAILED ); + } + + if( listen( *fd, POLARSSL_NET_LISTEN_BACKLOG ) != 0 ) + { + close( *fd ); + return( POLARSSL_ERR_NET_LISTEN_FAILED ); + } + + return( 0 ); +#endif /* POLARSSL_HAVE_IPV6 */ +} + +#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ + !defined(EFI32) +/* + * Check if the requested operation would be blocking on a non-blocking socket + * and thus 'failed' with a negative return value. + */ +static int net_would_block( int fd ) +{ + ((void) fd); + return( WSAGetLastError() == WSAEWOULDBLOCK ); +} +#else +/* + * Check if the requested operation would be blocking on a non-blocking socket + * and thus 'failed' with a negative return value. + * + * Note: on a blocking socket this function always returns 0! + */ +static int net_would_block( int fd ) +{ +#if 0 + /* + * Never return 'WOULD BLOCK' on a non-blocking socket + */ + if( ( fcntl( fd, F_GETFL ) & O_NONBLOCK ) != O_NONBLOCK ) + return( 0 ); + + switch( errno ) + { +#if defined EAGAIN + case EAGAIN: +#endif +#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + return( 1 ); + } +#endif + return( 0 ); +} +#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +/* + * Accept a connection from a remote client + */ +int net_accept( int bind_fd, int *client_fd, void *client_ip ) +{ +#if defined(POLARSSL_HAVE_IPV6) + struct sockaddr_storage client_addr; +#else + struct sockaddr_in client_addr; +#endif + +#if defined(__socklen_t_defined) || defined(_SOCKLEN_T) || \ + defined(_SOCKLEN_T_DECLARED) + socklen_t n = (socklen_t) sizeof( client_addr ); +#else + int n = (int) sizeof( client_addr ); +#endif + + *client_fd = (int) accept( bind_fd, (struct sockaddr *) + &client_addr, &n ); + + if( *client_fd < 0 ) + { + if( net_would_block( *client_fd ) != 0 ) + return( POLARSSL_ERR_NET_WANT_READ ); + + return( POLARSSL_ERR_NET_ACCEPT_FAILED ); + } + + if( client_ip != NULL ) + { +#if defined(POLARSSL_HAVE_IPV6) + if( client_addr.ss_family == AF_INET ) + { + struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr; + memcpy( client_ip, &addr4->sin_addr.s_addr, + sizeof( addr4->sin_addr.s_addr ) ); + } + else + { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr; + memcpy( client_ip, &addr6->sin6_addr.s6_addr, + sizeof( addr6->sin6_addr.s6_addr ) ); + } +#else + memcpy( client_ip, &client_addr.sin_addr.s_addr, + sizeof( client_addr.sin_addr.s_addr ) ); +#endif /* POLARSSL_HAVE_IPV6 */ + } + + return( 0 ); +} + +/* + * Set the socket blocking or non-blocking + */ +int net_set_block( int fd ) +{ +#if ( defined(_WIN32) || defined(_WIN32_WCE) || defined(__ICCARM__) || defined(__CC_ARM)) && !defined(EFIX64) && \ + !defined(EFI32) + unsigned long n = 0; + return( ioctlsocket( fd, FIONBIO, &n ) ); +#else + return( fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) & ~O_NONBLOCK ) ); +#endif +} + +int net_set_nonblock( int fd ) +{ +#if ( defined(_WIN32) || defined(_WIN32_WCE) || defined(__ICCARM__) || defined(__CC_ARM)) && !defined(EFIX64) && \ + !defined(EFI32) + unsigned long n = 1; + return( ioctlsocket( fd, FIONBIO, &n ) ); +#else + return( fcntl( fd, F_SETFL, fcntl( fd, F_GETFL ) | O_NONBLOCK ) ); +#endif +} + +#if defined(POLARSSL_HAVE_TIME) +/* + * Portable usleep helper + */ +void net_usleep( unsigned long usec ) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = usec; + select( 0, NULL, NULL, NULL, &tv ); +} +#endif /* POLARSSL_HAVE_TIME */ + +/* + * Read at most 'len' characters + */ +int net_recv( void *ctx, unsigned char *buf, size_t len ) +{ + int fd = *((int *) ctx); + int ret = read( fd, buf, len ); + + if( ret < 0 ) + { + if( net_would_block( fd ) != 0 ) + return( POLARSSL_ERR_NET_WANT_READ ); + +#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ + !defined(EFI32) + if( WSAGetLastError() == WSAECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); +#else +#ifdef ERRNO + if( errno == EPIPE || errno == ECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); + + if( errno == EINTR ) + return( POLARSSL_ERR_NET_WANT_READ ); +#endif +#endif + + return( POLARSSL_ERR_NET_RECV_FAILED ); + } + + return( ret ); +} + +/* + * Write at most 'len' characters + */ +int net_send( void *ctx, const unsigned char *buf, size_t len ) +{ + int fd = *((int *) ctx); + int ret = write( fd, buf, len ); + + if( ret < 0 ) + { + if( net_would_block( fd ) != 0 ) + return( POLARSSL_ERR_NET_WANT_WRITE ); + +#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \ + !defined(EFI32) + if( WSAGetLastError() == WSAECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); +#else +#ifdef ERRNO + if( errno == EPIPE || errno == ECONNRESET ) + return( POLARSSL_ERR_NET_CONN_RESET ); + + if( errno == EINTR ) + return( POLARSSL_ERR_NET_WANT_WRITE ); +#endif +#endif + + return( POLARSSL_ERR_NET_SEND_FAILED ); + } + + return( ret ); +} + +/* + * Gracefully close the connection + */ +void net_close( int fd ) +{ + shutdown( fd, 2 ); + close( fd ); +} + +#endif /* POLARSSL_NET_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/oid.c b/component/common/network/ssl/polarssl-1.3.8/library/oid.c new file mode 100644 index 0000000..f0afafe --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/oid.c @@ -0,0 +1,684 @@ +/** + * \file oid.c + * + * \brief Object Identifier (OID) database + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_OID_C) + +#include "polarssl/oid.h" +#include "polarssl/rsa.h" + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +#include "polarssl/x509.h" +#endif + +#include + +/* + * Macro to automatically add the size of #define'd OIDs + */ +#define ADD_LEN(s) s, OID_SIZE(s) + +/* + * Macro to generate an internal function for oid_XXX_from_asn1() (used by + * the other functions) + */ +#define FN_OID_TYPED_FROM_ASN1( TYPE_T, NAME, LIST ) \ +static const TYPE_T * oid_ ## NAME ## _from_asn1( const asn1_buf *oid ) \ +{ \ + const TYPE_T *p = LIST; \ + const oid_descriptor_t *cur = (const oid_descriptor_t *) p; \ + if( p == NULL || oid == NULL ) return( NULL ); \ + while( cur->asn1 != NULL ) { \ + if( cur->asn1_len == oid->len && \ + memcmp( cur->asn1, oid->p, oid->len ) == 0 ) { \ + return( p ); \ + } \ + p++; \ + cur = (const oid_descriptor_t *) p; \ + } \ + return( NULL ); \ +} + +/* + * Macro to generate a function for retrieving a single attribute from the + * descriptor of an oid_descriptor_t wrapper. + */ +#define FN_OID_GET_DESCRIPTOR_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \ +int FN_NAME( const asn1_buf *oid, ATTR1_TYPE * ATTR1 ) \ +{ \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid ); \ + if( data == NULL ) return( POLARSSL_ERR_OID_NOT_FOUND ); \ + *ATTR1 = data->descriptor.ATTR1; \ + return( 0 ); \ +} + +/* + * Macro to generate a function for retrieving a single attribute from an + * oid_descriptor_t wrapper. + */ +#define FN_OID_GET_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \ +int FN_NAME( const asn1_buf *oid, ATTR1_TYPE * ATTR1 ) \ +{ \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid ); \ + if( data == NULL ) return( POLARSSL_ERR_OID_NOT_FOUND ); \ + *ATTR1 = data->ATTR1; \ + return( 0 ); \ +} + +/* + * Macro to generate a function for retrieving two attributes from an + * oid_descriptor_t wrapper. + */ +#define FN_OID_GET_ATTR2(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1, \ + ATTR2_TYPE, ATTR2) \ +int FN_NAME( const asn1_buf *oid, ATTR1_TYPE * ATTR1, ATTR2_TYPE * ATTR2 ) \ +{ \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1( oid ); \ + if( data == NULL ) return( POLARSSL_ERR_OID_NOT_FOUND ); \ + *ATTR1 = data->ATTR1; \ + *ATTR2 = data->ATTR2; \ + return( 0 ); \ +} + +/* + * Macro to generate a function for retrieving the OID based on a single + * attribute from a oid_descriptor_t wrapper. + */ +#define FN_OID_GET_OID_BY_ATTR1(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1) \ +int FN_NAME( ATTR1_TYPE ATTR1, const char **oid, size_t *olen ) \ +{ \ + const TYPE_T *cur = LIST; \ + while( cur->descriptor.asn1 != NULL ) { \ + if( cur->ATTR1 == ATTR1 ) { \ + *oid = cur->descriptor.asn1; \ + *olen = cur->descriptor.asn1_len; \ + return( 0 ); \ + } \ + cur++; \ + } \ + return( POLARSSL_ERR_OID_NOT_FOUND ); \ +} + +/* + * Macro to generate a function for retrieving the OID based on two + * attributes from a oid_descriptor_t wrapper. + */ +#define FN_OID_GET_OID_BY_ATTR2(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1, \ + ATTR2_TYPE, ATTR2) \ +int FN_NAME( ATTR1_TYPE ATTR1, ATTR2_TYPE ATTR2, const char **oid , \ + size_t *olen ) \ +{ \ + const TYPE_T *cur = LIST; \ + while( cur->descriptor.asn1 != NULL ) { \ + if( cur->ATTR1 == ATTR1 && cur->ATTR2 == ATTR2 ) { \ + *oid = cur->descriptor.asn1; \ + *olen = cur->descriptor.asn1_len; \ + return( 0 ); \ + } \ + cur++; \ + } \ + return( POLARSSL_ERR_OID_NOT_FOUND ); \ +} + +/* + * For X520 attribute types + */ +typedef struct { + oid_descriptor_t descriptor; + const char *short_name; +} oid_x520_attr_t; + +static const oid_x520_attr_t oid_x520_attr_type[] = +{ + { + { ADD_LEN( OID_AT_CN ), "id-at-commonName", "Common Name" }, + "CN", + }, + { + { ADD_LEN( OID_AT_COUNTRY ), "id-at-countryName", "Country" }, + "C", + }, + { + { ADD_LEN( OID_AT_LOCALITY ), "id-at-locality", "Locality" }, + "L", + }, + { + { ADD_LEN( OID_AT_STATE ), "id-at-state", "State" }, + "ST", + }, + { + { ADD_LEN( OID_AT_ORGANIZATION ),"id-at-organizationName", "Organization" }, + "O", + }, + { + { ADD_LEN( OID_AT_ORG_UNIT ), "id-at-organizationalUnitName", "Org Unit" }, + "OU", + }, + { + { ADD_LEN( OID_PKCS9_EMAIL ), "emailAddress", "E-mail address" }, + "emailAddress", + }, + { + { ADD_LEN( OID_AT_SERIAL_NUMBER ),"id-at-serialNumber", "Serial number" }, + "serialNumber", + }, + { + { ADD_LEN( OID_AT_POSTAL_ADDRESS ),"id-at-postalAddress", "Postal address" }, + "postalAddress", + }, + { + { ADD_LEN( OID_AT_POSTAL_CODE ), "id-at-postalCode", "Postal code" }, + "postalCode", + }, + { + { ADD_LEN( OID_AT_SUR_NAME ), "id-at-surName", "Surname" }, + "SN", + }, + { + { ADD_LEN( OID_AT_GIVEN_NAME ), "id-at-givenName", "Given name" }, + "GN", + }, + { + { ADD_LEN( OID_AT_INITIALS ), "id-at-initials", "Initials" }, + "initials", + }, + { + { ADD_LEN( OID_AT_GENERATION_QUALIFIER ), "id-at-generationQualifier", "Generation qualifier" }, + "generationQualifier", + }, + { + { ADD_LEN( OID_AT_TITLE ), "id-at-title", "Title" }, + "title", + }, + { + { ADD_LEN( OID_AT_DN_QUALIFIER ),"id-at-dnQualifier", "Distinguished Name qualifier" }, + "dnQualifier", + }, + { + { ADD_LEN( OID_AT_PSEUDONYM ), "id-at-pseudonym", "Pseudonym" }, + "pseudonym", + }, + { + { ADD_LEN( OID_DOMAIN_COMPONENT ), "id-domainComponent", "Domain component" }, + "DC", + }, + { + { NULL, 0, NULL, NULL }, + NULL, + } +}; + +FN_OID_TYPED_FROM_ASN1(oid_x520_attr_t, x520_attr, oid_x520_attr_type); +FN_OID_GET_ATTR1(oid_get_attr_short_name, oid_x520_attr_t, x520_attr, const char *, short_name); + +#if defined(POLARSSL_X509_USE_C) || defined(POLARSSL_X509_CREATE_C) +/* + * For X509 extensions + */ +typedef struct { + oid_descriptor_t descriptor; + int ext_type; +} oid_x509_ext_t; + +static const oid_x509_ext_t oid_x509_ext[] = +{ + { + { ADD_LEN( OID_BASIC_CONSTRAINTS ), "id-ce-basicConstraints", "Basic Constraints" }, + EXT_BASIC_CONSTRAINTS, + }, + { + { ADD_LEN( OID_KEY_USAGE ), "id-ce-keyUsage", "Key Usage" }, + EXT_KEY_USAGE, + }, + { + { ADD_LEN( OID_EXTENDED_KEY_USAGE ), "id-ce-keyUsage", "Extended Key Usage" }, + EXT_EXTENDED_KEY_USAGE, + }, + { + { ADD_LEN( OID_SUBJECT_ALT_NAME ), "id-ce-subjectAltName", "Subject Alt Name" }, + EXT_SUBJECT_ALT_NAME, + }, + { + { ADD_LEN( OID_NS_CERT_TYPE ), "id-netscape-certtype", "Netscape Certificate Type" }, + EXT_NS_CERT_TYPE, + }, + { + { NULL, 0, NULL, NULL }, + 0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_x509_ext_t, x509_ext, oid_x509_ext); +FN_OID_GET_ATTR1(oid_get_x509_ext_type, oid_x509_ext_t, x509_ext, int, ext_type); + +static const oid_descriptor_t oid_ext_key_usage[] = +{ + { ADD_LEN( OID_SERVER_AUTH ), "id-kp-serverAuth", "TLS Web Server Authentication" }, + { ADD_LEN( OID_CLIENT_AUTH ), "id-kp-clientAuth", "TLS Web Client Authentication" }, + { ADD_LEN( OID_CODE_SIGNING ), "id-kp-codeSigning", "Code Signing" }, + { ADD_LEN( OID_EMAIL_PROTECTION ), "id-kp-emailProtection", "E-mail Protection" }, + { ADD_LEN( OID_TIME_STAMPING ), "id-kp-timeStamping", "Time Stamping" }, + { ADD_LEN( OID_OCSP_SIGNING ), "id-kp-OCSPSigning", "OCSP Signing" }, + { NULL, 0, NULL, NULL }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_descriptor_t, ext_key_usage, oid_ext_key_usage); +FN_OID_GET_ATTR1(oid_get_extended_key_usage, oid_descriptor_t, ext_key_usage, const char *, description); +#endif /* POLARSSL_X509_USE_C || POLARSSL_X509_CREATE_C */ + +#if defined(POLARSSL_MD_C) +/* + * For SignatureAlgorithmIdentifier + */ +typedef struct { + oid_descriptor_t descriptor; + md_type_t md_alg; + pk_type_t pk_alg; +} oid_sig_alg_t; + +static const oid_sig_alg_t oid_sig_alg[] = +{ + { + { ADD_LEN( OID_PKCS1_MD2 ), "md2WithRSAEncryption", "RSA with MD2" }, + POLARSSL_MD_MD2, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_MD4 ), "md4WithRSAEncryption", "RSA with MD4" }, + POLARSSL_MD_MD4, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_MD5 ), "md5WithRSAEncryption", "RSA with MD5" }, + POLARSSL_MD_MD5, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_SHA1 ), "sha-1WithRSAEncryption", "RSA with SHA1" }, + POLARSSL_MD_SHA1, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_SHA224 ), "sha224WithRSAEncryption", "RSA with SHA-224" }, + POLARSSL_MD_SHA224, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_SHA256 ), "sha256WithRSAEncryption", "RSA with SHA-256" }, + POLARSSL_MD_SHA256, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_SHA384 ), "sha384WithRSAEncryption", "RSA with SHA-384" }, + POLARSSL_MD_SHA384, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_PKCS1_SHA512 ), "sha512WithRSAEncryption", "RSA with SHA-512" }, + POLARSSL_MD_SHA512, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_RSA_SHA_OBS ), "sha-1WithRSAEncryption", "RSA with SHA1" }, + POLARSSL_MD_SHA1, POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_ECDSA_SHA1 ), "ecdsa-with-SHA1", "ECDSA with SHA1" }, + POLARSSL_MD_SHA1, POLARSSL_PK_ECDSA, + }, + { + { ADD_LEN( OID_ECDSA_SHA224 ), "ecdsa-with-SHA224", "ECDSA with SHA224" }, + POLARSSL_MD_SHA224, POLARSSL_PK_ECDSA, + }, + { + { ADD_LEN( OID_ECDSA_SHA256 ), "ecdsa-with-SHA256", "ECDSA with SHA256" }, + POLARSSL_MD_SHA256, POLARSSL_PK_ECDSA, + }, + { + { ADD_LEN( OID_ECDSA_SHA384 ), "ecdsa-with-SHA384", "ECDSA with SHA384" }, + POLARSSL_MD_SHA384, POLARSSL_PK_ECDSA, + }, + { + { ADD_LEN( OID_ECDSA_SHA512 ), "ecdsa-with-SHA512", "ECDSA with SHA512" }, + POLARSSL_MD_SHA512, POLARSSL_PK_ECDSA, + }, + { + { ADD_LEN( OID_RSASSA_PSS ), "RSASSA-PSS", "RSASSA-PSS" }, + POLARSSL_MD_NONE, POLARSSL_PK_RSASSA_PSS, + }, + { + { NULL, 0, NULL, NULL }, + (md_type_t)0, (pk_type_t)0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_sig_alg_t, sig_alg, oid_sig_alg); +FN_OID_GET_DESCRIPTOR_ATTR1(oid_get_sig_alg_desc, oid_sig_alg_t, sig_alg, const char *, description); +FN_OID_GET_ATTR2(oid_get_sig_alg, oid_sig_alg_t, sig_alg, md_type_t, md_alg, pk_type_t, pk_alg); +FN_OID_GET_OID_BY_ATTR2(oid_get_oid_by_sig_alg, oid_sig_alg_t, oid_sig_alg, pk_type_t, pk_alg, md_type_t, md_alg); +#endif /* POLARSSL_MD_C */ + +/* + * For PublicKeyInfo (PKCS1, RFC 5480) + */ +typedef struct { + oid_descriptor_t descriptor; + pk_type_t pk_alg; +} oid_pk_alg_t; + +static const oid_pk_alg_t oid_pk_alg[] = +{ + { + { ADD_LEN( OID_PKCS1_RSA ), "rsaEncryption", "RSA" }, + POLARSSL_PK_RSA, + }, + { + { ADD_LEN( OID_EC_ALG_UNRESTRICTED ), "id-ecPublicKey", "Generic EC key" }, + POLARSSL_PK_ECKEY, + }, + { + { ADD_LEN( OID_EC_ALG_ECDH ), "id-ecDH", "EC key for ECDH" }, + POLARSSL_PK_ECKEY_DH, + }, + { + { NULL, 0, NULL, NULL }, + (pk_type_t)0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_pk_alg_t, pk_alg, oid_pk_alg); +FN_OID_GET_ATTR1(oid_get_pk_alg, oid_pk_alg_t, pk_alg, pk_type_t, pk_alg); +FN_OID_GET_OID_BY_ATTR1(oid_get_oid_by_pk_alg, oid_pk_alg_t, oid_pk_alg, pk_type_t, pk_alg); + +#if defined(POLARSSL_ECP_C) +/* + * For namedCurve (RFC 5480) + */ +typedef struct { + oid_descriptor_t descriptor; + ecp_group_id grp_id; +} oid_ecp_grp_t; + +static const oid_ecp_grp_t oid_ecp_grp[] = +{ + { + { ADD_LEN( OID_EC_GRP_SECP192R1 ), "secp192r1", "secp192r1" }, + POLARSSL_ECP_DP_SECP192R1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP224R1 ), "secp224r1", "secp224r1" }, + POLARSSL_ECP_DP_SECP224R1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP256R1 ), "secp256r1", "secp256r1" }, + POLARSSL_ECP_DP_SECP256R1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP384R1 ), "secp384r1", "secp384r1" }, + POLARSSL_ECP_DP_SECP384R1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP521R1 ), "secp521r1", "secp521r1" }, + POLARSSL_ECP_DP_SECP521R1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP192K1 ), "secp192k1", "secp192k1" }, + POLARSSL_ECP_DP_SECP192K1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP224K1 ), "secp224k1", "secp224k1" }, + POLARSSL_ECP_DP_SECP224K1, + }, + { + { ADD_LEN( OID_EC_GRP_SECP256K1 ), "secp256k1", "secp256k1" }, + POLARSSL_ECP_DP_SECP256K1, + }, + { + { ADD_LEN( OID_EC_GRP_BP256R1 ), "brainpoolP256r1","brainpool256r1" }, + POLARSSL_ECP_DP_BP256R1, + }, + { + { ADD_LEN( OID_EC_GRP_BP384R1 ), "brainpoolP384r1","brainpool384r1" }, + POLARSSL_ECP_DP_BP384R1, + }, + { + { ADD_LEN( OID_EC_GRP_BP512R1 ), "brainpoolP512r1","brainpool512r1" }, + POLARSSL_ECP_DP_BP512R1, + }, + { + { NULL, 0, NULL, NULL }, + 0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_ecp_grp_t, grp_id, oid_ecp_grp); +FN_OID_GET_ATTR1(oid_get_ec_grp, oid_ecp_grp_t, grp_id, ecp_group_id, grp_id); +FN_OID_GET_OID_BY_ATTR1(oid_get_oid_by_ec_grp, oid_ecp_grp_t, oid_ecp_grp, ecp_group_id, grp_id); +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_CIPHER_C) +/* + * For PKCS#5 PBES2 encryption algorithm + */ +typedef struct { + oid_descriptor_t descriptor; + cipher_type_t cipher_alg; +} oid_cipher_alg_t; + +static const oid_cipher_alg_t oid_cipher_alg[] = +{ + { + { ADD_LEN( OID_DES_CBC ), "desCBC", "DES-CBC" }, + POLARSSL_CIPHER_DES_CBC, + }, + { + { ADD_LEN( OID_DES_EDE3_CBC ), "des-ede3-cbc", "DES-EDE3-CBC" }, + POLARSSL_CIPHER_DES_EDE3_CBC, + }, + { + { NULL, 0, NULL, NULL }, + (cipher_type_t)0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_cipher_alg_t, cipher_alg, oid_cipher_alg); +FN_OID_GET_ATTR1(oid_get_cipher_alg, oid_cipher_alg_t, cipher_alg, cipher_type_t, cipher_alg); +#endif /* POLARSSL_CIPHER_C */ + +#if defined(POLARSSL_MD_C) +/* + * For digestAlgorithm + */ +typedef struct { + oid_descriptor_t descriptor; + md_type_t md_alg; +} oid_md_alg_t; + +static const oid_md_alg_t oid_md_alg[] = +{ + { + { ADD_LEN( OID_DIGEST_ALG_MD2 ), "id-md2", "MD2" }, + POLARSSL_MD_MD2, + }, + { + { ADD_LEN( OID_DIGEST_ALG_MD4 ), "id-md4", "MD4" }, + POLARSSL_MD_MD4, + }, + { + { ADD_LEN( OID_DIGEST_ALG_MD5 ), "id-md5", "MD5" }, + POLARSSL_MD_MD5, + }, + { + { ADD_LEN( OID_DIGEST_ALG_SHA1 ), "id-sha1", "SHA-1" }, + POLARSSL_MD_SHA1, + }, + { + { ADD_LEN( OID_DIGEST_ALG_SHA224 ), "id-sha224", "SHA-224" }, + POLARSSL_MD_SHA224, + }, + { + { ADD_LEN( OID_DIGEST_ALG_SHA256 ), "id-sha256", "SHA-256" }, + POLARSSL_MD_SHA256, + }, + { + { ADD_LEN( OID_DIGEST_ALG_SHA384 ), "id-sha384", "SHA-384" }, + POLARSSL_MD_SHA384, + }, + { + { ADD_LEN( OID_DIGEST_ALG_SHA512 ), "id-sha512", "SHA-512" }, + POLARSSL_MD_SHA512, + }, + { + { NULL, 0, NULL, NULL }, + (md_type_t)0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg); +FN_OID_GET_ATTR1(oid_get_md_alg, oid_md_alg_t, md_alg, md_type_t, md_alg); +FN_OID_GET_OID_BY_ATTR1(oid_get_oid_by_md, oid_md_alg_t, oid_md_alg, md_type_t, md_alg); +#endif /* POLARSSL_MD_C */ + +#if defined(POLARSSL_PKCS12_C) +/* + * For PKCS#12 PBEs + */ +typedef struct { + oid_descriptor_t descriptor; + md_type_t md_alg; + cipher_type_t cipher_alg; +} oid_pkcs12_pbe_alg_t; + +static const oid_pkcs12_pbe_alg_t oid_pkcs12_pbe_alg[] = +{ + { + { ADD_LEN( OID_PKCS12_PBE_SHA1_DES3_EDE_CBC ), "pbeWithSHAAnd3-KeyTripleDES-CBC", "PBE with SHA1 and 3-Key 3DES" }, + POLARSSL_MD_SHA1, POLARSSL_CIPHER_DES_EDE3_CBC, + }, + { + { ADD_LEN( OID_PKCS12_PBE_SHA1_DES2_EDE_CBC ), "pbeWithSHAAnd2-KeyTripleDES-CBC", "PBE with SHA1 and 2-Key 3DES" }, + POLARSSL_MD_SHA1, POLARSSL_CIPHER_DES_EDE_CBC, + }, + { + { NULL, 0, NULL, NULL }, + 0, 0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_pkcs12_pbe_alg_t, pkcs12_pbe_alg, oid_pkcs12_pbe_alg); +FN_OID_GET_ATTR2(oid_get_pkcs12_pbe_alg, oid_pkcs12_pbe_alg_t, pkcs12_pbe_alg, md_type_t, md_alg, cipher_type_t, cipher_alg); +#endif /* POLARSSL_PKCS12_C */ + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +static int compat_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if( res < 0 ) + return( (int) size + 20 ); + + return( res ); +} + +#define snprintf compat_snprintf +#endif /* _MSC_VER && !snprintf && !EFIX64 && !EFI32 */ + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( POLARSSL_ERR_OID_BUF_TOO_SMALL ); \ + \ + if( (unsigned int) ret >= n ) { \ + p[n - 1] = '\0'; \ + return( POLARSSL_ERR_OID_BUF_TOO_SMALL ); \ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +/* Return the x.y.z.... style numeric string for the given OID */ +int oid_get_numeric_string( char *buf, size_t size, + const asn1_buf *oid ) +{ + int ret; + size_t i, n; + unsigned int value; + char *p; + + p = buf; + n = size; + + /* First byte contains first two dots */ + if( oid->len > 0 ) + { + ret = snprintf( p, n, "%d.%d", oid->p[0] / 40, oid->p[0] % 40 ); + SAFE_SNPRINTF(); + } + + value = 0; + for( i = 1; i < oid->len; i++ ) + { + /* Prevent overflow in value. */ + if( ( ( value << 7 ) >> 7 ) != value ) + return( POLARSSL_ERR_OID_BUF_TOO_SMALL ); + + value <<= 7; + value += oid->p[i] & 0x7F; + + if( !( oid->p[i] & 0x80 ) ) + { + /* Last byte */ + ret = snprintf( p, n, ".%d", value ); + SAFE_SNPRINTF(); + value = 0; + } + } + + return( (int) ( size - n ) ); +} + +#endif /* POLARSSL_OID_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/padlock.c b/component/common/network/ssl/polarssl-1.3.8/library/padlock.c new file mode 100644 index 0000000..5d06390 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/padlock.c @@ -0,0 +1,168 @@ +/* + * VIA PadLock support functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * This implementation is based on the VIA PadLock Programming Guide: + * + * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/ + * programming_guide.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PADLOCK_C) + +#include "polarssl/padlock.h" + +#if defined(POLARSSL_HAVE_X86) + +/* + * PadLock detection routine + */ +int padlock_supports( int feature ) +{ + static int flags = -1; + int ebx = 0, edx = 0; + + if( flags == -1 ) + { + asm( "movl %%ebx, %0 \n\t" + "movl $0xC0000000, %%eax \n\t" + "cpuid \n\t" + "cmpl $0xC0000001, %%eax \n\t" + "movl $0, %%edx \n\t" + "jb unsupported \n\t" + "movl $0xC0000001, %%eax \n\t" + "cpuid \n\t" + "unsupported: \n\t" + "movl %%edx, %1 \n\t" + "movl %2, %%ebx \n\t" + : "=m" (ebx), "=m" (edx) + : "m" (ebx) + : "eax", "ecx", "edx" ); + + flags = edx; + } + + return( flags & feature ); +} + +/* + * PadLock AES-ECB block en(de)cryption + */ +int padlock_xcryptecb( aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16] ) +{ + int ebx = 0; + uint32_t *rk; + uint32_t *blk; + uint32_t *ctrl; + unsigned char buf[256]; + + rk = ctx->rk; + blk = PADLOCK_ALIGN16( buf ); + memcpy( blk, input, 16 ); + + ctrl = blk + 4; + *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + ( mode^1 ) - 10 ) << 9 ); + + asm( "pushfl \n\t" + "popfl \n\t" + "movl %%ebx, %0 \n\t" + "movl $1, %%ecx \n\t" + "movl %2, %%edx \n\t" + "movl %3, %%ebx \n\t" + "movl %4, %%esi \n\t" + "movl %4, %%edi \n\t" + ".byte 0xf3,0x0f,0xa7,0xc8 \n\t" + "movl %1, %%ebx \n\t" + : "=m" (ebx) + : "m" (ebx), "m" (ctrl), "m" (rk), "m" (blk) + : "ecx", "edx", "esi", "edi" ); + + memcpy( output, blk, 16 ); + + return( 0 ); +} + +/* + * PadLock AES-CBC buffer en(de)cryption + */ +int padlock_xcryptcbc( aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output ) +{ + int ebx = 0; + size_t count; + uint32_t *rk; + uint32_t *iw; + uint32_t *ctrl; + unsigned char buf[256]; + + if( ( (long) input & 15 ) != 0 || + ( (long) output & 15 ) != 0 ) + return( POLARSSL_ERR_PADLOCK_DATA_MISALIGNED ); + + rk = ctx->rk; + iw = PADLOCK_ALIGN16( buf ); + memcpy( iw, iv, 16 ); + + ctrl = iw + 4; + *ctrl = 0x80 | ctx->nr | ( ( ctx->nr + ( mode ^ 1 ) - 10 ) << 9 ); + + count = ( length + 15 ) >> 4; + + asm( "pushfl \n\t" + "popfl \n\t" + "movl %%ebx, %0 \n\t" + "movl %2, %%ecx \n\t" + "movl %3, %%edx \n\t" + "movl %4, %%ebx \n\t" + "movl %5, %%esi \n\t" + "movl %6, %%edi \n\t" + "movl %7, %%eax \n\t" + ".byte 0xf3,0x0f,0xa7,0xd0 \n\t" + "movl %1, %%ebx \n\t" + : "=m" (ebx) + : "m" (ebx), "m" (count), "m" (ctrl), + "m" (rk), "m" (input), "m" (output), "m" (iw) + : "eax", "ecx", "edx", "esi", "edi" ); + + memcpy( iv, iw, 16 ); + + return( 0 ); +} + +#endif /* POLARSSL_HAVE_X86 */ + +#endif /* POLARSSL_PADLOCK_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pbkdf2.c b/component/common/network/ssl/polarssl-1.3.8/library/pbkdf2.c new file mode 100644 index 0000000..e76f066 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pbkdf2.c @@ -0,0 +1,64 @@ +/** + * \file pbkdf2.c + * + * \brief Password-Based Key Derivation Function 2 (from PKCS#5) + * DEPRECATED: Use pkcs5.c instead + * + * \author Mathias Olsson + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * PBKDF2 is part of PKCS#5 + * + * http://tools.ietf.org/html/rfc2898 (Specification) + * http://tools.ietf.org/html/rfc6070 (Test vectors) + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PBKDF2_C) + +#include "polarssl/pbkdf2.h" +#include "polarssl/pkcs5.h" + +int pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, size_t plen, + const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output ) +{ + return pkcs5_pbkdf2_hmac( ctx, password, plen, salt, slen, iteration_count, + key_length, output ); +} + +#if defined(POLARSSL_SELF_TEST) +int pbkdf2_self_test( int verbose ) +{ + return pkcs5_self_test( verbose ); +} +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_PBKDF2_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pem.c b/component/common/network/ssl/polarssl-1.3.8/library/pem.c new file mode 100644 index 0000000..485d829 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pem.c @@ -0,0 +1,445 @@ +/* + * Privacy Enhanced Mail (PEM) decoding + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PEM_PARSE_C) || defined(POLARSSL_PEM_WRITE_C) +#include "polarssl/pem.h" +#include "polarssl/base64.h" +#include "polarssl/des.h" +#include "polarssl/aes.h" +#include "polarssl/md5.h" +#include "polarssl/cipher.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if defined(POLARSSL_PEM_PARSE_C) +void pem_init( pem_context *ctx ) +{ + memset( ctx, 0, sizeof( pem_context ) ); +} + +#if defined(POLARSSL_MD5_C) && defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C) ) +/* + * Read a 16-byte hex string and convert it to binary + */ +static int pem_get_iv( const unsigned char *s, unsigned char *iv, + size_t iv_len ) +{ + size_t i, j, k; + + memset( iv, 0, iv_len ); + + for( i = 0; i < iv_len * 2; i++, s++ ) + { + if( *s >= '0' && *s <= '9' ) j = *s - '0'; else + if( *s >= 'A' && *s <= 'F' ) j = *s - '7'; else + if( *s >= 'a' && *s <= 'f' ) j = *s - 'W'; else + return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); + + k = ( ( i & 1 ) != 0 ) ? j : j << 4; + + iv[i >> 1] = (unsigned char)( iv[i >> 1] | k ); + } + + return( 0 ); +} + +static void pem_pbkdf1( unsigned char *key, size_t keylen, + unsigned char *iv, + const unsigned char *pwd, size_t pwdlen ) +{ + md5_context md5_ctx; + unsigned char md5sum[16]; + size_t use_len; + + md5_init( &md5_ctx ); + + /* + * key[ 0..15] = MD5(pwd || IV) + */ + md5_starts( &md5_ctx ); + md5_update( &md5_ctx, pwd, pwdlen ); + md5_update( &md5_ctx, iv, 8 ); + md5_finish( &md5_ctx, md5sum ); + + if( keylen <= 16 ) + { + memcpy( key, md5sum, keylen ); + + md5_free( &md5_ctx ); + polarssl_zeroize( md5sum, 16 ); + return; + } + + memcpy( key, md5sum, 16 ); + + /* + * key[16..23] = MD5(key[ 0..15] || pwd || IV]) + */ + md5_starts( &md5_ctx ); + md5_update( &md5_ctx, md5sum, 16 ); + md5_update( &md5_ctx, pwd, pwdlen ); + md5_update( &md5_ctx, iv, 8 ); + md5_finish( &md5_ctx, md5sum ); + + use_len = 16; + if( keylen < 32 ) + use_len = keylen - 16; + + memcpy( key + 16, md5sum, use_len ); + + md5_free( &md5_ctx ); + polarssl_zeroize( md5sum, 16 ); +} + +#if defined(POLARSSL_DES_C) +/* + * Decrypt with DES-CBC, using PBKDF1 for key derivation + */ +static void pem_des_decrypt( unsigned char des_iv[8], + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen ) +{ + des_context des_ctx; + unsigned char des_key[8]; + + des_init( &des_ctx ); + + pem_pbkdf1( des_key, 8, des_iv, pwd, pwdlen ); + + des_setkey_dec( &des_ctx, des_key ); + des_crypt_cbc( &des_ctx, DES_DECRYPT, buflen, + des_iv, buf, buf ); + + des_free( &des_ctx ); + polarssl_zeroize( des_key, 8 ); +} + +/* + * Decrypt with 3DES-CBC, using PBKDF1 for key derivation + */ +static void pem_des3_decrypt( unsigned char des3_iv[8], + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen ) +{ + des3_context des3_ctx; + unsigned char des3_key[24]; + + des3_init( &des3_ctx ); + + pem_pbkdf1( des3_key, 24, des3_iv, pwd, pwdlen ); + + des3_set3key_dec( &des3_ctx, des3_key ); + des3_crypt_cbc( &des3_ctx, DES_DECRYPT, buflen, + des3_iv, buf, buf ); + + des3_free( &des3_ctx ); + polarssl_zeroize( des3_key, 24 ); +} +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_AES_C) +/* + * Decrypt with AES-XXX-CBC, using PBKDF1 for key derivation + */ +static void pem_aes_decrypt( unsigned char aes_iv[16], unsigned int keylen, + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen ) +{ + aes_context aes_ctx; + unsigned char aes_key[32]; + + aes_init( &aes_ctx ); + + pem_pbkdf1( aes_key, keylen, aes_iv, pwd, pwdlen ); + + aes_setkey_dec( &aes_ctx, aes_key, keylen * 8 ); + aes_crypt_cbc( &aes_ctx, AES_DECRYPT, buflen, + aes_iv, buf, buf ); + + aes_free( &aes_ctx ); + polarssl_zeroize( aes_key, keylen ); +} +#endif /* POLARSSL_AES_C */ + +#endif /* POLARSSL_MD5_C && POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_DES_C ) */ + +int pem_read_buffer( pem_context *ctx, const char *header, const char *footer, + const unsigned char *data, const unsigned char *pwd, + size_t pwdlen, size_t *use_len ) +{ + int ret, enc; + size_t len; + unsigned char *buf; + const unsigned char *s1, *s2, *end; +#if defined(POLARSSL_MD5_C) && defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C) ) + unsigned char pem_iv[16]; + cipher_type_t enc_alg = POLARSSL_CIPHER_NONE; +#else + ((void) pwd); + ((void) pwdlen); +#endif /* POLARSSL_MD5_C && POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_DES_C ) */ + + if( ctx == NULL ) + return( POLARSSL_ERR_PEM_BAD_INPUT_DATA ); + + s1 = (unsigned char *) strstr( (const char *) data, header ); + + if( s1 == NULL ) + return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); + + s2 = (unsigned char *) strstr( (const char *) data, footer ); + + if( s2 == NULL || s2 <= s1 ) + return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); + + s1 += strlen( header ); + if( *s1 == '\r' ) s1++; + if( *s1 == '\n' ) s1++; + else return( POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ); + + end = s2; + end += strlen( footer ); + if( *end == '\r' ) end++; + if( *end == '\n' ) end++; + *use_len = end - data; + + enc = 0; + + if( memcmp( s1, "Proc-Type: 4,ENCRYPTED", 22 ) == 0 ) + { +#if defined(POLARSSL_MD5_C) && defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C) ) + enc++; + + s1 += 22; + if( *s1 == '\r' ) s1++; + if( *s1 == '\n' ) s1++; + else return( POLARSSL_ERR_PEM_INVALID_DATA ); + + +#if defined(POLARSSL_DES_C) + if( memcmp( s1, "DEK-Info: DES-EDE3-CBC,", 23 ) == 0 ) + { + enc_alg = POLARSSL_CIPHER_DES_EDE3_CBC; + + s1 += 23; + if( pem_get_iv( s1, pem_iv, 8 ) != 0 ) + return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); + + s1 += 16; + } + else if( memcmp( s1, "DEK-Info: DES-CBC,", 18 ) == 0 ) + { + enc_alg = POLARSSL_CIPHER_DES_CBC; + + s1 += 18; + if( pem_get_iv( s1, pem_iv, 8) != 0 ) + return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); + + s1 += 16; + } +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_AES_C) + if( memcmp( s1, "DEK-Info: AES-", 14 ) == 0 ) + { + if( memcmp( s1, "DEK-Info: AES-128-CBC,", 22 ) == 0 ) + enc_alg = POLARSSL_CIPHER_AES_128_CBC; + else if( memcmp( s1, "DEK-Info: AES-192-CBC,", 22 ) == 0 ) + enc_alg = POLARSSL_CIPHER_AES_192_CBC; + else if( memcmp( s1, "DEK-Info: AES-256-CBC,", 22 ) == 0 ) + enc_alg = POLARSSL_CIPHER_AES_256_CBC; + else + return( POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG ); + + s1 += 22; + if( pem_get_iv( s1, pem_iv, 16 ) != 0 ) + return( POLARSSL_ERR_PEM_INVALID_ENC_IV ); + + s1 += 32; + } +#endif /* POLARSSL_AES_C */ + + if( enc_alg == POLARSSL_CIPHER_NONE ) + return( POLARSSL_ERR_PEM_UNKNOWN_ENC_ALG ); + + if( *s1 == '\r' ) s1++; + if( *s1 == '\n' ) s1++; + else return( POLARSSL_ERR_PEM_INVALID_DATA ); +#else + return( POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_MD5_C && POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_DES_C ) */ + } + + len = 0; + ret = base64_decode( NULL, &len, s1, s2 - s1 ); + + if( ret == POLARSSL_ERR_BASE64_INVALID_CHARACTER ) + return( POLARSSL_ERR_PEM_INVALID_DATA + ret ); + + if( ( buf = (unsigned char *) polarssl_malloc( len ) ) == NULL ) + return( POLARSSL_ERR_PEM_MALLOC_FAILED ); + + if( ( ret = base64_decode( buf, &len, s1, s2 - s1 ) ) != 0 ) + { + polarssl_free( buf ); + return( POLARSSL_ERR_PEM_INVALID_DATA + ret ); + } + + if( enc != 0 ) + { +#if defined(POLARSSL_MD5_C) && defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_DES_C) || defined(POLARSSL_AES_C) ) + if( pwd == NULL ) + { + polarssl_free( buf ); + return( POLARSSL_ERR_PEM_PASSWORD_REQUIRED ); + } + +#if defined(POLARSSL_DES_C) + if( enc_alg == POLARSSL_CIPHER_DES_EDE3_CBC ) + pem_des3_decrypt( pem_iv, buf, len, pwd, pwdlen ); + else if( enc_alg == POLARSSL_CIPHER_DES_CBC ) + pem_des_decrypt( pem_iv, buf, len, pwd, pwdlen ); +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_AES_C) + if( enc_alg == POLARSSL_CIPHER_AES_128_CBC ) + pem_aes_decrypt( pem_iv, 16, buf, len, pwd, pwdlen ); + else if( enc_alg == POLARSSL_CIPHER_AES_192_CBC ) + pem_aes_decrypt( pem_iv, 24, buf, len, pwd, pwdlen ); + else if( enc_alg == POLARSSL_CIPHER_AES_256_CBC ) + pem_aes_decrypt( pem_iv, 32, buf, len, pwd, pwdlen ); +#endif /* POLARSSL_AES_C */ + + /* + * The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3 + * length bytes (allow 4 to be sure) in all known use cases. + * + * Use that as heurisitic to try detecting password mismatchs. + */ + if( len <= 2 || buf[0] != 0x30 || buf[1] > 0x83 ) + { + polarssl_free( buf ); + return( POLARSSL_ERR_PEM_PASSWORD_MISMATCH ); + } +#else + polarssl_free( buf ); + return( POLARSSL_ERR_PEM_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_MD5_C && POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_DES_C ) */ + } + + ctx->buf = buf; + ctx->buflen = len; + + return( 0 ); +} + +void pem_free( pem_context *ctx ) +{ + polarssl_free( ctx->buf ); + polarssl_free( ctx->info ); + + polarssl_zeroize( ctx, sizeof( pem_context ) ); +} +#endif /* POLARSSL_PEM_PARSE_C */ + +#if defined(POLARSSL_PEM_WRITE_C) +int pem_write_buffer( const char *header, const char *footer, + const unsigned char *der_data, size_t der_len, + unsigned char *buf, size_t buf_len, size_t *olen ) +{ + int ret; + unsigned char *encode_buf, *c, *p = buf; + size_t len = 0, use_len = 0, add_len = 0; + + base64_encode( NULL, &use_len, der_data, der_len ); + add_len = strlen( header ) + strlen( footer ) + ( use_len / 64 ) + 1; + + if( use_len + add_len > buf_len ) + { + *olen = use_len + add_len; + return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); + } + + if( ( encode_buf = polarssl_malloc( use_len ) ) == NULL ) + return( POLARSSL_ERR_PEM_MALLOC_FAILED ); + + if( ( ret = base64_encode( encode_buf, &use_len, der_data, + der_len ) ) != 0 ) + { + polarssl_free( encode_buf ); + return( ret ); + } + + memcpy( p, header, strlen( header ) ); + p += strlen( header ); + c = encode_buf; + + while( use_len ) + { + len = ( use_len > 64 ) ? 64 : use_len; + memcpy( p, c, len ); + use_len -= len; + p += len; + c += len; + *p++ = '\n'; + } + + memcpy( p, footer, strlen( footer ) ); + p += strlen( footer ); + + *p++ = '\0'; + *olen = p - buf; + + polarssl_free( encode_buf ); + return( 0 ); +} +#endif /* POLARSSL_PEM_WRITE_C */ +#endif /* POLARSSL_PEM_PARSE_C || POLARSSL_PEM_WRITE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pk.c b/component/common/network/ssl/polarssl-1.3.8/library/pk.c new file mode 100644 index 0000000..11faf3c --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pk.c @@ -0,0 +1,351 @@ +/* + * Public Key abstraction layer + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PK_C) + +#include "polarssl/pk.h" +#include "polarssl/pk_wrap.h" + +#if defined(POLARSSL_RSA_C) +#include "polarssl/rsa.h" +#endif +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif +#if defined(POLARSSL_ECDSA_C) +#include "polarssl/ecdsa.h" +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Initialise a pk_context + */ +void pk_init( pk_context *ctx ) +{ + if( ctx == NULL ) + return; + + ctx->pk_info = NULL; + ctx->pk_ctx = NULL; +} + +/* + * Free (the components of) a pk_context + */ +void pk_free( pk_context *ctx ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return; + + ctx->pk_info->ctx_free_func( ctx->pk_ctx ); + + polarssl_zeroize( ctx, sizeof( pk_context ) ); +} + +/* + * Get pk_info structure from type + */ +const pk_info_t * pk_info_from_type( pk_type_t pk_type ) +{ + switch( pk_type ) { +#if defined(POLARSSL_RSA_C) + case POLARSSL_PK_RSA: + return( &rsa_info ); +#endif +#if defined(POLARSSL_ECP_C) + case POLARSSL_PK_ECKEY: + return( &eckey_info ); + case POLARSSL_PK_ECKEY_DH: + return( &eckeydh_info ); +#endif +#if defined(POLARSSL_ECDSA_C) + case POLARSSL_PK_ECDSA: + return( &ecdsa_info ); +#endif + /* POLARSSL_PK_RSA_ALT omitted on purpose */ + default: + return( NULL ); + } +} + +/* + * Initialise context + */ +int pk_init_ctx( pk_context *ctx, const pk_info_t *info ) +{ + if( ctx == NULL || info == NULL || ctx->pk_info != NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ( ctx->pk_ctx = info->ctx_alloc_func() ) == NULL ) + return( POLARSSL_ERR_PK_MALLOC_FAILED ); + + ctx->pk_info = info; + + return( 0 ); +} + +/* + * Initialize an RSA-alt context + */ +int pk_init_ctx_rsa_alt( pk_context *ctx, void * key, + pk_rsa_alt_decrypt_func decrypt_func, + pk_rsa_alt_sign_func sign_func, + pk_rsa_alt_key_len_func key_len_func ) +{ + rsa_alt_context *rsa_alt; + const pk_info_t *info = &rsa_alt_info; + + if( ctx == NULL || ctx->pk_info != NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ( ctx->pk_ctx = info->ctx_alloc_func() ) == NULL ) + return( POLARSSL_ERR_PK_MALLOC_FAILED ); + + ctx->pk_info = info; + + rsa_alt = (rsa_alt_context *) ctx->pk_ctx; + + rsa_alt->key = key; + rsa_alt->decrypt_func = decrypt_func; + rsa_alt->sign_func = sign_func; + rsa_alt->key_len_func = key_len_func; + + return( 0 ); +} + +/* + * Tell if a PK can do the operations of the given type + */ +int pk_can_do( pk_context *ctx, pk_type_t type ) +{ + /* null or NONE context can't do anything */ + if( ctx == NULL || ctx->pk_info == NULL ) + return( 0 ); + + return( ctx->pk_info->can_do( type ) ); +} + +/* + * Helper for pk_sign and pk_verify + */ +static inline int pk_hashlen_helper( md_type_t md_alg, size_t *hash_len ) +{ + const md_info_t *md_info; + + if( *hash_len != 0 ) + return( 0 ); + + if( ( md_info = md_info_from_type( md_alg ) ) == NULL ) + return( -1 ); + + *hash_len = md_info->size; + return( 0 ); +} + +/* + * Verify a signature + */ +int pk_verify( pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ) +{ + if( ctx == NULL || ctx->pk_info == NULL || + pk_hashlen_helper( md_alg, &hash_len ) != 0 ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ctx->pk_info->verify_func == NULL ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + return( ctx->pk_info->verify_func( ctx->pk_ctx, md_alg, hash, hash_len, + sig, sig_len ) ); +} + +/* + * Verify a signature with options + */ +int pk_verify_ext( pk_type_t type, const void *options, + pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ! pk_can_do( ctx, type ) ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + if( type == POLARSSL_PK_RSASSA_PSS ) + { +#if defined(POLARSSL_RSA_C) && defined(POLARSSL_PKCS1_V21) + int ret; + const pk_rsassa_pss_options *pss_opts; + + if( options == NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + pss_opts = (const pk_rsassa_pss_options *) options; + + if( sig_len < pk_get_len( ctx ) ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + ret = rsa_rsassa_pss_verify_ext( pk_rsa( *ctx ), + NULL, NULL, RSA_PUBLIC, + md_alg, hash_len, hash, + pss_opts->mgf1_hash_id, + pss_opts->expected_salt_len, + sig ); + if( ret != 0 ) + return( ret ); + + if( sig_len > pk_get_len( ctx ) ) + return( POLARSSL_ERR_PK_SIG_LEN_MISMATCH ); + + return( 0 ); +#else + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); +#endif + } + + /* General case: no options */ + if( options != NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + return( pk_verify( ctx, md_alg, hash, hash_len, sig, sig_len ) ); +} + +/* + * Make a signature + */ +int pk_sign( pk_context *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + if( ctx == NULL || ctx->pk_info == NULL || + pk_hashlen_helper( md_alg, &hash_len ) != 0 ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ctx->pk_info->sign_func == NULL ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + return( ctx->pk_info->sign_func( ctx->pk_ctx, md_alg, hash, hash_len, + sig, sig_len, f_rng, p_rng ) ); +} + +/* + * Decrypt message + */ +int pk_decrypt( pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ctx->pk_info->decrypt_func == NULL ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + return( ctx->pk_info->decrypt_func( ctx->pk_ctx, input, ilen, + output, olen, osize, f_rng, p_rng ) ); +} + +/* + * Encrypt message + */ +int pk_encrypt( pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ctx->pk_info->encrypt_func == NULL ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + return( ctx->pk_info->encrypt_func( ctx->pk_ctx, input, ilen, + output, olen, osize, f_rng, p_rng ) ); +} + +/* + * Get key size in bits + */ +size_t pk_get_size( const pk_context *ctx ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( 0 ); + + return( ctx->pk_info->get_size( ctx->pk_ctx ) ); +} + +/* + * Export debug information + */ +int pk_debug( const pk_context *ctx, pk_debug_item *items ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + if( ctx->pk_info->debug_func == NULL ) + return( POLARSSL_ERR_PK_TYPE_MISMATCH ); + + ctx->pk_info->debug_func( ctx->pk_ctx, items ); + return( 0 ); +} + +/* + * Access the PK type name + */ +const char * pk_get_name( const pk_context *ctx ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( "invalid PK" ); + + return( ctx->pk_info->name ); +} + +/* + * Access the PK type + */ +pk_type_t pk_get_type( const pk_context *ctx ) +{ + if( ctx == NULL || ctx->pk_info == NULL ) + return( POLARSSL_PK_NONE ); + + return( ctx->pk_info->type ); +} + +#endif /* POLARSSL_PK_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pk_wrap.c b/component/common/network/ssl/polarssl-1.3.8/library/pk_wrap.c new file mode 100644 index 0000000..5e9ff60 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pk_wrap.c @@ -0,0 +1,452 @@ +/* + * Public Key abstraction layer: wrapper functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PK_C) + +#include "polarssl/pk_wrap.h" + +/* Even if RSA not activated, for the sake of RSA-alt */ +#include "polarssl/rsa.h" + +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif + +#if defined(POLARSSL_ECDSA_C) +#include "polarssl/ecdsa.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if defined(POLARSSL_RSA_C) +static int rsa_can_do( pk_type_t type ) +{ + return( type == POLARSSL_PK_RSA || + type == POLARSSL_PK_RSASSA_PSS ); +} + +static size_t rsa_get_size( const void *ctx ) +{ + return( 8 * ((const rsa_context *) ctx)->len ); +} + +static int rsa_verify_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ) +{ + int ret; + + if( sig_len < ((rsa_context *) ctx)->len ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( ret = rsa_pkcs1_verify( (rsa_context *) ctx, NULL, NULL, + RSA_PUBLIC, md_alg, + (unsigned int) hash_len, hash, sig ) ) != 0 ) + return( ret ); + + if( sig_len > ((rsa_context *) ctx)->len ) + return( POLARSSL_ERR_PK_SIG_LEN_MISMATCH ); + + return( 0 ); +} + +static int rsa_sign_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + *sig_len = ((rsa_context *) ctx)->len; + + return( rsa_pkcs1_sign( (rsa_context *) ctx, f_rng, p_rng, RSA_PRIVATE, + md_alg, (unsigned int) hash_len, hash, sig ) ); +} + +static int rsa_decrypt_wrap( void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + if( ilen != ((rsa_context *) ctx)->len ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + return( rsa_pkcs1_decrypt( (rsa_context *) ctx, f_rng, p_rng, + RSA_PRIVATE, olen, input, output, osize ) ); +} + +static int rsa_encrypt_wrap( void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + ((void) osize); + + *olen = ((rsa_context *) ctx)->len; + + return( rsa_pkcs1_encrypt( (rsa_context *) ctx, + f_rng, p_rng, RSA_PUBLIC, ilen, input, output ) ); +} + +static void *rsa_alloc_wrap( void ) +{ + void *ctx = polarssl_malloc( sizeof( rsa_context ) ); + + if( ctx != NULL ) + rsa_init( (rsa_context *) ctx, 0, 0 ); + + return( ctx ); +} + +static void rsa_free_wrap( void *ctx ) +{ + rsa_free( (rsa_context *) ctx ); + polarssl_free( ctx ); +} + +static void rsa_debug( const void *ctx, pk_debug_item *items ) +{ + items->type = POLARSSL_PK_DEBUG_MPI; + items->name = "rsa.N"; + items->value = &( ((rsa_context *) ctx)->N ); + + items++; + + items->type = POLARSSL_PK_DEBUG_MPI; + items->name = "rsa.E"; + items->value = &( ((rsa_context *) ctx)->E ); +} + +const pk_info_t rsa_info = { + POLARSSL_PK_RSA, + "RSA", + rsa_get_size, + rsa_can_do, + rsa_verify_wrap, + rsa_sign_wrap, + rsa_decrypt_wrap, + rsa_encrypt_wrap, + rsa_alloc_wrap, + rsa_free_wrap, + rsa_debug, +}; +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) +/* + * Generic EC key + */ +static int eckey_can_do( pk_type_t type ) +{ + return( type == POLARSSL_PK_ECKEY || + type == POLARSSL_PK_ECKEY_DH || + type == POLARSSL_PK_ECDSA ); +} + +static size_t eckey_get_size( const void *ctx ) +{ + return( ((ecp_keypair *) ctx)->grp.pbits ); +} + +#if defined(POLARSSL_ECDSA_C) +/* Forward declarations */ +static int ecdsa_verify_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ); + +static int ecdsa_sign_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ); + +static int eckey_verify_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ) +{ + int ret; + ecdsa_context ecdsa; + + ecdsa_init( &ecdsa ); + + if( ( ret = ecdsa_from_keypair( &ecdsa, ctx ) ) == 0 ) + ret = ecdsa_verify_wrap( &ecdsa, md_alg, hash, hash_len, sig, sig_len ); + + ecdsa_free( &ecdsa ); + + return( ret ); +} + +static int eckey_sign_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret; + ecdsa_context ecdsa; + + ecdsa_init( &ecdsa ); + + if( ( ret = ecdsa_from_keypair( &ecdsa, ctx ) ) == 0 ) + ret = ecdsa_sign_wrap( &ecdsa, md_alg, hash, hash_len, sig, sig_len, + f_rng, p_rng ); + + ecdsa_free( &ecdsa ); + + return( ret ); +} + +#endif /* POLARSSL_ECDSA_C */ + +static void *eckey_alloc_wrap( void ) +{ + void *ctx = polarssl_malloc( sizeof( ecp_keypair ) ); + + if( ctx != NULL ) + ecp_keypair_init( ctx ); + + return( ctx ); +} + +static void eckey_free_wrap( void *ctx ) +{ + ecp_keypair_free( (ecp_keypair *) ctx ); + polarssl_free( ctx ); +} + +static void eckey_debug( const void *ctx, pk_debug_item *items ) +{ + items->type = POLARSSL_PK_DEBUG_ECP; + items->name = "eckey.Q"; + items->value = &( ((ecp_keypair *) ctx)->Q ); +} + +const pk_info_t eckey_info = { + POLARSSL_PK_ECKEY, + "EC", + eckey_get_size, + eckey_can_do, +#if defined(POLARSSL_ECDSA_C) + eckey_verify_wrap, + eckey_sign_wrap, +#else + NULL, + NULL, +#endif + NULL, + NULL, + eckey_alloc_wrap, + eckey_free_wrap, + eckey_debug, +}; + +/* + * EC key restricted to ECDH + */ +static int eckeydh_can_do( pk_type_t type ) +{ + return( type == POLARSSL_PK_ECKEY || + type == POLARSSL_PK_ECKEY_DH ); +} + +const pk_info_t eckeydh_info = { + POLARSSL_PK_ECKEY_DH, + "EC_DH", + eckey_get_size, /* Same underlying key structure */ + eckeydh_can_do, + NULL, + NULL, + NULL, + NULL, + eckey_alloc_wrap, /* Same underlying key structure */ + eckey_free_wrap, /* Same underlying key structure */ + eckey_debug, /* Same underlying key structure */ +}; +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_ECDSA_C) +static int ecdsa_can_do( pk_type_t type ) +{ + return( type == POLARSSL_PK_ECDSA ); +} + +static int ecdsa_verify_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len ) +{ + int ret; + ((void) md_alg); + + ret = ecdsa_read_signature( (ecdsa_context *) ctx, + hash, hash_len, sig, sig_len ); + + if( ret == POLARSSL_ERR_ECP_SIG_LEN_MISMATCH ) + return( POLARSSL_ERR_PK_SIG_LEN_MISMATCH ); + + return( ret ); +} + +static int ecdsa_sign_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + /* Use deterministic ECDSA by default if available */ +#if defined(POLARSSL_ECDSA_DETERMINISTIC) + ((void) f_rng); + ((void) p_rng); + + return( ecdsa_write_signature_det( (ecdsa_context *) ctx, + hash, hash_len, sig, sig_len, md_alg ) ); +#else + ((void) md_alg); + + return( ecdsa_write_signature( (ecdsa_context *) ctx, + hash, hash_len, sig, sig_len, f_rng, p_rng ) ); +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ +} + +static void *ecdsa_alloc_wrap( void ) +{ + void *ctx = polarssl_malloc( sizeof( ecdsa_context ) ); + + if( ctx != NULL ) + ecdsa_init( (ecdsa_context *) ctx ); + + return( ctx ); +} + +static void ecdsa_free_wrap( void *ctx ) +{ + ecdsa_free( (ecdsa_context *) ctx ); + polarssl_free( ctx ); +} + +const pk_info_t ecdsa_info = { + POLARSSL_PK_ECDSA, + "ECDSA", + eckey_get_size, /* Compatible key structures */ + ecdsa_can_do, + ecdsa_verify_wrap, + ecdsa_sign_wrap, + NULL, + NULL, + ecdsa_alloc_wrap, + ecdsa_free_wrap, + eckey_debug, /* Compatible key structures */ +}; +#endif /* POLARSSL_ECDSA_C */ + +/* + * Support for alternative RSA-private implementations + */ + +static int rsa_alt_can_do( pk_type_t type ) +{ + return( type == POLARSSL_PK_RSA ); +} + +static size_t rsa_alt_get_size( const void *ctx ) +{ + const rsa_alt_context *rsa_alt = (const rsa_alt_context *) ctx; + + return( 8 * rsa_alt->key_len_func( rsa_alt->key ) ); +} + +static int rsa_alt_sign_wrap( void *ctx, md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + rsa_alt_context *rsa_alt = (rsa_alt_context *) ctx; + + *sig_len = rsa_alt->key_len_func( rsa_alt->key ); + + return( rsa_alt->sign_func( rsa_alt->key, f_rng, p_rng, RSA_PRIVATE, + md_alg, (unsigned int) hash_len, hash, sig ) ); +} + +static int rsa_alt_decrypt_wrap( void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + rsa_alt_context *rsa_alt = (rsa_alt_context *) ctx; + + ((void) f_rng); + ((void) p_rng); + + if( ilen != rsa_alt->key_len_func( rsa_alt->key ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + return( rsa_alt->decrypt_func( rsa_alt->key, + RSA_PRIVATE, olen, input, output, osize ) ); +} + +static void *rsa_alt_alloc_wrap( void ) +{ + void *ctx = polarssl_malloc( sizeof( rsa_alt_context ) ); + + if( ctx != NULL ) + memset( ctx, 0, sizeof( rsa_alt_context ) ); + + return( ctx ); +} + +static void rsa_alt_free_wrap( void *ctx ) +{ + polarssl_zeroize( ctx, sizeof( rsa_alt_context ) ); + polarssl_free( ctx ); +} + +const pk_info_t rsa_alt_info = { + POLARSSL_PK_RSA_ALT, + "RSA-alt", + rsa_alt_get_size, + rsa_alt_can_do, + NULL, + rsa_alt_sign_wrap, + rsa_alt_decrypt_wrap, + NULL, + rsa_alt_alloc_wrap, + rsa_alt_free_wrap, + NULL, +}; + +#endif /* POLARSSL_PK_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pkcs11.c b/component/common/network/ssl/polarssl-1.3.8/library/pkcs11.c new file mode 100644 index 0000000..64e7ce3 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pkcs11.c @@ -0,0 +1,236 @@ +/** + * \file pkcs11.c + * + * \brief Wrapper for PKCS#11 library libpkcs11-helper + * + * \author Adriaan de Jong + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "polarssl/pkcs11.h" + +#if defined(POLARSSL_PKCS11_C) +#include "polarssl/md.h" +#include "polarssl/oid.h" +#include "polarssl/x509_crt.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +int pkcs11_x509_cert_init( x509_crt *cert, pkcs11h_certificate_t pkcs11_cert ) +{ + int ret = 1; + unsigned char *cert_blob = NULL; + size_t cert_blob_size = 0; + + if( cert == NULL ) + { + ret = 2; + goto cleanup; + } + + if( pkcs11h_certificate_getCertificateBlob( pkcs11_cert, NULL, + &cert_blob_size ) != CKR_OK ) + { + ret = 3; + goto cleanup; + } + + cert_blob = polarssl_malloc( cert_blob_size ); + if( NULL == cert_blob ) + { + ret = 4; + goto cleanup; + } + + if( pkcs11h_certificate_getCertificateBlob( pkcs11_cert, cert_blob, + &cert_blob_size ) != CKR_OK ) + { + ret = 5; + goto cleanup; + } + + if( 0 != x509_crt_parse( cert, cert_blob, cert_blob_size ) ) + { + ret = 6; + goto cleanup; + } + + ret = 0; + +cleanup: + if( NULL != cert_blob ) + polarssl_free( cert_blob ); + + return( ret ); +} + + +int pkcs11_priv_key_init( pkcs11_context *priv_key, + pkcs11h_certificate_t pkcs11_cert ) +{ + int ret = 1; + x509_crt cert; + + x509_crt_init( &cert ); + + if( priv_key == NULL ) + goto cleanup; + + if( 0 != pkcs11_x509_cert_init( &cert, pkcs11_cert ) ) + goto cleanup; + + priv_key->len = pk_get_len( &cert.pk ); + priv_key->pkcs11h_cert = pkcs11_cert; + + ret = 0; + +cleanup: + x509_crt_free( &cert ); + + return( ret ); +} + +void pkcs11_priv_key_free( pkcs11_context *priv_key ) +{ + if( NULL != priv_key ) + pkcs11h_certificate_freeCertificate( priv_key->pkcs11h_cert ); +} + +int pkcs11_decrypt( pkcs11_context *ctx, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + size_t input_len, output_len; + + if( NULL == ctx ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( RSA_PRIVATE != mode ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + output_len = input_len = ctx->len; + + if( input_len < 16 || input_len > output_max_len ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + /* Determine size of output buffer */ + if( pkcs11h_certificate_decryptAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, input, + input_len, NULL, &output_len ) != CKR_OK ) + { + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( output_len > output_max_len ) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + if( pkcs11h_certificate_decryptAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, input, + input_len, output, &output_len ) != CKR_OK ) + { + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + *olen = output_len; + return( 0 ); +} + +int pkcs11_sign( pkcs11_context *ctx, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t sig_len = 0, asn_len = 0, oid_size = 0; + unsigned char *p = sig; + const char *oid; + + if( NULL == ctx ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( RSA_PRIVATE != mode ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( md_alg != POLARSSL_MD_NONE ) + { + const md_info_t *md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( oid_get_oid_by_md( md_alg, &oid, &oid_size ) != 0 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hashlen = md_get_size( md_info ); + asn_len = 10 + oid_size; + } + + sig_len = ctx->len; + if( hashlen > sig_len || asn_len > sig_len || + hashlen + asn_len > sig_len ) + { + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + if( md_alg != POLARSSL_MD_NONE ) + { + /* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x08 + oid_size + hashlen ); + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x04 + oid_size ); + *p++ = ASN1_OID; + *p++ = oid_size & 0xFF; + memcpy( p, oid, oid_size ); + p += oid_size; + *p++ = ASN1_NULL; + *p++ = 0x00; + *p++ = ASN1_OCTET_STRING; + *p++ = hashlen; + } + + memcpy( p, hash, hashlen ); + + if( pkcs11h_certificate_signAny( ctx->pkcs11h_cert, CKM_RSA_PKCS, sig, + asn_len + hashlen, sig, &sig_len ) != CKR_OK ) + { + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + return( 0 ); +} + +#endif /* defined(POLARSSL_PKCS11_C) */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pkcs12.c b/component/common/network/ssl/polarssl-1.3.8/library/pkcs12.c new file mode 100644 index 0000000..0cf2edf --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pkcs12.c @@ -0,0 +1,360 @@ +/* + * PKCS#12 Personal Information Exchange Syntax + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The PKCS #12 Personal Information Exchange Syntax Standard v1.1 + * + * http://www.rsa.com/rsalabs/pkcs/files/h11301-wp-pkcs-12v1-1-personal-information-exchange-syntax.pdf + * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1-1.asn + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PKCS12_C) + +#include "polarssl/pkcs12.h" +#include "polarssl/asn1.h" +#include "polarssl/cipher.h" + +#if defined(POLARSSL_ARC4_C) +#include "polarssl/arc4.h" +#endif + +#if defined(POLARSSL_DES_C) +#include "polarssl/des.h" +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +static int pkcs12_parse_pbe_params( asn1_buf *params, + asn1_buf *salt, int *iterations ) +{ + int ret; + unsigned char **p = ¶ms->p; + const unsigned char *end = params->p + params->len; + + /* + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } + * + */ + if( params->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + if( ( ret = asn1_get_tag( p, end, &salt->len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + ret ); + + salt->p = *p; + *p += salt->len; + + if( ( ret = asn1_get_int( p, end, iterations ) ) != 0 ) + return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + ret ); + + if( *p != end ) + return( POLARSSL_ERR_PKCS12_PBE_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +static int pkcs12_pbe_derive_key_iv( asn1_buf *pbe_params, md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen ) +{ + int ret, iterations; + asn1_buf salt; + size_t i; + unsigned char unipwd[258]; + + memset( &salt, 0, sizeof(asn1_buf) ); + memset( &unipwd, 0, sizeof(unipwd) ); + + if( ( ret = pkcs12_parse_pbe_params( pbe_params, &salt, + &iterations ) ) != 0 ) + return( ret ); + + for( i = 0; i < pwdlen; i++ ) + unipwd[i * 2 + 1] = pwd[i]; + + if( ( ret = pkcs12_derivation( key, keylen, unipwd, pwdlen * 2 + 2, + salt.p, salt.len, md_type, + PKCS12_DERIVE_KEY, iterations ) ) != 0 ) + { + return( ret ); + } + + if( iv == NULL || ivlen == 0 ) + return( 0 ); + + if( ( ret = pkcs12_derivation( iv, ivlen, unipwd, pwdlen * 2 + 2, + salt.p, salt.len, md_type, + PKCS12_DERIVE_IV, iterations ) ) != 0 ) + { + return( ret ); + } + return( 0 ); +} + +int pkcs12_pbe_sha1_rc4_128( asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t len, + unsigned char *output ) +{ +#if !defined(POLARSSL_ARC4_C) + ((void) pbe_params); + ((void) mode); + ((void) pwd); + ((void) pwdlen); + ((void) data); + ((void) len); + ((void) output); + return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); +#else + int ret; + unsigned char key[16]; + arc4_context ctx; + ((void) mode); + + arc4_init( &ctx ); + + if( ( ret = pkcs12_pbe_derive_key_iv( pbe_params, POLARSSL_MD_SHA1, + pwd, pwdlen, + key, 16, NULL, 0 ) ) != 0 ) + { + return( ret ); + } + + arc4_setup( &ctx, key, 16 ); + if( ( ret = arc4_crypt( &ctx, len, data, output ) ) != 0 ) + goto exit; + +exit: + polarssl_zeroize( key, sizeof( key ) ); + arc4_free( &ctx ); + + return( ret ); +#endif /* POLARSSL_ARC4_C */ +} + +int pkcs12_pbe( asn1_buf *pbe_params, int mode, + cipher_type_t cipher_type, md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t len, + unsigned char *output ) +{ + int ret, keylen = 0; + unsigned char key[32]; + unsigned char iv[16]; + const cipher_info_t *cipher_info; + cipher_context_t cipher_ctx; + size_t olen = 0; + + cipher_info = cipher_info_from_type( cipher_type ); + if( cipher_info == NULL ) + return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); + + keylen = cipher_info->key_length / 8; + + if( ( ret = pkcs12_pbe_derive_key_iv( pbe_params, md_type, pwd, pwdlen, + key, keylen, + iv, cipher_info->iv_size ) ) != 0 ) + { + return( ret ); + } + + cipher_init( &cipher_ctx ); + + if( ( ret = cipher_init_ctx( &cipher_ctx, cipher_info ) ) != 0 ) + goto exit; + + if( ( ret = cipher_setkey( &cipher_ctx, key, 8 * keylen, mode ) ) != 0 ) + goto exit; + + if( ( ret = cipher_set_iv( &cipher_ctx, iv, cipher_info->iv_size ) ) != 0 ) + goto exit; + + if( ( ret = cipher_reset( &cipher_ctx ) ) != 0 ) + goto exit; + + if( ( ret = cipher_update( &cipher_ctx, data, len, + output, &olen ) ) != 0 ) + { + goto exit; + } + + if( ( ret = cipher_finish( &cipher_ctx, output + olen, &olen ) ) != 0 ) + ret = POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH; + +exit: + polarssl_zeroize( key, sizeof( key ) ); + polarssl_zeroize( iv, sizeof( iv ) ); + cipher_free( &cipher_ctx ); + + return( ret ); +} + +static void pkcs12_fill_buffer( unsigned char *data, size_t data_len, + const unsigned char *filler, size_t fill_len ) +{ + unsigned char *p = data; + size_t use_len; + + while( data_len > 0 ) + { + use_len = ( data_len > fill_len ) ? fill_len : data_len; + memcpy( p, filler, use_len ); + p += use_len; + data_len -= use_len; + } +} + +int pkcs12_derivation( unsigned char *data, size_t datalen, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *salt, size_t saltlen, + md_type_t md_type, int id, int iterations ) +{ + int ret; + unsigned int j; + + unsigned char diversifier[128]; + unsigned char salt_block[128], pwd_block[128], hash_block[128]; + unsigned char hash_output[POLARSSL_MD_MAX_SIZE]; + unsigned char *p; + unsigned char c; + + size_t hlen, use_len, v, i; + + const md_info_t *md_info; + md_context_t md_ctx; + + // This version only allows max of 64 bytes of password or salt + if( datalen > 128 || pwdlen > 64 || saltlen > 64 ) + return( POLARSSL_ERR_PKCS12_BAD_INPUT_DATA ); + + md_info = md_info_from_type( md_type ); + if( md_info == NULL ) + return( POLARSSL_ERR_PKCS12_FEATURE_UNAVAILABLE ); + + md_init( &md_ctx ); + + if( ( ret = md_init_ctx( &md_ctx, md_info ) ) != 0 ) + return( ret ); + hlen = md_get_size( md_info ); + + if( hlen <= 32 ) + v = 64; + else + v = 128; + + memset( diversifier, (unsigned char) id, v ); + + pkcs12_fill_buffer( salt_block, v, salt, saltlen ); + pkcs12_fill_buffer( pwd_block, v, pwd, pwdlen ); + + p = data; + while( datalen > 0 ) + { + // Calculate hash( diversifier || salt_block || pwd_block ) + if( ( ret = md_starts( &md_ctx ) ) != 0 ) + goto exit; + + if( ( ret = md_update( &md_ctx, diversifier, v ) ) != 0 ) + goto exit; + + if( ( ret = md_update( &md_ctx, salt_block, v ) ) != 0 ) + goto exit; + + if( ( ret = md_update( &md_ctx, pwd_block, v ) ) != 0 ) + goto exit; + + if( ( ret = md_finish( &md_ctx, hash_output ) ) != 0 ) + goto exit; + + // Perform remaining ( iterations - 1 ) recursive hash calculations + for( i = 1; i < (size_t) iterations; i++ ) + { + if( ( ret = md( md_info, hash_output, hlen, hash_output ) ) != 0 ) + goto exit; + } + + use_len = ( datalen > hlen ) ? hlen : datalen; + memcpy( p, hash_output, use_len ); + datalen -= use_len; + p += use_len; + + if( datalen == 0 ) + break; + + // Concatenating copies of hash_output into hash_block (B) + pkcs12_fill_buffer( hash_block, v, hash_output, hlen ); + + // B += 1 + for( i = v; i > 0; i-- ) + if( ++hash_block[i - 1] != 0 ) + break; + + // salt_block += B + c = 0; + for( i = v; i > 0; i-- ) + { + j = salt_block[i - 1] + hash_block[i - 1] + c; + c = (unsigned char) (j >> 8); + salt_block[i - 1] = j & 0xFF; + } + + // pwd_block += B + c = 0; + for( i = v; i > 0; i-- ) + { + j = pwd_block[i - 1] + hash_block[i - 1] + c; + c = (unsigned char) (j >> 8); + pwd_block[i - 1] = j & 0xFF; + } + } + + ret = 0; + +exit: + polarssl_zeroize( salt_block, sizeof( salt_block ) ); + polarssl_zeroize( pwd_block, sizeof( pwd_block ) ); + polarssl_zeroize( hash_block, sizeof( hash_block ) ); + polarssl_zeroize( hash_output, sizeof( hash_output ) ); + + md_free( &md_ctx ); + + return( ret ); +} + +#endif /* POLARSSL_PKCS12_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pkcs5.c b/component/common/network/ssl/polarssl-1.3.8/library/pkcs5.c new file mode 100644 index 0000000..e769783 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pkcs5.c @@ -0,0 +1,417 @@ +/** + * \file pkcs5.c + * + * \brief PKCS#5 functions + * + * \author Mathias Olsson + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * PKCS#5 includes PBKDF2 and more + * + * http://tools.ietf.org/html/rfc2898 (Specification) + * http://tools.ietf.org/html/rfc6070 (Test vectors) + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PKCS5_C) + +#include "polarssl/pkcs5.h" +#include "polarssl/asn1.h" +#include "polarssl/cipher.h" +#include "polarssl/oid.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +static int pkcs5_parse_pbkdf2_params( const asn1_buf *params, + asn1_buf *salt, int *iterations, + int *keylen, md_type_t *md_type ) +{ + int ret; + asn1_buf prf_alg_oid; + unsigned char *p = params->p; + const unsigned char *end = params->p + params->len; + + if( params->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + /* + * PBKDF2-params ::= SEQUENCE { + * salt OCTET STRING, + * iterationCount INTEGER, + * keyLength INTEGER OPTIONAL + * prf AlgorithmIdentifier DEFAULT algid-hmacWithSHA1 + * } + * + */ + if( ( ret = asn1_get_tag( &p, end, &salt->len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + + salt->p = p; + p += salt->len; + + if( ( ret = asn1_get_int( &p, end, iterations ) ) != 0 ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + + if( p == end ) + return( 0 ); + + if( ( ret = asn1_get_int( &p, end, keylen ) ) != 0 ) + { + if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + } + + if( p == end ) + return( 0 ); + + if( ( ret = asn1_get_alg_null( &p, end, &prf_alg_oid ) ) != 0 ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + + if( !OID_CMP( OID_HMAC_SHA1, &prf_alg_oid ) ) + return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); + + *md_type = POLARSSL_MD_SHA1; + + if( p != end ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +int pkcs5_pbes2( asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t datalen, + unsigned char *output ) +{ + int ret, iterations = 0, keylen = 0; + unsigned char *p, *end; + asn1_buf kdf_alg_oid, enc_scheme_oid, kdf_alg_params, enc_scheme_params; + asn1_buf salt; + md_type_t md_type = POLARSSL_MD_SHA1; + unsigned char key[32], iv[32]; + size_t olen = 0; + const md_info_t *md_info; + const cipher_info_t *cipher_info; + md_context_t md_ctx; + cipher_type_t cipher_alg; + cipher_context_t cipher_ctx; + + p = pbe_params->p; + end = p + pbe_params->len; + + /* + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} + * } + */ + if( pbe_params->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + if( ( ret = asn1_get_alg( &p, end, &kdf_alg_oid, &kdf_alg_params ) ) != 0 ) + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + + // Only PBKDF2 supported at the moment + // + if( !OID_CMP( OID_PKCS5_PBKDF2, &kdf_alg_oid ) ) + return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); + + if( ( ret = pkcs5_parse_pbkdf2_params( &kdf_alg_params, + &salt, &iterations, &keylen, + &md_type ) ) != 0 ) + { + return( ret ); + } + + md_info = md_info_from_type( md_type ); + if( md_info == NULL ) + return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); + + if( ( ret = asn1_get_alg( &p, end, &enc_scheme_oid, + &enc_scheme_params ) ) != 0 ) + { + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT + ret ); + } + + if( oid_get_cipher_alg( &enc_scheme_oid, &cipher_alg ) != 0 ) + return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); + + cipher_info = cipher_info_from_type( cipher_alg ); + if( cipher_info == NULL ) + return( POLARSSL_ERR_PKCS5_FEATURE_UNAVAILABLE ); + + /* + * The value of keylen from pkcs5_parse_pbkdf2_params() is ignored + * since it is optional and we don't know if it was set or not + */ + keylen = cipher_info->key_length / 8; + + if( enc_scheme_params.tag != ASN1_OCTET_STRING || + enc_scheme_params.len != cipher_info->iv_size ) + { + return( POLARSSL_ERR_PKCS5_INVALID_FORMAT ); + } + + md_init( &md_ctx ); + cipher_init( &cipher_ctx ); + + memcpy( iv, enc_scheme_params.p, enc_scheme_params.len ); + + if( ( ret = md_init_ctx( &md_ctx, md_info ) ) != 0 ) + goto exit; + + if( ( ret = pkcs5_pbkdf2_hmac( &md_ctx, pwd, pwdlen, salt.p, salt.len, + iterations, keylen, key ) ) != 0 ) + { + goto exit; + } + + if( ( ret = cipher_init_ctx( &cipher_ctx, cipher_info ) ) != 0 ) + goto exit; + + if( ( ret = cipher_setkey( &cipher_ctx, key, 8 * keylen, mode ) ) != 0 ) + goto exit; + + if( ( ret = cipher_crypt( &cipher_ctx, iv, enc_scheme_params.len, + data, datalen, output, &olen ) ) != 0 ) + ret = POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH; + +exit: + md_free( &md_ctx ); + cipher_free( &cipher_ctx ); + + return( ret ); +} + +int pkcs5_pbkdf2_hmac( md_context_t *ctx, const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output ) +{ + int ret, j; + unsigned int i; + unsigned char md1[POLARSSL_MD_MAX_SIZE]; + unsigned char work[POLARSSL_MD_MAX_SIZE]; + unsigned char md_size = md_get_size( ctx->md_info ); + size_t use_len; + unsigned char *out_p = output; + unsigned char counter[4]; + + memset( counter, 0, 4 ); + counter[3] = 1; + + if( iteration_count > 0xFFFFFFFF ) + return( POLARSSL_ERR_PKCS5_BAD_INPUT_DATA ); + + while( key_length ) + { + // U1 ends up in work + // + if( ( ret = md_hmac_starts( ctx, password, plen ) ) != 0 ) + return( ret ); + + if( ( ret = md_hmac_update( ctx, salt, slen ) ) != 0 ) + return( ret ); + + if( ( ret = md_hmac_update( ctx, counter, 4 ) ) != 0 ) + return( ret ); + + if( ( ret = md_hmac_finish( ctx, work ) ) != 0 ) + return( ret ); + + memcpy( md1, work, md_size ); + + for( i = 1; i < iteration_count; i++ ) + { + // U2 ends up in md1 + // + if( ( ret = md_hmac_starts( ctx, password, plen ) ) != 0 ) + return( ret ); + + if( ( ret = md_hmac_update( ctx, md1, md_size ) ) != 0 ) + return( ret ); + + if( ( ret = md_hmac_finish( ctx, md1 ) ) != 0 ) + return( ret ); + + // U1 xor U2 + // + for( j = 0; j < md_size; j++ ) + work[j] ^= md1[j]; + } + + use_len = ( key_length < md_size ) ? key_length : md_size; + memcpy( out_p, work, use_len ); + + key_length -= (uint32_t) use_len; + out_p += use_len; + + for( i = 4; i > 0; i-- ) + if( ++counter[i - 1] != 0 ) + break; + } + + return( 0 ); +} + +#if defined(POLARSSL_SELF_TEST) + +#if !defined(POLARSSL_SHA1_C) +int pkcs5_self_test( int verbose ) +{ + if( verbose != 0 ) + polarssl_printf( " PBKDF2 (SHA1): skipped\n\n" ); + + return( 0 ); +} +#else + +#include + +#define MAX_TESTS 6 + +size_t plen[MAX_TESTS] = + { 8, 8, 8, 8, 24, 9 }; + +unsigned char password[MAX_TESTS][32] = +{ + "password", + "password", + "password", + "password", + "passwordPASSWORDpassword", + "pass\0word", +}; + +size_t slen[MAX_TESTS] = + { 4, 4, 4, 4, 36, 5 }; + +unsigned char salt[MAX_TESTS][40] = +{ + "salt", + "salt", + "salt", + "salt", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + "sa\0lt", +}; + +uint32_t it_cnt[MAX_TESTS] = + { 1, 2, 4096, 16777216, 4096, 4096 }; + +uint32_t key_len[MAX_TESTS] = + { 20, 20, 20, 20, 25, 16 }; + + +unsigned char result_key[MAX_TESTS][32] = +{ + { 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6 }, + { 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57 }, + { 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1 }, + { 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, + 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, + 0x26, 0x34, 0xe9, 0x84 }, + { 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38 }, + { 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 }, +}; + +int pkcs5_self_test( int verbose ) +{ + md_context_t sha1_ctx; + const md_info_t *info_sha1; + int ret, i; + unsigned char key[64]; + + md_init( &sha1_ctx ); + + info_sha1 = md_info_from_type( POLARSSL_MD_SHA1 ); + if( info_sha1 == NULL ) + { + ret = 1; + goto exit; + } + + if( ( ret = md_init_ctx( &sha1_ctx, info_sha1 ) ) != 0 ) + { + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( " PBKDF2 note: test #3 may be slow!\n" ); + + for( i = 0; i < MAX_TESTS; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " PBKDF2 (SHA1) #%d: ", i ); + + ret = pkcs5_pbkdf2_hmac( &sha1_ctx, password[i], plen[i], salt[i], + slen[i], it_cnt[i], key_len[i], key ); + if( ret != 0 || + memcmp( result_key[i], key, key_len[i] ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + polarssl_printf( "\n" ); + +exit: + md_free( &sha1_ctx ); + + return( 0 ); +} +#endif /* POLARSSL_SHA1_C */ + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_PKCS5_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pkparse.c b/component/common/network/ssl/polarssl-1.3.8/library/pkparse.c new file mode 100644 index 0000000..29217a2 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pkparse.c @@ -0,0 +1,1256 @@ +/* + * Public Key layer for parsing key files and structures + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PK_PARSE_C) + +#include "polarssl/pk.h" +#include "polarssl/asn1.h" +#include "polarssl/oid.h" + +#if defined(POLARSSL_RSA_C) +#include "polarssl/rsa.h" +#endif +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif +#if defined(POLARSSL_ECDSA_C) +#include "polarssl/ecdsa.h" +#endif +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif +#if defined(POLARSSL_PKCS5_C) +#include "polarssl/pkcs5.h" +#endif +#if defined(POLARSSL_PKCS12_C) +#include "polarssl/pkcs12.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#if defined(POLARSSL_FS_IO) +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Load all data from a file into a given buffer. + */ +static int load_file( const char *path, unsigned char **buf, size_t *n ) +{ + FILE *f; + long size; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_PK_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + if( ( size = ftell( f ) ) == -1 ) + { + fclose( f ); + return( POLARSSL_ERR_PK_FILE_IO_ERROR ); + } + fseek( f, 0, SEEK_SET ); + + *n = (size_t) size; + + if( *n + 1 == 0 || + ( *buf = (unsigned char *) polarssl_malloc( *n + 1 ) ) == NULL ) + { + fclose( f ); + return( POLARSSL_ERR_PK_MALLOC_FAILED ); + } + + if( fread( *buf, 1, *n, f ) != *n ) + { + fclose( f ); + polarssl_free( *buf ); + return( POLARSSL_ERR_PK_FILE_IO_ERROR ); + } + + fclose( f ); + + (*buf)[*n] = '\0'; + + return( 0 ); +} + +/* + * Load and parse a private key + */ +int pk_parse_keyfile( pk_context *ctx, + const char *path, const char *pwd ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + if( pwd == NULL ) + ret = pk_parse_key( ctx, buf, n, NULL, 0 ); + else + ret = pk_parse_key( ctx, buf, n, + (const unsigned char *) pwd, strlen( pwd ) ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} + +/* + * Load and parse a public key + */ +int pk_parse_public_keyfile( pk_context *ctx, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = pk_parse_public_key( ctx, buf, n ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(POLARSSL_ECP_C) +/* Minimally parse an ECParameters buffer to and asn1_buf + * + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } + * -- implicitCurve NULL + * } + */ +static int pk_get_ecparams( unsigned char **p, const unsigned char *end, + asn1_buf *params ) +{ + int ret; + + /* Tag may be either OID or SEQUENCE */ + params->tag = **p; + if( params->tag != ASN1_OID +#if defined(POLARSSL_PK_PARSE_EC_EXTENDED) + && params->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) +#endif + ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + } + + if( ( ret = asn1_get_tag( p, end, ¶ms->len, params->tag ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + params->p = *p; + *p += params->len; + + if( *p != end ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +#if defined(POLARSSL_PK_PARSE_EC_EXTENDED) +/* + * Parse a SpecifiedECDomain (SEC 1 C.2) and (mostly) fill the group with it. + * WARNING: the resulting group should only be used with + * pk_group_id_from_specified(), since its base point may not be set correctly + * if it was encoded compressed. + * + * SpecifiedECDomain ::= SEQUENCE { + * version SpecifiedECDomainVersion(ecdpVer1 | ecdpVer2 | ecdpVer3, ...), + * fieldID FieldID {{FieldTypes}}, + * curve Curve, + * base ECPoint, + * order INTEGER, + * cofactor INTEGER OPTIONAL, + * hash HashAlgorithm OPTIONAL, + * ... + * } + * + * We only support prime-field as field type, and ignore hash and cofactor. + */ +static int pk_group_from_specified( const asn1_buf *params, ecp_group *grp ) +{ + int ret; + unsigned char *p = params->p; + const unsigned char * const end = params->p + params->len; + const unsigned char *end_field, *end_curve; + size_t len; + int ver; + + /* SpecifiedECDomainVersion ::= INTEGER { 1, 2, 3 } */ + if( ( ret = asn1_get_int( &p, end, &ver ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( ver < 1 || ver > 3 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); + + /* + * FieldID { FIELD-ID:IOSet } ::= SEQUENCE { -- Finite field + * fieldType FIELD-ID.&id({IOSet}), + * parameters FIELD-ID.&Type({IOSet}{@fieldType}) + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( ret ); + + end_field = p + len; + + /* + * FIELD-ID ::= TYPE-IDENTIFIER + * FieldTypes FIELD-ID ::= { + * { Prime-p IDENTIFIED BY prime-field } | + * { Characteristic-two IDENTIFIED BY characteristic-two-field } + * } + * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + */ + if( ( ret = asn1_get_tag( &p, end_field, &len, ASN1_OID ) ) != 0 ) + return( ret ); + + if( len != OID_SIZE( OID_ANSI_X9_62_PRIME_FIELD ) || + memcmp( p, OID_ANSI_X9_62_PRIME_FIELD, len ) != 0 ) + { + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); + } + + p += len; + + /* Prime-p ::= INTEGER -- Field of size p. */ + if( ( ret = asn1_get_mpi( &p, end_field, &grp->P ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + grp->pbits = mpi_msb( &grp->P ); + + if( p != end_field ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + /* + * Curve ::= SEQUENCE { + * a FieldElement, + * b FieldElement, + * seed BIT STRING OPTIONAL + * -- Shall be present if used in SpecifiedECDomain + * -- with version equal to ecdpVer2 or ecdpVer3 + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( ret ); + + end_curve = p + len; + + /* + * FieldElement ::= OCTET STRING + * containing an integer in the case of a prime field + */ + if( ( ret = asn1_get_tag( &p, end_curve, &len, ASN1_OCTET_STRING ) ) != 0 || + ( ret = mpi_read_binary( &grp->A, p, len ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + p += len; + + if( ( ret = asn1_get_tag( &p, end_curve, &len, ASN1_OCTET_STRING ) ) != 0 || + ( ret = mpi_read_binary( &grp->B, p, len ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + p += len; + + /* Ignore seed BIT STRING OPTIONAL */ + if( ( ret = asn1_get_tag( &p, end_curve, &len, ASN1_BIT_STRING ) ) == 0 ) + p += len; + + if( p != end_curve ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + /* + * ECPoint ::= OCTET STRING + */ + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( ( ret = ecp_point_read_binary( grp, &grp->G, + ( const unsigned char *) p, len ) ) != 0 ) + { + /* + * If we can't read the point because it's compressed, cheat by + * reading only the X coordinate and the parity bit of Y. + */ + if( ret != POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE || + ( p[0] != 0x02 && p[0] != 0x03 ) || + len != mpi_size( &grp->P ) + 1 || + mpi_read_binary( &grp->G.X, p + 1, len - 1 ) != 0 || + mpi_lset( &grp->G.Y, p[0] - 2 ) != 0 || + mpi_lset( &grp->G.Z, 1 ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); + } + } + + p += len; + + /* + * order INTEGER + */ + if( ( ret = asn1_get_mpi( &p, end, &grp->N ) ) ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + grp->nbits = mpi_msb( &grp->N ); + + /* + * Allow optional elements by purposefully not enforcing p == end here. + */ + + return( 0 ); +} + +/* + * Find the group id associated with an (almost filled) group as generated by + * pk_group_from_specified(), or return an error if unknown. + */ +static int pk_group_id_from_group( const ecp_group *grp, ecp_group_id *grp_id ) +{ + int ret = 0; + ecp_group ref; + const ecp_group_id *id; + + ecp_group_init( &ref ); + + for( id = ecp_grp_id_list(); *id != POLARSSL_ECP_DP_NONE; id++ ) + { + /* Load the group associated to that id */ + ecp_group_free( &ref ); + MPI_CHK( ecp_use_known_dp( &ref, *id ) ); + + /* Compare to the group we were given, starting with easy tests */ + if( grp->pbits == ref.pbits && grp->nbits == ref.nbits && + mpi_cmp_mpi( &grp->P, &ref.P ) == 0 && + mpi_cmp_mpi( &grp->A, &ref.A ) == 0 && + mpi_cmp_mpi( &grp->B, &ref.B ) == 0 && + mpi_cmp_mpi( &grp->N, &ref.N ) == 0 && + mpi_cmp_mpi( &grp->G.X, &ref.G.X ) == 0 && + mpi_cmp_mpi( &grp->G.Z, &ref.G.Z ) == 0 && + /* For Y we may only know the parity bit, so compare only that */ + mpi_get_bit( &grp->G.Y, 0 ) == mpi_get_bit( &ref.G.Y, 0 ) ) + { + break; + } + + } + +cleanup: + ecp_group_free( &ref ); + + *grp_id = *id; + + if( ret == 0 && *id == POLARSSL_ECP_DP_NONE ) + ret = POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE; + + return( ret ); +} + +/* + * Parse a SpecifiedECDomain (SEC 1 C.2) and find the associated group ID + */ +static int pk_group_id_from_specified( const asn1_buf *params, + ecp_group_id *grp_id ) +{ + int ret; + ecp_group grp; + + ecp_group_init( &grp ); + + if( ( ret = pk_group_from_specified( params, &grp ) ) != 0 ) + goto cleanup; + + ret = pk_group_id_from_group( &grp, grp_id ); + +cleanup: + ecp_group_free( &grp ); + + return( ret ); +} +#endif /* POLARSSL_PK_PARSE_EC_EXTENDED */ + +/* + * Use EC parameters to initialise an EC group + * + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } + * -- implicitCurve NULL + */ +static int pk_use_ecparams( const asn1_buf *params, ecp_group *grp ) +{ + int ret; + ecp_group_id grp_id; + + if( params->tag == ASN1_OID ) + { + if( oid_get_ec_grp( params, &grp_id ) != 0 ) + return( POLARSSL_ERR_PK_UNKNOWN_NAMED_CURVE ); + } + else + { +#if defined(POLARSSL_PK_PARSE_EC_EXTENDED) + if( ( ret = pk_group_id_from_specified( params, &grp_id ) ) != 0 ) + return( ret ); +#else + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); +#endif + } + + /* + * grp may already be initilialized; if so, make sure IDs match + */ + if( grp->id != POLARSSL_ECP_DP_NONE && grp->id != grp_id ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); + + if( ( ret = ecp_use_known_dp( grp, grp_id ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +/* + * EC public key is an EC point + * + * The caller is responsible for clearing the structure upon failure if + * desired. Take care to pass along the possible ECP_FEATURE_UNAVAILABLE + * return code of ecp_point_read_binary() and leave p in a usable state. + */ +static int pk_get_ecpubkey( unsigned char **p, const unsigned char *end, + ecp_keypair *key ) +{ + int ret; + + if( ( ret = ecp_point_read_binary( &key->grp, &key->Q, + (const unsigned char *) *p, end - *p ) ) == 0 ) + { + ret = ecp_check_pubkey( &key->grp, &key->Q ); + } + + /* + * We know ecp_point_read_binary consumed all bytes or failed + */ + *p = (unsigned char *) end; + + return( ret ); +} +#endif /* POLARSSL_ECP_C */ + +#if defined(POLARSSL_RSA_C) +/* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ +static int pk_get_rsapubkey( unsigned char **p, + const unsigned char *end, + rsa_context *rsa ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + ret ); + + if( *p + len != end ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( ret = asn1_get_mpi( p, end, &rsa->N ) ) != 0 || + ( ret = asn1_get_mpi( p, end, &rsa->E ) ) != 0 ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + ret ); + + if( *p != end ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( ret = rsa_check_pubkey( rsa ) ) != 0 ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY ); + + rsa->len = mpi_size( &rsa->N ); + + return( 0 ); +} +#endif /* POLARSSL_RSA_C */ + +/* Get a PK algorithm identifier + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +static int pk_get_pk_alg( unsigned char **p, + const unsigned char *end, + pk_type_t *pk_alg, asn1_buf *params ) +{ + int ret; + asn1_buf alg_oid; + + memset( params, 0, sizeof(asn1_buf) ); + + if( ( ret = asn1_get_alg( p, end, &alg_oid, params ) ) != 0 ) + return( POLARSSL_ERR_PK_INVALID_ALG + ret ); + + if( oid_get_pk_alg( &alg_oid, pk_alg ) != 0 ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + /* + * No parameters with RSA (only for EC) + */ + if( *pk_alg == POLARSSL_PK_RSA && + ( ( params->tag != ASN1_NULL && params->tag != 0 ) || + params->len != 0 ) ) + { + return( POLARSSL_ERR_PK_INVALID_ALG ); + } + + return( 0 ); +} + +/* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ +int pk_parse_subpubkey( unsigned char **p, const unsigned char *end, + pk_context *pk ) +{ + int ret; + size_t len; + asn1_buf alg_params; + pk_type_t pk_alg = POLARSSL_PK_NONE; + const pk_info_t *pk_info; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + end = *p + len; + + if( ( ret = pk_get_pk_alg( p, end, &pk_alg, &alg_params ) ) != 0 ) + return( ret ); + + if( ( ret = asn1_get_bitstring_null( p, end, &len ) ) != 0 ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + ret ); + + if( *p + len != end ) + return( POLARSSL_ERR_PK_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( pk_info = pk_info_from_type( pk_alg ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 ) + return( ret ); + +#if defined(POLARSSL_RSA_C) + if( pk_alg == POLARSSL_PK_RSA ) + { + ret = pk_get_rsapubkey( p, end, pk_rsa( *pk ) ); + } else +#endif /* POLARSSL_RSA_C */ +#if defined(POLARSSL_ECP_C) + if( pk_alg == POLARSSL_PK_ECKEY_DH || pk_alg == POLARSSL_PK_ECKEY ) + { + ret = pk_use_ecparams( &alg_params, &pk_ec( *pk )->grp ); + if( ret == 0 ) + ret = pk_get_ecpubkey( p, end, pk_ec( *pk ) ); + } else +#endif /* POLARSSL_ECP_C */ + ret = POLARSSL_ERR_PK_UNKNOWN_PK_ALG; + + if( ret == 0 && *p != end ) + ret = POLARSSL_ERR_PK_INVALID_PUBKEY + POLARSSL_ERR_ASN1_LENGTH_MISMATCH; + + if( ret != 0 ) + pk_free( pk ); + + return( ret ); +} + +#if defined(POLARSSL_RSA_C) +/* + * Parse a PKCS#1 encoded private RSA key + */ +static int pk_parse_key_pkcs1_der( rsa_context *rsa, + const unsigned char *key, + size_t keylen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + + p = (unsigned char *) key; + end = p + keylen; + + /* + * This function parses the RSAPrivateKey (PKCS#1) + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + if( rsa->ver != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_VERSION ); + } + + if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) + { + rsa_free( rsa ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + rsa->len = mpi_size( &rsa->N ); + + if( p != end ) + { + rsa_free( rsa ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) + { + rsa_free( rsa ); + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) +/* + * Parse a SEC1 encoded private EC key + */ +static int pk_parse_key_sec1_der( ecp_keypair *eck, + const unsigned char *key, + size_t keylen ) +{ + int ret; + int version, pubkey_done; + size_t len; + asn1_buf params; + unsigned char *p = (unsigned char *) key; + unsigned char *end = p + keylen; + unsigned char *end2; + + /* + * RFC 5915, or SEC1 Appendix C.4 + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &version ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( version != 1 ) + return( POLARSSL_ERR_PK_KEY_INVALID_VERSION ); + + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( ( ret = mpi_read_binary( &eck->d, p, len ) ) != 0 ) + { + ecp_keypair_free( eck ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + p += len; + + /* + * Is 'parameters' present? + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) == 0 ) + { + if( ( ret = pk_get_ecparams( &p, p + len, ¶ms) ) != 0 || + ( ret = pk_use_ecparams( ¶ms, &eck->grp ) ) != 0 ) + { + ecp_keypair_free( eck ); + return( ret ); + } + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + ecp_keypair_free( eck ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + /* + * Is 'publickey' present? If not, or if we can't read it (eg because it + * is compressed), create it from the private key. + */ + pubkey_done = 0; + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1 ) ) == 0 ) + { + end2 = p + len; + + if( ( ret = asn1_get_bitstring_null( &p, end2, &len ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( p + len != end2 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( ret = pk_get_ecpubkey( &p, end2, eck ) ) == 0 ) + pubkey_done = 1; + else + { + /* + * The only acceptable failure mode of pk_get_ecpubkey() above + * is if the point format is not recognized. + */ + if( ret != POLARSSL_ERR_ECP_FEATURE_UNAVAILABLE ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); + } + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + ecp_keypair_free( eck ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + if( ! pubkey_done && + ( ret = ecp_mul( &eck->grp, &eck->Q, &eck->d, &eck->grp.G, + NULL, NULL ) ) != 0 ) + { + ecp_keypair_free( eck ); + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + if( ( ret = ecp_check_privkey( &eck->grp, &eck->d ) ) != 0 ) + { + ecp_keypair_free( eck ); + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_ECP_C */ + +/* + * Parse an unencrypted PKCS#8 encoded private key + */ +static int pk_parse_key_pkcs8_unencrypted_der( + pk_context *pk, + const unsigned char* key, + size_t keylen ) +{ + int ret, version; + size_t len; + asn1_buf params; + unsigned char *p = (unsigned char *) key; + unsigned char *end = p + keylen; + pk_type_t pk_alg = POLARSSL_PK_NONE; + const pk_info_t *pk_info; + + /* + * This function parses the PrivatKeyInfo object (PKCS#8 v1.2 = RFC 5208) + * + * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] IMPLICIT Attributes OPTIONAL } + * + * Version ::= INTEGER + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * PrivateKey ::= OCTET STRING + * + * The PrivateKey OCTET STRING is a SEC1 ECPrivateKey + */ + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &version ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( version != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_VERSION + ret ); + + if( ( ret = pk_get_pk_alg( &p, end, &pk_alg, ¶ms ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( len < 1 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( ( pk_info = pk_info_from_type( pk_alg ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 ) + return( ret ); + +#if defined(POLARSSL_RSA_C) + if( pk_alg == POLARSSL_PK_RSA ) + { + if( ( ret = pk_parse_key_pkcs1_der( pk_rsa( *pk ), p, len ) ) != 0 ) + { + pk_free( pk ); + return( ret ); + } + } else +#endif /* POLARSSL_RSA_C */ +#if defined(POLARSSL_ECP_C) + if( pk_alg == POLARSSL_PK_ECKEY || pk_alg == POLARSSL_PK_ECKEY_DH ) + { + if( ( ret = pk_use_ecparams( ¶ms, &pk_ec( *pk )->grp ) ) != 0 || + ( ret = pk_parse_key_sec1_der( pk_ec( *pk ), p, len ) ) != 0 ) + { + pk_free( pk ); + return( ret ); + } + } else +#endif /* POLARSSL_ECP_C */ + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + return( 0 ); +} + +/* + * Parse an encrypted PKCS#8 encoded private key + */ +static int pk_parse_key_pkcs8_encrypted_der( + pk_context *pk, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen ) +{ + int ret, decrypted = 0; + size_t len; + unsigned char buf[2048]; + unsigned char *p, *end; + asn1_buf pbe_alg_oid, pbe_params; +#if defined(POLARSSL_PKCS12_C) + cipher_type_t cipher_alg; + md_type_t md_alg; +#endif + + memset( buf, 0, sizeof( buf ) ); + + p = (unsigned char *) key; + end = p + keylen; + + if( pwdlen == 0 ) + return( POLARSSL_ERR_PK_PASSWORD_REQUIRED ); + + /* + * This function parses the EncryptedPrivatKeyInfo object (PKCS#8) + * + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm EncryptionAlgorithmIdentifier, + * encryptedData EncryptedData + * } + * + * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * EncryptedData ::= OCTET STRING + * + * The EncryptedData OCTET STRING is a PKCS#8 PrivateKeyInfo + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_alg( &p, end, &pbe_alg_oid, &pbe_params ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT + ret ); + + if( len > sizeof( buf ) ) + return( POLARSSL_ERR_PK_BAD_INPUT_DATA ); + + /* + * Decrypt EncryptedData with appropriate PDE + */ +#if defined(POLARSSL_PKCS12_C) + if( oid_get_pkcs12_pbe_alg( &pbe_alg_oid, &md_alg, &cipher_alg ) == 0 ) + { + if( ( ret = pkcs12_pbe( &pbe_params, PKCS12_PBE_DECRYPT, + cipher_alg, md_alg, + pwd, pwdlen, p, len, buf ) ) != 0 ) + { + if( ret == POLARSSL_ERR_PKCS12_PASSWORD_MISMATCH ) + return( POLARSSL_ERR_PK_PASSWORD_MISMATCH ); + + return( ret ); + } + + decrypted = 1; + } + else if( OID_CMP( OID_PKCS12_PBE_SHA1_RC4_128, &pbe_alg_oid ) ) + { + if( ( ret = pkcs12_pbe_sha1_rc4_128( &pbe_params, + PKCS12_PBE_DECRYPT, + pwd, pwdlen, + p, len, buf ) ) != 0 ) + { + return( ret ); + } + + // Best guess for password mismatch when using RC4. If first tag is + // not ASN1_CONSTRUCTED | ASN1_SEQUENCE + // + if( *buf != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_PK_PASSWORD_MISMATCH ); + + decrypted = 1; + } + else +#endif /* POLARSSL_PKCS12_C */ +#if defined(POLARSSL_PKCS5_C) + if( OID_CMP( OID_PKCS5_PBES2, &pbe_alg_oid ) ) + { + if( ( ret = pkcs5_pbes2( &pbe_params, PKCS5_DECRYPT, pwd, pwdlen, + p, len, buf ) ) != 0 ) + { + if( ret == POLARSSL_ERR_PKCS5_PASSWORD_MISMATCH ) + return( POLARSSL_ERR_PK_PASSWORD_MISMATCH ); + + return( ret ); + } + + decrypted = 1; + } + else +#endif /* POLARSSL_PKCS5_C */ + { + ((void) pwd); + } + + if( decrypted == 0 ) + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); + + return( pk_parse_key_pkcs8_unencrypted_der( pk, buf, len ) ); +} + +/* + * Parse a private key + */ +int pk_parse_key( pk_context *pk, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen ) +{ + int ret; + const pk_info_t *pk_info; + +#if defined(POLARSSL_PEM_PARSE_C) + size_t len; + pem_context pem; + + pem_init( &pem ); + +#if defined(POLARSSL_RSA_C) + ret = pem_read_buffer( &pem, + "-----BEGIN RSA PRIVATE KEY-----", + "-----END RSA PRIVATE KEY-----", + key, pwd, pwdlen, &len ); + if( ret == 0 ) + { + if( ( pk_info = pk_info_from_type( POLARSSL_PK_RSA ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 || + ( ret = pk_parse_key_pkcs1_der( pk_rsa( *pk ), + pem.buf, pem.buflen ) ) != 0 ) + { + pk_free( pk ); + } + + pem_free( &pem ); + return( ret ); + } + else if( ret == POLARSSL_ERR_PEM_PASSWORD_MISMATCH ) + return( POLARSSL_ERR_PK_PASSWORD_MISMATCH ); + else if( ret == POLARSSL_ERR_PEM_PASSWORD_REQUIRED ) + return( POLARSSL_ERR_PK_PASSWORD_REQUIRED ); + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + return( ret ); +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) + ret = pem_read_buffer( &pem, + "-----BEGIN EC PRIVATE KEY-----", + "-----END EC PRIVATE KEY-----", + key, pwd, pwdlen, &len ); + if( ret == 0 ) + { + if( ( pk_info = pk_info_from_type( POLARSSL_PK_ECKEY ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 || + ( ret = pk_parse_key_sec1_der( pk_ec( *pk ), + pem.buf, pem.buflen ) ) != 0 ) + { + pk_free( pk ); + } + + pem_free( &pem ); + return( ret ); + } + else if( ret == POLARSSL_ERR_PEM_PASSWORD_MISMATCH ) + return( POLARSSL_ERR_PK_PASSWORD_MISMATCH ); + else if( ret == POLARSSL_ERR_PEM_PASSWORD_REQUIRED ) + return( POLARSSL_ERR_PK_PASSWORD_REQUIRED ); + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + return( ret ); +#endif /* POLARSSL_ECP_C */ + + ret = pem_read_buffer( &pem, + "-----BEGIN PRIVATE KEY-----", + "-----END PRIVATE KEY-----", + key, NULL, 0, &len ); + if( ret == 0 ) + { + if( ( ret = pk_parse_key_pkcs8_unencrypted_der( pk, + pem.buf, pem.buflen ) ) != 0 ) + { + pk_free( pk ); + } + + pem_free( &pem ); + return( ret ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + return( ret ); + + ret = pem_read_buffer( &pem, + "-----BEGIN ENCRYPTED PRIVATE KEY-----", + "-----END ENCRYPTED PRIVATE KEY-----", + key, NULL, 0, &len ); + if( ret == 0 ) + { + if( ( ret = pk_parse_key_pkcs8_encrypted_der( pk, + pem.buf, pem.buflen, + pwd, pwdlen ) ) != 0 ) + { + pk_free( pk ); + } + + pem_free( &pem ); + return( ret ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + return( ret ); +#else + ((void) pwd); + ((void) pwdlen); +#endif /* POLARSSL_PEM_PARSE_C */ + + /* + * At this point we only know it's not a PEM formatted key. Could be any + * of the known DER encoded private key formats + * + * We try the different DER format parsers to see if one passes without + * error + */ + if( ( ret = pk_parse_key_pkcs8_encrypted_der( pk, key, keylen, + pwd, pwdlen ) ) == 0 ) + { + return( 0 ); + } + + pk_free( pk ); + + if( ret == POLARSSL_ERR_PK_PASSWORD_MISMATCH ) + { + return( ret ); + } + + if( ( ret = pk_parse_key_pkcs8_unencrypted_der( pk, key, keylen ) ) == 0 ) + return( 0 ); + + pk_free( pk ); + +#if defined(POLARSSL_RSA_C) + if( ( pk_info = pk_info_from_type( POLARSSL_PK_RSA ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 || + ( ret = pk_parse_key_pkcs1_der( pk_rsa( *pk ), key, keylen ) ) == 0 ) + { + return( 0 ); + } + + pk_free( pk ); +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) + if( ( pk_info = pk_info_from_type( POLARSSL_PK_ECKEY ) ) == NULL ) + return( POLARSSL_ERR_PK_UNKNOWN_PK_ALG ); + + if( ( ret = pk_init_ctx( pk, pk_info ) ) != 0 || + ( ret = pk_parse_key_sec1_der( pk_ec( *pk ), key, keylen ) ) == 0 ) + { + return( 0 ); + } + + pk_free( pk ); +#endif /* POLARSSL_ECP_C */ + + return( POLARSSL_ERR_PK_KEY_INVALID_FORMAT ); +} + +/* + * Parse a public key + */ +int pk_parse_public_key( pk_context *ctx, + const unsigned char *key, size_t keylen ) +{ + int ret; + unsigned char *p; +#if defined(POLARSSL_PEM_PARSE_C) + size_t len; + pem_context pem; + + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN PUBLIC KEY-----", + "-----END PUBLIC KEY-----", + key, NULL, 0, &len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + key = pem.buf; + keylen = pem.buflen; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } +#endif /* POLARSSL_PEM_PARSE_C */ + p = (unsigned char *) key; + + ret = pk_parse_subpubkey( &p, p + keylen, ctx ); + +#if defined(POLARSSL_PEM_PARSE_C) + pem_free( &pem ); +#endif + + return( ret ); +} + +#endif /* POLARSSL_PK_PARSE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/pkwrite.c b/component/common/network/ssl/polarssl-1.3.8/library/pkwrite.c new file mode 100644 index 0000000..3b0bbdb --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/pkwrite.c @@ -0,0 +1,358 @@ +/* + * Public Key layer for writing key files and structures + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PK_WRITE_C) + +#include "polarssl/pk.h" +#include "polarssl/asn1write.h" +#include "polarssl/oid.h" + +#if defined(POLARSSL_RSA_C) +#include "polarssl/rsa.h" +#endif +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif +#if defined(POLARSSL_ECDSA_C) +#include "polarssl/ecdsa.h" +#endif +#if defined(POLARSSL_PEM_WRITE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#if defined(POLARSSL_RSA_C) +/* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ +static int pk_write_rsa_pubkey( unsigned char **p, unsigned char *start, + rsa_context *rsa ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_mpi( p, start, &rsa->E ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( p, start, &rsa->N ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} +#endif /* POLARSSL_RSA_C */ + +#if defined(POLARSSL_ECP_C) +/* + * EC public key is an EC point + */ +static int pk_write_ec_pubkey( unsigned char **p, unsigned char *start, + ecp_keypair *ec ) +{ + int ret; + size_t len = 0; + unsigned char buf[POLARSSL_ECP_MAX_PT_LEN]; + + if( ( ret = ecp_point_write_binary( &ec->grp, &ec->Q, + POLARSSL_ECP_PF_UNCOMPRESSED, + &len, buf, sizeof( buf ) ) ) != 0 ) + { + return( ret ); + } + + if( *p - start < (int) len ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + *p -= len; + memcpy( *p, buf, len ); + + return( (int) len ); +} + +/* + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * } + */ +static int pk_write_ec_param( unsigned char **p, unsigned char *start, + ecp_keypair *ec ) +{ + int ret; + size_t len = 0; + const char *oid; + size_t oid_len; + + if( ( ret = oid_get_oid_by_ec_grp( ec->grp.id, &oid, &oid_len ) ) != 0 ) + return( ret ); + + ASN1_CHK_ADD( len, asn1_write_oid( p, start, oid, oid_len ) ); + + return( (int) len ); +} +#endif /* POLARSSL_ECP_C */ + +int pk_write_pubkey( unsigned char **p, unsigned char *start, + const pk_context *key ) +{ + int ret; + size_t len = 0; + +#if defined(POLARSSL_RSA_C) + if( pk_get_type( key ) == POLARSSL_PK_RSA ) + ASN1_CHK_ADD( len, pk_write_rsa_pubkey( p, start, pk_rsa( *key ) ) ); + else +#endif +#if defined(POLARSSL_ECP_C) + if( pk_get_type( key ) == POLARSSL_PK_ECKEY ) + ASN1_CHK_ADD( len, pk_write_ec_pubkey( p, start, pk_ec( *key ) ) ); + else +#endif + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); + + return( (int) len ); +} + +int pk_write_pubkey_der( pk_context *key, unsigned char *buf, size_t size ) +{ + int ret; + unsigned char *c; + size_t len = 0, par_len = 0, oid_len; + const char *oid; + + c = buf + size; + + ASN1_CHK_ADD( len, pk_write_pubkey( &c, buf, key ) ); + + if( c - buf < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + *--c = 0; + len += 1; + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_BIT_STRING ) ); + + if( ( ret = oid_get_oid_by_pk_alg( pk_get_type( key ), + &oid, &oid_len ) ) != 0 ) + { + return( ret ); + } + +#if defined(POLARSSL_ECP_C) + if( pk_get_type( key ) == POLARSSL_PK_ECKEY ) + { + ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, pk_ec( *key ) ) ); + } +#endif + + ASN1_CHK_ADD( len, asn1_write_algorithm_identifier( &c, buf, oid, oid_len, + par_len ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +int pk_write_key_der( pk_context *key, unsigned char *buf, size_t size ) +{ + int ret; + unsigned char *c = buf + size; + size_t len = 0; + +#if defined(POLARSSL_RSA_C) + if( pk_get_type( key ) == POLARSSL_PK_RSA ) + { + rsa_context *rsa = pk_rsa( *key ); + + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->QP ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->DQ ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->DP ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->Q ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->P ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->D ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->E ) ); + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &rsa->N ) ); + ASN1_CHK_ADD( len, asn1_write_int( &c, buf, 0 ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + } + else +#endif /* POLARSSL_RSA_C */ +#if defined(POLARSSL_ECP_C) + if( pk_get_type( key ) == POLARSSL_PK_ECKEY ) + { + ecp_keypair *ec = pk_ec( *key ); + size_t pub_len = 0, par_len = 0; + + /* + * RFC 5915, or SEC1 Appendix C.4 + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + */ + + /* publicKey */ + ASN1_CHK_ADD( pub_len, pk_write_ec_pubkey( &c, buf, ec ) ); + + if( c - buf < 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + *--c = 0; + pub_len += 1; + + ASN1_CHK_ADD( pub_len, asn1_write_len( &c, buf, pub_len ) ); + ASN1_CHK_ADD( pub_len, asn1_write_tag( &c, buf, ASN1_BIT_STRING ) ); + + ASN1_CHK_ADD( pub_len, asn1_write_len( &c, buf, pub_len ) ); + ASN1_CHK_ADD( pub_len, asn1_write_tag( &c, buf, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1 ) ); + len += pub_len; + + /* parameters */ + ASN1_CHK_ADD( par_len, pk_write_ec_param( &c, buf, ec ) ); + + ASN1_CHK_ADD( par_len, asn1_write_len( &c, buf, par_len ) ); + ASN1_CHK_ADD( par_len, asn1_write_tag( &c, buf, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ); + len += par_len; + + /* privateKey: write as MPI then fix tag */ + ASN1_CHK_ADD( len, asn1_write_mpi( &c, buf, &ec->d ) ); + *c = ASN1_OCTET_STRING; + + /* version */ + ASN1_CHK_ADD( len, asn1_write_int( &c, buf, 1 ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + } + else +#endif /* POLARSSL_ECP_C */ + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); + + return( (int) len ); +} + +#if defined(POLARSSL_PEM_WRITE_C) + +#define PEM_BEGIN_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\n" +#define PEM_END_PUBLIC_KEY "-----END PUBLIC KEY-----\n" + +#define PEM_BEGIN_PRIVATE_KEY_RSA "-----BEGIN RSA PRIVATE KEY-----\n" +#define PEM_END_PRIVATE_KEY_RSA "-----END RSA PRIVATE KEY-----\n" +#define PEM_BEGIN_PRIVATE_KEY_EC "-----BEGIN EC PRIVATE KEY-----\n" +#define PEM_END_PRIVATE_KEY_EC "-----END EC PRIVATE KEY-----\n" + +int pk_write_pubkey_pem( pk_context *key, unsigned char *buf, size_t size ) +{ + int ret; + unsigned char output_buf[4096]; + size_t olen = 0; + + if( ( ret = pk_write_pubkey_der( key, output_buf, + sizeof(output_buf) ) ) < 0 ) + { + return( ret ); + } + + if( ( ret = pem_write_buffer( PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} + +int pk_write_key_pem( pk_context *key, unsigned char *buf, size_t size ) +{ + int ret; + unsigned char output_buf[4096]; + const char *begin, *end; + size_t olen = 0; + + if( ( ret = pk_write_key_der( key, output_buf, sizeof(output_buf) ) ) < 0 ) + return( ret ); + +#if defined(POLARSSL_RSA_C) + if( pk_get_type( key ) == POLARSSL_PK_RSA ) + { + begin = PEM_BEGIN_PRIVATE_KEY_RSA; + end = PEM_END_PRIVATE_KEY_RSA; + } + else +#endif +#if defined(POLARSSL_ECP_C) + if( pk_get_type( key ) == POLARSSL_PK_ECKEY ) + { + begin = PEM_BEGIN_PRIVATE_KEY_EC; + end = PEM_END_PRIVATE_KEY_EC; + } + else +#endif + return( POLARSSL_ERR_PK_FEATURE_UNAVAILABLE ); + + if( ( ret = pem_write_buffer( begin, end, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_PEM_WRITE_C */ + +#endif /* POLARSSL_PK_WRITE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/platform.c b/component/common/network/ssl/polarssl-1.3.8/library/platform.c new file mode 100644 index 0000000..d57cbc8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/platform.c @@ -0,0 +1,116 @@ +/* + * Platform abstraction layer + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_PLATFORM_C) + +#include "polarssl/platform.h" + +#if defined(POLARSSL_PLATFORM_MEMORY) +#if !defined(POLARSSL_PLATFORM_STD_MALLOC) +static void *platform_malloc_uninit( size_t len ) +{ + ((void) len); + return( NULL ); +} + +#define POLARSSL_PLATFORM_STD_MALLOC platform_malloc_uninit +#endif /* !POLARSSL_PLATFORM_STD_MALLOC */ + +#if !defined(POLARSSL_PLATFORM_STD_FREE) +static void platform_free_uninit( void *ptr ) +{ + ((void) ptr); +} + +#define POLARSSL_PLATFORM_STD_FREE platform_free_uninit +#endif /* !POLARSSL_PLATFORM_STD_FREE */ + +void * (*polarssl_malloc)( size_t ) = POLARSSL_PLATFORM_STD_MALLOC; +void (*polarssl_free)( void * ) = POLARSSL_PLATFORM_STD_FREE; + +int platform_set_malloc_free( void * (*malloc_func)( size_t ), + void (*free_func)( void * ) ) +{ + polarssl_malloc = malloc_func; + polarssl_free = free_func; + return( 0 ); +} +#endif /* POLARSSL_PLATFORM_MEMORY */ + +#if defined(POLARSSL_PLATFORM_PRINTF_ALT) +#if !defined(POLARSSL_PLATFORM_STD_PRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_printf_uninit( const char *format, ... ) +{ + ((void) format); + return( 0 ); +} + +#define POLARSSL_PLATFORM_STD_PRINTF platform_printf_uninit +#endif /* !POLARSSL_PLATFORM_STD_PRINTF */ + +int (*polarssl_printf)( const char *, ... ) = POLARSSL_PLATFORM_STD_PRINTF; + +int platform_set_printf( int (*printf_func)( const char *, ... ) ) +{ + polarssl_printf = printf_func; + return( 0 ); +} +#endif /* POLARSSL_PLATFORM_PRINTF_ALT */ + +#if defined(POLARSSL_PLATFORM_FPRINTF_ALT) +#if !defined(POLARSSL_PLATFORM_STD_FPRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_fprintf_uninit( FILE *stream, const char *format, ... ) +{ + ((void) stream); + ((void) format); + return( 0 ); +} + +#define POLARSSL_PLATFORM_STD_FPRINTF platform_fprintf_uninit +#endif /* !POLARSSL_PLATFORM_STD_FPRINTF */ + +int (*polarssl_fprintf)( FILE *, const char *, ... ) = + POLARSSL_PLATFORM_STD_FPRINTF; + +int platform_set_fprintf( int (*fprintf_func)( FILE *, const char *, ... ) ) +{ + polarssl_fprintf = fprintf_func; + return( 0 ); +} +#endif /* POLARSSL_PLATFORM_FPRINTF_ALT */ + +#endif /* POLARSSL_PLATFORM_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ripemd160.c b/component/common/network/ssl/polarssl-1.3.8/library/ripemd160.c new file mode 100644 index 0000000..fcd7760 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ripemd160.c @@ -0,0 +1,653 @@ +/* + * RIPE MD-160 implementation + * + * Copyright (C) 2014-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * The RIPEMD-160 algorithm was designed by RIPE in 1996 + * http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + * http://ehash.iaik.tugraz.at/wiki/RIPEMD-160 + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_RIPEMD160_C) + +#include "polarssl/ripemd160.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] ) \ + | ( (uint32_t) (b)[(i) + 1] << 8 ) \ + | ( (uint32_t) (b)[(i) + 2] << 16 ) \ + | ( (uint32_t) (b)[(i) + 3] << 24 ); \ +} +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \ +} +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +void ripemd160_init( ripemd160_context *ctx ) +{ + memset( ctx, 0, sizeof( ripemd160_context ) ); +} + +void ripemd160_free( ripemd160_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( ripemd160_context ) ); +} + +/* + * RIPEMD-160 context setup + */ +void ripemd160_starts( ripemd160_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +/* + * Process one block + */ +void ripemd160_process( ripemd160_context *ctx, const unsigned char data[64] ) +{ + uint32_t A, B, C, D, E, Ap, Bp, Cp, Dp, Ep, X[16]; + + GET_UINT32_LE( X[ 0], data, 0 ); + GET_UINT32_LE( X[ 1], data, 4 ); + GET_UINT32_LE( X[ 2], data, 8 ); + GET_UINT32_LE( X[ 3], data, 12 ); + GET_UINT32_LE( X[ 4], data, 16 ); + GET_UINT32_LE( X[ 5], data, 20 ); + GET_UINT32_LE( X[ 6], data, 24 ); + GET_UINT32_LE( X[ 7], data, 28 ); + GET_UINT32_LE( X[ 8], data, 32 ); + GET_UINT32_LE( X[ 9], data, 36 ); + GET_UINT32_LE( X[10], data, 40 ); + GET_UINT32_LE( X[11], data, 44 ); + GET_UINT32_LE( X[12], data, 48 ); + GET_UINT32_LE( X[13], data, 52 ); + GET_UINT32_LE( X[14], data, 56 ); + GET_UINT32_LE( X[15], data, 60 ); + + A = Ap = ctx->state[0]; + B = Bp = ctx->state[1]; + C = Cp = ctx->state[2]; + D = Dp = ctx->state[3]; + E = Ep = ctx->state[4]; + +#define F1( x, y, z ) ( x ^ y ^ z ) +#define F2( x, y, z ) ( ( x & y ) | ( ~x & z ) ) +#define F3( x, y, z ) ( ( x | ~y ) ^ z ) +#define F4( x, y, z ) ( ( x & z ) | ( y & ~z ) ) +#define F5( x, y, z ) ( x ^ ( y | ~z ) ) + +#define S( x, n ) ( ( x << n ) | ( x >> (32 - n) ) ) + +#define P( a, b, c, d, e, r, s, f, k ) \ + a += f( b, c, d ) + X[r] + k; \ + a = S( a, s ) + e; \ + c = S( c, 10 ); + +#define P2( a, b, c, d, e, r, s, rp, sp ) \ + P( a, b, c, d, e, r, s, F, K ); \ + P( a ## p, b ## p, c ## p, d ## p, e ## p, rp, sp, Fp, Kp ); + +#define F F1 +#define K 0x00000000 +#define Fp F5 +#define Kp 0x50A28BE6 + P2( A, B, C, D, E, 0, 11, 5, 8 ); + P2( E, A, B, C, D, 1, 14, 14, 9 ); + P2( D, E, A, B, C, 2, 15, 7, 9 ); + P2( C, D, E, A, B, 3, 12, 0, 11 ); + P2( B, C, D, E, A, 4, 5, 9, 13 ); + P2( A, B, C, D, E, 5, 8, 2, 15 ); + P2( E, A, B, C, D, 6, 7, 11, 15 ); + P2( D, E, A, B, C, 7, 9, 4, 5 ); + P2( C, D, E, A, B, 8, 11, 13, 7 ); + P2( B, C, D, E, A, 9, 13, 6, 7 ); + P2( A, B, C, D, E, 10, 14, 15, 8 ); + P2( E, A, B, C, D, 11, 15, 8, 11 ); + P2( D, E, A, B, C, 12, 6, 1, 14 ); + P2( C, D, E, A, B, 13, 7, 10, 14 ); + P2( B, C, D, E, A, 14, 9, 3, 12 ); + P2( A, B, C, D, E, 15, 8, 12, 6 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F2 +#define K 0x5A827999 +#define Fp F4 +#define Kp 0x5C4DD124 + P2( E, A, B, C, D, 7, 7, 6, 9 ); + P2( D, E, A, B, C, 4, 6, 11, 13 ); + P2( C, D, E, A, B, 13, 8, 3, 15 ); + P2( B, C, D, E, A, 1, 13, 7, 7 ); + P2( A, B, C, D, E, 10, 11, 0, 12 ); + P2( E, A, B, C, D, 6, 9, 13, 8 ); + P2( D, E, A, B, C, 15, 7, 5, 9 ); + P2( C, D, E, A, B, 3, 15, 10, 11 ); + P2( B, C, D, E, A, 12, 7, 14, 7 ); + P2( A, B, C, D, E, 0, 12, 15, 7 ); + P2( E, A, B, C, D, 9, 15, 8, 12 ); + P2( D, E, A, B, C, 5, 9, 12, 7 ); + P2( C, D, E, A, B, 2, 11, 4, 6 ); + P2( B, C, D, E, A, 14, 7, 9, 15 ); + P2( A, B, C, D, E, 11, 13, 1, 13 ); + P2( E, A, B, C, D, 8, 12, 2, 11 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F3 +#define K 0x6ED9EBA1 +#define Fp F3 +#define Kp 0x6D703EF3 + P2( D, E, A, B, C, 3, 11, 15, 9 ); + P2( C, D, E, A, B, 10, 13, 5, 7 ); + P2( B, C, D, E, A, 14, 6, 1, 15 ); + P2( A, B, C, D, E, 4, 7, 3, 11 ); + P2( E, A, B, C, D, 9, 14, 7, 8 ); + P2( D, E, A, B, C, 15, 9, 14, 6 ); + P2( C, D, E, A, B, 8, 13, 6, 6 ); + P2( B, C, D, E, A, 1, 15, 9, 14 ); + P2( A, B, C, D, E, 2, 14, 11, 12 ); + P2( E, A, B, C, D, 7, 8, 8, 13 ); + P2( D, E, A, B, C, 0, 13, 12, 5 ); + P2( C, D, E, A, B, 6, 6, 2, 14 ); + P2( B, C, D, E, A, 13, 5, 10, 13 ); + P2( A, B, C, D, E, 11, 12, 0, 13 ); + P2( E, A, B, C, D, 5, 7, 4, 7 ); + P2( D, E, A, B, C, 12, 5, 13, 5 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F4 +#define K 0x8F1BBCDC +#define Fp F2 +#define Kp 0x7A6D76E9 + P2( C, D, E, A, B, 1, 11, 8, 15 ); + P2( B, C, D, E, A, 9, 12, 6, 5 ); + P2( A, B, C, D, E, 11, 14, 4, 8 ); + P2( E, A, B, C, D, 10, 15, 1, 11 ); + P2( D, E, A, B, C, 0, 14, 3, 14 ); + P2( C, D, E, A, B, 8, 15, 11, 14 ); + P2( B, C, D, E, A, 12, 9, 15, 6 ); + P2( A, B, C, D, E, 4, 8, 0, 14 ); + P2( E, A, B, C, D, 13, 9, 5, 6 ); + P2( D, E, A, B, C, 3, 14, 12, 9 ); + P2( C, D, E, A, B, 7, 5, 2, 12 ); + P2( B, C, D, E, A, 15, 6, 13, 9 ); + P2( A, B, C, D, E, 14, 8, 9, 12 ); + P2( E, A, B, C, D, 5, 6, 7, 5 ); + P2( D, E, A, B, C, 6, 5, 10, 15 ); + P2( C, D, E, A, B, 2, 12, 14, 8 ); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F5 +#define K 0xA953FD4E +#define Fp F1 +#define Kp 0x00000000 + P2( B, C, D, E, A, 4, 9, 12, 8 ); + P2( A, B, C, D, E, 0, 15, 15, 5 ); + P2( E, A, B, C, D, 5, 5, 10, 12 ); + P2( D, E, A, B, C, 9, 11, 4, 9 ); + P2( C, D, E, A, B, 7, 6, 1, 12 ); + P2( B, C, D, E, A, 12, 8, 5, 5 ); + P2( A, B, C, D, E, 2, 13, 8, 14 ); + P2( E, A, B, C, D, 10, 12, 7, 6 ); + P2( D, E, A, B, C, 14, 5, 6, 8 ); + P2( C, D, E, A, B, 1, 12, 2, 13 ); + P2( B, C, D, E, A, 3, 13, 13, 6 ); + P2( A, B, C, D, E, 8, 14, 14, 5 ); + P2( E, A, B, C, D, 11, 11, 0, 15 ); + P2( D, E, A, B, C, 6, 8, 3, 13 ); + P2( C, D, E, A, B, 15, 5, 9, 11 ); + P2( B, C, D, E, A, 13, 6, 11, 11 ); +#undef F +#undef K +#undef Fp +#undef Kp + + C = ctx->state[1] + C + Dp; + ctx->state[1] = ctx->state[2] + D + Ep; + ctx->state[2] = ctx->state[3] + E + Ap; + ctx->state[3] = ctx->state[4] + A + Bp; + ctx->state[4] = ctx->state[0] + B + Cp; + ctx->state[0] = C; +} + +/* + * RIPEMD-160 process buffer + */ +void ripemd160_update( ripemd160_context *ctx, + const unsigned char *input, size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + ripemd160_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + ripemd160_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + { + memcpy( (void *) (ctx->buffer + left), input, ilen ); + } +} + +static const unsigned char ripemd160_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 +}; + +/* + * RIPEMD-160 final digest + */ +void ripemd160_finish( ripemd160_context *ctx, unsigned char output[20] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_LE( low, msglen, 0 ); + PUT_UINT32_LE( high, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + ripemd160_update( ctx, ripemd160_padding, padn ); + ripemd160_update( ctx, msglen, 8 ); + + PUT_UINT32_LE( ctx->state[0], output, 0 ); + PUT_UINT32_LE( ctx->state[1], output, 4 ); + PUT_UINT32_LE( ctx->state[2], output, 8 ); + PUT_UINT32_LE( ctx->state[3], output, 12 ); + PUT_UINT32_LE( ctx->state[4], output, 16 ); +} + +/* + * output = RIPEMD-160( input buffer ) + */ +void ripemd160( const unsigned char *input, size_t ilen, + unsigned char output[20] ) +{ + ripemd160_context ctx; + + ripemd160_init( &ctx ); + ripemd160_starts( &ctx ); + ripemd160_update( &ctx, input, ilen ); + ripemd160_finish( &ctx, output ); + ripemd160_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = RIPEMD-160( file contents ) + */ +int ripemd160_file( const char *path, unsigned char output[20] ) +{ + FILE *f; + size_t n; + ripemd160_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_RIPEMD160_FILE_IO_ERROR ); + + ripemd160_init( &ctx ); + ripemd160_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + ripemd160_update( &ctx, buf, n ); + + ripemd160_finish( &ctx, output ); + ripemd160_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_RIPEMD160_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * RIPEMD-160 HMAC context setup + */ +void ripemd160_hmac_starts( ripemd160_context *ctx, + const unsigned char *key, size_t keylen ) +{ + size_t i; + unsigned char sum[20]; + + if( keylen > 64 ) + { + ripemd160( key, keylen, sum ); + keylen = 20; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + ripemd160_starts( ctx ); + ripemd160_update( ctx, ctx->ipad, 64 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * RIPEMD-160 HMAC process buffer + */ +void ripemd160_hmac_update( ripemd160_context *ctx, + const unsigned char *input, size_t ilen ) +{ + ripemd160_update( ctx, input, ilen ); +} + +/* + * RIPEMD-160 HMAC final digest + */ +void ripemd160_hmac_finish( ripemd160_context *ctx, unsigned char output[20] ) +{ + unsigned char tmpbuf[20]; + + ripemd160_finish( ctx, tmpbuf ); + ripemd160_starts( ctx ); + ripemd160_update( ctx, ctx->opad, 64 ); + ripemd160_update( ctx, tmpbuf, 20 ); + ripemd160_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * RIPEMD-160 HMAC context reset + */ +void ripemd160_hmac_reset( ripemd160_context *ctx ) +{ + ripemd160_starts( ctx ); + ripemd160_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-RIPEMD-160( hmac key, input buffer ) + */ +void ripemd160_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20] ) +{ + ripemd160_context ctx; + + ripemd160_init( &ctx ); + ripemd160_hmac_starts( &ctx, key, keylen ); + ripemd160_hmac_update( &ctx, input, ilen ); + ripemd160_hmac_finish( &ctx, output ); + ripemd160_free( &ctx ); +} + + +#if defined(POLARSSL_SELF_TEST) +/* + * Test vectors from the RIPEMD-160 paper and + * http://homes.esat.kuleuven.be/~bosselae/ripemd160.html#HMAC + */ +#define TESTS 8 +#define KEYS 2 +static const char *ripemd160_test_input[TESTS] = +{ + "", + "a", + "abc", + "message digest", + "abcdefghijklmnopqrstuvwxyz", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", +}; + +static const unsigned char ripemd160_test_md[TESTS][20] = +{ + { 0x9c, 0x11, 0x85, 0xa5, 0xc5, 0xe9, 0xfc, 0x54, 0x61, 0x28, + 0x08, 0x97, 0x7e, 0xe8, 0xf5, 0x48, 0xb2, 0x25, 0x8d, 0x31 }, + { 0x0b, 0xdc, 0x9d, 0x2d, 0x25, 0x6b, 0x3e, 0xe9, 0xda, 0xae, + 0x34, 0x7b, 0xe6, 0xf4, 0xdc, 0x83, 0x5a, 0x46, 0x7f, 0xfe }, + { 0x8e, 0xb2, 0x08, 0xf7, 0xe0, 0x5d, 0x98, 0x7a, 0x9b, 0x04, + 0x4a, 0x8e, 0x98, 0xc6, 0xb0, 0x87, 0xf1, 0x5a, 0x0b, 0xfc }, + { 0x5d, 0x06, 0x89, 0xef, 0x49, 0xd2, 0xfa, 0xe5, 0x72, 0xb8, + 0x81, 0xb1, 0x23, 0xa8, 0x5f, 0xfa, 0x21, 0x59, 0x5f, 0x36 }, + { 0xf7, 0x1c, 0x27, 0x10, 0x9c, 0x69, 0x2c, 0x1b, 0x56, 0xbb, + 0xdc, 0xeb, 0x5b, 0x9d, 0x28, 0x65, 0xb3, 0x70, 0x8d, 0xbc }, + { 0x12, 0xa0, 0x53, 0x38, 0x4a, 0x9c, 0x0c, 0x88, 0xe4, 0x05, + 0xa0, 0x6c, 0x27, 0xdc, 0xf4, 0x9a, 0xda, 0x62, 0xeb, 0x2b }, + { 0xb0, 0xe2, 0x0b, 0x6e, 0x31, 0x16, 0x64, 0x02, 0x86, 0xed, + 0x3a, 0x87, 0xa5, 0x71, 0x30, 0x79, 0xb2, 0x1f, 0x51, 0x89 }, + { 0x9b, 0x75, 0x2e, 0x45, 0x57, 0x3d, 0x4b, 0x39, 0xf4, 0xdb, + 0xd3, 0x32, 0x3c, 0xab, 0x82, 0xbf, 0x63, 0x32, 0x6b, 0xfb }, +}; + +static const unsigned char ripemd160_test_hmac[KEYS][TESTS][20] = +{ + { + { 0xcf, 0x38, 0x76, 0x77, 0xbf, 0xda, 0x84, 0x83, 0xe6, 0x3b, + 0x57, 0xe0, 0x6c, 0x3b, 0x5e, 0xcd, 0x8b, 0x7f, 0xc0, 0x55 }, + { 0x0d, 0x35, 0x1d, 0x71, 0xb7, 0x8e, 0x36, 0xdb, 0xb7, 0x39, + 0x1c, 0x81, 0x0a, 0x0d, 0x2b, 0x62, 0x40, 0xdd, 0xba, 0xfc }, + { 0xf7, 0xef, 0x28, 0x8c, 0xb1, 0xbb, 0xcc, 0x61, 0x60, 0xd7, + 0x65, 0x07, 0xe0, 0xa3, 0xbb, 0xf7, 0x12, 0xfb, 0x67, 0xd6 }, + { 0xf8, 0x36, 0x62, 0xcc, 0x8d, 0x33, 0x9c, 0x22, 0x7e, 0x60, + 0x0f, 0xcd, 0x63, 0x6c, 0x57, 0xd2, 0x57, 0x1b, 0x1c, 0x34 }, + { 0x84, 0x3d, 0x1c, 0x4e, 0xb8, 0x80, 0xac, 0x8a, 0xc0, 0xc9, + 0xc9, 0x56, 0x96, 0x50, 0x79, 0x57, 0xd0, 0x15, 0x5d, 0xdb }, + { 0x60, 0xf5, 0xef, 0x19, 0x8a, 0x2d, 0xd5, 0x74, 0x55, 0x45, + 0xc1, 0xf0, 0xc4, 0x7a, 0xa3, 0xfb, 0x57, 0x76, 0xf8, 0x81 }, + { 0xe4, 0x9c, 0x13, 0x6a, 0x9e, 0x56, 0x27, 0xe0, 0x68, 0x1b, + 0x80, 0x8a, 0x3b, 0x97, 0xe6, 0xa6, 0xe6, 0x61, 0xae, 0x79 }, + { 0x31, 0xbe, 0x3c, 0xc9, 0x8c, 0xee, 0x37, 0xb7, 0x9b, 0x06, + 0x19, 0xe3, 0xe1, 0xc2, 0xbe, 0x4f, 0x1a, 0xa5, 0x6e, 0x6c }, + }, + { + { 0xfe, 0x69, 0xa6, 0x6c, 0x74, 0x23, 0xee, 0xa9, 0xc8, 0xfa, + 0x2e, 0xff, 0x8d, 0x9d, 0xaf, 0xb4, 0xf1, 0x7a, 0x62, 0xf5 }, + { 0x85, 0x74, 0x3e, 0x89, 0x9b, 0xc8, 0x2d, 0xbf, 0xa3, 0x6f, + 0xaa, 0xa7, 0xa2, 0x5b, 0x7c, 0xfd, 0x37, 0x24, 0x32, 0xcd }, + { 0x6e, 0x4a, 0xfd, 0x50, 0x1f, 0xa6, 0xb4, 0xa1, 0x82, 0x3c, + 0xa3, 0xb1, 0x0b, 0xd9, 0xaa, 0x0b, 0xa9, 0x7b, 0xa1, 0x82 }, + { 0x2e, 0x06, 0x6e, 0x62, 0x4b, 0xad, 0xb7, 0x6a, 0x18, 0x4c, + 0x8f, 0x90, 0xfb, 0xa0, 0x53, 0x33, 0x0e, 0x65, 0x0e, 0x92 }, + { 0x07, 0xe9, 0x42, 0xaa, 0x4e, 0x3c, 0xd7, 0xc0, 0x4d, 0xed, + 0xc1, 0xd4, 0x6e, 0x2e, 0x8c, 0xc4, 0xc7, 0x41, 0xb3, 0xd9 }, + { 0xb6, 0x58, 0x23, 0x18, 0xdd, 0xcf, 0xb6, 0x7a, 0x53, 0xa6, + 0x7d, 0x67, 0x6b, 0x8a, 0xd8, 0x69, 0xad, 0xed, 0x62, 0x9a }, + { 0xf1, 0xbe, 0x3e, 0xe8, 0x77, 0x70, 0x31, 0x40, 0xd3, 0x4f, + 0x97, 0xea, 0x1a, 0xb3, 0xa0, 0x7c, 0x14, 0x13, 0x33, 0xe2 }, + { 0x85, 0xf1, 0x64, 0x70, 0x3e, 0x61, 0xa6, 0x31, 0x31, 0xbe, + 0x7e, 0x45, 0x95, 0x8e, 0x07, 0x94, 0x12, 0x39, 0x04, 0xf9 }, + }, +}; + +static const unsigned char ripemd160_test_key[KEYS][20] = +{ + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x23, 0x45, 0x67 }, + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, + 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x11, 0x22, 0x33 }, +}; + +/* + * Checkup routine + */ +int ripemd160_self_test( int verbose ) +{ + int i, j; + unsigned char output[20]; + + memset( output, 0, sizeof output ); + + for( i = 0; i < TESTS; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " RIPEMD-160 test #%d: ", i + 1 ); + + ripemd160( (const unsigned char *) ripemd160_test_input[i], + strlen( ripemd160_test_input[i] ), + output ); + + if( memcmp( output, ripemd160_test_md[i], 20 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + for( j = 0; j < KEYS; j++ ) + { + if( verbose != 0 ) + polarssl_printf( " HMAC-RIPEMD-160 test #%d, key #%d: ", + i + 1, j + 1 ); + + ripemd160_hmac( ripemd160_test_key[j], 20, + (const unsigned char *) ripemd160_test_input[i], + strlen( ripemd160_test_input[i] ), + output ); + + if( memcmp( output, ripemd160_test_hmac[j][i], 20 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + } + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_RIPEMD160_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/rsa.c b/component/common/network/ssl/polarssl-1.3.8/library/rsa.c new file mode 100644 index 0000000..0fd5199 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/rsa.c @@ -0,0 +1,1657 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * RSA was designed by Ron Rivest, Adi Shamir and Len Adleman. + * + * http://theory.lcs.mit.edu/~rivest/rsapaper.pdf + * http://www.cacr.math.uwaterloo.ca/hac/about/chap8.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_RSA_C) + +#include "polarssl/rsa.h" +#include "polarssl/oid.h" + +#if defined(POLARSSL_PKCS1_V21) +#include "polarssl/md.h" +#endif + +#include +#include + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* + * Initialize an RSA context + */ +void rsa_init( rsa_context *ctx, + int padding, + int hash_id ) +{ + memset( ctx, 0, sizeof( rsa_context ) ); + + rsa_set_padding( ctx, padding, hash_id ); + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_init( &ctx->mutex ); +#endif +} + +/* + * Set padding for an existing RSA context + */ +void rsa_set_padding( rsa_context *ctx, int padding, int hash_id ) +{ + ctx->padding = padding; + ctx->hash_id = hash_id; +} + +#if defined(POLARSSL_GENPRIME) + +/* + * Generate an RSA keypair + */ +int rsa_gen_key( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent ) +{ + int ret; + mpi P1, Q1, H, G; + + if( f_rng == NULL || nbits < 128 || exponent < 3 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + mpi_init( &P1 ); mpi_init( &Q1 ); mpi_init( &H ); mpi_init( &G ); + + /* + * find primes P and Q with Q < P so that: + * GCD( E, (P-1)*(Q-1) ) == 1 + */ + MPI_CHK( mpi_lset( &ctx->E, exponent ) ); + + do + { + MPI_CHK( mpi_gen_prime( &ctx->P, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + MPI_CHK( mpi_gen_prime( &ctx->Q, ( nbits + 1 ) >> 1, 0, + f_rng, p_rng ) ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) < 0 ) + mpi_swap( &ctx->P, &ctx->Q ); + + if( mpi_cmp_mpi( &ctx->P, &ctx->Q ) == 0 ) + continue; + + MPI_CHK( mpi_mul_mpi( &ctx->N, &ctx->P, &ctx->Q ) ); + if( mpi_msb( &ctx->N ) != nbits ) + continue; + + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + } + while( mpi_cmp_int( &G, 1 ) != 0 ); + + /* + * D = E^-1 mod ((P-1)*(Q-1)) + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MPI_CHK( mpi_inv_mod( &ctx->D , &ctx->E, &H ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &ctx->DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &ctx->QP, &ctx->Q, &ctx->P ) ); + + ctx->len = ( mpi_msb( &ctx->N ) + 7 ) >> 3; + +cleanup: + + mpi_free( &P1 ); mpi_free( &Q1 ); mpi_free( &H ); mpi_free( &G ); + + if( ret != 0 ) + { + rsa_free( ctx ); + return( POLARSSL_ERR_RSA_KEY_GEN_FAILED + ret ); + } + + return( 0 ); +} + +#endif /* POLARSSL_GENPRIME */ + +/* + * Check a public RSA key + */ +int rsa_check_pubkey( const rsa_context *ctx ) +{ + if( !ctx->N.p || !ctx->E.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( ( ctx->N.p[0] & 1 ) == 0 || + ( ctx->E.p[0] & 1 ) == 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->N ) < 128 || + mpi_msb( &ctx->N ) > POLARSSL_MPI_MAX_BITS ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + if( mpi_msb( &ctx->E ) < 2 || + mpi_cmp_mpi( &ctx->E, &ctx->N ) >= 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + return( 0 ); +} + +/* + * Check a private RSA key + */ +int rsa_check_privkey( const rsa_context *ctx ) +{ + int ret; + mpi PQ, DE, P1, Q1, H, I, G, G2, L1, L2, DP, DQ, QP; + + if( ( ret = rsa_check_pubkey( ctx ) ) != 0 ) + return( ret ); + + if( !ctx->P.p || !ctx->Q.p || !ctx->D.p ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED ); + + mpi_init( &PQ ); mpi_init( &DE ); mpi_init( &P1 ); mpi_init( &Q1 ); + mpi_init( &H ); mpi_init( &I ); mpi_init( &G ); mpi_init( &G2 ); + mpi_init( &L1 ); mpi_init( &L2 ); mpi_init( &DP ); mpi_init( &DQ ); + mpi_init( &QP ); + + MPI_CHK( mpi_mul_mpi( &PQ, &ctx->P, &ctx->Q ) ); + MPI_CHK( mpi_mul_mpi( &DE, &ctx->D, &ctx->E ) ); + MPI_CHK( mpi_sub_int( &P1, &ctx->P, 1 ) ); + MPI_CHK( mpi_sub_int( &Q1, &ctx->Q, 1 ) ); + MPI_CHK( mpi_mul_mpi( &H, &P1, &Q1 ) ); + MPI_CHK( mpi_gcd( &G, &ctx->E, &H ) ); + + MPI_CHK( mpi_gcd( &G2, &P1, &Q1 ) ); + MPI_CHK( mpi_div_mpi( &L1, &L2, &H, &G2 ) ); + MPI_CHK( mpi_mod_mpi( &I, &DE, &L1 ) ); + + MPI_CHK( mpi_mod_mpi( &DP, &ctx->D, &P1 ) ); + MPI_CHK( mpi_mod_mpi( &DQ, &ctx->D, &Q1 ) ); + MPI_CHK( mpi_inv_mod( &QP, &ctx->Q, &ctx->P ) ); + /* + * Check for a valid PKCS1v2 private key + */ + if( mpi_cmp_mpi( &PQ, &ctx->N ) != 0 || + mpi_cmp_mpi( &DP, &ctx->DP ) != 0 || + mpi_cmp_mpi( &DQ, &ctx->DQ ) != 0 || + mpi_cmp_mpi( &QP, &ctx->QP ) != 0 || + mpi_cmp_int( &L2, 0 ) != 0 || + mpi_cmp_int( &I, 1 ) != 0 || + mpi_cmp_int( &G, 1 ) != 0 ) + { + ret = POLARSSL_ERR_RSA_KEY_CHECK_FAILED; + } + +cleanup: + mpi_free( &PQ ); mpi_free( &DE ); mpi_free( &P1 ); mpi_free( &Q1 ); + mpi_free( &H ); mpi_free( &I ); mpi_free( &G ); mpi_free( &G2 ); + mpi_free( &L1 ); mpi_free( &L2 ); mpi_free( &DP ); mpi_free( &DQ ); + mpi_free( &QP ); + + if( ret == POLARSSL_ERR_RSA_KEY_CHECK_FAILED ) + return( ret ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_KEY_CHECK_FAILED + ret ); + + return( 0 ); +} + +/* + * Do an RSA public key operation + */ +int rsa_public( rsa_context *ctx, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T; + + mpi_init( &T ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + + olen = ctx->len; + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->E, &ctx->N, &ctx->RN ) ); + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + + mpi_free( &T ); + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PUBLIC_FAILED + ret ); + + return( 0 ); +} + +#if !defined(POLARSSL_RSA_NO_CRT) +/* + * Generate or update blinding values, see section 10 of: + * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, + * DSS, and other systems. In : Advances in Cryptology—CRYPTO’96. Springer + * Berlin Heidelberg, 1996. p. 104-113. + */ +static int rsa_prepare_blinding( rsa_context *ctx, mpi *Vi, mpi *Vf, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng ) +{ + int ret, count = 0; + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_lock( &ctx->mutex ); +#endif + + if( ctx->Vf.p != NULL ) + { + /* We already have blinding values, just update them by squaring */ + MPI_CHK( mpi_mul_mpi( &ctx->Vi, &ctx->Vi, &ctx->Vi ) ); + MPI_CHK( mpi_mod_mpi( &ctx->Vi, &ctx->Vi, &ctx->N ) ); + MPI_CHK( mpi_mul_mpi( &ctx->Vf, &ctx->Vf, &ctx->Vf ) ); + MPI_CHK( mpi_mod_mpi( &ctx->Vf, &ctx->Vf, &ctx->N ) ); + + goto done; + } + + /* Unblinding value: Vf = random number, invertible mod N */ + do { + if( count++ > 10 ) + return( POLARSSL_ERR_RSA_RNG_FAILED ); + + MPI_CHK( mpi_fill_random( &ctx->Vf, ctx->len - 1, f_rng, p_rng ) ); + MPI_CHK( mpi_gcd( &ctx->Vi, &ctx->Vf, &ctx->N ) ); + } while( mpi_cmp_int( &ctx->Vi, 1 ) != 0 ); + + /* Blinding value: Vi = Vf^(-e) mod N */ + MPI_CHK( mpi_inv_mod( &ctx->Vi, &ctx->Vf, &ctx->N ) ); + MPI_CHK( mpi_exp_mod( &ctx->Vi, &ctx->Vi, &ctx->E, &ctx->N, &ctx->RN ) ); + +done: + if( Vi != &ctx->Vi ) + { + MPI_CHK( mpi_copy( Vi, &ctx->Vi ) ); + MPI_CHK( mpi_copy( Vf, &ctx->Vf ) ); + } + +cleanup: +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_unlock( &ctx->mutex ); +#endif + + return( ret ); +} +#endif /* !POLARSSL_RSA_NO_CRT */ + +/* + * Do an RSA private key operation + */ +int rsa_private( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output ) +{ + int ret; + size_t olen; + mpi T, T1, T2; +#if !defined(POLARSSL_RSA_NO_CRT) + mpi *Vi, *Vf; + + /* + * When using the Chinese Remainder Theorem, we use blinding values. + * Without threading, we just read them directly from the context, + * otherwise we make a local copy in order to reduce locking contention. + */ +#if defined(POLARSSL_THREADING_C) + mpi Vi_copy, Vf_copy; + + mpi_init( &Vi_copy ); mpi_init( &Vf_copy ); + Vi = &Vi_copy; + Vf = &Vf_copy; +#else + Vi = &ctx->Vi; + Vf = &ctx->Vf; +#endif +#endif /* !POLARSSL_RSA_NO_CRT */ + + mpi_init( &T ); mpi_init( &T1 ); mpi_init( &T2 ); + + MPI_CHK( mpi_read_binary( &T, input, ctx->len ) ); + if( mpi_cmp_mpi( &T, &ctx->N ) >= 0 ) + { + mpi_free( &T ); + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + } + +#if defined(POLARSSL_RSA_NO_CRT) + ((void) f_rng); + ((void) p_rng); + MPI_CHK( mpi_exp_mod( &T, &T, &ctx->D, &ctx->N, &ctx->RN ) ); +#else + if( f_rng != NULL ) + { + /* + * Blinding + * T = T * Vi mod N + */ + MPI_CHK( rsa_prepare_blinding( ctx, Vi, Vf, f_rng, p_rng ) ); + MPI_CHK( mpi_mul_mpi( &T, &T, Vi ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } + + /* + * faster decryption using the CRT + * + * T1 = input ^ dP mod P + * T2 = input ^ dQ mod Q + */ + MPI_CHK( mpi_exp_mod( &T1, &T, &ctx->DP, &ctx->P, &ctx->RP ) ); + MPI_CHK( mpi_exp_mod( &T2, &T, &ctx->DQ, &ctx->Q, &ctx->RQ ) ); + + /* + * T = (T1 - T2) * (Q^-1 mod P) mod P + */ + MPI_CHK( mpi_sub_mpi( &T, &T1, &T2 ) ); + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->QP ) ); + MPI_CHK( mpi_mod_mpi( &T, &T1, &ctx->P ) ); + + /* + * T = T2 + T * Q + */ + MPI_CHK( mpi_mul_mpi( &T1, &T, &ctx->Q ) ); + MPI_CHK( mpi_add_mpi( &T, &T2, &T1 ) ); + + if( f_rng != NULL ) + { + /* + * Unblind + * T = T * Vf mod N + */ + MPI_CHK( mpi_mul_mpi( &T, &T, Vf ) ); + MPI_CHK( mpi_mod_mpi( &T, &T, &ctx->N ) ); + } +#endif /* POLARSSL_RSA_NO_CRT */ + + olen = ctx->len; + MPI_CHK( mpi_write_binary( &T, output, olen ) ); + +cleanup: + mpi_free( &T ); mpi_free( &T1 ); mpi_free( &T2 ); +#if !defined(POLARSSL_RSA_NO_CRT) && defined(POLARSSL_THREADING_C) + mpi_free( &Vi_copy ); mpi_free( &Vf_copy ); +#endif + + if( ret != 0 ) + return( POLARSSL_ERR_RSA_PRIVATE_FAILED + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_ctx message digest context to use + */ +static void mgf_mask( unsigned char *dst, size_t dlen, unsigned char *src, + size_t slen, md_context_t *md_ctx ) +{ + unsigned char mask[POLARSSL_MD_MAX_SIZE]; + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + + memset( mask, 0, POLARSSL_MD_MAX_SIZE ); + memset( counter, 0, 4 ); + + hlen = md_ctx->md_info->size; + + // Generate and apply dbMask + // + p = dst; + + while( dlen > 0 ) + { + use_len = hlen; + if( dlen < hlen ) + use_len = dlen; + + md_starts( md_ctx ); + md_update( md_ctx, src, slen ); + md_update( md_ctx, counter, 4 ); + md_finish( md_ctx, mask ); + + for( i = 0; i < use_len; ++i ) + *p++ ^= mask[i]; + + counter[3]++; + + dlen -= use_len; + } +} +#endif /* POLARSSL_PKCS1_V21 */ + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int rsa_rsaes_oaep_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t olen; + int ret; + unsigned char *p = output; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + hlen = md_get_size( md_info ); + + if( olen < ilen + 2 * hlen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( output, 0, olen ); + + *p++ = 0; + + // Generate a random octet string seed + // + if( ( ret = f_rng( p_rng, p, hlen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p += hlen; + + // Construct DB + // + md( md_info, label, label_len, p ); + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + memcpy( p, input, ilen ); + + md_init( &md_ctx ); + md_init_ctx( &md_ctx, md_info ); + + // maskedDB: Apply dbMask to DB + // + mgf_mask( output + hlen + 1, olen - hlen - 1, output + 1, hlen, + &md_ctx ); + + // maskedSeed: Apply seedMask to seed + // + mgf_mask( output + 1, hlen, output + hlen + 1, olen - hlen - 1, + &md_ctx ); + + md_free( &md_ctx ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +#if defined(POLARSSL_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int rsa_rsaes_pkcs1_v15_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + size_t nb_pad, olen; + int ret; + unsigned char *p = output; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( olen < ilen + 11 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + if( mode == RSA_PUBLIC ) + { + *p++ = RSA_CRYPT; + + while( nb_pad-- > 0 ) + { + int rng_dl = 100; + + do { + ret = f_rng( p_rng, p, 1 ); + } while( *p == 0 && --rng_dl && ret == 0 ); + + // Check if RNG failed to generate data + // + if( rng_dl == 0 || ret != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + p++; + } + } + else + { + *p++ = RSA_SIGN; + + while( nb_pad-- > 0 ) + *p++ = 0xFF; + } + + *p++ = 0; + memcpy( p, input, ilen ); + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, output, output ) + : rsa_private( ctx, f_rng, p_rng, output, output ) ); +} +#endif /* POLARSSL_PKCS1_V15 */ + +/* + * Add the message padding, then do an RSA operation + */ +int rsa_pkcs1_encrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t ilen, + const unsigned char *input, + unsigned char *output ) +{ + switch( ctx->padding ) + { +#if defined(POLARSSL_PKCS1_V15) + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_encrypt( ctx, f_rng, p_rng, mode, ilen, + input, output ); +#endif + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_encrypt( ctx, f_rng, p_rng, mode, NULL, 0, + ilen, input, output ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int rsa_rsaes_oaep_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len ) +{ + int ret; + size_t ilen, i, pad_len; + unsigned char *p, bad, pad_done; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char lhash[POLARSSL_MD_MAX_SIZE]; + unsigned int hlen; + const md_info_t *md_info; + md_context_t md_ctx; + + /* + * Parameters sanity checks + */ + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + /* + * RSA operation + */ + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + /* + * Unmask data and generate lHash + */ + hlen = md_get_size( md_info ); + + md_init( &md_ctx ); + md_init_ctx( &md_ctx, md_info ); + + /* Generate lHash */ + md( md_info, label, label_len, lhash ); + + /* seed: Apply seedMask to maskedSeed */ + mgf_mask( buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + &md_ctx ); + + /* DB: Apply dbMask to maskedDB */ + mgf_mask( buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + &md_ctx ); + + md_free( &md_ctx ); + + /* + * Check contents, in "constant-time" + */ + p = buf; + bad = 0; + + bad |= *p++; /* First byte must be 0 */ + + p += hlen; /* Skip seed */ + + /* Check lHash */ + for( i = 0; i < hlen; i++ ) + bad |= lhash[i] ^ *p++; + + /* Get zero-padding len, but always read till end of buffer + * (minus one, for the 01 byte) */ + pad_len = 0; + pad_done = 0; + for( i = 0; i < ilen - 2 * hlen - 2; i++ ) + { + pad_done |= p[i]; + pad_len += ( pad_done == 0 ); + } + + p += pad_len; + bad |= *p++ ^ 0x01; + + /* + * The only information "leaked" is whether the padding was correct or not + * (eg, no data is copied if it was not correct). This meets the + * recommendations in PKCS#1 v2.2: an opponent cannot distinguish between + * the different error conditions. + */ + if( bad != 0 ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( ilen - ( p - buf ) > output_max_len ) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +#if defined(POLARSSL_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int rsa_rsaes_pkcs1_v15_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret; + size_t ilen, pad_count = 0, i; + unsigned char *p, bad, pad_done = 0; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ilen = ctx->len; + + if( ilen < 16 || ilen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, input, buf ) + : rsa_private( ctx, f_rng, p_rng, input, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + bad = 0; + + /* + * Check and get padding len in "constant-time" + */ + bad |= *p++; /* First byte must be 0 */ + + /* This test does not depend on secret data */ + if( mode == RSA_PRIVATE ) + { + bad |= *p++ ^ RSA_CRYPT; + + /* Get padding len, but always read till end of buffer + * (minus one, for the 00 byte) */ + for( i = 0; i < ilen - 3; i++ ) + { + pad_done |= ( p[i] == 0 ); + pad_count += ( pad_done == 0 ); + } + + p += pad_count; + bad |= *p++; /* Must be zero */ + } + else + { + bad |= *p++ ^ RSA_SIGN; + + /* Get padding len, but always read till end of buffer + * (minus one, for the 00 byte) */ + for( i = 0; i < ilen - 3; i++ ) + { + pad_done |= ( p[i] != 0xFF ); + pad_count += ( pad_done == 0 ); + } + + p += pad_count; + bad |= *p++; /* Must be zero */ + } + + if( bad ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( ilen - ( p - buf ) > output_max_len ) + return( POLARSSL_ERR_RSA_OUTPUT_TOO_LARGE ); + + *olen = ilen - (p - buf); + memcpy( output, p, *olen ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V15 */ + +/* + * Do an RSA operation, then remove the message padding + */ +int rsa_pkcs1_decrypt( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch( ctx->padding ) + { +#if defined(POLARSSL_PKCS1_V15) + case RSA_PKCS_V15: + return rsa_rsaes_pkcs1_v15_decrypt( ctx, f_rng, p_rng, mode, olen, + input, output, output_max_len ); +#endif + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsaes_oaep_decrypt( ctx, f_rng, p_rng, mode, NULL, 0, + olen, input, output, + output_max_len ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int rsa_rsassa_pss_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t olen; + unsigned char *p = sig; + unsigned char salt[POLARSSL_MD_MAX_SIZE]; + unsigned int slen, hlen, offset = 0; + int ret; + size_t msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( f_rng == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + + if( md_alg != POLARSSL_MD_NONE ) + { + // Gather length of hash to sign + // + md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hashlen = md_get_size( md_info ); + } + + md_info = md_info_from_type( ctx->hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = hlen; + + if( olen < hlen + slen + 2 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + memset( sig, 0, olen ); + + // Generate salt of length slen + // + if( ( ret = f_rng( p_rng, salt, slen ) ) != 0 ) + return( POLARSSL_ERR_RSA_RNG_FAILED + ret ); + + // Note: EMSA-PSS encoding is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + p += olen - hlen * 2 - 2; + *p++ = 0x01; + memcpy( p, salt, slen ); + p += slen; + + md_init( &md_ctx ); + md_init_ctx( &md_ctx, md_info ); + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, p, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, salt, slen ); + md_finish( &md_ctx, p ); + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + offset = 1; + + // maskedDB: Apply dbMask to DB + // + mgf_mask( sig + offset, olen - hlen - 1 - offset, p, hlen, &md_ctx ); + + md_free( &md_ctx ); + + msb = mpi_msb( &ctx->N ) - 1; + sig[0] &= 0xFF >> ( olen * 8 - msb ); + + p += hlen; + *p++ = 0xBC; + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V21 */ + +#if defined(POLARSSL_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ +/* + * Do an RSA operation to sign the message digest + */ +int rsa_rsassa_pkcs1_v15_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + size_t nb_pad, olen, oid_size = 0; + unsigned char *p = sig; + const char *oid; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + olen = ctx->len; + nb_pad = olen - 3; + + if( md_alg != POLARSSL_MD_NONE ) + { + const md_info_t *md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + if( oid_get_oid_by_md( md_alg, &oid, &oid_size ) != 0 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + nb_pad -= 10 + oid_size; + + hashlen = md_get_size( md_info ); + } + + nb_pad -= hashlen; + + if( ( nb_pad < 8 ) || ( nb_pad > olen ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + *p++ = 0; + *p++ = RSA_SIGN; + memset( p, 0xFF, nb_pad ); + p += nb_pad; + *p++ = 0; + + if( md_alg == POLARSSL_MD_NONE ) + { + memcpy( p, hash, hashlen ); + } + else + { + /* + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + */ + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x08 + oid_size + hashlen ); + *p++ = ASN1_SEQUENCE | ASN1_CONSTRUCTED; + *p++ = (unsigned char) ( 0x04 + oid_size ); + *p++ = ASN1_OID; + *p++ = oid_size & 0xFF; + memcpy( p, oid, oid_size ); + p += oid_size; + *p++ = ASN1_NULL; + *p++ = 0x00; + *p++ = ASN1_OCTET_STRING; + *p++ = hashlen; + memcpy( p, hash, hashlen ); + } + + return( ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, sig ) + : rsa_private( ctx, f_rng, p_rng, sig, sig ) ); +} +#endif /* POLARSSL_PKCS1_V15 */ + +/* + * Do an RSA operation to sign the message digest + */ +int rsa_pkcs1_sign( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig ) +{ + switch( ctx->padding ) + { +#if defined(POLARSSL_PKCS1_V15) + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_sign( ctx, f_rng, p_rng, mode, md_alg, + hashlen, hash, sig ); +#endif + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_sign( ctx, f_rng, p_rng, mode, md_alg, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +#if defined(POLARSSL_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify_ext( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + md_type_t mgf1_hash_id, + int expected_salt_len, + const unsigned char *sig ) +{ + int ret; + size_t siglen; + unsigned char *p; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + unsigned char result[POLARSSL_MD_MAX_SIZE]; + unsigned char zeros[8]; + unsigned int hlen; + size_t slen, msb; + const md_info_t *md_info; + md_context_t md_ctx; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V21 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( buf[siglen - 1] != 0xBC ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + if( md_alg != POLARSSL_MD_NONE ) + { + // Gather length of hash to sign + // + md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hashlen = md_get_size( md_info ); + } + + md_info = md_info_from_type( mgf1_hash_id ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + hlen = md_get_size( md_info ); + slen = siglen - hlen - 1; /* Currently length of salt + padding */ + + memset( zeros, 0, 8 ); + + // Note: EMSA-PSS verification is over the length of N - 1 bits + // + msb = mpi_msb( &ctx->N ) - 1; + + // Compensate for boundary condition when applying mask + // + if( msb % 8 == 0 ) + { + p++; + siglen -= 1; + } + if( buf[0] >> ( 8 - siglen * 8 + msb ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + md_init( &md_ctx ); + md_init_ctx( &md_ctx, md_info ); + + mgf_mask( p, siglen - hlen - 1, p + siglen - hlen - 1, hlen, &md_ctx ); + + buf[0] &= 0xFF >> ( siglen * 8 - msb ); + + while( p < buf + siglen && *p == 0 ) + p++; + + if( p == buf + siglen || + *p++ != 0x01 ) + { + md_free( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + /* Actual salt len */ + slen -= p - buf; + + if( expected_salt_len != RSA_SALT_LEN_ANY && + slen != (size_t) expected_salt_len ) + { + md_free( &md_ctx ); + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } + + // Generate H = Hash( M' ) + // + md_starts( &md_ctx ); + md_update( &md_ctx, zeros, 8 ); + md_update( &md_ctx, hash, hashlen ); + md_update( &md_ctx, p, slen ); + md_finish( &md_ctx, result ); + + md_free( &md_ctx ); + + if( memcmp( p + slen, result, hlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); +} + +/* + * Simplified PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int rsa_rsassa_pss_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + md_type_t mgf1_hash_id = ( ctx->hash_id != POLARSSL_MD_NONE ) + ? (md_type_t) ctx->hash_id + : md_alg; + + return( rsa_rsassa_pss_verify_ext( ctx, f_rng, p_rng, mode, + md_alg, hashlen, hash, + mgf1_hash_id, RSA_SALT_LEN_ANY, + sig ) ); + +} +#endif /* POLARSSL_PKCS1_V21 */ + +#if defined(POLARSSL_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int rsa_rsassa_pkcs1_v15_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + int ret; + size_t len, siglen, asn1_len; + unsigned char *p, *end; + unsigned char buf[POLARSSL_MPI_MAX_SIZE]; + md_type_t msg_md_alg; + const md_info_t *md_info; + asn1_buf oid; + + if( mode == RSA_PRIVATE && ctx->padding != RSA_PKCS_V15 ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + siglen = ctx->len; + + if( siglen < 16 || siglen > sizeof( buf ) ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + + ret = ( mode == RSA_PUBLIC ) + ? rsa_public( ctx, sig, buf ) + : rsa_private( ctx, f_rng, p_rng, sig, buf ); + + if( ret != 0 ) + return( ret ); + + p = buf; + + if( *p++ != 0 || *p++ != RSA_SIGN ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + + while( *p != 0 ) + { + if( p >= buf + siglen - 1 || *p != 0xFF ) + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + p++; + } + p++; + + len = siglen - ( p - buf ); + + if( len == hashlen && md_alg == POLARSSL_MD_NONE ) + { + if( memcmp( p, hash, hashlen ) == 0 ) + return( 0 ); + else + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + } + + md_info = md_info_from_type( md_alg ); + if( md_info == NULL ) + return( POLARSSL_ERR_RSA_BAD_INPUT_DATA ); + hashlen = md_get_size( md_info ); + + end = p + len; + + // Parse the ASN.1 structure inside the PKCS#1 v1.5 structure + // + if( ( ret = asn1_get_tag( &p, end, &asn1_len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( asn1_len + 2 != len ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( ret = asn1_get_tag( &p, end, &asn1_len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( asn1_len + 6 + hashlen != len ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( ret = asn1_get_tag( &p, end, &oid.len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + oid.p = p; + p += oid.len; + + if( oid_get_md_alg( &oid, &msg_md_alg ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( md_alg != msg_md_alg ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + /* + * assume the algorithm parameters must be NULL + */ + if( ( ret = asn1_get_tag( &p, end, &asn1_len, ASN1_NULL ) ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( ( ret = asn1_get_tag( &p, end, &asn1_len, ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( asn1_len != hashlen ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + if( memcmp( p, hash, hashlen ) != 0 ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + p += hashlen; + + if( p != end ) + return( POLARSSL_ERR_RSA_VERIFY_FAILED ); + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V15 */ + +/* + * Do an RSA operation and check the message digest + */ +int rsa_pkcs1_verify( rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int mode, + md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig ) +{ + switch( ctx->padding ) + { +#if defined(POLARSSL_PKCS1_V15) + case RSA_PKCS_V15: + return rsa_rsassa_pkcs1_v15_verify( ctx, f_rng, p_rng, mode, md_alg, + hashlen, hash, sig ); +#endif + +#if defined(POLARSSL_PKCS1_V21) + case RSA_PKCS_V21: + return rsa_rsassa_pss_verify( ctx, f_rng, p_rng, mode, md_alg, + hashlen, hash, sig ); +#endif + + default: + return( POLARSSL_ERR_RSA_INVALID_PADDING ); + } +} + +/* + * Copy the components of an RSA key + */ +int rsa_copy( rsa_context *dst, const rsa_context *src ) +{ + int ret; + + dst->ver = src->ver; + dst->len = src->len; + + MPI_CHK( mpi_copy( &dst->N, &src->N ) ); + MPI_CHK( mpi_copy( &dst->E, &src->E ) ); + + MPI_CHK( mpi_copy( &dst->D, &src->D ) ); + MPI_CHK( mpi_copy( &dst->P, &src->P ) ); + MPI_CHK( mpi_copy( &dst->Q, &src->Q ) ); + MPI_CHK( mpi_copy( &dst->DP, &src->DP ) ); + MPI_CHK( mpi_copy( &dst->DQ, &src->DQ ) ); + MPI_CHK( mpi_copy( &dst->QP, &src->QP ) ); + + MPI_CHK( mpi_copy( &dst->RN, &src->RN ) ); + MPI_CHK( mpi_copy( &dst->RP, &src->RP ) ); + MPI_CHK( mpi_copy( &dst->RQ, &src->RQ ) ); + +#if !defined(POLARSSL_RSA_NO_CRT) + MPI_CHK( mpi_copy( &dst->Vi, &src->Vi ) ); + MPI_CHK( mpi_copy( &dst->Vf, &src->Vf ) ); +#endif + + dst->padding = src->padding; + dst->hash_id = src->hash_id; + +cleanup: + if( ret != 0 ) + rsa_free( dst ); + + return( ret ); +} + +/* + * Free the components of an RSA key + */ +void rsa_free( rsa_context *ctx ) +{ +#if !defined(POLARSSL_RSA_NO_CRT) + mpi_free( &ctx->Vi ); mpi_free( &ctx->Vf ); +#endif + mpi_free( &ctx->RQ ); mpi_free( &ctx->RP ); mpi_free( &ctx->RN ); + mpi_free( &ctx->QP ); mpi_free( &ctx->DQ ); mpi_free( &ctx->DP ); + mpi_free( &ctx->Q ); mpi_free( &ctx->P ); mpi_free( &ctx->D ); + mpi_free( &ctx->E ); mpi_free( &ctx->N ); + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_free( &ctx->mutex ); +#endif +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/sha1.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +#if defined(POLARSSL_PKCS1_V15) +static int myrand( void *rng_state, unsigned char *output, size_t len ) +{ +#if !defined(__OpenBSD__) + size_t i; + + if( rng_state != NULL ) + rng_state = NULL; + + for( i = 0; i < len; ++i ) + output[i] = rand(); +#else + if( rng_state != NULL ) + rng_state = NULL; + + arc4random_buf( output, len ); +#endif /* !OpenBSD */ + + return( 0 ); +} +#endif /* POLARSSL_PKCS1_V15 */ + +/* + * Checkup routine + */ +int rsa_self_test( int verbose ) +{ + int ret = 0; +#if defined(POLARSSL_PKCS1_V15) + size_t len; + rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(POLARSSL_SHA1_C) + unsigned char sha1sum[20]; +#endif + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + rsa.len = KEY_LEN; + MPI_CHK( mpi_read_string( &rsa.N , 16, RSA_N ) ); + MPI_CHK( mpi_read_string( &rsa.E , 16, RSA_E ) ); + MPI_CHK( mpi_read_string( &rsa.D , 16, RSA_D ) ); + MPI_CHK( mpi_read_string( &rsa.P , 16, RSA_P ) ); + MPI_CHK( mpi_read_string( &rsa.Q , 16, RSA_Q ) ); + MPI_CHK( mpi_read_string( &rsa.DP, 16, RSA_DP ) ); + MPI_CHK( mpi_read_string( &rsa.DQ, 16, RSA_DQ ) ); + MPI_CHK( mpi_read_string( &rsa.QP, 16, RSA_QP ) ); + + if( verbose != 0 ) + polarssl_printf( " RSA key validation: " ); + + if( rsa_check_pubkey( &rsa ) != 0 || + rsa_check_privkey( &rsa ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n PKCS#1 encryption : " ); + + memcpy( rsa_plaintext, RSA_PT, PT_LEN ); + + if( rsa_pkcs1_encrypt( &rsa, myrand, NULL, RSA_PUBLIC, PT_LEN, + rsa_plaintext, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n PKCS#1 decryption : " ); + + if( rsa_pkcs1_decrypt( &rsa, myrand, NULL, RSA_PRIVATE, &len, + rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted) ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + +#if defined(POLARSSL_SHA1_C) + if( verbose != 0 ) + polarssl_printf( "passed\n PKCS#1 data sign : " ); + + sha1( rsa_plaintext, PT_LEN, sha1sum ); + + if( rsa_pkcs1_sign( &rsa, myrand, NULL, RSA_PRIVATE, POLARSSL_MD_SHA1, 0, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n PKCS#1 sig. verify: " ); + + if( rsa_pkcs1_verify( &rsa, NULL, NULL, RSA_PUBLIC, POLARSSL_MD_SHA1, 0, + sha1sum, rsa_ciphertext ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n\n" ); +#endif /* POLARSSL_SHA1_C */ + +cleanup: + rsa_free( &rsa ); +#else /* POLARSSL_PKCS1_V15 */ + ((void) verbose); +#endif /* POLARSSL_PKCS1_V15 */ + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_RSA_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/sha1.c b/component/common/network/ssl/polarssl-1.3.8/library/sha1.c new file mode 100644 index 0000000..20408c7 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/sha1.c @@ -0,0 +1,661 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SHA1_C) + +#include "polarssl/sha1.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_SHA1_ALT) + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +void sha1_init( sha1_context *ctx ) +{ + memset( ctx, 0, sizeof( sha1_context ) ); +} + +void sha1_free( sha1_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( sha1_context ) ); +} + +/* + * SHA-1 context setup + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void sha1_process( sha1_context *ctx, const unsigned char data[64] ) +{ + uint32_t temp, W[16], A, B, C, D, E; + + GET_UINT32_BE( W[ 0], data, 0 ); + GET_UINT32_BE( W[ 1], data, 4 ); + GET_UINT32_BE( W[ 2], data, 8 ); + GET_UINT32_BE( W[ 3], data, 12 ); + GET_UINT32_BE( W[ 4], data, 16 ); + GET_UINT32_BE( W[ 5], data, 20 ); + GET_UINT32_BE( W[ 6], data, 24 ); + GET_UINT32_BE( W[ 7], data, 28 ); + GET_UINT32_BE( W[ 8], data, 32 ); + GET_UINT32_BE( W[ 9], data, 36 ); + GET_UINT32_BE( W[10], data, 40 ); + GET_UINT32_BE( W[11], data, 44 ); + GET_UINT32_BE( W[12], data, 48 ); + GET_UINT32_BE( W[13], data, 52 ); + GET_UINT32_BE( W[14], data, 56 ); + GET_UINT32_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ + W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* + * SHA-1 process buffer + */ +void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + sha1_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha1_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + memcpy( (void *) (ctx->buffer + left), input, ilen ); +} + +static const unsigned char sha1_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 +}; + +/* + * SHA-1 final digest + */ +void sha1_finish( sha1_context *ctx, unsigned char output[20] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], output, 0 ); + PUT_UINT32_BE( ctx->state[1], output, 4 ); + PUT_UINT32_BE( ctx->state[2], output, 8 ); + PUT_UINT32_BE( ctx->state[3], output, 12 ); + PUT_UINT32_BE( ctx->state[4], output, 16 ); +} + +#endif /* !POLARSSL_SHA1_ALT */ + +/* + * output = SHA-1( input buffer ) + */ +void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_init( &ctx ); + sha1_starts( &ctx ); + sha1_update( &ctx, input, ilen ); + sha1_finish( &ctx, output ); + sha1_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = SHA-1( file contents ) + */ +int sha1_file( const char *path, unsigned char output[20] ) +{ + FILE *f; + size_t n; + sha1_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); + + sha1_init( &ctx ); + sha1_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha1_update( &ctx, buf, n ); + + sha1_finish( &ctx, output ); + sha1_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * SHA-1 HMAC context setup + */ +void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, + size_t keylen ) +{ + size_t i; + unsigned char sum[20]; + + if( keylen > 64 ) + { + sha1( key, keylen, sum ); + keylen = 20; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha1_starts( ctx ); + sha1_update( ctx, ctx->ipad, 64 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * SHA-1 HMAC process buffer + */ +void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, + size_t ilen ) +{ + sha1_update( ctx, input, ilen ); +} + +/* + * SHA-1 HMAC final digest + */ +void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) +{ + unsigned char tmpbuf[20]; + + sha1_finish( ctx, tmpbuf ); + sha1_starts( ctx ); + sha1_update( ctx, ctx->opad, 64 ); + sha1_update( ctx, tmpbuf, 20 ); + sha1_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * SHA1 HMAC context reset + */ +void sha1_hmac_reset( sha1_context *ctx ) +{ + sha1_starts( ctx ); + sha1_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-SHA-1( hmac key, input buffer ) + */ +void sha1_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[20] ) +{ + sha1_context ctx; + + sha1_init( &ctx ); + sha1_hmac_starts( &ctx, key, keylen ); + sha1_hmac_update( &ctx, input, ilen ); + sha1_hmac_finish( &ctx, output ); + sha1_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) +/* + * FIPS-180-1 test vectors + */ +static unsigned char sha1_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha1_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha1_test_sum[3][20] = +{ + { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, + 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, + 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, + { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, + 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } +}; + +/* + * RFC 2202 test vectors + */ +static unsigned char sha1_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 80 times */ + { "" } +}; + +static const int sha1_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 80, 80 +}; + +static unsigned char sha1_hmac_test_buf[7][74] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "Test Using Larger Than Block-Size Key and Larger" + " Than One Block-Size Data" } +}; + +static const int sha1_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 73 +}; + +static const unsigned char sha1_hmac_test_sum[7][20] = +{ + { 0xB6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xE2, 0x8B, + 0xC0, 0xB6, 0xFB, 0x37, 0x8C, 0x8E, 0xF1, 0x46, 0xBE, 0x00 }, + { 0xEF, 0xFC, 0xDF, 0x6A, 0xE5, 0xEB, 0x2F, 0xA2, 0xD2, 0x74, + 0x16, 0xD5, 0xF1, 0x84, 0xDF, 0x9C, 0x25, 0x9A, 0x7C, 0x79 }, + { 0x12, 0x5D, 0x73, 0x42, 0xB9, 0xAC, 0x11, 0xCD, 0x91, 0xA3, + 0x9A, 0xF4, 0x8A, 0xA1, 0x7B, 0x4F, 0x63, 0xF1, 0x75, 0xD3 }, + { 0x4C, 0x90, 0x07, 0xF4, 0x02, 0x62, 0x50, 0xC6, 0xBC, 0x84, + 0x14, 0xF9, 0xBF, 0x50, 0xC8, 0x6C, 0x2D, 0x72, 0x35, 0xDA }, + { 0x4C, 0x1A, 0x03, 0x42, 0x4B, 0x55, 0xE0, 0x7F, 0xE7, 0xF2, + 0x7B, 0xE1 }, + { 0xAA, 0x4A, 0xE5, 0xE1, 0x52, 0x72, 0xD0, 0x0E, 0x95, 0x70, + 0x56, 0x37, 0xCE, 0x8A, 0x3B, 0x55, 0xED, 0x40, 0x21, 0x12 }, + { 0xE8, 0xE9, 0x9D, 0x0F, 0x45, 0x23, 0x7D, 0x78, 0x6D, 0x6B, + 0xBA, 0xA7, 0x96, 0x5C, 0x78, 0x08, 0xBB, 0xFF, 0x1A, 0x91 } +}; + +/* + * Checkup routine + */ +int sha1_self_test( int verbose ) +{ + int i, j, buflen, ret = 0; + unsigned char buf[1024]; + unsigned char sha1sum[20]; + sha1_context ctx; + + sha1_init( &ctx ); + + /* + * SHA-1 + */ + for( i = 0; i < 3; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " SHA-1 test #%d: ", i + 1 ); + + sha1_starts( &ctx ); + + if( i == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha1_update( &ctx, buf, buflen ); + } + else + sha1_update( &ctx, sha1_test_buf[i], + sha1_test_buflen[i] ); + + sha1_finish( &ctx, sha1sum ); + + if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + for( i = 0; i < 7; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " HMAC-SHA-1 test #%d: ", i + 1 ); + + if( i == 5 || i == 6 ) + { + memset( buf, '\xAA', buflen = 80 ); + sha1_hmac_starts( &ctx, buf, buflen ); + } + else + sha1_hmac_starts( &ctx, sha1_hmac_test_key[i], + sha1_hmac_test_keylen[i] ); + + sha1_hmac_update( &ctx, sha1_hmac_test_buf[i], + sha1_hmac_test_buflen[i] ); + + sha1_hmac_finish( &ctx, sha1sum ); + + buflen = ( i == 4 ) ? 12 : 20; + + if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + sha1_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_SHA1_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/sha256.c b/component/common/network/ssl/polarssl-1.3.8/library/sha256.c new file mode 100644 index 0000000..4fc6698 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/sha256.c @@ -0,0 +1,742 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SHA256_C) + +#include "polarssl/sha256.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_SHA256_ALT) + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +void sha256_init( sha256_context *ctx ) +{ + memset( ctx, 0, sizeof( sha256_context ) ); +} + +void sha256_free( sha256_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( sha256_context ) ); +} + +/* + * SHA-256 context setup + */ +void sha256_starts( sha256_context *ctx, int is224 ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + if( is224 == 0 ) + { + /* SHA-256 */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; + } + else + { + /* SHA-224 */ + ctx->state[0] = 0xC1059ED8; + ctx->state[1] = 0x367CD507; + ctx->state[2] = 0x3070DD17; + ctx->state[3] = 0xF70E5939; + ctx->state[4] = 0xFFC00B31; + ctx->state[5] = 0x68581511; + ctx->state[6] = 0x64F98FA7; + ctx->state[7] = 0xBEFA4FA4; + } + + ctx->is224 = is224; +} + +void sha256_process( sha256_context *ctx, const unsigned char data[64] ) +{ + uint32_t temp1, temp2, W[64]; + uint32_t A, B, C, D, E, F, G, H; + + GET_UINT32_BE( W[ 0], data, 0 ); + GET_UINT32_BE( W[ 1], data, 4 ); + GET_UINT32_BE( W[ 2], data, 8 ); + GET_UINT32_BE( W[ 3], data, 12 ); + GET_UINT32_BE( W[ 4], data, 16 ); + GET_UINT32_BE( W[ 5], data, 20 ); + GET_UINT32_BE( W[ 6], data, 24 ); + GET_UINT32_BE( W[ 7], data, 28 ); + GET_UINT32_BE( W[ 8], data, 32 ); + GET_UINT32_BE( W[ 9], data, 36 ); + GET_UINT32_BE( W[10], data, 40 ); + GET_UINT32_BE( W[11], data, 44 ); + GET_UINT32_BE( W[12], data, 48 ); + GET_UINT32_BE( W[13], data, 52 ); + GET_UINT32_BE( W[14], data, 56 ); + GET_UINT32_BE( W[15], data, 60 ); + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); + P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); + P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); + P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); + P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); + P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); + P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); + P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); + P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); + P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); + P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); + P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); + P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); + P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); + P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); + P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); + P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); + P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); + P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); + P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); + P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); + P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); + P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); + P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); + P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); + P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); + P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); + P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); + P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); + P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); + P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); + P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); + P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); + P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); + P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); + P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); + P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); + P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); + P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); + P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); + P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); + P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); + P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); + P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); + P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); + P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); + P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); + P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); + P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); + P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); + P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); + P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); + P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); + P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); + P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); + P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); + P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); + P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); + P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); + P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); + P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); + P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); + P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); + P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +/* + * SHA-256 process buffer + */ +void sha256_update( sha256_context *ctx, const unsigned char *input, + size_t ilen ) +{ + size_t fill; + uint32_t left; + + if( ilen == 0 ) + return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < (uint32_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + sha256_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 64 ) + { + sha256_process( ctx, input ); + input += 64; + ilen -= 64; + } + + if( ilen > 0 ) + memcpy( (void *) (ctx->buffer + left), input, ilen ); +} + +static const unsigned char sha256_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 +}; + +/* + * SHA-256 final digest + */ +void sha256_finish( sha256_context *ctx, unsigned char output[32] ) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha256_update( ctx, sha256_padding, padn ); + sha256_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], output, 0 ); + PUT_UINT32_BE( ctx->state[1], output, 4 ); + PUT_UINT32_BE( ctx->state[2], output, 8 ); + PUT_UINT32_BE( ctx->state[3], output, 12 ); + PUT_UINT32_BE( ctx->state[4], output, 16 ); + PUT_UINT32_BE( ctx->state[5], output, 20 ); + PUT_UINT32_BE( ctx->state[6], output, 24 ); + + if( ctx->is224 == 0 ) + PUT_UINT32_BE( ctx->state[7], output, 28 ); +} + +#endif /* !POLARSSL_SHA256_ALT */ + +/* + * output = SHA-256( input buffer ) + */ +void sha256( const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) +{ + sha256_context ctx; + + sha256_init( &ctx ); + sha256_starts( &ctx, is224 ); + sha256_update( &ctx, input, ilen ); + sha256_finish( &ctx, output ); + sha256_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = SHA-256( file contents ) + */ +int sha256_file( const char *path, unsigned char output[32], int is224 ) +{ + FILE *f; + size_t n; + sha256_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_SHA256_FILE_IO_ERROR ); + + sha256_init( &ctx ); + sha256_starts( &ctx, is224 ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha256_update( &ctx, buf, n ); + + sha256_finish( &ctx, output ); + sha256_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_SHA256_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * SHA-256 HMAC context setup + */ +void sha256_hmac_starts( sha256_context *ctx, const unsigned char *key, + size_t keylen, int is224 ) +{ + size_t i; + unsigned char sum[32]; + + if( keylen > 64 ) + { + sha256( key, keylen, sum, is224 ); + keylen = ( is224 ) ? 28 : 32; + key = sum; + } + + memset( ctx->ipad, 0x36, 64 ); + memset( ctx->opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha256_starts( ctx, is224 ); + sha256_update( ctx, ctx->ipad, 64 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * SHA-256 HMAC process buffer + */ +void sha256_hmac_update( sha256_context *ctx, const unsigned char *input, + size_t ilen ) +{ + sha256_update( ctx, input, ilen ); +} + +/* + * SHA-256 HMAC final digest + */ +void sha256_hmac_finish( sha256_context *ctx, unsigned char output[32] ) +{ + int is224, hlen; + unsigned char tmpbuf[32]; + + is224 = ctx->is224; + hlen = ( is224 == 0 ) ? 32 : 28; + + sha256_finish( ctx, tmpbuf ); + sha256_starts( ctx, is224 ); + sha256_update( ctx, ctx->opad, 64 ); + sha256_update( ctx, tmpbuf, hlen ); + sha256_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * SHA-256 HMAC context reset + */ +void sha256_hmac_reset( sha256_context *ctx ) +{ + sha256_starts( ctx, ctx->is224 ); + sha256_update( ctx, ctx->ipad, 64 ); +} + +/* + * output = HMAC-SHA-256( hmac key, input buffer ) + */ +void sha256_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[32], int is224 ) +{ + sha256_context ctx; + + sha256_init( &ctx ); + sha256_hmac_starts( &ctx, key, keylen, is224 ); + sha256_hmac_update( &ctx, input, ilen ); + sha256_hmac_finish( &ctx, output ); + sha256_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) +/* + * FIPS-180-2 test vectors + */ +static unsigned char sha256_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const int sha256_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha256_test_sum[6][32] = +{ + /* + * SHA-224 test vectors + */ + { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, + 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2, 0x55, 0xB3, + 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, + 0xE3, 0x6C, 0x9D, 0xA7 }, + { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, + 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89, 0x01, 0x50, + 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, + 0x52, 0x52, 0x25, 0x25 }, + { 0x20, 0x79, 0x46, 0x55, 0x98, 0x0C, 0x91, 0xD8, + 0xBB, 0xB4, 0xC1, 0xEA, 0x97, 0x61, 0x8A, 0x4B, + 0xF0, 0x3F, 0x42, 0x58, 0x19, 0x48, 0xB2, 0xEE, + 0x4E, 0xE7, 0xAD, 0x67 }, + + /* + * SHA-256 test vectors + */ + { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, + 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, + 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, + 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, + { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, + 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, + 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, + 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, + { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, + 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, + 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, + 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } +}; + +/* + * RFC 4231 test vectors + */ +static unsigned char sha256_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 131 times */ + { "" } +}; + +static const int sha256_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 131, 131 +}; + +static unsigned char sha256_hmac_test_buf[7][153] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "This is a test using a larger than block-size key " + "and a larger than block-size data. The key needs to " + "be hashed before being used by the HMAC algorithm." } +}; + +static const int sha256_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 152 +}; + +static const unsigned char sha256_hmac_test_sum[14][32] = +{ + /* + * HMAC-SHA-224 test vectors + */ + { 0x89, 0x6F, 0xB1, 0x12, 0x8A, 0xBB, 0xDF, 0x19, + 0x68, 0x32, 0x10, 0x7C, 0xD4, 0x9D, 0xF3, 0x3F, + 0x47, 0xB4, 0xB1, 0x16, 0x99, 0x12, 0xBA, 0x4F, + 0x53, 0x68, 0x4B, 0x22 }, + { 0xA3, 0x0E, 0x01, 0x09, 0x8B, 0xC6, 0xDB, 0xBF, + 0x45, 0x69, 0x0F, 0x3A, 0x7E, 0x9E, 0x6D, 0x0F, + 0x8B, 0xBE, 0xA2, 0xA3, 0x9E, 0x61, 0x48, 0x00, + 0x8F, 0xD0, 0x5E, 0x44 }, + { 0x7F, 0xB3, 0xCB, 0x35, 0x88, 0xC6, 0xC1, 0xF6, + 0xFF, 0xA9, 0x69, 0x4D, 0x7D, 0x6A, 0xD2, 0x64, + 0x93, 0x65, 0xB0, 0xC1, 0xF6, 0x5D, 0x69, 0xD1, + 0xEC, 0x83, 0x33, 0xEA }, + { 0x6C, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3C, 0xAC, + 0x6A, 0x2A, 0xBC, 0x1B, 0xB3, 0x82, 0x62, 0x7C, + 0xEC, 0x6A, 0x90, 0xD8, 0x6E, 0xFC, 0x01, 0x2D, + 0xE7, 0xAF, 0xEC, 0x5A }, + { 0x0E, 0x2A, 0xEA, 0x68, 0xA9, 0x0C, 0x8D, 0x37, + 0xC9, 0x88, 0xBC, 0xDB, 0x9F, 0xCA, 0x6F, 0xA8 }, + { 0x95, 0xE9, 0xA0, 0xDB, 0x96, 0x20, 0x95, 0xAD, + 0xAE, 0xBE, 0x9B, 0x2D, 0x6F, 0x0D, 0xBC, 0xE2, + 0xD4, 0x99, 0xF1, 0x12, 0xF2, 0xD2, 0xB7, 0x27, + 0x3F, 0xA6, 0x87, 0x0E }, + { 0x3A, 0x85, 0x41, 0x66, 0xAC, 0x5D, 0x9F, 0x02, + 0x3F, 0x54, 0xD5, 0x17, 0xD0, 0xB3, 0x9D, 0xBD, + 0x94, 0x67, 0x70, 0xDB, 0x9C, 0x2B, 0x95, 0xC9, + 0xF6, 0xF5, 0x65, 0xD1 }, + + /* + * HMAC-SHA-256 test vectors + */ + { 0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53, + 0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B, + 0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7, + 0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7 }, + { 0x5B, 0xDC, 0xC1, 0x46, 0xBF, 0x60, 0x75, 0x4E, + 0x6A, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xC7, + 0x5A, 0x00, 0x3F, 0x08, 0x9D, 0x27, 0x39, 0x83, + 0x9D, 0xEC, 0x58, 0xB9, 0x64, 0xEC, 0x38, 0x43 }, + { 0x77, 0x3E, 0xA9, 0x1E, 0x36, 0x80, 0x0E, 0x46, + 0x85, 0x4D, 0xB8, 0xEB, 0xD0, 0x91, 0x81, 0xA7, + 0x29, 0x59, 0x09, 0x8B, 0x3E, 0xF8, 0xC1, 0x22, + 0xD9, 0x63, 0x55, 0x14, 0xCE, 0xD5, 0x65, 0xFE }, + { 0x82, 0x55, 0x8A, 0x38, 0x9A, 0x44, 0x3C, 0x0E, + 0xA4, 0xCC, 0x81, 0x98, 0x99, 0xF2, 0x08, 0x3A, + 0x85, 0xF0, 0xFA, 0xA3, 0xE5, 0x78, 0xF8, 0x07, + 0x7A, 0x2E, 0x3F, 0xF4, 0x67, 0x29, 0x66, 0x5B }, + { 0xA3, 0xB6, 0x16, 0x74, 0x73, 0x10, 0x0E, 0xE0, + 0x6E, 0x0C, 0x79, 0x6C, 0x29, 0x55, 0x55, 0x2B }, + { 0x60, 0xE4, 0x31, 0x59, 0x1E, 0xE0, 0xB6, 0x7F, + 0x0D, 0x8A, 0x26, 0xAA, 0xCB, 0xF5, 0xB7, 0x7F, + 0x8E, 0x0B, 0xC6, 0x21, 0x37, 0x28, 0xC5, 0x14, + 0x05, 0x46, 0x04, 0x0F, 0x0E, 0xE3, 0x7F, 0x54 }, + { 0x9B, 0x09, 0xFF, 0xA7, 0x1B, 0x94, 0x2F, 0xCB, + 0x27, 0x63, 0x5F, 0xBC, 0xD5, 0xB0, 0xE9, 0x44, + 0xBF, 0xDC, 0x63, 0x64, 0x4F, 0x07, 0x13, 0x93, + 0x8A, 0x7F, 0x51, 0x53, 0x5C, 0x3A, 0x35, 0xE2 } +}; + +/* + * Checkup routine + */ +int sha256_self_test( int verbose ) +{ + int i, j, k, buflen, ret = 0; + unsigned char buf[1024]; + unsigned char sha256sum[32]; + sha256_context ctx; + + sha256_init( &ctx ); + + for( i = 0; i < 6; i++ ) + { + j = i % 3; + k = i < 3; + + if( verbose != 0 ) + polarssl_printf( " SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + sha256_starts( &ctx, k ); + + if( j == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha256_update( &ctx, buf, buflen ); + } + else + sha256_update( &ctx, sha256_test_buf[j], + sha256_test_buflen[j] ); + + sha256_finish( &ctx, sha256sum ); + + if( memcmp( sha256sum, sha256_test_sum[i], 32 - k * 4 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + for( i = 0; i < 14; i++ ) + { + j = i % 7; + k = i < 7; + + if( verbose != 0 ) + polarssl_printf( " HMAC-SHA-%d test #%d: ", 256 - k * 32, j + 1 ); + + if( j == 5 || j == 6 ) + { + memset( buf, '\xAA', buflen = 131 ); + sha256_hmac_starts( &ctx, buf, buflen, k ); + } + else + sha256_hmac_starts( &ctx, sha256_hmac_test_key[j], + sha256_hmac_test_keylen[j], k ); + + sha256_hmac_update( &ctx, sha256_hmac_test_buf[j], + sha256_hmac_test_buflen[j] ); + + sha256_hmac_finish( &ctx, sha256sum ); + + buflen = ( j == 4 ) ? 16 : 32 - k * 4; + + if( memcmp( sha256sum, sha256_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + sha256_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_SHA256_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/sha512.c b/component/common/network/ssl/polarssl-1.3.8/library/sha512.c new file mode 100644 index 0000000..f1d1525 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/sha512.c @@ -0,0 +1,796 @@ +/* + * FIPS-180-2 compliant SHA-384/512 implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SHA-512 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SHA512_C) + +#include "polarssl/sha512.h" + +#if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) +#include +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if !defined(POLARSSL_SHA512_ALT) + +/* + * 64-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT64_BE +#define GET_UINT64_BE(n,b,i) \ +{ \ + (n) = ( (uint64_t) (b)[(i) ] << 56 ) \ + | ( (uint64_t) (b)[(i) + 1] << 48 ) \ + | ( (uint64_t) (b)[(i) + 2] << 40 ) \ + | ( (uint64_t) (b)[(i) + 3] << 32 ) \ + | ( (uint64_t) (b)[(i) + 4] << 24 ) \ + | ( (uint64_t) (b)[(i) + 5] << 16 ) \ + | ( (uint64_t) (b)[(i) + 6] << 8 ) \ + | ( (uint64_t) (b)[(i) + 7] ); \ +} +#endif /* GET_UINT64_BE */ + +#ifndef PUT_UINT64_BE +#define PUT_UINT64_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 56 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 48 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 40 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) >> 32 ); \ + (b)[(i) + 4] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 5] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 6] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 7] = (unsigned char) ( (n) ); \ +} +#endif /* PUT_UINT64_BE */ + +/* + * Round constants + */ +static const uint64_t K[80] = +{ + UL64(0x428A2F98D728AE22), UL64(0x7137449123EF65CD), + UL64(0xB5C0FBCFEC4D3B2F), UL64(0xE9B5DBA58189DBBC), + UL64(0x3956C25BF348B538), UL64(0x59F111F1B605D019), + UL64(0x923F82A4AF194F9B), UL64(0xAB1C5ED5DA6D8118), + UL64(0xD807AA98A3030242), UL64(0x12835B0145706FBE), + UL64(0x243185BE4EE4B28C), UL64(0x550C7DC3D5FFB4E2), + UL64(0x72BE5D74F27B896F), UL64(0x80DEB1FE3B1696B1), + UL64(0x9BDC06A725C71235), UL64(0xC19BF174CF692694), + UL64(0xE49B69C19EF14AD2), UL64(0xEFBE4786384F25E3), + UL64(0x0FC19DC68B8CD5B5), UL64(0x240CA1CC77AC9C65), + UL64(0x2DE92C6F592B0275), UL64(0x4A7484AA6EA6E483), + UL64(0x5CB0A9DCBD41FBD4), UL64(0x76F988DA831153B5), + UL64(0x983E5152EE66DFAB), UL64(0xA831C66D2DB43210), + UL64(0xB00327C898FB213F), UL64(0xBF597FC7BEEF0EE4), + UL64(0xC6E00BF33DA88FC2), UL64(0xD5A79147930AA725), + UL64(0x06CA6351E003826F), UL64(0x142929670A0E6E70), + UL64(0x27B70A8546D22FFC), UL64(0x2E1B21385C26C926), + UL64(0x4D2C6DFC5AC42AED), UL64(0x53380D139D95B3DF), + UL64(0x650A73548BAF63DE), UL64(0x766A0ABB3C77B2A8), + UL64(0x81C2C92E47EDAEE6), UL64(0x92722C851482353B), + UL64(0xA2BFE8A14CF10364), UL64(0xA81A664BBC423001), + UL64(0xC24B8B70D0F89791), UL64(0xC76C51A30654BE30), + UL64(0xD192E819D6EF5218), UL64(0xD69906245565A910), + UL64(0xF40E35855771202A), UL64(0x106AA07032BBD1B8), + UL64(0x19A4C116B8D2D0C8), UL64(0x1E376C085141AB53), + UL64(0x2748774CDF8EEB99), UL64(0x34B0BCB5E19B48A8), + UL64(0x391C0CB3C5C95A63), UL64(0x4ED8AA4AE3418ACB), + UL64(0x5B9CCA4F7763E373), UL64(0x682E6FF3D6B2B8A3), + UL64(0x748F82EE5DEFB2FC), UL64(0x78A5636F43172F60), + UL64(0x84C87814A1F0AB72), UL64(0x8CC702081A6439EC), + UL64(0x90BEFFFA23631E28), UL64(0xA4506CEBDE82BDE9), + UL64(0xBEF9A3F7B2C67915), UL64(0xC67178F2E372532B), + UL64(0xCA273ECEEA26619C), UL64(0xD186B8C721C0C207), + UL64(0xEADA7DD6CDE0EB1E), UL64(0xF57D4F7FEE6ED178), + UL64(0x06F067AA72176FBA), UL64(0x0A637DC5A2C898A6), + UL64(0x113F9804BEF90DAE), UL64(0x1B710B35131C471B), + UL64(0x28DB77F523047D84), UL64(0x32CAAB7B40C72493), + UL64(0x3C9EBE0A15C9BEBC), UL64(0x431D67C49C100D4C), + UL64(0x4CC5D4BECB3E42B6), UL64(0x597F299CFC657E2A), + UL64(0x5FCB6FAB3AD6FAEC), UL64(0x6C44198C4A475817) +}; + +void sha512_init( sha512_context *ctx ) +{ + memset( ctx, 0, sizeof( sha512_context ) ); +} + +void sha512_free( sha512_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( sha512_context ) ); +} + +/* + * SHA-512 context setup + */ +void sha512_starts( sha512_context *ctx, int is384 ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + if( is384 == 0 ) + { + /* SHA-512 */ + ctx->state[0] = UL64(0x6A09E667F3BCC908); + ctx->state[1] = UL64(0xBB67AE8584CAA73B); + ctx->state[2] = UL64(0x3C6EF372FE94F82B); + ctx->state[3] = UL64(0xA54FF53A5F1D36F1); + ctx->state[4] = UL64(0x510E527FADE682D1); + ctx->state[5] = UL64(0x9B05688C2B3E6C1F); + ctx->state[6] = UL64(0x1F83D9ABFB41BD6B); + ctx->state[7] = UL64(0x5BE0CD19137E2179); + } + else + { + /* SHA-384 */ + ctx->state[0] = UL64(0xCBBB9D5DC1059ED8); + ctx->state[1] = UL64(0x629A292A367CD507); + ctx->state[2] = UL64(0x9159015A3070DD17); + ctx->state[3] = UL64(0x152FECD8F70E5939); + ctx->state[4] = UL64(0x67332667FFC00B31); + ctx->state[5] = UL64(0x8EB44A8768581511); + ctx->state[6] = UL64(0xDB0C2E0D64F98FA7); + ctx->state[7] = UL64(0x47B5481DBEFA4FA4); + } + + ctx->is384 = is384; +} + +void sha512_process( sha512_context *ctx, const unsigned char data[128] ) +{ + int i; + uint64_t temp1, temp2, W[80]; + uint64_t A, B, C, D, E, F, G, H; + +#define SHR(x,n) (x >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (64 - n))) + +#define S0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7)) +#define S1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x, 6)) + +#define S2(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39)) +#define S3(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + for( i = 0; i < 16; i++ ) + { + GET_UINT64_BE( W[i], data, i << 3 ); + } + + for( ; i < 80; i++ ) + { + W[i] = S1(W[i - 2]) + W[i - 7] + + S0(W[i - 15]) + W[i - 16]; + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + i = 0; + + do + { + P( A, B, C, D, E, F, G, H, W[i], K[i] ); i++; + P( H, A, B, C, D, E, F, G, W[i], K[i] ); i++; + P( G, H, A, B, C, D, E, F, W[i], K[i] ); i++; + P( F, G, H, A, B, C, D, E, W[i], K[i] ); i++; + P( E, F, G, H, A, B, C, D, W[i], K[i] ); i++; + P( D, E, F, G, H, A, B, C, W[i], K[i] ); i++; + P( C, D, E, F, G, H, A, B, W[i], K[i] ); i++; + P( B, C, D, E, F, G, H, A, W[i], K[i] ); i++; + } + while( i < 80 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +/* + * SHA-512 process buffer + */ +void sha512_update( sha512_context *ctx, const unsigned char *input, + size_t ilen ) +{ + size_t fill; + unsigned int left; + + if( ilen == 0 ) + return; + + left = (unsigned int) (ctx->total[0] & 0x7F); + fill = 128 - left; + + ctx->total[0] += (uint64_t) ilen; + + if( ctx->total[0] < (uint64_t) ilen ) + ctx->total[1]++; + + if( left && ilen >= fill ) + { + memcpy( (void *) (ctx->buffer + left), input, fill ); + sha512_process( ctx, ctx->buffer ); + input += fill; + ilen -= fill; + left = 0; + } + + while( ilen >= 128 ) + { + sha512_process( ctx, input ); + input += 128; + ilen -= 128; + } + + if( ilen > 0 ) + memcpy( (void *) (ctx->buffer + left), input, ilen ); +} + +static const unsigned char sha512_padding[128] = +{ + 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, + 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, 0 +}; + +/* + * SHA-512 final digest + */ +void sha512_finish( sha512_context *ctx, unsigned char output[64] ) +{ + size_t last, padn; + uint64_t high, low; + unsigned char msglen[16]; + + high = ( ctx->total[0] >> 61 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT64_BE( high, msglen, 0 ); + PUT_UINT64_BE( low, msglen, 8 ); + + last = (size_t)( ctx->total[0] & 0x7F ); + padn = ( last < 112 ) ? ( 112 - last ) : ( 240 - last ); + + sha512_update( ctx, sha512_padding, padn ); + sha512_update( ctx, msglen, 16 ); + + PUT_UINT64_BE( ctx->state[0], output, 0 ); + PUT_UINT64_BE( ctx->state[1], output, 8 ); + PUT_UINT64_BE( ctx->state[2], output, 16 ); + PUT_UINT64_BE( ctx->state[3], output, 24 ); + PUT_UINT64_BE( ctx->state[4], output, 32 ); + PUT_UINT64_BE( ctx->state[5], output, 40 ); + + if( ctx->is384 == 0 ) + { + PUT_UINT64_BE( ctx->state[6], output, 48 ); + PUT_UINT64_BE( ctx->state[7], output, 56 ); + } +} + +#endif /* !POLARSSL_SHA512_ALT */ + +/* + * output = SHA-512( input buffer ) + */ +void sha512( const unsigned char *input, size_t ilen, + unsigned char output[64], int is384 ) +{ + sha512_context ctx; + + sha512_init( &ctx ); + sha512_starts( &ctx, is384 ); + sha512_update( &ctx, input, ilen ); + sha512_finish( &ctx, output ); + sha512_free( &ctx ); +} + +#if defined(POLARSSL_FS_IO) +/* + * output = SHA-512( file contents ) + */ +int sha512_file( const char *path, unsigned char output[64], int is384 ) +{ + FILE *f; + size_t n; + sha512_context ctx; + unsigned char buf[1024]; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_SHA512_FILE_IO_ERROR ); + + sha512_init( &ctx ); + sha512_starts( &ctx, is384 ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha512_update( &ctx, buf, n ); + + sha512_finish( &ctx, output ); + sha512_free( &ctx ); + + if( ferror( f ) != 0 ) + { + fclose( f ); + return( POLARSSL_ERR_SHA512_FILE_IO_ERROR ); + } + + fclose( f ); + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * SHA-512 HMAC context setup + */ +void sha512_hmac_starts( sha512_context *ctx, const unsigned char *key, + size_t keylen, int is384 ) +{ + size_t i; + unsigned char sum[64]; + + if( keylen > 128 ) + { + sha512( key, keylen, sum, is384 ); + keylen = ( is384 ) ? 48 : 64; + key = sum; + } + + memset( ctx->ipad, 0x36, 128 ); + memset( ctx->opad, 0x5C, 128 ); + + for( i = 0; i < keylen; i++ ) + { + ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); + ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); + } + + sha512_starts( ctx, is384 ); + sha512_update( ctx, ctx->ipad, 128 ); + + polarssl_zeroize( sum, sizeof( sum ) ); +} + +/* + * SHA-512 HMAC process buffer + */ +void sha512_hmac_update( sha512_context *ctx, + const unsigned char *input, size_t ilen ) +{ + sha512_update( ctx, input, ilen ); +} + +/* + * SHA-512 HMAC final digest + */ +void sha512_hmac_finish( sha512_context *ctx, unsigned char output[64] ) +{ + int is384, hlen; + unsigned char tmpbuf[64]; + + is384 = ctx->is384; + hlen = ( is384 == 0 ) ? 64 : 48; + + sha512_finish( ctx, tmpbuf ); + sha512_starts( ctx, is384 ); + sha512_update( ctx, ctx->opad, 128 ); + sha512_update( ctx, tmpbuf, hlen ); + sha512_finish( ctx, output ); + + polarssl_zeroize( tmpbuf, sizeof( tmpbuf ) ); +} + +/* + * SHA-512 HMAC context reset + */ +void sha512_hmac_reset( sha512_context *ctx ) +{ + sha512_starts( ctx, ctx->is384 ); + sha512_update( ctx, ctx->ipad, 128 ); +} + +/* + * output = HMAC-SHA-512( hmac key, input buffer ) + */ +void sha512_hmac( const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char output[64], int is384 ) +{ + sha512_context ctx; + + sha512_init( &ctx ); + sha512_hmac_starts( &ctx, key, keylen, is384 ); + sha512_hmac_update( &ctx, input, ilen ); + sha512_hmac_finish( &ctx, output ); + sha512_free( &ctx ); +} + +#if defined(POLARSSL_SELF_TEST) + +/* + * FIPS-180-2 test vectors + */ +static unsigned char sha512_test_buf[3][113] = +{ + { "abc" }, + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" }, + { "" } +}; + +static const int sha512_test_buflen[3] = +{ + 3, 112, 1000 +}; + +static const unsigned char sha512_test_sum[6][64] = +{ + /* + * SHA-384 test vectors + */ + { 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, + 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, 0x07, + 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, + 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, 0x5B, 0xED, + 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, + 0x58, 0xBA, 0xEC, 0xA1, 0x34, 0xC8, 0x25, 0xA7 }, + { 0x09, 0x33, 0x0C, 0x33, 0xF7, 0x11, 0x47, 0xE8, + 0x3D, 0x19, 0x2F, 0xC7, 0x82, 0xCD, 0x1B, 0x47, + 0x53, 0x11, 0x1B, 0x17, 0x3B, 0x3B, 0x05, 0xD2, + 0x2F, 0xA0, 0x80, 0x86, 0xE3, 0xB0, 0xF7, 0x12, + 0xFC, 0xC7, 0xC7, 0x1A, 0x55, 0x7E, 0x2D, 0xB9, + 0x66, 0xC3, 0xE9, 0xFA, 0x91, 0x74, 0x60, 0x39 }, + { 0x9D, 0x0E, 0x18, 0x09, 0x71, 0x64, 0x74, 0xCB, + 0x08, 0x6E, 0x83, 0x4E, 0x31, 0x0A, 0x4A, 0x1C, + 0xED, 0x14, 0x9E, 0x9C, 0x00, 0xF2, 0x48, 0x52, + 0x79, 0x72, 0xCE, 0xC5, 0x70, 0x4C, 0x2A, 0x5B, + 0x07, 0xB8, 0xB3, 0xDC, 0x38, 0xEC, 0xC4, 0xEB, + 0xAE, 0x97, 0xDD, 0xD8, 0x7F, 0x3D, 0x89, 0x85 }, + + /* + * SHA-512 test vectors + */ + { 0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA, + 0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31, + 0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2, + 0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A, + 0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8, + 0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD, + 0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E, + 0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F }, + { 0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, + 0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F, + 0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, + 0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18, + 0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, + 0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A, + 0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, + 0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09 }, + { 0xE7, 0x18, 0x48, 0x3D, 0x0C, 0xE7, 0x69, 0x64, + 0x4E, 0x2E, 0x42, 0xC7, 0xBC, 0x15, 0xB4, 0x63, + 0x8E, 0x1F, 0x98, 0xB1, 0x3B, 0x20, 0x44, 0x28, + 0x56, 0x32, 0xA8, 0x03, 0xAF, 0xA9, 0x73, 0xEB, + 0xDE, 0x0F, 0xF2, 0x44, 0x87, 0x7E, 0xA6, 0x0A, + 0x4C, 0xB0, 0x43, 0x2C, 0xE5, 0x77, 0xC3, 0x1B, + 0xEB, 0x00, 0x9C, 0x5C, 0x2C, 0x49, 0xAA, 0x2E, + 0x4E, 0xAD, 0xB2, 0x17, 0xAD, 0x8C, 0xC0, 0x9B } +}; + +/* + * RFC 4231 test vectors + */ +static unsigned char sha512_hmac_test_key[7][26] = +{ + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B" }, + { "Jefe" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C" }, + { "" }, /* 0xAA 131 times */ + { "" } +}; + +static const int sha512_hmac_test_keylen[7] = +{ + 20, 4, 20, 25, 20, 131, 131 +}; + +static unsigned char sha512_hmac_test_buf[7][153] = +{ + { "Hi There" }, + { "what do ya want for nothing?" }, + { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, + { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, + { "Test With Truncation" }, + { "Test Using Larger Than Block-Size Key - Hash Key First" }, + { "This is a test using a larger than block-size key " + "and a larger than block-size data. The key needs to " + "be hashed before being used by the HMAC algorithm." } +}; + +static const int sha512_hmac_test_buflen[7] = +{ + 8, 28, 50, 50, 20, 54, 152 +}; + +static const unsigned char sha512_hmac_test_sum[14][64] = +{ + /* + * HMAC-SHA-384 test vectors + */ + { 0xAF, 0xD0, 0x39, 0x44, 0xD8, 0x48, 0x95, 0x62, + 0x6B, 0x08, 0x25, 0xF4, 0xAB, 0x46, 0x90, 0x7F, + 0x15, 0xF9, 0xDA, 0xDB, 0xE4, 0x10, 0x1E, 0xC6, + 0x82, 0xAA, 0x03, 0x4C, 0x7C, 0xEB, 0xC5, 0x9C, + 0xFA, 0xEA, 0x9E, 0xA9, 0x07, 0x6E, 0xDE, 0x7F, + 0x4A, 0xF1, 0x52, 0xE8, 0xB2, 0xFA, 0x9C, 0xB6 }, + { 0xAF, 0x45, 0xD2, 0xE3, 0x76, 0x48, 0x40, 0x31, + 0x61, 0x7F, 0x78, 0xD2, 0xB5, 0x8A, 0x6B, 0x1B, + 0x9C, 0x7E, 0xF4, 0x64, 0xF5, 0xA0, 0x1B, 0x47, + 0xE4, 0x2E, 0xC3, 0x73, 0x63, 0x22, 0x44, 0x5E, + 0x8E, 0x22, 0x40, 0xCA, 0x5E, 0x69, 0xE2, 0xC7, + 0x8B, 0x32, 0x39, 0xEC, 0xFA, 0xB2, 0x16, 0x49 }, + { 0x88, 0x06, 0x26, 0x08, 0xD3, 0xE6, 0xAD, 0x8A, + 0x0A, 0xA2, 0xAC, 0xE0, 0x14, 0xC8, 0xA8, 0x6F, + 0x0A, 0xA6, 0x35, 0xD9, 0x47, 0xAC, 0x9F, 0xEB, + 0xE8, 0x3E, 0xF4, 0xE5, 0x59, 0x66, 0x14, 0x4B, + 0x2A, 0x5A, 0xB3, 0x9D, 0xC1, 0x38, 0x14, 0xB9, + 0x4E, 0x3A, 0xB6, 0xE1, 0x01, 0xA3, 0x4F, 0x27 }, + { 0x3E, 0x8A, 0x69, 0xB7, 0x78, 0x3C, 0x25, 0x85, + 0x19, 0x33, 0xAB, 0x62, 0x90, 0xAF, 0x6C, 0xA7, + 0x7A, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9C, + 0xC5, 0x57, 0x7C, 0x6E, 0x1F, 0x57, 0x3B, 0x4E, + 0x68, 0x01, 0xDD, 0x23, 0xC4, 0xA7, 0xD6, 0x79, + 0xCC, 0xF8, 0xA3, 0x86, 0xC6, 0x74, 0xCF, 0xFB }, + { 0x3A, 0xBF, 0x34, 0xC3, 0x50, 0x3B, 0x2A, 0x23, + 0xA4, 0x6E, 0xFC, 0x61, 0x9B, 0xAE, 0xF8, 0x97 }, + { 0x4E, 0xCE, 0x08, 0x44, 0x85, 0x81, 0x3E, 0x90, + 0x88, 0xD2, 0xC6, 0x3A, 0x04, 0x1B, 0xC5, 0xB4, + 0x4F, 0x9E, 0xF1, 0x01, 0x2A, 0x2B, 0x58, 0x8F, + 0x3C, 0xD1, 0x1F, 0x05, 0x03, 0x3A, 0xC4, 0xC6, + 0x0C, 0x2E, 0xF6, 0xAB, 0x40, 0x30, 0xFE, 0x82, + 0x96, 0x24, 0x8D, 0xF1, 0x63, 0xF4, 0x49, 0x52 }, + { 0x66, 0x17, 0x17, 0x8E, 0x94, 0x1F, 0x02, 0x0D, + 0x35, 0x1E, 0x2F, 0x25, 0x4E, 0x8F, 0xD3, 0x2C, + 0x60, 0x24, 0x20, 0xFE, 0xB0, 0xB8, 0xFB, 0x9A, + 0xDC, 0xCE, 0xBB, 0x82, 0x46, 0x1E, 0x99, 0xC5, + 0xA6, 0x78, 0xCC, 0x31, 0xE7, 0x99, 0x17, 0x6D, + 0x38, 0x60, 0xE6, 0x11, 0x0C, 0x46, 0x52, 0x3E }, + + /* + * HMAC-SHA-512 test vectors + */ + { 0x87, 0xAA, 0x7C, 0xDE, 0xA5, 0xEF, 0x61, 0x9D, + 0x4F, 0xF0, 0xB4, 0x24, 0x1A, 0x1D, 0x6C, 0xB0, + 0x23, 0x79, 0xF4, 0xE2, 0xCE, 0x4E, 0xC2, 0x78, + 0x7A, 0xD0, 0xB3, 0x05, 0x45, 0xE1, 0x7C, 0xDE, + 0xDA, 0xA8, 0x33, 0xB7, 0xD6, 0xB8, 0xA7, 0x02, + 0x03, 0x8B, 0x27, 0x4E, 0xAE, 0xA3, 0xF4, 0xE4, + 0xBE, 0x9D, 0x91, 0x4E, 0xEB, 0x61, 0xF1, 0x70, + 0x2E, 0x69, 0x6C, 0x20, 0x3A, 0x12, 0x68, 0x54 }, + { 0x16, 0x4B, 0x7A, 0x7B, 0xFC, 0xF8, 0x19, 0xE2, + 0xE3, 0x95, 0xFB, 0xE7, 0x3B, 0x56, 0xE0, 0xA3, + 0x87, 0xBD, 0x64, 0x22, 0x2E, 0x83, 0x1F, 0xD6, + 0x10, 0x27, 0x0C, 0xD7, 0xEA, 0x25, 0x05, 0x54, + 0x97, 0x58, 0xBF, 0x75, 0xC0, 0x5A, 0x99, 0x4A, + 0x6D, 0x03, 0x4F, 0x65, 0xF8, 0xF0, 0xE6, 0xFD, + 0xCA, 0xEA, 0xB1, 0xA3, 0x4D, 0x4A, 0x6B, 0x4B, + 0x63, 0x6E, 0x07, 0x0A, 0x38, 0xBC, 0xE7, 0x37 }, + { 0xFA, 0x73, 0xB0, 0x08, 0x9D, 0x56, 0xA2, 0x84, + 0xEF, 0xB0, 0xF0, 0x75, 0x6C, 0x89, 0x0B, 0xE9, + 0xB1, 0xB5, 0xDB, 0xDD, 0x8E, 0xE8, 0x1A, 0x36, + 0x55, 0xF8, 0x3E, 0x33, 0xB2, 0x27, 0x9D, 0x39, + 0xBF, 0x3E, 0x84, 0x82, 0x79, 0xA7, 0x22, 0xC8, + 0x06, 0xB4, 0x85, 0xA4, 0x7E, 0x67, 0xC8, 0x07, + 0xB9, 0x46, 0xA3, 0x37, 0xBE, 0xE8, 0x94, 0x26, + 0x74, 0x27, 0x88, 0x59, 0xE1, 0x32, 0x92, 0xFB }, + { 0xB0, 0xBA, 0x46, 0x56, 0x37, 0x45, 0x8C, 0x69, + 0x90, 0xE5, 0xA8, 0xC5, 0xF6, 0x1D, 0x4A, 0xF7, + 0xE5, 0x76, 0xD9, 0x7F, 0xF9, 0x4B, 0x87, 0x2D, + 0xE7, 0x6F, 0x80, 0x50, 0x36, 0x1E, 0xE3, 0xDB, + 0xA9, 0x1C, 0xA5, 0xC1, 0x1A, 0xA2, 0x5E, 0xB4, + 0xD6, 0x79, 0x27, 0x5C, 0xC5, 0x78, 0x80, 0x63, + 0xA5, 0xF1, 0x97, 0x41, 0x12, 0x0C, 0x4F, 0x2D, + 0xE2, 0xAD, 0xEB, 0xEB, 0x10, 0xA2, 0x98, 0xDD }, + { 0x41, 0x5F, 0xAD, 0x62, 0x71, 0x58, 0x0A, 0x53, + 0x1D, 0x41, 0x79, 0xBC, 0x89, 0x1D, 0x87, 0xA6 }, + { 0x80, 0xB2, 0x42, 0x63, 0xC7, 0xC1, 0xA3, 0xEB, + 0xB7, 0x14, 0x93, 0xC1, 0xDD, 0x7B, 0xE8, 0xB4, + 0x9B, 0x46, 0xD1, 0xF4, 0x1B, 0x4A, 0xEE, 0xC1, + 0x12, 0x1B, 0x01, 0x37, 0x83, 0xF8, 0xF3, 0x52, + 0x6B, 0x56, 0xD0, 0x37, 0xE0, 0x5F, 0x25, 0x98, + 0xBD, 0x0F, 0xD2, 0x21, 0x5D, 0x6A, 0x1E, 0x52, + 0x95, 0xE6, 0x4F, 0x73, 0xF6, 0x3F, 0x0A, 0xEC, + 0x8B, 0x91, 0x5A, 0x98, 0x5D, 0x78, 0x65, 0x98 }, + { 0xE3, 0x7B, 0x6A, 0x77, 0x5D, 0xC8, 0x7D, 0xBA, + 0xA4, 0xDF, 0xA9, 0xF9, 0x6E, 0x5E, 0x3F, 0xFD, + 0xDE, 0xBD, 0x71, 0xF8, 0x86, 0x72, 0x89, 0x86, + 0x5D, 0xF5, 0xA3, 0x2D, 0x20, 0xCD, 0xC9, 0x44, + 0xB6, 0x02, 0x2C, 0xAC, 0x3C, 0x49, 0x82, 0xB1, + 0x0D, 0x5E, 0xEB, 0x55, 0xC3, 0xE4, 0xDE, 0x15, + 0x13, 0x46, 0x76, 0xFB, 0x6D, 0xE0, 0x44, 0x60, + 0x65, 0xC9, 0x74, 0x40, 0xFA, 0x8C, 0x6A, 0x58 } +}; + +/* + * Checkup routine + */ +int sha512_self_test( int verbose ) +{ + int i, j, k, buflen, ret = 0; + unsigned char buf[1024]; + unsigned char sha512sum[64]; + sha512_context ctx; + + sha512_init( &ctx ); + + for( i = 0; i < 6; i++ ) + { + j = i % 3; + k = i < 3; + + if( verbose != 0 ) + polarssl_printf( " SHA-%d test #%d: ", 512 - k * 128, j + 1 ); + + sha512_starts( &ctx, k ); + + if( j == 2 ) + { + memset( buf, 'a', buflen = 1000 ); + + for( j = 0; j < 1000; j++ ) + sha512_update( &ctx, buf, buflen ); + } + else + sha512_update( &ctx, sha512_test_buf[j], + sha512_test_buflen[j] ); + + sha512_finish( &ctx, sha512sum ); + + if( memcmp( sha512sum, sha512_test_sum[i], 64 - k * 16 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + for( i = 0; i < 14; i++ ) + { + j = i % 7; + k = i < 7; + + if( verbose != 0 ) + polarssl_printf( " HMAC-SHA-%d test #%d: ", 512 - k * 128, j + 1 ); + + if( j == 5 || j == 6 ) + { + memset( buf, '\xAA', buflen = 131 ); + sha512_hmac_starts( &ctx, buf, buflen, k ); + } + else + sha512_hmac_starts( &ctx, sha512_hmac_test_key[j], + sha512_hmac_test_keylen[j], k ); + + sha512_hmac_update( &ctx, sha512_hmac_test_buf[j], + sha512_hmac_test_buflen[j] ); + + sha512_hmac_finish( &ctx, sha512sum ); + + buflen = ( j == 4 ) ? 16 : 64 - k * 16; + + if( memcmp( sha512sum, sha512_hmac_test_sum[i], buflen ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + sha512_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_SHA512_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ssl_cache.c b/component/common/network/ssl/polarssl-1.3.8/library/ssl_cache.c new file mode 100644 index 0000000..836b685 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ssl_cache.c @@ -0,0 +1,335 @@ +/* + * SSL session cache implementation + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * These session callbacks use a simple chained list + * to store and retrieve the session information. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SSL_CACHE_C) + +#include "polarssl/ssl_cache.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +void ssl_cache_init( ssl_cache_context *cache ) +{ + memset( cache, 0, sizeof( ssl_cache_context ) ); + + cache->timeout = SSL_CACHE_DEFAULT_TIMEOUT; + cache->max_entries = SSL_CACHE_DEFAULT_MAX_ENTRIES; + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_init( &cache->mutex ); +#endif +} + +int ssl_cache_get( void *data, ssl_session *session ) +{ + int ret = 1; +#if defined(POLARSSL_HAVE_TIME) + time_t t = time( NULL ); +#endif + ssl_cache_context *cache = (ssl_cache_context *) data; + ssl_cache_entry *cur, *entry; + +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_lock( &cache->mutex ) != 0 ) + return( 1 ); +#endif + + cur = cache->chain; + entry = NULL; + + while( cur != NULL ) + { + entry = cur; + cur = cur->next; + +#if defined(POLARSSL_HAVE_TIME) + if( cache->timeout != 0 && + (int) ( t - entry->timestamp ) > cache->timeout ) + continue; +#endif + + if( session->ciphersuite != entry->session.ciphersuite || + session->compression != entry->session.compression || + session->length != entry->session.length ) + continue; + + if( memcmp( session->id, entry->session.id, + entry->session.length ) != 0 ) + continue; + + memcpy( session->master, entry->session.master, 48 ); + + session->verify_result = entry->session.verify_result; + +#if defined(POLARSSL_X509_CRT_PARSE_C) + /* + * Restore peer certificate (without rest of the original chain) + */ + if( entry->peer_cert.p != NULL ) + { + session->peer_cert = + (x509_crt *) polarssl_malloc( sizeof(x509_crt) ); + + if( session->peer_cert == NULL ) + { + ret = 1; + goto exit; + } + + x509_crt_init( session->peer_cert ); + if( x509_crt_parse( session->peer_cert, entry->peer_cert.p, + entry->peer_cert.len ) != 0 ) + { + polarssl_free( session->peer_cert ); + session->peer_cert = NULL; + ret = 1; + goto exit; + } + } +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + ret = 0; + goto exit; + } + +exit: +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &cache->mutex ) != 0 ) + ret = 1; +#endif + + return( ret ); +} + +int ssl_cache_set( void *data, const ssl_session *session ) +{ + int ret = 1; +#if defined(POLARSSL_HAVE_TIME) + time_t t = time( NULL ), oldest = 0; + ssl_cache_entry *old = NULL; +#endif + ssl_cache_context *cache = (ssl_cache_context *) data; + ssl_cache_entry *cur, *prv; + int count = 0; + +#if defined(POLARSSL_THREADING_C) + if( ( ret = polarssl_mutex_lock( &cache->mutex ) ) != 0 ) + return( ret ); +#endif + + cur = cache->chain; + prv = NULL; + + while( cur != NULL ) + { + count++; + +#if defined(POLARSSL_HAVE_TIME) + if( cache->timeout != 0 && + (int) ( t - cur->timestamp ) > cache->timeout ) + { + cur->timestamp = t; + break; /* expired, reuse this slot, update timestamp */ + } +#endif + + if( memcmp( session->id, cur->session.id, cur->session.length ) == 0 ) + break; /* client reconnected, keep timestamp for session id */ + +#if defined(POLARSSL_HAVE_TIME) + if( oldest == 0 || cur->timestamp < oldest ) + { + oldest = cur->timestamp; + old = cur; + } +#endif + + prv = cur; + cur = cur->next; + } + + if( cur == NULL ) + { +#if defined(POLARSSL_HAVE_TIME) + /* + * Reuse oldest entry if max_entries reached + */ + if( count >= cache->max_entries ) + { + if( old == NULL ) + { + ret = 1; + goto exit; + } + + cur = old; + } +#else /* POLARSSL_HAVE_TIME */ + /* + * Reuse first entry in chain if max_entries reached, + * but move to last place + */ + if( count >= cache->max_entries ) + { + if( cache->chain == NULL ) + { + ret = 1; + goto exit; + } + + cur = cache->chain; + cache->chain = cur->next; + cur->next = NULL; + prv->next = cur; + } +#endif /* POLARSSL_HAVE_TIME */ + else + { + /* + * max_entries not reached, create new entry + */ + cur = (ssl_cache_entry *) + polarssl_malloc( sizeof(ssl_cache_entry) ); + if( cur == NULL ) + { + ret = 1; + goto exit; + } + + memset( cur, 0, sizeof(ssl_cache_entry) ); + + if( prv == NULL ) + cache->chain = cur; + else + prv->next = cur; + } + +#if defined(POLARSSL_HAVE_TIME) + cur->timestamp = t; +#endif + } + + memcpy( &cur->session, session, sizeof( ssl_session ) ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + /* + * If we're reusing an entry, free its certificate first + */ + if( cur->peer_cert.p != NULL ) + { + polarssl_free( cur->peer_cert.p ); + memset( &cur->peer_cert, 0, sizeof(x509_buf) ); + } + + /* + * Store peer certificate + */ + if( session->peer_cert != NULL ) + { + cur->peer_cert.p = (unsigned char *) + polarssl_malloc( session->peer_cert->raw.len ); + if( cur->peer_cert.p == NULL ) + { + ret = 1; + goto exit; + } + + memcpy( cur->peer_cert.p, session->peer_cert->raw.p, + session->peer_cert->raw.len ); + cur->peer_cert.len = session->peer_cert->raw.len; + + cur->session.peer_cert = NULL; + } +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + ret = 0; + +exit: +#if defined(POLARSSL_THREADING_C) + if( polarssl_mutex_unlock( &cache->mutex ) != 0 ) + ret = 1; +#endif + + return( ret ); +} + +#if defined(POLARSSL_HAVE_TIME) +void ssl_cache_set_timeout( ssl_cache_context *cache, int timeout ) +{ + if( timeout < 0 ) timeout = 0; + + cache->timeout = timeout; +} +#endif /* POLARSSL_HAVE_TIME */ + +void ssl_cache_set_max_entries( ssl_cache_context *cache, int max ) +{ + if( max < 0 ) max = 0; + + cache->max_entries = max; +} + +void ssl_cache_free( ssl_cache_context *cache ) +{ + ssl_cache_entry *cur, *prv; + + cur = cache->chain; + + while( cur != NULL ) + { + prv = cur; + cur = cur->next; + + ssl_session_free( &prv->session ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + polarssl_free( prv->peer_cert.p ); +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + polarssl_free( prv ); + } + +#if defined(POLARSSL_THREADING_C) + polarssl_mutex_free( &cache->mutex ); +#endif +} + +#endif /* POLARSSL_SSL_CACHE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ssl_ciphersuites.c b/component/common/network/ssl/polarssl-1.3.8/library/ssl_ciphersuites.c new file mode 100644 index 0000000..df838e2 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ssl_ciphersuites.c @@ -0,0 +1,1843 @@ +/** + * \file ssl_ciphersuites.c + * + * \brief SSL ciphersuites for PolarSSL + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SSL_TLS_C) + +#include "polarssl/ssl_ciphersuites.h" +#include "polarssl/ssl.h" + +#include + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +/* + * Ordered from most preferred to least preferred in terms of security. + * + * Current rule (except rc4, weak and null which come last): + * 1. By key exchange: + * Forward-secure non-PSK > forward-secure PSK > other non-PSK > other PSK + * 2. By key length and cipher: + * AES-256 > Camellia-256 > AES-128 > Camellia-128 > 3DES + * 3. By cipher mode when relevant GCM > CCM > CBC > CCM_8 + * 4. By hash function used when relevant + * 5. By key exchange/auth again: EC > non-EC + */ +static const int ciphersuite_preference[] = +{ +#if defined(SSL_CIPHERSUITES) + SSL_CIPHERSUITES, +#else + /* All AES-256 ephemeral suites */ + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + TLS_DHE_RSA_WITH_AES_256_CCM, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + TLS_DHE_RSA_WITH_AES_256_CCM_8, + + /* All CAMELLIA-256 ephemeral suites */ + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + + /* All AES-128 ephemeral suites */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + TLS_DHE_RSA_WITH_AES_128_CCM, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + TLS_DHE_RSA_WITH_AES_128_CCM_8, + + /* All CAMELLIA-128 ephemeral suites */ + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + + /* All remaining >= 128-bit ephemeral suites */ + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + + /* The PSK ephemeral suites */ + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + TLS_DHE_PSK_WITH_AES_256_CCM, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + TLS_DHE_PSK_WITH_AES_256_CCM_8, + + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + TLS_DHE_PSK_WITH_AES_128_CCM, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, + TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + TLS_DHE_PSK_WITH_AES_128_CCM_8, + + TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + + /* All AES-256 suites */ + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_256_CCM, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_256_CCM_8, + + /* All CAMELLIA-256 suites */ + TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + + /* All AES-128 suites */ + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_128_CCM, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_128_CCM_8, + + /* All CAMELLIA-128 suites */ + TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + + /* All remaining >= 128-bit suites */ + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + + /* The RSA PSK suites */ + TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, + TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, + + TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, + TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, + + TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + + /* The PSK suites */ + TLS_PSK_WITH_AES_256_GCM_SHA384, + TLS_PSK_WITH_AES_256_CCM, + TLS_PSK_WITH_AES_256_CBC_SHA384, + TLS_PSK_WITH_AES_256_CBC_SHA, + TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, + TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, + TLS_PSK_WITH_AES_256_CCM_8, + + TLS_PSK_WITH_AES_128_GCM_SHA256, + TLS_PSK_WITH_AES_128_CCM, + TLS_PSK_WITH_AES_128_CBC_SHA256, + TLS_PSK_WITH_AES_128_CBC_SHA, + TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, + TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, + TLS_PSK_WITH_AES_128_CCM_8, + + TLS_PSK_WITH_3DES_EDE_CBC_SHA, + + /* RC4 suites */ + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_ECDHE_PSK_WITH_RC4_128_SHA, + TLS_DHE_PSK_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_RC4_128_MD5, + TLS_ECDH_RSA_WITH_RC4_128_SHA, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + TLS_RSA_PSK_WITH_RC4_128_SHA, + TLS_PSK_WITH_RC4_128_SHA, + + /* Weak suites */ + TLS_DHE_RSA_WITH_DES_CBC_SHA, + TLS_RSA_WITH_DES_CBC_SHA, + + /* NULL suites */ + TLS_ECDHE_ECDSA_WITH_NULL_SHA, + TLS_ECDHE_RSA_WITH_NULL_SHA, + TLS_ECDHE_PSK_WITH_NULL_SHA384, + TLS_ECDHE_PSK_WITH_NULL_SHA256, + TLS_ECDHE_PSK_WITH_NULL_SHA, + TLS_DHE_PSK_WITH_NULL_SHA384, + TLS_DHE_PSK_WITH_NULL_SHA256, + TLS_DHE_PSK_WITH_NULL_SHA, + + TLS_RSA_WITH_NULL_SHA256, + TLS_RSA_WITH_NULL_SHA, + TLS_RSA_WITH_NULL_MD5, + TLS_ECDH_RSA_WITH_NULL_SHA, + TLS_ECDH_ECDSA_WITH_NULL_SHA, + TLS_RSA_PSK_WITH_NULL_SHA384, + TLS_RSA_PSK_WITH_NULL_SHA256, + TLS_RSA_PSK_WITH_NULL_SHA, + TLS_PSK_WITH_NULL_SHA384, + TLS_PSK_WITH_NULL_SHA256, + TLS_PSK_WITH_NULL_SHA, + +#endif + 0 +}; + +static const ssl_ciphersuite_t ciphersuite_definitions[] = +{ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA1_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA512_C */ +#if defined(POLARSSL_CCM_C) + { TLS_ECDHE_ECDSA_WITH_AES_256_CCM, "TLS-ECDHE-ECDSA-WITH-AES-256-CCM", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CCM, "TLS-ECDHE-ECDSA-WITH-AES-128-CCM", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, +#endif /* POLARSSL_CCM_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_ECDSA_WITH_NULL_SHA, "TLS-ECDHE-ECDSA-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA1_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS-ECDHE-RSA-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_RSA_WITH_NULL_SHA, "TLS-ECDHE-RSA-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA512_C) && defined(POLARSSL_GCM_C) + { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C && POLARSSL_GCM_C */ + +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_GCM_C) + { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_CCM_C) + { TLS_DHE_RSA_WITH_AES_256_CCM, "TLS-DHE-RSA-WITH-AES-256-CCM", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_DHE_RSA_WITH_AES_256_CCM_8, "TLS-DHE-RSA-WITH-AES-256-CCM-8", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, + { TLS_DHE_RSA_WITH_AES_128_CCM, "TLS-DHE-RSA-WITH-AES-128-CCM", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_DHE_RSA_WITH_AES_128_CCM_8, "TLS-DHE-RSA-WITH-AES-128-CCM-8", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, +#endif /* POLARSSL_CCM_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA512_C) && defined(POLARSSL_GCM_C) + { TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS-RSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C && POLARSSL_GCM_C */ + +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_GCM_C) + { TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS-RSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS-RSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS-RSA-WITH-AES-256-CBC-SHA256", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA1_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_RSA_WITH_AES_128_CBC_SHA, "TLS-RSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_RSA_WITH_AES_256_CBC_SHA, "TLS-RSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_CCM_C) + { TLS_RSA_WITH_AES_256_CCM, "TLS-RSA-WITH-AES-256-CCM", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_RSA_WITH_AES_256_CCM_8, "TLS-RSA-WITH-AES-256-CCM-8", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, + { TLS_RSA_WITH_AES_128_CCM, "TLS-RSA-WITH-AES-128-CCM", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_RSA_WITH_AES_128_CCM_8, "TLS-RSA-WITH-AES-128-CCM-8", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, +#endif /* POLARSSL_CCM_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-RSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-RSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS-RSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_MD5_C) + { TLS_RSA_WITH_RC4_128_MD5, "TLS-RSA-WITH-RC4-128-MD5", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_MD5, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif + +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_RC4_128_SHA, "TLS-RSA-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif +#endif /* POLARSSL_ARC4_C */ +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA1_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-ECDH-RSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-ECDH-RSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_RSA_WITH_RC4_128_SHA, "TLS-ECDH-RSA-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_RSA_WITH_NULL_SHA, "TLS-ECDH-RSA-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_SHA1_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_SHA256_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) + { TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_GCM_C) + { TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + { TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_ECDSA_WITH_RC4_128_SHA, "TLS-ECDH-ECDSA-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ + +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDH_ECDSA_WITH_NULL_SHA, "TLS-ECDH-ECDSA-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDH_ECDSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_PSK_WITH_AES_128_GCM_SHA256, "TLS-PSK-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_PSK_WITH_AES_256_GCM_SHA384, "TLS-PSK-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_PSK_WITH_AES_128_CBC_SHA256, "TLS-PSK-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_PSK_WITH_AES_256_CBC_SHA384, "TLS-PSK-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_PSK_WITH_AES_128_CBC_SHA, "TLS-PSK-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_PSK_WITH_AES_256_CBC_SHA, "TLS-PSK-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_CCM_C) + { TLS_PSK_WITH_AES_256_CCM, "TLS-PSK-WITH-AES-256-CCM", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_PSK_WITH_AES_256_CCM_8, "TLS-PSK-WITH-AES-256-CCM-8", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, + { TLS_PSK_WITH_AES_128_CCM, "TLS-PSK-WITH-AES-128-CCM", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_PSK_WITH_AES_128_CCM_8, "TLS-PSK-WITH-AES-128-CCM-8", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, +#endif /* POLARSSL_CCM_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-PSK-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-PSK-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_PSK_WITH_3DES_EDE_CBC_SHA, "TLS-PSK-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_PSK_WITH_RC4_128_SHA, "TLS-PSK-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, "TLS-DHE-PSK-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, "TLS-DHE-PSK-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, "TLS-DHE-PSK-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, "TLS-DHE-PSK-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_PSK_WITH_AES_128_CBC_SHA, "TLS-DHE-PSK-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_DHE_PSK_WITH_AES_256_CBC_SHA, "TLS-DHE-PSK-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_CCM_C) + { TLS_DHE_PSK_WITH_AES_256_CCM, "TLS-DHE-PSK-WITH-AES-256-CCM", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_DHE_PSK_WITH_AES_256_CCM_8, "TLS-DHE-PSK-WITH-AES-256-CCM-8", + POLARSSL_CIPHER_AES_256_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, + { TLS_DHE_PSK_WITH_AES_128_CCM, "TLS-DHE-PSK-WITH-AES-128-CCM", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + { TLS_DHE_PSK_WITH_AES_128_CCM_8, "TLS-DHE-PSK-WITH-AES-128-CCM-8", + POLARSSL_CIPHER_AES_128_CCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_SHORT_TAG }, +#endif /* POLARSSL_CCM_C */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, "TLS-DHE-PSK-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_PSK_WITH_RC4_128_SHA, "TLS-DHE-PSK-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#if defined(POLARSSL_AES_C) + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, "TLS-ECDHE-PSK-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_PSK_WITH_RC4_128_SHA, "TLS-ECDHE-PSK-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) +#if defined(POLARSSL_AES_C) +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, "TLS-RSA-PSK-WITH-AES-128-GCM-SHA256", + POLARSSL_CIPHER_AES_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, "TLS-RSA-PSK-WITH-AES-256-GCM-SHA384", + POLARSSL_CIPHER_AES_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ + +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, "TLS-RSA-PSK-WITH-AES-128-CBC-SHA256", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, "TLS-RSA-PSK-WITH-AES-256-CBC-SHA384", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ + +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_PSK_WITH_AES_128_CBC_SHA, "TLS-RSA-PSK-WITH-AES-128-CBC-SHA", + POLARSSL_CIPHER_AES_128_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, + + { TLS_RSA_PSK_WITH_AES_256_CBC_SHA, "TLS-RSA-PSK-WITH-AES-256-CBC-SHA", + POLARSSL_CIPHER_AES_256_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_AES_C */ + +#if defined(POLARSSL_CAMELLIA_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_CBC, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_CBC, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ + +#if defined(POLARSSL_GCM_C) +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-RSA-PSK-WITH-CAMELLIA-128-GCM-SHA256", + POLARSSL_CIPHER_CAMELLIA_128_GCM, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) + { TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-RSA-PSK-WITH-CAMELLIA-256-GCM-SHA384", + POLARSSL_CIPHER_CAMELLIA_256_GCM, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_GCM_C */ +#endif /* POLARSSL_CAMELLIA_C */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, "TLS-RSA-PSK-WITH-3DES-EDE-CBC-SHA", + POLARSSL_CIPHER_DES_EDE3_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ + +#if defined(POLARSSL_ARC4_C) +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_PSK_WITH_RC4_128_SHA, "TLS-RSA-PSK-WITH-RC4-128-SHA", + POLARSSL_CIPHER_ARC4_128, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + 0 }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_ARC4_C */ +#endif /* POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) +#if defined(POLARSSL_CIPHER_NULL_CIPHER) +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) +#if defined(POLARSSL_MD5_C) + { TLS_RSA_WITH_NULL_MD5, "TLS-RSA-WITH-NULL-MD5", + POLARSSL_CIPHER_NULL, POLARSSL_MD_MD5, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_NULL_SHA, "TLS-RSA-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_WITH_NULL_SHA256, "TLS-RSA-WITH-NULL-SHA256", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_PSK_WITH_NULL_SHA, "TLS-PSK-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ + +#if defined(POLARSSL_SHA256_C) + { TLS_PSK_WITH_NULL_SHA256, "TLS-PSK-WITH-NULL-SHA256", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA512_C) + { TLS_PSK_WITH_NULL_SHA384, "TLS-PSK-WITH-NULL-SHA384", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_PSK_WITH_NULL_SHA, "TLS-DHE-PSK-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ + +#if defined(POLARSSL_SHA256_C) + { TLS_DHE_PSK_WITH_NULL_SHA256, "TLS-DHE-PSK-WITH-NULL-SHA256", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA512_C) + { TLS_DHE_PSK_WITH_NULL_SHA384, "TLS-DHE-PSK-WITH-NULL-SHA384", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_DHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_ECDHE_PSK_WITH_NULL_SHA, "TLS-ECDHE-PSK-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ + +#if defined(POLARSSL_SHA256_C) + { TLS_ECDHE_PSK_WITH_NULL_SHA256, "TLS-ECDHE-PSK-WITH-NULL-SHA256", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA512_C) + { TLS_ECDHE_PSK_WITH_NULL_SHA384, "TLS-ECDHE-PSK-WITH-NULL-SHA384", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_ECDHE_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_1, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_PSK_WITH_NULL_SHA, "TLS-RSA-PSK-WITH-NULL-SHA", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ + +#if defined(POLARSSL_SHA256_C) + { TLS_RSA_PSK_WITH_NULL_SHA256, "TLS-RSA-PSK-WITH-NULL-SHA256", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA256, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif + +#if defined(POLARSSL_SHA512_C) + { TLS_RSA_PSK_WITH_NULL_SHA384, "TLS-RSA-PSK-WITH-NULL-SHA384", + POLARSSL_CIPHER_NULL, POLARSSL_MD_SHA384, POLARSSL_KEY_EXCHANGE_RSA_PSK, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif +#endif /* POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ + +#if defined(POLARSSL_DES_C) +#if defined(POLARSSL_CIPHER_MODE_CBC) +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS-DHE-RSA-WITH-DES-CBC-SHA", + POLARSSL_CIPHER_DES_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_DHE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) +#if defined(POLARSSL_SHA1_C) + { TLS_RSA_WITH_DES_CBC_SHA, "TLS-RSA-WITH-DES-CBC-SHA", + POLARSSL_CIPHER_DES_CBC, POLARSSL_MD_SHA1, POLARSSL_KEY_EXCHANGE_RSA, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0, + SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_3, + POLARSSL_CIPHERSUITE_WEAK }, +#endif /* POLARSSL_SHA1_C */ +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* POLARSSL_DES_C */ +#endif /* POLARSSL_ENABLE_WEAK_CIPHERSUITES */ + + { 0, "", 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +#if defined(SSL_CIPHERSUITES) +const int *ssl_list_ciphersuites( void ) +{ + return( ciphersuite_preference ); +} +#else +#define MAX_CIPHERSUITES sizeof( ciphersuite_definitions ) / \ + sizeof( ciphersuite_definitions[0] ) +static int supported_ciphersuites[MAX_CIPHERSUITES]; +static int supported_init = 0; + +const int *ssl_list_ciphersuites( void ) +{ + /* + * On initial call filter out all ciphersuites not supported by current + * build based on presence in the ciphersuite_definitions. + */ + if( supported_init == 0 ) + { + const int *p; + int *q; + + for( p = ciphersuite_preference, q = supported_ciphersuites; + *p != 0 && q < supported_ciphersuites + MAX_CIPHERSUITES - 1; + p++ ) + { +#if defined(POLARSSL_REMOVE_ARC4_CIPHERSUITES) + const ssl_ciphersuite_t *cs_info; + if( ( cs_info = ssl_ciphersuite_from_id( *p ) ) != NULL && + cs_info->cipher != POLARSSL_CIPHER_ARC4_128 ) +#else + if( ssl_ciphersuite_from_id( *p ) != NULL ) +#endif + *(q++) = *p; + } + *q = 0; + + supported_init = 1; + } + + return( supported_ciphersuites ); +}; +#endif /* SSL_CIPHERSUITES */ + +const ssl_ciphersuite_t *ssl_ciphersuite_from_string( + const char *ciphersuite_name ) +{ + const ssl_ciphersuite_t *cur = ciphersuite_definitions; + + if( NULL == ciphersuite_name ) + return( NULL ); + + while( cur->id != 0 ) + { + if( 0 == strcasecmp( cur->name, ciphersuite_name ) ) + return( cur ); + + cur++; + } + + return( NULL ); +} + +const ssl_ciphersuite_t *ssl_ciphersuite_from_id( int ciphersuite ) +{ + const ssl_ciphersuite_t *cur = ciphersuite_definitions; + + while( cur->id != 0 ) + { + if( cur->id == ciphersuite ) + return( cur ); + + cur++; + } + + return( NULL ); +} + +const char *ssl_get_ciphersuite_name( const int ciphersuite_id ) +{ + const ssl_ciphersuite_t *cur; + + cur = ssl_ciphersuite_from_id( ciphersuite_id ); + + if( cur == NULL ) + return( "unknown" ); + + return( cur->name ); +} + +int ssl_get_ciphersuite_id( const char *ciphersuite_name ) +{ + const ssl_ciphersuite_t *cur; + + cur = ssl_ciphersuite_from_string( ciphersuite_name ); + + if( cur == NULL ) + return( 0 ); + + return( cur->id ); +} + +#if defined(POLARSSL_PK_C) +pk_type_t ssl_get_ciphersuite_sig_pk_alg( const ssl_ciphersuite_t *info ) +{ + switch( info->key_exchange ) + { + case POLARSSL_KEY_EXCHANGE_RSA: + case POLARSSL_KEY_EXCHANGE_DHE_RSA: + case POLARSSL_KEY_EXCHANGE_ECDHE_RSA: + case POLARSSL_KEY_EXCHANGE_RSA_PSK: + return( POLARSSL_PK_RSA ); + + case POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA: + return( POLARSSL_PK_ECDSA ); + + case POLARSSL_KEY_EXCHANGE_ECDH_RSA: + case POLARSSL_KEY_EXCHANGE_ECDH_ECDSA: + return( POLARSSL_PK_ECKEY ); + + default: + return( POLARSSL_PK_NONE ); + } +} +#endif /* POLARSSL_PK_C */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) +int ssl_ciphersuite_uses_ec( const ssl_ciphersuite_t *info ) +{ + switch( info->key_exchange ) + { + case POLARSSL_KEY_EXCHANGE_ECDHE_RSA: + case POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA: + case POLARSSL_KEY_EXCHANGE_ECDHE_PSK: + case POLARSSL_KEY_EXCHANGE_ECDH_RSA: + case POLARSSL_KEY_EXCHANGE_ECDH_ECDSA: + return( 1 ); + + default: + return( 0 ); + } +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +int ssl_ciphersuite_uses_psk( const ssl_ciphersuite_t *info ) +{ + switch( info->key_exchange ) + { + case POLARSSL_KEY_EXCHANGE_PSK: + case POLARSSL_KEY_EXCHANGE_RSA_PSK: + case POLARSSL_KEY_EXCHANGE_DHE_PSK: + case POLARSSL_KEY_EXCHANGE_ECDHE_PSK: + return( 1 ); + + default: + return( 0 ); + } +} +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#endif /* POLARSSL_SSL_TLS_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ssl_cli.c b/component/common/network/ssl/polarssl-1.3.8/library/ssl_cli.c new file mode 100644 index 0000000..d38d769 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ssl_cli.c @@ -0,0 +1,2635 @@ +/* + * SSLv3/TLSv1 client-side functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SSL_CLI_C) + +#include "polarssl/debug.h" +#include "polarssl/ssl.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include + +#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +#if defined(POLARSSL_HAVE_TIME) +#include +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} +#endif + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) +static void ssl_write_hostname_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + *olen = 0; + + if( ssl->hostname == NULL ) + return; + + SSL_DEBUG_MSG( 3, ( "client hello, adding server name extension: %s", + ssl->hostname ) ); + + /* + * struct { + * NameType name_type; + * select (name_type) { + * case host_name: HostName; + * } name; + * } ServerName; + * + * enum { + * host_name(0), (255) + * } NameType; + * + * opaque HostName<1..2^16-1>; + * + * struct { + * ServerName server_name_list<1..2^16-1> + * } ServerNameList; + */ + *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME ) & 0xFF ); + + *p++ = (unsigned char)( ( (ssl->hostname_len + 5) >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( (ssl->hostname_len + 5) ) & 0xFF ); + + *p++ = (unsigned char)( ( (ssl->hostname_len + 3) >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( (ssl->hostname_len + 3) ) & 0xFF ); + + *p++ = (unsigned char)( ( TLS_EXT_SERVERNAME_HOSTNAME ) & 0xFF ); + *p++ = (unsigned char)( ( ssl->hostname_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ssl->hostname_len ) & 0xFF ); + + memcpy( p, ssl->hostname, ssl->hostname_len ); + + *olen = ssl->hostname_len + 9; +} +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ + +static void ssl_write_renegotiation_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + *olen = 0; + + if( ssl->renegotiation != SSL_RENEGOTIATION ) + return; + + SSL_DEBUG_MSG( 3, ( "client hello, adding renegotiation extension" ) ); + + /* + * Secure renegotiation + */ + *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO ) & 0xFF ); + + *p++ = 0x00; + *p++ = ( ssl->verify_data_len + 1 ) & 0xFF; + *p++ = ssl->verify_data_len & 0xFF; + + memcpy( p, ssl->own_verify_data, ssl->verify_data_len ); + + *olen = 5 + ssl->verify_data_len; +} + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +static void ssl_write_signature_algorithms_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + size_t sig_alg_len = 0; +#if defined(POLARSSL_RSA_C) || defined(POLARSSL_ECDSA_C) + unsigned char *sig_alg_list = buf + 6; +#endif + + *olen = 0; + + if( ssl->max_minor_ver != SSL_MINOR_VERSION_3 ) + return; + + SSL_DEBUG_MSG( 3, ( "client hello, adding signature_algorithms extension" ) ); + + /* + * Prepare signature_algorithms extension (TLS 1.2) + */ +#if defined(POLARSSL_RSA_C) +#if defined(POLARSSL_SHA512_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA512; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA384; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; +#endif +#if defined(POLARSSL_SHA256_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA256; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA224; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; +#endif +#if defined(POLARSSL_SHA1_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA1; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; +#endif +#if defined(POLARSSL_MD5_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_MD5; + sig_alg_list[sig_alg_len++] = SSL_SIG_RSA; +#endif +#endif /* POLARSSL_RSA_C */ +#if defined(POLARSSL_ECDSA_C) +#if defined(POLARSSL_SHA512_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA512; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA384; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; +#endif +#if defined(POLARSSL_SHA256_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA256; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA224; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; +#endif +#if defined(POLARSSL_SHA1_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_SHA1; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; +#endif +#if defined(POLARSSL_MD5_C) + sig_alg_list[sig_alg_len++] = SSL_HASH_MD5; + sig_alg_list[sig_alg_len++] = SSL_SIG_ECDSA; +#endif +#endif /* POLARSSL_ECDSA_C */ + + /* + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + */ + *p++ = (unsigned char)( ( TLS_EXT_SIG_ALG >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SIG_ALG ) & 0xFF ); + + *p++ = (unsigned char)( ( ( sig_alg_len + 2 ) >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ( sig_alg_len + 2 ) ) & 0xFF ); + + *p++ = (unsigned char)( ( sig_alg_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( sig_alg_len ) & 0xFF ); + + *olen = 6 + sig_alg_len; +} +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) +static void ssl_write_supported_elliptic_curves_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + unsigned char *elliptic_curve_list = p + 6; + size_t elliptic_curve_len = 0; + const ecp_curve_info *info; +#if defined(POLARSSL_SSL_SET_CURVES) + const ecp_group_id *grp_id; +#else + ((void) ssl); +#endif + + *olen = 0; + + SSL_DEBUG_MSG( 3, ( "client hello, adding supported_elliptic_curves extension" ) ); + +#if defined(POLARSSL_SSL_SET_CURVES) + for( grp_id = ssl->curve_list; *grp_id != POLARSSL_ECP_DP_NONE; grp_id++ ) + { + info = ecp_curve_info_from_grp_id( *grp_id ); +#else + for( info = ecp_curve_list(); info->grp_id != POLARSSL_ECP_DP_NONE; info++ ) + { +#endif + + elliptic_curve_list[elliptic_curve_len++] = info->tls_id >> 8; + elliptic_curve_list[elliptic_curve_len++] = info->tls_id & 0xFF; + } + + if( elliptic_curve_len == 0 ) + return; + + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_ELLIPTIC_CURVES >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_ELLIPTIC_CURVES ) & 0xFF ); + + *p++ = (unsigned char)( ( ( elliptic_curve_len + 2 ) >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ( elliptic_curve_len + 2 ) ) & 0xFF ); + + *p++ = (unsigned char)( ( ( elliptic_curve_len ) >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ( elliptic_curve_len ) ) & 0xFF ); + + *olen = 6 + elliptic_curve_len; +} + +static void ssl_write_supported_point_formats_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + ((void) ssl); + + *olen = 0; + + SSL_DEBUG_MSG( 3, ( "client hello, adding supported_point_formats extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_POINT_FORMATS >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_POINT_FORMATS ) & 0xFF ); + + *p++ = 0x00; + *p++ = 2; + + *p++ = 1; + *p++ = POLARSSL_ECP_PF_UNCOMPRESSED; + + *olen = 6; +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +static void ssl_write_max_fragment_length_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->mfl_code == SSL_MAX_FRAG_LEN_NONE ) { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding max_fragment_length extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH ) & 0xFF ); + + *p++ = 0x00; + *p++ = 1; + + *p++ = ssl->mfl_code; + + *olen = 5; +} +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +static void ssl_write_truncated_hmac_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->trunc_hmac == SSL_TRUNC_HMAC_DISABLED ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding truncated_hmac extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_TRUNCATED_HMAC >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_TRUNCATED_HMAC ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static void ssl_write_session_ticket_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + size_t tlen = ssl->session_negotiate->ticket_len; + + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding session ticket extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET ) & 0xFF ); + + *p++ = (unsigned char)( ( tlen >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( tlen ) & 0xFF ); + + *olen = 4; + + if( ssl->session_negotiate->ticket == NULL || + ssl->session_negotiate->ticket_len == 0 ) + { + return; + } + + SSL_DEBUG_MSG( 3, ( "sending session ticket of length %d", tlen ) ); + + memcpy( p, ssl->session_negotiate->ticket, tlen ); + + *olen += tlen; +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_SSL_ALPN) +static void ssl_write_alpn_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + unsigned char *p = buf; + const char **cur; + + if( ssl->alpn_list == NULL ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "client hello, adding alpn extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + + /* Skip writing extension and list length for now */ + p += 4; + + for( cur = ssl->alpn_list; *cur != NULL; cur++ ) + { + *p = (unsigned char)( strlen( *cur ) & 0xFF ); + memcpy( p + 1, *cur, *p ); + p += 1 + *p; + } + + *olen = p - buf; + + /* List length = olen - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */ + buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF ); + buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF ); + + /* Extension length = olen - 2 (ext_type) - 2 (ext_len) */ + buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF ); + buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF ); +} +#endif /* POLARSSL_SSL_ALPN */ + +static int ssl_write_client_hello( ssl_context *ssl ) +{ + int ret; + size_t i, n, olen, ext_len = 0; + unsigned char *buf; + unsigned char *p, *q; +#if defined(POLARSSL_HAVE_TIME) + time_t t; +#endif + const int *ciphersuites; + const ssl_ciphersuite_t *ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write client hello" ) ); + + if( ssl->f_rng == NULL ) + { + SSL_DEBUG_MSG( 1, ( "no RNG provided") ); + return( POLARSSL_ERR_SSL_NO_RNG ); + } + + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) + { + ssl->major_ver = ssl->min_major_ver; + ssl->minor_ver = ssl->min_minor_ver; + } + + if( ssl->max_major_ver == 0 && ssl->max_minor_ver == 0 ) + { + ssl->max_major_ver = SSL_MAX_MAJOR_VERSION; + ssl->max_minor_ver = SSL_MAX_MINOR_VERSION; + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 highest version supported + * 6 . 9 current UNIX time + * 10 . 37 random bytes + */ + buf = ssl->out_msg; + p = buf + 4; + + *p++ = (unsigned char) ssl->max_major_ver; + *p++ = (unsigned char) ssl->max_minor_ver; + + SSL_DEBUG_MSG( 3, ( "client hello, max version: [%d:%d]", + buf[4], buf[5] ) ); + +#if defined(POLARSSL_HAVE_TIME) + t = time( NULL ); + *p++ = (unsigned char)( t >> 24 ); + *p++ = (unsigned char)( t >> 16 ); + *p++ = (unsigned char)( t >> 8 ); + *p++ = (unsigned char)( t ); + + SSL_DEBUG_MSG( 3, ( "client hello, current time: %lu", t ) ); +#else + if( ( ret = ssl->f_rng( ssl->p_rng, p, 4 ) ) != 0 ) + return( ret ); + + p += 4; +#endif /* POLARSSL_HAVE_TIME */ + + if( ( ret = ssl->f_rng( ssl->p_rng, p, 28 ) ) != 0 ) + return( ret ); + + p += 28; + + memcpy( ssl->handshake->randbytes, buf + 6, 32 ); + + SSL_DEBUG_BUF( 3, "client hello, random bytes", buf + 6, 32 ); + + /* + * 38 . 38 session id length + * 39 . 39+n session id + * 40+n . 41+n ciphersuitelist length + * 42+n . .. ciphersuitelist + * .. . .. compression methods length + * .. . .. compression methods + * .. . .. extensions length + * .. . .. extensions + */ + n = ssl->session_negotiate->length; + + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || n < 16 || n > 32 || + ssl->handshake->resume == 0 ) + { + n = 0; + } + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + /* + * RFC 5077 section 3.4: "When presenting a ticket, the client MAY + * generate and include a Session ID in the TLS ClientHello." + */ + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ssl->session_negotiate->ticket != NULL && + ssl->session_negotiate->ticket_len != 0 ) + { + ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, 32 ); + + if( ret != 0 ) + return( ret ); + + ssl->session_negotiate->length = n = 32; + } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + + *p++ = (unsigned char) n; + + for( i = 0; i < n; i++ ) + *p++ = ssl->session_negotiate->id[i]; + + SSL_DEBUG_MSG( 3, ( "client hello, session id len.: %d", n ) ); + SSL_DEBUG_BUF( 3, "client hello, session id", buf + 39, n ); + + ciphersuites = ssl->ciphersuite_list[ssl->minor_ver]; + n = 0; + q = p; + + // Skip writing ciphersuite length for now + p += 2; + + /* + * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV + */ + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) + { + *p++ = (unsigned char)( SSL_EMPTY_RENEGOTIATION_INFO >> 8 ); + *p++ = (unsigned char)( SSL_EMPTY_RENEGOTIATION_INFO ); + n++; + } + + for( i = 0; ciphersuites[i] != 0; i++ ) + { + ciphersuite_info = ssl_ciphersuite_from_id( ciphersuites[i] ); + + if( ciphersuite_info == NULL ) + continue; + + if( ciphersuite_info->min_minor_ver > ssl->max_minor_ver || + ciphersuite_info->max_minor_ver < ssl->min_minor_ver ) + continue; + + SSL_DEBUG_MSG( 3, ( "client hello, add ciphersuite: %2d", + ciphersuites[i] ) ); + + n++; + *p++ = (unsigned char)( ciphersuites[i] >> 8 ); + *p++ = (unsigned char)( ciphersuites[i] ); + } + + *q++ = (unsigned char)( n >> 7 ); + *q++ = (unsigned char)( n << 1 ); + + SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites", n ) ); + + +#if defined(POLARSSL_ZLIB_SUPPORT) + SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 2 ) ); + SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d %d", + SSL_COMPRESS_DEFLATE, SSL_COMPRESS_NULL ) ); + + *p++ = 2; + *p++ = SSL_COMPRESS_DEFLATE; + *p++ = SSL_COMPRESS_NULL; +#else + SSL_DEBUG_MSG( 3, ( "client hello, compress len.: %d", 1 ) ); + SSL_DEBUG_MSG( 3, ( "client hello, compress alg.: %d", SSL_COMPRESS_NULL ) ); + + *p++ = 1; + *p++ = SSL_COMPRESS_NULL; +#endif /* POLARSSL_ZLIB_SUPPORT */ + + // First write extensions, then the total length + // +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + ssl_write_hostname_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + + ssl_write_renegotiation_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + ssl_write_signature_algorithms_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + ssl_write_supported_elliptic_curves_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; + + ssl_write_supported_point_formats_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + ssl_write_max_fragment_length_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_ALPN) + ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + + SSL_DEBUG_MSG( 3, ( "client hello, total extension length: %d", + ext_len ) ); + + if( ext_len > 0 ) + { + *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ext_len ) & 0xFF ); + p += ext_len; + } + + ssl->out_msglen = p - buf; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_CLIENT_HELLO; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write client hello" ) ); + + return( 0 ); +} + +static int ssl_parse_renegotiation_info( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + int ret; + + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) + { + if( len != 1 || buf[0] != 0x0 ) + { + SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; + } + else + { + /* Check verify-data in constant-time. The length OTOH is no secret */ + if( len != 1 + ssl->verify_data_len * 2 || + buf[0] != ssl->verify_data_len * 2 || + safer_memcmp( buf + 1, + ssl->own_verify_data, ssl->verify_data_len ) != 0 || + safer_memcmp( buf + 1 + ssl->verify_data_len, + ssl->peer_verify_data, ssl->verify_data_len ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + } + + return( 0 ); +} + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +static int ssl_parse_max_fragment_length_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + /* + * server should use the extension only if we did, + * and if so the server's value should match ours (and len is always 1) + */ + if( ssl->mfl_code == SSL_MAX_FRAG_LEN_NONE || + len != 1 || + buf[0] != ssl->mfl_code ) + { + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + return( 0 ); +} +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( ssl->trunc_hmac == SSL_TRUNC_HMAC_DISABLED || + len != 0 ) + { + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ((void) buf); + + ssl->session_negotiate->trunc_hmac = SSL_TRUNC_HMAC_ENABLED; + + return( 0 ); +} +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static int ssl_parse_session_ticket_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED || + len != 0 ) + { + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ((void) buf); + + ssl->handshake->new_session_ticket = 1; + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) +static int ssl_parse_supported_point_formats_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + size_t list_size; + const unsigned char *p; + + list_size = buf[0]; + if( list_size + 1 != len ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + p = buf + 1; + while( list_size > 0 ) + { + if( p[0] == POLARSSL_ECP_PF_UNCOMPRESSED || + p[0] == POLARSSL_ECP_PF_COMPRESSED ) + { + ssl->handshake->ecdh_ctx.point_format = p[0]; + SSL_DEBUG_MSG( 4, ( "point format selected: %d", p[0] ) ); + return( 0 ); + } + + list_size--; + p++; + } + + SSL_DEBUG_MSG( 1, ( "no point format in common" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_ALPN) +static int ssl_parse_alpn_ext( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + size_t list_len, name_len; + const char **p; + + /* If we didn't send it, the server shouldn't send it */ + if( ssl->alpn_list == NULL ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + * + * the "ProtocolNameList" MUST contain exactly one "ProtocolName" + */ + + /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ + if( len < 4 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + list_len = ( buf[0] << 8 ) | buf[1]; + if( list_len != len - 2 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + name_len = buf[2]; + if( name_len != list_len - 1 ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + + /* Check that the server chosen protocol was in our list and save it */ + for( p = ssl->alpn_list; *p != NULL; p++ ) + { + if( name_len == strlen( *p ) && + memcmp( buf + 3, *p, name_len ) == 0 ) + { + ssl->alpn_chosen = *p; + return( 0 ); + } + } + + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); +} +#endif /* POLARSSL_SSL_ALPN */ + +static int ssl_parse_server_hello( ssl_context *ssl ) +{ + int ret, i, comp; + size_t n; + size_t ext_len = 0; + unsigned char *buf, *ext; + int renegotiation_info_seen = 0; + int handshake_failure = 0; +#if defined(POLARSSL_DEBUG_C) + uint32_t t; +#endif + + SSL_DEBUG_MSG( 2, ( "=> parse server hello" ) ); + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 protocol version + * 6 . 9 UNIX time() + * 10 . 37 random bytes + */ + buf = ssl->in_msg; + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + SSL_DEBUG_MSG( 3, ( "server hello, chosen version: [%d:%d]", + buf[4], buf[5] ) ); + + if( ssl->in_hslen < 42 || + buf[0] != SSL_HS_SERVER_HELLO || + buf[4] != SSL_MAJOR_VERSION_3 ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + if( buf[5] > ssl->max_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + ssl->minor_ver = buf[5]; + + if( ssl->minor_ver < ssl->min_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "server only supports ssl smaller than minimum" + " [%d:%d] < [%d:%d]", ssl->major_ver, + ssl->minor_ver, buf[4], buf[5] ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_PROTOCOL_VERSION ); + + return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); + } + +#if defined(POLARSSL_DEBUG_C) + t = ( (uint32_t) buf[6] << 24 ) + | ( (uint32_t) buf[7] << 16 ) + | ( (uint32_t) buf[8] << 8 ) + | ( (uint32_t) buf[9] ); + SSL_DEBUG_MSG( 3, ( "server hello, current time: %lu", t ) ); +#endif + + memcpy( ssl->handshake->randbytes + 32, buf + 6, 32 ); + + n = buf[38]; + + SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 ); + + if( n > 32 ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + /* + * 38 . 38 session id length + * 39 . 38+n session id + * 39+n . 40+n chosen ciphersuite + * 41+n . 41+n chosen compression alg. + * 42+n . 43+n extensions length + * 44+n . 44+n+m extensions + */ + if( ssl->in_hslen > 42 + n ) + { + ext_len = ( ( buf[42 + n] << 8 ) + | ( buf[43 + n] ) ); + + if( ( ext_len > 0 && ext_len < 4 ) || + ssl->in_hslen != 44 + n + ext_len ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + } + + i = ( buf[39 + n] << 8 ) | buf[40 + n]; + comp = buf[41 + n]; + + /* + * Initialize update checksum functions + */ + ssl->transform_negotiate->ciphersuite_info = ssl_ciphersuite_from_id( i ); + + if( ssl->transform_negotiate->ciphersuite_info == NULL ) + { + SSL_DEBUG_MSG( 1, ( "ciphersuite info for %04x not found", i ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + ssl_optimize_checksum( ssl, ssl->transform_negotiate->ciphersuite_info ); + + SSL_DEBUG_MSG( 3, ( "server hello, session id len.: %d", n ) ); + SSL_DEBUG_BUF( 3, "server hello, session id", buf + 39, n ); + + /* + * Check if the session can be resumed + */ + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE || + ssl->handshake->resume == 0 || n == 0 || + ssl->session_negotiate->ciphersuite != i || + ssl->session_negotiate->compression != comp || + ssl->session_negotiate->length != n || + memcmp( ssl->session_negotiate->id, buf + 39, n ) != 0 ) + { + ssl->state++; + ssl->handshake->resume = 0; +#if defined(POLARSSL_HAVE_TIME) + ssl->session_negotiate->start = time( NULL ); +#endif + ssl->session_negotiate->ciphersuite = i; + ssl->session_negotiate->compression = comp; + ssl->session_negotiate->length = n; + memcpy( ssl->session_negotiate->id, buf + 39, n ); + } + else + { + ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; + + if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); + return( ret ); + } + } + + SSL_DEBUG_MSG( 3, ( "%s session has been resumed", + ssl->handshake->resume ? "a" : "no" ) ); + + SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %d", i ) ); + SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: %d", buf[41 + n] ) ); + + i = 0; + while( 1 ) + { + if( ssl->ciphersuite_list[ssl->minor_ver][i] == 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + if( ssl->ciphersuite_list[ssl->minor_ver][i++] == + ssl->session_negotiate->ciphersuite ) + { + break; + } + } + + if( comp != SSL_COMPRESS_NULL +#if defined(POLARSSL_ZLIB_SUPPORT) + && comp != SSL_COMPRESS_DEFLATE +#endif + ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + ssl->session_negotiate->compression = comp; + + ext = buf + 44 + n; + + SSL_DEBUG_MSG( 2, ( "server hello, total extension length: %d", ext_len ) ); + + while( ext_len ) + { + unsigned int ext_id = ( ( ext[0] << 8 ) + | ( ext[1] ) ); + unsigned int ext_size = ( ( ext[2] << 8 ) + | ( ext[3] ) ); + + if( ext_size + 4 > ext_len ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + switch( ext_id ) + { + case TLS_EXT_RENEGOTIATION_INFO: + SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) ); + renegotiation_info_seen = 1; + + if( ( ret = ssl_parse_renegotiation_info( ssl, ext + 4, + ext_size ) ) != 0 ) + return( ret ); + + break; + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + case TLS_EXT_MAX_FRAGMENT_LENGTH: + SSL_DEBUG_MSG( 3, ( "found max_fragment_length extension" ) ); + + if( ( ret = ssl_parse_max_fragment_length_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + case TLS_EXT_TRUNCATED_HMAC: + SSL_DEBUG_MSG( 3, ( "found truncated_hmac extension" ) ); + + if( ( ret = ssl_parse_truncated_hmac_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + case TLS_EXT_SESSION_TICKET: + SSL_DEBUG_MSG( 3, ( "found session_ticket extension" ) ); + + if( ( ret = ssl_parse_session_ticket_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + case TLS_EXT_SUPPORTED_POINT_FORMATS: + SSL_DEBUG_MSG( 3, ( "found supported_point_formats extension" ) ); + + if( ( ret = ssl_parse_supported_point_formats_ext( ssl, + ext + 4, ext_size ) ) != 0 ) + { + return( ret ); + } + + break; +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_ALPN) + case TLS_EXT_ALPN: + SSL_DEBUG_MSG( 3, ( "found alpn extension" ) ); + + if( ( ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ) ) != 0 ) + return( ret ); + + break; +#endif /* POLARSSL_SSL_ALPN */ + + default: + SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", + ext_id ) ); + } + + ext_len -= 4 + ext_size; + ext += 4 + ext_size; + + if( ext_len > 0 && ext_len < 4 ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + } + + /* + * Renegotiation security checks + */ + if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); + handshake_failure = 1; + } + + if( handshake_failure == 1 ) + { + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO ); + } + + SSL_DEBUG_MSG( 2, ( "<= parse server hello" ) ); + + return( 0 ); +} + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) +static int ssl_parse_server_dh_params( ssl_context *ssl, unsigned char **p, + unsigned char *end ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + + /* + * Ephemeral DH parameters: + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + if( ( ret = dhm_read_params( &ssl->handshake->dhm_ctx, p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 2, ( "dhm_read_params" ), ret ); + return( ret ); + } + + if( ssl->handshake->dhm_ctx.len < 64 || + ssl->handshake->dhm_ctx.len > 512 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message (DHM length)" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + SSL_DEBUG_MPI( 3, "DHM: P ", &ssl->handshake->dhm_ctx.P ); + SSL_DEBUG_MPI( 3, "DHM: G ", &ssl->handshake->dhm_ctx.G ); + SSL_DEBUG_MPI( 3, "DHM: GY", &ssl->handshake->dhm_ctx.GY ); + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +static int ssl_check_server_ecdh_params( const ssl_context *ssl ) +{ + const ecp_curve_info *curve_info; + + curve_info = ecp_curve_info_from_grp_id( ssl->handshake->ecdh_ctx.grp.id ); + if( curve_info == NULL ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_MSG( 2, ( "ECDH curve: %s", curve_info->name ) ); + +#if defined(POLARSSL_SSL_ECP_SET_CURVES) + if( ! ssl_curve_is_acceptable( ssl, ssl->handshake->ecdh_ctx.grp.id ) ) +#else + if( ssl->handshake->ecdh_ctx.grp.nbits < 163 || + ssl->handshake->ecdh_ctx.grp.nbits > 521 ) +#endif + return( -1 ); + + SSL_DEBUG_ECP( 3, "ECDH: Qp", &ssl->handshake->ecdh_ctx.Qp ); + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +static int ssl_parse_server_ecdh_params( ssl_context *ssl, + unsigned char **p, + unsigned char *end ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + + /* + * Ephemeral ECDH parameters: + * + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ + if( ( ret = ecdh_read_params( &ssl->handshake->ecdh_ctx, + (const unsigned char **) p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ecdh_read_params" ), ret ); + return( ret ); + } + + if( ssl_check_server_ecdh_params( ssl ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message (ECDHE curve)" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +static int ssl_parse_server_psk_hint( ssl_context *ssl, + unsigned char **p, + unsigned char *end ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + size_t len; + ((void) ssl); + + /* + * PSK parameters: + * + * opaque psk_identity_hint<0..2^16-1>; + */ + len = (*p)[0] << 8 | (*p)[1]; + *p += 2; + + if( (*p) + len > end ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message (psk_identity_hint length)" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + // TODO: Retrieve PSK identity hint and callback to app + // + *p += len; + ret = 0; + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) +/* + * Generate a pre-master secret and encrypt it with the server's RSA key + */ +static int ssl_write_encrypted_pms( ssl_context *ssl, + size_t offset, size_t *olen, + size_t pms_offset ) +{ + int ret; + size_t len_bytes = ssl->minor_ver == SSL_MINOR_VERSION_0 ? 0 : 2; + unsigned char *p = ssl->handshake->premaster + pms_offset; + + /* + * Generate (part of) the pre-master as + * struct { + * ProtocolVersion client_version; + * opaque random[46]; + * } PreMasterSecret; + */ + p[0] = (unsigned char) ssl->max_major_ver; + p[1] = (unsigned char) ssl->max_minor_ver; + + if( ( ret = ssl->f_rng( ssl->p_rng, p + 2, 46 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "f_rng", ret ); + return( ret ); + } + + ssl->handshake->pmslen = 48; + + /* + * Now write it out, encrypted + */ + if( ! pk_can_do( &ssl->session_negotiate->peer_cert->pk, + POLARSSL_PK_RSA ) ) + { + SSL_DEBUG_MSG( 1, ( "certificate key type mismatch" ) ); + return( POLARSSL_ERR_SSL_PK_TYPE_MISMATCH ); + } + + if( ( ret = pk_encrypt( &ssl->session_negotiate->peer_cert->pk, + p, ssl->handshake->pmslen, + ssl->out_msg + offset + len_bytes, olen, + SSL_MAX_CONTENT_LEN - offset - len_bytes, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "rsa_pkcs1_encrypt", ret ); + return( ret ); + } + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( len_bytes == 2 ) + { + ssl->out_msg[offset+0] = (unsigned char)( *olen >> 8 ); + ssl->out_msg[offset+1] = (unsigned char)( *olen ); + *olen += 2; + } +#endif + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +static int ssl_parse_signature_algorithm( ssl_context *ssl, + unsigned char **p, + unsigned char *end, + md_type_t *md_alg, + pk_type_t *pk_alg ) +{ + ((void) ssl); + *md_alg = POLARSSL_MD_NONE; + *pk_alg = POLARSSL_PK_NONE; + + /* Only in TLS 1.2 */ + if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) + { + return( 0 ); + } + + if( (*p) + 2 > end ) + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + + /* + * Get hash algorithm + */ + if( ( *md_alg = ssl_md_alg_from_hash( (*p)[0] ) ) == POLARSSL_MD_NONE ) + { + SSL_DEBUG_MSG( 2, ( "Server used unsupported " + "HashAlgorithm %d", *(p)[0] ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + /* + * Get signature algorithm + */ + if( ( *pk_alg = ssl_pk_alg_from_sig( (*p)[1] ) ) == POLARSSL_PK_NONE ) + { + SSL_DEBUG_MSG( 2, ( "server used unsupported " + "SignatureAlgorithm %d", (*p)[1] ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + SSL_DEBUG_MSG( 2, ( "Server used SignatureAlgorithm %d", (*p)[1] ) ); + SSL_DEBUG_MSG( 2, ( "Server used HashAlgorithm %d", (*p)[0] ) ); + *p += 2; + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +static int ssl_get_ecdh_params_from_cert( ssl_context *ssl ) +{ + int ret; + const ecp_keypair *peer_key; + + if( ! pk_can_do( &ssl->session_negotiate->peer_cert->pk, + POLARSSL_PK_ECKEY ) ) + { + SSL_DEBUG_MSG( 1, ( "server key not ECDH capable" ) ); + return( POLARSSL_ERR_SSL_PK_TYPE_MISMATCH ); + } + + peer_key = pk_ec( ssl->session_negotiate->peer_cert->pk ); + + if( ( ret = ecdh_get_params( &ssl->handshake->ecdh_ctx, peer_key, + POLARSSL_ECDH_THEIRS ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ecdh_get_params" ), ret ); + return( ret ); + } + + if( ssl_check_server_ecdh_params( ssl ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server certificate (ECDH curve)" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +static int ssl_parse_server_key_exchange( ssl_context *ssl ) +{ + int ret; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + unsigned char *p, *end; +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + size_t sig_len, params_len; + unsigned char hash[64]; + md_type_t md_alg = POLARSSL_MD_NONE; + size_t hashlen; + pk_type_t pk_alg = POLARSSL_PK_NONE; +#endif + + SSL_DEBUG_MSG( 2, ( "=> parse server key exchange" ) ); + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse server key exchange" ) ); + ssl->state++; + return( 0 ); + } + ((void) p); + ((void) end); +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_ECDSA ) + { + if( ( ret = ssl_get_ecdh_params_from_cert( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_get_ecdh_params_from_cert", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= skip parse server key exchange" ) ); + ssl->state++; + return( 0 ); + } + ((void) p); + ((void) end); +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + /* + * ServerKeyExchange may be skipped with PSK and RSA-PSK when the server + * doesn't use a psk_identity_hint + */ + if( ssl->in_msg[0] != SSL_HS_SERVER_KEY_EXCHANGE ) + { + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + { + ssl->record_read = 1; + goto exit; + } + + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + p = ssl->in_msg + 4; + end = ssl->in_msg + ssl->in_hslen; + SSL_DEBUG_BUF( 3, "server key exchange", p, ssl->in_hslen - 4 ); + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + if( ssl_parse_server_psk_hint( ssl, &p, end ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + } /* FALLTROUGH */ +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + ; /* nothing more to do */ + else +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED || + POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + if( ssl_parse_server_dh_params( ssl, &p, end ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA ) + { + if( ssl_parse_server_ecdh_params( ssl, &p, end ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA ) + { + params_len = p - ( ssl->in_msg + 4 ); + + /* + * Handle the digitally-signed structure + */ +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + if( ssl_parse_signature_algorithm( ssl, &p, end, + &md_alg, &pk_alg ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + if( pk_alg != ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info ) ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ssl->minor_ver < SSL_MINOR_VERSION_3 ) + { + pk_alg = ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info ); + + /* Default hash for ECDSA is SHA-1 */ + if( pk_alg == POLARSSL_PK_ECDSA && md_alg == POLARSSL_MD_NONE ) + md_alg = POLARSSL_MD_SHA1; + } + else +#endif + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + /* + * Read signature + */ + sig_len = ( p[0] << 8 ) | p[1]; + p += 2; + + if( end != p + sig_len ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE ); + } + + SSL_DEBUG_BUF( 3, "signature", p, sig_len ); + + /* + * Compute the hash that has been signed + */ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( md_alg == POLARSSL_MD_NONE ) + { + md5_context md5; + sha1_context sha1; + + md5_init( &md5 ); + sha1_init( &sha1 ); + + hashlen = 36; + + /* + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * md5_hash + * MD5(ClientHello.random + ServerHello.random + * + ServerParams); + * sha_hash + * SHA(ClientHello.random + ServerHello.random + * + ServerParams); + */ + md5_starts( &md5 ); + md5_update( &md5, ssl->handshake->randbytes, 64 ); + md5_update( &md5, ssl->in_msg + 4, params_len ); + md5_finish( &md5, hash ); + + sha1_starts( &sha1 ); + sha1_update( &sha1, ssl->handshake->randbytes, 64 ); + sha1_update( &sha1, ssl->in_msg + 4, params_len ); + sha1_finish( &sha1, hash + 16 ); + + md5_free( &md5 ); + sha1_free( &sha1 ); + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 || POLARSSL_SSL_PROTO_TLS1 || \ + POLARSSL_SSL_PROTO_TLS1_1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( md_alg != POLARSSL_MD_NONE ) + { + md_context_t ctx; + + md_init( &ctx ); + + /* Info from md_alg will be used instead */ + hashlen = 0; + + /* + * digitally-signed struct { + * opaque client_random[32]; + * opaque server_random[32]; + * ServerDHParams params; + * }; + */ + if( ( ret = md_init_ctx( &ctx, + md_info_from_type( md_alg ) ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "md_init_ctx", ret ); + return( ret ); + } + + md_starts( &ctx ); + md_update( &ctx, ssl->handshake->randbytes, 64 ); + md_update( &ctx, ssl->in_msg + 4, params_len ); + md_finish( &ctx, hash ); + md_free( &ctx ); + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || \ + POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen != 0 ? hashlen : + (unsigned int) ( md_info_from_type( md_alg ) )->size ); + + /* + * Verify signature + */ + if( ! pk_can_do( &ssl->session_negotiate->peer_cert->pk, pk_alg ) ) + { + SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) ); + return( POLARSSL_ERR_SSL_PK_TYPE_MISMATCH ); + } + + if( ( ret = pk_verify( &ssl->session_negotiate->peer_cert->pk, + md_alg, hash, hashlen, p, sig_len ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "pk_verify", ret ); + return( ret ); + } + } +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +exit: + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse server key exchange" ) ); + + return( 0 ); +} + +#if !defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +static int ssl_parse_certificate_request( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate request" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate request" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} +#else +static int ssl_parse_certificate_request( ssl_context *ssl ) +{ + int ret; + unsigned char *buf, *p; + size_t n = 0, m = 0; + size_t cert_type_len = 0, dn_len = 0; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate request" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate request" ) ); + ssl->state++; + return( 0 ); + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 4 cert type count + * 5 .. m-1 cert types + * m .. m+1 sig alg length (TLS 1.2 only) + * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) + * n .. n+1 length of all DNs + * n+2 .. n+3 length of DN 1 + * n+4 .. ... Distinguished Name #1 + * ... .. ... length of DN 2, etc. + */ + if( ssl->record_read == 0 ) + { + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + ssl->record_read = 1; + } + + ssl->client_auth = 0; + ssl->state++; + + if( ssl->in_msg[0] == SSL_HS_CERTIFICATE_REQUEST ) + ssl->client_auth++; + + SSL_DEBUG_MSG( 3, ( "got %s certificate request", + ssl->client_auth ? "a" : "no" ) ); + + if( ssl->client_auth == 0 ) + goto exit; + + ssl->record_read = 0; + + // TODO: handshake_failure alert for an anonymous server to request + // client authentication + + buf = ssl->in_msg; + + // Retrieve cert types + // + cert_type_len = buf[4]; + n = cert_type_len; + + if( ssl->in_hslen < 6 + n ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); + } + + p = buf + 5; + while( cert_type_len > 0 ) + { +#if defined(POLARSSL_RSA_C) + if( *p == SSL_CERT_TYPE_RSA_SIGN && + pk_can_do( ssl_own_key( ssl ), POLARSSL_PK_RSA ) ) + { + ssl->handshake->cert_type = SSL_CERT_TYPE_RSA_SIGN; + break; + } + else +#endif +#if defined(POLARSSL_ECDSA_C) + if( *p == SSL_CERT_TYPE_ECDSA_SIGN && + pk_can_do( ssl_own_key( ssl ), POLARSSL_PK_ECDSA ) ) + { + ssl->handshake->cert_type = SSL_CERT_TYPE_ECDSA_SIGN; + break; + } + else +#endif + { + ; /* Unsupported cert type, ignore */ + } + + cert_type_len--; + p++; + } + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + /* Ignored, see comments about hash in write_certificate_verify */ + // TODO: should check the signature part against our pk_key though + size_t sig_alg_len = ( ( buf[5 + n] << 8 ) + | ( buf[6 + n] ) ); + + p = buf + 7 + n; + m += 2; + n += sig_alg_len; + + if( ssl->in_hslen < 6 + n ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); + } + } +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + /* Ignore certificate_authorities, we only have one cert anyway */ + // TODO: should not send cert if no CA matches + dn_len = ( ( buf[5 + m + n] << 8 ) + | ( buf[6 + m + n] ) ); + + n += dn_len; + if( ssl->in_hslen != 7 + m + n ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate request message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_REQUEST ); + } + +exit: + SSL_DEBUG_MSG( 2, ( "<= parse certificate request" ) ); + + return( 0 ); +} +#endif /* !POLARSSL_KEY_EXCHANGE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +static int ssl_parse_server_hello_done( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> parse server hello done" ) ); + + if( ssl->record_read == 0 ) + { + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + } + ssl->record_read = 0; + + if( ssl->in_hslen != 4 || + ssl->in_msg[0] != SSL_HS_SERVER_HELLO_DONE ) + { + SSL_DEBUG_MSG( 1, ( "bad server hello done message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_SERVER_HELLO_DONE ); + } + + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse server hello done" ) ); + + return( 0 ); +} + +static int ssl_write_client_key_exchange( ssl_context *ssl ) +{ + int ret; + size_t i, n; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write client key exchange" ) ); + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA ) + { + /* + * DHM key exchange -- send G^X mod P + */ + n = ssl->handshake->dhm_ctx.len; + + ssl->out_msg[4] = (unsigned char)( n >> 8 ); + ssl->out_msg[5] = (unsigned char)( n ); + i = 6; + + ret = dhm_make_public( &ssl->handshake->dhm_ctx, + (int) mpi_size( &ssl->handshake->dhm_ctx.P ), + &ssl->out_msg[i], n, + ssl->f_rng, ssl->p_rng ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_make_public", ret ); + return( ret ); + } + + SSL_DEBUG_MPI( 3, "DHM: X ", &ssl->handshake->dhm_ctx.X ); + SSL_DEBUG_MPI( 3, "DHM: GX", &ssl->handshake->dhm_ctx.GX ); + + ssl->handshake->pmslen = POLARSSL_PREMASTER_SIZE; + + if( ( ret = dhm_calc_secret( &ssl->handshake->dhm_ctx, + ssl->handshake->premaster, + &ssl->handshake->pmslen, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_calc_secret", ret ); + return( ret ); + } + + SSL_DEBUG_MPI( 3, "DHM: K ", &ssl->handshake->dhm_ctx.K ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_ECDSA ) + { + /* + * ECDH key exchange -- send client public value + */ + i = 4; + + ret = ecdh_make_public( &ssl->handshake->ecdh_ctx, + &n, + &ssl->out_msg[i], 1000, + ssl->f_rng, ssl->p_rng ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_make_public", ret ); + return( ret ); + } + + SSL_DEBUG_ECP( 3, "ECDH: Q", &ssl->handshake->ecdh_ctx.Q ); + + if( ( ret = ecdh_calc_secret( &ssl->handshake->ecdh_ctx, + &ssl->handshake->pmslen, + ssl->handshake->premaster, + POLARSSL_MPI_MAX_SIZE, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_calc_secret", ret ); + return( ret ); + } + + SSL_DEBUG_MPI( 3, "ECDH: z", &ssl->handshake->ecdh_ctx.z ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + /* + * opaque psk_identity<0..2^16-1>; + */ + if( ssl->psk == NULL || ssl->psk_identity == NULL ) + return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); + + i = 4; + n = ssl->psk_identity_len; + ssl->out_msg[i++] = (unsigned char)( n >> 8 ); + ssl->out_msg[i++] = (unsigned char)( n ); + + memcpy( ssl->out_msg + i, ssl->psk_identity, ssl->psk_identity_len ); + i += ssl->psk_identity_len; + +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK ) + { + n = 0; + } + else +#endif +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + { + if( ( ret = ssl_write_encrypted_pms( ssl, i, &n, 2 ) ) != 0 ) + return( ret ); + } + else +#endif +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + /* + * ClientDiffieHellmanPublic public (DHM send G^X mod P) + */ + n = ssl->handshake->dhm_ctx.len; + ssl->out_msg[i++] = (unsigned char)( n >> 8 ); + ssl->out_msg[i++] = (unsigned char)( n ); + + ret = dhm_make_public( &ssl->handshake->dhm_ctx, + (int) mpi_size( &ssl->handshake->dhm_ctx.P ), + &ssl->out_msg[i], n, + ssl->f_rng, ssl->p_rng ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_make_public", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + /* + * ClientECDiffieHellmanPublic public; + */ + ret = ecdh_make_public( &ssl->handshake->ecdh_ctx, &n, + &ssl->out_msg[i], SSL_MAX_CONTENT_LEN - i, + ssl->f_rng, ssl->p_rng ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_make_public", ret ); + return( ret ); + } + + SSL_DEBUG_ECP( 3, "ECDH: Q", &ssl->handshake->ecdh_ctx.Q ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + if( ( ret = ssl_psk_derive_premaster( ssl, + ciphersuite_info->key_exchange ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_psk_derive_premaster", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA ) + { + i = 4; + if( ( ret = ssl_write_encrypted_pms( ssl, i, &n, 0 ) ) != 0 ) + return( ret ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ + { + ((void) ciphersuite_info); + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); + return( ret ); + } + + ssl->out_msglen = i + n; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_CLIENT_KEY_EXCHANGE; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write client key exchange" ) ); + + return( 0 ); +} + +#if !defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +static int ssl_write_certificate_verify( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write certificate verify" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} +#else +static int ssl_write_certificate_verify( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + size_t n = 0, offset = 0; + unsigned char hash[48]; + unsigned char *hash_start = hash; + md_type_t md_alg = POLARSSL_MD_NONE; + unsigned int hashlen; + + SSL_DEBUG_MSG( 2, ( "=> write certificate verify" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + if( ssl->client_auth == 0 || ssl_own_cert( ssl ) == NULL ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + if( ssl_own_key( ssl ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "got no private key" ) ); + return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + + /* + * Make an RSA signature of the handshake digests + */ + ssl->handshake->calc_verify( ssl, hash ); + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) + { + /* + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * md5_hash + * MD5(handshake_messages); + * + * sha_hash + * SHA(handshake_messages); + */ + hashlen = 36; + md_alg = POLARSSL_MD_NONE; + + /* + * For ECDSA, default hash is SHA-1 only + */ + if( pk_can_do( ssl_own_key( ssl ), POLARSSL_PK_ECDSA ) ) + { + hash_start += 16; + hashlen -= 16; + md_alg = POLARSSL_MD_SHA1; + } + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 || POLARSSL_SSL_PROTO_TLS1 || \ + POLARSSL_SSL_PROTO_TLS1_1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + /* + * digitally-signed struct { + * opaque handshake_messages[handshake_messages_length]; + * }; + * + * Taking shortcut here. We assume that the server always allows the + * PRF Hash function and has sent it in the allowed signature + * algorithms list received in the Certificate Request message. + * + * Until we encounter a server that does not, we will take this + * shortcut. + * + * Reason: Otherwise we should have running hashes for SHA512 and SHA224 + * in order to satisfy 'weird' needs from the server side. + */ + if( ssl->transform_negotiate->ciphersuite_info->mac == + POLARSSL_MD_SHA384 ) + { + md_alg = POLARSSL_MD_SHA384; + ssl->out_msg[4] = SSL_HASH_SHA384; + } + else + { + md_alg = POLARSSL_MD_SHA256; + ssl->out_msg[4] = SSL_HASH_SHA256; + } + ssl->out_msg[5] = ssl_sig_from_pk( ssl_own_key( ssl ) ); + + /* Info from md_alg will be used instead */ + hashlen = 0; + offset = 2; + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + if( ( ret = pk_sign( ssl_own_key( ssl ), md_alg, hash_start, hashlen, + ssl->out_msg + 6 + offset, &n, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "pk_sign", ret ); + return( ret ); + } + + ssl->out_msg[4 + offset] = (unsigned char)( n >> 8 ); + ssl->out_msg[5 + offset] = (unsigned char)( n ); + + ssl->out_msglen = 6 + n + offset; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_CERTIFICATE_VERIFY; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write certificate verify" ) ); + + return( ret ); +} +#endif /* !POLARSSL_KEY_EXCHANGE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static int ssl_parse_new_session_ticket( ssl_context *ssl ) +{ + int ret; + uint32_t lifetime; + size_t ticket_len; + unsigned char *ticket; + + SSL_DEBUG_MSG( 2, ( "=> parse new session ticket" ) ); + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 0 . 0 handshake message type + * 1 . 3 handshake message length + * 4 . 7 ticket_lifetime_hint + * 8 . 9 ticket_len (n) + * 10 . 9+n ticket content + */ + if( ssl->in_msg[0] != SSL_HS_NEW_SESSION_TICKET || + ssl->in_hslen < 10 ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET ); + } + + lifetime = ( ssl->in_msg[4] << 24 ) | ( ssl->in_msg[5] << 16 ) | + ( ssl->in_msg[6] << 8 ) | ( ssl->in_msg[7] ); + + ticket_len = ( ssl->in_msg[8] << 8 ) | ( ssl->in_msg[9] ); + + if( ticket_len + 10 != ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad new session ticket message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_NEW_SESSION_TICKET ); + } + + SSL_DEBUG_MSG( 3, ( "ticket length: %d", ticket_len ) ); + + /* We're not waiting for a NewSessionTicket message any more */ + ssl->handshake->new_session_ticket = 0; + + /* + * Zero-length ticket means the server changed his mind and doesn't want + * to send a ticket after all, so just forget it + */ + if( ticket_len == 0 ) + return( 0 ); + + polarssl_zeroize( ssl->session_negotiate->ticket, + ssl->session_negotiate->ticket_len ); + polarssl_free( ssl->session_negotiate->ticket ); + ssl->session_negotiate->ticket = NULL; + ssl->session_negotiate->ticket_len = 0; + + if( ( ticket = polarssl_malloc( ticket_len ) ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "ticket malloc failed" ) ); + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + memcpy( ticket, ssl->in_msg + 10, ticket_len ); + + ssl->session_negotiate->ticket = ticket; + ssl->session_negotiate->ticket_len = ticket_len; + ssl->session_negotiate->ticket_lifetime = lifetime; + + /* + * RFC 5077 section 3.4: + * "If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello." + */ + SSL_DEBUG_MSG( 3, ( "ticket in use, discarding session id" ) ); + ssl->session_negotiate->length = 0; + + SSL_DEBUG_MSG( 2, ( "<= parse new session ticket" ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +/* + * SSL handshake -- client side -- single step + */ +int ssl_handshake_client_step( ssl_context *ssl ) +{ + int ret = 0; + + if( ssl->state == SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) ); + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + return( ret ); + + switch( ssl->state ) + { + case SSL_HELLO_REQUEST: + ssl->state = SSL_CLIENT_HELLO; + break; + + /* + * ==> ClientHello + */ + case SSL_CLIENT_HELLO: + ret = ssl_write_client_hello( ssl ); + break; + + /* + * <== ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case SSL_SERVER_HELLO: + ret = ssl_parse_server_hello( ssl ); + break; + + case SSL_SERVER_CERTIFICATE: + ret = ssl_parse_certificate( ssl ); + break; + + case SSL_SERVER_KEY_EXCHANGE: + ret = ssl_parse_server_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_REQUEST: + ret = ssl_parse_certificate_request( ssl ); + break; + + case SSL_SERVER_HELLO_DONE: + ret = ssl_parse_server_hello_done( ssl ); + break; + + /* + * ==> ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case SSL_CLIENT_CERTIFICATE: + ret = ssl_write_certificate( ssl ); + break; + + case SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_write_client_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_VERIFY: + ret = ssl_write_certificate_verify( ssl ); + break; + + case SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = ssl_write_change_cipher_spec( ssl ); + break; + + case SSL_CLIENT_FINISHED: + ret = ssl_write_finished( ssl ); + break; + + /* + * <== ( NewSessionTicket ) + * ChangeCipherSpec + * Finished + */ + case SSL_SERVER_CHANGE_CIPHER_SPEC: +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( ssl->handshake->new_session_ticket != 0 ) + ret = ssl_parse_new_session_ticket( ssl ); + else +#endif + ret = ssl_parse_change_cipher_spec( ssl ); + break; + + case SSL_SERVER_FINISHED: + ret = ssl_parse_finished( ssl ); + break; + + case SSL_FLUSH_BUFFERS: + SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); + ssl->state = SSL_HANDSHAKE_WRAPUP; + break; + + case SSL_HANDSHAKE_WRAPUP: + ssl_handshake_wrapup( ssl ); + break; + + default: + SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + return( ret ); +} +#endif /* POLARSSL_SSL_CLI_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ssl_srv.c b/component/common/network/ssl/polarssl-1.3.8/library/ssl_srv.c new file mode 100644 index 0000000..25be988 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ssl_srv.c @@ -0,0 +1,3269 @@ +/* + * SSLv3/TLSv1 server-side functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SSL_SRV_C) + +#include "polarssl/debug.h" +#include "polarssl/ssl.h" +#if defined(POLARSSL_ECP_C) +#include "polarssl/ecp.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include + +#if defined(POLARSSL_HAVE_TIME) +#include +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Serialize a session in the following format: + * 0 . n-1 session structure, n = sizeof(ssl_session) + * n . n+2 peer_cert length = m (0 if no certificate) + * n+3 . n+2+m peer cert ASN.1 + * + * Assumes ticket is NULL (always true on server side). + */ +static int ssl_save_session( const ssl_session *session, + unsigned char *buf, size_t buf_len, + size_t *olen ) +{ + unsigned char *p = buf; + size_t left = buf_len; +#if defined(POLARSSL_X509_CRT_PARSE_C) + size_t cert_len; +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + if( left < sizeof( ssl_session ) ) + return( -1 ); + + memcpy( p, session, sizeof( ssl_session ) ); + p += sizeof( ssl_session ); + left -= sizeof( ssl_session ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + if( session->peer_cert == NULL ) + cert_len = 0; + else + cert_len = session->peer_cert->raw.len; + + if( left < 3 + cert_len ) + return( -1 ); + + *p++ = (unsigned char)( cert_len >> 16 & 0xFF ); + *p++ = (unsigned char)( cert_len >> 8 & 0xFF ); + *p++ = (unsigned char)( cert_len & 0xFF ); + + if( session->peer_cert != NULL ) + memcpy( p, session->peer_cert->raw.p, cert_len ); + + p += cert_len; +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + *olen = p - buf; + + return( 0 ); +} + +/* + * Unserialise session, see ssl_save_session() + */ +static int ssl_load_session( ssl_session *session, + const unsigned char *buf, size_t len ) +{ + const unsigned char *p = buf; + const unsigned char * const end = buf + len; +#if defined(POLARSSL_X509_CRT_PARSE_C) + size_t cert_len; +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + if( p + sizeof( ssl_session ) > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + memcpy( session, p, sizeof( ssl_session ) ); + p += sizeof( ssl_session ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + if( p + 3 > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + cert_len = ( p[0] << 16 ) | ( p[1] << 8 ) | p[2]; + p += 3; + + if( cert_len == 0 ) + { + session->peer_cert = NULL; + } + else + { + int ret; + + if( p + cert_len > end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + session->peer_cert = polarssl_malloc( sizeof( x509_crt ) ); + + if( session->peer_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + x509_crt_init( session->peer_cert ); + + if( ( ret = x509_crt_parse_der( session->peer_cert, + p, cert_len ) ) != 0 ) + { + x509_crt_free( session->peer_cert ); + polarssl_free( session->peer_cert ); + session->peer_cert = NULL; + return( ret ); + } + + p += cert_len; + } +#endif /* POLARSSL_X509_CRT_PARSE_C */ + + if( p != end ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + return( 0 ); +} + +/* + * Create session ticket, secured as recommended in RFC 5077 section 4: + * + * struct { + * opaque key_name[16]; + * opaque iv[16]; + * opaque encrypted_state<0..2^16-1>; + * opaque mac[32]; + * } ticket; + * + * (the internal state structure differs, however). + */ +static int ssl_write_ticket( ssl_context *ssl, size_t *tlen ) +{ + int ret; + unsigned char * const start = ssl->out_msg + 10; + unsigned char *p = start; + unsigned char *state; + unsigned char iv[16]; + size_t clear_len, enc_len, pad_len, i; + + *tlen = 0; + + if( ssl->ticket_keys == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + /* Write key name */ + memcpy( p, ssl->ticket_keys->key_name, 16 ); + p += 16; + + /* Generate and write IV (with a copy for aes_crypt) */ + if( ( ret = ssl->f_rng( ssl->p_rng, p, 16 ) ) != 0 ) + return( ret ); + memcpy( iv, p, 16 ); + p += 16; + + /* + * Dump session state + * + * After the session state itself, we still need room for 16 bytes of + * padding and 32 bytes of MAC, so there's only so much room left + */ + state = p + 2; + if( ssl_save_session( ssl->session_negotiate, state, + SSL_MAX_CONTENT_LEN - ( state - ssl->out_ctr ) - 48, + &clear_len ) != 0 ) + { + return( POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE ); + } + SSL_DEBUG_BUF( 3, "session ticket cleartext", state, clear_len ); + + /* Apply PKCS padding */ + pad_len = 16 - clear_len % 16; + enc_len = clear_len + pad_len; + for( i = clear_len; i < enc_len; i++ ) + state[i] = (unsigned char) pad_len; + + /* Encrypt */ + if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->enc, AES_ENCRYPT, + enc_len, iv, state, state ) ) != 0 ) + { + return( ret ); + } + + /* Write length */ + *p++ = (unsigned char)( ( enc_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( enc_len ) & 0xFF ); + p = state + enc_len; + + /* Compute and write MAC( key_name + iv + enc_state_len + enc_state ) */ + sha256_hmac( ssl->ticket_keys->mac_key, 16, start, p - start, p, 0 ); + p += 32; + + *tlen = p - start; + + SSL_DEBUG_BUF( 3, "session ticket structure", start, *tlen ); + + return( 0 ); +} + +/* + * Load session ticket (see ssl_write_ticket for structure) + */ +static int ssl_parse_ticket( ssl_context *ssl, + unsigned char *buf, + size_t len ) +{ + int ret; + ssl_session session; + unsigned char *key_name = buf; + unsigned char *iv = buf + 16; + unsigned char *enc_len_p = iv + 16; + unsigned char *ticket = enc_len_p + 2; + unsigned char *mac; + unsigned char computed_mac[32]; + size_t enc_len, clear_len, i; + unsigned char pad_len, diff; + + SSL_DEBUG_BUF( 3, "session ticket structure", buf, len ); + + if( len < 34 || ssl->ticket_keys == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + enc_len = ( enc_len_p[0] << 8 ) | enc_len_p[1]; + mac = ticket + enc_len; + + if( len != enc_len + 66 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + /* Check name, in constant time though it's not a big secret */ + diff = 0; + for( i = 0; i < 16; i++ ) + diff |= key_name[i] ^ ssl->ticket_keys->key_name[i]; + /* don't return yet, check the MAC anyway */ + + /* Check mac, with constant-time buffer comparison */ + sha256_hmac( ssl->ticket_keys->mac_key, 16, buf, len - 32, + computed_mac, 0 ); + + for( i = 0; i < 32; i++ ) + diff |= mac[i] ^ computed_mac[i]; + + /* Now return if ticket is not authentic, since we want to avoid + * decrypting arbitrary attacker-chosen data */ + if( diff != 0 ) + return( POLARSSL_ERR_SSL_INVALID_MAC ); + + /* Decrypt */ + if( ( ret = aes_crypt_cbc( &ssl->ticket_keys->dec, AES_DECRYPT, + enc_len, iv, ticket, ticket ) ) != 0 ) + { + return( ret ); + } + + /* Check PKCS padding */ + pad_len = ticket[enc_len - 1]; + + ret = 0; + for( i = 2; i < pad_len; i++ ) + if( ticket[enc_len - i] != pad_len ) + ret = POLARSSL_ERR_SSL_BAD_INPUT_DATA; + if( ret != 0 ) + return( ret ); + + clear_len = enc_len - pad_len; + + SSL_DEBUG_BUF( 3, "session ticket cleartext", ticket, clear_len ); + + /* Actually load session */ + if( ( ret = ssl_load_session( &session, ticket, clear_len ) ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "failed to parse ticket content" ) ); + ssl_session_free( &session ); + return( ret ); + } + +#if defined(POLARSSL_HAVE_TIME) + /* Check if still valid */ + if( (int) ( time( NULL) - session.start ) > ssl->ticket_lifetime ) + { + SSL_DEBUG_MSG( 1, ( "session ticket expired" ) ); + ssl_session_free( &session ); + return( POLARSSL_ERR_SSL_SESSION_TICKET_EXPIRED ); + } +#endif + + /* + * Keep the session ID sent by the client, since we MUST send it back to + * inform him we're accepting the ticket (RFC 5077 section 3.4) + */ + session.length = ssl->session_negotiate->length; + memcpy( &session.id, ssl->session_negotiate->id, session.length ); + + ssl_session_free( ssl->session_negotiate ); + memcpy( ssl->session_negotiate, &session, sizeof( ssl_session ) ); + + /* Zeroize instead of free as we copied the content */ + polarssl_zeroize( &session, sizeof( ssl_session ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) +/* + * Wrapper around f_sni, allowing use of ssl_set_own_cert() but + * making it act on ssl->hanshake->sni_key_cert instead. + */ +static int ssl_sni_wrapper( ssl_context *ssl, + const unsigned char* name, size_t len ) +{ + int ret; + ssl_key_cert *key_cert_ori = ssl->key_cert; + + ssl->key_cert = NULL; + ret = ssl->f_sni( ssl->p_sni, ssl, name, len ); + ssl->handshake->sni_key_cert = ssl->key_cert; + + ssl->key_cert = key_cert_ori; + + return( ret ); +} + +static int ssl_parse_servername_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + int ret; + size_t servername_list_size, hostname_len; + const unsigned char *p; + + SSL_DEBUG_MSG( 3, ( "parse ServerName extension" ) ); + + servername_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); + if( servername_list_size + 2 != len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + p = buf + 2; + while( servername_list_size > 0 ) + { + hostname_len = ( ( p[1] << 8 ) | p[2] ); + if( hostname_len + 3 > servername_list_size ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( p[0] == TLS_EXT_SERVERNAME_HOSTNAME ) + { + ret = ssl_sni_wrapper( ssl, p + 3, hostname_len ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_sni_wrapper", ret ); + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_UNRECOGNIZED_NAME ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + return( 0 ); + } + + servername_list_size -= hostname_len + 3; + p += hostname_len + 3; + } + + if( servername_list_size != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + return( 0 ); +} +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ + +static int ssl_parse_renegotiation_info( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + int ret; + + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE ) + { + if( len != 1 || buf[0] != 0x0 ) + { + SSL_DEBUG_MSG( 1, ( "non-zero length renegotiated connection field" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; + } + else + { + /* Check verify-data in constant-time. The length OTOH is no secret */ + if( len != 1 + ssl->verify_data_len || + buf[0] != ssl->verify_data_len || + safer_memcmp( buf + 1, ssl->peer_verify_data, + ssl->verify_data_len ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "non-matching renegotiated connection field" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + } + + return( 0 ); +} + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +static int ssl_parse_signature_algorithms_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + size_t sig_alg_list_size; + const unsigned char *p; + const unsigned char *end = buf + len; + const int *md_cur; + + + sig_alg_list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); + if( sig_alg_list_size + 2 != len || + sig_alg_list_size % 2 != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* + * For now, ignore the SignatureAlgorithm part and rely on offered + * ciphersuites only for that part. To be fixed later. + * + * So, just look at the HashAlgorithm part. + */ + for( md_cur = md_list(); *md_cur != POLARSSL_MD_NONE; md_cur++ ) { + for( p = buf + 2; p < end; p += 2 ) { + if( *md_cur == (int) ssl_md_alg_from_hash( p[0] ) ) { + ssl->handshake->sig_alg = p[0]; + break; + } + } + } + + SSL_DEBUG_MSG( 3, ( "client hello v3, signature_algorithm ext: %d", + ssl->handshake->sig_alg ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) +static int ssl_parse_supported_elliptic_curves( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + size_t list_size, our_size; + const unsigned char *p; + const ecp_curve_info *curve_info, **curves; + + list_size = ( ( buf[0] << 8 ) | ( buf[1] ) ); + if( list_size + 2 != len || + list_size % 2 != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* Don't allow our peer to make us allocate too much memory, + * and leave room for a final 0 */ + our_size = list_size / 2 + 1; + if( our_size > POLARSSL_ECP_DP_MAX ) + our_size = POLARSSL_ECP_DP_MAX; + + if( ( curves = polarssl_malloc( our_size * sizeof( *curves ) ) ) == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + /* explicit void pointer cast for buggy MS compiler */ + memset( (void *) curves, 0, our_size * sizeof( *curves ) ); + ssl->handshake->curves = curves; + + p = buf + 2; + while( list_size > 0 && our_size > 1 ) + { + curve_info = ecp_curve_info_from_tls_id( ( p[0] << 8 ) | p[1] ); + + if( curve_info != NULL ) + { + *curves++ = curve_info; + our_size--; + } + + list_size -= 2; + p += 2; + } + + return( 0 ); +} + +static int ssl_parse_supported_point_formats( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + size_t list_size; + const unsigned char *p; + + list_size = buf[0]; + if( list_size + 1 != len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + p = buf + 2; + while( list_size > 0 ) + { + if( p[0] == POLARSSL_ECP_PF_UNCOMPRESSED || + p[0] == POLARSSL_ECP_PF_COMPRESSED ) + { + ssl->handshake->ecdh_ctx.point_format = p[0]; + SSL_DEBUG_MSG( 4, ( "point format selected: %d", p[0] ) ); + return( 0 ); + } + + list_size--; + p++; + } + + return( 0 ); +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +static int ssl_parse_max_fragment_length_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( len != 1 || buf[0] >= SSL_MAX_FRAG_LEN_INVALID ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->session_negotiate->mfl_code = buf[0]; + + return( 0 ); +} +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +static int ssl_parse_truncated_hmac_ext( ssl_context *ssl, + const unsigned char *buf, + size_t len ) +{ + if( len != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ((void) buf); + + ssl->session_negotiate->trunc_hmac = SSL_TRUNC_HMAC_ENABLED; + + return( 0 ); +} +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static int ssl_parse_session_ticket_ext( ssl_context *ssl, + unsigned char *buf, + size_t len ) +{ + int ret; + + if( ssl->session_tickets == SSL_SESSION_TICKETS_DISABLED ) + return( 0 ); + + /* Remember the client asked us to send a new ticket */ + ssl->handshake->new_session_ticket = 1; + + SSL_DEBUG_MSG( 3, ( "ticket length: %d", len ) ); + + if( len == 0 ) + return( 0 ); + + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ) + { + SSL_DEBUG_MSG( 3, ( "ticket rejected: renegotiating" ) ); + return( 0 ); + } + + /* + * Failures are ok: just ignore the ticket and proceed. + */ + if( ( ret = ssl_parse_ticket( ssl, buf, len ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_parse_ticket", ret ); + return( 0 ); + } + + SSL_DEBUG_MSG( 3, ( "session successfully restored from ticket" ) ); + + ssl->handshake->resume = 1; + + /* Don't send a new ticket after all, this one is OK */ + ssl->handshake->new_session_ticket = 0; + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_SSL_ALPN) +static int ssl_parse_alpn_ext( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + size_t list_len, cur_len, ours_len; + const unsigned char *theirs, *start, *end; + const char **ours; + + /* If ALPN not configured, just ignore the extension */ + if( ssl->alpn_list == NULL ) + return( 0 ); + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + + /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ + if( len < 4 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + list_len = ( buf[0] << 8 ) | buf[1]; + if( list_len != len - 2 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + /* + * Use our order of preference + */ + start = buf + 2; + end = buf + len; + for( ours = ssl->alpn_list; *ours != NULL; ours++ ) + { + ours_len = strlen( *ours ); + for( theirs = start; theirs != end; theirs += cur_len ) + { + /* If the list is well formed, we should get equality first */ + if( theirs > end ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + cur_len = *theirs++; + + /* Empty strings MUST NOT be included */ + if( cur_len == 0 ) + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + + if( cur_len == ours_len && + memcmp( theirs, *ours, cur_len ) == 0 ) + { + ssl->alpn_chosen = *ours; + return( 0 ); + } + } + } + + /* If we get there, no match was found */ + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); +} +#endif /* POLARSSL_SSL_ALPN */ + +/* + * Auxiliary functions for ServerHello parsing and related actions + */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/* + * Return 1 if the given EC key uses the given curve, 0 otherwise + */ +#if defined(POLARSSL_ECDSA_C) +static int ssl_key_matches_curves( pk_context *pk, + const ecp_curve_info **curves ) +{ + const ecp_curve_info **crv = curves; + ecp_group_id grp_id = pk_ec( *pk )->grp.id; + + while( *crv != NULL ) + { + if( (*crv)->grp_id == grp_id ) + return( 1 ); + crv++; + } + + return( 0 ); +} +#endif /* POLARSSL_ECDSA_C */ + +/* + * Try picking a certificate for this ciphersuite, + * return 0 on success and -1 on failure. + */ +static int ssl_pick_cert( ssl_context *ssl, + const ssl_ciphersuite_t * ciphersuite_info ) +{ + ssl_key_cert *cur, *list; + pk_type_t pk_alg = ssl_get_ciphersuite_sig_pk_alg( ciphersuite_info ); + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + if( ssl->handshake->sni_key_cert != NULL ) + list = ssl->handshake->sni_key_cert; + else +#endif + list = ssl->handshake->key_cert; + + if( pk_alg == POLARSSL_PK_NONE ) + return( 0 ); + + for( cur = list; cur != NULL; cur = cur->next ) + { + if( ! pk_can_do( cur->key, pk_alg ) ) + continue; + + /* + * This avoids sending the client a cert it'll reject based on + * keyUsage or other extensions. + * + * It also allows the user to provision different certificates for + * different uses based on keyUsage, eg if they want to avoid signing + * and decrypting with the same RSA key. + */ + if( ssl_check_cert_usage( cur->cert, ciphersuite_info, + SSL_IS_SERVER ) != 0 ) + { + continue; + } + +#if defined(POLARSSL_ECDSA_C) + if( pk_alg == POLARSSL_PK_ECDSA ) + { + if( ssl_key_matches_curves( cur->key, ssl->handshake->curves ) ) + break; + } + else +#endif + break; + } + + if( cur == NULL ) + return( -1 ); + + ssl->handshake->key_cert = cur; + return( 0 ); +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +/* + * Check if a given ciphersuite is suitable for use with our config/keys/etc + * Sets ciphersuite_info only if the suite matches. + */ +static int ssl_ciphersuite_match( ssl_context *ssl, int suite_id, + const ssl_ciphersuite_t **ciphersuite_info ) +{ + const ssl_ciphersuite_t *suite_info; + + suite_info = ssl_ciphersuite_from_id( suite_id ); + if( suite_info == NULL ) + { + SSL_DEBUG_MSG( 1, ( "ciphersuite info for %04x not found", suite_id ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + if( suite_info->min_minor_ver > ssl->minor_ver || + suite_info->max_minor_ver < ssl->minor_ver ) + return( 0 ); + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + if( ssl_ciphersuite_uses_ec( suite_info ) && + ( ssl->handshake->curves == NULL || + ssl->handshake->curves[0] == NULL ) ) + return( 0 ); +#endif + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + /* If the ciphersuite requires a pre-shared key and we don't + * have one, skip it now rather than failing later */ + if( ssl_ciphersuite_uses_psk( suite_info ) && + ssl->f_psk == NULL && + ( ssl->psk == NULL || ssl->psk_identity == NULL || + ssl->psk_identity_len == 0 || ssl->psk_len == 0 ) ) + return( 0 ); +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) + /* + * Final check: if ciphersuite requires us to have a + * certificate/key of a particular type: + * - select the appropriate certificate if we have one, or + * - try the next ciphersuite if we don't + * This must be done last since we modify the key_cert list. + */ + if( ssl_pick_cert( ssl, suite_info ) != 0 ) + return( 0 ); +#endif + + *ciphersuite_info = suite_info; + return( 0 ); +} + +#if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) +static int ssl_parse_client_hello_v2( ssl_context *ssl ) +{ + int ret; + unsigned int i, j; + size_t n; + unsigned int ciph_len, sess_len, chal_len; + unsigned char *buf, *p; + const int *ciphersuites; + const ssl_ciphersuite_t *ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse client hello v2" ) ); + + if( ssl->renegotiation != SSL_INITIAL_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "client hello v2 illegal for renegotiation" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + buf = ssl->in_hdr; + + SSL_DEBUG_BUF( 4, "record header", buf, 5 ); + + SSL_DEBUG_MSG( 3, ( "client hello v2, message type: %d", + buf[2] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v2, message len.: %d", + ( ( buf[0] & 0x7F ) << 8 ) | buf[1] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v2, max. version: [%d:%d]", + buf[3], buf[4] ) ); + + /* + * SSLv2 Client Hello + * + * Record layer: + * 0 . 1 message length + * + * SSL layer: + * 2 . 2 message type + * 3 . 4 protocol version + */ + if( buf[2] != SSL_HS_CLIENT_HELLO || + buf[3] != SSL_MAJOR_VERSION_3 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + n = ( ( buf[0] << 8 ) | buf[1] ) & 0x7FFF; + + if( n < 17 || n > 512 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->major_ver = SSL_MAJOR_VERSION_3; + ssl->minor_ver = ( buf[4] <= ssl->max_minor_ver ) + ? buf[4] : ssl->max_minor_ver; + + if( ssl->minor_ver < ssl->min_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum" + " [%d:%d] < [%d:%d]", + ssl->major_ver, ssl->minor_ver, + ssl->min_major_ver, ssl->min_minor_ver ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_PROTOCOL_VERSION ); + return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); + } + + ssl->handshake->max_major_ver = buf[3]; + ssl->handshake->max_minor_ver = buf[4]; + + if( ( ret = ssl_fetch_input( ssl, 2 + n ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); + return( ret ); + } + + ssl->handshake->update_checksum( ssl, buf + 2, n ); + + buf = ssl->in_msg; + n = ssl->in_left - 5; + + /* + * 0 . 1 ciphersuitelist length + * 2 . 3 session id length + * 4 . 5 challenge length + * 6 . .. ciphersuitelist + * .. . .. session id + * .. . .. challenge + */ + SSL_DEBUG_BUF( 4, "record contents", buf, n ); + + ciph_len = ( buf[0] << 8 ) | buf[1]; + sess_len = ( buf[2] << 8 ) | buf[3]; + chal_len = ( buf[4] << 8 ) | buf[5]; + + SSL_DEBUG_MSG( 3, ( "ciph_len: %d, sess_len: %d, chal_len: %d", + ciph_len, sess_len, chal_len ) ); + + /* + * Make sure each parameter length is valid + */ + if( ciph_len < 3 || ( ciph_len % 3 ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( sess_len > 32 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( chal_len < 8 || chal_len > 32 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( n != 6 + ciph_len + sess_len + chal_len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + SSL_DEBUG_BUF( 3, "client hello, ciphersuitelist", + buf + 6, ciph_len ); + SSL_DEBUG_BUF( 3, "client hello, session id", + buf + 6 + ciph_len, sess_len ); + SSL_DEBUG_BUF( 3, "client hello, challenge", + buf + 6 + ciph_len + sess_len, chal_len ); + + p = buf + 6 + ciph_len; + ssl->session_negotiate->length = sess_len; + memset( ssl->session_negotiate->id, 0, + sizeof( ssl->session_negotiate->id ) ); + memcpy( ssl->session_negotiate->id, p, ssl->session_negotiate->length ); + + p += sess_len; + memset( ssl->handshake->randbytes, 0, 64 ); + memcpy( ssl->handshake->randbytes + 32 - chal_len, p, chal_len ); + + /* + * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV + */ + for( i = 0, p = buf + 6; i < ciph_len; i += 3, p += 3 ) + { + if( p[0] == 0 && p[1] == 0 && p[2] == SSL_EMPTY_RENEGOTIATION_INFO ) + { + SSL_DEBUG_MSG( 3, ( "received TLS_EMPTY_RENEGOTIATION_INFO " ) ); + if( ssl->renegotiation == SSL_RENEGOTIATION ) + { + SSL_DEBUG_MSG( 1, ( "received RENEGOTIATION SCSV during renegotiation" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; + break; + } + } + + ciphersuites = ssl->ciphersuite_list[ssl->minor_ver]; + ciphersuite_info = NULL; +#if defined(POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE) + for( j = 0, p = buf + 6; j < ciph_len; j += 3, p += 3 ) + { + for( i = 0; ciphersuites[i] != 0; i++ ) +#else + for( i = 0; ciphersuites[i] != 0; i++ ) + { + for( j = 0, p = buf + 6; j < ciph_len; j += 3, p += 3 ) +#endif + { + if( p[0] != 0 || + p[1] != ( ( ciphersuites[i] >> 8 ) & 0xFF ) || + p[2] != ( ( ciphersuites[i] ) & 0xFF ) ) + continue; + + if( ( ret = ssl_ciphersuite_match( ssl, ciphersuites[i], + &ciphersuite_info ) ) != 0 ) + return( ret ); + + if( ciphersuite_info != NULL ) + goto have_ciphersuite_v2; + } + } + + SSL_DEBUG_MSG( 1, ( "got no ciphersuites in common" ) ); + + return( POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN ); + +have_ciphersuite_v2: + ssl->session_negotiate->ciphersuite = ciphersuites[i]; + ssl->transform_negotiate->ciphersuite_info = ciphersuite_info; + ssl_optimize_checksum( ssl, ssl->transform_negotiate->ciphersuite_info ); + + /* + * SSLv2 Client Hello relevant renegotiation security checks + */ + if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->in_left = 0; + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse client hello v2" ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO */ + +static int ssl_parse_client_hello( ssl_context *ssl ) +{ + int ret; + unsigned int i, j; + size_t n; + unsigned int ciph_len, sess_len; + unsigned int comp_len; + unsigned int ext_len = 0; + unsigned char *buf, *p, *ext; + int renegotiation_info_seen = 0; + int handshake_failure = 0; + const int *ciphersuites; + const ssl_ciphersuite_t *ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse client hello" ) ); + + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); + return( ret ); + } + + buf = ssl->in_hdr; + +#if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) + if( ( buf[0] & 0x80 ) != 0 ) + return ssl_parse_client_hello_v2( ssl ); +#endif + + SSL_DEBUG_BUF( 4, "record header", buf, 5 ); + + SSL_DEBUG_MSG( 3, ( "client hello v3, message type: %d", + buf[0] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v3, message len.: %d", + ( buf[3] << 8 ) | buf[4] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v3, protocol ver: [%d:%d]", + buf[1], buf[2] ) ); + + /* + * SSLv3/TLS Client Hello + * + * Record layer: + * 0 . 0 message type + * 1 . 2 protocol version + * 3 . 4 message length + */ + + /* According to RFC 5246 Appendix E.1, the version here is typically + * "{03,00}, the lowest version number supported by the client, [or] the + * value of ClientHello.client_version", so the only meaningful check here + * is the major version shouldn't be less than 3 */ + if( buf[0] != SSL_MSG_HANDSHAKE || + buf[1] < SSL_MAJOR_VERSION_3 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + n = ( buf[3] << 8 ) | buf[4]; + + if( n < 45 || n > SSL_MAX_CONTENT_LEN ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + if( ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ( ret = ssl_fetch_input( ssl, 5 + n ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); + return( ret ); + } + + buf = ssl->in_msg; + if( !ssl->renegotiation ) + n = ssl->in_left - 5; + else + n = ssl->in_msglen; + + ssl->handshake->update_checksum( ssl, buf, n ); + + /* + * SSL layer: + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 protocol version + * 6 . 9 UNIX time() + * 10 . 37 random bytes + * 38 . 38 session id length + * 39 . 38+x session id + * 39+x . 40+x ciphersuitelist length + * 41+x . 40+y ciphersuitelist + * 41+y . 41+y compression alg length + * 42+y . 41+z compression algs + * .. . .. extensions + */ + SSL_DEBUG_BUF( 4, "record contents", buf, n ); + + SSL_DEBUG_MSG( 3, ( "client hello v3, handshake type: %d", + buf[0] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v3, handshake len.: %d", + ( buf[1] << 16 ) | ( buf[2] << 8 ) | buf[3] ) ); + SSL_DEBUG_MSG( 3, ( "client hello v3, max. version: [%d:%d]", + buf[4], buf[5] ) ); + + /* + * Check the handshake type and protocol version + */ + if( buf[0] != SSL_HS_CLIENT_HELLO ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->major_ver = buf[4]; + ssl->minor_ver = buf[5]; + + ssl->handshake->max_major_ver = ssl->major_ver; + ssl->handshake->max_minor_ver = ssl->minor_ver; + + if( ssl->major_ver < ssl->min_major_ver || + ssl->minor_ver < ssl->min_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum" + " [%d:%d] < [%d:%d]", + ssl->major_ver, ssl->minor_ver, + ssl->min_major_ver, ssl->min_minor_ver ) ); + + ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_PROTOCOL_VERSION ); + + return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION ); + } + + if( ssl->major_ver > ssl->max_major_ver ) + { + ssl->major_ver = ssl->max_major_ver; + ssl->minor_ver = ssl->max_minor_ver; + } + else if( ssl->minor_ver > ssl->max_minor_ver ) + ssl->minor_ver = ssl->max_minor_ver; + + memcpy( ssl->handshake->randbytes, buf + 6, 32 ); + + /* + * Check the handshake message length + */ + if( buf[1] != 0 || n != (unsigned int) 4 + ( ( buf[2] << 8 ) | buf[3] ) ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* + * Check the session length + */ + sess_len = buf[38]; + + if( sess_len > 32 || sess_len > n - 42 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + ssl->session_negotiate->length = sess_len; + memset( ssl->session_negotiate->id, 0, + sizeof( ssl->session_negotiate->id ) ); + memcpy( ssl->session_negotiate->id, buf + 39, + ssl->session_negotiate->length ); + + /* + * Check the ciphersuitelist length + */ + ciph_len = ( buf[39 + sess_len] << 8 ) + | ( buf[40 + sess_len] ); + + if( ciph_len < 2 || ( ciph_len % 2 ) != 0 || ciph_len > n - 42 - sess_len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* + * Check the compression algorithms length + */ + comp_len = buf[41 + sess_len + ciph_len]; + + if( comp_len < 1 || comp_len > 16 || + comp_len > n - 42 - sess_len - ciph_len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* + * Check the extension length + */ + if( n > 42 + sess_len + ciph_len + comp_len ) + { + ext_len = ( buf[42 + sess_len + ciph_len + comp_len] << 8 ) + | ( buf[43 + sess_len + ciph_len + comp_len] ); + + if( ( ext_len > 0 && ext_len < 4 ) || + n != 44 + sess_len + ciph_len + comp_len + ext_len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + SSL_DEBUG_BUF( 3, "Ext", buf + 44 + sess_len + ciph_len + comp_len, ext_len); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + } + + ssl->session_negotiate->compression = SSL_COMPRESS_NULL; +#if defined(POLARSSL_ZLIB_SUPPORT) + for( i = 0; i < comp_len; ++i ) + { + if( buf[42 + sess_len + ciph_len + i] == SSL_COMPRESS_DEFLATE ) + { + ssl->session_negotiate->compression = SSL_COMPRESS_DEFLATE; + break; + } + } +#endif + + SSL_DEBUG_BUF( 3, "client hello, random bytes", + buf + 6, 32 ); + SSL_DEBUG_BUF( 3, "client hello, session id", + buf + 38, sess_len ); + SSL_DEBUG_BUF( 3, "client hello, ciphersuitelist", + buf + 41 + sess_len, ciph_len ); + SSL_DEBUG_BUF( 3, "client hello, compression", + buf + 42 + sess_len + ciph_len, comp_len ); + + /* + * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV + */ + for( i = 0, p = buf + 41 + sess_len; i < ciph_len; i += 2, p += 2 ) + { + if( p[0] == 0 && p[1] == SSL_EMPTY_RENEGOTIATION_INFO ) + { + SSL_DEBUG_MSG( 3, ( "received TLS_EMPTY_RENEGOTIATION_INFO " ) ); + if( ssl->renegotiation == SSL_RENEGOTIATION ) + { + SSL_DEBUG_MSG( 1, ( "received RENEGOTIATION SCSV during renegotiation" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + ssl->secure_renegotiation = SSL_SECURE_RENEGOTIATION; + break; + } + } + + ext = buf + 44 + sess_len + ciph_len + comp_len; + + while( ext_len ) + { + unsigned int ext_id = ( ( ext[0] << 8 ) + | ( ext[1] ) ); + unsigned int ext_size = ( ( ext[2] << 8 ) + | ( ext[3] ) ); + + if( ext_size + 4 > ext_len ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + switch( ext_id ) + { +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + case TLS_EXT_SERVERNAME: + SSL_DEBUG_MSG( 3, ( "found ServerName extension" ) ); + if( ssl->f_sni == NULL ) + break; + + ret = ssl_parse_servername_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ + + case TLS_EXT_RENEGOTIATION_INFO: + SSL_DEBUG_MSG( 3, ( "found renegotiation extension" ) ); + renegotiation_info_seen = 1; + + ret = ssl_parse_renegotiation_info( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + case TLS_EXT_SIG_ALG: + SSL_DEBUG_MSG( 3, ( "found signature_algorithms extension" ) ); + if( ssl->renegotiation == SSL_RENEGOTIATION ) + break; + + ret = ssl_parse_signature_algorithms_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + case TLS_EXT_SUPPORTED_ELLIPTIC_CURVES: + SSL_DEBUG_MSG( 3, ( "found supported elliptic curves extension" ) ); + + ret = ssl_parse_supported_elliptic_curves( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; + + case TLS_EXT_SUPPORTED_POINT_FORMATS: + SSL_DEBUG_MSG( 3, ( "found supported point formats extension" ) ); + ssl->handshake->cli_exts |= TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT; + + ret = ssl_parse_supported_point_formats( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + case TLS_EXT_MAX_FRAGMENT_LENGTH: + SSL_DEBUG_MSG( 3, ( "found max fragment length extension" ) ); + + ret = ssl_parse_max_fragment_length_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + case TLS_EXT_TRUNCATED_HMAC: + SSL_DEBUG_MSG( 3, ( "found truncated hmac extension" ) ); + + ret = ssl_parse_truncated_hmac_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + case TLS_EXT_SESSION_TICKET: + SSL_DEBUG_MSG( 3, ( "found session ticket extension" ) ); + + ret = ssl_parse_session_ticket_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +#if defined(POLARSSL_SSL_ALPN) + case TLS_EXT_ALPN: + SSL_DEBUG_MSG( 3, ( "found alpn extension" ) ); + + ret = ssl_parse_alpn_ext( ssl, ext + 4, ext_size ); + if( ret != 0 ) + return( ret ); + break; +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + + default: + SSL_DEBUG_MSG( 3, ( "unknown extension found: %d (ignoring)", + ext_id ) ); + } + + ext_len -= 4 + ext_size; + ext += 4 + ext_size; + + if( ext_len > 0 && ext_len < 4 ) + { + SSL_DEBUG_MSG( 1, ( "bad client hello message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + } + + /* + * Renegotiation security checks + */ + if( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_BREAK_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation, breaking off handshake" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension missing (secure)" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == SSL_LEGACY_NO_RENEGOTIATION ) + { + SSL_DEBUG_MSG( 1, ( "legacy renegotiation not allowed" ) ); + handshake_failure = 1; + } + else if( ssl->renegotiation == SSL_RENEGOTIATION && + ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1 ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation_info extension present (legacy)" ) ); + handshake_failure = 1; + } + + if( handshake_failure == 1 ) + { + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_HELLO ); + } + + /* + * Search for a matching ciphersuite + * (At the end because we need information from the EC-based extensions + * and certificate from the SNI callback triggered by the SNI extension.) + */ + ciphersuites = ssl->ciphersuite_list[ssl->minor_ver]; + ciphersuite_info = NULL; +#if defined(POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE) + for( j = 0, p = buf + 41 + sess_len; j < ciph_len; j += 2, p += 2 ) + { + for( i = 0; ciphersuites[i] != 0; i++ ) +#else + for( i = 0; ciphersuites[i] != 0; i++ ) + { + for( j = 0, p = buf + 41 + sess_len; j < ciph_len; j += 2, p += 2 ) +#endif + { + if( p[0] != ( ( ciphersuites[i] >> 8 ) & 0xFF ) || + p[1] != ( ( ciphersuites[i] ) & 0xFF ) ) + continue; + + if( ( ret = ssl_ciphersuite_match( ssl, ciphersuites[i], + &ciphersuite_info ) ) != 0 ) + return( ret ); + + if( ciphersuite_info != NULL ) + goto have_ciphersuite; + } + } + + SSL_DEBUG_MSG( 1, ( "got no ciphersuites in common" ) ); + + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + + return( POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN ); + +have_ciphersuite: + ssl->session_negotiate->ciphersuite = ciphersuites[i]; + ssl->transform_negotiate->ciphersuite_info = ciphersuite_info; + ssl_optimize_checksum( ssl, ssl->transform_negotiate->ciphersuite_info ); + + ssl->in_left = 0; + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse client hello" ) ); + + return( 0 ); +} + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +static void ssl_write_truncated_hmac_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->session_negotiate->trunc_hmac == SSL_TRUNC_HMAC_DISABLED ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, adding truncated hmac extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_TRUNCATED_HMAC >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_TRUNCATED_HMAC ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static void ssl_write_session_ticket_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->handshake->new_session_ticket == 0 ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, adding session ticket extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SESSION_TICKET ) & 0xFF ); + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +static void ssl_write_renegotiation_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->secure_renegotiation != SSL_SECURE_RENEGOTIATION ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, secure renegotiation extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_RENEGOTIATION_INFO ) & 0xFF ); + + *p++ = 0x00; + *p++ = ( ssl->verify_data_len * 2 + 1 ) & 0xFF; + *p++ = ssl->verify_data_len * 2 & 0xFF; + + memcpy( p, ssl->peer_verify_data, ssl->verify_data_len ); + p += ssl->verify_data_len; + memcpy( p, ssl->own_verify_data, ssl->verify_data_len ); + p += ssl->verify_data_len; + + *olen = 5 + ssl->verify_data_len * 2; +} + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +static void ssl_write_max_fragment_length_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + + if( ssl->session_negotiate->mfl_code == SSL_MAX_FRAG_LEN_NONE ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, max_fragment_length extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_MAX_FRAGMENT_LENGTH ) & 0xFF ); + + *p++ = 0x00; + *p++ = 1; + + *p++ = ssl->session_negotiate->mfl_code; + + *olen = 5; +} +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) +static void ssl_write_supported_point_formats_ext( ssl_context *ssl, + unsigned char *buf, + size_t *olen ) +{ + unsigned char *p = buf; + ((void) ssl); + + if( ( ssl->handshake->cli_exts & + TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT ) == 0 ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, supported_point_formats extension" ) ); + + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_POINT_FORMATS >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( TLS_EXT_SUPPORTED_POINT_FORMATS ) & 0xFF ); + + *p++ = 0x00; + *p++ = 2; + + *p++ = 1; + *p++ = POLARSSL_ECP_PF_UNCOMPRESSED; + + *olen = 6; +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +#if defined(POLARSSL_SSL_ALPN ) +static void ssl_write_alpn_ext( ssl_context *ssl, + unsigned char *buf, size_t *olen ) +{ + if( ssl->alpn_chosen == NULL ) + { + *olen = 0; + return; + } + + SSL_DEBUG_MSG( 3, ( "server hello, adding alpn extension" ) ); + + /* + * 0 . 1 ext identifier + * 2 . 3 ext length + * 4 . 5 protocol list length + * 6 . 6 protocol name length + * 7 . 7+n protocol name + */ + buf[0] = (unsigned char)( ( TLS_EXT_ALPN >> 8 ) & 0xFF ); + buf[1] = (unsigned char)( ( TLS_EXT_ALPN ) & 0xFF ); + + *olen = 7 + strlen( ssl->alpn_chosen ); + + buf[2] = (unsigned char)( ( ( *olen - 4 ) >> 8 ) & 0xFF ); + buf[3] = (unsigned char)( ( ( *olen - 4 ) ) & 0xFF ); + + buf[4] = (unsigned char)( ( ( *olen - 6 ) >> 8 ) & 0xFF ); + buf[5] = (unsigned char)( ( ( *olen - 6 ) ) & 0xFF ); + + buf[6] = (unsigned char)( ( ( *olen - 7 ) ) & 0xFF ); + + memcpy( buf + 7, ssl->alpn_chosen, *olen - 7 ); +} +#endif /* POLARSSL_ECDH_C || POLARSSL_ECDSA_C */ + +static int ssl_write_server_hello( ssl_context *ssl ) +{ +#if defined(POLARSSL_HAVE_TIME) + time_t t; +#endif + int ret; + size_t olen, ext_len = 0, n; + unsigned char *buf, *p; + + SSL_DEBUG_MSG( 2, ( "=> write server hello" ) ); + + if( ssl->f_rng == NULL ) + { + SSL_DEBUG_MSG( 1, ( "no RNG provided") ); + return( POLARSSL_ERR_SSL_NO_RNG ); + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 protocol version + * 6 . 9 UNIX time() + * 10 . 37 random bytes + */ + buf = ssl->out_msg; + p = buf + 4; + + *p++ = (unsigned char) ssl->major_ver; + *p++ = (unsigned char) ssl->minor_ver; + + SSL_DEBUG_MSG( 3, ( "server hello, chosen version: [%d:%d]", + buf[4], buf[5] ) ); + +#if defined(POLARSSL_HAVE_TIME) + t = time( NULL ); + *p++ = (unsigned char)( t >> 24 ); + *p++ = (unsigned char)( t >> 16 ); + *p++ = (unsigned char)( t >> 8 ); + *p++ = (unsigned char)( t ); + + SSL_DEBUG_MSG( 3, ( "server hello, current time: %lu", t ) ); +#else + if( ( ret = ssl->f_rng( ssl->p_rng, p, 4 ) ) != 0 ) + return( ret ); + + p += 4; +#endif /* POLARSSL_HAVE_TIME */ + + if( ( ret = ssl->f_rng( ssl->p_rng, p, 28 ) ) != 0 ) + return( ret ); + + p += 28; + + memcpy( ssl->handshake->randbytes + 32, buf + 6, 32 ); + + SSL_DEBUG_BUF( 3, "server hello, random bytes", buf + 6, 32 ); + + /* + * Resume is 0 by default, see ssl_handshake_init(). + * It may be already set to 1 by ssl_parse_session_ticket_ext(). + * If not, try looking up session ID in our cache. + */ + if( ssl->handshake->resume == 0 && + ssl->renegotiation == SSL_INITIAL_HANDSHAKE && + ssl->session_negotiate->length != 0 && + ssl->f_get_cache != NULL && + ssl->f_get_cache( ssl->p_get_cache, ssl->session_negotiate ) == 0 ) + { + SSL_DEBUG_MSG( 3, ( "session successfully restored from cache" ) ); + ssl->handshake->resume = 1; + } + + if( ssl->handshake->resume == 0 ) + { + /* + * New session, create a new session id, + * unless we're about to issue a session ticket + */ + ssl->state++; + +#if defined(POLARSSL_HAVE_TIME) + ssl->session_negotiate->start = time( NULL ); +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( ssl->handshake->new_session_ticket != 0 ) + { + ssl->session_negotiate->length = n = 0; + memset( ssl->session_negotiate->id, 0, 32 ); + } + else +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + { + ssl->session_negotiate->length = n = 32; + if( ( ret = ssl->f_rng( ssl->p_rng, ssl->session_negotiate->id, + n ) ) != 0 ) + return( ret ); + } + } + else + { + /* + * Resuming a session + */ + n = ssl->session_negotiate->length; + ssl->state = SSL_SERVER_CHANGE_CIPHER_SPEC; + + if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); + return( ret ); + } + } + + /* + * 38 . 38 session id length + * 39 . 38+n session id + * 39+n . 40+n chosen ciphersuite + * 41+n . 41+n chosen compression alg. + * 42+n . 43+n extensions length + * 44+n . 43+n+m extensions + */ + *p++ = (unsigned char) ssl->session_negotiate->length; + memcpy( p, ssl->session_negotiate->id, ssl->session_negotiate->length ); + p += ssl->session_negotiate->length; + + SSL_DEBUG_MSG( 3, ( "server hello, session id len.: %d", n ) ); + SSL_DEBUG_BUF( 3, "server hello, session id", buf + 39, n ); + SSL_DEBUG_MSG( 3, ( "%s session has been resumed", + ssl->handshake->resume ? "a" : "no" ) ); + + *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite >> 8 ); + *p++ = (unsigned char)( ssl->session_negotiate->ciphersuite ); + *p++ = (unsigned char)( ssl->session_negotiate->compression ); + + SSL_DEBUG_MSG( 3, ( "server hello, chosen ciphersuite: %s", + ssl_get_ciphersuite_name( ssl->session_negotiate->ciphersuite ) ) ); + SSL_DEBUG_MSG( 3, ( "server hello, compress alg.: 0x%02X", + ssl->session_negotiate->compression ) ); + + /* + * First write extensions, then the total length + */ + ssl_write_renegotiation_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + ssl_write_max_fragment_length_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + ssl_write_truncated_hmac_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + ssl_write_session_ticket_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + ssl_write_supported_point_formats_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + +#if defined(POLARSSL_SSL_ALPN) + ssl_write_alpn_ext( ssl, p + 2 + ext_len, &olen ); + ext_len += olen; +#endif + + SSL_DEBUG_MSG( 3, ( "server hello, total extension length: %d", ext_len ) ); + + if( ext_len > 0 ) + { + *p++ = (unsigned char)( ( ext_len >> 8 ) & 0xFF ); + *p++ = (unsigned char)( ( ext_len ) & 0xFF ); + p += ext_len; + } + + ssl->out_msglen = p - buf; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_SERVER_HELLO; + + ret = ssl_write_record( ssl ); + + SSL_DEBUG_MSG( 2, ( "<= write server hello" ) ); + + return( ret ); +} + +#if !defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +static int ssl_write_certificate_request( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write certificate request" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} +#else +static int ssl_write_certificate_request( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + size_t dn_size, total_dn_size; /* excluding length bytes */ + size_t ct_len, sa_len; /* including length bytes */ + unsigned char *buf, *p; + const x509_crt *crt; + + SSL_DEBUG_MSG( 2, ( "=> write certificate request" ) ); + + ssl->state++; + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ssl->authmode == SSL_VERIFY_NONE ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) ); + return( 0 ); + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 4 cert type count + * 5 .. m-1 cert types + * m .. m+1 sig alg length (TLS 1.2 only) + * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) + * n .. n+1 length of all DNs + * n+2 .. n+3 length of DN 1 + * n+4 .. ... Distinguished Name #1 + * ... .. ... length of DN 2, etc. + */ + buf = ssl->out_msg; + p = buf + 4; + + /* + * Supported certificate types + * + * ClientCertificateType certificate_types<1..2^8-1>; + * enum { (255) } ClientCertificateType; + */ + ct_len = 0; + +#if defined(POLARSSL_RSA_C) + p[1 + ct_len++] = SSL_CERT_TYPE_RSA_SIGN; +#endif +#if defined(POLARSSL_ECDSA_C) + p[1 + ct_len++] = SSL_CERT_TYPE_ECDSA_SIGN; +#endif + + p[0] = (unsigned char) ct_len++; + p += ct_len; + + sa_len = 0; +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + /* + * Add signature_algorithms for verify (TLS 1.2) + * + * SignatureAndHashAlgorithm supported_signature_algorithms<2..2^16-2>; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * enum { (255) } HashAlgorithm; + * enum { (255) } SignatureAlgorithm; + */ + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + /* + * Only use current running hash algorithm that is already required + * for requested ciphersuite. + */ + ssl->handshake->verify_sig_alg = SSL_HASH_SHA256; + + if( ssl->transform_negotiate->ciphersuite_info->mac == + POLARSSL_MD_SHA384 ) + { + ssl->handshake->verify_sig_alg = SSL_HASH_SHA384; + } + + /* + * Supported signature algorithms + */ +#if defined(POLARSSL_RSA_C) + p[2 + sa_len++] = ssl->handshake->verify_sig_alg; + p[2 + sa_len++] = SSL_SIG_RSA; +#endif +#if defined(POLARSSL_ECDSA_C) + p[2 + sa_len++] = ssl->handshake->verify_sig_alg; + p[2 + sa_len++] = SSL_SIG_ECDSA; +#endif + + p[0] = (unsigned char)( sa_len >> 8 ); + p[1] = (unsigned char)( sa_len ); + sa_len += 2; + p += sa_len; + } +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + /* + * DistinguishedName certificate_authorities<0..2^16-1>; + * opaque DistinguishedName<1..2^16-1>; + */ + p += 2; + crt = ssl->ca_chain; + + total_dn_size = 0; + while( crt != NULL && crt->version != 0 ) + { + if( p - buf > 4096 ) + break; + + dn_size = crt->subject_raw.len; + *p++ = (unsigned char)( dn_size >> 8 ); + *p++ = (unsigned char)( dn_size ); + memcpy( p, crt->subject_raw.p, dn_size ); + p += dn_size; + + SSL_DEBUG_BUF( 3, "requested DN", p, dn_size ); + + total_dn_size += 2 + dn_size; + crt = crt->next; + } + + ssl->out_msglen = p - buf; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_CERTIFICATE_REQUEST; + ssl->out_msg[4 + ct_len + sa_len] = (unsigned char)( total_dn_size >> 8 ); + ssl->out_msg[5 + ct_len + sa_len] = (unsigned char)( total_dn_size ); + + ret = ssl_write_record( ssl ); + + SSL_DEBUG_MSG( 2, ( "<= write certificate request" ) ); + + return( ret ); +} +#endif /* !POLARSSL_KEY_EXCHANGE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +static int ssl_get_ecdh_params_from_cert( ssl_context *ssl ) +{ + int ret; + + if( ! pk_can_do( ssl_own_key( ssl ), POLARSSL_PK_ECKEY ) ) + { + SSL_DEBUG_MSG( 1, ( "server key not ECDH capable" ) ); + return( POLARSSL_ERR_SSL_PK_TYPE_MISMATCH ); + } + + if( ( ret = ecdh_get_params( &ssl->handshake->ecdh_ctx, + pk_ec( *ssl_own_key( ssl ) ), + POLARSSL_ECDH_OURS ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ecdh_get_params" ), ret ); + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +static int ssl_write_server_key_exchange( ssl_context *ssl ) +{ + int ret; + size_t n = 0; + const ssl_ciphersuite_t *ciphersuite_info = + ssl->transform_negotiate->ciphersuite_info; + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + unsigned char *p = ssl->out_msg + 4; + unsigned char *dig_signed = p; + size_t dig_signed_len = 0, len; + ((void) dig_signed); + ((void) dig_signed_len); +#endif + + SSL_DEBUG_MSG( 2, ( "=> write server key exchange" ) ); + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write server key exchange" ) ); + ssl->state++; + return( 0 ); + } +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_ECDSA ) + { + ssl_get_ecdh_params_from_cert( ssl ); + + SSL_DEBUG_MSG( 2, ( "<= skip write server key exchange" ) ); + ssl->state++; + return( 0 ); + } +#endif + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + /* TODO: Support identity hints */ + *(p++) = 0x00; + *(p++) = 0x00; + + n += 2; + } +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + /* + * Ephemeral DH parameters: + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + if( ( ret = mpi_copy( &ssl->handshake->dhm_ctx.P, &ssl->dhm_P ) ) != 0 || + ( ret = mpi_copy( &ssl->handshake->dhm_ctx.G, &ssl->dhm_G ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_copy", ret ); + return( ret ); + } + + if( ( ret = dhm_make_params( &ssl->handshake->dhm_ctx, + (int) mpi_size( &ssl->handshake->dhm_ctx.P ), + p, &len, ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_make_params", ret ); + return( ret ); + } + + dig_signed = p; + dig_signed_len = len; + + p += len; + n += len; + + SSL_DEBUG_MPI( 3, "DHM: X ", &ssl->handshake->dhm_ctx.X ); + SSL_DEBUG_MPI( 3, "DHM: P ", &ssl->handshake->dhm_ctx.P ); + SSL_DEBUG_MPI( 3, "DHM: G ", &ssl->handshake->dhm_ctx.G ); + SSL_DEBUG_MPI( 3, "DHM: GX", &ssl->handshake->dhm_ctx.GX ); + } +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__ECDHE_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + /* + * Ephemeral ECDH parameters: + * + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ + const ecp_curve_info **curve = NULL; +#if defined(POLARSSL_SSL_SET_CURVES) + const ecp_group_id *gid; + + /* Match our preference list against the offered curves */ + for( gid = ssl->curve_list; *gid != POLARSSL_ECP_DP_NONE; gid++ ) + for( curve = ssl->handshake->curves; *curve != NULL; curve++ ) + if( (*curve)->grp_id == *gid ) + goto curve_matching_done; + +curve_matching_done: +#else + curve = ssl->handshake->curves; +#endif + + if( *curve == NULL ) + { + SSL_DEBUG_MSG( 1, ( "no matching curve for ECDHE" ) ); + return( POLARSSL_ERR_SSL_NO_CIPHER_CHOSEN ); + } + + SSL_DEBUG_MSG( 2, ( "ECDHE curve: %s", (*curve)->name ) ); + + if( ( ret = ecp_use_known_dp( &ssl->handshake->ecdh_ctx.grp, + (*curve)->grp_id ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecp_use_known_dp", ret ); + return( ret ); + } + + if( ( ret = ecdh_make_params( &ssl->handshake->ecdh_ctx, &len, + p, SSL_MAX_CONTENT_LEN - n, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_make_params", ret ); + return( ret ); + } + + dig_signed = p; + dig_signed_len = len; + + p += len; + n += len; + + SSL_DEBUG_ECP( 3, "ECDH: Q ", &ssl->handshake->ecdh_ctx.Q ); + } +#endif /* POLARSSL_KEY_EXCHANGE__SOME__ECDHE_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA ) + { + size_t signature_len = 0; + unsigned int hashlen = 0; + unsigned char hash[64]; + md_type_t md_alg = POLARSSL_MD_NONE; + + /* + * Choose hash algorithm. NONE means MD5 + SHA1 here. + */ +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + md_alg = ssl_md_alg_from_hash( ssl->handshake->sig_alg ); + + if( md_alg == POLARSSL_MD_NONE ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ciphersuite_info->key_exchange == + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA ) + { + md_alg = POLARSSL_MD_SHA1; + } + else +#endif + { + md_alg = POLARSSL_MD_NONE; + } + + /* + * Compute the hash to be signed + */ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( md_alg == POLARSSL_MD_NONE ) + { + md5_context md5; + sha1_context sha1; + + md5_init( &md5 ); + sha1_init( &sha1 ); + + /* + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * + * md5_hash + * MD5(ClientHello.random + ServerHello.random + * + ServerParams); + * sha_hash + * SHA(ClientHello.random + ServerHello.random + * + ServerParams); + */ + md5_starts( &md5 ); + md5_update( &md5, ssl->handshake->randbytes, 64 ); + md5_update( &md5, dig_signed, dig_signed_len ); + md5_finish( &md5, hash ); + + sha1_starts( &sha1 ); + sha1_update( &sha1, ssl->handshake->randbytes, 64 ); + sha1_update( &sha1, dig_signed, dig_signed_len ); + sha1_finish( &sha1, hash + 16 ); + + hashlen = 36; + + md5_free( &md5 ); + sha1_free( &sha1 ); + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 || POLARSSL_SSL_PROTO_TLS1 || \ + POLARSSL_SSL_PROTO_TLS1_1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( md_alg != POLARSSL_MD_NONE ) + { + md_context_t ctx; + const md_info_t *md_info = md_info_from_type( md_alg ); + + md_init( &ctx ); + + /* Info from md_alg will be used instead */ + hashlen = 0; + + /* + * digitally-signed struct { + * opaque client_random[32]; + * opaque server_random[32]; + * ServerDHParams params; + * }; + */ + if( ( ret = md_init_ctx( &ctx, md_info ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "md_init_ctx", ret ); + return( ret ); + } + + md_starts( &ctx ); + md_update( &ctx, ssl->handshake->randbytes, 64 ); + md_update( &ctx, dig_signed, dig_signed_len ); + md_finish( &ctx, hash ); + md_free( &ctx ); + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || \ + POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_BUF( 3, "parameters hash", hash, hashlen != 0 ? hashlen : + (unsigned int) ( md_info_from_type( md_alg ) )->size ); + + /* + * Make the signature + */ + if( ssl_own_key( ssl ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "got no private key" ) ); + return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + *(p++) = ssl->handshake->sig_alg; + *(p++) = ssl_sig_from_pk( ssl_own_key( ssl ) ); + + n += 2; + } +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + if( ( ret = pk_sign( ssl_own_key( ssl ), md_alg, hash, hashlen, + p + 2 , &signature_len, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "pk_sign", ret ); + return( ret ); + } + + *(p++) = (unsigned char)( signature_len >> 8 ); + *(p++) = (unsigned char)( signature_len ); + n += 2; + + SSL_DEBUG_BUF( 3, "my signature", p, signature_len ); + + p += signature_len; + n += signature_len; + } +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || + POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + + ssl->out_msglen = 4 + n; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_SERVER_KEY_EXCHANGE; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write server key exchange" ) ); + + return( 0 ); +} + +static int ssl_write_server_hello_done( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> write server hello done" ) ); + + ssl->out_msglen = 4; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_SERVER_HELLO_DONE; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write server hello done" ) ); + + return( 0 ); +} + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) +static int ssl_parse_client_dh_public( ssl_context *ssl, unsigned char **p, + const unsigned char *end ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + size_t n; + + /* + * Receive G^Y mod P, premaster = (G^Y)^X mod P + */ + if( *p + 2 > end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + n = ( (*p)[0] << 8 ) | (*p)[1]; + *p += 2; + + if( *p + n > end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + if( ( ret = dhm_read_public( &ssl->handshake->dhm_ctx, *p, n ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_read_public", ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP ); + } + + *p += n; + + SSL_DEBUG_MPI( 3, "DHM: GY", &ssl->handshake->dhm_ctx.GY ); + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) +static int ssl_parse_encrypted_pms( ssl_context *ssl, + const unsigned char *p, + const unsigned char *end, + size_t pms_offset ) +{ + int ret; + size_t len = pk_get_len( ssl_own_key( ssl ) ); + unsigned char *pms = ssl->handshake->premaster + pms_offset; + + if( ! pk_can_do( ssl_own_key( ssl ), POLARSSL_PK_RSA ) ) + { + SSL_DEBUG_MSG( 1, ( "got no RSA private key" ) ); + return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + + /* + * Decrypt the premaster using own private RSA key + */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver != SSL_MINOR_VERSION_0 ) + { + if( *p++ != ( ( len >> 8 ) & 0xFF ) || + *p++ != ( ( len ) & 0xFF ) ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + } +#endif + + if( p + len != end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + ret = pk_decrypt( ssl_own_key( ssl ), p, len, + pms, &ssl->handshake->pmslen, + sizeof( ssl->handshake->premaster ) - pms_offset, + ssl->f_rng, ssl->p_rng ); + + if( ret != 0 || ssl->handshake->pmslen != 48 || + pms[0] != ssl->handshake->max_major_ver || + pms[1] != ssl->handshake->max_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + + /* + * Protection against Bleichenbacher's attack: + * invalid PKCS#1 v1.5 padding must not cause + * the connection to end immediately; instead, + * send a bad_record_mac later in the handshake. + */ + ssl->handshake->pmslen = 48; + + ret = ssl->f_rng( ssl->p_rng, pms, ssl->handshake->pmslen ); + if( ret != 0 ) + return( ret ); + } + + return( ret ); +} +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +static int ssl_parse_client_psk_identity( ssl_context *ssl, unsigned char **p, + const unsigned char *end ) +{ + int ret = 0; + size_t n; + + if( ssl->f_psk == NULL && + ( ssl->psk == NULL || ssl->psk_identity == NULL || + ssl->psk_identity_len == 0 || ssl->psk_len == 0 ) ) + { + SSL_DEBUG_MSG( 1, ( "got no pre-shared key" ) ); + return( POLARSSL_ERR_SSL_PRIVATE_KEY_REQUIRED ); + } + + /* + * Receive client pre-shared key identity name + */ + if( *p + 2 > end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + n = ( (*p)[0] << 8 ) | (*p)[1]; + *p += 2; + + if( n < 1 || n > 65535 || *p + n > end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + if( ssl->f_psk != NULL ) + { + if( ssl->f_psk( ssl->p_psk, ssl, *p, n ) != 0 ) + ret = POLARSSL_ERR_SSL_UNKNOWN_IDENTITY; + } + else + { + /* Identity is not a big secret since clients send it in the clear, + * but treat it carefully anyway, just in case */ + if( n != ssl->psk_identity_len || + safer_memcmp( ssl->psk_identity, *p, n ) != 0 ) + { + ret = POLARSSL_ERR_SSL_UNKNOWN_IDENTITY; + } + } + + if( ret == POLARSSL_ERR_SSL_UNKNOWN_IDENTITY ) + { + SSL_DEBUG_BUF( 3, "Unknown PSK identity", *p, n ); + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY ) ) != 0 ) + { + return( ret ); + } + + return( POLARSSL_ERR_SSL_UNKNOWN_IDENTITY ); + } + + *p += n; + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +static int ssl_parse_client_key_exchange( ssl_context *ssl ) +{ + int ret; + const ssl_ciphersuite_t *ciphersuite_info; + + ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse client key exchange" ) ); + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + if( ssl->in_msg[0] != SSL_HS_CLIENT_KEY_EXCHANGE ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_RSA ) + { + unsigned char *p = ssl->in_msg + 4; + unsigned char *end = ssl->in_msg + ssl->in_hslen; + + if( ( ret = ssl_parse_client_dh_public( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_dh_public" ), ret ); + return( ret ); + } + + if( p != end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + ssl->handshake->pmslen = POLARSSL_PREMASTER_SIZE; + + if( ( ret = dhm_calc_secret( &ssl->handshake->dhm_ctx, + ssl->handshake->premaster, + &ssl->handshake->pmslen, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_calc_secret", ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS ); + } + + SSL_DEBUG_MPI( 3, "DHM: K ", &ssl->handshake->dhm_ctx.K ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDH_ECDSA ) + { + if( ( ret = ecdh_read_public( &ssl->handshake->ecdh_ctx, + ssl->in_msg + 4, ssl->in_hslen - 4 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_read_public", ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP ); + } + + SSL_DEBUG_ECP( 3, "ECDH: Qp ", &ssl->handshake->ecdh_ctx.Qp ); + + if( ( ret = ecdh_calc_secret( &ssl->handshake->ecdh_ctx, + &ssl->handshake->pmslen, + ssl->handshake->premaster, + POLARSSL_MPI_MAX_SIZE, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_calc_secret", ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_CS ); + } + + SSL_DEBUG_MPI( 3, "ECDH: z ", &ssl->handshake->ecdh_ctx.z ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED || + POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK ) + { + unsigned char *p = ssl->in_msg + 4; + unsigned char *end = ssl->in_msg + ssl->in_hslen; + + if( ( ret = ssl_parse_client_psk_identity( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_psk_identity" ), ret ); + return( ret ); + } + + if( p != end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + if( ( ret = ssl_psk_derive_premaster( ssl, + ciphersuite_info->key_exchange ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_psk_derive_premaster", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + { + unsigned char *p = ssl->in_msg + 4; + unsigned char *end = ssl->in_msg + ssl->in_hslen; + + if( ( ret = ssl_parse_client_psk_identity( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_psk_identity" ), ret ); + return( ret ); + } + + if( ( ret = ssl_parse_encrypted_pms( ssl, p, end, 2 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_encrypted_pms" ), ret ); + return( ret ); + } + + if( ( ret = ssl_psk_derive_premaster( ssl, + ciphersuite_info->key_exchange ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_psk_derive_premaster", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + unsigned char *p = ssl->in_msg + 4; + unsigned char *end = ssl->in_msg + ssl->in_hslen; + + if( ( ret = ssl_parse_client_psk_identity( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_psk_identity" ), ret ); + return( ret ); + } + if( ( ret = ssl_parse_client_dh_public( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_dh_public" ), ret ); + return( ret ); + } + + if( p != end ) + { + SSL_DEBUG_MSG( 1, ( "bad client key exchange" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE ); + } + + if( ( ret = ssl_psk_derive_premaster( ssl, + ciphersuite_info->key_exchange ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_psk_derive_premaster", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + unsigned char *p = ssl->in_msg + 4; + unsigned char *end = ssl->in_msg + ssl->in_hslen; + + if( ( ret = ssl_parse_client_psk_identity( ssl, &p, end ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_client_psk_identity" ), ret ); + return( ret ); + } + + if( ( ret = ecdh_read_public( &ssl->handshake->ecdh_ctx, + p, end - p ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_read_public", ret ); + return( POLARSSL_ERR_SSL_BAD_HS_CLIENT_KEY_EXCHANGE_RP ); + } + + SSL_DEBUG_ECP( 3, "ECDH: Qp ", &ssl->handshake->ecdh_ctx.Qp ); + + if( ( ret = ssl_psk_derive_premaster( ssl, + ciphersuite_info->key_exchange ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_psk_derive_premaster", ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA ) + { + if( ( ret = ssl_parse_encrypted_pms( ssl, + ssl->in_msg + 4, + ssl->in_msg + ssl->in_hslen, + 0 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, ( "ssl_parse_parse_encrypted_pms_secret" ), ret ); + return( ret ); + } + } + else +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + if( ( ret = ssl_derive_keys( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_derive_keys", ret ); + return( ret ); + } + + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse client key exchange" ) ); + + return( 0 ); +} + +#if !defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +static int ssl_parse_certificate_verify( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} +#else +static int ssl_parse_certificate_verify( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + size_t sa_len, sig_len; + unsigned char hash[48]; + unsigned char *hash_start = hash; + size_t hashlen; +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + pk_type_t pk_alg; +#endif + md_type_t md_alg; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate verify" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + if( ssl->session_negotiate->peer_cert == NULL ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) ); + ssl->state++; + return( 0 ); + } + + ssl->handshake->calc_verify( ssl, hash ); + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + ssl->state++; + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + + if( ssl->in_msg[0] != SSL_HS_CERTIFICATE_VERIFY ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 sig alg (TLS 1.2 only) + * 4+n . 5+n signature length (n = sa_len) + * 6+n . 6+n+m signature (m = sig_len) + */ + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ssl->minor_ver != SSL_MINOR_VERSION_3 ) + { + sa_len = 0; + + md_alg = POLARSSL_MD_NONE; + hashlen = 36; + + /* For ECDSA, use SHA-1, not MD-5 + SHA-1 */ + if( pk_can_do( &ssl->session_negotiate->peer_cert->pk, + POLARSSL_PK_ECDSA ) ) + { + hash_start += 16; + hashlen -= 16; + md_alg = POLARSSL_MD_SHA1; + } + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 || POLARSSL_SSL_PROTO_TLS1 || + POLARSSL_SSL_PROTO_TLS1_1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + sa_len = 2; + + /* + * Hash + */ + if( ssl->in_msg[4] != ssl->handshake->verify_sig_alg ) + { + SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg" + " for verify message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + + md_alg = ssl_md_alg_from_hash( ssl->handshake->verify_sig_alg ); + + /* Info from md_alg will be used instead */ + hashlen = 0; + + /* + * Signature + */ + if( ( pk_alg = ssl_pk_alg_from_sig( ssl->in_msg[5] ) ) + == POLARSSL_PK_NONE ) + { + SSL_DEBUG_MSG( 1, ( "peer not adhering to requested sig_alg" + " for verify message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + + /* + * Check the certificate's key type matches the signature alg + */ + if( ! pk_can_do( &ssl->session_negotiate->peer_cert->pk, pk_alg ) ) + { + SSL_DEBUG_MSG( 1, ( "sig_alg doesn't match cert key" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + sig_len = ( ssl->in_msg[4 + sa_len] << 8 ) | ssl->in_msg[5 + sa_len]; + + if( sa_len + sig_len + 6 != ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate verify message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY ); + } + + if( ( ret = pk_verify( &ssl->session_negotiate->peer_cert->pk, + md_alg, hash_start, hashlen, + ssl->in_msg + 6 + sa_len, sig_len ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "pk_verify", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= parse certificate verify" ) ); + + return( ret ); +} +#endif /* !POLARSSL_KEY_EXCHANGE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED && + !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static int ssl_write_new_session_ticket( ssl_context *ssl ) +{ + int ret; + size_t tlen; + uint32_t lifetime = (uint32_t) ssl->ticket_lifetime; + + SSL_DEBUG_MSG( 2, ( "=> write new session ticket" ) ); + + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_NEW_SESSION_TICKET; + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 4 . 7 ticket_lifetime_hint (0 = unspecified) + * 8 . 9 ticket_len (n) + * 10 . 9+n ticket content + */ + + ssl->out_msg[4] = ( lifetime >> 24 ) & 0xFF; + ssl->out_msg[5] = ( lifetime >> 16 ) & 0xFF; + ssl->out_msg[6] = ( lifetime >> 8 ) & 0xFF; + ssl->out_msg[7] = ( lifetime ) & 0xFF; + + if( ( ret = ssl_write_ticket( ssl, &tlen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_ticket", ret ); + tlen = 0; + } + + ssl->out_msg[8] = (unsigned char)( ( tlen >> 8 ) & 0xFF ); + ssl->out_msg[9] = (unsigned char)( ( tlen ) & 0xFF ); + + ssl->out_msglen = 10 + tlen; + + /* + * Morally equivalent to updating ssl->state, but NewSessionTicket and + * ChangeCipherSpec share the same state. + */ + ssl->handshake->new_session_ticket = 0; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write new session ticket" ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +/* + * SSL handshake -- server side -- single step + */ +int ssl_handshake_server_step( ssl_context *ssl ) +{ + int ret = 0; + + if( ssl->state == SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + SSL_DEBUG_MSG( 2, ( "server state: %d", ssl->state ) ); + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + return( ret ); + + switch( ssl->state ) + { + case SSL_HELLO_REQUEST: + ssl->state = SSL_CLIENT_HELLO; + break; + + /* + * <== ClientHello + */ + case SSL_CLIENT_HELLO: + ret = ssl_parse_client_hello( ssl ); + break; + + /* + * ==> ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case SSL_SERVER_HELLO: + ret = ssl_write_server_hello( ssl ); + break; + + case SSL_SERVER_CERTIFICATE: + ret = ssl_write_certificate( ssl ); + break; + + case SSL_SERVER_KEY_EXCHANGE: + ret = ssl_write_server_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_REQUEST: + ret = ssl_write_certificate_request( ssl ); + break; + + case SSL_SERVER_HELLO_DONE: + ret = ssl_write_server_hello_done( ssl ); + break; + + /* + * <== ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case SSL_CLIENT_CERTIFICATE: + ret = ssl_parse_certificate( ssl ); + break; + + case SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_parse_client_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_VERIFY: + ret = ssl_parse_certificate_verify( ssl ); + break; + + case SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = ssl_parse_change_cipher_spec( ssl ); + break; + + case SSL_CLIENT_FINISHED: + ret = ssl_parse_finished( ssl ); + break; + + /* + * ==> ( NewSessionTicket ) + * ChangeCipherSpec + * Finished + */ + case SSL_SERVER_CHANGE_CIPHER_SPEC: +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( ssl->handshake->new_session_ticket != 0 ) + ret = ssl_write_new_session_ticket( ssl ); + else +#endif + ret = ssl_write_change_cipher_spec( ssl ); + break; + + case SSL_SERVER_FINISHED: + ret = ssl_write_finished( ssl ); + break; + + case SSL_FLUSH_BUFFERS: + SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); + ssl->state = SSL_HANDSHAKE_WRAPUP; + break; + + case SSL_HANDSHAKE_WRAPUP: + ssl_handshake_wrapup( ssl ); + break; + + default: + SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + return( ret ); +} +#endif /* POLARSSL_SSL_SRV_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/ssl_tls.c b/component/common/network/ssl/polarssl-1.3.8/library/ssl_tls.c new file mode 100644 index 0000000..20a220d --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/ssl_tls.c @@ -0,0 +1,4873 @@ +/* + * SSLv3/TLSv1 shared functions + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The SSL 3.0 specification was drafted by Netscape in 1996, + * and became an IETF standard in 1999. + * + * http://wp.netscape.com/eng/ssl3/ + * http://www.ietf.org/rfc/rfc2246.txt + * http://www.ietf.org/rfc/rfc4346.txt + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SSL_TLS_C) + +#include "polarssl/debug.h" +#include "polarssl/ssl.h" + +#if defined(POLARSSL_X509_CRT_PARSE_C) && \ + defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) +#include "polarssl/oid.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +/* + * Convert max_fragment_length codes to length. + * RFC 6066 says: + * enum{ + * 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255) + * } MaxFragmentLength; + * and we add 0 -> extension unused + */ +static unsigned int mfl_code_to_length[SSL_MAX_FRAG_LEN_INVALID] = +{ + SSL_MAX_CONTENT_LEN, /* SSL_MAX_FRAG_LEN_NONE */ + 512, /* SSL_MAX_FRAG_LEN_512 */ + 1024, /* SSL_MAX_FRAG_LEN_1024 */ + 2048, /* SSL_MAX_FRAG_LEN_2048 */ + 4096, /* SSL_MAX_FRAG_LEN_4096 */ +}; +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +static int ssl_session_copy( ssl_session *dst, const ssl_session *src ) +{ + ssl_session_free( dst ); + memcpy( dst, src, sizeof( ssl_session ) ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + if( src->peer_cert != NULL ) + { + int ret; + + dst->peer_cert = (x509_crt *) polarssl_malloc( sizeof(x509_crt) ); + if( dst->peer_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + x509_crt_init( dst->peer_cert ); + + if( ( ret = x509_crt_parse_der( dst->peer_cert, src->peer_cert->raw.p, + src->peer_cert->raw.len ) ) != 0 ) + { + polarssl_free( dst->peer_cert ); + dst->peer_cert = NULL; + return( ret ); + } + } +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( src->ticket != NULL ) + { + dst->ticket = (unsigned char *) polarssl_malloc( src->ticket_len ); + if( dst->ticket == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memcpy( dst->ticket, src->ticket, src->ticket_len ); + } +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + + return( 0 ); +} + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) +int (*ssl_hw_record_init)( ssl_context *ssl, + const unsigned char *key_enc, const unsigned char *key_dec, + size_t keylen, + const unsigned char *iv_enc, const unsigned char *iv_dec, + size_t ivlen, + const unsigned char *mac_enc, const unsigned char *mac_dec, + size_t maclen ) = NULL; +int (*ssl_hw_record_activate)( ssl_context *ssl, int direction) = NULL; +int (*ssl_hw_record_reset)( ssl_context *ssl ) = NULL; +int (*ssl_hw_record_write)( ssl_context *ssl ) = NULL; +int (*ssl_hw_record_read)( ssl_context *ssl ) = NULL; +int (*ssl_hw_record_finish)( ssl_context *ssl ) = NULL; +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ + +/* + * Key material generation + */ +#if defined(POLARSSL_SSL_PROTO_SSL3) +static int ssl3_prf( const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen ) +{ + size_t i; + md5_context md5; + sha1_context sha1; + unsigned char padding[16]; + unsigned char sha1sum[20]; + ((void)label); + + md5_init( &md5 ); + sha1_init( &sha1 ); + + /* + * SSLv3: + * block = + * MD5( secret + SHA1( 'A' + secret + random ) ) + + * MD5( secret + SHA1( 'BB' + secret + random ) ) + + * MD5( secret + SHA1( 'CCC' + secret + random ) ) + + * ... + */ + for( i = 0; i < dlen / 16; i++ ) + { + memset( padding, (unsigned char) ('A' + i), 1 + i ); + + sha1_starts( &sha1 ); + sha1_update( &sha1, padding, 1 + i ); + sha1_update( &sha1, secret, slen ); + sha1_update( &sha1, random, rlen ); + sha1_finish( &sha1, sha1sum ); + + md5_starts( &md5 ); + md5_update( &md5, secret, slen ); + md5_update( &md5, sha1sum, 20 ); + md5_finish( &md5, dstbuf + i * 16 ); + } + + md5_free( &md5 ); + sha1_free( &sha1 ); + + polarssl_zeroize( padding, sizeof( padding ) ); + polarssl_zeroize( sha1sum, sizeof( sha1sum ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) +static int tls1_prf( const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen ) +{ + size_t nb, hs; + size_t i, j, k; + const unsigned char *S1, *S2; + unsigned char tmp[128]; + unsigned char h_i[20]; + + if( sizeof( tmp ) < 20 + strlen( label ) + rlen ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + hs = ( slen + 1 ) / 2; + S1 = secret; + S2 = secret + slen - hs; + + nb = strlen( label ); + memcpy( tmp + 20, label, nb ); + memcpy( tmp + 20 + nb, random, rlen ); + nb += rlen; + + /* + * First compute P_md5(secret,label+random)[0..dlen] + */ + md5_hmac( S1, hs, tmp + 20, nb, 4 + tmp ); + + for( i = 0; i < dlen; i += 16 ) + { + md5_hmac( S1, hs, 4 + tmp, 16 + nb, h_i ); + md5_hmac( S1, hs, 4 + tmp, 16, 4 + tmp ); + + k = ( i + 16 > dlen ) ? dlen % 16 : 16; + + for( j = 0; j < k; j++ ) + dstbuf[i + j] = h_i[j]; + } + + /* + * XOR out with P_sha1(secret,label+random)[0..dlen] + */ + sha1_hmac( S2, hs, tmp + 20, nb, tmp ); + + for( i = 0; i < dlen; i += 20 ) + { + sha1_hmac( S2, hs, tmp, 20 + nb, h_i ); + sha1_hmac( S2, hs, tmp, 20, tmp ); + + k = ( i + 20 > dlen ) ? dlen % 20 : 20; + + for( j = 0; j < k; j++ ) + dstbuf[i + j] = (unsigned char)( dstbuf[i + j] ^ h_i[j] ); + } + + polarssl_zeroize( tmp, sizeof( tmp ) ); + polarssl_zeroize( h_i, sizeof( h_i ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_PROTO_TLS1) || POLARSSL_SSL_PROTO_TLS1_1 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) +static int tls_prf_sha256( const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen ) +{ + size_t nb; + size_t i, j, k; + unsigned char tmp[128]; + unsigned char h_i[32]; + + if( sizeof( tmp ) < 32 + strlen( label ) + rlen ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + nb = strlen( label ); + memcpy( tmp + 32, label, nb ); + memcpy( tmp + 32 + nb, random, rlen ); + nb += rlen; + + /* + * Compute P_(secret, label + random)[0..dlen] + */ + sha256_hmac( secret, slen, tmp + 32, nb, tmp, 0 ); + + for( i = 0; i < dlen; i += 32 ) + { + sha256_hmac( secret, slen, tmp, 32 + nb, h_i, 0 ); + sha256_hmac( secret, slen, tmp, 32, tmp, 0 ); + + k = ( i + 32 > dlen ) ? dlen % 32 : 32; + + for( j = 0; j < k; j++ ) + dstbuf[i + j] = h_i[j]; + } + + polarssl_zeroize( tmp, sizeof( tmp ) ); + polarssl_zeroize( h_i, sizeof( h_i ) ); + + return( 0 ); +} +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) +static int tls_prf_sha384( const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen ) +{ + size_t nb; + size_t i, j, k; + unsigned char tmp[128]; + unsigned char h_i[48]; + + if( sizeof( tmp ) < 48 + strlen( label ) + rlen ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + nb = strlen( label ); + memcpy( tmp + 48, label, nb ); + memcpy( tmp + 48 + nb, random, rlen ); + nb += rlen; + + /* + * Compute P_(secret, label + random)[0..dlen] + */ + sha512_hmac( secret, slen, tmp + 48, nb, tmp, 1 ); + + for( i = 0; i < dlen; i += 48 ) + { + sha512_hmac( secret, slen, tmp, 48 + nb, h_i, 1 ); + sha512_hmac( secret, slen, tmp, 48, tmp, 1 ); + + k = ( i + 48 > dlen ) ? dlen % 48 : 48; + + for( j = 0; j < k; j++ ) + dstbuf[i + j] = h_i[j]; + } + + polarssl_zeroize( tmp, sizeof( tmp ) ); + polarssl_zeroize( h_i, sizeof( h_i ) ); + + return( 0 ); +} +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +static void ssl_update_checksum_start( ssl_context *, const unsigned char *, size_t ); + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) +static void ssl_update_checksum_md5sha1( ssl_context *, const unsigned char *, size_t ); +#endif + +#if defined(POLARSSL_SSL_PROTO_SSL3) +static void ssl_calc_verify_ssl( ssl_context *, unsigned char * ); +static void ssl_calc_finished_ssl( ssl_context *, unsigned char *, int ); +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) +static void ssl_calc_verify_tls( ssl_context *, unsigned char * ); +static void ssl_calc_finished_tls( ssl_context *, unsigned char *, int ); +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) +static void ssl_update_checksum_sha256( ssl_context *, const unsigned char *, size_t ); +static void ssl_calc_verify_tls_sha256( ssl_context *,unsigned char * ); +static void ssl_calc_finished_tls_sha256( ssl_context *,unsigned char *, int ); +#endif + +#if defined(POLARSSL_SHA512_C) +static void ssl_update_checksum_sha384( ssl_context *, const unsigned char *, size_t ); +static void ssl_calc_verify_tls_sha384( ssl_context *, unsigned char * ); +static void ssl_calc_finished_tls_sha384( ssl_context *, unsigned char *, int ); +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +int ssl_derive_keys( ssl_context *ssl ) +{ + int ret = 0; + unsigned char tmp[64]; + unsigned char keyblk[256]; + unsigned char *key1; + unsigned char *key2; + unsigned char *mac_enc; + unsigned char *mac_dec; + size_t iv_copy_len; + const cipher_info_t *cipher_info; + const md_info_t *md_info; + + ssl_session *session = ssl->session_negotiate; + ssl_transform *transform = ssl->transform_negotiate; + ssl_handshake_params *handshake = ssl->handshake; + + SSL_DEBUG_MSG( 2, ( "=> derive keys" ) ); + + cipher_info = cipher_info_from_type( transform->ciphersuite_info->cipher ); + if( cipher_info == NULL ) + { + SSL_DEBUG_MSG( 1, ( "cipher info for %d not found", + transform->ciphersuite_info->cipher ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + md_info = md_info_from_type( transform->ciphersuite_info->mac ); + if( md_info == NULL ) + { + SSL_DEBUG_MSG( 1, ( "md info for %d not found", + transform->ciphersuite_info->mac ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + /* + * Set appropriate PRF function and other SSL / TLS / TLS1.2 functions + */ +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + handshake->tls_prf = ssl3_prf; + handshake->calc_verify = ssl_calc_verify_ssl; + handshake->calc_finished = ssl_calc_finished_ssl; + } + else +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ssl->minor_ver < SSL_MINOR_VERSION_3 ) + { + handshake->tls_prf = tls1_prf; + handshake->calc_verify = ssl_calc_verify_tls; + handshake->calc_finished = ssl_calc_finished_tls; + } + else +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA512_C) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 && + transform->ciphersuite_info->mac == POLARSSL_MD_SHA384 ) + { + handshake->tls_prf = tls_prf_sha384; + handshake->calc_verify = ssl_calc_verify_tls_sha384; + handshake->calc_finished = ssl_calc_finished_tls_sha384; + } + else +#endif +#if defined(POLARSSL_SHA256_C) + if( ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + handshake->tls_prf = tls_prf_sha256; + handshake->calc_verify = ssl_calc_verify_tls_sha256; + handshake->calc_finished = ssl_calc_finished_tls_sha256; + } + else +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + /* + * SSLv3: + * master = + * MD5( premaster + SHA1( 'A' + premaster + randbytes ) ) + + * MD5( premaster + SHA1( 'BB' + premaster + randbytes ) ) + + * MD5( premaster + SHA1( 'CCC' + premaster + randbytes ) ) + * + * TLSv1+: + * master = PRF( premaster, "master secret", randbytes )[0..47] + */ + if( handshake->resume == 0 ) + { + SSL_DEBUG_BUF( 3, "premaster secret", handshake->premaster, + handshake->pmslen ); + + handshake->tls_prf( handshake->premaster, handshake->pmslen, + "master secret", + handshake->randbytes, 64, session->master, 48 ); + + polarssl_zeroize( handshake->premaster, sizeof(handshake->premaster) ); + } + else + SSL_DEBUG_MSG( 3, ( "no premaster (session resumed)" ) ); + + /* + * Swap the client and server random values. + */ + memcpy( tmp, handshake->randbytes, 64 ); + memcpy( handshake->randbytes, tmp + 32, 32 ); + memcpy( handshake->randbytes + 32, tmp, 32 ); + polarssl_zeroize( tmp, sizeof( tmp ) ); + + /* + * SSLv3: + * key block = + * MD5( master + SHA1( 'A' + master + randbytes ) ) + + * MD5( master + SHA1( 'BB' + master + randbytes ) ) + + * MD5( master + SHA1( 'CCC' + master + randbytes ) ) + + * MD5( master + SHA1( 'DDDD' + master + randbytes ) ) + + * ... + * + * TLSv1: + * key block = PRF( master, "key expansion", randbytes ) + */ + handshake->tls_prf( session->master, 48, "key expansion", + handshake->randbytes, 64, keyblk, 256 ); + + SSL_DEBUG_MSG( 3, ( "ciphersuite = %s", + ssl_get_ciphersuite_name( session->ciphersuite ) ) ); + SSL_DEBUG_BUF( 3, "master secret", session->master, 48 ); + SSL_DEBUG_BUF( 4, "random bytes", handshake->randbytes, 64 ); + SSL_DEBUG_BUF( 4, "key block", keyblk, 256 ); + + polarssl_zeroize( handshake->randbytes, sizeof( handshake->randbytes ) ); + + /* + * Determine the appropriate key, IV and MAC length. + */ + + transform->keylen = cipher_info->key_length / 8; + + if( cipher_info->mode == POLARSSL_MODE_GCM || + cipher_info->mode == POLARSSL_MODE_CCM ) + { + transform->maclen = 0; + + transform->ivlen = 12; + transform->fixed_ivlen = 4; + + /* Minimum length is expicit IV + tag */ + transform->minlen = transform->ivlen - transform->fixed_ivlen + + ( transform->ciphersuite_info->flags & + POLARSSL_CIPHERSUITE_SHORT_TAG ? 8 : 16 ); + } + else + { + int ret; + + /* Initialize HMAC contexts */ + if( ( ret = md_init_ctx( &transform->md_ctx_enc, md_info ) ) != 0 || + ( ret = md_init_ctx( &transform->md_ctx_dec, md_info ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "md_init_ctx", ret ); + return( ret ); + } + + /* Get MAC length */ + transform->maclen = md_get_size( md_info ); + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + /* + * If HMAC is to be truncated, we shall keep the leftmost bytes, + * (rfc 6066 page 13 or rfc 2104 section 4), + * so we only need to adjust the length here. + */ + if( session->trunc_hmac == SSL_TRUNC_HMAC_ENABLED ) + transform->maclen = SSL_TRUNCATED_HMAC_LEN; +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + + /* IV length */ + transform->ivlen = cipher_info->iv_size; + + /* Minimum length */ + if( cipher_info->mode == POLARSSL_MODE_STREAM ) + transform->minlen = transform->maclen; + else + { + /* + * GenericBlockCipher: + * first multiple of blocklen greater than maclen + * + IV except for SSL3 and TLS 1.0 + */ + transform->minlen = transform->maclen + + cipher_info->block_size + - transform->maclen % cipher_info->block_size; + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 || + ssl->minor_ver == SSL_MINOR_VERSION_1 ) + ; /* No need to adjust minlen */ + else +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver == SSL_MINOR_VERSION_2 || + ssl->minor_ver == SSL_MINOR_VERSION_3 ) + { + transform->minlen += transform->ivlen; + } + else +#endif + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + } + + SSL_DEBUG_MSG( 3, ( "keylen: %d, minlen: %d, ivlen: %d, maclen: %d", + transform->keylen, transform->minlen, transform->ivlen, + transform->maclen ) ); + + /* + * Finally setup the cipher contexts, IVs and MAC secrets. + */ + if( ssl->endpoint == SSL_IS_CLIENT ) + { + key1 = keyblk + transform->maclen * 2; + key2 = keyblk + transform->maclen * 2 + transform->keylen; + + mac_enc = keyblk; + mac_dec = keyblk + transform->maclen; + + /* + * This is not used in TLS v1.1. + */ + iv_copy_len = ( transform->fixed_ivlen ) ? + transform->fixed_ivlen : transform->ivlen; + memcpy( transform->iv_enc, key2 + transform->keylen, iv_copy_len ); + memcpy( transform->iv_dec, key2 + transform->keylen + iv_copy_len, + iv_copy_len ); + } + else + { + key1 = keyblk + transform->maclen * 2 + transform->keylen; + key2 = keyblk + transform->maclen * 2; + + mac_enc = keyblk + transform->maclen; + mac_dec = keyblk; + + /* + * This is not used in TLS v1.1. + */ + iv_copy_len = ( transform->fixed_ivlen ) ? + transform->fixed_ivlen : transform->ivlen; + memcpy( transform->iv_dec, key1 + transform->keylen, iv_copy_len ); + memcpy( transform->iv_enc, key1 + transform->keylen + iv_copy_len, + iv_copy_len ); + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + if( transform->maclen > sizeof transform->mac_enc ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + memcpy( transform->mac_enc, mac_enc, transform->maclen ); + memcpy( transform->mac_dec, mac_dec, transform->maclen ); + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver >= SSL_MINOR_VERSION_1 ) + { + md_hmac_starts( &transform->md_ctx_enc, mac_enc, transform->maclen ); + md_hmac_starts( &transform->md_ctx_dec, mac_dec, transform->maclen ); + } + else +#endif + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_init != NULL ) + { + int ret = 0; + + SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_init()" ) ); + + if( ( ret = ssl_hw_record_init( ssl, key1, key2, transform->keylen, + transform->iv_enc, transform->iv_dec, + iv_copy_len, + mac_enc, mac_dec, + transform->maclen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_init", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + } +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ + + if( ( ret = cipher_init_ctx( &transform->cipher_ctx_enc, + cipher_info ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_init_ctx", ret ); + return( ret ); + } + + if( ( ret = cipher_init_ctx( &transform->cipher_ctx_dec, + cipher_info ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_init_ctx", ret ); + return( ret ); + } + + if( ( ret = cipher_setkey( &transform->cipher_ctx_enc, key1, + cipher_info->key_length, + POLARSSL_ENCRYPT ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_setkey", ret ); + return( ret ); + } + + if( ( ret = cipher_setkey( &transform->cipher_ctx_dec, key2, + cipher_info->key_length, + POLARSSL_DECRYPT ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_setkey", ret ); + return( ret ); + } + +#if defined(POLARSSL_CIPHER_MODE_CBC) + if( cipher_info->mode == POLARSSL_MODE_CBC ) + { + if( ( ret = cipher_set_padding_mode( &transform->cipher_ctx_enc, + POLARSSL_PADDING_NONE ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_set_padding_mode", ret ); + return( ret ); + } + + if( ( ret = cipher_set_padding_mode( &transform->cipher_ctx_dec, + POLARSSL_PADDING_NONE ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_set_padding_mode", ret ); + return( ret ); + } + } +#endif /* POLARSSL_CIPHER_MODE_CBC */ + + polarssl_zeroize( keyblk, sizeof( keyblk ) ); + +#if defined(POLARSSL_ZLIB_SUPPORT) + // Initialize compression + // + if( session->compression == SSL_COMPRESS_DEFLATE ) + { + if( ssl->compress_buf == NULL ) + { + SSL_DEBUG_MSG( 3, ( "Allocating compression buffer" ) ); + ssl->compress_buf = polarssl_malloc( SSL_BUFFER_LEN ); + if( ssl->compress_buf == NULL ) + { + SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", + SSL_BUFFER_LEN ) ); + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + } + + SSL_DEBUG_MSG( 3, ( "Initializing zlib states" ) ); + + memset( &transform->ctx_deflate, 0, sizeof( transform->ctx_deflate ) ); + memset( &transform->ctx_inflate, 0, sizeof( transform->ctx_inflate ) ); + + if( deflateInit( &transform->ctx_deflate, + Z_DEFAULT_COMPRESSION ) != Z_OK || + inflateInit( &transform->ctx_inflate ) != Z_OK ) + { + SSL_DEBUG_MSG( 1, ( "Failed to initialize compression" ) ); + return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); + } + } +#endif /* POLARSSL_ZLIB_SUPPORT */ + + SSL_DEBUG_MSG( 2, ( "<= derive keys" ) ); + + return( 0 ); +} + +#if defined(POLARSSL_SSL_PROTO_SSL3) +void ssl_calc_verify_ssl( ssl_context *ssl, unsigned char hash[36] ) +{ + md5_context md5; + sha1_context sha1; + unsigned char pad_1[48]; + unsigned char pad_2[48]; + + SSL_DEBUG_MSG( 2, ( "=> calc verify ssl" ) ); + + memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); + memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); + + memset( pad_1, 0x36, 48 ); + memset( pad_2, 0x5C, 48 ); + + md5_update( &md5, ssl->session_negotiate->master, 48 ); + md5_update( &md5, pad_1, 48 ); + md5_finish( &md5, hash ); + + md5_starts( &md5 ); + md5_update( &md5, ssl->session_negotiate->master, 48 ); + md5_update( &md5, pad_2, 48 ); + md5_update( &md5, hash, 16 ); + md5_finish( &md5, hash ); + + sha1_update( &sha1, ssl->session_negotiate->master, 48 ); + sha1_update( &sha1, pad_1, 40 ); + sha1_finish( &sha1, hash + 16 ); + + sha1_starts( &sha1 ); + sha1_update( &sha1, ssl->session_negotiate->master, 48 ); + sha1_update( &sha1, pad_2, 40 ); + sha1_update( &sha1, hash + 16, 20 ); + sha1_finish( &sha1, hash + 16 ); + + SSL_DEBUG_BUF( 3, "calculated verify result", hash, 36 ); + SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); + + md5_free( &md5 ); + sha1_free( &sha1 ); + + return; +} +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) +void ssl_calc_verify_tls( ssl_context *ssl, unsigned char hash[36] ) +{ + md5_context md5; + sha1_context sha1; + + SSL_DEBUG_MSG( 2, ( "=> calc verify tls" ) ); + + memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); + memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); + + md5_finish( &md5, hash ); + sha1_finish( &sha1, hash + 16 ); + + SSL_DEBUG_BUF( 3, "calculated verify result", hash, 36 ); + SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); + + md5_free( &md5 ); + sha1_free( &sha1 ); + + return; +} +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) +void ssl_calc_verify_tls_sha256( ssl_context *ssl, unsigned char hash[32] ) +{ + sha256_context sha256; + + SSL_DEBUG_MSG( 2, ( "=> calc verify sha256" ) ); + + memcpy( &sha256, &ssl->handshake->fin_sha256, sizeof(sha256_context) ); + sha256_finish( &sha256, hash ); + + SSL_DEBUG_BUF( 3, "calculated verify result", hash, 32 ); + SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); + + sha256_free( &sha256 ); + + return; +} +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) +void ssl_calc_verify_tls_sha384( ssl_context *ssl, unsigned char hash[48] ) +{ + sha512_context sha512; + + SSL_DEBUG_MSG( 2, ( "=> calc verify sha384" ) ); + + memcpy( &sha512, &ssl->handshake->fin_sha512, sizeof(sha512_context) ); + sha512_finish( &sha512, hash ); + + SSL_DEBUG_BUF( 3, "calculated verify result", hash, 48 ); + SSL_DEBUG_MSG( 2, ( "<= calc verify" ) ); + + sha512_free( &sha512 ); + + return; +} +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +int ssl_psk_derive_premaster( ssl_context *ssl, key_exchange_type_t key_ex ) +{ + unsigned char *p = ssl->handshake->premaster; + unsigned char *end = p + sizeof( ssl->handshake->premaster ); + + /* + * PMS = struct { + * opaque other_secret<0..2^16-1>; + * opaque psk<0..2^16-1>; + * }; + * with "other_secret" depending on the particular key exchange + */ +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) + if( key_ex == POLARSSL_KEY_EXCHANGE_PSK ) + { + if( end - p < 2 + (int) ssl->psk_len ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + *(p++) = (unsigned char)( ssl->psk_len >> 8 ); + *(p++) = (unsigned char)( ssl->psk_len ); + p += ssl->psk_len; + } + else +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + if( key_ex == POLARSSL_KEY_EXCHANGE_RSA_PSK ) + { + /* + * other_secret already set by the ClientKeyExchange message, + * and is 48 bytes long + */ + *p++ = 0; + *p++ = 48; + p += 48; + } + else +#endif /* POLARSSL_KEY_EXCHANGE_RSA_PKS_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + if( key_ex == POLARSSL_KEY_EXCHANGE_DHE_PSK ) + { + int ret; + size_t len = end - ( p + 2 ); + + /* Write length only when we know the actual value */ + if( ( ret = dhm_calc_secret( &ssl->handshake->dhm_ctx, + p + 2, &len, + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "dhm_calc_secret", ret ); + return( ret ); + } + *(p++) = (unsigned char)( len >> 8 ); + *(p++) = (unsigned char)( len ); + p += len; + + SSL_DEBUG_MPI( 3, "DHM: K ", &ssl->handshake->dhm_ctx.K ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if( key_ex == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + int ret; + size_t zlen; + + if( ( ret = ecdh_calc_secret( &ssl->handshake->ecdh_ctx, &zlen, + p + 2, end - ( p + 2 ), + ssl->f_rng, ssl->p_rng ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ecdh_calc_secret", ret ); + return( ret ); + } + + *(p++) = (unsigned char)( zlen >> 8 ); + *(p++) = (unsigned char)( zlen ); + p += zlen; + + SSL_DEBUG_MPI( 3, "ECDH: z", &ssl->handshake->ecdh_ctx.z ); + } + else +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + /* opaque psk<0..2^16-1>; */ + if( end - p < 2 + (int) ssl->psk_len ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + *(p++) = (unsigned char)( ssl->psk_len >> 8 ); + *(p++) = (unsigned char)( ssl->psk_len ); + memcpy( p, ssl->psk, ssl->psk_len ); + p += ssl->psk_len; + + ssl->handshake->pmslen = p - ssl->handshake->premaster; + + return( 0 ); +} +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#if defined(POLARSSL_SSL_PROTO_SSL3) +/* + * SSLv3.0 MAC functions + */ +static void ssl_mac( md_context_t *md_ctx, unsigned char *secret, + unsigned char *buf, size_t len, + unsigned char *ctr, int type ) +{ + unsigned char header[11]; + unsigned char padding[48]; + int padlen = 0; + int md_size = md_get_size( md_ctx->md_info ); + int md_type = md_get_type( md_ctx->md_info ); + + if( md_type == POLARSSL_MD_MD5 ) + padlen = 48; + else if( md_type == POLARSSL_MD_SHA1 ) + padlen = 40; + else if( md_type == POLARSSL_MD_SHA256 ) + padlen = 32; + else if( md_type == POLARSSL_MD_SHA384 ) + padlen = 16; + + memcpy( header, ctr, 8 ); + header[ 8] = (unsigned char) type; + header[ 9] = (unsigned char)( len >> 8 ); + header[10] = (unsigned char)( len ); + + memset( padding, 0x36, padlen ); + md_starts( md_ctx ); + md_update( md_ctx, secret, md_size ); + md_update( md_ctx, padding, padlen ); + md_update( md_ctx, header, 11 ); + md_update( md_ctx, buf, len ); + md_finish( md_ctx, buf + len ); + + memset( padding, 0x5C, padlen ); + md_starts( md_ctx ); + md_update( md_ctx, secret, md_size ); + md_update( md_ctx, padding, padlen ); + md_update( md_ctx, buf + len, md_size ); + md_finish( md_ctx, buf + len ); +} +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +/* + * Encryption/decryption functions + */ +static int ssl_encrypt_buf( ssl_context *ssl ) +{ + size_t i; + const cipher_mode_t mode = cipher_get_cipher_mode( + &ssl->transform_out->cipher_ctx_enc ); + + SSL_DEBUG_MSG( 2, ( "=> encrypt buf" ) ); + + /* + * Add MAC before encrypt, except for AEAD modes + */ +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ + ( defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) || defined(POLARSSL_DES_C) ) ) + if( mode != POLARSSL_MODE_GCM && + mode != POLARSSL_MODE_CCM ) + { +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + ssl_mac( &ssl->transform_out->md_ctx_enc, + ssl->transform_out->mac_enc, + ssl->out_msg, ssl->out_msglen, + ssl->out_ctr, ssl->out_msgtype ); + } + else +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver >= SSL_MINOR_VERSION_1 ) + { + md_hmac_update( &ssl->transform_out->md_ctx_enc, ssl->out_ctr, 13 ); + md_hmac_update( &ssl->transform_out->md_ctx_enc, + ssl->out_msg, ssl->out_msglen ); + md_hmac_finish( &ssl->transform_out->md_ctx_enc, + ssl->out_msg + ssl->out_msglen ); + md_hmac_reset( &ssl->transform_out->md_ctx_enc ); + } + else +#endif + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_BUF( 4, "computed mac", + ssl->out_msg + ssl->out_msglen, + ssl->transform_out->maclen ); + + ssl->out_msglen += ssl->transform_out->maclen; + } +#endif /* AEAD not the only option */ + + /* + * Encrypt + */ +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) + if( mode == POLARSSL_MODE_STREAM ) + { + int ret; + size_t olen = 0; + + SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " + "including %d bytes of padding", + ssl->out_msglen, 0 ) ); + + SSL_DEBUG_BUF( 4, "before encrypt: output payload", + ssl->out_msg, ssl->out_msglen ); + + if( ( ret = cipher_crypt( &ssl->transform_out->cipher_ctx_enc, + ssl->transform_out->iv_enc, + ssl->transform_out->ivlen, + ssl->out_msg, ssl->out_msglen, + ssl->out_msg, &olen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_crypt", ret ); + return( ret ); + } + + if( ssl->out_msglen != olen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else +#endif /* POLARSSL_ARC4_C || POLARSSL_CIPHER_NULL_CIPHER */ +#if defined(POLARSSL_GCM_C) || defined(POLARSSL_CCM_C) + if( mode == POLARSSL_MODE_GCM || + mode == POLARSSL_MODE_CCM ) + { + int ret; + size_t enc_msglen, olen; + unsigned char *enc_msg; + unsigned char add_data[13]; + unsigned char taglen = ssl->transform_out->ciphersuite_info->flags & + POLARSSL_CIPHERSUITE_SHORT_TAG ? 8 : 16; + + memcpy( add_data, ssl->out_ctr, 8 ); + add_data[8] = ssl->out_msgtype; + add_data[9] = ssl->major_ver; + add_data[10] = ssl->minor_ver; + add_data[11] = ( ssl->out_msglen >> 8 ) & 0xFF; + add_data[12] = ssl->out_msglen & 0xFF; + + SSL_DEBUG_BUF( 4, "additional data used for AEAD", + add_data, 13 ); + + /* + * Generate IV + */ + ret = ssl->f_rng( ssl->p_rng, + ssl->transform_out->iv_enc + ssl->transform_out->fixed_ivlen, + ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); + if( ret != 0 ) + return( ret ); + + memcpy( ssl->out_iv, + ssl->transform_out->iv_enc + ssl->transform_out->fixed_ivlen, + ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); + + SSL_DEBUG_BUF( 4, "IV used", ssl->out_iv, + ssl->transform_out->ivlen - ssl->transform_out->fixed_ivlen ); + + /* + * Fix pointer positions and message length with added IV + */ + enc_msg = ssl->out_msg; + enc_msglen = ssl->out_msglen; + ssl->out_msglen += ssl->transform_out->ivlen - + ssl->transform_out->fixed_ivlen; + + SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " + "including %d bytes of padding", + ssl->out_msglen, 0 ) ); + + SSL_DEBUG_BUF( 4, "before encrypt: output payload", + ssl->out_msg, ssl->out_msglen ); + + /* + * Encrypt and authenticate + */ + if( ( ret = cipher_auth_encrypt( &ssl->transform_out->cipher_ctx_enc, + ssl->transform_out->iv_enc, + ssl->transform_out->ivlen, + add_data, 13, + enc_msg, enc_msglen, + enc_msg, &olen, + enc_msg + enc_msglen, taglen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_auth_encrypt", ret ); + return( ret ); + } + + if( olen != enc_msglen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + ssl->out_msglen += taglen; + + SSL_DEBUG_BUF( 4, "after encrypt: tag", enc_msg + enc_msglen, taglen ); + } + else +#endif /* POLARSSL_GCM_C || POLARSSL_CCM_C */ +#if defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) || defined(POLARSSL_DES_C) ) + if( mode == POLARSSL_MODE_CBC ) + { + int ret; + unsigned char *enc_msg; + size_t enc_msglen, padlen, olen = 0; + + padlen = ssl->transform_out->ivlen - ( ssl->out_msglen + 1 ) % + ssl->transform_out->ivlen; + if( padlen == ssl->transform_out->ivlen ) + padlen = 0; + + for( i = 0; i <= padlen; i++ ) + ssl->out_msg[ssl->out_msglen + i] = (unsigned char) padlen; + + ssl->out_msglen += padlen + 1; + + enc_msglen = ssl->out_msglen; + enc_msg = ssl->out_msg; + +#if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) + /* + * Prepend per-record IV for block cipher in TLS v1.1 and up as per + * Method 1 (6.2.3.2. in RFC4346 and RFC5246) + */ + if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) + { + /* + * Generate IV + */ + int ret = ssl->f_rng( ssl->p_rng, ssl->transform_out->iv_enc, + ssl->transform_out->ivlen ); + if( ret != 0 ) + return( ret ); + + memcpy( ssl->out_iv, ssl->transform_out->iv_enc, + ssl->transform_out->ivlen ); + + /* + * Fix pointer positions and message length with added IV + */ + enc_msg = ssl->out_msg; + enc_msglen = ssl->out_msglen; + ssl->out_msglen += ssl->transform_out->ivlen; + } +#endif /* POLARSSL_SSL_PROTO_TLS1_1 || POLARSSL_SSL_PROTO_TLS1_2 */ + + SSL_DEBUG_MSG( 3, ( "before encrypt: msglen = %d, " + "including %d bytes of IV and %d bytes of padding", + ssl->out_msglen, ssl->transform_out->ivlen, + padlen + 1 ) ); + + SSL_DEBUG_BUF( 4, "before encrypt: output payload", + ssl->out_iv, ssl->out_msglen ); + + if( ( ret = cipher_crypt( &ssl->transform_out->cipher_ctx_enc, + ssl->transform_out->iv_enc, + ssl->transform_out->ivlen, + enc_msg, enc_msglen, + enc_msg, &olen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_crypt", ret ); + return( ret ); + } + + if( enc_msglen != olen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) + if( ssl->minor_ver < SSL_MINOR_VERSION_2 ) + { + /* + * Save IV in SSL3 and TLS1 + */ + memcpy( ssl->transform_out->iv_enc, + ssl->transform_out->cipher_ctx_enc.iv, + ssl->transform_out->ivlen ); + } +#endif + } + else +#endif /* POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_CAMELLIA_C ) */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + for( i = 8; i > 0; i-- ) + if( ++ssl->out_ctr[i - 1] != 0 ) + break; + + /* The loops goes to its end iff the counter is wrapping */ + if( i == 0 ) + { + SSL_DEBUG_MSG( 1, ( "outgoing message counter would wrap" ) ); + return( POLARSSL_ERR_SSL_COUNTER_WRAPPING ); + } + + SSL_DEBUG_MSG( 2, ( "<= encrypt buf" ) ); + + return( 0 ); +} + +#define POLARSSL_SSL_MAX_MAC_SIZE 48 + +static int ssl_decrypt_buf( ssl_context *ssl ) +{ + size_t i; + const cipher_mode_t mode = cipher_get_cipher_mode( + &ssl->transform_in->cipher_ctx_dec ); +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ + ( defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) || defined(POLARSSL_DES_C) ) ) + size_t padlen = 0, correct = 1; +#endif + + SSL_DEBUG_MSG( 2, ( "=> decrypt buf" ) ); + + if( ssl->in_msglen < ssl->transform_in->minlen ) + { + SSL_DEBUG_MSG( 1, ( "in_msglen (%d) < minlen (%d)", + ssl->in_msglen, ssl->transform_in->minlen ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) + if( mode == POLARSSL_MODE_STREAM ) + { + int ret; + size_t olen = 0; + + padlen = 0; + + if( ( ret = cipher_crypt( &ssl->transform_in->cipher_ctx_dec, + ssl->transform_in->iv_dec, + ssl->transform_in->ivlen, + ssl->in_msg, ssl->in_msglen, + ssl->in_msg, &olen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_crypt", ret ); + return( ret ); + } + + if( ssl->in_msglen != olen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else +#endif /* POLARSSL_ARC4_C || POLARSSL_CIPHER_NULL_CIPHER */ +#if defined(POLARSSL_GCM_C) || defined(POLARSSL_CCM_C) + if( mode == POLARSSL_MODE_GCM || + mode == POLARSSL_MODE_CCM ) + { + int ret; + size_t dec_msglen, olen; + unsigned char *dec_msg; + unsigned char *dec_msg_result; + unsigned char add_data[13]; + unsigned char taglen = ssl->transform_in->ciphersuite_info->flags & + POLARSSL_CIPHERSUITE_SHORT_TAG ? 8 : 16; + unsigned char explicit_iv_len = ssl->transform_in->ivlen - + ssl->transform_in->fixed_ivlen; + + if( ssl->in_msglen < explicit_iv_len + taglen ) + { + SSL_DEBUG_MSG( 1, ( "msglen (%d) < explicit_iv_len (%d) " + "+ taglen (%d)", ssl->in_msglen, + explicit_iv_len, taglen ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + dec_msglen = ssl->in_msglen - explicit_iv_len - taglen; + + dec_msg = ssl->in_msg; + dec_msg_result = ssl->in_msg; + ssl->in_msglen = dec_msglen; + + memcpy( add_data, ssl->in_ctr, 8 ); + add_data[8] = ssl->in_msgtype; + add_data[9] = ssl->major_ver; + add_data[10] = ssl->minor_ver; + add_data[11] = ( ssl->in_msglen >> 8 ) & 0xFF; + add_data[12] = ssl->in_msglen & 0xFF; + + SSL_DEBUG_BUF( 4, "additional data used for AEAD", + add_data, 13 ); + + memcpy( ssl->transform_in->iv_dec + ssl->transform_in->fixed_ivlen, + ssl->in_iv, + ssl->transform_in->ivlen - ssl->transform_in->fixed_ivlen ); + + SSL_DEBUG_BUF( 4, "IV used", ssl->transform_in->iv_dec, + ssl->transform_in->ivlen ); + SSL_DEBUG_BUF( 4, "TAG used", dec_msg + dec_msglen, taglen ); + + /* + * Decrypt and authenticate + */ + if( ( ret = cipher_auth_decrypt( &ssl->transform_in->cipher_ctx_dec, + ssl->transform_in->iv_dec, + ssl->transform_in->ivlen, + add_data, 13, + dec_msg, dec_msglen, + dec_msg_result, &olen, + dec_msg + dec_msglen, taglen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_auth_decrypt", ret ); + + if( ret == POLARSSL_ERR_CIPHER_AUTH_FAILED ) + return( POLARSSL_ERR_SSL_INVALID_MAC ); + + return( ret ); + } + + if( olen != dec_msglen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else +#endif /* POLARSSL_GCM_C || POLARSSL_CCM_C */ +#if defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) || defined(POLARSSL_DES_C) ) + if( mode == POLARSSL_MODE_CBC ) + { + /* + * Decrypt and check the padding + */ + int ret; + unsigned char *dec_msg; + unsigned char *dec_msg_result; + size_t dec_msglen; + size_t minlen = 0; + size_t olen = 0; + + /* + * Check immediate ciphertext sanity + */ + if( ssl->in_msglen % ssl->transform_in->ivlen != 0 ) + { + SSL_DEBUG_MSG( 1, ( "msglen (%d) %% ivlen (%d) != 0", + ssl->in_msglen, ssl->transform_in->ivlen ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + +#if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) + minlen += ssl->transform_in->ivlen; +#endif + + if( ssl->in_msglen < minlen + ssl->transform_in->ivlen || + ssl->in_msglen < minlen + ssl->transform_in->maclen + 1 ) + { + SSL_DEBUG_MSG( 1, ( "msglen (%d) < max( ivlen(%d), maclen (%d) " + "+ 1 ) ( + expl IV )", ssl->in_msglen, + ssl->transform_in->ivlen, + ssl->transform_in->maclen ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + + dec_msglen = ssl->in_msglen; + dec_msg = ssl->in_msg; + dec_msg_result = ssl->in_msg; + +#if defined(POLARSSL_SSL_PROTO_TLS1_1) || defined(POLARSSL_SSL_PROTO_TLS1_2) + /* + * Initialize for prepended IV for block cipher in TLS v1.1 and up + */ + if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) + { + dec_msglen -= ssl->transform_in->ivlen; + ssl->in_msglen -= ssl->transform_in->ivlen; + + for( i = 0; i < ssl->transform_in->ivlen; i++ ) + ssl->transform_in->iv_dec[i] = ssl->in_iv[i]; + } +#endif /* POLARSSL_SSL_PROTO_TLS1_1 || POLARSSL_SSL_PROTO_TLS1_2 */ + + if( ( ret = cipher_crypt( &ssl->transform_in->cipher_ctx_dec, + ssl->transform_in->iv_dec, + ssl->transform_in->ivlen, + dec_msg, dec_msglen, + dec_msg_result, &olen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "cipher_crypt", ret ); + return( ret ); + } + + if( dec_msglen != olen ) + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) + if( ssl->minor_ver < SSL_MINOR_VERSION_2 ) + { + /* + * Save IV in SSL3 and TLS1 + */ + memcpy( ssl->transform_in->iv_dec, + ssl->transform_in->cipher_ctx_dec.iv, + ssl->transform_in->ivlen ); + } +#endif + + padlen = 1 + ssl->in_msg[ssl->in_msglen - 1]; + + if( ssl->in_msglen < ssl->transform_in->maclen + padlen ) + { +#if defined(POLARSSL_SSL_DEBUG_ALL) + SSL_DEBUG_MSG( 1, ( "msglen (%d) < maclen (%d) + padlen (%d)", + ssl->in_msglen, ssl->transform_in->maclen, padlen ) ); +#endif + padlen = 0; + correct = 0; + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + if( padlen > ssl->transform_in->ivlen ) + { +#if defined(POLARSSL_SSL_DEBUG_ALL) + SSL_DEBUG_MSG( 1, ( "bad padding length: is %d, " + "should be no more than %d", + padlen, ssl->transform_in->ivlen ) ); +#endif + correct = 0; + } + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver > SSL_MINOR_VERSION_0 ) + { + /* + * TLSv1+: always check the padding up to the first failure + * and fake check up to 256 bytes of padding + */ + size_t pad_count = 0, real_count = 1; + size_t padding_idx = ssl->in_msglen - padlen - 1; + + /* + * Padding is guaranteed to be incorrect if: + * 1. padlen >= ssl->in_msglen + * + * 2. padding_idx >= SSL_MAX_CONTENT_LEN + + * ssl->transform_in->maclen + * + * In both cases we reset padding_idx to a safe value (0) to + * prevent out-of-buffer reads. + */ + correct &= ( ssl->in_msglen >= padlen + 1 ); + correct &= ( padding_idx < SSL_MAX_CONTENT_LEN + + ssl->transform_in->maclen ); + + padding_idx *= correct; + + for( i = 1; i <= 256; i++ ) + { + real_count &= ( i <= padlen ); + pad_count += real_count * + ( ssl->in_msg[padding_idx + i] == padlen - 1 ); + } + + correct &= ( pad_count == padlen ); /* Only 1 on correct padding */ + +#if defined(POLARSSL_SSL_DEBUG_ALL) + if( padlen > 0 && correct == 0 ) + SSL_DEBUG_MSG( 1, ( "bad padding byte detected" ) ); +#endif + padlen &= correct * 0x1FF; + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || \ + POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else +#endif /* POLARSSL_CIPHER_MODE_CBC && + ( POLARSSL_AES_C || POLARSSL_CAMELLIA_C ) */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_BUF( 4, "raw buffer after decryption", + ssl->in_msg, ssl->in_msglen ); + + /* + * Always compute the MAC (RFC4346, CBCTIME), except for AEAD of course + */ +#if defined(POLARSSL_ARC4_C) || defined(POLARSSL_CIPHER_NULL_CIPHER) || \ + ( defined(POLARSSL_CIPHER_MODE_CBC) && \ + ( defined(POLARSSL_AES_C) || defined(POLARSSL_CAMELLIA_C) || defined(POLARSSL_DES_C) ) ) + if( mode != POLARSSL_MODE_GCM && + mode != POLARSSL_MODE_CCM ) + { + unsigned char tmp[POLARSSL_SSL_MAX_MAC_SIZE]; + + ssl->in_msglen -= ( ssl->transform_in->maclen + padlen ); + + ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 ); + ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen ); + + memcpy( tmp, ssl->in_msg + ssl->in_msglen, ssl->transform_in->maclen ); + +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + ssl_mac( &ssl->transform_in->md_ctx_dec, + ssl->transform_in->mac_dec, + ssl->in_msg, ssl->in_msglen, + ssl->in_ctr, ssl->in_msgtype ); + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver > SSL_MINOR_VERSION_0 ) + { + /* + * Process MAC and always update for padlen afterwards to make + * total time independent of padlen + * + * extra_run compensates MAC check for padlen + * + * Known timing attacks: + * - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) + * + * We use ( ( Lx + 8 ) / 64 ) to handle 'negative Lx' values + * correctly. (We round down instead of up, so -56 is the correct + * value for our calculations instead of -55) + */ + size_t j, extra_run = 0; + extra_run = ( 13 + ssl->in_msglen + padlen + 8 ) / 64 - + ( 13 + ssl->in_msglen + 8 ) / 64; + + extra_run &= correct * 0xFF; + + md_hmac_update( &ssl->transform_in->md_ctx_dec, ssl->in_ctr, 13 ); + md_hmac_update( &ssl->transform_in->md_ctx_dec, ssl->in_msg, + ssl->in_msglen ); + md_hmac_finish( &ssl->transform_in->md_ctx_dec, + ssl->in_msg + ssl->in_msglen ); + for( j = 0; j < extra_run; j++ ) + md_process( &ssl->transform_in->md_ctx_dec, ssl->in_msg ); + + md_hmac_reset( &ssl->transform_in->md_ctx_dec ); + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || \ + POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + + SSL_DEBUG_BUF( 4, "message mac", tmp, ssl->transform_in->maclen ); + SSL_DEBUG_BUF( 4, "computed mac", ssl->in_msg + ssl->in_msglen, + ssl->transform_in->maclen ); + + if( safer_memcmp( tmp, ssl->in_msg + ssl->in_msglen, + ssl->transform_in->maclen ) != 0 ) + { +#if defined(POLARSSL_SSL_DEBUG_ALL) + SSL_DEBUG_MSG( 1, ( "message mac does not match" ) ); +#endif + correct = 0; + } + + /* + * Finally check the correct flag + */ + if( correct == 0 ) + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } +#endif /* AEAD not the only option */ + + if( ssl->in_msglen == 0 ) + { + ssl->nb_zero++; + + /* + * Three or more empty messages may be a DoS attack + * (excessive CPU consumption). + */ + if( ssl->nb_zero > 3 ) + { + SSL_DEBUG_MSG( 1, ( "received four consecutive empty " + "messages, possible DoS attack" ) ); + return( POLARSSL_ERR_SSL_INVALID_MAC ); + } + } + else + ssl->nb_zero = 0; + + for( i = 8; i > 0; i-- ) + if( ++ssl->in_ctr[i - 1] != 0 ) + break; + + /* The loops goes to its end iff the counter is wrapping */ + if( i == 0 ) + { + SSL_DEBUG_MSG( 1, ( "incoming message counter would wrap" ) ); + return( POLARSSL_ERR_SSL_COUNTER_WRAPPING ); + } + + SSL_DEBUG_MSG( 2, ( "<= decrypt buf" ) ); + + return( 0 ); +} + +#if defined(POLARSSL_ZLIB_SUPPORT) +/* + * Compression/decompression functions + */ +static int ssl_compress_buf( ssl_context *ssl ) +{ + int ret; + unsigned char *msg_post = ssl->out_msg; + size_t len_pre = ssl->out_msglen; + unsigned char *msg_pre = ssl->compress_buf; + + SSL_DEBUG_MSG( 2, ( "=> compress buf" ) ); + + if( len_pre == 0 ) + return( 0 ); + + memcpy( msg_pre, ssl->out_msg, len_pre ); + + SSL_DEBUG_MSG( 3, ( "before compression: msglen = %d, ", + ssl->out_msglen ) ); + + SSL_DEBUG_BUF( 4, "before compression: output payload", + ssl->out_msg, ssl->out_msglen ); + + ssl->transform_out->ctx_deflate.next_in = msg_pre; + ssl->transform_out->ctx_deflate.avail_in = len_pre; + ssl->transform_out->ctx_deflate.next_out = msg_post; + ssl->transform_out->ctx_deflate.avail_out = SSL_BUFFER_LEN; + + ret = deflate( &ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH ); + if( ret != Z_OK ) + { + SSL_DEBUG_MSG( 1, ( "failed to perform compression (%d)", ret ) ); + return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); + } + + ssl->out_msglen = SSL_BUFFER_LEN - + ssl->transform_out->ctx_deflate.avail_out; + + SSL_DEBUG_MSG( 3, ( "after compression: msglen = %d, ", + ssl->out_msglen ) ); + + SSL_DEBUG_BUF( 4, "after compression: output payload", + ssl->out_msg, ssl->out_msglen ); + + SSL_DEBUG_MSG( 2, ( "<= compress buf" ) ); + + return( 0 ); +} + +static int ssl_decompress_buf( ssl_context *ssl ) +{ + int ret; + unsigned char *msg_post = ssl->in_msg; + size_t len_pre = ssl->in_msglen; + unsigned char *msg_pre = ssl->compress_buf; + + SSL_DEBUG_MSG( 2, ( "=> decompress buf" ) ); + + if( len_pre == 0 ) + return( 0 ); + + memcpy( msg_pre, ssl->in_msg, len_pre ); + + SSL_DEBUG_MSG( 3, ( "before decompression: msglen = %d, ", + ssl->in_msglen ) ); + + SSL_DEBUG_BUF( 4, "before decompression: input payload", + ssl->in_msg, ssl->in_msglen ); + + ssl->transform_in->ctx_inflate.next_in = msg_pre; + ssl->transform_in->ctx_inflate.avail_in = len_pre; + ssl->transform_in->ctx_inflate.next_out = msg_post; + ssl->transform_in->ctx_inflate.avail_out = SSL_MAX_CONTENT_LEN; + + ret = inflate( &ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH ); + if( ret != Z_OK ) + { + SSL_DEBUG_MSG( 1, ( "failed to perform decompression (%d)", ret ) ); + return( POLARSSL_ERR_SSL_COMPRESSION_FAILED ); + } + + ssl->in_msglen = SSL_MAX_CONTENT_LEN - + ssl->transform_in->ctx_inflate.avail_out; + + SSL_DEBUG_MSG( 3, ( "after decompression: msglen = %d, ", + ssl->in_msglen ) ); + + SSL_DEBUG_BUF( 4, "after decompression: input payload", + ssl->in_msg, ssl->in_msglen ); + + SSL_DEBUG_MSG( 2, ( "<= decompress buf" ) ); + + return( 0 ); +} +#endif /* POLARSSL_ZLIB_SUPPORT */ + +/* + * Fill the input message buffer + */ +int ssl_fetch_input( ssl_context *ssl, size_t nb_want ) +{ + int ret; + size_t len; + + SSL_DEBUG_MSG( 2, ( "=> fetch input" ) ); + + if( nb_want > SSL_BUFFER_LEN - 8 ) + { + SSL_DEBUG_MSG( 1, ( "requesting more data than fits" ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + while( ssl->in_left < nb_want ) + { + len = nb_want - ssl->in_left; + ret = ssl->f_recv( ssl->p_recv, ssl->in_hdr + ssl->in_left, len ); + + SSL_DEBUG_MSG( 2, ( "in_left: %d, nb_want: %d", + ssl->in_left, nb_want ) ); + SSL_DEBUG_RET( 2, "ssl->f_recv", ret ); + + if( ret == 0 ) + return( POLARSSL_ERR_SSL_CONN_EOF ); + + if( ret < 0 ) + return( ret ); + + ssl->in_left += ret; + } + + SSL_DEBUG_MSG( 2, ( "<= fetch input" ) ); + + return( 0 ); +} + +/* + * Flush any data not yet written + */ +int ssl_flush_output( ssl_context *ssl ) +{ + int ret; + unsigned char *buf; + + SSL_DEBUG_MSG( 2, ( "=> flush output" ) ); + + while( ssl->out_left > 0 ) + { + SSL_DEBUG_MSG( 2, ( "message length: %d, out_left: %d", + 5 + ssl->out_msglen, ssl->out_left ) ); + + buf = ssl->out_hdr + 5 + ssl->out_msglen - ssl->out_left; + ret = ssl->f_send( ssl->p_send, buf, ssl->out_left ); + + SSL_DEBUG_RET( 2, "ssl->f_send", ret ); + + if( ret <= 0 ) + return( ret ); + + ssl->out_left -= ret; + } + + SSL_DEBUG_MSG( 2, ( "<= flush output" ) ); + + return( 0 ); +} + +/* + * Record layer functions + */ +int ssl_write_record( ssl_context *ssl ) +{ + int ret, done = 0; + size_t len = ssl->out_msglen; + + SSL_DEBUG_MSG( 2, ( "=> write record" ) ); + + if( ssl->out_msgtype == SSL_MSG_HANDSHAKE ) + { + ssl->out_msg[1] = (unsigned char)( ( len - 4 ) >> 16 ); + ssl->out_msg[2] = (unsigned char)( ( len - 4 ) >> 8 ); + ssl->out_msg[3] = (unsigned char)( ( len - 4 ) ); + + if( ssl->out_msg[0] != SSL_HS_HELLO_REQUEST ) + ssl->handshake->update_checksum( ssl, ssl->out_msg, len ); + } + +#if defined(POLARSSL_ZLIB_SUPPORT) + if( ssl->transform_out != NULL && + ssl->session_out->compression == SSL_COMPRESS_DEFLATE ) + { + if( ( ret = ssl_compress_buf( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_compress_buf", ret ); + return( ret ); + } + + len = ssl->out_msglen; + } +#endif /*POLARSSL_ZLIB_SUPPORT */ + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_write != NULL ) + { + SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_write()" ) ); + + ret = ssl_hw_record_write( ssl ); + if( ret != 0 && ret != POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_write", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + + if( ret == 0 ) + done = 1; + } +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ + if( !done ) + { + ssl->out_hdr[0] = (unsigned char) ssl->out_msgtype; + ssl->out_hdr[1] = (unsigned char) ssl->major_ver; + ssl->out_hdr[2] = (unsigned char) ssl->minor_ver; + ssl->out_hdr[3] = (unsigned char)( len >> 8 ); + ssl->out_hdr[4] = (unsigned char)( len ); + + if( ssl->transform_out != NULL ) + { + if( ( ret = ssl_encrypt_buf( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_encrypt_buf", ret ); + return( ret ); + } + + len = ssl->out_msglen; + ssl->out_hdr[3] = (unsigned char)( len >> 8 ); + ssl->out_hdr[4] = (unsigned char)( len ); + } + + ssl->out_left = 5 + ssl->out_msglen; + + SSL_DEBUG_MSG( 3, ( "output record: msgtype = %d, " + "version = [%d:%d], msglen = %d", + ssl->out_hdr[0], ssl->out_hdr[1], ssl->out_hdr[2], + ( ssl->out_hdr[3] << 8 ) | ssl->out_hdr[4] ) ); + + SSL_DEBUG_BUF( 4, "output record sent to network", + ssl->out_hdr, 5 + ssl->out_msglen ); + } + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write record" ) ); + + return( 0 ); +} + +int ssl_read_record( ssl_context *ssl ) +{ + int ret, done = 0; + + SSL_DEBUG_MSG( 2, ( "=> read record" ) ); + + if( ssl->in_hslen != 0 && + ssl->in_hslen < ssl->in_msglen ) + { + /* + * Get next Handshake message in the current record + */ + ssl->in_msglen -= ssl->in_hslen; + + memmove( ssl->in_msg, ssl->in_msg + ssl->in_hslen, + ssl->in_msglen ); + + ssl->in_hslen = 4; + ssl->in_hslen += ( ssl->in_msg[2] << 8 ) | ssl->in_msg[3]; + + SSL_DEBUG_MSG( 3, ( "handshake message: msglen =" + " %d, type = %d, hslen = %d", + ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen ) ); + + if( ssl->in_msglen < 4 || ssl->in_msg[1] != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->in_msglen < ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->state != SSL_HANDSHAKE_OVER ) + ssl->handshake->update_checksum( ssl, ssl->in_msg, ssl->in_hslen ); + + return( 0 ); + } + + ssl->in_hslen = 0; + + /* + * Read the record header and validate it + */ + if( ( ret = ssl_fetch_input( ssl, 5 ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); + return( ret ); + } + + ssl->in_msgtype = ssl->in_hdr[0]; + ssl->in_msglen = ( ssl->in_hdr[3] << 8 ) | ssl->in_hdr[4]; + + SSL_DEBUG_MSG( 3, ( "input record: msgtype = %d, " + "version = [%d:%d], msglen = %d", + ssl->in_hdr[0], ssl->in_hdr[1], ssl->in_hdr[2], + ( ssl->in_hdr[3] << 8 ) | ssl->in_hdr[4] ) ); + + if( ssl->in_hdr[1] != ssl->major_ver ) + { + SSL_DEBUG_MSG( 1, ( "major version mismatch" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->in_hdr[2] > ssl->max_minor_ver ) + { + SSL_DEBUG_MSG( 1, ( "minor version mismatch" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + /* Sanity check (outer boundaries) */ + if( ssl->in_msglen < 1 || ssl->in_msglen > SSL_BUFFER_LEN - 13 ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + /* + * Make sure the message length is acceptable for the current transform + * and protocol version. + */ + if( ssl->transform_in == NULL ) + { + if( ssl->in_msglen > SSL_MAX_CONTENT_LEN ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + } + else + { + if( ssl->in_msglen < ssl->transform_in->minlen ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 && + ssl->in_msglen > ssl->transform_in->minlen + SSL_MAX_CONTENT_LEN ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + /* + * TLS encrypted messages can have up to 256 bytes of padding + */ + if( ssl->minor_ver >= SSL_MINOR_VERSION_1 && + ssl->in_msglen > ssl->transform_in->minlen + + SSL_MAX_CONTENT_LEN + 256 ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } +#endif + } + + /* + * Read and optionally decrypt the message contents + */ + if( ( ret = ssl_fetch_input( ssl, 5 + ssl->in_msglen ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_fetch_input", ret ); + return( ret ); + } + + SSL_DEBUG_BUF( 4, "input record from network", + ssl->in_hdr, 5 + ssl->in_msglen ); + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_read != NULL ) + { + SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_read()" ) ); + + ret = ssl_hw_record_read( ssl ); + if( ret != 0 && ret != POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_read", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + + if( ret == 0 ) + done = 1; + } +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ + if( !done && ssl->transform_in != NULL ) + { + if( ( ret = ssl_decrypt_buf( ssl ) ) != 0 ) + { +#if defined(POLARSSL_SSL_ALERT_MESSAGES) + if( ret == POLARSSL_ERR_SSL_INVALID_MAC ) + { + ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_BAD_RECORD_MAC ); + } +#endif + SSL_DEBUG_RET( 1, "ssl_decrypt_buf", ret ); + return( ret ); + } + + SSL_DEBUG_BUF( 4, "input payload after decrypt", + ssl->in_msg, ssl->in_msglen ); + + if( ssl->in_msglen > SSL_MAX_CONTENT_LEN ) + { + SSL_DEBUG_MSG( 1, ( "bad message length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + } + +#if defined(POLARSSL_ZLIB_SUPPORT) + if( ssl->transform_in != NULL && + ssl->session_in->compression == SSL_COMPRESS_DEFLATE ) + { + if( ( ret = ssl_decompress_buf( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_decompress_buf", ret ); + return( ret ); + } + + ssl->in_hdr[3] = (unsigned char)( ssl->in_msglen >> 8 ); + ssl->in_hdr[4] = (unsigned char)( ssl->in_msglen ); + } +#endif /* POLARSSL_ZLIB_SUPPORT */ + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE && + ssl->in_msgtype != SSL_MSG_ALERT && + ssl->in_msgtype != SSL_MSG_CHANGE_CIPHER_SPEC && + ssl->in_msgtype != SSL_MSG_APPLICATION_DATA ) + { + SSL_DEBUG_MSG( 1, ( "unknown record type" ) ); + + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_UNEXPECTED_MESSAGE ) ) != 0 ) + { + return( ret ); + } + + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->in_msgtype == SSL_MSG_HANDSHAKE ) + { + ssl->in_hslen = 4; + ssl->in_hslen += ( ssl->in_msg[2] << 8 ) | ssl->in_msg[3]; + + SSL_DEBUG_MSG( 3, ( "handshake message: msglen =" + " %d, type = %d, hslen = %d", + ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen ) ); + + /* + * Additional checks to validate the handshake header + */ + if( ssl->in_msglen < 4 || ssl->in_msg[1] != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->in_msglen < ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad handshake length" ) ); + return( POLARSSL_ERR_SSL_INVALID_RECORD ); + } + + if( ssl->state != SSL_HANDSHAKE_OVER ) + ssl->handshake->update_checksum( ssl, ssl->in_msg, ssl->in_hslen ); + } + + if( ssl->in_msgtype == SSL_MSG_ALERT ) + { + SSL_DEBUG_MSG( 2, ( "got an alert message, type: [%d:%d]", + ssl->in_msg[0], ssl->in_msg[1] ) ); + + /* + * Ignore non-fatal alerts, except close_notify + */ + if( ssl->in_msg[0] == SSL_ALERT_LEVEL_FATAL ) + { + SSL_DEBUG_MSG( 1, ( "is a fatal alert message (msg %d)", + ssl->in_msg[1] ) ); + /** + * Subtract from error code as ssl->in_msg[1] is 7-bit positive + * error identifier. + */ + return( POLARSSL_ERR_SSL_FATAL_ALERT_MESSAGE ); + } + + if( ssl->in_msg[0] == SSL_ALERT_LEVEL_WARNING && + ssl->in_msg[1] == SSL_ALERT_MSG_CLOSE_NOTIFY ) + { + SSL_DEBUG_MSG( 2, ( "is a close notify message" ) ); + return( POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY ); + } + } + + ssl->in_left = 0; + + SSL_DEBUG_MSG( 2, ( "<= read record" ) ); + + return( 0 ); +} + +int ssl_send_fatal_handshake_failure( ssl_context *ssl ) +{ + int ret; + + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_FATAL, + SSL_ALERT_MSG_HANDSHAKE_FAILURE ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} + +int ssl_send_alert_message( ssl_context *ssl, + unsigned char level, + unsigned char message ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> send alert message" ) ); + + ssl->out_msgtype = SSL_MSG_ALERT; + ssl->out_msglen = 2; + ssl->out_msg[0] = level; + ssl->out_msg[1] = message; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= send alert message" ) ); + + return( 0 ); +} + +/* + * Handshake functions + */ +#if !defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \ + !defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +int ssl_write_certificate( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write certificate" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} + +int ssl_parse_certificate( ssl_context *ssl ) +{ + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) ); + ssl->state++; + return( 0 ); + } + + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); +} +#else +int ssl_write_certificate( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + size_t i, n; + const x509_crt *crt; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> write certificate" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) ); + ssl->state++; + return( 0 ); + } + + if( ssl->endpoint == SSL_IS_CLIENT ) + { + if( ssl->client_auth == 0 ) + { + SSL_DEBUG_MSG( 2, ( "<= skip write certificate" ) ); + ssl->state++; + return( 0 ); + } + +#if defined(POLARSSL_SSL_PROTO_SSL3) + /* + * If using SSLv3 and got no cert, send an Alert message + * (otherwise an empty Certificate message will be sent). + */ + if( ssl_own_cert( ssl ) == NULL && + ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + ssl->out_msglen = 2; + ssl->out_msgtype = SSL_MSG_ALERT; + ssl->out_msg[0] = SSL_ALERT_LEVEL_WARNING; + ssl->out_msg[1] = SSL_ALERT_MSG_NO_CERT; + + SSL_DEBUG_MSG( 2, ( "got no certificate to send" ) ); + goto write_msg; + } +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + } + else /* SSL_IS_SERVER */ + { + if( ssl_own_cert( ssl ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "got no certificate to send" ) ); + return( POLARSSL_ERR_SSL_CERTIFICATE_REQUIRED ); + } + } + + SSL_DEBUG_CRT( 3, "own certificate", ssl_own_cert( ssl ) ); + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 6 length of all certs + * 7 . 9 length of cert. 1 + * 10 . n-1 peer certificate + * n . n+2 length of cert. 2 + * n+3 . ... upper level cert, etc. + */ + i = 7; + crt = ssl_own_cert( ssl ); + + while( crt != NULL ) + { + n = crt->raw.len; + if( n > SSL_MAX_CONTENT_LEN - 3 - i ) + { + SSL_DEBUG_MSG( 1, ( "certificate too large, %d > %d", + i + 3 + n, SSL_MAX_CONTENT_LEN ) ); + return( POLARSSL_ERR_SSL_CERTIFICATE_TOO_LARGE ); + } + + ssl->out_msg[i ] = (unsigned char)( n >> 16 ); + ssl->out_msg[i + 1] = (unsigned char)( n >> 8 ); + ssl->out_msg[i + 2] = (unsigned char)( n ); + + i += 3; memcpy( ssl->out_msg + i, crt->raw.p, n ); + i += n; crt = crt->next; + } + + ssl->out_msg[4] = (unsigned char)( ( i - 7 ) >> 16 ); + ssl->out_msg[5] = (unsigned char)( ( i - 7 ) >> 8 ); + ssl->out_msg[6] = (unsigned char)( ( i - 7 ) ); + + ssl->out_msglen = i; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_CERTIFICATE; + +#if defined(POLARSSL_SSL_PROTO_SSL3) +write_msg: +#endif + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write certificate" ) ); + + return( ret ); +} + +int ssl_parse_certificate( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + size_t i, n; + const ssl_ciphersuite_t *ciphersuite_info = ssl->transform_negotiate->ciphersuite_info; + + SSL_DEBUG_MSG( 2, ( "=> parse certificate" ) ); + + if( ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_ECDHE_PSK ) + { + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) ); + ssl->state++; + return( 0 ); + } + + if( ssl->endpoint == SSL_IS_SERVER && + ( ssl->authmode == SSL_VERIFY_NONE || + ciphersuite_info->key_exchange == POLARSSL_KEY_EXCHANGE_RSA_PSK ) ) + { + ssl->session_negotiate->verify_result = BADCERT_SKIP_VERIFY; + SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) ); + ssl->state++; + return( 0 ); + } + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + ssl->state++; + +#if defined(POLARSSL_SSL_PROTO_SSL3) + /* + * Check if the client sent an empty certificate + */ + if( ssl->endpoint == SSL_IS_SERVER && + ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + if( ssl->in_msglen == 2 && + ssl->in_msgtype == SSL_MSG_ALERT && + ssl->in_msg[0] == SSL_ALERT_LEVEL_WARNING && + ssl->in_msg[1] == SSL_ALERT_MSG_NO_CERT ) + { + SSL_DEBUG_MSG( 1, ( "SSLv3 client has no certificate" ) ); + + ssl->session_negotiate->verify_result = BADCERT_MISSING; + if( ssl->authmode == SSL_VERIFY_OPTIONAL ) + return( 0 ); + else + return( POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE ); + } + } +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->endpoint == SSL_IS_SERVER && + ssl->minor_ver != SSL_MINOR_VERSION_0 ) + { + if( ssl->in_hslen == 7 && + ssl->in_msgtype == SSL_MSG_HANDSHAKE && + ssl->in_msg[0] == SSL_HS_CERTIFICATE && + memcmp( ssl->in_msg + 4, "\0\0\0", 3 ) == 0 ) + { + SSL_DEBUG_MSG( 1, ( "TLSv1 client has no certificate" ) ); + + ssl->session_negotiate->verify_result = BADCERT_MISSING; + if( ssl->authmode == SSL_VERIFY_REQUIRED ) + return( POLARSSL_ERR_SSL_NO_CLIENT_CERTIFICATE ); + else + return( 0 ); + } + } +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || \ + POLARSSL_SSL_PROTO_TLS1_2 */ + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + if( ssl->in_msg[0] != SSL_HS_CERTIFICATE || ssl->in_hslen < 10 ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + /* + * Same message structure as in ssl_write_certificate() + */ + n = ( ssl->in_msg[5] << 8 ) | ssl->in_msg[6]; + + if( ssl->in_msg[4] != 0 || ssl->in_hslen != 7 + n ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + /* In case we tried to reuse a session but it failed */ + if( ssl->session_negotiate->peer_cert != NULL ) + { + x509_crt_free( ssl->session_negotiate->peer_cert ); + polarssl_free( ssl->session_negotiate->peer_cert ); + } + + if( ( ssl->session_negotiate->peer_cert = (x509_crt *) polarssl_malloc( + sizeof( x509_crt ) ) ) == NULL ) + { + SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", + sizeof( x509_crt ) ) ); + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + x509_crt_init( ssl->session_negotiate->peer_cert ); + + i = 7; + + while( i < ssl->in_hslen ) + { + if( ssl->in_msg[i] != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + n = ( (unsigned int) ssl->in_msg[i + 1] << 8 ) + | (unsigned int) ssl->in_msg[i + 2]; + i += 3; + + if( n < 128 || i + n > ssl->in_hslen ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + ret = x509_crt_parse_der( ssl->session_negotiate->peer_cert, + ssl->in_msg + i, n ); + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, " x509_crt_parse_der", ret ); + return( ret ); + } + + i += n; + } + + SSL_DEBUG_CRT( 3, "peer certificate", ssl->session_negotiate->peer_cert ); + + /* + * On client, make sure the server cert doesn't change during renego to + * avoid "triple handshake" attack: https://secure-resumption.com/ + */ + if( ssl->endpoint == SSL_IS_CLIENT && + ssl->renegotiation == SSL_RENEGOTIATION ) + { + if( ssl->session->peer_cert == NULL ) + { + SSL_DEBUG_MSG( 1, ( "new server cert during renegotiation" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + + if( ssl->session->peer_cert->raw.len != + ssl->session_negotiate->peer_cert->raw.len || + memcmp( ssl->session->peer_cert->raw.p, + ssl->session_negotiate->peer_cert->raw.p, + ssl->session->peer_cert->raw.len ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "server cert changed during renegotiation" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE ); + } + } + + if( ssl->authmode != SSL_VERIFY_NONE ) + { + if( ssl->ca_chain == NULL ) + { + SSL_DEBUG_MSG( 1, ( "got no CA chain" ) ); + return( POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED ); + } + + /* + * Main check: verify certificate + */ + ret = x509_crt_verify( ssl->session_negotiate->peer_cert, + ssl->ca_chain, ssl->ca_crl, ssl->peer_cn, + &ssl->session_negotiate->verify_result, + ssl->f_vrfy, ssl->p_vrfy ); + + if( ret != 0 ) + { + SSL_DEBUG_RET( 1, "x509_verify_cert", ret ); + } + + /* + * Secondary checks: always done, but change 'ret' only if it was 0 + */ + +#if defined(POLARSSL_SSL_SET_CURVES) + { + pk_context *pk = &ssl->session_negotiate->peer_cert->pk; + + /* If certificate uses an EC key, make sure the curve is OK */ + if( pk_can_do( pk, POLARSSL_PK_ECKEY ) && + ! ssl_curve_is_acceptable( ssl, pk_ec( *pk )->grp.id ) ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate (EC key curve)" ) ); + if( ret == 0 ) + ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE; + } + } +#endif /* POLARSSL_SSL_SET_CURVES */ + + if( ssl_check_cert_usage( ssl->session_negotiate->peer_cert, + ciphersuite_info, + ! ssl->endpoint ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad certificate (usage extensions)" ) ); + if( ret == 0 ) + ret = POLARSSL_ERR_SSL_BAD_HS_CERTIFICATE; + } + + if( ssl->authmode != SSL_VERIFY_REQUIRED ) + ret = 0; + } + + SSL_DEBUG_MSG( 2, ( "<= parse certificate" ) ); + + return( ret ); +} +#endif /* !POLARSSL_KEY_EXCHANGE_RSA_ENABLED + !POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED + !POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED + !POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED + !POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + !POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED + !POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +int ssl_write_change_cipher_spec( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> write change cipher spec" ) ); + + ssl->out_msgtype = SSL_MSG_CHANGE_CIPHER_SPEC; + ssl->out_msglen = 1; + ssl->out_msg[0] = 1; + + ssl->state++; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write change cipher spec" ) ); + + return( 0 ); +} + +int ssl_parse_change_cipher_spec( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> parse change cipher spec" ) ); + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_CHANGE_CIPHER_SPEC ) + { + SSL_DEBUG_MSG( 1, ( "bad change cipher spec message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + if( ssl->in_msglen != 1 || ssl->in_msg[0] != 1 ) + { + SSL_DEBUG_MSG( 1, ( "bad change cipher spec message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_CHANGE_CIPHER_SPEC ); + } + + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse change cipher spec" ) ); + + return( 0 ); +} + +void ssl_optimize_checksum( ssl_context *ssl, + const ssl_ciphersuite_t *ciphersuite_info ) +{ + ((void) ciphersuite_info); + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + if( ssl->minor_ver < SSL_MINOR_VERSION_3 ) + ssl->handshake->update_checksum = ssl_update_checksum_md5sha1; + else +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA512_C) + if( ciphersuite_info->mac == POLARSSL_MD_SHA384 ) + ssl->handshake->update_checksum = ssl_update_checksum_sha384; + else +#endif +#if defined(POLARSSL_SHA256_C) + if( ciphersuite_info->mac != POLARSSL_MD_SHA384 ) + ssl->handshake->update_checksum = ssl_update_checksum_sha256; + else +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return; + } +} + +static void ssl_update_checksum_start( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + md5_update( &ssl->handshake->fin_md5 , buf, len ); + sha1_update( &ssl->handshake->fin_sha1, buf, len ); +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) + sha256_update( &ssl->handshake->fin_sha256, buf, len ); +#endif +#if defined(POLARSSL_SHA512_C) + sha512_update( &ssl->handshake->fin_sha512, buf, len ); +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ +} + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) +static void ssl_update_checksum_md5sha1( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + md5_update( &ssl->handshake->fin_md5 , buf, len ); + sha1_update( &ssl->handshake->fin_sha1, buf, len ); +} +#endif + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) +static void ssl_update_checksum_sha256( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + sha256_update( &ssl->handshake->fin_sha256, buf, len ); +} +#endif + +#if defined(POLARSSL_SHA512_C) +static void ssl_update_checksum_sha384( ssl_context *ssl, + const unsigned char *buf, size_t len ) +{ + sha512_update( &ssl->handshake->fin_sha512, buf, len ); +} +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +#if defined(POLARSSL_SSL_PROTO_SSL3) +static void ssl_calc_finished_ssl( + ssl_context *ssl, unsigned char *buf, int from ) +{ + const char *sender; + md5_context md5; + sha1_context sha1; + + unsigned char padbuf[48]; + unsigned char md5sum[16]; + unsigned char sha1sum[20]; + + ssl_session *session = ssl->session_negotiate; + if( !session ) + session = ssl->session; + + SSL_DEBUG_MSG( 2, ( "=> calc finished ssl" ) ); + + memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); + memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); + + /* + * SSLv3: + * hash = + * MD5( master + pad2 + + * MD5( handshake + sender + master + pad1 ) ) + * + SHA1( master + pad2 + + * SHA1( handshake + sender + master + pad1 ) ) + */ + +#if !defined(POLARSSL_MD5_ALT) + SSL_DEBUG_BUF( 4, "finished md5 state", (unsigned char *) + md5.state, sizeof( md5.state ) ); +#endif + +#if !defined(POLARSSL_SHA1_ALT) + SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *) + sha1.state, sizeof( sha1.state ) ); +#endif + + sender = ( from == SSL_IS_CLIENT ) ? "CLNT" + : "SRVR"; + + memset( padbuf, 0x36, 48 ); + + md5_update( &md5, (const unsigned char *) sender, 4 ); + md5_update( &md5, session->master, 48 ); + md5_update( &md5, padbuf, 48 ); + md5_finish( &md5, md5sum ); + + sha1_update( &sha1, (const unsigned char *) sender, 4 ); + sha1_update( &sha1, session->master, 48 ); + sha1_update( &sha1, padbuf, 40 ); + sha1_finish( &sha1, sha1sum ); + + memset( padbuf, 0x5C, 48 ); + + md5_starts( &md5 ); + md5_update( &md5, session->master, 48 ); + md5_update( &md5, padbuf, 48 ); + md5_update( &md5, md5sum, 16 ); + md5_finish( &md5, buf ); + + sha1_starts( &sha1 ); + sha1_update( &sha1, session->master, 48 ); + sha1_update( &sha1, padbuf , 40 ); + sha1_update( &sha1, sha1sum, 20 ); + sha1_finish( &sha1, buf + 16 ); + + SSL_DEBUG_BUF( 3, "calc finished result", buf, 36 ); + + md5_free( &md5 ); + sha1_free( &sha1 ); + + polarssl_zeroize( padbuf, sizeof( padbuf ) ); + polarssl_zeroize( md5sum, sizeof( md5sum ) ); + polarssl_zeroize( sha1sum, sizeof( sha1sum ) ); + + SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); +} +#endif /* POLARSSL_SSL_PROTO_SSL3 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) +static void ssl_calc_finished_tls( + ssl_context *ssl, unsigned char *buf, int from ) +{ + int len = 12; + const char *sender; + md5_context md5; + sha1_context sha1; + unsigned char padbuf[36]; + + ssl_session *session = ssl->session_negotiate; + if( !session ) + session = ssl->session; + + SSL_DEBUG_MSG( 2, ( "=> calc finished tls" ) ); + + memcpy( &md5 , &ssl->handshake->fin_md5 , sizeof(md5_context) ); + memcpy( &sha1, &ssl->handshake->fin_sha1, sizeof(sha1_context) ); + + /* + * TLSv1: + * hash = PRF( master, finished_label, + * MD5( handshake ) + SHA1( handshake ) )[0..11] + */ + +#if !defined(POLARSSL_MD5_ALT) + SSL_DEBUG_BUF( 4, "finished md5 state", (unsigned char *) + md5.state, sizeof( md5.state ) ); +#endif + +#if !defined(POLARSSL_SHA1_ALT) + SSL_DEBUG_BUF( 4, "finished sha1 state", (unsigned char *) + sha1.state, sizeof( sha1.state ) ); +#endif + + sender = ( from == SSL_IS_CLIENT ) + ? "client finished" + : "server finished"; + + md5_finish( &md5, padbuf ); + sha1_finish( &sha1, padbuf + 16 ); + + ssl->handshake->tls_prf( session->master, 48, sender, + padbuf, 36, buf, len ); + + SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); + + md5_free( &md5 ); + sha1_free( &sha1 ); + + polarssl_zeroize( padbuf, sizeof( padbuf ) ); + + SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); +} +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 */ + +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) +static void ssl_calc_finished_tls_sha256( + ssl_context *ssl, unsigned char *buf, int from ) +{ + int len = 12; + const char *sender; + sha256_context sha256; + unsigned char padbuf[32]; + + ssl_session *session = ssl->session_negotiate; + if( !session ) + session = ssl->session; + + SSL_DEBUG_MSG( 2, ( "=> calc finished tls sha256" ) ); + + memcpy( &sha256, &ssl->handshake->fin_sha256, sizeof(sha256_context) ); + + /* + * TLSv1.2: + * hash = PRF( master, finished_label, + * Hash( handshake ) )[0.11] + */ + +#if !defined(POLARSSL_SHA256_ALT) + SSL_DEBUG_BUF( 4, "finished sha2 state", (unsigned char *) + sha256.state, sizeof( sha256.state ) ); +#endif + + sender = ( from == SSL_IS_CLIENT ) + ? "client finished" + : "server finished"; + + sha256_finish( &sha256, padbuf ); + + ssl->handshake->tls_prf( session->master, 48, sender, + padbuf, 32, buf, len ); + + SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); + + sha256_free( &sha256 ); + + polarssl_zeroize( padbuf, sizeof( padbuf ) ); + + SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); +} +#endif /* POLARSSL_SHA256_C */ + +#if defined(POLARSSL_SHA512_C) +static void ssl_calc_finished_tls_sha384( + ssl_context *ssl, unsigned char *buf, int from ) +{ + int len = 12; + const char *sender; + sha512_context sha512; + unsigned char padbuf[48]; + + ssl_session *session = ssl->session_negotiate; + if( !session ) + session = ssl->session; + + SSL_DEBUG_MSG( 2, ( "=> calc finished tls sha384" ) ); + + memcpy( &sha512, &ssl->handshake->fin_sha512, sizeof(sha512_context) ); + + /* + * TLSv1.2: + * hash = PRF( master, finished_label, + * Hash( handshake ) )[0.11] + */ + +#if !defined(POLARSSL_SHA512_ALT) + SSL_DEBUG_BUF( 4, "finished sha512 state", (unsigned char *) + sha512.state, sizeof( sha512.state ) ); +#endif + + sender = ( from == SSL_IS_CLIENT ) + ? "client finished" + : "server finished"; + + sha512_finish( &sha512, padbuf ); + + ssl->handshake->tls_prf( session->master, 48, sender, + padbuf, 48, buf, len ); + + SSL_DEBUG_BUF( 3, "calc finished result", buf, len ); + + sha512_free( &sha512 ); + + polarssl_zeroize( padbuf, sizeof( padbuf ) ); + + SSL_DEBUG_MSG( 2, ( "<= calc finished" ) ); +} +#endif /* POLARSSL_SHA512_C */ +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + +void ssl_handshake_wrapup( ssl_context *ssl ) +{ + int resume = ssl->handshake->resume; + + SSL_DEBUG_MSG( 3, ( "=> handshake wrapup" ) ); + + /* + * Free our handshake params + */ + ssl_handshake_free( ssl->handshake ); + polarssl_free( ssl->handshake ); + ssl->handshake = NULL; + + if( ssl->renegotiation == SSL_RENEGOTIATION ) + { + ssl->renegotiation = SSL_RENEGOTIATION_DONE; + ssl->renego_records_seen = 0; + } + + /* + * Switch in our now active transform context + */ + if( ssl->transform ) + { + ssl_transform_free( ssl->transform ); + polarssl_free( ssl->transform ); + } + ssl->transform = ssl->transform_negotiate; + ssl->transform_negotiate = NULL; + + if( ssl->session ) + { + ssl_session_free( ssl->session ); + polarssl_free( ssl->session ); + } + ssl->session = ssl->session_negotiate; + ssl->session_negotiate = NULL; + + /* + * Add cache entry + */ + if( ssl->f_set_cache != NULL && + ssl->session->length != 0 && + resume == 0 ) + { + if( ssl->f_set_cache( ssl->p_set_cache, ssl->session ) != 0 ) + SSL_DEBUG_MSG( 1, ( "cache did not store session" ) ); + } + + ssl->state++; + + SSL_DEBUG_MSG( 3, ( "<= handshake wrapup" ) ); +} + +int ssl_write_finished( ssl_context *ssl ) +{ + int ret, hash_len; + + SSL_DEBUG_MSG( 2, ( "=> write finished" ) ); + + /* + * Set the out_msg pointer to the correct location based on IV length + */ + if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) + { + ssl->out_msg = ssl->out_iv + ssl->transform_negotiate->ivlen - + ssl->transform_negotiate->fixed_ivlen; + } + else + ssl->out_msg = ssl->out_iv; + + ssl->handshake->calc_finished( ssl, ssl->out_msg + 4, ssl->endpoint ); + + // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) + hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12; + + ssl->verify_data_len = hash_len; + memcpy( ssl->own_verify_data, ssl->out_msg + 4, hash_len ); + + ssl->out_msglen = 4 + hash_len; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_FINISHED; + + /* + * In case of session resuming, invert the client and server + * ChangeCipherSpec messages order. + */ + if( ssl->handshake->resume != 0 ) + { + if( ssl->endpoint == SSL_IS_CLIENT ) + ssl->state = SSL_HANDSHAKE_WRAPUP; + else + ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; + } + else + ssl->state++; + + /* + * Switch to our negotiated transform and session parameters for outbound + * data. + */ + SSL_DEBUG_MSG( 3, ( "switching to new transform spec for outbound data" ) ); + ssl->transform_out = ssl->transform_negotiate; + ssl->session_out = ssl->session_negotiate; + memset( ssl->out_ctr, 0, 8 ); + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_activate != NULL ) + { + if( ( ret = ssl_hw_record_activate( ssl, SSL_CHANNEL_OUTBOUND ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_activate", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + } +#endif + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= write finished" ) ); + + return( 0 ); +} + +int ssl_parse_finished( ssl_context *ssl ) +{ + int ret; + unsigned int hash_len; + unsigned char buf[36]; + + SSL_DEBUG_MSG( 2, ( "=> parse finished" ) ); + + ssl->handshake->calc_finished( ssl, buf, ssl->endpoint ^ 1 ); + + /* + * Switch to our negotiated transform and session parameters for inbound + * data. + */ + SSL_DEBUG_MSG( 3, ( "switching to new transform spec for inbound data" ) ); + ssl->transform_in = ssl->transform_negotiate; + ssl->session_in = ssl->session_negotiate; + memset( ssl->in_ctr, 0, 8 ); + + /* + * Set the in_msg pointer to the correct location based on IV length + */ + if( ssl->minor_ver >= SSL_MINOR_VERSION_2 ) + { + ssl->in_msg = ssl->in_iv + ssl->transform_negotiate->ivlen - + ssl->transform_negotiate->fixed_ivlen; + } + else + ssl->in_msg = ssl->in_iv; + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_activate != NULL ) + { + if( ( ret = ssl_hw_record_activate( ssl, SSL_CHANNEL_INBOUND ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_activate", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + } +#endif + + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msgtype != SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + // TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) + hash_len = ( ssl->minor_ver == SSL_MINOR_VERSION_0 ) ? 36 : 12; + + if( ssl->in_msg[0] != SSL_HS_FINISHED || + ssl->in_hslen != 4 + hash_len ) + { + SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_FINISHED ); + } + + if( safer_memcmp( ssl->in_msg + 4, buf, hash_len ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "bad finished message" ) ); + return( POLARSSL_ERR_SSL_BAD_HS_FINISHED ); + } + + ssl->verify_data_len = hash_len; + memcpy( ssl->peer_verify_data, buf, hash_len ); + + if( ssl->handshake->resume != 0 ) + { + if( ssl->endpoint == SSL_IS_CLIENT ) + ssl->state = SSL_CLIENT_CHANGE_CIPHER_SPEC; + + if( ssl->endpoint == SSL_IS_SERVER ) + ssl->state = SSL_HANDSHAKE_WRAPUP; + } + else + ssl->state++; + + SSL_DEBUG_MSG( 2, ( "<= parse finished" ) ); + + return( 0 ); +} + +static void ssl_handshake_params_init( ssl_handshake_params *handshake ) +{ + memset( handshake, 0, sizeof( ssl_handshake_params ) ); + +#if defined(POLARSSL_SSL_PROTO_SSL3) || defined(POLARSSL_SSL_PROTO_TLS1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_1) + md5_init( &handshake->fin_md5 ); + sha1_init( &handshake->fin_sha1 ); + md5_starts( &handshake->fin_md5 ); + sha1_starts( &handshake->fin_sha1 ); +#endif +#if defined(POLARSSL_SSL_PROTO_TLS1_2) +#if defined(POLARSSL_SHA256_C) + sha256_init( &handshake->fin_sha256 ); + sha256_starts( &handshake->fin_sha256, 0 ); +#endif +#if defined(POLARSSL_SHA512_C) + sha512_init( &handshake->fin_sha512 ); + sha512_starts( &handshake->fin_sha512, 1 ); +#endif +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ + + handshake->update_checksum = ssl_update_checksum_start; + handshake->sig_alg = SSL_HASH_SHA1; + +#if defined(POLARSSL_DHM_C) + dhm_init( &handshake->dhm_ctx ); +#endif +#if defined(POLARSSL_ECDH_C) + ecdh_init( &handshake->ecdh_ctx ); +#endif +} + +static void ssl_transform_init( ssl_transform *transform ) +{ + memset( transform, 0, sizeof(ssl_transform) ); + + cipher_init( &transform->cipher_ctx_enc ); + cipher_init( &transform->cipher_ctx_dec ); + + md_init( &transform->md_ctx_enc ); + md_init( &transform->md_ctx_dec ); +} + +void ssl_session_init( ssl_session *session ) +{ + memset( session, 0, sizeof(ssl_session) ); +} + +static int ssl_handshake_init( ssl_context *ssl ) +{ + /* Clear old handshake information if present */ + if( ssl->transform_negotiate ) + ssl_transform_free( ssl->transform_negotiate ); + if( ssl->session_negotiate ) + ssl_session_free( ssl->session_negotiate ); + if( ssl->handshake ) + ssl_handshake_free( ssl->handshake ); + + /* + * Either the pointers are now NULL or cleared properly and can be freed. + * Now allocate missing structures. + */ + if( ssl->transform_negotiate == NULL ) + { + ssl->transform_negotiate = + (ssl_transform *) polarssl_malloc( sizeof(ssl_transform) ); + } + + if( ssl->session_negotiate == NULL ) + { + ssl->session_negotiate = + (ssl_session *) polarssl_malloc( sizeof(ssl_session) ); + } + + if( ssl->handshake == NULL) + { + ssl->handshake = (ssl_handshake_params *) + polarssl_malloc( sizeof(ssl_handshake_params) ); + } + + /* All pointers should exist and can be directly freed without issue */ + if( ssl->handshake == NULL || + ssl->transform_negotiate == NULL || + ssl->session_negotiate == NULL ) + { + SSL_DEBUG_MSG( 1, ( "malloc() of ssl sub-contexts failed" ) ); + + polarssl_free( ssl->handshake ); + polarssl_free( ssl->transform_negotiate ); + polarssl_free( ssl->session_negotiate ); + + ssl->handshake = NULL; + ssl->transform_negotiate = NULL; + ssl->session_negotiate = NULL; + + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + /* Initialize structures */ + ssl_session_init( ssl->session_negotiate ); + ssl_transform_init( ssl->transform_negotiate ); + ssl_handshake_params_init( ssl->handshake ); + +#if defined(POLARSSL_X509_CRT_PARSE_C) + ssl->handshake->key_cert = ssl->key_cert; +#endif + + return( 0 ); +} + +/* + * Initialize an SSL context + */ +int ssl_init( ssl_context *ssl ) +{ + int ret; + int len = SSL_BUFFER_LEN; + + memset( ssl, 0, sizeof( ssl_context ) ); + + /* + * Sane defaults + */ + ssl->min_major_ver = SSL_MIN_MAJOR_VERSION; + ssl->min_minor_ver = SSL_MIN_MINOR_VERSION; + ssl->max_major_ver = SSL_MAX_MAJOR_VERSION; + ssl->max_minor_ver = SSL_MAX_MINOR_VERSION; + + ssl_set_ciphersuites( ssl, ssl_list_ciphersuites() ); + + ssl->renego_max_records = SSL_RENEGO_MAX_RECORDS_DEFAULT; + +#if defined(POLARSSL_DHM_C) + if( ( ret = mpi_read_string( &ssl->dhm_P, 16, + POLARSSL_DHM_RFC5114_MODP_1024_P) ) != 0 || + ( ret = mpi_read_string( &ssl->dhm_G, 16, + POLARSSL_DHM_RFC5114_MODP_1024_G) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_read_string", ret ); + return( ret ); + } +#endif + + /* + * Prepare base structures + */ + ssl->in_ctr = (unsigned char *) polarssl_malloc( len ); + ssl->in_hdr = ssl->in_ctr + 8; + ssl->in_iv = ssl->in_ctr + 13; + ssl->in_msg = ssl->in_ctr + 13; + + if( ssl->in_ctr == NULL ) + { + SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len ) ); + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + ssl->out_ctr = (unsigned char *) polarssl_malloc( len ); + ssl->out_hdr = ssl->out_ctr + 8; + ssl->out_iv = ssl->out_ctr + 13; + ssl->out_msg = ssl->out_ctr + 13; + + if( ssl->out_ctr == NULL ) + { + SSL_DEBUG_MSG( 1, ( "malloc(%d bytes) failed", len ) ); + polarssl_free( ssl->in_ctr ); + ssl->in_ctr = NULL; + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + } + + memset( ssl-> in_ctr, 0, SSL_BUFFER_LEN ); + memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + ssl->ticket_lifetime = SSL_DEFAULT_TICKET_LIFETIME; +#endif + +#if defined(POLARSSL_SSL_SET_CURVES) + ssl->curve_list = ecp_grp_id_list( ); +#endif + + if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +/* + * Reset an initialized and used SSL context for re-use while retaining + * all application-set variables, function pointers and data. + */ +int ssl_session_reset( ssl_context *ssl ) +{ + int ret; + + ssl->state = SSL_HELLO_REQUEST; + ssl->renegotiation = SSL_INITIAL_HANDSHAKE; + ssl->secure_renegotiation = SSL_LEGACY_RENEGOTIATION; + + ssl->verify_data_len = 0; + memset( ssl->own_verify_data, 0, 36 ); + memset( ssl->peer_verify_data, 0, 36 ); + + ssl->in_offt = NULL; + + ssl->in_msg = ssl->in_ctr + 13; + ssl->in_msgtype = 0; + ssl->in_msglen = 0; + ssl->in_left = 0; + + ssl->in_hslen = 0; + ssl->nb_zero = 0; + ssl->record_read = 0; + + ssl->out_msg = ssl->out_ctr + 13; + ssl->out_msgtype = 0; + ssl->out_msglen = 0; + ssl->out_left = 0; + + ssl->transform_in = NULL; + ssl->transform_out = NULL; + + ssl->renego_records_seen = 0; + + memset( ssl->out_ctr, 0, SSL_BUFFER_LEN ); + memset( ssl->in_ctr, 0, SSL_BUFFER_LEN ); + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_reset != NULL ) + { + SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_reset()" ) ); + if( ( ret = ssl_hw_record_reset( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_hw_record_reset", ret ); + return( POLARSSL_ERR_SSL_HW_ACCEL_FAILED ); + } + } +#endif + + if( ssl->transform ) + { + ssl_transform_free( ssl->transform ); + polarssl_free( ssl->transform ); + ssl->transform = NULL; + } + + if( ssl->session ) + { + ssl_session_free( ssl->session ); + polarssl_free( ssl->session ); + ssl->session = NULL; + } + +#if defined(POLARSSL_SSL_ALPN) + ssl->alpn_chosen = NULL; +#endif + + if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +static void ssl_ticket_keys_free( ssl_ticket_keys *tkeys ) +{ + aes_free( &tkeys->enc ); + aes_free( &tkeys->dec ); + + polarssl_zeroize( tkeys, sizeof(ssl_ticket_keys) ); +} + +/* + * Allocate and initialize ticket keys + */ +static int ssl_ticket_keys_init( ssl_context *ssl ) +{ + int ret; + ssl_ticket_keys *tkeys; + unsigned char buf[16]; + + if( ssl->ticket_keys != NULL ) + return( 0 ); + + tkeys = (ssl_ticket_keys *) polarssl_malloc( sizeof(ssl_ticket_keys) ); + if( tkeys == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + aes_init( &tkeys->enc ); + aes_init( &tkeys->dec ); + + if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->key_name, 16 ) ) != 0 ) + { + ssl_ticket_keys_free( tkeys ); + polarssl_free( tkeys ); + return( ret ); + } + + if( ( ret = ssl->f_rng( ssl->p_rng, buf, 16 ) ) != 0 || + ( ret = aes_setkey_enc( &tkeys->enc, buf, 128 ) ) != 0 || + ( ret = aes_setkey_dec( &tkeys->dec, buf, 128 ) ) != 0 ) + { + ssl_ticket_keys_free( tkeys ); + polarssl_free( tkeys ); + return( ret ); + } + + if( ( ret = ssl->f_rng( ssl->p_rng, tkeys->mac_key, 16 ) ) != 0 ) + { + ssl_ticket_keys_free( tkeys ); + polarssl_free( tkeys ); + return( ret ); + } + + ssl->ticket_keys = tkeys; + + return( 0 ); +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +/* + * SSL set accessors + */ +void ssl_set_endpoint( ssl_context *ssl, int endpoint ) +{ + ssl->endpoint = endpoint; + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( endpoint == SSL_IS_CLIENT ) + ssl->session_tickets = SSL_SESSION_TICKETS_ENABLED; +#endif +} + +void ssl_set_authmode( ssl_context *ssl, int authmode ) +{ + ssl->authmode = authmode; +} + +#if defined(POLARSSL_X509_CRT_PARSE_C) +void ssl_set_verify( ssl_context *ssl, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ) +{ + ssl->f_vrfy = f_vrfy; + ssl->p_vrfy = p_vrfy; +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +void ssl_set_rng( ssl_context *ssl, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + ssl->f_rng = f_rng; + ssl->p_rng = p_rng; +} + +void ssl_set_dbg( ssl_context *ssl, + void (*f_dbg)(void *, int, const char *), + void *p_dbg ) +{ + ssl->f_dbg = f_dbg; + ssl->p_dbg = p_dbg; +} + +void ssl_set_bio( ssl_context *ssl, + int (*f_recv)(void *, unsigned char *, size_t), void *p_recv, + int (*f_send)(void *, const unsigned char *, size_t), void *p_send ) +{ + ssl->f_recv = f_recv; + ssl->f_send = f_send; + ssl->p_recv = p_recv; + ssl->p_send = p_send; +} + +void ssl_set_session_cache( ssl_context *ssl, + int (*f_get_cache)(void *, ssl_session *), void *p_get_cache, + int (*f_set_cache)(void *, const ssl_session *), void *p_set_cache ) +{ + ssl->f_get_cache = f_get_cache; + ssl->p_get_cache = p_get_cache; + ssl->f_set_cache = f_set_cache; + ssl->p_set_cache = p_set_cache; +} + +int ssl_set_session( ssl_context *ssl, const ssl_session *session ) +{ + int ret; + + if( ssl == NULL || + session == NULL || + ssl->session_negotiate == NULL || + ssl->endpoint != SSL_IS_CLIENT ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + if( ( ret = ssl_session_copy( ssl->session_negotiate, session ) ) != 0 ) + return( ret ); + + ssl->handshake->resume = 1; + + return( 0 ); +} + +void ssl_set_ciphersuites( ssl_context *ssl, const int *ciphersuites ) +{ + ssl->ciphersuite_list[SSL_MINOR_VERSION_0] = ciphersuites; + ssl->ciphersuite_list[SSL_MINOR_VERSION_1] = ciphersuites; + ssl->ciphersuite_list[SSL_MINOR_VERSION_2] = ciphersuites; + ssl->ciphersuite_list[SSL_MINOR_VERSION_3] = ciphersuites; +} + +void ssl_set_ciphersuites_for_version( ssl_context *ssl, + const int *ciphersuites, + int major, int minor ) +{ + if( major != SSL_MAJOR_VERSION_3 ) + return; + + if( minor < SSL_MINOR_VERSION_0 || minor > SSL_MINOR_VERSION_3 ) + return; + + ssl->ciphersuite_list[minor] = ciphersuites; +} + +#if defined(POLARSSL_X509_CRT_PARSE_C) +/* Add a new (empty) key_cert entry an return a pointer to it */ +static ssl_key_cert *ssl_add_key_cert( ssl_context *ssl ) +{ + ssl_key_cert *key_cert, *last; + + key_cert = (ssl_key_cert *) polarssl_malloc( sizeof(ssl_key_cert) ); + if( key_cert == NULL ) + return( NULL ); + + memset( key_cert, 0, sizeof( ssl_key_cert ) ); + + /* Append the new key_cert to the (possibly empty) current list */ + if( ssl->key_cert == NULL ) + { + ssl->key_cert = key_cert; + if( ssl->handshake != NULL ) + ssl->handshake->key_cert = key_cert; + } + else + { + last = ssl->key_cert; + while( last->next != NULL ) + last = last->next; + last->next = key_cert; + } + + return( key_cert ); +} + +void ssl_set_ca_chain( ssl_context *ssl, x509_crt *ca_chain, + x509_crl *ca_crl, const char *peer_cn ) +{ + ssl->ca_chain = ca_chain; + ssl->ca_crl = ca_crl; + ssl->peer_cn = peer_cn; +} + +int ssl_set_own_cert( ssl_context *ssl, x509_crt *own_cert, + pk_context *pk_key ) +{ + ssl_key_cert *key_cert = ssl_add_key_cert( ssl ); + + if( key_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + key_cert->cert = own_cert; + key_cert->key = pk_key; + + return( 0 ); +} + +#if defined(POLARSSL_RSA_C) +int ssl_set_own_cert_rsa( ssl_context *ssl, x509_crt *own_cert, + rsa_context *rsa_key ) +{ + int ret; + ssl_key_cert *key_cert = ssl_add_key_cert( ssl ); + + if( key_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + key_cert->key = (pk_context *) polarssl_malloc( sizeof(pk_context) ); + if( key_cert->key == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + pk_init( key_cert->key ); + + ret = pk_init_ctx( key_cert->key, pk_info_from_type( POLARSSL_PK_RSA ) ); + if( ret != 0 ) + return( ret ); + + if( ( ret = rsa_copy( pk_rsa( *key_cert->key ), rsa_key ) ) != 0 ) + return( ret ); + + key_cert->cert = own_cert; + key_cert->key_own_alloc = 1; + + return( 0 ); +} +#endif /* POLARSSL_RSA_C */ + +int ssl_set_own_cert_alt( ssl_context *ssl, x509_crt *own_cert, + void *rsa_key, + rsa_decrypt_func rsa_decrypt, + rsa_sign_func rsa_sign, + rsa_key_len_func rsa_key_len ) +{ + int ret; + ssl_key_cert *key_cert = ssl_add_key_cert( ssl ); + + if( key_cert == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + key_cert->key = (pk_context *) polarssl_malloc( sizeof(pk_context) ); + if( key_cert->key == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + pk_init( key_cert->key ); + + if( ( ret = pk_init_ctx_rsa_alt( key_cert->key, rsa_key, + rsa_decrypt, rsa_sign, rsa_key_len ) ) != 0 ) + return( ret ); + + key_cert->cert = own_cert; + key_cert->key_own_alloc = 1; + + return( 0 ); +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) +int ssl_set_psk( ssl_context *ssl, const unsigned char *psk, size_t psk_len, + const unsigned char *psk_identity, size_t psk_identity_len ) +{ + if( psk == NULL || psk_identity == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + if( psk_len > POLARSSL_PSK_MAX_LEN ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + if( ssl->psk != NULL ) + { + polarssl_free( ssl->psk ); + polarssl_free( ssl->psk_identity ); + } + + ssl->psk_len = psk_len; + ssl->psk_identity_len = psk_identity_len; + + ssl->psk = (unsigned char *) polarssl_malloc( ssl->psk_len ); + ssl->psk_identity = (unsigned char *) + polarssl_malloc( ssl->psk_identity_len ); + + if( ssl->psk == NULL || ssl->psk_identity == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memcpy( ssl->psk, psk, ssl->psk_len ); + memcpy( ssl->psk_identity, psk_identity, ssl->psk_identity_len ); + + return( 0 ); +} + +void ssl_set_psk_cb( ssl_context *ssl, + int (*f_psk)(void *, ssl_context *, const unsigned char *, + size_t), + void *p_psk ) +{ + ssl->f_psk = f_psk; + ssl->p_psk = p_psk; +} +#endif /* POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED */ + +#if defined(POLARSSL_DHM_C) +int ssl_set_dh_param( ssl_context *ssl, const char *dhm_P, const char *dhm_G ) +{ + int ret; + + if( ( ret = mpi_read_string( &ssl->dhm_P, 16, dhm_P ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_read_string", ret ); + return( ret ); + } + + if( ( ret = mpi_read_string( &ssl->dhm_G, 16, dhm_G ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_read_string", ret ); + return( ret ); + } + + return( 0 ); +} + +int ssl_set_dh_param_ctx( ssl_context *ssl, dhm_context *dhm_ctx ) +{ + int ret; + + if( ( ret = mpi_copy( &ssl->dhm_P, &dhm_ctx->P ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_copy", ret ); + return( ret ); + } + + if( ( ret = mpi_copy( &ssl->dhm_G, &dhm_ctx->G ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "mpi_copy", ret ); + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_DHM_C */ + +#if defined(POLARSSL_SSL_SET_CURVES) +/* + * Set the allowed elliptic curves + */ +void ssl_set_curves( ssl_context *ssl, const ecp_group_id *curve_list ) +{ + ssl->curve_list = curve_list; +} +#endif + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) +int ssl_set_hostname( ssl_context *ssl, const char *hostname ) +{ + if( hostname == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + ssl->hostname_len = strlen( hostname ); + + if( ssl->hostname_len + 1 == 0 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + ssl->hostname = (unsigned char *) polarssl_malloc( ssl->hostname_len + 1 ); + + if( ssl->hostname == NULL ) + return( POLARSSL_ERR_SSL_MALLOC_FAILED ); + + memcpy( ssl->hostname, (const unsigned char *) hostname, + ssl->hostname_len ); + + ssl->hostname[ssl->hostname_len] = '\0'; + + return( 0 ); +} + +void ssl_set_sni( ssl_context *ssl, + int (*f_sni)(void *, ssl_context *, + const unsigned char *, size_t), + void *p_sni ) +{ + ssl->f_sni = f_sni; + ssl->p_sni = p_sni; +} +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ + +#if defined(POLARSSL_SSL_ALPN) +int ssl_set_alpn_protocols( ssl_context *ssl, const char **protos ) +{ + size_t cur_len, tot_len; + const char **p; + + /* + * "Empty strings MUST NOT be included and byte strings MUST NOT be + * truncated". Check lengths now rather than later. + */ + tot_len = 0; + for( p = protos; *p != NULL; p++ ) + { + cur_len = strlen( *p ); + tot_len += cur_len; + + if( cur_len == 0 || cur_len > 255 || tot_len > 65535 ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + ssl->alpn_list = protos; + + return( 0 ); +} + +const char *ssl_get_alpn_protocol( const ssl_context *ssl ) +{ + return( ssl->alpn_chosen ); +} +#endif /* POLARSSL_SSL_ALPN */ + +void ssl_set_max_version( ssl_context *ssl, int major, int minor ) +{ + if( major >= SSL_MIN_MAJOR_VERSION && major <= SSL_MAX_MAJOR_VERSION && + minor >= SSL_MIN_MINOR_VERSION && minor <= SSL_MAX_MINOR_VERSION ) + { + ssl->max_major_ver = major; + ssl->max_minor_ver = minor; + } +} + +void ssl_set_min_version( ssl_context *ssl, int major, int minor ) +{ + if( major >= SSL_MIN_MAJOR_VERSION && major <= SSL_MAX_MAJOR_VERSION && + minor >= SSL_MIN_MINOR_VERSION && minor <= SSL_MAX_MINOR_VERSION ) + { + ssl->min_major_ver = major; + ssl->min_minor_ver = minor; + } +} + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) +int ssl_set_max_frag_len( ssl_context *ssl, unsigned char mfl_code ) +{ + if( mfl_code >= SSL_MAX_FRAG_LEN_INVALID || + mfl_code_to_length[mfl_code] > SSL_MAX_CONTENT_LEN ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + ssl->mfl_code = mfl_code; + + return( 0 ); +} +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) +int ssl_set_truncated_hmac( ssl_context *ssl, int truncate ) +{ + if( ssl->endpoint != SSL_IS_CLIENT ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + ssl->trunc_hmac = truncate; + + return( 0 ); +} +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ + +void ssl_set_renegotiation( ssl_context *ssl, int renegotiation ) +{ + ssl->disable_renegotiation = renegotiation; +} + +void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy ) +{ + ssl->allow_legacy_renegotiation = allow_legacy; +} + +void ssl_set_renegotiation_enforced( ssl_context *ssl, int max_records ) +{ + ssl->renego_max_records = max_records; +} + +#if defined(POLARSSL_SSL_SESSION_TICKETS) +int ssl_set_session_tickets( ssl_context *ssl, int use_tickets ) +{ + ssl->session_tickets = use_tickets; + + if( ssl->endpoint == SSL_IS_CLIENT ) + return( 0 ); + + if( ssl->f_rng == NULL ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + return( ssl_ticket_keys_init( ssl ) ); +} + +void ssl_set_session_ticket_lifetime( ssl_context *ssl, int lifetime ) +{ + ssl->ticket_lifetime = lifetime; +} +#endif /* POLARSSL_SSL_SESSION_TICKETS */ + +/* + * SSL get accessors + */ +size_t ssl_get_bytes_avail( const ssl_context *ssl ) +{ + return( ssl->in_offt == NULL ? 0 : ssl->in_msglen ); +} + +int ssl_get_verify_result( const ssl_context *ssl ) +{ + return( ssl->session->verify_result ); +} + +const char *ssl_get_ciphersuite( const ssl_context *ssl ) +{ + if( ssl == NULL || ssl->session == NULL ) + return( NULL ); + + return ssl_get_ciphersuite_name( ssl->session->ciphersuite ); +} + +const char *ssl_get_version( const ssl_context *ssl ) +{ + switch( ssl->minor_ver ) + { + case SSL_MINOR_VERSION_0: + return( "SSLv3.0" ); + + case SSL_MINOR_VERSION_1: + return( "TLSv1.0" ); + + case SSL_MINOR_VERSION_2: + return( "TLSv1.1" ); + + case SSL_MINOR_VERSION_3: + return( "TLSv1.2" ); + + default: + break; + } + return( "unknown" ); +} + +#if defined(POLARSSL_X509_CRT_PARSE_C) +const x509_crt *ssl_get_peer_cert( const ssl_context *ssl ) +{ + if( ssl == NULL || ssl->session == NULL ) + return( NULL ); + + return( ssl->session->peer_cert ); +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +int ssl_get_session( const ssl_context *ssl, ssl_session *dst ) +{ + if( ssl == NULL || + dst == NULL || + ssl->session == NULL || + ssl->endpoint != SSL_IS_CLIENT ) + { + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + } + + return( ssl_session_copy( dst, ssl->session ) ); +} + +/* + * Perform a single step of the SSL handshake + */ +int ssl_handshake_step( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + +#if defined(POLARSSL_SSL_CLI_C) + if( ssl->endpoint == SSL_IS_CLIENT ) + ret = ssl_handshake_client_step( ssl ); +#endif + +#if defined(POLARSSL_SSL_SRV_C) + if( ssl->endpoint == SSL_IS_SERVER ) + ret = ssl_handshake_server_step( ssl ); +#endif + + return( ret ); +} + +/* + * Perform the SSL handshake + */ +int ssl_handshake( ssl_context *ssl ) +{ + int ret = 0; + + SSL_DEBUG_MSG( 2, ( "=> handshake" ) ); + + while( ssl->state != SSL_HANDSHAKE_OVER ) + { + ret = ssl_handshake_step( ssl ); + + if( ret != 0 ) + break; + } + + SSL_DEBUG_MSG( 2, ( "<= handshake" ) ); + + return( ret ); +} + +#if defined(POLARSSL_SSL_SRV_C) +/* + * Write HelloRequest to request renegotiation on server + */ +static int ssl_write_hello_request( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> write hello request" ) ); + + ssl->out_msglen = 4; + ssl->out_msgtype = SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = SSL_HS_HELLO_REQUEST; + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + + ssl->renegotiation = SSL_RENEGOTIATION_PENDING; + + SSL_DEBUG_MSG( 2, ( "<= write hello request" ) ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SRV_C */ + +/* + * Actually renegotiate current connection, triggered by either: + * - calling ssl_renegotiate() on client, + * - receiving a HelloRequest on client during ssl_read(), + * - receiving any handshake message on server during ssl_read() after the + * initial handshake is completed + * If the handshake doesn't complete due to waiting for I/O, it will continue + * during the next calls to ssl_renegotiate() or ssl_read() respectively. + */ +static int ssl_start_renegotiation( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> renegotiate" ) ); + + if( ( ret = ssl_handshake_init( ssl ) ) != 0 ) + return( ret ); + + ssl->state = SSL_HELLO_REQUEST; + ssl->renegotiation = SSL_RENEGOTIATION; + + if( ( ret = ssl_handshake( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_handshake", ret ); + return( ret ); + } + + SSL_DEBUG_MSG( 2, ( "<= renegotiate" ) ); + + return( 0 ); +} + +/* + * Renegotiate current connection on client, + * or request renegotiation on server + */ +int ssl_renegotiate( ssl_context *ssl ) +{ + int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE; + +#if defined(POLARSSL_SSL_SRV_C) + /* On server, just send the request */ + if( ssl->endpoint == SSL_IS_SERVER ) + { + if( ssl->state != SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + return( ssl_write_hello_request( ssl ) ); + } +#endif /* POLARSSL_SSL_SRV_C */ + +#if defined(POLARSSL_SSL_CLI_C) + /* + * On client, either start the renegotiation process or, + * if already in progress, continue the handshake + */ + if( ssl->renegotiation != SSL_RENEGOTIATION ) + { + if( ssl->state != SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + + if( ( ret = ssl_start_renegotiation( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret ); + return( ret ); + } + } + else + { + if( ( ret = ssl_handshake( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_handshake", ret ); + return( ret ); + } + } +#endif /* POLARSSL_SSL_CLI_C */ + + return( ret ); +} + +/* + * Receive application data decrypted from the SSL layer + */ +int ssl_read( ssl_context *ssl, unsigned char *buf, size_t len ) +{ + int ret; + size_t n; + + SSL_DEBUG_MSG( 2, ( "=> read" ) ); + + if( ssl->state != SSL_HANDSHAKE_OVER ) + { + if( ( ret = ssl_handshake( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_handshake", ret ); + return( ret ); + } + } + + if( ssl->in_offt == NULL ) + { + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + if( ret == POLARSSL_ERR_SSL_CONN_EOF ) + return( 0 ); + + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + + if( ssl->in_msglen == 0 && + ssl->in_msgtype == SSL_MSG_APPLICATION_DATA ) + { + /* + * OpenSSL sends empty messages to randomize the IV + */ + if( ( ret = ssl_read_record( ssl ) ) != 0 ) + { + if( ret == POLARSSL_ERR_SSL_CONN_EOF ) + return( 0 ); + + SSL_DEBUG_RET( 1, "ssl_read_record", ret ); + return( ret ); + } + } + + if( ssl->in_msgtype == SSL_MSG_HANDSHAKE ) + { + SSL_DEBUG_MSG( 1, ( "received handshake message" ) ); + + if( ssl->endpoint == SSL_IS_CLIENT && + ( ssl->in_msg[0] != SSL_HS_HELLO_REQUEST || + ssl->in_hslen != 4 ) ) + { + SSL_DEBUG_MSG( 1, ( "handshake received (not HelloRequest)" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + if( ssl->disable_renegotiation == SSL_RENEGOTIATION_DISABLED || + ( ssl->secure_renegotiation == SSL_LEGACY_RENEGOTIATION && + ssl->allow_legacy_renegotiation == + SSL_LEGACY_NO_RENEGOTIATION ) ) + { + SSL_DEBUG_MSG( 3, ( "ignoring renegotiation, sending alert" ) ); + +#if defined(POLARSSL_SSL_PROTO_SSL3) + if( ssl->minor_ver == SSL_MINOR_VERSION_0 ) + { + /* + * SSLv3 does not have a "no_renegotiation" alert + */ + if( ( ret = ssl_send_fatal_handshake_failure( ssl ) ) != 0 ) + return( ret ); + } + else +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) || defined(POLARSSL_SSL_PROTO_TLS1_1) || \ + defined(POLARSSL_SSL_PROTO_TLS1_2) + if( ssl->minor_ver >= SSL_MINOR_VERSION_1 ) + { + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_WARNING, + SSL_ALERT_MSG_NO_RENEGOTIATION ) ) != 0 ) + { + return( ret ); + } + } + else +#endif /* POLARSSL_SSL_PROTO_TLS1 || POLARSSL_SSL_PROTO_TLS1_1 || + POLARSSL_SSL_PROTO_TLS1_2 */ + { + SSL_DEBUG_MSG( 1, ( "should never happen" ) ); + return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); + } + } + else + { + if( ( ret = ssl_start_renegotiation( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_start_renegotiation", ret ); + return( ret ); + } + + return( POLARSSL_ERR_NET_WANT_READ ); + } + } + else if( ssl->renegotiation == SSL_RENEGOTIATION_PENDING ) + { + ssl->renego_records_seen++; + + if( ssl->renego_max_records >= 0 && + ssl->renego_records_seen > ssl->renego_max_records ) + { + SSL_DEBUG_MSG( 1, ( "renegotiation requested, " + "but not honored by client" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + } + else if( ssl->in_msgtype != SSL_MSG_APPLICATION_DATA ) + { + SSL_DEBUG_MSG( 1, ( "bad application data message" ) ); + return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE ); + } + + ssl->in_offt = ssl->in_msg; + } + + n = ( len < ssl->in_msglen ) + ? len : ssl->in_msglen; + + memcpy( buf, ssl->in_offt, n ); + ssl->in_msglen -= n; + + if( ssl->in_msglen == 0 ) + /* all bytes consumed */ + ssl->in_offt = NULL; + else + /* more data available */ + ssl->in_offt += n; + + SSL_DEBUG_MSG( 2, ( "<= read" ) ); + + return( (int) n ); +} + +/* + * Send application data to be encrypted by the SSL layer + */ +int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len ) +{ + int ret; + size_t n; + unsigned int max_len = SSL_MAX_CONTENT_LEN; + + SSL_DEBUG_MSG( 2, ( "=> write" ) ); + + if( ssl->state != SSL_HANDSHAKE_OVER ) + { + if( ( ret = ssl_handshake( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_handshake", ret ); + return( ret ); + } + } + +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + /* + * Assume mfl_code is correct since it was checked when set + */ + max_len = mfl_code_to_length[ssl->mfl_code]; + + /* + * Check if a smaller max length was negotiated + */ + if( ssl->session_out != NULL && + mfl_code_to_length[ssl->session_out->mfl_code] < max_len ) + { + max_len = mfl_code_to_length[ssl->session_out->mfl_code]; + } +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ + + n = ( len < max_len) ? len : max_len; + + if( ssl->out_left != 0 ) + { + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); + return( ret ); + } + } + else + { + ssl->out_msglen = n; + ssl->out_msgtype = SSL_MSG_APPLICATION_DATA; + memcpy( ssl->out_msg, buf, n ); + + if( ( ret = ssl_write_record( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_write_record", ret ); + return( ret ); + } + } + + SSL_DEBUG_MSG( 2, ( "<= write" ) ); + + return( (int) n ); +} + +/* + * Notify the peer that the connection is being closed + */ +int ssl_close_notify( ssl_context *ssl ) +{ + int ret; + + SSL_DEBUG_MSG( 2, ( "=> write close notify" ) ); + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + { + SSL_DEBUG_RET( 1, "ssl_flush_output", ret ); + return( ret ); + } + + if( ssl->state == SSL_HANDSHAKE_OVER ) + { + if( ( ret = ssl_send_alert_message( ssl, + SSL_ALERT_LEVEL_WARNING, + SSL_ALERT_MSG_CLOSE_NOTIFY ) ) != 0 ) + { + return( ret ); + } + } + + SSL_DEBUG_MSG( 2, ( "<= write close notify" ) ); + + return( ret ); +} + +void ssl_transform_free( ssl_transform *transform ) +{ + if( transform == NULL ) + return; + +#if defined(POLARSSL_ZLIB_SUPPORT) + deflateEnd( &transform->ctx_deflate ); + inflateEnd( &transform->ctx_inflate ); +#endif + + cipher_free( &transform->cipher_ctx_enc ); + cipher_free( &transform->cipher_ctx_dec ); + + md_free( &transform->md_ctx_enc ); + md_free( &transform->md_ctx_dec ); + + polarssl_zeroize( transform, sizeof( ssl_transform ) ); +} + +#if defined(POLARSSL_X509_CRT_PARSE_C) +static void ssl_key_cert_free( ssl_key_cert *key_cert ) +{ + ssl_key_cert *cur = key_cert, *next; + + while( cur != NULL ) + { + next = cur->next; + + if( cur->key_own_alloc ) + { + pk_free( cur->key ); + polarssl_free( cur->key ); + } + polarssl_free( cur ); + + cur = next; + } +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +void ssl_handshake_free( ssl_handshake_params *handshake ) +{ + if( handshake == NULL ) + return; + +#if defined(POLARSSL_DHM_C) + dhm_free( &handshake->dhm_ctx ); +#endif +#if defined(POLARSSL_ECDH_C) + ecdh_free( &handshake->ecdh_ctx ); +#endif + +#if defined(POLARSSL_ECDH_C) || defined(POLARSSL_ECDSA_C) + /* explicit void pointer cast for buggy MS compiler */ + polarssl_free( (void *) handshake->curves ); +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) && \ + defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + /* + * Free only the linked list wrapper, not the keys themselves + * since the belong to the SNI callback + */ + if( handshake->sni_key_cert != NULL ) + { + ssl_key_cert *cur = handshake->sni_key_cert, *next; + + while( cur != NULL ) + { + next = cur->next; + polarssl_free( cur ); + cur = next; + } + } +#endif /* POLARSSL_X509_CRT_PARSE_C && POLARSSL_SSL_SERVER_NAME_INDICATION */ + + polarssl_zeroize( handshake, sizeof( ssl_handshake_params ) ); +} + +void ssl_session_free( ssl_session *session ) +{ + if( session == NULL ) + return; + +#if defined(POLARSSL_X509_CRT_PARSE_C) + if( session->peer_cert != NULL ) + { + x509_crt_free( session->peer_cert ); + polarssl_free( session->peer_cert ); + } +#endif + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + polarssl_free( session->ticket ); +#endif + + polarssl_zeroize( session, sizeof( ssl_session ) ); +} + +/* + * Free an SSL context + */ +void ssl_free( ssl_context *ssl ) +{ + if( ssl == NULL ) + return; + + SSL_DEBUG_MSG( 2, ( "=> free" ) ); + + if( ssl->out_ctr != NULL ) + { + polarssl_zeroize( ssl->out_ctr, SSL_BUFFER_LEN ); + polarssl_free( ssl->out_ctr ); + } + + if( ssl->in_ctr != NULL ) + { + polarssl_zeroize( ssl->in_ctr, SSL_BUFFER_LEN ); + polarssl_free( ssl->in_ctr ); + } + +#if defined(POLARSSL_ZLIB_SUPPORT) + if( ssl->compress_buf != NULL ) + { + polarssl_zeroize( ssl->compress_buf, SSL_BUFFER_LEN ); + polarssl_free( ssl->compress_buf ); + } +#endif + +#if defined(POLARSSL_DHM_C) + mpi_free( &ssl->dhm_P ); + mpi_free( &ssl->dhm_G ); +#endif + + if( ssl->transform ) + { + ssl_transform_free( ssl->transform ); + polarssl_free( ssl->transform ); + } + + if( ssl->handshake ) + { + ssl_handshake_free( ssl->handshake ); + ssl_transform_free( ssl->transform_negotiate ); + ssl_session_free( ssl->session_negotiate ); + + polarssl_free( ssl->handshake ); + polarssl_free( ssl->transform_negotiate ); + polarssl_free( ssl->session_negotiate ); + } + + if( ssl->session ) + { + ssl_session_free( ssl->session ); + polarssl_free( ssl->session ); + } + +#if defined(POLARSSL_SSL_SESSION_TICKETS) + if( ssl->ticket_keys ) + { + ssl_ticket_keys_free( ssl->ticket_keys ); + polarssl_free( ssl->ticket_keys ); + } +#endif + +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + if( ssl->hostname != NULL ) + { + polarssl_zeroize( ssl->hostname, ssl->hostname_len ); + polarssl_free( ssl->hostname ); + ssl->hostname_len = 0; + } +#endif + +#if defined(POLARSSL_KEY_EXCHANGE__SOME__PSK_ENABLED) + if( ssl->psk != NULL ) + { + polarssl_zeroize( ssl->psk, ssl->psk_len ); + polarssl_zeroize( ssl->psk_identity, ssl->psk_identity_len ); + polarssl_free( ssl->psk ); + polarssl_free( ssl->psk_identity ); + ssl->psk_len = 0; + ssl->psk_identity_len = 0; + } +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) + ssl_key_cert_free( ssl->key_cert ); +#endif + +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + if( ssl_hw_record_finish != NULL ) + { + SSL_DEBUG_MSG( 2, ( "going for ssl_hw_record_finish()" ) ); + ssl_hw_record_finish( ssl ); + } +#endif + + SSL_DEBUG_MSG( 2, ( "<= free" ) ); + + /* Actually clear after last debug message */ + polarssl_zeroize( ssl, sizeof( ssl_context ) ); +} + +#if defined(POLARSSL_PK_C) +/* + * Convert between POLARSSL_PK_XXX and SSL_SIG_XXX + */ +unsigned char ssl_sig_from_pk( pk_context *pk ) +{ +#if defined(POLARSSL_RSA_C) + if( pk_can_do( pk, POLARSSL_PK_RSA ) ) + return( SSL_SIG_RSA ); +#endif +#if defined(POLARSSL_ECDSA_C) + if( pk_can_do( pk, POLARSSL_PK_ECDSA ) ) + return( SSL_SIG_ECDSA ); +#endif + return( SSL_SIG_ANON ); +} + +pk_type_t ssl_pk_alg_from_sig( unsigned char sig ) +{ + switch( sig ) + { +#if defined(POLARSSL_RSA_C) + case SSL_SIG_RSA: + return( POLARSSL_PK_RSA ); +#endif +#if defined(POLARSSL_ECDSA_C) + case SSL_SIG_ECDSA: + return( POLARSSL_PK_ECDSA ); +#endif + default: + return( POLARSSL_PK_NONE ); + } +} +#endif /* POLARSSL_PK_C */ + +/* + * Convert between SSL_HASH_XXX and POLARSSL_MD_XXX + */ +md_type_t ssl_md_alg_from_hash( unsigned char hash ) +{ + switch( hash ) + { +#if defined(POLARSSL_MD5_C) + case SSL_HASH_MD5: + return( POLARSSL_MD_MD5 ); +#endif +#if defined(POLARSSL_SHA1_C) + case SSL_HASH_SHA1: + return( POLARSSL_MD_SHA1 ); +#endif +#if defined(POLARSSL_SHA256_C) + case SSL_HASH_SHA224: + return( POLARSSL_MD_SHA224 ); + case SSL_HASH_SHA256: + return( POLARSSL_MD_SHA256 ); +#endif +#if defined(POLARSSL_SHA512_C) + case SSL_HASH_SHA384: + return( POLARSSL_MD_SHA384 ); + case SSL_HASH_SHA512: + return( POLARSSL_MD_SHA512 ); +#endif + default: + return( POLARSSL_MD_NONE ); + } +} + +#if defined(POLARSSL_SSL_SET_CURVES) +/* + * Check is a curve proposed by the peer is in our list. + * Return 1 if we're willing to use it, 0 otherwise. + */ +int ssl_curve_is_acceptable( const ssl_context *ssl, ecp_group_id grp_id ) +{ + const ecp_group_id *gid; + + for( gid = ssl->curve_list; *gid != POLARSSL_ECP_DP_NONE; gid++ ) + if( *gid == grp_id ) + return( 1 ); + + return( 0 ); +} +#endif /* POLARSSL_SSL_SET_CURVES */ + +#if defined(POLARSSL_X509_CRT_PARSE_C) +int ssl_check_cert_usage( const x509_crt *cert, + const ssl_ciphersuite_t *ciphersuite, + int cert_endpoint ) +{ +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) + int usage = 0; +#endif +#if defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) + const char *ext_oid; + size_t ext_len; +#endif + +#if !defined(POLARSSL_X509_CHECK_KEY_USAGE) && \ + !defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) + ((void) cert); + ((void) cert_endpoint); +#endif + +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) + if( cert_endpoint == SSL_IS_SERVER ) + { + /* Server part of the key exchange */ + switch( ciphersuite->key_exchange ) + { + case POLARSSL_KEY_EXCHANGE_RSA: + case POLARSSL_KEY_EXCHANGE_RSA_PSK: + usage = KU_KEY_ENCIPHERMENT; + break; + + case POLARSSL_KEY_EXCHANGE_DHE_RSA: + case POLARSSL_KEY_EXCHANGE_ECDHE_RSA: + case POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA: + usage = KU_DIGITAL_SIGNATURE; + break; + + case POLARSSL_KEY_EXCHANGE_ECDH_RSA: + case POLARSSL_KEY_EXCHANGE_ECDH_ECDSA: + usage = KU_KEY_AGREEMENT; + break; + + /* Don't use default: we want warnings when adding new values */ + case POLARSSL_KEY_EXCHANGE_NONE: + case POLARSSL_KEY_EXCHANGE_PSK: + case POLARSSL_KEY_EXCHANGE_DHE_PSK: + case POLARSSL_KEY_EXCHANGE_ECDHE_PSK: + usage = 0; + } + } + else + { + /* Client auth: we only implement rsa_sign and ecdsa_sign for now */ + usage = KU_DIGITAL_SIGNATURE; + } + + if( x509_crt_check_key_usage( cert, usage ) != 0 ) + return( -1 ); +#else + ((void) ciphersuite); +#endif /* POLARSSL_X509_CHECK_KEY_USAGE */ + +#if defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) + if( cert_endpoint == SSL_IS_SERVER ) + { + ext_oid = OID_SERVER_AUTH; + ext_len = OID_SIZE( OID_SERVER_AUTH ); + } + else + { + ext_oid = OID_CLIENT_AUTH; + ext_len = OID_SIZE( OID_CLIENT_AUTH ); + } + + if( x509_crt_check_extended_key_usage( cert, ext_oid, ext_len ) != 0 ) + return( -1 ); +#endif /* POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE */ + + return( 0 ); +} +#endif /* POLARSSL_X509_CRT_PARSE_C */ + +#endif /* POLARSSL_SSL_TLS_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/threading.c b/component/common/network/ssl/polarssl-1.3.8/library/threading.c new file mode 100644 index 0000000..522c70f --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/threading.c @@ -0,0 +1,113 @@ +/* + * Threading abstraction layer + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_THREADING_C) + +#include "polarssl/threading.h" + +#if defined(POLARSSL_THREADING_PTHREAD) +static int threading_mutex_init_pthread( threading_mutex_t *mutex ) +{ + if( mutex == NULL ) + return( POLARSSL_ERR_THREADING_BAD_INPUT_DATA ); + + if( pthread_mutex_init( mutex, NULL ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); + + return( 0 ); +} + +static int threading_mutex_free_pthread( threading_mutex_t *mutex ) +{ + if( mutex == NULL ) + return( POLARSSL_ERR_THREADING_BAD_INPUT_DATA ); + + if( pthread_mutex_destroy( mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); + + return( 0 ); +} + +static int threading_mutex_lock_pthread( threading_mutex_t *mutex ) +{ + if( mutex == NULL ) + return( POLARSSL_ERR_THREADING_BAD_INPUT_DATA ); + + if( pthread_mutex_lock( mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); + + return( 0 ); +} + +static int threading_mutex_unlock_pthread( threading_mutex_t *mutex ) +{ + if( mutex == NULL ) + return( POLARSSL_ERR_THREADING_BAD_INPUT_DATA ); + + if( pthread_mutex_unlock( mutex ) != 0 ) + return( POLARSSL_ERR_THREADING_MUTEX_ERROR ); + + return( 0 ); +} + +int (*polarssl_mutex_init)( threading_mutex_t * ) = threading_mutex_init_pthread; +int (*polarssl_mutex_free)( threading_mutex_t * ) = threading_mutex_free_pthread; +int (*polarssl_mutex_lock)( threading_mutex_t * ) = threading_mutex_lock_pthread; +int (*polarssl_mutex_unlock)( threading_mutex_t * ) = threading_mutex_unlock_pthread; +#endif /* POLARSSL_THREADING_PTHREAD */ + +#if defined(POLARSSL_THREADING_ALT) +static int threading_mutex_fail( threading_mutex_t *mutex ) +{ + ((void) mutex ); + return( POLARSSL_ERR_THREADING_BAD_INPUT_DATA ); +} + +int (*polarssl_mutex_init)( threading_mutex_t * ) = threading_mutex_fail; +int (*polarssl_mutex_free)( threading_mutex_t * ) = threading_mutex_fail; +int (*polarssl_mutex_lock)( threading_mutex_t * ) = threading_mutex_fail; +int (*polarssl_mutex_unlock)( threading_mutex_t * ) = threading_mutex_fail; + +int threading_set_alt( int (*mutex_init)( threading_mutex_t * ), + int (*mutex_free)( threading_mutex_t * ), + int (*mutex_lock)( threading_mutex_t * ), + int (*mutex_unlock)( threading_mutex_t * ) ) +{ + polarssl_mutex_init = mutex_init; + polarssl_mutex_free = mutex_free; + polarssl_mutex_lock = mutex_lock; + polarssl_mutex_unlock = mutex_unlock; + + return( 0 ); +} +#endif /* POLARSSL_THREADING_ALT_C */ + +#endif /* POLARSSL_THREADING_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/timing.c b/component/common/network/ssl/polarssl-1.3.8/library/timing.c new file mode 100644 index 0000000..6c1dfa4 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/timing.c @@ -0,0 +1,500 @@ +/* + * Portable interface to the CPU cycle counter + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_SELF_TEST) && defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#include +#define polarssl_printf printf +#endif + +#if defined(POLARSSL_TIMING_C) && !defined(POLARSSL_TIMING_ALT) + +#include "polarssl/timing.h" + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#include +#include + +struct _hr_time +{ + LARGE_INTEGER start; +}; + +#else + +#include +#include +#include +#include +#include + +struct _hr_time +{ + struct timeval start; +}; + +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long tsc; + __asm rdtsc + __asm mov [tsc], eax + return( tsc ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && defined(__i386__) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long lo, hi; + asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); + return( lo ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && __i386__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) ) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long lo, hi; + asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); + return( lo | ( hi << 32 ) ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && ( __amd64__ || __x86_64__ ) */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) ) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long tbl, tbu0, tbu1; + + do + { + asm volatile( "mftbu %0" : "=r" (tbu0) ); + asm volatile( "mftb %0" : "=r" (tbl ) ); + asm volatile( "mftbu %0" : "=r" (tbu1) ); + } + while( tbu0 != tbu1 ); + + return( tbl ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && ( __powerpc__ || __ppc__ ) */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && defined(__sparc64__) + +#if defined(__OpenBSD__) +#warning OpenBSD does not allow access to tick register using software version instead +#else +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long tick; + asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) ); + return( tick ); +} +#endif /* __OpenBSD__ */ +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && __sparc64__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long tick; + asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" ); + asm volatile( "mov %%g1, %0" : "=r" (tick) ); + return( tick ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && __sparc__ && !__sparc64__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && defined(__alpha__) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long cc; + asm volatile( "rpcc %0" : "=r" (cc) ); + return( cc & 0xFFFFFFFF ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && __alpha__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(POLARSSL_HAVE_ASM) && \ + defined(__GNUC__) && defined(__ia64__) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + unsigned long itc; + asm volatile( "mov %0 = ar.itc" : "=r" (itc) ); + return( itc ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && POLARSSL_HAVE_ASM && + __GNUC__ && __ia64__ */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) && defined(_MSC_VER) && \ + !defined(EFIX64) && !defined(EFI32) + +#define POLARSSL_HAVE_HARDCLOCK + +unsigned long hardclock( void ) +{ + LARGE_INTEGER offset; + + QueryPerformanceCounter( &offset ); + + return( (unsigned long)( offset.QuadPart ) ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */ + +#if !defined(POLARSSL_HAVE_HARDCLOCK) + +#define POLARSSL_HAVE_HARDCLOCK + +static int hardclock_init = 0; +static struct timeval tv_init; + +unsigned long hardclock( void ) +{ + struct timeval tv_cur; + + if( hardclock_init == 0 ) + { + gettimeofday( &tv_init, NULL ); + hardclock_init = 1; + } + + gettimeofday( &tv_cur, NULL ); + return( ( tv_cur.tv_sec - tv_init.tv_sec ) * 1000000 + + ( tv_cur.tv_usec - tv_init.tv_usec ) ); +} +#endif /* !POLARSSL_HAVE_HARDCLOCK */ + +volatile int alarmed = 0; + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +unsigned long get_timer( struct hr_time *val, int reset ) +{ + unsigned long delta; + LARGE_INTEGER offset, hfreq; + struct _hr_time *t = (struct _hr_time *) val; + + QueryPerformanceCounter( &offset ); + QueryPerformanceFrequency( &hfreq ); + + delta = (unsigned long)( ( 1000 * + ( offset.QuadPart - t->start.QuadPart ) ) / + hfreq.QuadPart ); + + if( reset ) + QueryPerformanceCounter( &t->start ); + + return( delta ); +} + +DWORD WINAPI TimerProc( LPVOID uElapse ) +{ + Sleep( (DWORD) uElapse ); + alarmed = 1; + return( TRUE ); +} + +void set_alarm( int seconds ) +{ + DWORD ThreadId; + + alarmed = 0; + CloseHandle( CreateThread( NULL, 0, TimerProc, + (LPVOID) ( seconds * 1000 ), 0, &ThreadId ) ); +} + +void m_sleep( int milliseconds ) +{ + Sleep( milliseconds ); +} + +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +unsigned long get_timer( struct hr_time *val, int reset ) +{ + unsigned long delta; + struct timeval offset; + struct _hr_time *t = (struct _hr_time *) val; + + gettimeofday( &offset, NULL ); + + delta = ( offset.tv_sec - t->start.tv_sec ) * 1000 + + ( offset.tv_usec - t->start.tv_usec ) / 1000; + + if( reset ) + { + t->start.tv_sec = offset.tv_sec; + t->start.tv_usec = offset.tv_usec; + } + + return( delta ); +} + +#if defined(INTEGRITY) +void m_sleep( int milliseconds ) +{ + usleep( milliseconds * 1000 ); +} + +#else /* INTEGRITY */ + +static void sighandler( int signum ) +{ + alarmed = 1; + signal( signum, sighandler ); +} + +void set_alarm( int seconds ) +{ + alarmed = 0; + signal( SIGALRM, sighandler ); + alarm( seconds ); +} + +void m_sleep( int milliseconds ) +{ + struct timeval tv; + + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = ( milliseconds % 1000 ) * 1000; + + select( 0, NULL, NULL, NULL, &tv ); +} +#endif /* INTEGRITY */ + +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +#if defined(POLARSSL_SELF_TEST) + +/* To test net_usleep against our functions */ +#if defined(POLARSSL_NET_C) +#include "polarssl/net.h" +#endif + +/* + * Busy-waits for the given number of milliseconds. + * Used for testing hardclock. + */ +static void busy_msleep( unsigned long msec ) +{ + struct hr_time hires; + unsigned long i = 0; /* for busy-waiting */ + volatile unsigned long j; /* to prevent optimisation */ + + (void) get_timer( &hires, 1 ); + + while( get_timer( &hires, 0 ) < msec ) + i++; + + j = i; + (void) j; +} + +/* + * Checkup routine + * + * Warning: this is work in progress, some tests may not be reliable enough + * yet! False positives may happen. + */ +int timing_self_test( int verbose ) +{ + unsigned long cycles, ratio; + unsigned long millisecs, secs; + int hardfail; + struct hr_time hires; + + if( verbose != 0 ) + polarssl_printf( " TIMING tests note: will take some time!\n" ); + + if( verbose != 0 ) + polarssl_printf( " TIMING test #1 (m_sleep / get_timer): " ); + + for( secs = 1; secs <= 3; secs++ ) + { + (void) get_timer( &hires, 1 ); + + m_sleep( 500 * secs ); + + millisecs = get_timer( &hires, 0 ); + + if( millisecs < 450 * secs || millisecs > 550 * secs ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " TIMING test #2 (set_alarm / get_timer): " ); + + for( secs = 1; secs <= 3; secs++ ) + { + (void) get_timer( &hires, 1 ); + + set_alarm( secs ); + while( !alarmed ) + ; + + millisecs = get_timer( &hires, 0 ); + + if( millisecs < 900 * secs || millisecs > 1100 * secs ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + + if( verbose != 0 ) + polarssl_printf( " TIMING test #3 (hardclock / get_timer): " ); + + /* + * Allow one failure for possible counter wrapping. + * On a 4Ghz 32-bit machine the cycle counter wraps about once per second; + * since the whole test is about 10ms, it shouldn't happen twice in a row. + */ + hardfail = 0; + +hard_test: + if( hardfail > 1 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + + /* Get a reference ratio cycles/ms */ + millisecs = 1; + cycles = hardclock(); + busy_msleep( millisecs ); + cycles = hardclock() - cycles; + ratio = cycles / millisecs; + + /* Check that the ratio is mostly constant */ + for( millisecs = 2; millisecs <= 4; millisecs++ ) + { + cycles = hardclock(); + busy_msleep( millisecs ); + cycles = hardclock() - cycles; + + /* Allow variation up to 20% */ + if( cycles / millisecs < ratio - ratio / 5 || + cycles / millisecs > ratio + ratio / 5 ) + { + hardfail++; + goto hard_test; + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + +#if defined(POLARSSL_NET_C) + if( verbose != 0 ) + polarssl_printf( " TIMING test #4 (net_usleep/ get_timer): " ); + + for( secs = 1; secs <= 3; secs++ ) + { + (void) get_timer( &hires, 1 ); + + net_usleep( 500000 * secs ); + + millisecs = get_timer( &hires, 0 ); + + if( millisecs < 450 * secs || millisecs > 550 * secs ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( 1 ); + } + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); +#endif /* POLARSSL_NET_C */ + + if( verbose != 0 ) + polarssl_printf( "\n" ); + + return( 0 ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_TIMING_C && !POLARSSL_TIMING_ALT */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/version.c b/component/common/network/ssl/polarssl-1.3.8/library/version.c new file mode 100644 index 0000000..c3c708a --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/version.c @@ -0,0 +1,56 @@ +/* + * Version information + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_VERSION_C) + +#include "polarssl/version.h" +#include + +const char version[] = POLARSSL_VERSION_STRING; + +unsigned int version_get_number() +{ + return( POLARSSL_VERSION_NUMBER ); +} + +void version_get_string( char *string ) +{ + memcpy( string, POLARSSL_VERSION_STRING, + sizeof( POLARSSL_VERSION_STRING ) ); +} + +void version_get_string_full( char *string ) +{ + memcpy( string, POLARSSL_VERSION_STRING_FULL, + sizeof( POLARSSL_VERSION_STRING_FULL ) ); +} + +#endif /* POLARSSL_VERSION_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/version_features.c b/component/common/network/ssl/polarssl-1.3.8/library/version_features.c new file mode 100644 index 0000000..1023198 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/version_features.c @@ -0,0 +1,560 @@ +/* + * Version feature information + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_VERSION_C) + +#include "polarssl/version.h" + +#include + +#if defined(_MSC_VER) && !defined strcasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strcasecmp _stricmp +#endif + +const char *features[] = { +#if defined(POLARSSL_VERSION_FEATURES) +#if defined(POLARSSL_HAVE_INT8) + "POLARSSL_HAVE_INT8", +#endif /* POLARSSL_HAVE_INT8 */ +#if defined(POLARSSL_HAVE_INT16) + "POLARSSL_HAVE_INT16", +#endif /* POLARSSL_HAVE_INT16 */ +#if defined(POLARSSL_HAVE_LONGLONG) + "POLARSSL_HAVE_LONGLONG", +#endif /* POLARSSL_HAVE_LONGLONG */ +#if defined(POLARSSL_HAVE_ASM) + "POLARSSL_HAVE_ASM", +#endif /* POLARSSL_HAVE_ASM */ +#if defined(POLARSSL_HAVE_SSE2) + "POLARSSL_HAVE_SSE2", +#endif /* POLARSSL_HAVE_SSE2 */ +#if defined(POLARSSL_HAVE_TIME) + "POLARSSL_HAVE_TIME", +#endif /* POLARSSL_HAVE_TIME */ +#if defined(POLARSSL_HAVE_IPV6) + "POLARSSL_HAVE_IPV6", +#endif /* POLARSSL_HAVE_IPV6 */ +#if defined(POLARSSL_PLATFORM_MEMORY) + "POLARSSL_PLATFORM_MEMORY", +#endif /* POLARSSL_PLATFORM_MEMORY */ +#if defined(POLARSSL_PLATFORM_NO_STD_FUNCTIONS) + "POLARSSL_PLATFORM_NO_STD_FUNCTIONS", +#endif /* POLARSSL_PLATFORM_NO_STD_FUNCTIONS */ +#if defined(POLARSSL_PLATFORM_PRINTF_ALT) + "POLARSSL_PLATFORM_PRINTF_ALT", +#endif /* POLARSSL_PLATFORM_PRINTF_ALT */ +#if defined(POLARSSL_PLATFORM_FPRINTF_ALT) + "POLARSSL_PLATFORM_FPRINTF_ALT", +#endif /* POLARSSL_PLATFORM_FPRINTF_ALT */ +#if defined(POLARSSL_TIMING_ALT) + "POLARSSL_TIMING_ALT", +#endif /* POLARSSL_TIMING_ALT */ +#if defined(POLARSSL_AES_ALT) + "POLARSSL_AES_ALT", +#endif /* POLARSSL_AES_ALT */ +#if defined(POLARSSL_ARC4_ALT) + "POLARSSL_ARC4_ALT", +#endif /* POLARSSL_ARC4_ALT */ +#if defined(POLARSSL_BLOWFISH_ALT) + "POLARSSL_BLOWFISH_ALT", +#endif /* POLARSSL_BLOWFISH_ALT */ +#if defined(POLARSSL_CAMELLIA_ALT) + "POLARSSL_CAMELLIA_ALT", +#endif /* POLARSSL_CAMELLIA_ALT */ +#if defined(POLARSSL_DES_ALT) + "POLARSSL_DES_ALT", +#endif /* POLARSSL_DES_ALT */ +#if defined(POLARSSL_XTEA_ALT) + "POLARSSL_XTEA_ALT", +#endif /* POLARSSL_XTEA_ALT */ +#if defined(POLARSSL_MD2_ALT) + "POLARSSL_MD2_ALT", +#endif /* POLARSSL_MD2_ALT */ +#if defined(POLARSSL_MD4_ALT) + "POLARSSL_MD4_ALT", +#endif /* POLARSSL_MD4_ALT */ +#if defined(POLARSSL_MD5_ALT) + "POLARSSL_MD5_ALT", +#endif /* POLARSSL_MD5_ALT */ +#if defined(POLARSSL_RIPEMD160_ALT) + "POLARSSL_RIPEMD160_ALT", +#endif /* POLARSSL_RIPEMD160_ALT */ +#if defined(POLARSSL_SHA1_ALT) + "POLARSSL_SHA1_ALT", +#endif /* POLARSSL_SHA1_ALT */ +#if defined(POLARSSL_SHA256_ALT) + "POLARSSL_SHA256_ALT", +#endif /* POLARSSL_SHA256_ALT */ +#if defined(POLARSSL_SHA512_ALT) + "POLARSSL_SHA512_ALT", +#endif /* POLARSSL_SHA512_ALT */ +#if defined(POLARSSL_AES_ROM_TABLES) + "POLARSSL_AES_ROM_TABLES", +#endif /* POLARSSL_AES_ROM_TABLES */ +#if defined(POLARSSL_CIPHER_MODE_CBC) + "POLARSSL_CIPHER_MODE_CBC", +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#if defined(POLARSSL_CIPHER_MODE_CFB) + "POLARSSL_CIPHER_MODE_CFB", +#endif /* POLARSSL_CIPHER_MODE_CFB */ +#if defined(POLARSSL_CIPHER_MODE_CTR) + "POLARSSL_CIPHER_MODE_CTR", +#endif /* POLARSSL_CIPHER_MODE_CTR */ +#if defined(POLARSSL_CIPHER_NULL_CIPHER) + "POLARSSL_CIPHER_NULL_CIPHER", +#endif /* POLARSSL_CIPHER_NULL_CIPHER */ +#if defined(POLARSSL_CIPHER_PADDING_PKCS7) + "POLARSSL_CIPHER_PADDING_PKCS7", +#endif /* POLARSSL_CIPHER_PADDING_PKCS7 */ +#if defined(POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS) + "POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS", +#endif /* POLARSSL_CIPHER_PADDING_ONE_AND_ZEROS */ +#if defined(POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN) + "POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN", +#endif /* POLARSSL_CIPHER_PADDING_ZEROS_AND_LEN */ +#if defined(POLARSSL_CIPHER_PADDING_ZEROS) + "POLARSSL_CIPHER_PADDING_ZEROS", +#endif /* POLARSSL_CIPHER_PADDING_ZEROS */ +#if defined(POLARSSL_ENABLE_WEAK_CIPHERSUITES) + "POLARSSL_ENABLE_WEAK_CIPHERSUITES", +#endif /* POLARSSL_ENABLE_WEAK_CIPHERSUITES */ +#if defined(POLARSSL_REMOVE_ARC4_CIPHERSUITES) + "POLARSSL_REMOVE_ARC4_CIPHERSUITES", +#endif /* POLARSSL_REMOVE_ARC4_CIPHERSUITES */ +#if defined(POLARSSL_ECP_DP_SECP192R1_ENABLED) + "POLARSSL_ECP_DP_SECP192R1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP192R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP224R1_ENABLED) + "POLARSSL_ECP_DP_SECP224R1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP224R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP256R1_ENABLED) + "POLARSSL_ECP_DP_SECP256R1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP256R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP384R1_ENABLED) + "POLARSSL_ECP_DP_SECP384R1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP384R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP521R1_ENABLED) + "POLARSSL_ECP_DP_SECP521R1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP521R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP192K1_ENABLED) + "POLARSSL_ECP_DP_SECP192K1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP192K1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP224K1_ENABLED) + "POLARSSL_ECP_DP_SECP224K1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP224K1_ENABLED */ +#if defined(POLARSSL_ECP_DP_SECP256K1_ENABLED) + "POLARSSL_ECP_DP_SECP256K1_ENABLED", +#endif /* POLARSSL_ECP_DP_SECP256K1_ENABLED */ +#if defined(POLARSSL_ECP_DP_BP256R1_ENABLED) + "POLARSSL_ECP_DP_BP256R1_ENABLED", +#endif /* POLARSSL_ECP_DP_BP256R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_BP384R1_ENABLED) + "POLARSSL_ECP_DP_BP384R1_ENABLED", +#endif /* POLARSSL_ECP_DP_BP384R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_BP512R1_ENABLED) + "POLARSSL_ECP_DP_BP512R1_ENABLED", +#endif /* POLARSSL_ECP_DP_BP512R1_ENABLED */ +#if defined(POLARSSL_ECP_DP_M221_ENABLED) + "POLARSSL_ECP_DP_M221_ENABLED", +#endif /* POLARSSL_ECP_DP_M221_ENABLED */ +#if defined(POLARSSL_ECP_DP_M255_ENABLED) + "POLARSSL_ECP_DP_M255_ENABLED", +#endif /* POLARSSL_ECP_DP_M255_ENABLED */ +#if defined(POLARSSL_ECP_DP_M383_ENABLED) + "POLARSSL_ECP_DP_M383_ENABLED", +#endif /* POLARSSL_ECP_DP_M383_ENABLED */ +#if defined(POLARSSL_ECP_DP_M511_ENABLED) + "POLARSSL_ECP_DP_M511_ENABLED", +#endif /* POLARSSL_ECP_DP_M511_ENABLED */ +#if defined(POLARSSL_ECP_NIST_OPTIM) + "POLARSSL_ECP_NIST_OPTIM", +#endif /* POLARSSL_ECP_NIST_OPTIM */ +#if defined(POLARSSL_ECDSA_DETERMINISTIC) + "POLARSSL_ECDSA_DETERMINISTIC", +#endif /* POLARSSL_ECDSA_DETERMINISTIC */ +#if defined(POLARSSL_KEY_EXCHANGE_PSK_ENABLED) + "POLARSSL_KEY_EXCHANGE_PSK_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED) + "POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + "POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED) + "POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_RSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_RSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_RSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED) + "POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED", +#endif /* POLARSSL_KEY_EXCHANGE_ECDH_RSA_ENABLED */ +#if defined(POLARSSL_PK_PARSE_EC_EXTENDED) + "POLARSSL_PK_PARSE_EC_EXTENDED", +#endif /* POLARSSL_PK_PARSE_EC_EXTENDED */ +#if defined(POLARSSL_ERROR_STRERROR_BC) + "POLARSSL_ERROR_STRERROR_BC", +#endif /* POLARSSL_ERROR_STRERROR_BC */ +#if defined(POLARSSL_ERROR_STRERROR_DUMMY) + "POLARSSL_ERROR_STRERROR_DUMMY", +#endif /* POLARSSL_ERROR_STRERROR_DUMMY */ +#if defined(POLARSSL_GENPRIME) + "POLARSSL_GENPRIME", +#endif /* POLARSSL_GENPRIME */ +#if defined(POLARSSL_FS_IO) + "POLARSSL_FS_IO", +#endif /* POLARSSL_FS_IO */ +#if defined(POLARSSL_NO_DEFAULT_ENTROPY_SOURCES) + "POLARSSL_NO_DEFAULT_ENTROPY_SOURCES", +#endif /* POLARSSL_NO_DEFAULT_ENTROPY_SOURCES */ +#if defined(POLARSSL_NO_PLATFORM_ENTROPY) + "POLARSSL_NO_PLATFORM_ENTROPY", +#endif /* POLARSSL_NO_PLATFORM_ENTROPY */ +#if defined(POLARSSL_ENTROPY_FORCE_SHA256) + "POLARSSL_ENTROPY_FORCE_SHA256", +#endif /* POLARSSL_ENTROPY_FORCE_SHA256 */ +#if defined(POLARSSL_MEMORY_DEBUG) + "POLARSSL_MEMORY_DEBUG", +#endif /* POLARSSL_MEMORY_DEBUG */ +#if defined(POLARSSL_MEMORY_BACKTRACE) + "POLARSSL_MEMORY_BACKTRACE", +#endif /* POLARSSL_MEMORY_BACKTRACE */ +#if defined(POLARSSL_PKCS1_V15) + "POLARSSL_PKCS1_V15", +#endif /* POLARSSL_PKCS1_V15 */ +#if defined(POLARSSL_PKCS1_V21) + "POLARSSL_PKCS1_V21", +#endif /* POLARSSL_PKCS1_V21 */ +#if defined(POLARSSL_RSA_NO_CRT) + "POLARSSL_RSA_NO_CRT", +#endif /* POLARSSL_RSA_NO_CRT */ +#if defined(POLARSSL_SELF_TEST) + "POLARSSL_SELF_TEST", +#endif /* POLARSSL_SELF_TEST */ +#if defined(POLARSSL_SSL_ALERT_MESSAGES) + "POLARSSL_SSL_ALERT_MESSAGES", +#endif /* POLARSSL_SSL_ALERT_MESSAGES */ +#if defined(POLARSSL_SSL_DEBUG_ALL) + "POLARSSL_SSL_DEBUG_ALL", +#endif /* POLARSSL_SSL_DEBUG_ALL */ +#if defined(POLARSSL_SSL_HW_RECORD_ACCEL) + "POLARSSL_SSL_HW_RECORD_ACCEL", +#endif /* POLARSSL_SSL_HW_RECORD_ACCEL */ +#if defined(POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) + "POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO", +#endif /* POLARSSL_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO */ +#if defined(POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE) + "POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE", +#endif /* POLARSSL_SSL_SRV_RESPECT_CLIENT_PREFERENCE */ +#if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) + "POLARSSL_SSL_MAX_FRAGMENT_LENGTH", +#endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */ +#if defined(POLARSSL_SSL_PROTO_SSL3) + "POLARSSL_SSL_PROTO_SSL3", +#endif /* POLARSSL_SSL_PROTO_SSL3 */ +#if defined(POLARSSL_SSL_PROTO_TLS1) + "POLARSSL_SSL_PROTO_TLS1", +#endif /* POLARSSL_SSL_PROTO_TLS1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1_1) + "POLARSSL_SSL_PROTO_TLS1_1", +#endif /* POLARSSL_SSL_PROTO_TLS1_1 */ +#if defined(POLARSSL_SSL_PROTO_TLS1_2) + "POLARSSL_SSL_PROTO_TLS1_2", +#endif /* POLARSSL_SSL_PROTO_TLS1_2 */ +#if defined(POLARSSL_SSL_ALPN) + "POLARSSL_SSL_ALPN", +#endif /* POLARSSL_SSL_ALPN */ +#if defined(POLARSSL_SSL_SESSION_TICKETS) + "POLARSSL_SSL_SESSION_TICKETS", +#endif /* POLARSSL_SSL_SESSION_TICKETS */ +#if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) + "POLARSSL_SSL_SERVER_NAME_INDICATION", +#endif /* POLARSSL_SSL_SERVER_NAME_INDICATION */ +#if defined(POLARSSL_SSL_TRUNCATED_HMAC) + "POLARSSL_SSL_TRUNCATED_HMAC", +#endif /* POLARSSL_SSL_TRUNCATED_HMAC */ +#if defined(POLARSSL_SSL_SET_CURVES) + "POLARSSL_SSL_SET_CURVES", +#endif /* POLARSSL_SSL_SET_CURVES */ +#if defined(POLARSSL_THREADING_ALT) + "POLARSSL_THREADING_ALT", +#endif /* POLARSSL_THREADING_ALT */ +#if defined(POLARSSL_THREADING_PTHREAD) + "POLARSSL_THREADING_PTHREAD", +#endif /* POLARSSL_THREADING_PTHREAD */ +#if defined(POLARSSL_VERSION_FEATURES) + "POLARSSL_VERSION_FEATURES", +#endif /* POLARSSL_VERSION_FEATURES */ +#if defined(POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3) + "POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3", +#endif /* POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3 */ +#if defined(POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) + "POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION", +#endif /* POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION */ +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) + "POLARSSL_X509_CHECK_KEY_USAGE", +#endif /* POLARSSL_X509_CHECK_KEY_USAGE */ +#if defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) + "POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE", +#endif /* POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE */ +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + "POLARSSL_X509_RSASSA_PSS_SUPPORT", +#endif /* POLARSSL_X509_RSASSA_PSS_SUPPORT */ +#if defined(POLARSSL_ZLIB_SUPPORT) + "POLARSSL_ZLIB_SUPPORT", +#endif /* POLARSSL_ZLIB_SUPPORT */ +#if defined(POLARSSL_AESNI_C) + "POLARSSL_AESNI_C", +#endif /* POLARSSL_AESNI_C */ +#if defined(POLARSSL_AES_C) + "POLARSSL_AES_C", +#endif /* POLARSSL_AES_C */ +#if defined(POLARSSL_ARC4_C) + "POLARSSL_ARC4_C", +#endif /* POLARSSL_ARC4_C */ +#if defined(POLARSSL_ASN1_PARSE_C) + "POLARSSL_ASN1_PARSE_C", +#endif /* POLARSSL_ASN1_PARSE_C */ +#if defined(POLARSSL_ASN1_WRITE_C) + "POLARSSL_ASN1_WRITE_C", +#endif /* POLARSSL_ASN1_WRITE_C */ +#if defined(POLARSSL_BASE64_C) + "POLARSSL_BASE64_C", +#endif /* POLARSSL_BASE64_C */ +#if defined(POLARSSL_BIGNUM_C) + "POLARSSL_BIGNUM_C", +#endif /* POLARSSL_BIGNUM_C */ +#if defined(POLARSSL_BLOWFISH_C) + "POLARSSL_BLOWFISH_C", +#endif /* POLARSSL_BLOWFISH_C */ +#if defined(POLARSSL_CAMELLIA_C) + "POLARSSL_CAMELLIA_C", +#endif /* POLARSSL_CAMELLIA_C */ +#if defined(POLARSSL_CCM_C) + "POLARSSL_CCM_C", +#endif /* POLARSSL_CCM_C */ +#if defined(POLARSSL_CERTS_C) + "POLARSSL_CERTS_C", +#endif /* POLARSSL_CERTS_C */ +#if defined(POLARSSL_CIPHER_C) + "POLARSSL_CIPHER_C", +#endif /* POLARSSL_CIPHER_C */ +#if defined(POLARSSL_CTR_DRBG_C) + "POLARSSL_CTR_DRBG_C", +#endif /* POLARSSL_CTR_DRBG_C */ +#if defined(POLARSSL_DEBUG_C) + "POLARSSL_DEBUG_C", +#endif /* POLARSSL_DEBUG_C */ +#if defined(POLARSSL_DES_C) + "POLARSSL_DES_C", +#endif /* POLARSSL_DES_C */ +#if defined(POLARSSL_DHM_C) + "POLARSSL_DHM_C", +#endif /* POLARSSL_DHM_C */ +#if defined(POLARSSL_ECDH_C) + "POLARSSL_ECDH_C", +#endif /* POLARSSL_ECDH_C */ +#if defined(POLARSSL_ECDSA_C) + "POLARSSL_ECDSA_C", +#endif /* POLARSSL_ECDSA_C */ +#if defined(POLARSSL_ECP_C) + "POLARSSL_ECP_C", +#endif /* POLARSSL_ECP_C */ +#if defined(POLARSSL_ENTROPY_C) + "POLARSSL_ENTROPY_C", +#endif /* POLARSSL_ENTROPY_C */ +#if defined(POLARSSL_ERROR_C) + "POLARSSL_ERROR_C", +#endif /* POLARSSL_ERROR_C */ +#if defined(POLARSSL_GCM_C) + "POLARSSL_GCM_C", +#endif /* POLARSSL_GCM_C */ +#if defined(POLARSSL_HAVEGE_C) + "POLARSSL_HAVEGE_C", +#endif /* POLARSSL_HAVEGE_C */ +#if defined(POLARSSL_HMAC_DRBG_C) + "POLARSSL_HMAC_DRBG_C", +#endif /* POLARSSL_HMAC_DRBG_C */ +#if defined(POLARSSL_MD_C) + "POLARSSL_MD_C", +#endif /* POLARSSL_MD_C */ +#if defined(POLARSSL_MD2_C) + "POLARSSL_MD2_C", +#endif /* POLARSSL_MD2_C */ +#if defined(POLARSSL_MD4_C) + "POLARSSL_MD4_C", +#endif /* POLARSSL_MD4_C */ +#if defined(POLARSSL_MD5_C) + "POLARSSL_MD5_C", +#endif /* POLARSSL_MD5_C */ +#if defined(POLARSSL_MEMORY_C) + "POLARSSL_MEMORY_C", +#endif /* POLARSSL_MEMORY_C */ +#if defined(POLARSSL_MEMORY_BUFFER_ALLOC_C) + "POLARSSL_MEMORY_BUFFER_ALLOC_C", +#endif /* POLARSSL_MEMORY_BUFFER_ALLOC_C */ +#if defined(POLARSSL_NET_C) + "POLARSSL_NET_C", +#endif /* POLARSSL_NET_C */ +#if defined(POLARSSL_OID_C) + "POLARSSL_OID_C", +#endif /* POLARSSL_OID_C */ +#if defined(POLARSSL_PADLOCK_C) + "POLARSSL_PADLOCK_C", +#endif /* POLARSSL_PADLOCK_C */ +#if defined(POLARSSL_PBKDF2_C) + "POLARSSL_PBKDF2_C", +#endif /* POLARSSL_PBKDF2_C */ +#if defined(POLARSSL_PEM_PARSE_C) + "POLARSSL_PEM_PARSE_C", +#endif /* POLARSSL_PEM_PARSE_C */ +#if defined(POLARSSL_PEM_WRITE_C) + "POLARSSL_PEM_WRITE_C", +#endif /* POLARSSL_PEM_WRITE_C */ +#if defined(POLARSSL_PK_C) + "POLARSSL_PK_C", +#endif /* POLARSSL_PK_C */ +#if defined(POLARSSL_PK_PARSE_C) + "POLARSSL_PK_PARSE_C", +#endif /* POLARSSL_PK_PARSE_C */ +#if defined(POLARSSL_PK_WRITE_C) + "POLARSSL_PK_WRITE_C", +#endif /* POLARSSL_PK_WRITE_C */ +#if defined(POLARSSL_PKCS5_C) + "POLARSSL_PKCS5_C", +#endif /* POLARSSL_PKCS5_C */ +#if defined(POLARSSL_PKCS11_C) + "POLARSSL_PKCS11_C", +#endif /* POLARSSL_PKCS11_C */ +#if defined(POLARSSL_PKCS12_C) + "POLARSSL_PKCS12_C", +#endif /* POLARSSL_PKCS12_C */ +#if defined(POLARSSL_PLATFORM_C) + "POLARSSL_PLATFORM_C", +#endif /* POLARSSL_PLATFORM_C */ +#if defined(POLARSSL_RIPEMD160_C) + "POLARSSL_RIPEMD160_C", +#endif /* POLARSSL_RIPEMD160_C */ +#if defined(POLARSSL_RSA_C) + "POLARSSL_RSA_C", +#endif /* POLARSSL_RSA_C */ +#if defined(POLARSSL_SHA1_C) + "POLARSSL_SHA1_C", +#endif /* POLARSSL_SHA1_C */ +#if defined(POLARSSL_SHA256_C) + "POLARSSL_SHA256_C", +#endif /* POLARSSL_SHA256_C */ +#if defined(POLARSSL_SHA512_C) + "POLARSSL_SHA512_C", +#endif /* POLARSSL_SHA512_C */ +#if defined(POLARSSL_SSL_CACHE_C) + "POLARSSL_SSL_CACHE_C", +#endif /* POLARSSL_SSL_CACHE_C */ +#if defined(POLARSSL_SSL_CLI_C) + "POLARSSL_SSL_CLI_C", +#endif /* POLARSSL_SSL_CLI_C */ +#if defined(POLARSSL_SSL_SRV_C) + "POLARSSL_SSL_SRV_C", +#endif /* POLARSSL_SSL_SRV_C */ +#if defined(POLARSSL_SSL_TLS_C) + "POLARSSL_SSL_TLS_C", +#endif /* POLARSSL_SSL_TLS_C */ +#if defined(POLARSSL_THREADING_C) + "POLARSSL_THREADING_C", +#endif /* POLARSSL_THREADING_C */ +#if defined(POLARSSL_TIMING_C) + "POLARSSL_TIMING_C", +#endif /* POLARSSL_TIMING_C */ +#if defined(POLARSSL_VERSION_C) + "POLARSSL_VERSION_C", +#endif /* POLARSSL_VERSION_C */ +#if defined(POLARSSL_X509_USE_C) + "POLARSSL_X509_USE_C", +#endif /* POLARSSL_X509_USE_C */ +#if defined(POLARSSL_X509_CRT_PARSE_C) + "POLARSSL_X509_CRT_PARSE_C", +#endif /* POLARSSL_X509_CRT_PARSE_C */ +#if defined(POLARSSL_X509_CRL_PARSE_C) + "POLARSSL_X509_CRL_PARSE_C", +#endif /* POLARSSL_X509_CRL_PARSE_C */ +#if defined(POLARSSL_X509_CSR_PARSE_C) + "POLARSSL_X509_CSR_PARSE_C", +#endif /* POLARSSL_X509_CSR_PARSE_C */ +#if defined(POLARSSL_X509_CREATE_C) + "POLARSSL_X509_CREATE_C", +#endif /* POLARSSL_X509_CREATE_C */ +#if defined(POLARSSL_X509_CRT_WRITE_C) + "POLARSSL_X509_CRT_WRITE_C", +#endif /* POLARSSL_X509_CRT_WRITE_C */ +#if defined(POLARSSL_X509_CSR_WRITE_C) + "POLARSSL_X509_CSR_WRITE_C", +#endif /* POLARSSL_X509_CSR_WRITE_C */ +#if defined(POLARSSL_XTEA_C) + "POLARSSL_XTEA_C", +#endif /* POLARSSL_XTEA_C */ +#endif /* POLARSSL_VERSION_FEATURES */ + NULL +}; + +int version_check_feature( const char *feature ) +{ + const char **idx = features; + + if( *idx == NULL ) + return( -2 ); + + if( feature == NULL ) + return( -1 ); + + while( *idx != NULL ) + { + if( !strcasecmp( *idx, feature ) ) + return( 0 ); + idx++; + } + return( -1 ); +} + +#endif /* POLARSSL_VERSION_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509.c b/component/common/network/ssl/polarssl-1.3.8/library/x509.c new file mode 100644 index 0000000..17c7a7d --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509.c @@ -0,0 +1,1102 @@ +/* + * X.509 common functions for parsing and verification + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_USE_C) + +#include "polarssl/x509.h" +#include "polarssl/asn1.h" +#include "polarssl/oid.h" +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) +#include +#else +#include +#endif + +#if defined(EFIX64) || defined(EFI32) +#include +#endif + +#if defined(POLARSSL_FS_IO) +#include +#if !defined(_WIN32) +#include +#include +#include +#endif +#endif + +/* + * CertificateSerialNumber ::= INTEGER + */ +int x509_get_serial( unsigned char **p, const unsigned char *end, + x509_buf *serial ) +{ + int ret; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_SERIAL + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != ( ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2 ) && + **p != ASN1_INTEGER ) + return( POLARSSL_ERR_X509_INVALID_SERIAL + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + serial->tag = *(*p)++; + + if( ( ret = asn1_get_len( p, end, &serial->len ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_SERIAL + ret ); + + serial->p = *p; + *p += serial->len; + + return( 0 ); +} + +/* Get an algorithm identifier without parameters (eg for signatures) + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +int x509_get_alg_null( unsigned char **p, const unsigned char *end, + x509_buf *alg ) +{ + int ret; + + if( ( ret = asn1_get_alg_null( p, end, alg ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + return( 0 ); +} + +/* + * Parse an algorithm identifier with (optional) paramaters + */ +int x509_get_alg( unsigned char **p, const unsigned char *end, + x509_buf *alg, x509_buf *params ) +{ + int ret; + + if( ( ret = asn1_get_alg( p, end, alg, params ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + return( 0 ); +} + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) +/* + * HashAlgorithm ::= AlgorithmIdentifier + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * + * For HashAlgorithm, parameters MUST be NULL or absent. + */ +static int x509_get_hash_alg( const x509_buf *alg, md_type_t *md_alg ) +{ + int ret; + unsigned char *p; + const unsigned char *end; + x509_buf md_oid; + size_t len; + + /* Make sure we got a SEQUENCE and setup bounds */ + if( alg->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + p = (unsigned char *) alg->p; + end = p + alg->len; + + if( p >= end ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + /* Parse md_oid */ + md_oid.tag = *p; + + if( ( ret = asn1_get_tag( &p, end, &md_oid.len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + md_oid.p = p; + p += md_oid.len; + + /* Get md_alg from md_oid */ + if( ( ret = oid_get_md_alg( &md_oid, md_alg ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + /* Make sure params is absent of NULL */ + if( p == end ) + return( 0 ); + + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_NULL ) ) != 0 || len != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p != end ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * RSASSA-PSS-params ::= SEQUENCE { + * hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, + * maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1Identifier, + * saltLength [2] INTEGER DEFAULT 20, + * trailerField [3] INTEGER DEFAULT 1 } + * -- Note that the tags in this Sequence are explicit. + * + * RFC 4055 (which defines use of RSASSA-PSS in PKIX) states that the value + * of trailerField MUST be 1, and PKCS#1 v2.2 doesn't even define any other + * option. Enfore this at parsing time. + */ +int x509_get_rsassa_pss_params( const x509_buf *params, + md_type_t *md_alg, md_type_t *mgf_md, + int *salt_len ) +{ + int ret; + unsigned char *p; + const unsigned char *end, *end2; + size_t len; + x509_buf alg_id, alg_params; + + /* First set everything to defaults */ + *md_alg = POLARSSL_MD_SHA1; + *mgf_md = POLARSSL_MD_SHA1; + *salt_len = 20; + + /* Make sure params is a SEQUENCE and setup bounds */ + if( params->tag != ( ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + p = (unsigned char *) params->p; + end = p + params->len; + + if( p == end ) + return( 0 ); + + /* + * HashAlgorithm + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) == 0 ) + { + end2 = p + len; + + /* HashAlgorithm ::= AlgorithmIdentifier (without parameters) */ + if( ( ret = x509_get_alg_null( &p, end2, &alg_id ) ) != 0 ) + return( ret ); + + if( ( ret = oid_get_md_alg( &alg_id, md_alg ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p != end2 ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p == end ) + return( 0 ); + + /* + * MaskGenAlgorithm + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 1 ) ) == 0 ) + { + end2 = p + len; + + /* MaskGenAlgorithm ::= AlgorithmIdentifier (params = HashAlgorithm) */ + if( ( ret = x509_get_alg( &p, end2, &alg_id, &alg_params ) ) != 0 ) + return( ret ); + + /* Only MFG1 is recognised for now */ + if( ! OID_CMP( OID_MGF1, &alg_id ) ) + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE + + POLARSSL_ERR_OID_NOT_FOUND ); + + /* Parse HashAlgorithm */ + if( ( ret = x509_get_hash_alg( &alg_params, mgf_md ) ) != 0 ) + return( ret ); + + if( p != end2 ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p == end ) + return( 0 ); + + /* + * salt_len + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 2 ) ) == 0 ) + { + end2 = p + len; + + if( ( ret = asn1_get_int( &p, end2, salt_len ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p != end2 ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p == end ) + return( 0 ); + + /* + * trailer_field (if present, must be 1) + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 3 ) ) == 0 ) + { + int trailer_field; + + end2 = p + len; + + if( ( ret = asn1_get_int( &p, end2, &trailer_field ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p != end2 ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( trailer_field != 1 ) + return( POLARSSL_ERR_X509_INVALID_ALG ); + } + else if( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( POLARSSL_ERR_X509_INVALID_ALG + ret ); + + if( p != end ) + return( POLARSSL_ERR_X509_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} +#endif /* POLARSSL_X509_RSASSA_PSS_SUPPORT */ + +/* + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_get_attr_type_value( unsigned char **p, + const unsigned char *end, + x509_name *cur ) +{ + int ret; + size_t len; + x509_buf *oid; + x509_buf *val; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_NAME + ret ); + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_NAME + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + oid = &cur->oid; + oid->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &oid->len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_NAME + ret ); + + oid->p = *p; + *p += oid->len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_NAME + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && + **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && + **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING ) + return( POLARSSL_ERR_X509_INVALID_NAME + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + val = &cur->val; + val->tag = *(*p)++; + + if( ( ret = asn1_get_len( p, end, &val->len ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_NAME + ret ); + + val->p = *p; + *p += val->len; + + cur->next = NULL; + + return( 0 ); +} + +/* + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +int x509_get_name( unsigned char **p, const unsigned char *end, + x509_name *cur ) +{ + int ret; + size_t len; + const unsigned char *end2; + x509_name *use; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SET ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_NAME + ret ); + + end2 = end; + end = *p + len; + use = cur; + + do + { + if( ( ret = x509_get_attr_type_value( p, end, use ) ) != 0 ) + return( ret ); + + if( *p != end ) + { + use->next = (x509_name *) polarssl_malloc( + sizeof( x509_name ) ); + + if( use->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memset( use->next, 0, sizeof( x509_name ) ); + + use = use->next; + } + } + while( *p != end ); + + /* + * recurse until end of SEQUENCE is reached + */ + if( *p == end2 ) + return( 0 ); + + cur->next = (x509_name *) polarssl_malloc( + sizeof( x509_name ) ); + + if( cur->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memset( cur->next, 0, sizeof( x509_name ) ); + + return( x509_get_name( p, end2, cur->next ) ); +} + +/* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + */ +int x509_get_time( unsigned char **p, const unsigned char *end, + x509_time *time ) +{ + int ret; + size_t len; + char date[64]; + unsigned char tag; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_DATE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + tag = **p; + + if( tag == ASN1_UTC_TIME ) + { + (*p)++; + ret = asn1_get_len( p, end, &len ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_INVALID_DATE + ret ); + + memset( date, 0, sizeof( date ) ); + memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? + len : sizeof( date ) - 1 ); + + if( sscanf( date, "%2d%2d%2d%2d%2d%2dZ", + &time->year, &time->mon, &time->day, + &time->hour, &time->min, &time->sec ) < 5 ) + return( POLARSSL_ERR_X509_INVALID_DATE ); + + time->year += 100 * ( time->year < 50 ); + time->year += 1900; + + *p += len; + + return( 0 ); + } + else if( tag == ASN1_GENERALIZED_TIME ) + { + (*p)++; + ret = asn1_get_len( p, end, &len ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_INVALID_DATE + ret ); + + memset( date, 0, sizeof( date ) ); + memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? + len : sizeof( date ) - 1 ); + + if( sscanf( date, "%4d%2d%2d%2d%2d%2dZ", + &time->year, &time->mon, &time->day, + &time->hour, &time->min, &time->sec ) < 5 ) + return( POLARSSL_ERR_X509_INVALID_DATE ); + + *p += len; + + return( 0 ); + } + else + return( POLARSSL_ERR_X509_INVALID_DATE + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); +} + +int x509_get_sig( unsigned char **p, const unsigned char *end, x509_buf *sig ) +{ + int ret; + size_t len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_SIGNATURE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + sig->tag = **p; + + if( ( ret = asn1_get_bitstring_null( p, end, &len ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_SIGNATURE + ret ); + + sig->len = len; + sig->p = *p; + + *p += len; + + return( 0 ); +} + +/* + * Get signature algorithm from alg OID and optional parameters + */ +int x509_get_sig_alg( const x509_buf *sig_oid, const x509_buf *sig_params, + md_type_t *md_alg, pk_type_t *pk_alg, + void **sig_opts ) +{ + int ret; + + if( *sig_opts != NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + if( ( ret = oid_get_sig_alg( sig_oid, md_alg, pk_alg ) ) != 0 ) + return( POLARSSL_ERR_X509_UNKNOWN_SIG_ALG + ret ); + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + if( *pk_alg == POLARSSL_PK_RSASSA_PSS ) + { + pk_rsassa_pss_options *pss_opts; + + pss_opts = polarssl_malloc( sizeof( pk_rsassa_pss_options ) ); + if( pss_opts == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + ret = x509_get_rsassa_pss_params( sig_params, + md_alg, + &pss_opts->mgf1_hash_id, + &pss_opts->expected_salt_len ); + if( ret != 0 ) + { + polarssl_free( pss_opts ); + return( ret ); + } + + *sig_opts = (void *) pss_opts; + } + else +#endif /* POLARSSL_X509_RSASSA_PSS_SUPPORT */ + { + /* Make sure parameters are absent or NULL */ + if( ( sig_params->tag != ASN1_NULL && sig_params->tag != 0 ) || + sig_params->len != 0 ) + return( POLARSSL_ERR_X509_INVALID_ALG ); + } + + return( 0 ); +} + +/* + * X.509 Extensions (No parsing of extensions, pointer should + * be either manually updated or extensions should be parsed! + */ +int x509_get_ext( unsigned char **p, const unsigned char *end, + x509_buf *ext, int tag ) +{ + int ret; + size_t len; + + if( *p == end ) + return( 0 ); + + ext->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &ext->len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | tag ) ) != 0 ) + return( ret ); + + ext->p = *p; + end = *p + ext->len; + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( end != *p + len ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load all data from a file into a given buffer. + */ +int x509_load_file( const char *path, unsigned char **buf, size_t *n ) +{ + FILE *f; + long size; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + if( ( size = ftell( f ) ) == -1 ) + { + fclose( f ); + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + } + fseek( f, 0, SEEK_SET ); + + *n = (size_t) size; + + if( *n + 1 == 0 || + ( *buf = (unsigned char *) polarssl_malloc( *n + 1 ) ) == NULL ) + { + fclose( f ); + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + if( fread( *buf, 1, *n, f ) != *n ) + { + fclose( f ); + polarssl_free( *buf ); + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + } + + fclose( f ); + + (*buf)[*n] = '\0'; + + return( 0 ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +static int compat_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if( res < 0 ) + return( (int) size + 20 ); + + return( res ); +} + +#define snprintf compat_snprintf +#endif /* _MSC_VER && !snprintf && !EFIX64 && !EFI32 */ + +#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( -1 ); \ + \ + if( (unsigned int) ret > n ) { \ + p[n - 1] = '\0'; \ + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); \ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +/* + * Store the name in printable form into buf; no more + * than size characters will be written + */ +int x509_dn_gets( char *buf, size_t size, const x509_name *dn ) +{ + int ret; + size_t i, n; + unsigned char c; + const x509_name *name; + const char *short_name = NULL; + char s[128], *p; + + memset( s, 0, sizeof( s ) ); + + name = dn; + p = buf; + n = size; + + while( name != NULL ) + { + if( !name->oid.p ) + { + name = name->next; + continue; + } + + if( name != dn ) + { + ret = snprintf( p, n, ", " ); + SAFE_SNPRINTF(); + } + + ret = oid_get_attr_short_name( &name->oid, &short_name ); + + if( ret == 0 ) + ret = snprintf( p, n, "%s=", short_name ); + else + ret = snprintf( p, n, "\?\?=" ); + SAFE_SNPRINTF(); + + for( i = 0; i < name->val.len; i++ ) + { + if( i >= sizeof( s ) - 1 ) + break; + + c = name->val.p[i]; + if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) + s[i] = '?'; + else s[i] = c; + } + s[i] = '\0'; + ret = snprintf( p, n, "%s", s ); + SAFE_SNPRINTF(); + name = name->next; + } + + return( (int) ( size - n ) ); +} + +/* + * Store the serial in printable form into buf; no more + * than size characters will be written + */ +int x509_serial_gets( char *buf, size_t size, const x509_buf *serial ) +{ + int ret; + size_t i, n, nr; + char *p; + + p = buf; + n = size; + + nr = ( serial->len <= 32 ) + ? serial->len : 28; + + for( i = 0; i < nr; i++ ) + { + if( i == 0 && nr > 1 && serial->p[i] == 0x0 ) + continue; + + ret = snprintf( p, n, "%02X%s", + serial->p[i], ( i < nr - 1 ) ? ":" : "" ); + SAFE_SNPRINTF(); + } + + if( nr != serial->len ) + { + ret = snprintf( p, n, "...." ); + SAFE_SNPRINTF(); + } + + return( (int) ( size - n ) ); +} + +/* + * Helper for writing signature algorithms + */ +int x509_sig_alg_gets( char *buf, size_t size, const x509_buf *sig_oid, + pk_type_t pk_alg, md_type_t md_alg, + const void *sig_opts ) +{ + int ret; + char *p = buf; + size_t n = size; + const char *desc = NULL; + + ret = oid_get_sig_alg_desc( sig_oid, &desc ); + if( ret != 0 ) + ret = snprintf( p, n, "???" ); + else + ret = snprintf( p, n, "%s", desc ); + SAFE_SNPRINTF(); + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + if( pk_alg == POLARSSL_PK_RSASSA_PSS ) + { + const pk_rsassa_pss_options *pss_opts; + const md_info_t *md_info, *mgf_md_info; + + pss_opts = (const pk_rsassa_pss_options *) sig_opts; + + md_info = md_info_from_type( md_alg ); + mgf_md_info = md_info_from_type( pss_opts->mgf1_hash_id ); + + ret = snprintf( p, n, " (%s, MGF1-%s, 0x%02X)", + md_info ? md_info->name : "???", + mgf_md_info ? mgf_md_info->name : "???", + pss_opts->expected_salt_len ); + SAFE_SNPRINTF(); + } +#else + ((void) pk_alg); + ((void) md_alg); + ((void) sig_opts); +#endif /* POLARSSL_X509_RSASSA_PSS_SUPPORT */ + + return( (int) size - n ); +} + +/* + * Helper for writing "RSA key size", "EC key size", etc + */ +int x509_key_size_helper( char *buf, size_t size, const char *name ) +{ + char *p = buf; + size_t n = size; + int ret; + + if( strlen( name ) + sizeof( " key size" ) > size ) + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); + + ret = snprintf( p, n, "%s key size", name ); + SAFE_SNPRINTF(); + + return( 0 ); +} + +/* + * Return an informational string describing the given OID + */ +const char *x509_oid_get_description( x509_buf *oid ) +{ + const char *desc = NULL; + int ret; + + ret = oid_get_extended_key_usage( oid, &desc ); + + if( ret != 0 ) + return( NULL ); + + return( desc ); +} + +/* Return the x.y.z.... style numeric string for the given OID */ +int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid ) +{ + return oid_get_numeric_string( buf, size, oid ); +} + +/* + * Return 0 if the x509_time is still valid, or 1 otherwise. + */ +#if defined(POLARSSL_HAVE_TIME) + +static void x509_get_current_time( x509_time *now ) +{ +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + SYSTEMTIME st; + + GetSystemTime( &st ); + + now->year = st.wYear; + now->mon = st.wMonth; + now->day = st.wDay; + now->hour = st.wHour; + now->min = st.wMinute; + now->sec = st.wSecond; +#else + struct tm lt; + time_t tt; + + tt = time( NULL ); + gmtime_r( &tt, < ); + + now->year = lt.tm_year + 1900; + now->mon = lt.tm_mon + 1; + now->day = lt.tm_mday; + now->hour = lt.tm_hour; + now->min = lt.tm_min; + now->sec = lt.tm_sec; +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +} + +/* + * Return 0 if before <= after, 1 otherwise + */ +static int x509_check_time( const x509_time *before, const x509_time *after ) +{ + if( before->year > after->year ) + return( 1 ); + + if( before->year == after->year && + before->mon > after->mon ) + return( 1 ); + + if( before->year == after->year && + before->mon == after->mon && + before->day > after->day ) + return( 1 ); + + if( before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour > after->hour ) + return( 1 ); + + if( before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour == after->hour && + before->min > after->min ) + return( 1 ); + + if( before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour == after->hour && + before->min == after->min && + before->sec > after->sec ) + return( 1 ); + + return( 0 ); +} + +int x509_time_expired( const x509_time *to ) +{ + x509_time now; + + x509_get_current_time( &now ); + + return( x509_check_time( &now, to ) ); +} + +int x509_time_future( const x509_time *from ) +{ + x509_time now; + + x509_get_current_time( &now ); + + return( x509_check_time( from, &now ) ); +} + +#else /* POLARSSL_HAVE_TIME */ + +int x509_time_expired( const x509_time *to ) +{ + ((void) to); + return( 0 ); +} + +int x509_time_future( const x509_time *from ) +{ + ((void) from); + return( 0 ); +} +#endif /* POLARSSL_HAVE_TIME */ + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/x509_crt.h" +#include "polarssl/certs.h" + +/* + * Checkup routine + */ +int x509_self_test( int verbose ) +{ +#if defined(POLARSSL_CERTS_C) && defined(POLARSSL_SHA1_C) + int ret; + int flags; + x509_crt cacert; + x509_crt clicert; + + if( verbose != 0 ) + polarssl_printf( " X.509 certificate load: " ); + + x509_crt_init( &clicert ); + + ret = x509_crt_parse( &clicert, (const unsigned char *) test_cli_crt, + strlen( test_cli_crt ) ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( ret ); + } + + x509_crt_init( &cacert ); + + ret = x509_crt_parse( &cacert, (const unsigned char *) test_ca_crt, + strlen( test_ca_crt ) ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + return( ret ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n X.509 signature verify: "); + + ret = x509_crt_verify( &clicert, &cacert, NULL, NULL, &flags, NULL, NULL ); + if( ret != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + polarssl_printf( "ret = %d, &flags = %04x\n", ret, flags ); + + return( ret ); + } + + if( verbose != 0 ) + polarssl_printf( "passed\n\n"); + + x509_crt_free( &cacert ); + x509_crt_free( &clicert ); + + return( 0 ); +#else + ((void) verbose); + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); +#endif /* POLARSSL_CERTS_C && POLARSSL_SHA1_C */ +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_X509_USE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509_create.c b/component/common/network/ssl/polarssl-1.3.8/library/x509_create.c new file mode 100644 index 0000000..1019313 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509_create.c @@ -0,0 +1,318 @@ +/* + * X.509 base functions for creating certificates / CSRs + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CREATE_C) + +#include "polarssl/x509.h" +#include "polarssl/asn1write.h" +#include "polarssl/oid.h" + +#if defined(_MSC_VER) && !defined strncasecmp && !defined(EFIX64) && \ + !defined(EFI32) +#define strncasecmp _strnicmp +#endif + +typedef struct { + const char *name; + size_t name_len; + const char*oid; +} x509_attr_descriptor_t; + +#define ADD_STRLEN( s ) s, sizeof( s ) - 1 + +static const x509_attr_descriptor_t x509_attrs[] = +{ + { ADD_STRLEN( "CN" ), OID_AT_CN }, + { ADD_STRLEN( "commonName" ), OID_AT_CN }, + { ADD_STRLEN( "C" ), OID_AT_COUNTRY }, + { ADD_STRLEN( "countryName" ), OID_AT_COUNTRY }, + { ADD_STRLEN( "O" ), OID_AT_ORGANIZATION }, + { ADD_STRLEN( "organizationName" ), OID_AT_ORGANIZATION }, + { ADD_STRLEN( "L" ), OID_AT_LOCALITY }, + { ADD_STRLEN( "locality" ), OID_AT_LOCALITY }, + { ADD_STRLEN( "R" ), OID_PKCS9_EMAIL }, + { ADD_STRLEN( "OU" ), OID_AT_ORG_UNIT }, + { ADD_STRLEN( "organizationalUnitName" ), OID_AT_ORG_UNIT }, + { ADD_STRLEN( "ST" ), OID_AT_STATE }, + { ADD_STRLEN( "stateOrProvinceName" ), OID_AT_STATE }, + { ADD_STRLEN( "emailAddress" ), OID_PKCS9_EMAIL }, + { ADD_STRLEN( "serialNumber" ), OID_AT_SERIAL_NUMBER }, + { ADD_STRLEN( "postalAddress" ), OID_AT_POSTAL_ADDRESS }, + { ADD_STRLEN( "postalCode" ), OID_AT_POSTAL_CODE }, + { ADD_STRLEN( "dnQualifier" ), OID_AT_DN_QUALIFIER }, + { ADD_STRLEN( "title" ), OID_AT_TITLE }, + { ADD_STRLEN( "surName" ), OID_AT_SUR_NAME }, + { ADD_STRLEN( "SN" ), OID_AT_SUR_NAME }, + { ADD_STRLEN( "givenName" ), OID_AT_GIVEN_NAME }, + { ADD_STRLEN( "GN" ), OID_AT_GIVEN_NAME }, + { ADD_STRLEN( "initials" ), OID_AT_INITIALS }, + { ADD_STRLEN( "pseudonym" ), OID_AT_PSEUDONYM }, + { ADD_STRLEN( "generationQualifier" ), OID_AT_GENERATION_QUALIFIER }, + { ADD_STRLEN( "domainComponent" ), OID_DOMAIN_COMPONENT }, + { ADD_STRLEN( "DC" ), OID_DOMAIN_COMPONENT }, + { NULL, 0, NULL } +}; + +static const char *x509_at_oid_from_name( const char *name, size_t name_len ) +{ + const x509_attr_descriptor_t *cur; + + for( cur = x509_attrs; cur->name != NULL; cur++ ) + if( cur->name_len == name_len && + strncasecmp( cur->name, name, name_len ) == 0 ) + break; + + return( cur->oid ); +} + +int x509_string_to_names( asn1_named_data **head, const char *name ) +{ + int ret = 0; + const char *s = name, *c = s; + const char *end = s + strlen( s ); + const char *oid = NULL; + int in_tag = 1; + + /* Clear existing chain if present */ + asn1_free_named_data_list( head ); + + while( c <= end ) + { + if( in_tag && *c == '=' ) + { + if( ( oid = x509_at_oid_from_name( s, c - s ) ) == NULL ) + { + ret = POLARSSL_ERR_X509_UNKNOWN_OID; + goto exit; + } + + s = c + 1; + in_tag = 0; + } + + if( !in_tag && ( *c == ',' || c == end ) ) + { + if( asn1_store_named_data( head, oid, strlen( oid ), + (unsigned char *) s, + c - s ) == NULL ) + { + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + while( c < end && *(c + 1) == ' ' ) + c++; + + s = c + 1; + in_tag = 1; + } + c++; + } + +exit: + + return( ret ); +} + +/* The first byte of the value in the asn1_named_data structure is reserved + * to store the critical boolean for us + */ +int x509_set_extension( asn1_named_data **head, const char *oid, size_t oid_len, + int critical, const unsigned char *val, size_t val_len ) +{ + asn1_named_data *cur; + + if( ( cur = asn1_store_named_data( head, oid, oid_len, + NULL, val_len + 1 ) ) == NULL ) + { + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + cur->val.p[0] = critical; + memcpy( cur->val.p + 1, val, val_len ); + + return( 0 ); +} + +/* + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_write_name( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + const unsigned char *name, size_t name_len ) +{ + int ret; + size_t len = 0; + + // Write PrintableString for all except OID_PKCS9_EMAIL + // + if( OID_SIZE( OID_PKCS9_EMAIL ) == oid_len && + memcmp( oid, OID_PKCS9_EMAIL, oid_len ) == 0 ) + { + ASN1_CHK_ADD( len, asn1_write_ia5_string( p, start, + (const char *) name, + name_len ) ); + } + else + { + ASN1_CHK_ADD( len, asn1_write_printable_string( p, start, + (const char *) name, + name_len ) ); + } + + // Write OID + // + ASN1_CHK_ADD( len, asn1_write_oid( p, start, oid, oid_len ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | + ASN1_SET ) ); + + return( (int) len ); +} + +int x509_write_names( unsigned char **p, unsigned char *start, + asn1_named_data *first ) +{ + int ret; + size_t len = 0; + asn1_named_data *cur = first; + + while( cur != NULL ) + { + ASN1_CHK_ADD( len, x509_write_name( p, start, (char *) cur->oid.p, + cur->oid.len, + cur->val.p, cur->val.len ) ); + cur = cur->next; + } + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +int x509_write_sig( unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + unsigned char *sig, size_t size ) +{ + int ret; + size_t len = 0; + + if( *p - start < (int) size + 1 ) + return( POLARSSL_ERR_ASN1_BUF_TOO_SMALL ); + + len = size; + (*p) -= len; + memcpy( *p, sig, len ); + + *--(*p) = 0; + len += 1; + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_BIT_STRING ) ); + + // Write OID + // + ASN1_CHK_ADD( len, asn1_write_algorithm_identifier( p, start, oid, + oid_len, 0 ) ); + + return( (int) len ); +} + +static int x509_write_extension( unsigned char **p, unsigned char *start, + asn1_named_data *ext ) +{ + int ret; + size_t len = 0; + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, ext->val.p + 1, + ext->val.len - 1 ) ); + ASN1_CHK_ADD( len, asn1_write_len( p, start, ext->val.len - 1 ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_OCTET_STRING ) ); + + if( ext->val.p[0] != 0 ) + { + ASN1_CHK_ADD( len, asn1_write_bool( p, start, 1 ) ); + } + + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, ext->oid.p, + ext->oid.len ) ); + ASN1_CHK_ADD( len, asn1_write_len( p, start, ext->oid.len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_OID ) ); + + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +/* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains the DER encoding of an ASN.1 value + * -- corresponding to the extension type identified + * -- by extnID + * } + */ +int x509_write_extensions( unsigned char **p, unsigned char *start, + asn1_named_data *first ) +{ + int ret; + size_t len = 0; + asn1_named_data *cur_ext = first; + + while( cur_ext != NULL ) + { + ASN1_CHK_ADD( len, x509_write_extension( p, start, cur_ext ) ); + cur_ext = cur_ext->next; + } + + return( (int) len ); +} + +#endif /* POLARSSL_X509_CREATE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509_crl.c b/component/common/network/ssl/polarssl-1.3.8/library/x509_crl.c new file mode 100644 index 0000000..7dd53c2 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509_crl.c @@ -0,0 +1,768 @@ +/* + * X.509 Certidicate Revocation List (CRL) parsing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CRL_PARSE_C) + +#include "polarssl/x509_crl.h" +#include "polarssl/oid.h" +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#include +#else +#include +#endif + +#if defined(POLARSSL_FS_IO) || defined(EFIX64) || defined(EFI32) +#include +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Version ::= INTEGER { v1(0), v2(1) } + */ +static int x509_crl_get_version( unsigned char **p, + const unsigned char *end, + int *ver ) +{ + int ret; + + if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + *ver = 0; + return( 0 ); + } + + return( POLARSSL_ERR_X509_INVALID_VERSION + ret ); + } + + return( 0 ); +} + +/* + * X.509 CRL v2 extensions (no extensions parsed yet.) + */ +static int x509_get_crl_ext( unsigned char **p, + const unsigned char *end, + x509_buf *ext ) +{ + int ret; + size_t len = 0; + + /* Get explicit tag */ + if( ( ret = x509_get_ext( p, end, ext, 0) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + while( *p < end ) + { + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + *p += len; + } + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 CRL v2 entry extensions (no extensions parsed yet.) + */ +static int x509_get_crl_entry_ext( unsigned char **p, + const unsigned char *end, + x509_buf *ext ) +{ + int ret; + size_t len = 0; + + /* OPTIONAL */ + if( end <= *p ) + return( 0 ); + + ext->tag = **p; + ext->p = *p; + + /* + * Get CRL-entry extension sequence header + * crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2 + */ + if( ( ret = asn1_get_tag( p, end, &ext->len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + ext->p = NULL; + return( 0 ); + } + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + } + + end = *p + ext->len; + + if( end != *p + ext->len ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + *p += len; + } + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 CRL Entries + */ +static int x509_get_entries( unsigned char **p, + const unsigned char *end, + x509_crl_entry *entry ) +{ + int ret; + size_t entry_len; + x509_crl_entry *cur_entry = entry; + + if( *p == end ) + return( 0 ); + + if( ( ret = asn1_get_tag( p, end, &entry_len, + ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + end = *p + entry_len; + + while( *p < end ) + { + size_t len2; + const unsigned char *end2; + + if( ( ret = asn1_get_tag( p, end, &len2, + ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) + { + return( ret ); + } + + cur_entry->raw.tag = **p; + cur_entry->raw.p = *p; + cur_entry->raw.len = len2; + end2 = *p + len2; + + if( ( ret = x509_get_serial( p, end2, &cur_entry->serial ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_time( p, end2, + &cur_entry->revocation_date ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_crl_entry_ext( p, end2, + &cur_entry->entry_ext ) ) != 0 ) + return( ret ); + + if( *p < end ) + { + cur_entry->next = polarssl_malloc( sizeof( x509_crl_entry ) ); + + if( cur_entry->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + cur_entry = cur_entry->next; + memset( cur_entry, 0, sizeof( x509_crl_entry ) ); + } + } + + return( 0 ); +} + +/* + * Parse one or more CRLs and add them to the chained list + */ +int x509_crl_parse( x509_crl *chain, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + x509_crl *crl; + x509_buf sig_params1, sig_params2; + +#if defined(POLARSSL_PEM_PARSE_C) + size_t use_len; + pem_context pem; +#endif + + memset( &sig_params1, 0, sizeof( x509_buf ) ); + memset( &sig_params2, 0, sizeof( x509_buf ) ); + + crl = chain; + + /* + * Check for valid input + */ + if( crl == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + while( crl->version != 0 && crl->next != NULL ) + crl = crl->next; + + /* + * Add new CRL on the end of the chain if needed. + */ + if( crl->version != 0 && crl->next == NULL ) + { + crl->next = (x509_crl *) polarssl_malloc( sizeof( x509_crl ) ); + + if( crl->next == NULL ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + crl = crl->next; + x509_crl_init( crl ); + } + +#if defined(POLARSSL_PEM_PARSE_C) + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN X509 CRL-----", + "-----END X509 CRL-----", + buf, NULL, 0, &use_len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + buflen -= use_len; + buf += use_len; + + /* + * Steal PEM buffer + */ + p = pem.buf; + pem.buf = NULL; + len = pem.buflen; + pem_free( &pem ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + else +#endif /* POLARSSL_PEM_PARSE_C */ + { + /* + * nope, copy the raw DER data + */ + p = (unsigned char *) polarssl_malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + buflen = 0; + } + + crl->raw.p = p; + crl->raw.len = len; + end = p + len; + + /* + * CertificateList ::= SEQUENCE { + * tbsCertList TBSCertList, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT ); + } + + if( len != (size_t) ( end - p ) ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + /* + * TBSCertList ::= SEQUENCE { + */ + crl->tbs.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + end = p + len; + crl->tbs.len = end - crl->tbs.p; + + /* + * Version ::= INTEGER OPTIONAL { v1(0), v2(1) } + * -- if present, MUST be v2 + * + * signature AlgorithmIdentifier + */ + if( ( ret = x509_crl_get_version( &p, end, &crl->version ) ) != 0 || + ( ret = x509_get_alg( &p, end, &crl->sig_oid1, &sig_params1 ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + crl->version++; + + if( crl->version > 2 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_UNKNOWN_VERSION ); + } + + if( ( ret = x509_get_sig_alg( &crl->sig_oid1, &sig_params1, + &crl->sig_md, &crl->sig_pk, + &crl->sig_opts ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_UNKNOWN_SIG_ALG ); + } + + /* + * issuer Name + */ + crl->issuer_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &crl->issuer ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + crl->issuer_raw.len = p - crl->issuer_raw.p; + + /* + * thisUpdate Time + * nextUpdate Time OPTIONAL + */ + if( ( ret = x509_get_time( &p, end, &crl->this_update ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( ( ret = x509_get_time( &p, end, &crl->next_update ) ) != 0 ) + { + if( ret != ( POLARSSL_ERR_X509_INVALID_DATE + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) && + ret != ( POLARSSL_ERR_X509_INVALID_DATE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ) ) + { + x509_crl_free( crl ); + return( ret ); + } + } + + /* + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate Time, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, MUST be v2 + * } OPTIONAL + */ + if( ( ret = x509_get_entries( &p, end, &crl->entry ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + /* + * crlExtensions EXPLICIT Extensions OPTIONAL + * -- if present, MUST be v2 + */ + if( crl->version == 2 ) + { + ret = x509_get_crl_ext( &p, end, &crl->crl_ext ); + + if( ret != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + } + + if( p != end ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + end = crl->raw.p + crl->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if( ( ret = x509_get_alg( &p, end, &crl->sig_oid2, &sig_params2 ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( crl->sig_oid1.len != crl->sig_oid2.len || + memcmp( crl->sig_oid1.p, crl->sig_oid2.p, crl->sig_oid1.len ) != 0 || + sig_params1.len != sig_params2.len || + memcmp( sig_params1.p, sig_params2.p, sig_params1.len ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_SIG_MISMATCH ); + } + + if( ( ret = x509_get_sig( &p, end, &crl->sig ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( p != end ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( buflen > 0 ) + { + crl->next = (x509_crl *) polarssl_malloc( sizeof( x509_crl ) ); + + if( crl->next == NULL ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + crl = crl->next; + x509_crl_init( crl ); + + return( x509_crl_parse( crl, buf, buflen ) ); + } + + return( 0 ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load one or more CRLs and add them to the chained list + */ +int x509_crl_parse_file( x509_crl *chain, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = x509_load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509_crl_parse( chain, buf, n ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +static int compat_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if( res < 0 ) + return( (int) size + 20 ); + + return( res ); +} + +#define snprintf compat_snprintf +#endif /* _MSC_VER && !snprintf && !EFIX64 && !EFI32 */ + +#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( -1 ); \ + \ + if( (unsigned int) ret > n ) { \ + p[n - 1] = '\0'; \ + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); \ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +/* + * Return an informational string about the certificate. + */ +#define BEFORE_COLON 14 +#define BC "14" +/* + * Return an informational string about the CRL. + */ +int x509_crl_info( char *buf, size_t size, const char *prefix, + const x509_crl *crl ) +{ + int ret; + size_t n; + char *p; + const x509_crl_entry *entry; + + p = buf; + n = size; + + ret = snprintf( p, n, "%sCRL version : %d", + prefix, crl->version ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissuer name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509_dn_gets( p, n, &crl->issuer ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sthis update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->this_update.year, crl->this_update.mon, + crl->this_update.day, crl->this_update.hour, + crl->this_update.min, crl->this_update.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%snext update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->next_update.year, crl->next_update.mon, + crl->next_update.day, crl->next_update.hour, + crl->next_update.min, crl->next_update.sec ); + SAFE_SNPRINTF(); + + entry = &crl->entry; + + ret = snprintf( p, n, "\n%sRevoked certificates:", + prefix ); + SAFE_SNPRINTF(); + + while( entry != NULL && entry->raw.len != 0 ) + { + ret = snprintf( p, n, "\n%sserial number: ", + prefix ); + SAFE_SNPRINTF(); + + ret = x509_serial_gets( p, n, &entry->serial ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, " revocation date: " \ + "%04d-%02d-%02d %02d:%02d:%02d", + entry->revocation_date.year, entry->revocation_date.mon, + entry->revocation_date.day, entry->revocation_date.hour, + entry->revocation_date.min, entry->revocation_date.sec ); + SAFE_SNPRINTF(); + + entry = entry->next; + } + + ret = snprintf( p, n, "\n%ssigned using : ", prefix ); + SAFE_SNPRINTF(); + + ret = x509_sig_alg_gets( p, n, &crl->sig_oid1, crl->sig_pk, crl->sig_md, + crl->sig_opts ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n" ); + SAFE_SNPRINTF(); + + return( (int) ( size - n ) ); +} + +/* + * Initialize a CRL chain + */ +void x509_crl_init( x509_crl *crl ) +{ + memset( crl, 0, sizeof(x509_crl) ); +} + +/* + * Unallocate all CRL data + */ +void x509_crl_free( x509_crl *crl ) +{ + x509_crl *crl_cur = crl; + x509_crl *crl_prv; + x509_name *name_cur; + x509_name *name_prv; + x509_crl_entry *entry_cur; + x509_crl_entry *entry_prv; + + if( crl == NULL ) + return; + + do + { +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + polarssl_free( crl_cur->sig_opts ); +#endif + + name_cur = crl_cur->issuer.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + polarssl_zeroize( name_prv, sizeof( x509_name ) ); + polarssl_free( name_prv ); + } + + entry_cur = crl_cur->entry.next; + while( entry_cur != NULL ) + { + entry_prv = entry_cur; + entry_cur = entry_cur->next; + polarssl_zeroize( entry_prv, sizeof( x509_crl_entry ) ); + polarssl_free( entry_prv ); + } + + if( crl_cur->raw.p != NULL ) + { + polarssl_zeroize( crl_cur->raw.p, crl_cur->raw.len ); + polarssl_free( crl_cur->raw.p ); + } + + crl_cur = crl_cur->next; + } + while( crl_cur != NULL ); + + crl_cur = crl; + do + { + crl_prv = crl_cur; + crl_cur = crl_cur->next; + + polarssl_zeroize( crl_prv, sizeof( x509_crl ) ); + if( crl_prv != crl ) + polarssl_free( crl_prv ); + } + while( crl_cur != NULL ); +} + +#endif /* POLARSSL_X509_CRL_PARSE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509_crt.c b/component/common/network/ssl/polarssl-1.3.8/library/x509_crt.c new file mode 100644 index 0000000..03cdda8 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509_crt.c @@ -0,0 +1,2018 @@ +/* + * X.509 certificate parsing and verification + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CRT_PARSE_C) + +#include "polarssl/x509_crt.h" +#include "polarssl/oid.h" +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#if defined(POLARSSL_THREADING_C) +#include "polarssl/threading.h" +#endif + +#include +#include +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) +#include +#else +#include +#endif + +#if defined(EFIX64) || defined(EFI32) +#include +#endif + +#if defined(POLARSSL_FS_IO) +#include +#if !defined(_WIN32) || defined(EFIX64) || defined(EFI32) +#include +#include +#include +#endif +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static int x509_get_version( unsigned char **p, + const unsigned char *end, + int *ver ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + *ver = 0; + return( 0 ); + } + + return( ret ); + } + + end = *p + len; + + if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_VERSION + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_VERSION + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + */ +static int x509_get_dates( unsigned char **p, + const unsigned char *end, + x509_time *from, + x509_time *to ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_DATE + ret ); + + end = *p + len; + + if( ( ret = x509_get_time( p, end, from ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_time( p, end, to ) ) != 0 ) + return( ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_DATE + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 v2/v3 unique identifier (not parsed) + */ +static int x509_get_uid( unsigned char **p, + const unsigned char *end, + x509_buf *uid, int n ) +{ + int ret; + + if( *p == end ) + return( 0 ); + + uid->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &uid->len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + uid->p = *p; + *p += uid->len; + + return( 0 ); +} + +static int x509_get_basic_constraints( unsigned char **p, + const unsigned char *end, + int *ca_istrue, + int *max_pathlen ) +{ + int ret; + size_t len; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + *ca_istrue = 0; /* DEFAULT FALSE */ + *max_pathlen = 0; /* endless */ + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( *p == end ) + return( 0 ); + + if( ( ret = asn1_get_bool( p, end, ca_istrue ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + ret = asn1_get_int( p, end, ca_istrue ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( *ca_istrue != 0 ) + *ca_istrue = 1; + } + + if( *p == end ) + return( 0 ); + + if( ( ret = asn1_get_int( p, end, max_pathlen ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + (*max_pathlen)++; + + return( 0 ); +} + +static int x509_get_ns_cert_type( unsigned char **p, + const unsigned char *end, + unsigned char *ns_cert_type) +{ + int ret; + x509_bitstring bs = { 0, 0, NULL }; + + if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( bs.len != 1 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + /* Get actual bitstring */ + *ns_cert_type = *bs.p; + return( 0 ); +} + +static int x509_get_key_usage( unsigned char **p, + const unsigned char *end, + unsigned char *key_usage) +{ + int ret; + x509_bitstring bs = { 0, 0, NULL }; + + if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( bs.len < 1 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + /* Get actual bitstring */ + *key_usage = *bs.p; + return( 0 ); +} + +/* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static int x509_get_ext_key_usage( unsigned char **p, + const unsigned char *end, + x509_sequence *ext_key_usage) +{ + int ret; + + if( ( ret = asn1_get_sequence_of( p, end, ext_key_usage, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + /* Sequence length must be >= 1 */ + if( ext_key_usage->buf.p == NULL ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + return( 0 ); +} + +/* + * SubjectAltName ::= GeneralNames + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * NOTE: PolarSSL only parses and uses dNSName at this point. + */ +static int x509_get_subject_alt_name( unsigned char **p, + const unsigned char *end, + x509_sequence *subject_alt_name ) +{ + int ret; + size_t len, tag_len; + asn1_buf *buf; + unsigned char tag; + asn1_sequence *cur = subject_alt_name; + + /* Get main sequence tag */ + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( *p + len != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + tag = **p; + (*p)++; + if( ( ret = asn1_get_len( p, end, &tag_len ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + /* Skip everything but DNS name */ + if( tag != ( ASN1_CONTEXT_SPECIFIC | 2 ) ) + { + *p += tag_len; + continue; + } + + /* Allocate and assign next pointer */ + if( cur->buf.p != NULL ) + { + cur->next = (asn1_sequence *) polarssl_malloc( + sizeof( asn1_sequence ) ); + + if( cur->next == NULL ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_MALLOC_FAILED ); + + memset( cur->next, 0, sizeof( asn1_sequence ) ); + cur = cur->next; + } + + buf = &(cur->buf); + buf->tag = tag; + buf->p = *p; + buf->len = tag_len; + *p += buf->len; + } + + /* Set final sequence entry's next pointer to NULL */ + cur->next = NULL; + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 v3 extensions + * + * TODO: Perform all of the basic constraints tests required by the RFC + * TODO: Set values for undetected extensions to a sane default? + * + */ +static int x509_get_crt_ext( unsigned char **p, + const unsigned char *end, + x509_crt *crt ) +{ + int ret; + size_t len; + unsigned char *end_ext_data, *end_ext_octet; + + if( ( ret = x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + while( *p < end ) + { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + x509_buf extn_oid = {0, 0, NULL}; + int is_critical = 0; /* DEFAULT FALSE */ + int ext_type = 0; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + end_ext_data = *p + len; + + /* Get extension ID */ + extn_oid.tag = **p; + + if( ( ret = asn1_get_tag( p, end, &extn_oid.len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + extn_oid.p = *p; + *p += extn_oid.len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + /* Get optional critical */ + if( ( ret = asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 && + ( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + /* Data should be octet string type */ + if( ( ret = asn1_get_tag( p, end_ext_data, &len, + ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + ret ); + + end_ext_octet = *p + len; + + if( end_ext_octet != end_ext_data ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + /* + * Detect supported extensions + */ + ret = oid_get_x509_ext_type( &extn_oid, &ext_type ); + + if( ret != 0 ) + { + /* No parser found, skip extension */ + *p = end_ext_octet; + +#if !defined(POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) + if( is_critical ) + { + /* Data is marked as critical: fail */ + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + } +#endif + continue; + } + + crt->ext_types |= ext_type; + + switch( ext_type ) + { + case EXT_BASIC_CONSTRAINTS: + /* Parse basic constraints */ + if( ( ret = x509_get_basic_constraints( p, end_ext_octet, + &crt->ca_istrue, &crt->max_pathlen ) ) != 0 ) + return( ret ); + break; + + case EXT_KEY_USAGE: + /* Parse key usage */ + if( ( ret = x509_get_key_usage( p, end_ext_octet, + &crt->key_usage ) ) != 0 ) + return( ret ); + break; + + case EXT_EXTENDED_KEY_USAGE: + /* Parse extended key usage */ + if( ( ret = x509_get_ext_key_usage( p, end_ext_octet, + &crt->ext_key_usage ) ) != 0 ) + return( ret ); + break; + + case EXT_SUBJECT_ALT_NAME: + /* Parse subject alt name */ + if( ( ret = x509_get_subject_alt_name( p, end_ext_octet, + &crt->subject_alt_names ) ) != 0 ) + return( ret ); + break; + + case EXT_NS_CERT_TYPE: + /* Parse netscape certificate type */ + if( ( ret = x509_get_ns_cert_type( p, end_ext_octet, + &crt->ns_cert_type ) ) != 0 ) + return( ret ); + break; + + default: + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); + } + } + + if( *p != end ) + return( POLARSSL_ERR_X509_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * Parse and fill a single X.509 certificate in DER format + */ +static int x509_crt_parse_der_core( x509_crt *crt, const unsigned char *buf, + size_t buflen ) +{ + int ret; + size_t len; + unsigned char *p, *end, *crt_end; + x509_buf sig_params1, sig_params2; + + memset( &sig_params1, 0, sizeof( x509_buf ) ); + memset( &sig_params2, 0, sizeof( x509_buf ) ); + + /* + * Check for valid input + */ + if( crt == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + p = (unsigned char *) polarssl_malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + crt->raw.p = p; + crt->raw.len = len; + end = p + len; + + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT ); + } + + if( len > (size_t) ( end - p ) ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + crt_end = p + len; + + /* + * TBSCertificate ::= SEQUENCE { + */ + crt->tbs.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + end = p + len; + crt->tbs.len = end - crt->tbs.p; + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * signature AlgorithmIdentifier + */ + if( ( ret = x509_get_version( &p, end, &crt->version ) ) != 0 || + ( ret = x509_get_serial( &p, end, &crt->serial ) ) != 0 || + ( ret = x509_get_alg( &p, end, &crt->sig_oid1, + &sig_params1 ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + crt->version++; + + if( crt->version > 3 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_UNKNOWN_VERSION ); + } + + if( ( ret = x509_get_sig_alg( &crt->sig_oid1, &sig_params1, + &crt->sig_md, &crt->sig_pk, + &crt->sig_opts ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + /* + * issuer Name + */ + crt->issuer_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &crt->issuer ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + crt->issuer_raw.len = p - crt->issuer_raw.p; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + */ + if( ( ret = x509_get_dates( &p, end, &crt->valid_from, + &crt->valid_to ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + /* + * subject Name + */ + crt->subject_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + if( len && ( ret = x509_get_name( &p, p + len, &crt->subject ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + crt->subject_raw.len = p - crt->subject_raw.p; + + /* + * SubjectPublicKeyInfo + */ + if( ( ret = pk_parse_subpubkey( &p, end, &crt->pk ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + /* + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + */ + if( crt->version == 2 || crt->version == 3 ) + { + ret = x509_get_uid( &p, end, &crt->issuer_id, 1 ); + if( ret != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + } + + if( crt->version == 2 || crt->version == 3 ) + { + ret = x509_get_uid( &p, end, &crt->subject_id, 2 ); + if( ret != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + } + +#if !defined(POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3) + if( crt->version == 3 ) + { +#endif + ret = x509_get_crt_ext( &p, end, crt ); + if( ret != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } +#if !defined(POLARSSL_X509_ALLOW_EXTENSIONS_NON_V3) + } +#endif + + if( p != end ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + end = crt_end; + + /* + * } + * -- end of TBSCertificate + * + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if( ( ret = x509_get_alg( &p, end, &crt->sig_oid2, &sig_params2 ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + if( crt->sig_oid1.len != crt->sig_oid2.len || + memcmp( crt->sig_oid1.p, crt->sig_oid2.p, crt->sig_oid1.len ) != 0 || + sig_params1.len != sig_params2.len || + memcmp( sig_params1.p, sig_params2.p, sig_params1.len ) != 0 ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_SIG_MISMATCH ); + } + + if( ( ret = x509_get_sig( &p, end, &crt->sig ) ) != 0 ) + { + x509_crt_free( crt ); + return( ret ); + } + + if( p != end ) + { + x509_crt_free( crt ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + return( 0 ); +} + +/* + * Parse one X.509 certificate in DER format from a buffer and add them to a + * chained list + */ +int x509_crt_parse_der( x509_crt *chain, const unsigned char *buf, + size_t buflen ) +{ + int ret; + x509_crt *crt = chain, *prev = NULL; + + /* + * Check for valid input + */ + if( crt == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + while( crt->version != 0 && crt->next != NULL ) + { + prev = crt; + crt = crt->next; + } + + /* + * Add new certificate on the end of the chain if needed. + */ + if( crt->version != 0 && crt->next == NULL ) + { + crt->next = (x509_crt *) polarssl_malloc( sizeof( x509_crt ) ); + + if( crt->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + prev = crt; + crt = crt->next; + x509_crt_init( crt ); + } + + if( ( ret = x509_crt_parse_der_core( crt, buf, buflen ) ) != 0 ) + { + if( prev ) + prev->next = NULL; + + if( crt != chain ) + polarssl_free( crt ); + + return( ret ); + } + + return( 0 ); +} + +/* + * Parse one or more PEM certificates from a buffer and add them to the chained + * list + */ +int x509_crt_parse( x509_crt *chain, const unsigned char *buf, size_t buflen ) +{ + int success = 0, first_error = 0, total_failed = 0; + int buf_format = X509_FORMAT_DER; + + /* + * Check for valid input + */ + if( chain == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + /* + * Determine buffer content. Buffer contains either one DER certificate or + * one or more PEM certificates. + */ +#if defined(POLARSSL_PEM_PARSE_C) + if( strstr( (const char *) buf, "-----BEGIN CERTIFICATE-----" ) != NULL ) + buf_format = X509_FORMAT_PEM; +#endif + + if( buf_format == X509_FORMAT_DER ) + return x509_crt_parse_der( chain, buf, buflen ); + +#if defined(POLARSSL_PEM_PARSE_C) + if( buf_format == X509_FORMAT_PEM ) + { + int ret; + pem_context pem; + + while( buflen > 0 ) + { + size_t use_len; + pem_init( &pem ); + + ret = pem_read_buffer( &pem, + "-----BEGIN CERTIFICATE-----", + "-----END CERTIFICATE-----", + buf, NULL, 0, &use_len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + buflen -= use_len; + buf += use_len; + } + else if( ret == POLARSSL_ERR_PEM_BAD_INPUT_DATA ) + { + return( ret ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + { + pem_free( &pem ); + + /* + * PEM header and footer were found + */ + buflen -= use_len; + buf += use_len; + + if( first_error == 0 ) + first_error = ret; + + continue; + } + else + break; + + ret = x509_crt_parse_der( chain, pem.buf, pem.buflen ); + + pem_free( &pem ); + + if( ret != 0 ) + { + /* + * Quit parsing on a memory error + */ + if( ret == POLARSSL_ERR_X509_MALLOC_FAILED ) + return( ret ); + + if( first_error == 0 ) + first_error = ret; + + total_failed++; + continue; + } + + success = 1; + } + } +#endif /* POLARSSL_PEM_PARSE_C */ + + if( success ) + return( total_failed ); + else if( first_error ) + return( first_error ); + else + return( POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load one or more certificates and add them to the chained list + */ +int x509_crt_parse_file( x509_crt *chain, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = x509_load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509_crt_parse( chain, buf, n ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} + +#if defined(POLARSSL_THREADING_PTHREAD) +static threading_mutex_t readdir_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +int x509_crt_parse_path( x509_crt *chain, const char *path ) +{ + int ret = 0; +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + int w_ret; + WCHAR szDir[MAX_PATH]; + char filename[MAX_PATH]; + char *p; + int len = (int) strlen( path ); + + WIN32_FIND_DATAW file_data; + HANDLE hFind; + + if( len > MAX_PATH - 3 ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + memset( szDir, 0, sizeof(szDir) ); + memset( filename, 0, MAX_PATH ); + memcpy( filename, path, len ); + filename[len++] = '\\'; + p = filename + len; + filename[len++] = '*'; + + w_ret = MultiByteToWideChar( CP_ACP, 0, filename, len, szDir, + MAX_PATH - 3 ); + + hFind = FindFirstFileW( szDir, &file_data ); + if( hFind == INVALID_HANDLE_VALUE ) + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + + len = MAX_PATH - len; + do + { + memset( p, 0, len ); + + if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + continue; + + w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName, + lstrlenW( file_data.cFileName ), + p, len - 1, + NULL, NULL ); + + w_ret = x509_crt_parse_file( chain, filename ); + if( w_ret < 0 ) + ret++; + else + ret += w_ret; + } + while( FindNextFileW( hFind, &file_data ) != 0 ); + + if( GetLastError() != ERROR_NO_MORE_FILES ) + ret = POLARSSL_ERR_X509_FILE_IO_ERROR; + + FindClose( hFind ); +#else /* _WIN32 */ + int t_ret; + struct stat sb; + struct dirent *entry; + char entry_name[255]; + DIR *dir = opendir( path ); + + if( dir == NULL ) + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + +#if defined(POLARSSL_THREADING_PTHREAD) + if( ( ret = polarssl_mutex_lock( &readdir_mutex ) ) != 0 ) + return( ret ); +#endif + + while( ( entry = readdir( dir ) ) != NULL ) + { + snprintf( entry_name, sizeof entry_name, "%s/%s", path, entry->d_name ); + + if( stat( entry_name, &sb ) == -1 ) + { + closedir( dir ); + ret = POLARSSL_ERR_X509_FILE_IO_ERROR; + goto cleanup; + } + + if( !S_ISREG( sb.st_mode ) ) + continue; + + // Ignore parse errors + // + t_ret = x509_crt_parse_file( chain, entry_name ); + if( t_ret < 0 ) + ret++; + else + ret += t_ret; + } + closedir( dir ); + +cleanup: +#if defined(POLARSSL_THREADING_PTHREAD) + if( polarssl_mutex_unlock( &readdir_mutex ) != 0 ) + ret = POLARSSL_ERR_THREADING_MUTEX_ERROR; +#endif + +#endif /* _WIN32 */ + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +static int compat_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if( res < 0 ) + return( (int) size + 20 ); + + return( res ); +} + +#define snprintf compat_snprintf +#endif /* _MSC_VER && !snprintf && !EFIX64 && !EFI32 */ + +#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( -1 ); \ + \ + if( (unsigned int) ret > n ) { \ + p[n - 1] = '\0'; \ + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); \ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +static int x509_info_subject_alt_name( char **buf, size_t *size, + const x509_sequence *subject_alt_name ) +{ + size_t i; + size_t n = *size; + char *p = *buf; + const x509_sequence *cur = subject_alt_name; + const char *sep = ""; + size_t sep_len = 0; + + while( cur != NULL ) + { + if( cur->buf.len + sep_len >= n ) + { + *p = '\0'; + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); + } + + n -= cur->buf.len + sep_len; + for( i = 0; i < sep_len; i++ ) + *p++ = sep[i]; + for( i = 0; i < cur->buf.len; i++ ) + *p++ = cur->buf.p[i]; + + sep = ", "; + sep_len = 2; + + cur = cur->next; + } + + *p = '\0'; + + *size = n; + *buf = p; + + return( 0 ); +} + +#define PRINT_ITEM(i) \ + { \ + ret = snprintf( p, n, "%s" i, sep ); \ + SAFE_SNPRINTF(); \ + sep = ", "; \ + } + +#define CERT_TYPE(type,name) \ + if( ns_cert_type & type ) \ + PRINT_ITEM( name ); + +static int x509_info_cert_type( char **buf, size_t *size, + unsigned char ns_cert_type ) +{ + int ret; + size_t n = *size; + char *p = *buf; + const char *sep = ""; + + CERT_TYPE( NS_CERT_TYPE_SSL_CLIENT, "SSL Client" ); + CERT_TYPE( NS_CERT_TYPE_SSL_SERVER, "SSL Server" ); + CERT_TYPE( NS_CERT_TYPE_EMAIL, "Email" ); + CERT_TYPE( NS_CERT_TYPE_OBJECT_SIGNING, "Object Signing" ); + CERT_TYPE( NS_CERT_TYPE_RESERVED, "Reserved" ); + CERT_TYPE( NS_CERT_TYPE_SSL_CA, "SSL CA" ); + CERT_TYPE( NS_CERT_TYPE_EMAIL_CA, "Email CA" ); + CERT_TYPE( NS_CERT_TYPE_OBJECT_SIGNING_CA, "Object Signing CA" ); + + *size = n; + *buf = p; + + return( 0 ); +} + +#define KEY_USAGE(code,name) \ + if( key_usage & code ) \ + PRINT_ITEM( name ); + +static int x509_info_key_usage( char **buf, size_t *size, + unsigned char key_usage ) +{ + int ret; + size_t n = *size; + char *p = *buf; + const char *sep = ""; + + KEY_USAGE( KU_DIGITAL_SIGNATURE, "Digital Signature" ); + KEY_USAGE( KU_NON_REPUDIATION, "Non Repudiation" ); + KEY_USAGE( KU_KEY_ENCIPHERMENT, "Key Encipherment" ); + KEY_USAGE( KU_DATA_ENCIPHERMENT, "Data Encipherment" ); + KEY_USAGE( KU_KEY_AGREEMENT, "Key Agreement" ); + KEY_USAGE( KU_KEY_CERT_SIGN, "Key Cert Sign" ); + KEY_USAGE( KU_CRL_SIGN, "CRL Sign" ); + + *size = n; + *buf = p; + + return( 0 ); +} + +static int x509_info_ext_key_usage( char **buf, size_t *size, + const x509_sequence *extended_key_usage ) +{ + int ret; + const char *desc; + size_t n = *size; + char *p = *buf; + const x509_sequence *cur = extended_key_usage; + const char *sep = ""; + + while( cur != NULL ) + { + if( oid_get_extended_key_usage( &cur->buf, &desc ) != 0 ) + desc = "???"; + + ret = snprintf( p, n, "%s%s", sep, desc ); + SAFE_SNPRINTF(); + + sep = ", "; + + cur = cur->next; + } + + *size = n; + *buf = p; + + return( 0 ); +} + +/* + * Return an informational string about the certificate. + */ +#define BEFORE_COLON 18 +#define BC "18" +int x509_crt_info( char *buf, size_t size, const char *prefix, + const x509_crt *crt ) +{ + int ret; + size_t n; + char *p; + char key_size_str[BEFORE_COLON]; + + p = buf; + n = size; + + ret = snprintf( p, n, "%scert. version : %d\n", + prefix, crt->version ); + SAFE_SNPRINTF(); + ret = snprintf( p, n, "%sserial number : ", + prefix ); + SAFE_SNPRINTF(); + + ret = x509_serial_gets( p, n, &crt->serial ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissuer name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509_dn_gets( p, n, &crt->issuer ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssubject name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509_dn_gets( p, n, &crt->subject ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissued on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_from.year, crt->valid_from.mon, + crt->valid_from.day, crt->valid_from.hour, + crt->valid_from.min, crt->valid_from.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sexpires on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_to.year, crt->valid_to.mon, + crt->valid_to.day, crt->valid_to.hour, + crt->valid_to.min, crt->valid_to.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssigned using : ", prefix ); + SAFE_SNPRINTF(); + + ret = x509_sig_alg_gets( p, n, &crt->sig_oid1, crt->sig_pk, + crt->sig_md, crt->sig_opts ); + SAFE_SNPRINTF(); + + /* Key size */ + if( ( ret = x509_key_size_helper( key_size_str, BEFORE_COLON, + pk_get_name( &crt->pk ) ) ) != 0 ) + { + return( ret ); + } + + ret = snprintf( p, n, "\n%s%-" BC "s: %d bits", prefix, key_size_str, + (int) pk_get_size( &crt->pk ) ); + SAFE_SNPRINTF(); + + /* + * Optional extensions + */ + + if( crt->ext_types & EXT_BASIC_CONSTRAINTS ) + { + ret = snprintf( p, n, "\n%sbasic constraints : CA=%s", prefix, + crt->ca_istrue ? "true" : "false" ); + SAFE_SNPRINTF(); + + if( crt->max_pathlen > 0 ) + { + ret = snprintf( p, n, ", max_pathlen=%d", crt->max_pathlen - 1 ); + SAFE_SNPRINTF(); + } + } + + if( crt->ext_types & EXT_SUBJECT_ALT_NAME ) + { + ret = snprintf( p, n, "\n%ssubject alt name : ", prefix ); + SAFE_SNPRINTF(); + + if( ( ret = x509_info_subject_alt_name( &p, &n, + &crt->subject_alt_names ) ) != 0 ) + return( ret ); + } + + if( crt->ext_types & EXT_NS_CERT_TYPE ) + { + ret = snprintf( p, n, "\n%scert. type : ", prefix ); + SAFE_SNPRINTF(); + + if( ( ret = x509_info_cert_type( &p, &n, crt->ns_cert_type ) ) != 0 ) + return( ret ); + } + + if( crt->ext_types & EXT_KEY_USAGE ) + { + ret = snprintf( p, n, "\n%skey usage : ", prefix ); + SAFE_SNPRINTF(); + + if( ( ret = x509_info_key_usage( &p, &n, crt->key_usage ) ) != 0 ) + return( ret ); + } + + if( crt->ext_types & EXT_EXTENDED_KEY_USAGE ) + { + ret = snprintf( p, n, "\n%sext key usage : ", prefix ); + SAFE_SNPRINTF(); + + if( ( ret = x509_info_ext_key_usage( &p, &n, + &crt->ext_key_usage ) ) != 0 ) + return( ret ); + } + + ret = snprintf( p, n, "\n" ); + SAFE_SNPRINTF(); + + return( (int) ( size - n ) ); +} + +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) +int x509_crt_check_key_usage( const x509_crt *crt, int usage ) +{ + if( ( crt->ext_types & EXT_KEY_USAGE ) != 0 && + ( crt->key_usage & usage ) != usage ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + return( 0 ); +} +#endif + +#if defined(POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE) +int x509_crt_check_extended_key_usage( const x509_crt *crt, + const char *usage_oid, + size_t usage_len ) +{ + const x509_sequence *cur; + + /* Extension is not mandatory, absent means no restriction */ + if( ( crt->ext_types & EXT_EXTENDED_KEY_USAGE ) == 0 ) + return( 0 ); + + /* + * Look for the requested usage (or wildcard ANY) in our list + */ + for( cur = &crt->ext_key_usage; cur != NULL; cur = cur->next ) + { + const x509_buf *cur_oid = &cur->buf; + + if( cur_oid->len == usage_len && + memcmp( cur_oid->p, usage_oid, usage_len ) == 0 ) + { + return( 0 ); + } + + if( OID_CMP( OID_ANY_EXTENDED_KEY_USAGE, cur_oid ) ) + return( 0 ); + } + + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); +} +#endif /* POLARSSL_X509_CHECK_EXTENDED_KEY_USAGE */ + +#if defined(POLARSSL_X509_CRL_PARSE_C) +/* + * Return 1 if the certificate is revoked, or 0 otherwise. + */ +int x509_crt_revoked( const x509_crt *crt, const x509_crl *crl ) +{ + const x509_crl_entry *cur = &crl->entry; + + while( cur != NULL && cur->serial.len != 0 ) + { + if( crt->serial.len == cur->serial.len && + memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 ) + { + if( x509_time_expired( &cur->revocation_date ) ) + return( 1 ); + } + + cur = cur->next; + } + + return( 0 ); +} + +/* + * Check that the given certificate is valid according to the CRL. + */ +static int x509_crt_verifycrl( x509_crt *crt, x509_crt *ca, + x509_crl *crl_list) +{ + int flags = 0; + unsigned char hash[POLARSSL_MD_MAX_SIZE]; + const md_info_t *md_info; + + if( ca == NULL ) + return( flags ); + + /* + * TODO: What happens if no CRL is present? + * Suggestion: Revocation state should be unknown if no CRL is present. + * For backwards compatibility this is not yet implemented. + */ + + while( crl_list != NULL ) + { + if( crl_list->version == 0 || + crl_list->issuer_raw.len != ca->subject_raw.len || + memcmp( crl_list->issuer_raw.p, ca->subject_raw.p, + crl_list->issuer_raw.len ) != 0 ) + { + crl_list = crl_list->next; + continue; + } + + /* + * Check if the CA is configured to sign CRLs + */ +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) + if( x509_crt_check_key_usage( ca, KU_CRL_SIGN ) != 0 ) + { + flags |= BADCRL_NOT_TRUSTED; + break; + } +#endif + + /* + * Check if CRL is correctly signed by the trusted CA + */ + md_info = md_info_from_type( crl_list->sig_md ); + if( md_info == NULL ) + { + /* + * Cannot check 'unknown' hash + */ + flags |= BADCRL_NOT_TRUSTED; + break; + } + + md( md_info, crl_list->tbs.p, crl_list->tbs.len, hash ); + + if( pk_verify_ext( crl_list->sig_pk, crl_list->sig_opts, &ca->pk, + crl_list->sig_md, hash, md_info->size, + crl_list->sig.p, crl_list->sig.len ) != 0 ) + { + flags |= BADCRL_NOT_TRUSTED; + break; + } + + /* + * Check for validity of CRL (Do not drop out) + */ + if( x509_time_expired( &crl_list->next_update ) ) + flags |= BADCRL_EXPIRED; + + if( x509_time_future( &crl_list->this_update ) ) + flags |= BADCRL_FUTURE; + + /* + * Check if certificate is revoked + */ + if( x509_crt_revoked( crt, crl_list ) ) + { + flags |= BADCERT_REVOKED; + break; + } + + crl_list = crl_list->next; + } + return( flags ); +} +#endif /* POLARSSL_X509_CRL_PARSE_C */ + +// Equal == 0, inequal == 1 +static int x509_name_cmp( const void *s1, const void *s2, size_t len ) +{ + size_t i; + unsigned char diff; + const unsigned char *n1 = s1, *n2 = s2; + + for( i = 0; i < len; i++ ) + { + diff = n1[i] ^ n2[i]; + + if( diff == 0 ) + continue; + + if( diff == 32 && + ( ( n1[i] >= 'a' && n1[i] <= 'z' ) || + ( n1[i] >= 'A' && n1[i] <= 'Z' ) ) ) + { + continue; + } + + return( 1 ); + } + + return( 0 ); +} + +static int x509_wildcard_verify( const char *cn, x509_buf *name ) +{ + size_t i; + size_t cn_idx = 0, cn_len = strlen( cn ); + + if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' ) + return( 0 ); + + for( i = 0; i < cn_len; ++i ) + { + if( cn[i] == '.' ) + { + cn_idx = i; + break; + } + } + + if( cn_idx == 0 ) + return( 0 ); + + if( cn_len - cn_idx == name->len - 1 && + x509_name_cmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 ) + { + return( 1 ); + } + + return( 0 ); +} + +/* + * Check if 'parent' is a suitable parent (signing CA) for 'child'. + * Return 0 if yes, -1 if not. + * + * top means parent is a locally-trusted certificate + * bottom means child is the end entity cert + */ +static int x509_crt_check_parent( const x509_crt *child, + const x509_crt *parent, + int top, int bottom ) +{ + int need_ca_bit; + + /* Parent must be the issuer */ + if( child->issuer_raw.len != parent->subject_raw.len || + memcmp( child->issuer_raw.p, parent->subject_raw.p, + child->issuer_raw.len ) != 0 ) + { + return( -1 ); + } + + /* Parent must have the basicConstraints CA bit set as a general rule */ + need_ca_bit = 1; + + /* Exception: v1/v2 certificates that are locally trusted. */ + if( top && parent->version < 3 ) + need_ca_bit = 0; + + /* Exception: self-signed end-entity certs that are locally trusted. */ + if( top && bottom && + child->raw.len == parent->raw.len && + memcmp( child->raw.p, parent->raw.p, child->raw.len ) == 0 ) + { + need_ca_bit = 0; + } + + if( need_ca_bit && ! parent->ca_istrue ) + return( -1 ); + +#if defined(POLARSSL_X509_CHECK_KEY_USAGE) + if( need_ca_bit && + x509_crt_check_key_usage( parent, KU_KEY_CERT_SIGN ) != 0 ) + { + return( -1 ); + } +#endif + + return( 0 ); +} + +static int x509_crt_verify_top( + x509_crt *child, x509_crt *trust_ca, + x509_crl *ca_crl, int path_cnt, int *flags, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ) +{ + int ret; + int ca_flags = 0, check_path_cnt = path_cnt + 1; + unsigned char hash[POLARSSL_MD_MAX_SIZE]; + const md_info_t *md_info; + + if( x509_time_expired( &child->valid_to ) ) + *flags |= BADCERT_EXPIRED; + + if( x509_time_future( &child->valid_from ) ) + *flags |= BADCERT_FUTURE; + + /* + * Child is the top of the chain. Check against the trust_ca list. + */ + *flags |= BADCERT_NOT_TRUSTED; + + md_info = md_info_from_type( child->sig_md ); + if( md_info == NULL ) + { + /* + * Cannot check 'unknown', no need to try any CA + */ + trust_ca = NULL; + } + else + md( md_info, child->tbs.p, child->tbs.len, hash ); + + for( /* trust_ca */ ; trust_ca != NULL; trust_ca = trust_ca->next ) + { + if( x509_crt_check_parent( child, trust_ca, 1, path_cnt == 0 ) != 0 ) + continue; + + /* + * Reduce path_len to check against if top of the chain is + * the same as the trusted CA + */ + if( child->subject_raw.len == trust_ca->subject_raw.len && + memcmp( child->subject_raw.p, trust_ca->subject_raw.p, + child->issuer_raw.len ) == 0 ) + { + check_path_cnt--; + } + + if( trust_ca->max_pathlen > 0 && + trust_ca->max_pathlen < check_path_cnt ) + { + continue; + } + + if( pk_verify_ext( child->sig_pk, child->sig_opts, &trust_ca->pk, + child->sig_md, hash, md_info->size, + child->sig.p, child->sig.len ) != 0 ) + { + continue; + } + + /* + * Top of chain is signed by a trusted CA + */ + *flags &= ~BADCERT_NOT_TRUSTED; + break; + } + + /* + * If top of chain is not the same as the trusted CA send a verify request + * to the callback for any issues with validity and CRL presence for the + * trusted CA certificate. + */ + if( trust_ca != NULL && + ( child->subject_raw.len != trust_ca->subject_raw.len || + memcmp( child->subject_raw.p, trust_ca->subject_raw.p, + child->issuer_raw.len ) != 0 ) ) + { +#if defined(POLARSSL_X509_CRL_PARSE_C) + /* Check trusted CA's CRL for the chain's top crt */ + *flags |= x509_crt_verifycrl( child, trust_ca, ca_crl ); +#else + ((void) ca_crl); +#endif + + if( x509_time_expired( &trust_ca->valid_to ) ) + ca_flags |= BADCERT_EXPIRED; + + if( x509_time_future( &trust_ca->valid_from ) ) + ca_flags |= BADCERT_FUTURE; + + if( NULL != f_vrfy ) + { + if( ( ret = f_vrfy( p_vrfy, trust_ca, path_cnt + 1, + &ca_flags ) ) != 0 ) + { + return( ret ); + } + } + } + + /* Call callback on top cert */ + if( NULL != f_vrfy ) + { + if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 ) + return( ret ); + } + + *flags |= ca_flags; + + return( 0 ); +} + +static int x509_crt_verify_child( + x509_crt *child, x509_crt *parent, x509_crt *trust_ca, + x509_crl *ca_crl, int path_cnt, int *flags, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ) +{ + int ret; + int parent_flags = 0; + unsigned char hash[POLARSSL_MD_MAX_SIZE]; + x509_crt *grandparent; + const md_info_t *md_info; + + if( x509_time_expired( &child->valid_to ) ) + *flags |= BADCERT_EXPIRED; + + if( x509_time_future( &child->valid_from ) ) + *flags |= BADCERT_FUTURE; + + md_info = md_info_from_type( child->sig_md ); + if( md_info == NULL ) + { + /* + * Cannot check 'unknown' hash + */ + *flags |= BADCERT_NOT_TRUSTED; + } + else + { + md( md_info, child->tbs.p, child->tbs.len, hash ); + + if( pk_verify_ext( child->sig_pk, child->sig_opts, &parent->pk, + child->sig_md, hash, md_info->size, + child->sig.p, child->sig.len ) != 0 ) + { + *flags |= BADCERT_NOT_TRUSTED; + } + } + +#if defined(POLARSSL_X509_CRL_PARSE_C) + /* Check trusted CA's CRL for the given crt */ + *flags |= x509_crt_verifycrl(child, parent, ca_crl); +#endif + + /* Look for a grandparent upwards the chain */ + for( grandparent = parent->next; + grandparent != NULL; + grandparent = grandparent->next ) + { + if( x509_crt_check_parent( parent, grandparent, + 0, path_cnt == 0 ) == 0 ) + break; + } + + /* Is our parent part of the chain or at the top? */ + if( grandparent != NULL ) + { + ret = x509_crt_verify_child( parent, grandparent, trust_ca, ca_crl, + path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); + if( ret != 0 ) + return( ret ); + } + else + { + ret = x509_crt_verify_top( parent, trust_ca, ca_crl, + path_cnt + 1, &parent_flags, f_vrfy, p_vrfy ); + if( ret != 0 ) + return( ret ); + } + + /* child is verified to be a child of the parent, call verify callback */ + if( NULL != f_vrfy ) + if( ( ret = f_vrfy( p_vrfy, child, path_cnt, flags ) ) != 0 ) + return( ret ); + + *flags |= parent_flags; + + return( 0 ); +} + +/* + * Verify the certificate validity + */ +int x509_crt_verify( x509_crt *crt, + x509_crt *trust_ca, + x509_crl *ca_crl, + const char *cn, int *flags, + int (*f_vrfy)(void *, x509_crt *, int, int *), + void *p_vrfy ) +{ + size_t cn_len; + int ret; + int pathlen = 0; + x509_crt *parent; + x509_name *name; + x509_sequence *cur = NULL; + + *flags = 0; + + if( cn != NULL ) + { + name = &crt->subject; + cn_len = strlen( cn ); + + if( crt->ext_types & EXT_SUBJECT_ALT_NAME ) + { + cur = &crt->subject_alt_names; + + while( cur != NULL ) + { + if( cur->buf.len == cn_len && + x509_name_cmp( cn, cur->buf.p, cn_len ) == 0 ) + break; + + if( cur->buf.len > 2 && + memcmp( cur->buf.p, "*.", 2 ) == 0 && + x509_wildcard_verify( cn, &cur->buf ) ) + break; + + cur = cur->next; + } + + if( cur == NULL ) + *flags |= BADCERT_CN_MISMATCH; + } + else + { + while( name != NULL ) + { + if( OID_CMP( OID_AT_CN, &name->oid ) ) + { + if( name->val.len == cn_len && + x509_name_cmp( name->val.p, cn, cn_len ) == 0 ) + break; + + if( name->val.len > 2 && + memcmp( name->val.p, "*.", 2 ) == 0 && + x509_wildcard_verify( cn, &name->val ) ) + break; + } + + name = name->next; + } + + if( name == NULL ) + *flags |= BADCERT_CN_MISMATCH; + } + } + + /* Look for a parent upwards the chain */ + for( parent = crt->next; parent != NULL; parent = parent->next ) + { + if( x509_crt_check_parent( crt, parent, 0, pathlen == 0 ) == 0 ) + break; + } + + /* Are we part of the chain or at the top? */ + if( parent != NULL ) + { + ret = x509_crt_verify_child( crt, parent, trust_ca, ca_crl, + pathlen, flags, f_vrfy, p_vrfy ); + if( ret != 0 ) + return( ret ); + } + else + { + ret = x509_crt_verify_top( crt, trust_ca, ca_crl, + pathlen, flags, f_vrfy, p_vrfy ); + if( ret != 0 ) + return( ret ); + } + + if( *flags != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + + return( 0 ); +} + +/* + * Initialize a certificate chain + */ +void x509_crt_init( x509_crt *crt ) +{ + memset( crt, 0, sizeof(x509_crt) ); +} + +/* + * Unallocate all certificate data + */ +void x509_crt_free( x509_crt *crt ) +{ + x509_crt *cert_cur = crt; + x509_crt *cert_prv; + x509_name *name_cur; + x509_name *name_prv; + x509_sequence *seq_cur; + x509_sequence *seq_prv; + + if( crt == NULL ) + return; + + do + { + pk_free( &cert_cur->pk ); + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + polarssl_free( cert_cur->sig_opts ); +#endif + + name_cur = cert_cur->issuer.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + polarssl_zeroize( name_prv, sizeof( x509_name ) ); + polarssl_free( name_prv ); + } + + name_cur = cert_cur->subject.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + polarssl_zeroize( name_prv, sizeof( x509_name ) ); + polarssl_free( name_prv ); + } + + seq_cur = cert_cur->ext_key_usage.next; + while( seq_cur != NULL ) + { + seq_prv = seq_cur; + seq_cur = seq_cur->next; + polarssl_zeroize( seq_prv, sizeof( x509_sequence ) ); + polarssl_free( seq_prv ); + } + + seq_cur = cert_cur->subject_alt_names.next; + while( seq_cur != NULL ) + { + seq_prv = seq_cur; + seq_cur = seq_cur->next; + polarssl_zeroize( seq_prv, sizeof( x509_sequence ) ); + polarssl_free( seq_prv ); + } + + if( cert_cur->raw.p != NULL ) + { + polarssl_zeroize( cert_cur->raw.p, cert_cur->raw.len ); + polarssl_free( cert_cur->raw.p ); + } + + cert_cur = cert_cur->next; + } + while( cert_cur != NULL ); + + cert_cur = crt; + do + { + cert_prv = cert_cur; + cert_cur = cert_cur->next; + + polarssl_zeroize( cert_prv, sizeof( x509_crt ) ); + if( cert_prv != crt ) + polarssl_free( cert_prv ); + } + while( cert_cur != NULL ); +} + +#endif /* POLARSSL_X509_CRT_PARSE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509_csr.c b/component/common/network/ssl/polarssl-1.3.8/library/x509_csr.c new file mode 100644 index 0000000..0b4f771 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509_csr.c @@ -0,0 +1,465 @@ +/* + * X.509 Certificate Signing Request (CSR) parsing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CSR_PARSE_C) + +#include "polarssl/x509_csr.h" +#include "polarssl/oid.h" +#if defined(POLARSSL_PEM_PARSE_C) +#include "polarssl/pem.h" +#endif + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_malloc malloc +#define polarssl_free free +#endif + +#include +#include + +#if defined(POLARSSL_FS_IO) || defined(EFIX64) || defined(EFI32) +#include +#endif + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * Version ::= INTEGER { v1(0) } + */ +static int x509_csr_get_version( unsigned char **p, + const unsigned char *end, + int *ver ) +{ + int ret; + + if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + *ver = 0; + return( 0 ); + } + + return( POLARSSL_ERR_X509_INVALID_VERSION + ret ); + } + + return( 0 ); +} + +/* + * Parse a CSR in DER format + */ +int x509_csr_parse_der( x509_csr *csr, + const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + x509_buf sig_params; + + memset( &sig_params, 0, sizeof( x509_buf ) ); + + /* + * Check for valid input + */ + if( csr == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + x509_csr_init( csr ); + + /* + * first copy the raw DER data + */ + p = (unsigned char *) polarssl_malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + csr->raw.p = p; + csr->raw.len = len; + end = p + len; + + /* + * CertificationRequest ::= SEQUENCE { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT ); + } + + if( len != (size_t) ( end - p ) ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + /* + * CertificationRequestInfo ::= SEQUENCE { + */ + csr->cri.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + end = p + len; + csr->cri.len = end - csr->cri.p; + + /* + * Version ::= INTEGER { v1(0) } + */ + if( ( ret = x509_csr_get_version( &p, end, &csr->version ) ) != 0 ) + { + x509_csr_free( csr ); + return( ret ); + } + + csr->version++; + + if( csr->version != 1 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_UNKNOWN_VERSION ); + } + + /* + * subject Name + */ + csr->subject_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &csr->subject ) ) != 0 ) + { + x509_csr_free( csr ); + return( ret ); + } + + csr->subject_raw.len = p - csr->subject_raw.p; + + /* + * subjectPKInfo SubjectPublicKeyInfo + */ + if( ( ret = pk_parse_subpubkey( &p, end, &csr->pk ) ) != 0 ) + { + x509_csr_free( csr ); + return( ret ); + } + + /* + * attributes [0] Attributes + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC ) ) != 0 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + ret ); + } + // TODO Parse Attributes / extension requests + + p += len; + + end = csr->raw.p + csr->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + */ + if( ( ret = x509_get_alg( &p, end, &csr->sig_oid, &sig_params ) ) != 0 ) + { + x509_csr_free( csr ); + return( ret ); + } + + if( ( ret = x509_get_sig_alg( &csr->sig_oid, &sig_params, + &csr->sig_md, &csr->sig_pk, + &csr->sig_opts ) ) != 0 ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_UNKNOWN_SIG_ALG ); + } + + if( ( ret = x509_get_sig( &p, end, &csr->sig ) ) != 0 ) + { + x509_csr_free( csr ); + return( ret ); + } + + if( p != end ) + { + x509_csr_free( csr ); + return( POLARSSL_ERR_X509_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + return( 0 ); +} + +/* + * Parse a CSR, allowing for PEM or raw DER encoding + */ +int x509_csr_parse( x509_csr *csr, const unsigned char *buf, size_t buflen ) +{ + int ret; +#if defined(POLARSSL_PEM_PARSE_C) + size_t use_len; + pem_context pem; +#endif + + /* + * Check for valid input + */ + if( csr == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + +#if defined(POLARSSL_PEM_PARSE_C) + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN CERTIFICATE REQUEST-----", + "-----END CERTIFICATE REQUEST-----", + buf, NULL, 0, &use_len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded, parse the result + */ + if( ( ret = x509_csr_parse_der( csr, pem.buf, pem.buflen ) ) != 0 ) + return( ret ); + + pem_free( &pem ); + return( 0 ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_FOOTER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + else +#endif /* POLARSSL_PEM_PARSE_C */ + return( x509_csr_parse_der( csr, buf, buflen ) ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load a CSR into the structure + */ +int x509_csr_parse_file( x509_csr *csr, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if( ( ret = x509_load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509_csr_parse( csr, buf, n ); + + polarssl_zeroize( buf, n + 1 ); + polarssl_free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +#if defined(_MSC_VER) && !defined snprintf && !defined(EFIX64) && \ + !defined(EFI32) +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +static int compat_snprintf( char *str, size_t size, const char *format, ... ) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if( res < 0 ) + return( (int) size + 20 ); + + return( res ); +} + +#define snprintf compat_snprintf +#endif /* _MSC_VER && !snprintf && !EFIX64 && !EFI32 */ + +#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( -1 ); \ + \ + if( (unsigned int) ret > n ) { \ + p[n - 1] = '\0'; \ + return( POLARSSL_ERR_DEBUG_BUF_TOO_SMALL ); \ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +#define BEFORE_COLON 14 +#define BC "14" +/* + * Return an informational string about the CSR. + */ +int x509_csr_info( char *buf, size_t size, const char *prefix, + const x509_csr *csr ) +{ + int ret; + size_t n; + char *p; + char key_size_str[BEFORE_COLON]; + + p = buf; + n = size; + + ret = snprintf( p, n, "%sCSR version : %d", + prefix, csr->version ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssubject name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509_dn_gets( p, n, &csr->subject ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssigned using : ", prefix ); + SAFE_SNPRINTF(); + + ret = x509_sig_alg_gets( p, n, &csr->sig_oid, csr->sig_pk, csr->sig_md, + csr->sig_opts ); + SAFE_SNPRINTF(); + + if( ( ret = x509_key_size_helper( key_size_str, BEFORE_COLON, + pk_get_name( &csr->pk ) ) ) != 0 ) + { + return( ret ); + } + + ret = snprintf( p, n, "\n%s%-" BC "s: %d bits\n", prefix, key_size_str, + (int) pk_get_size( &csr->pk ) ); + SAFE_SNPRINTF(); + + return( (int) ( size - n ) ); +} + +/* + * Initialize a CSR + */ +void x509_csr_init( x509_csr *csr ) +{ + memset( csr, 0, sizeof(x509_csr) ); +} + +/* + * Unallocate all CSR data + */ +void x509_csr_free( x509_csr *csr ) +{ + x509_name *name_cur; + x509_name *name_prv; + + if( csr == NULL ) + return; + + pk_free( &csr->pk ); + +#if defined(POLARSSL_X509_RSASSA_PSS_SUPPORT) + polarssl_free( csr->sig_opts ); +#endif + + name_cur = csr->subject.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + polarssl_zeroize( name_prv, sizeof( x509_name ) ); + polarssl_free( name_prv ); + } + + if( csr->raw.p != NULL ) + { + polarssl_zeroize( csr->raw.p, csr->raw.len ); + polarssl_free( csr->raw.p ); + } + + polarssl_zeroize( csr, sizeof( x509_csr ) ); +} + +#endif /* POLARSSL_X509_CSR_PARSE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509write_crt.c b/component/common/network/ssl/polarssl-1.3.8/library/x509write_crt.c new file mode 100644 index 0000000..e298c24 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509write_crt.c @@ -0,0 +1,452 @@ +/* + * X.509 certificate writing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * References: + * - certificates: RFC 5280, updated by RFC 6818 + * - CSRs: PKCS#10 v1.7 aka RFC 2986 + * - attributes: PKCS#9 v2.0 aka RFC 2985 + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CRT_WRITE_C) + +#include "polarssl/x509_crt.h" +#include "polarssl/oid.h" +#include "polarssl/asn1write.h" +#include "polarssl/sha1.h" + +#if defined(POLARSSL_PEM_WRITE_C) +#include "polarssl/pem.h" +#endif /* POLARSSL_PEM_WRITE_C */ + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +void x509write_crt_init( x509write_cert *ctx ) +{ + memset( ctx, 0, sizeof(x509write_cert) ); + + mpi_init( &ctx->serial ); + ctx->version = X509_CRT_VERSION_3; +} + +void x509write_crt_free( x509write_cert *ctx ) +{ + mpi_free( &ctx->serial ); + + asn1_free_named_data_list( &ctx->subject ); + asn1_free_named_data_list( &ctx->issuer ); + asn1_free_named_data_list( &ctx->extensions ); + + polarssl_zeroize( ctx, sizeof(x509write_cert) ); +} + +void x509write_crt_set_version( x509write_cert *ctx, int version ) +{ + ctx->version = version; +} + +void x509write_crt_set_md_alg( x509write_cert *ctx, md_type_t md_alg ) +{ + ctx->md_alg = md_alg; +} + +void x509write_crt_set_subject_key( x509write_cert *ctx, pk_context *key ) +{ + ctx->subject_key = key; +} + +void x509write_crt_set_issuer_key( x509write_cert *ctx, pk_context *key ) +{ + ctx->issuer_key = key; +} + +int x509write_crt_set_subject_name( x509write_cert *ctx, + const char *subject_name ) +{ + return x509_string_to_names( &ctx->subject, subject_name ); +} + +int x509write_crt_set_issuer_name( x509write_cert *ctx, + const char *issuer_name ) +{ + return x509_string_to_names( &ctx->issuer, issuer_name ); +} + +int x509write_crt_set_serial( x509write_cert *ctx, const mpi *serial ) +{ + int ret; + + if( ( ret = mpi_copy( &ctx->serial, serial ) ) != 0 ) + return( ret ); + + return( 0 ); +} + +int x509write_crt_set_validity( x509write_cert *ctx, const char *not_before, + const char *not_after ) +{ + if( strlen( not_before ) != X509_RFC5280_UTC_TIME_LEN - 1 || + strlen( not_after ) != X509_RFC5280_UTC_TIME_LEN - 1 ) + { + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + } + strncpy( ctx->not_before, not_before, X509_RFC5280_UTC_TIME_LEN ); + strncpy( ctx->not_after , not_after , X509_RFC5280_UTC_TIME_LEN ); + ctx->not_before[X509_RFC5280_UTC_TIME_LEN - 1] = 'Z'; + ctx->not_after[X509_RFC5280_UTC_TIME_LEN - 1] = 'Z'; + + return( 0 ); +} + +int x509write_crt_set_extension( x509write_cert *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len ) +{ + return x509_set_extension( &ctx->extensions, oid, oid_len, + critical, val, val_len ); +} + +int x509write_crt_set_basic_constraints( x509write_cert *ctx, + int is_ca, int max_pathlen ) +{ + int ret; + unsigned char buf[9]; + unsigned char *c = buf + sizeof(buf); + size_t len = 0; + + memset( buf, 0, sizeof(buf) ); + + if( is_ca && max_pathlen > 127 ) + return( POLARSSL_ERR_X509_BAD_INPUT_DATA ); + + if( is_ca ) + { + if( max_pathlen >= 0 ) + { + ASN1_CHK_ADD( len, asn1_write_int( &c, buf, max_pathlen ) ); + } + ASN1_CHK_ADD( len, asn1_write_bool( &c, buf, 1 ) ); + } + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return x509write_crt_set_extension( ctx, OID_BASIC_CONSTRAINTS, + OID_SIZE( OID_BASIC_CONSTRAINTS ), + 0, buf + sizeof(buf) - len, len ); +} + +#if defined(POLARSSL_SHA1_C) +int x509write_crt_set_subject_key_identifier( x509write_cert *ctx ) +{ + int ret; + unsigned char buf[POLARSSL_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */ + unsigned char *c = buf + sizeof(buf); + size_t len = 0; + + memset( buf, 0, sizeof(buf) ); + ASN1_CHK_ADD( len, pk_write_pubkey( &c, buf, ctx->subject_key ) ); + + sha1( buf + sizeof(buf) - len, len, buf + sizeof(buf) - 20 ); + c = buf + sizeof(buf) - 20; + len = 20; + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_OCTET_STRING ) ); + + return x509write_crt_set_extension( ctx, OID_SUBJECT_KEY_IDENTIFIER, + OID_SIZE( OID_SUBJECT_KEY_IDENTIFIER ), + 0, buf + sizeof(buf) - len, len ); +} + +int x509write_crt_set_authority_key_identifier( x509write_cert *ctx ) +{ + int ret; + unsigned char buf[POLARSSL_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */ + unsigned char *c = buf + sizeof(buf); + size_t len = 0; + + memset( buf, 0, sizeof(buf) ); + ASN1_CHK_ADD( len, pk_write_pubkey( &c, buf, ctx->issuer_key ) ); + + sha1( buf + sizeof(buf) - len, len, buf + sizeof(buf) - 20 ); + c = buf + sizeof(buf) - 20; + len = 20; + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONTEXT_SPECIFIC | 0 ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return x509write_crt_set_extension( ctx, OID_AUTHORITY_KEY_IDENTIFIER, + OID_SIZE( OID_AUTHORITY_KEY_IDENTIFIER ), + 0, buf + sizeof(buf) - len, len ); +} +#endif /* POLARSSL_SHA1_C */ + +int x509write_crt_set_key_usage( x509write_cert *ctx, unsigned char key_usage ) +{ + unsigned char buf[4]; + unsigned char *c; + int ret; + + c = buf + 4; + + if( ( ret = asn1_write_bitstring( &c, buf, &key_usage, 7 ) ) != 4 ) + return( ret ); + + ret = x509write_crt_set_extension( ctx, OID_KEY_USAGE, + OID_SIZE( OID_KEY_USAGE ), + 1, buf, 4 ); + if( ret != 0 ) + return( ret ); + + return( 0 ); +} + +int x509write_crt_set_ns_cert_type( x509write_cert *ctx, + unsigned char ns_cert_type ) +{ + unsigned char buf[4]; + unsigned char *c; + int ret; + + c = buf + 4; + + if( ( ret = asn1_write_bitstring( &c, buf, &ns_cert_type, 8 ) ) != 4 ) + return( ret ); + + ret = x509write_crt_set_extension( ctx, OID_NS_CERT_TYPE, + OID_SIZE( OID_NS_CERT_TYPE ), + 0, buf, 4 ); + if( ret != 0 ) + return( ret ); + + return( 0 ); +} + +static int x509_write_time( unsigned char **p, unsigned char *start, + const char *time, size_t size ) +{ + int ret; + size_t len = 0; + + /* + * write ASN1_UTC_TIME if year < 2050 (2 bytes shorter) + */ + if( time[0] == '2' && time[1] == '0' && time [2] < '5' ) + { + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, + (const unsigned char *) time + 2, + size - 2 ) ); + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_UTC_TIME ) ); + } + else + { + ASN1_CHK_ADD( len, asn1_write_raw_buffer( p, start, + (const unsigned char *) time, + size ) ); + ASN1_CHK_ADD( len, asn1_write_len( p, start, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( p, start, ASN1_GENERALIZED_TIME ) ); + } + + return( (int) len ); +} + +int x509write_crt_der( x509write_cert *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + const char *sig_oid; + size_t sig_oid_len = 0; + unsigned char *c, *c2; + unsigned char hash[64]; + unsigned char sig[POLARSSL_MPI_MAX_SIZE]; + unsigned char tmp_buf[2048]; + size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len; + size_t len = 0; + pk_type_t pk_alg; + + /* + * Prepare data to be signed in tmp_buf + */ + c = tmp_buf + sizeof( tmp_buf ); + + /* Signature algorithm needed in TBS, and later for actual signature */ + pk_alg = pk_get_type( ctx->issuer_key ); + if( pk_alg == POLARSSL_PK_ECKEY ) + pk_alg = POLARSSL_PK_ECDSA; + + if( ( ret = oid_get_oid_by_sig_alg( pk_alg, ctx->md_alg, + &sig_oid, &sig_oid_len ) ) != 0 ) + { + return( ret ); + } + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + */ + ASN1_CHK_ADD( len, x509_write_extensions( &c, tmp_buf, ctx->extensions ) ); + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONTEXT_SPECIFIC | + ASN1_CONSTRUCTED | 3 ) ); + + /* + * SubjectPublicKeyInfo + */ + ASN1_CHK_ADD( pub_len, pk_write_pubkey_der( ctx->subject_key, + tmp_buf, c - tmp_buf ) ); + c -= pub_len; + len += pub_len; + + /* + * Subject ::= Name + */ + ASN1_CHK_ADD( len, x509_write_names( &c, tmp_buf, ctx->subject ) ); + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + */ + sub_len = 0; + + ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_after, + X509_RFC5280_UTC_TIME_LEN ) ); + + ASN1_CHK_ADD( sub_len, x509_write_time( &c, tmp_buf, ctx->not_before, + X509_RFC5280_UTC_TIME_LEN ) ); + + len += sub_len; + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, sub_len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + /* + * Issuer ::= Name + */ + ASN1_CHK_ADD( len, x509_write_names( &c, tmp_buf, ctx->issuer ) ); + + /* + * Signature ::= AlgorithmIdentifier + */ + ASN1_CHK_ADD( len, asn1_write_algorithm_identifier( &c, tmp_buf, + sig_oid, strlen( sig_oid ), 0 ) ); + + /* + * Serial ::= INTEGER + */ + ASN1_CHK_ADD( len, asn1_write_mpi( &c, tmp_buf, &ctx->serial ) ); + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + sub_len = 0; + ASN1_CHK_ADD( sub_len, asn1_write_int( &c, tmp_buf, ctx->version ) ); + len += sub_len; + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, sub_len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONTEXT_SPECIFIC | + ASN1_CONSTRUCTED | 0 ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + /* + * Make signature + */ + md( md_info_from_type( ctx->md_alg ), c, len, hash ); + + if( ( ret = pk_sign( ctx->issuer_key, ctx->md_alg, hash, 0, sig, &sig_len, + f_rng, p_rng ) ) != 0 ) + { + return( ret ); + } + + /* + * Write data to output buffer + */ + c2 = buf + size; + ASN1_CHK_ADD( sig_and_oid_len, x509_write_sig( &c2, buf, + sig_oid, sig_oid_len, sig, sig_len ) ); + + c2 -= len; + memcpy( c2, c, len ); + + len += sig_and_oid_len; + ASN1_CHK_ADD( len, asn1_write_len( &c2, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c2, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" +#define PEM_END_CRT "-----END CERTIFICATE-----\n" + +#if defined(POLARSSL_PEM_WRITE_C) +int x509write_crt_pem( x509write_cert *crt, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + unsigned char output_buf[4096]; + size_t olen = 0; + + if( ( ret = x509write_crt_der( crt, output_buf, sizeof(output_buf), + f_rng, p_rng ) ) < 0 ) + { + return( ret ); + } + + if( ( ret = pem_write_buffer( PEM_BEGIN_CRT, PEM_END_CRT, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_PEM_WRITE_C */ + +#endif /* POLARSSL_X509_CRT_WRITE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/x509write_csr.c b/component/common/network/ssl/polarssl-1.3.8/library/x509write_csr.c new file mode 100644 index 0000000..53ae9c6 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/x509write_csr.c @@ -0,0 +1,260 @@ +/* + * X.509 Certificate Signing Request writing + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * References: + * - CSRs: PKCS#10 v1.7 aka RFC 2986 + * - attributes: PKCS#9 v2.0 aka RFC 2985 + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_X509_CSR_WRITE_C) + +#include "polarssl/x509_csr.h" +#include "polarssl/oid.h" +#include "polarssl/asn1write.h" + +#if defined(POLARSSL_PEM_WRITE_C) +#include "polarssl/pem.h" +#endif + +#include +#include + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +void x509write_csr_init( x509write_csr *ctx ) +{ + memset( ctx, 0, sizeof(x509write_csr) ); +} + +void x509write_csr_free( x509write_csr *ctx ) +{ + asn1_free_named_data_list( &ctx->subject ); + asn1_free_named_data_list( &ctx->extensions ); + + polarssl_zeroize( ctx, sizeof(x509write_csr) ); +} + +void x509write_csr_set_md_alg( x509write_csr *ctx, md_type_t md_alg ) +{ + ctx->md_alg = md_alg; +} + +void x509write_csr_set_key( x509write_csr *ctx, pk_context *key ) +{ + ctx->key = key; +} + +int x509write_csr_set_subject_name( x509write_csr *ctx, + const char *subject_name ) +{ + return x509_string_to_names( &ctx->subject, subject_name ); +} + +int x509write_csr_set_extension( x509write_csr *ctx, + const char *oid, size_t oid_len, + const unsigned char *val, size_t val_len ) +{ + return x509_set_extension( &ctx->extensions, oid, oid_len, + 0, val, val_len ); +} + +int x509write_csr_set_key_usage( x509write_csr *ctx, unsigned char key_usage ) +{ + unsigned char buf[4]; + unsigned char *c; + int ret; + + c = buf + 4; + + if( ( ret = asn1_write_bitstring( &c, buf, &key_usage, 7 ) ) != 4 ) + return( ret ); + + ret = x509write_csr_set_extension( ctx, OID_KEY_USAGE, + OID_SIZE( OID_KEY_USAGE ), + buf, 4 ); + if( ret != 0 ) + return( ret ); + + return( 0 ); +} + +int x509write_csr_set_ns_cert_type( x509write_csr *ctx, + unsigned char ns_cert_type ) +{ + unsigned char buf[4]; + unsigned char *c; + int ret; + + c = buf + 4; + + if( ( ret = asn1_write_bitstring( &c, buf, &ns_cert_type, 8 ) ) != 4 ) + return( ret ); + + ret = x509write_csr_set_extension( ctx, OID_NS_CERT_TYPE, + OID_SIZE( OID_NS_CERT_TYPE ), + buf, 4 ); + if( ret != 0 ) + return( ret ); + + return( 0 ); +} + +int x509write_csr_der( x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + const char *sig_oid; + size_t sig_oid_len = 0; + unsigned char *c, *c2; + unsigned char hash[64]; + unsigned char sig[POLARSSL_MPI_MAX_SIZE]; + unsigned char tmp_buf[2048]; + size_t pub_len = 0, sig_and_oid_len = 0, sig_len; + size_t len = 0; + pk_type_t pk_alg; + + /* + * Prepare data to be signed in tmp_buf + */ + c = tmp_buf + sizeof( tmp_buf ); + + ASN1_CHK_ADD( len, x509_write_extensions( &c, tmp_buf, ctx->extensions ) ); + + if( len ) + { + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SET ) ); + + ASN1_CHK_ADD( len, asn1_write_oid( &c, tmp_buf, OID_PKCS9_CSR_EXT_REQ, + OID_SIZE( OID_PKCS9_CSR_EXT_REQ ) ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + } + + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_CONTEXT_SPECIFIC ) ); + + ASN1_CHK_ADD( pub_len, pk_write_pubkey_der( ctx->key, + tmp_buf, c - tmp_buf ) ); + c -= pub_len; + len += pub_len; + + /* + * Subject ::= Name + */ + ASN1_CHK_ADD( len, x509_write_names( &c, tmp_buf, ctx->subject ) ); + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + ASN1_CHK_ADD( len, asn1_write_int( &c, tmp_buf, 0 ) ); + + ASN1_CHK_ADD( len, asn1_write_len( &c, tmp_buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c, tmp_buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + /* + * Prepare signature + */ + md( md_info_from_type( ctx->md_alg ), c, len, hash ); + + pk_alg = pk_get_type( ctx->key ); + if( pk_alg == POLARSSL_PK_ECKEY ) + pk_alg = POLARSSL_PK_ECDSA; + + if( ( ret = pk_sign( ctx->key, ctx->md_alg, hash, 0, sig, &sig_len, + f_rng, p_rng ) ) != 0 || + ( ret = oid_get_oid_by_sig_alg( pk_alg, ctx->md_alg, + &sig_oid, &sig_oid_len ) ) != 0 ) + { + return( ret ); + } + + /* + * Write data to output buffer + */ + c2 = buf + size; + ASN1_CHK_ADD( sig_and_oid_len, x509_write_sig( &c2, buf, + sig_oid, sig_oid_len, sig, sig_len ) ); + + c2 -= len; + memcpy( c2, c, len ); + + len += sig_and_oid_len; + ASN1_CHK_ADD( len, asn1_write_len( &c2, buf, len ) ); + ASN1_CHK_ADD( len, asn1_write_tag( &c2, buf, ASN1_CONSTRUCTED | + ASN1_SEQUENCE ) ); + + return( (int) len ); +} + +#define PEM_BEGIN_CSR "-----BEGIN CERTIFICATE REQUEST-----\n" +#define PEM_END_CSR "-----END CERTIFICATE REQUEST-----\n" + +#if defined(POLARSSL_PEM_WRITE_C) +int x509write_csr_pem( x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret; + unsigned char output_buf[4096]; + size_t olen = 0; + + if( ( ret = x509write_csr_der( ctx, output_buf, sizeof(output_buf), + f_rng, p_rng ) ) < 0 ) + { + return( ret ); + } + + if( ( ret = pem_write_buffer( PEM_BEGIN_CSR, PEM_END_CSR, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen ) ) != 0 ) + { + return( ret ); + } + + return( 0 ); +} +#endif /* POLARSSL_PEM_WRITE_C */ + +#endif /* POLARSSL_X509_CSR_WRITE_C */ diff --git a/component/common/network/ssl/polarssl-1.3.8/library/xtea.c b/component/common/network/ssl/polarssl-1.3.8/library/xtea.c new file mode 100644 index 0000000..75215c5 --- /dev/null +++ b/component/common/network/ssl/polarssl-1.3.8/library/xtea.c @@ -0,0 +1,283 @@ +/* + * An 32-bit implementation of the XTEA algorithm + * + * Copyright (C) 2006-2014, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined(POLARSSL_CONFIG_FILE) +#include "polarssl/config.h" +#else +#include POLARSSL_CONFIG_FILE +#endif + +#if defined(POLARSSL_XTEA_C) + +#include "polarssl/xtea.h" + +#if defined(POLARSSL_PLATFORM_C) +#include "polarssl/platform.h" +#else +#define polarssl_printf printf +#endif + +#if !defined(POLARSSL_XTEA_ALT) + +/* Implementation that should never be optimized out by the compiler */ +static void polarssl_zeroize( void *v, size_t n ) { + volatile unsigned char *p = v; while( n-- ) *p++ = 0; +} + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ +} +#endif + +void xtea_init( xtea_context *ctx ) +{ + memset( ctx, 0, sizeof( xtea_context ) ); +} + +void xtea_free( xtea_context *ctx ) +{ + if( ctx == NULL ) + return; + + polarssl_zeroize( ctx, sizeof( xtea_context ) ); +} + +/* + * XTEA key schedule + */ +void xtea_setup( xtea_context *ctx, const unsigned char key[16] ) +{ + int i; + + memset( ctx, 0, sizeof(xtea_context) ); + + for( i = 0; i < 4; i++ ) + { + GET_UINT32_BE( ctx->k[i], key, i << 2 ); + } +} + +/* + * XTEA encrypt function + */ +int xtea_crypt_ecb( xtea_context *ctx, int mode, + const unsigned char input[8], unsigned char output[8]) +{ + uint32_t *k, v0, v1, i; + + k = ctx->k; + + GET_UINT32_BE( v0, input, 0 ); + GET_UINT32_BE( v1, input, 4 ); + + if( mode == XTEA_ENCRYPT ) + { + uint32_t sum = 0, delta = 0x9E3779B9; + + for( i = 0; i < 32; i++ ) + { + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); + sum += delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); + } + } + else /* XTEA_DECRYPT */ + { + uint32_t delta = 0x9E3779B9, sum = delta * 32; + + for( i = 0; i < 32; i++ ) + { + v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); + sum -= delta; + v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); + } + } + + PUT_UINT32_BE( v0, output, 0 ); + PUT_UINT32_BE( v1, output, 4 ); + + return( 0 ); +} + +#if defined(POLARSSL_CIPHER_MODE_CBC) +/* + * XTEA-CBC buffer encryption/decryption + */ +int xtea_crypt_cbc( xtea_context *ctx, int mode, size_t length, + unsigned char iv[8], const unsigned char *input, + unsigned char *output) +{ + int i; + unsigned char temp[8]; + + if( length % 8 ) + return( POLARSSL_ERR_XTEA_INVALID_INPUT_LENGTH ); + + if( mode == XTEA_DECRYPT ) + { + while( length > 0 ) + { + memcpy( temp, input, 8 ); + xtea_crypt_ecb( ctx, mode, input, output ); + + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( output[i] ^ iv[i] ); + + memcpy( iv, temp, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + else + { + while( length > 0 ) + { + for( i = 0; i < 8; i++ ) + output[i] = (unsigned char)( input[i] ^ iv[i] ); + + xtea_crypt_ecb( ctx, mode, output, output ); + memcpy( iv, output, 8 ); + + input += 8; + output += 8; + length -= 8; + } + } + + return( 0 ); +} +#endif /* POLARSSL_CIPHER_MODE_CBC */ +#endif /* !POLARSSL_XTEA_ALT */ + +#if defined(POLARSSL_SELF_TEST) + +#include +#include + +/* + * XTEA tests vectors (non-official) + */ + +static const unsigned char xtea_test_key[6][16] = +{ + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 } +}; + +static const unsigned char xtea_test_pt[6][8] = +{ + { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 }, + { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, + { 0x5a, 0x5b, 0x6e, 0x27, 0x89, 0x48, 0xd7, 0x7f }, + { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 }, + { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, + { 0x70, 0xe1, 0x22, 0x5d, 0x6e, 0x4e, 0x76, 0x55 } +}; + +static const unsigned char xtea_test_ct[6][8] = +{ + { 0x49, 0x7d, 0xf3, 0xd0, 0x72, 0x61, 0x2c, 0xb5 }, + { 0xe7, 0x8f, 0x2d, 0x13, 0x74, 0x43, 0x41, 0xd8 }, + { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 }, + { 0xa0, 0x39, 0x05, 0x89, 0xf8, 0xb8, 0xef, 0xa5 }, + { 0xed, 0x23, 0x37, 0x5a, 0x82, 0x1a, 0x8c, 0x2d }, + { 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41 } +}; + +/* + * Checkup routine + */ +int xtea_self_test( int verbose ) +{ + int i, ret = 0; + unsigned char buf[8]; + xtea_context ctx; + + xtea_init( &ctx ); + for( i = 0; i < 6; i++ ) + { + if( verbose != 0 ) + polarssl_printf( " XTEA test #%d: ", i + 1 ); + + memcpy( buf, xtea_test_pt[i], 8 ); + + xtea_setup( &ctx, xtea_test_key[i] ); + xtea_crypt_ecb( &ctx, XTEA_ENCRYPT, buf, buf ); + + if( memcmp( buf, xtea_test_ct[i], 8 ) != 0 ) + { + if( verbose != 0 ) + polarssl_printf( "failed\n" ); + + ret = 1; + goto exit; + } + + if( verbose != 0 ) + polarssl_printf( "passed\n" ); + } + + if( verbose != 0 ) + polarssl_printf( "\n" ); + +exit: + xtea_free( &ctx ); + + return( ret ); +} + +#endif /* POLARSSL_SELF_TEST */ + +#endif /* POLARSSL_XTEA_C */ diff --git a/component/common/test/wlan/wlan_test_inc.h b/component/common/test/wlan/wlan_test_inc.h new file mode 100644 index 0000000..de1e156 --- /dev/null +++ b/component/common/test/wlan/wlan_test_inc.h @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------// +#ifndef __MAIN_TEST_H +#define __MAIN_TEST_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported test functions ------------------------------------------------------- */ +void do_ping_test(char *ip, int size, int count, int interval); +void do_ping_call(char *ip, int loop, int count); +void do_deinit_test(int mode, int ap_started); +void interactive_question(char *question, char *choice, char *buf, int buf_size); +void start_interactive_mode(void); +void post_init(unsigned int config_start_ap); + +#ifdef __cplusplus + } +#endif + +#endif // __MAIN_TEST_H + +//----------------------------------------------------------------------------// diff --git a/component/common/utilities/cJSON.c b/component/common/utilities/cJSON.c new file mode 100644 index 0000000..1e2f8e1 --- /dev/null +++ b/component/common/utilities/cJSON.c @@ -0,0 +1,596 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +static const char *ep; + +const char *cJSON_GetErrorPtr(void) {return ep;} + +static int cJSON_strcasecmp(const char *s1,const char *s2) +{ + if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +/* Delete a cJSON structure. */ +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); + if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ + if (*num=='e' || *num=='E') /* Exponent? */ + { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ + while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ + } + + n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ + + item->valuedouble=n; + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + double d=item->valuedouble; + if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) + { + str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ + if (str) sprintf(str,"%d",item->valueint); + } + else + { + str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); + else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); + else sprintf(str,"%f",d); + } + } + return str; +} + +static unsigned parse_hex4(const char *str) +{ + unsigned h=0; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + h=h<<4;str++; + if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; + if (*str!='\"') {ep=str;return 0;} /* not a string! */ + + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': /* transcode utf16 to utf8. */ + uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + + if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + + if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ + uc2=parse_hex4(ptr+3);ptr+=6; + if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ + uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); + } + + len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + + switch (len) { + case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + + if (!str) return cJSON_strdup(""); + ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + + out=(char*)cJSON_malloc(len+3); + if (!out) return 0; + + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (token=*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +/* Invote print_string_ptr (which is useful) on an item. */ +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +/* Predeclare these prototypes. */ +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth,int fmt); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth,int fmt); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth,int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} + +/* Parse an object - create a new root, and populate. */ +cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +{ + const char *end=0; + cJSON *c=cJSON_New_Item(); + ep=0; + if (!c) return 0; /* memory fail */ + + end=parse_value(c,skip(value)); + if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} + if (return_parse_end) *return_parse_end=end; + return c; +} +/* Default options for cJSON_Parse */ +cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} + +/* Render a cJSON item/entity/structure to text. */ +char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} +char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} + +/* Parser core - when encountering text, process appropriately. */ +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; /* Fail on null. */ + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + ep=value;return 0; /* failure. */ +} + +/* Render a value to text. */ +static char *print_value(cJSON *item,int depth,int fmt) +{ + char *out=0; + if (!item) return 0; + switch ((item->type)&255) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth,fmt);break; + case cJSON_Object: out=print_object(item,depth,fmt);break; + } + return out; +} + +/* Build an array from input text. */ +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') {ep=value;return 0;} /* not an array! */ + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; /* memory fail */ + value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; /* memory fail */ + } + + if (*value==']') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an array to text */ +static char *print_array(cJSON *item,int depth,int fmt) +{ + char **entries; + char *out=0,*ptr,*ret;int len=5; + cJSON *child=item->child; + int numentries=0,i=0,fail=0; + + /* How many entries in the array? */ + while (child) numentries++,child=child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out=(char*)cJSON_malloc(3); + if (out) strcpy(out,"[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries=(char**)cJSON_malloc(numentries*sizeof(char*)); + if (!entries) return 0; + memset(entries,0,numentries*sizeof(char*)); + /* Retrieve all the results: */ + child=item->child; + while (child && !fail) + { + ret=print_value(child,depth+1,fmt); + entries[i++]=ret; + if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; + child=child->next; + } + + /* If we didn't fail, try to malloc the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + /* If that fails, we fail. */ + if (!out) fail=1; + + /* Handle failure. */ + if (fail) + { + for (i=0;itype=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; /* empty array. */ + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') {ep=value;return 0;} /* fail! */ + value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ + if (!value) return 0; + } + + if (*value=='}') return value+1; /* end of array */ + ep=value;return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char *print_object(cJSON *item,int depth,int fmt) +{ + char **entries=0,**names=0; + char *out=0,*ptr,*ret,*str;int len=7,i=0,j; + cJSON *child=item->child; + int numentries=0,fail=0; + /* Count the number of entries. */ + while (child) numentries++,child=child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out=(char*)cJSON_malloc(fmt?depth+4:3); + if (!out) return 0; + ptr=out;*ptr++='{'; + if (fmt) {*ptr++='\n';for (i=0;ichild;depth++;if (fmt) len+=depth; + while (child) + { + names[i]=str=print_string_ptr(child->string); + entries[i++]=ret=print_value(child,depth,fmt); + if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; + child=child->next; + } + + /* Try to allocate the output string */ + if (!fail) out=(char*)cJSON_malloc(len); + if (!out) fail=1; + + /* Handle failure */ + if (fail) + { + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +/* Utility for handling references. */ +static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} +void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} + +cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0; + if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;} +void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} +cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} +void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} + +/* Replace array/object items with new ones. */ +void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; + newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; + if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} +void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){if(newitem->string) cJSON_free(newitem->string);newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} + +/* Create basic types: */ +cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} +cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} +cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} +cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} + +/* Create Arrays: */ +cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} + +/* Duplication */ +cJSON *cJSON_Duplicate(cJSON *item,int recurse) +{ + cJSON *newitem,*cptr,*nptr=0,*newchild; + /* Bail on bad ptr */ + if (!item) return 0; + /* Create new item */ + newitem=cJSON_New_Item(); + if (!newitem) return 0; + /* Copy over all vars */ + newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; + if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} + if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} + /* If non-recursive, then we're done! */ + if (!recurse) return newitem; + /* Walk the ->next chain for the child. */ + cptr=item->child; + while (cptr) + { + newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) {cJSON_Delete(newitem);return 0;} + if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ + cptr=cptr->next; + } + return newitem; +} + +void cJSON_Minify(char *json) +{ + char *into=json; + while (*json) + { + if (*json==' ') json++; + else if (*json=='\t') json++; // Whitespace characters. + else if (*json=='\r') json++; + else if (*json=='\n') json++; + else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. + else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. + else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. + else *into++=*json++; // All other characters. + } + *into=0; // and null-terminate. +} diff --git a/component/common/utilities/cJSON.h b/component/common/utilities/cJSON.h new file mode 100644 index 0000000..0d886d6 --- /dev/null +++ b/component/common/utilities/cJSON.h @@ -0,0 +1,145 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON { + struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/* Supply malloc, realloc and free functions to cJSON */ +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ +extern char *cJSON_Print(cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +extern const char *cJSON_GetErrorPtr(void); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array/object. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated); + +extern void cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/common/utilities/ssl_client.c b/component/common/utilities/ssl_client.c new file mode 100644 index 0000000..be24a2c --- /dev/null +++ b/component/common/utilities/ssl_client.c @@ -0,0 +1,283 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "polarssl/config.h" + +#include +#include + +#include "polarssl/net.h" +#include "polarssl/ssl.h" +#include "polarssl/error.h" +#include "polarssl/memory.h" + +#define SERVER_PORT 443 +#define SERVER_HOST "192.168.13.15" +#define GET_REQUEST "GET / HTTP/1.0\r\n\r\n" +#define DEBUG_LEVEL 0 +#define SSL_USE_SRP 0 +#define STACKSIZE 1150 + +static int is_task = 0; +char server_host[16]; +#if SSL_USE_SRP +char srp_username[16]; +char srp_password[16]; +#endif + +static void my_debug(void *ctx, int level, const char *str) +{ + if(level <= DEBUG_LEVEL) { + printf("\n\r%s", str); + } +} + +static unsigned int arc4random(void) +{ + unsigned int res = xTaskGetTickCount(); + static unsigned int seed = 0xDEADB00B; + + seed = ((seed & 0x007F00FF) << 7) ^ + ((seed & 0x0F80FF00) >> 8) ^ // be sure to stir those low bits + (res << 13) ^ (res >> 9); // using the clock too! + + return seed; +} + +static void get_random_bytes(void *buf, size_t len) +{ + unsigned int ranbuf; + unsigned int *lp; + int i, count; + count = len / sizeof(unsigned int); + lp = (unsigned int *) buf; + + for(i = 0; i < count; i ++) { + lp[i] = arc4random(); + len -= sizeof(unsigned int); + } + + if(len > 0) { + ranbuf = arc4random(); + memcpy(&lp[i], &ranbuf, len); + } +} + +static int my_random(void *p_rng, unsigned char *output, size_t output_len) +{ + get_random_bytes(output, output_len); + return 0; +} + +#ifdef POLARSSL_MEMORY_C +static size_t min_heap_size = 0; + +void* my_malloc(size_t size) +{ + void *ptr = pvPortMalloc(size); + size_t current_heap_size = xPortGetFreeHeapSize(); + + if((current_heap_size < min_heap_size) || (min_heap_size == 0)) + min_heap_size = current_heap_size; + + return ptr; +} + +#define my_free vPortFree + +#endif + +static void ssl_client(void *param) +{ + int ret, len, server_fd = -1; + unsigned char buf[512]; + ssl_context ssl; + +#ifdef POLARSSL_MEMORY_C + memory_set_own(my_malloc, my_free); +#endif + /* + * 0. Initialize the session data + */ + memset(&ssl, 0, sizeof(ssl_context)); + + /* + * 1. Start the connection + */ + printf("\n\r . Connecting to tcp/%s/%d...", server_host, SERVER_PORT); + + if((ret = net_connect(&server_fd, server_host, SERVER_PORT)) != 0) { + printf(" failed\n\r ! net_connect returned %d\n", ret); + goto exit; + } + + printf(" ok\n"); + + /* + * 2. Setup stuff + */ + printf("\n\r . Setting up the SSL/TLS structure..." ); + + if((ret = ssl_init(&ssl)) != 0) { + printf(" failed\n\r ! ssl_init returned %d\n", ret); + goto exit; + } + + printf(" ok\n"); + + ssl_set_endpoint(&ssl, SSL_IS_CLIENT); + ssl_set_authmode(&ssl, SSL_VERIFY_NONE); + ssl_set_rng(&ssl, my_random, NULL); +#ifdef POLARSSL_DEBUG_C + debug_set_threshold(DEBUG_LEVEL); +#endif + ssl_set_dbg(&ssl, my_debug, NULL); + ssl_set_bio(&ssl, net_recv, &server_fd, net_send, &server_fd); +#if SSL_USE_SRP + if(strlen(srp_username)) + ssl_set_srp(&ssl, srp_username, strlen(srp_username), srp_password, strlen(srp_password)); +#endif + /* + * 3. Handshake + */ + printf("\n\r . Performing the SSL/TLS handshake..."); + + while((ret = ssl_handshake(&ssl)) != 0) { + if(ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE) { + printf(" failed\n\r ! ssl_handshake returned -0x%x\n", -ret); + goto exit; + } + } + + printf(" ok\n"); + printf("\n\r . Use ciphersuite %s\n", ssl_get_ciphersuite(&ssl)); + + /* + * 4. Write the GET request + */ + printf("\n\r > Write to server:"); + + len = sprintf((char *) buf, GET_REQUEST); + + while((ret = ssl_write(&ssl, buf, len)) <= 0) { + if(ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE) { + printf(" failed\n\r ! ssl_write returned %d\n", ret); + goto exit; + } + } + + len = ret; + printf(" %d bytes written\n\r\n\r%s\n", len, (char *) buf); + + /* + * 5. Read the HTTP response + */ + printf("\n\r < Read from server:"); + + do { + len = sizeof(buf) - 1; + memset(buf, 0, sizeof(buf)); + ret = ssl_read(&ssl, buf, len); + + if(ret == POLARSSL_ERR_NET_WANT_READ || ret == POLARSSL_ERR_NET_WANT_WRITE) + continue; + + if(ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) + break; + + if(ret < 0) { + printf(" failed\n\r ! ssl_read returned %d\n", ret); + break; + } + + if(ret == 0) { + printf("\n\rEOF\n"); + break; + } + + len = ret; + printf(" %d bytes read\n\r\n\r%s\n", len, (char *) buf); + } + while(1); + + ssl_close_notify(&ssl); + +exit: + +#ifdef POLARSSL_ERROR_C + if(ret != 0) { + char error_buf[100]; + polarssl_strerror(ret, error_buf, 100); + printf("\n\rLast error was: %d - %s\n", ret, error_buf); + } +#endif + + net_close(server_fd); + ssl_free(&ssl); + + if(is_task) { +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\n\rMin available stack size of %s = %d * %d bytes\n\r", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif +#ifdef POLARSSL_MEMORY_C + if(min_heap_size > 0) + printf("\n\rMin available heap size = %d bytes during %s\n\r", min_heap_size, __FUNCTION__); +#endif + vTaskDelete(NULL); + } + + if(param != NULL) + *((int *) param) = ret; +} + +void start_ssl_client(void) +{ + is_task = 1; + //strcpy(server_host, SERVER_HOST); + + if(xTaskCreate(ssl_client, "ssl_client", STACKSIZE, NULL, tskIDLE_PRIORITY + 1, NULL) != pdPASS) + printf("\n\r%s xTaskCreate failed", __FUNCTION__); +} + +void do_ssl_connect(void) +{ + int ret; + static int success = 0; + static int fail = 0; + + is_task = 0; + strcpy(server_host, SERVER_HOST); + ssl_client(&ret); + + if(ret != 0) + printf("\n\r%s fail (success %d times, fail %d times)\n\r", __FUNCTION__, success, ++ fail); + else + printf("\n\r%s success (success %d times, fail %d times)\n\r", __FUNCTION__, ++ success, fail); +} + +void cmd_ssl_client(int argc, char **argv) +{ + if(argc == 2) { + strcpy(server_host, argv[1]); +#if SSL_USE_SRP + strcpy(srp_username, ""); + strcpy(srp_password, ""); +#endif + } +#if SSL_USE_SRP + else if(argc == 4) { + strcpy(server_host, argv[1]); + strcpy(srp_username, argv[2]); + strcpy(srp_password, argv[3]); + } +#endif + else { +#if SSL_USE_SRP + printf("\n\rUsage: %s SSL_SERVER_HOST [SRP_USER_NAME SRP_PASSWORD]", argv[0]); +#else + printf("\n\rUsage: %s SSL_SERVER_HOST", argv[0]); +#endif + return; + } + + start_ssl_client(); +} diff --git a/component/common/utilities/tcpecho.c b/component/common/utilities/tcpecho.c new file mode 100644 index 0000000..69d2124 --- /dev/null +++ b/component/common/utilities/tcpecho.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2001-2003 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 + +#include "lwip/sys.h" +#include "lwip/api.h" + +#define TCPECHO_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) + + + +/*-----------------------------------------------------------------------------------*/ +static void tcpecho_thread(void *arg) +{ + struct netconn *conn, *newconn; + err_t err; + + LWIP_UNUSED_ARG(arg); + + /* Create a new connection identifier. */ + conn = netconn_new(NETCONN_TCP); + + if (conn!=NULL) + { + /* Bind connection to well known port number 7. */ + err = netconn_bind(conn, NULL, 7); + + if (err == ERR_OK) + { + /* Tell connection to go into listening mode. */ + netconn_listen(conn); + + while (1) + { + /* Grab new connection. */ + newconn = netconn_accept(conn); + + /* Process the new connection. */ + if (newconn) + { + struct netbuf *buf; + void *data; + u16_t len; + + while ((buf = netconn_recv(newconn)) != NULL) + { + do + { + netbuf_data(buf, &data, &len); + netconn_write(newconn, data, len, NETCONN_COPY); + + } + while (netbuf_next(buf) >= 0); + + netbuf_delete(buf); + } + + /* Close connection and discard connection identifier. */ + netconn_close(newconn); + netconn_delete(newconn); + } + } + } + else + { + printf(" can not bind TCP netconn"); + } + } + else + { + printf("can not create TCP netconn"); + } +} +/*-----------------------------------------------------------------------------------*/ + +void tcpecho_init(void) +{ + sys_thread_new("tcpecho_thread", tcpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE, TCPECHO_THREAD_PRIO); +} +/*-----------------------------------------------------------------------------------*/ + +void cmd_tcpecho(int argc, char **argv) +{ + printf("\n\rInit TCP ECHO Server ..."); + tcpecho_init(); + printf("\n\r\nPlease use echotool to connect to this echo server. ex. echotool 192.168.0.1 /p tcp /r 7 /n 0"); +} +#endif /* LWIP_NETCONN */ diff --git a/component/common/utilities/tcptest.c b/component/common/utilities/tcptest.c new file mode 100644 index 0000000..5095aa0 --- /dev/null +++ b/component/common/utilities/tcptest.c @@ -0,0 +1,534 @@ +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" + +#include +#include +#include +#include +#include + +#define TCP_PACKET_COUNT 10000 +#define BSD_STACK_SIZE 256 + +#define HOST_IP "192.168.1.101" +#define REMOTE_IP ((u32_t)0xc0a80165UL) /*192.168.1.101*/ +#define LOCAL_IP ((u32_t)0xc0a80164UL) /*192.168.1.100*/ + +unsigned int g_srv_buf_size = 1500; +unsigned int g_cli_buf_size = 1500; +xTaskHandle g_server_task = NULL; +xTaskHandle g_client_task = NULL; + +xTaskHandle udpcllient_task = NULL; +xTaskHandle udpserver_task = NULL; + +unsigned char g_start_server = 0; +unsigned char g_start_client = 0; +unsigned char g_terminate = 0; + +unsigned char udp_start_server = 0; +unsigned char udp_start_client= 0; +char g_server_ip[16]; +unsigned long g_ulPacketCount = TCP_PACKET_COUNT; + +int BsdTcpClient(const char *host_ip, unsigned short usPort) +{ + int iCounter; + short sTestBufLen; + struct sockaddr_in sAddr; + int iAddrSize; + int iSockFD; + int iStatus; + long lLoopCount = 0; + char *cBsdBuf = NULL; + + if(g_cli_buf_size > 4300) + g_cli_buf_size = 4300; + else if (g_cli_buf_size == 0) + g_cli_buf_size = 1500; + + cBsdBuf = pvPortMalloc(g_cli_buf_size); + if(NULL == cBsdBuf){ + printf("\n\rTCP: Allocate client buffer failed.\n"); + return -1; + } + + // filling the buffer + for (iCounter = 0; iCounter < g_cli_buf_size; iCounter++) { + cBsdBuf[iCounter] = (char)(iCounter % 10); + } + sTestBufLen = g_cli_buf_size; + + //filling the TCP server socket address + FD_ZERO(&sAddr); + sAddr.sin_family = AF_INET; + sAddr.sin_port = htons(usPort); + sAddr.sin_addr.s_addr = inet_addr(host_ip); + + iAddrSize = sizeof(struct sockaddr_in); + + // creating a TCP socket + iSockFD = socket(AF_INET, SOCK_STREAM, 0); + if( iSockFD < 0 ) { + printf("\n\rTCP ERROR: create tcp client socket fd error!"); + goto Exit1; + } + + printf("\n\rTCP: ServerIP=%s port=%d.", host_ip, usPort); + printf("\n\rTCP: Create socket %d.", iSockFD); + // connecting to TCP server + iStatus = connect(iSockFD, (struct sockaddr *)&sAddr, iAddrSize); + if (iStatus < 0) { + printf("\n\rTCP ERROR: tcp client connect server error! "); + goto Exit; + } + + printf("\n\rTCP: Connect server successfully."); + // sending multiple packets to the TCP server + while (lLoopCount < g_ulPacketCount && !g_terminate) { + // sending packet + iStatus = send(iSockFD, cBsdBuf, sTestBufLen, 0 ); + if( iStatus <= 0 ) { + printf("\r\nTCP ERROR: tcp client send data error! iStatus:%d", iStatus); + goto Exit; + } + lLoopCount++; + //printf("BsdTcpClient:: send data count:%ld iStatus:%d \n\r", lLoopCount, iStatus); + } + + printf("\n\rTCP: Sent %u packets successfully.",g_ulPacketCount); + +Exit: + //closing the socket after sending 1000 packets + close(iSockFD); + +Exit1: + //free buffer + vPortFree(cBsdBuf); + + return 0; +} + +int BsdTcpServer(unsigned short usPort) +{ + struct sockaddr_in sAddr; + struct sockaddr_in sLocalAddr; + int iCounter; + int iAddrSize; + int iSockFD; + int iStatus; + int iNewSockFD; + long lLoopCount = 0; + //long lNonBlocking = 1; + int iTestBufLen; + int n; + char *cBsdBuf = NULL; + + if(g_srv_buf_size > 5000) + g_srv_buf_size = 5000; + else if (g_srv_buf_size == 0) + g_srv_buf_size = 1500; + + cBsdBuf = pvPortMalloc(g_srv_buf_size); + if(NULL == cBsdBuf){ + printf("\n\rTCP: Allocate server buffer failed.\n"); + return -1; + } + + // filling the buffer + for (iCounter = 0; iCounter < g_srv_buf_size; iCounter++) { + cBsdBuf[iCounter] = (char)(iCounter % 10); + } + iTestBufLen = g_srv_buf_size; + + // creating a TCP socket + iSockFD = socket(AF_INET, SOCK_STREAM, 0); + if( iSockFD < 0 ) { + goto Exit2; + } + + printf("\n\rTCP: Create server socket %d\n\r", iSockFD); + n = 1; + setsockopt( iSockFD, SOL_SOCKET, SO_REUSEADDR, + (const char *) &n, sizeof( n ) ); + + //filling the TCP server socket address + memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr)); + sLocalAddr.sin_family = AF_INET; + sLocalAddr.sin_len = sizeof(sLocalAddr); + sLocalAddr.sin_port = htons(usPort); + sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + iAddrSize = sizeof(sLocalAddr); + + // binding the TCP socket to the TCP server address + iStatus = bind(iSockFD, (struct sockaddr *)&sLocalAddr, iAddrSize); + if( iStatus < 0 ) { + printf("\n\rTCP ERROR: bind tcp server socket fd error! "); + goto Exit1; + } + printf("\n\rTCP: Bind successfully."); + + // putting the socket for listening to the incoming TCP connection + iStatus = listen(iSockFD, 20); + if( iStatus != 0 ) { + printf("\n\rTCP ERROR: listen tcp server socket fd error! "); + goto Exit1; + } + printf("\n\rTCP: Listen port %d", usPort); + + // setting socket option to make the socket as non blocking + //iStatus = setsockopt(iSockFD, SOL_SOCKET, SO_NONBLOCKING, + // &lNonBlocking, sizeof(lNonBlocking)); + //if( iStatus < 0 ) { + // return -1; + //} +Restart: + iNewSockFD = -1; + lLoopCount = 0; + + // waiting for an incoming TCP connection + while( iNewSockFD < 0 ) { + // accepts a connection form a TCP client, if there is any + // otherwise returns SL_EAGAIN + int addrlen=sizeof(sAddr); + iNewSockFD = accept(iSockFD, ( struct sockaddr *)&sAddr, + (socklen_t*)&addrlen); + if( iNewSockFD < 0 ) { + printf("\n\rTCP ERROR: Accept tcp client socket fd error! "); + goto Exit1; + } + printf("\n\rTCP: Accept socket %d successfully.", iNewSockFD); + } + + // waits packets from the connected TCP client + while (!g_terminate) { + iStatus = recv(iNewSockFD, cBsdBuf, iTestBufLen, 0); //MSG_DONTWAIT MSG_WAITALL + if( iStatus < 0 ) { + printf("\n\rTCP ERROR: server recv data error iStatus:%d ", iStatus); + goto Exit; + } else if (iStatus == 0) { + printf("\n\rTCP: Recieved %u packets successfully.", lLoopCount); + close(iNewSockFD); + goto Restart; + } + lLoopCount++; + } + +Exit: + // close the connected socket after receiving from connected TCP client + close(iNewSockFD); + +Exit1: + // close the listening socket + close(iSockFD); + +Exit2: + //free buffer + vPortFree(cBsdBuf); + + return 0; +} + +static void TcpServerHandler(void *param) +{ + unsigned short port = 5001; + + printf("\n\rTCP: Start tcp Server!"); + if(g_start_server) + BsdTcpServer(port); + +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\n\rMin available stack size of %s = %d * %d bytes\n\r", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + printf("\n\rTCP: Tcp server stopped!"); + g_server_task = NULL; + vTaskDelete(NULL); +} + +static void TcpClientHandler(void *param) +{ + unsigned short port = 5001; + + printf("\n\rTCP: Start tcp client!"); + if(g_start_client) + BsdTcpClient(g_server_ip, port); + +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\n\rMin available stack size of %s = %d * %d bytes\n\r", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + printf("\n\rTCP: Tcp client stopped!"); + g_client_task = NULL; + vTaskDelete(NULL); +} + + +/***************************udp related*********************************/ +int udpclient() +{ + int cli_sockfd; + socklen_t addrlen; + struct sockaddr_in cli_addr; + int loop= 0; + char *buffer ; +// int delay = 2; + + + if(!g_ulPacketCount) + g_ulPacketCount = 100; + + if(!g_cli_buf_size) + g_cli_buf_size = 1500; + + buffer = (char*)pvPortMalloc(g_cli_buf_size); + + if(NULL == buffer){ + printf("\n\rudpclient: Allocate buffer failed.\n"); + return -1; + } + + /*create socket*/ + memset(buffer, 0, g_cli_buf_size); + cli_sockfd=socket(AF_INET,SOCK_DGRAM,0); + if (cli_sockfd<0) { + printf("create socket failed\r\n\n"); + return 1; + } + + /* fill sockaddr_in*/ + addrlen=sizeof(struct sockaddr_in); + memset(&cli_addr, 0, addrlen); + + cli_addr.sin_family=AF_INET; + cli_addr.sin_addr.s_addr=inet_addr(g_server_ip); + cli_addr.sin_port=htons(5001); + + /* send data to server*/ + while(loop < g_ulPacketCount && !g_terminate) { + if(sendto(cli_sockfd, buffer, g_cli_buf_size, 0,(struct sockaddr*)&cli_addr, addrlen) < 0) { +// Dynamic delay to prevent send fail due to limited skb, this will degrade throughtput +// if(delay < 100) +// delay += 2; + } + +// vTaskDelay(delay); + loop++; + } + close(cli_sockfd); + //free buffer + vPortFree(buffer); + return 0; +} + +int udpserver() +{ + int ser_sockfd; + socklen_t addrlen; + struct sockaddr_in ser_addr, peer_addr; + uint32_t start_time, end_time; + unsigned char *buffer; + int total_size = 0, report_interval = 1; + + if (g_srv_buf_size == 0) + g_srv_buf_size = 1500; + + buffer = pvPortMalloc(g_srv_buf_size); + + if(NULL == buffer){ + printf("\n\rudpclient: Allocate buffer failed.\n"); + return -1; + } + + /*create socket*/ + ser_sockfd=socket(AF_INET,SOCK_DGRAM,0); + if (ser_sockfd<0) { + printf("\n\rudp server success"); + return 1; + } + + /*fill the socket in*/ + addrlen=sizeof(ser_addr); + memset(&ser_addr, 0,addrlen); + ser_addr.sin_family=AF_INET; + ser_addr.sin_addr.s_addr=htonl(INADDR_ANY); + ser_addr.sin_port=htons(5001); + + /*bind*/ + if (bind(ser_sockfd,(struct sockaddr *)&ser_addr,addrlen)<0) { + printf("bind failed\r\n"); + return 1; + } + + start_time = xTaskGetTickCount(); + total_size = 0; + + while(1) { + int read_size = 0; + addrlen = sizeof(peer_addr); + read_size=recvfrom(ser_sockfd,buffer,g_srv_buf_size,0,(struct sockaddr *) &peer_addr,&addrlen); + if(read_size < 0){ + printf("%s recv error\r\n", __FUNCTION__); + goto Exit; + } + + end_time = xTaskGetTickCount(); + total_size += read_size; + if((end_time - start_time) >= (configTICK_RATE_HZ * report_interval)) { + printf("\nUDP recv %d bytes in %d ticks, %d Kbits/sec\n", + total_size, end_time - start_time, total_size * 8 / 1024 / ((end_time - start_time) / configTICK_RATE_HZ)); + start_time = end_time; + total_size = 0; + } + + /*ack data to client*/ +// Not send ack to prevent send fail due to limited skb, but it will have warning at iperf client +// sendto(ser_sockfd,buffer,read_size,0,(struct sockaddr*)&peer_addr,sizeof(peer_addr)); + } + +Exit: + close(ser_sockfd); + //free buffer + vPortFree(buffer); + return 0; +} + +void Udpclienthandler(void *param) +{ + /*here gives the udp demo code*/ + printf("\n\rUdp client test"); + + udpclient(); +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\n\rMin available stack size of %s = %d * %d bytes", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + printf("\n\rUDP: udp client stopped!"); + udpcllient_task = NULL; + vTaskDelete(NULL); +} + +void Udpserverhandler(void *param) +{ + /*here gives the udp demo code*/ + printf("\n\rUdp server test"); + + udpserver(); +#if defined(INCLUDE_uxTaskGetStackHighWaterMark) && (INCLUDE_uxTaskGetStackHighWaterMark == 1) + printf("\n\rMin available stack size of %s = %d * %d bytes", __FUNCTION__, uxTaskGetStackHighWaterMark(NULL), sizeof(portBASE_TYPE)); +#endif + printf("\n\rUDP: udp client stopped!"); + udpserver_task = NULL; + vTaskDelete(NULL); +} + +/***************************end of udp*********************************/ +void cmd_tcp(int argc, char **argv) +{ + g_terminate = g_start_server = g_start_client = 0; + g_ulPacketCount = 10000; + memset(g_server_ip, 0, 16); + + if(argc < 2) + goto Exit; + + if(strcmp(argv[1], "-s") == 0 ||strcmp(argv[1], "s") == 0) { + if(g_server_task){ + printf("\n\rTCP: Tcp Server is already running."); + return; + }else{ + g_start_server = 1; + if(argc == 3) + g_srv_buf_size = atoi(argv[2]); + } + }else if(strcmp(argv[1], "-c") == 0 || strcmp(argv[1], "c") == 0) { + if(g_client_task){ + printf("\n\rTCP: Tcp client is already running. Please enter \"tcp stop\" to stop it."); + return; + }else{ + if(argc < 4) + goto Exit; + g_start_client = 1; + strncpy(g_server_ip, argv[2], (strlen(argv[2])>16)?16:strlen(argv[2])); + g_cli_buf_size = atoi(argv[3]); + if(argc == 5) + g_ulPacketCount = atoi(argv[4]); + } + + }else if(strcmp(argv[1], "stop") == 0){ + g_terminate = 1; + }else + goto Exit; + + if(g_start_server && (NULL == g_server_task)){ + if(xTaskCreate(TcpServerHandler, "tcp_server", BSD_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, &g_server_task) != pdPASS) + printf("\n\rTCP ERROR: Create tcp server task failed."); + } + if(g_start_client && (NULL == g_client_task)){ + if(xTaskCreate(TcpClientHandler, "tcp_client", BSD_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, &g_client_task) != pdPASS) + printf("\n\rTCP ERROR: Create tcp client task failed."); + } + + return; +Exit: + printf("\n\rTCP: Tcp test command format error!"); + printf("\n\rPlease Enter: \"tcp -s\" to start tcp server or \"tcp <-c *.*.*.*> [count]]\" to start tcp client\n\r"); + return; +} + +void cmd_udp(int argc, char **argv) +{ + g_terminate = udp_start_server = udp_start_client = 0; + g_ulPacketCount = 10000; + if(argc == 2){ + if(strcmp(argv[1], "-s") == 0 ||strcmp(argv[1], "s") == 0){ + if(udpserver_task){ + printf("\r\nUDP: UDP Server is already running."); + return; + }else{ + udp_start_server = 1; + } + }else if(strcmp(argv[1], "-c") == 0 || strcmp(argv[1], "c") == 0){ + if(udpcllient_task){ + printf("\r\nUDP: UDP Server is already running."); + return; + }else{ + udp_start_client= 1; + } + }else if(strcmp(argv[1], "stop") == 0){ + g_terminate = 1; + }else + goto Exit; + }else if(strcmp(argv[1], "-c") == 0 || strcmp(argv[1], "c") == 0) { + if(udpcllient_task){ + printf("\n\nUDP: UDP client is already running. Please enter \"udp stop\" to stop it."); + return; + }else{ + if(argc < 4) + goto Exit; + udp_start_client = 1; + strncpy(g_server_ip, argv[2], (strlen(argv[2])>16)?16:strlen(argv[2])); + g_cli_buf_size = atoi(argv[3]); + if(argc == 5) + g_ulPacketCount = atoi(argv[4]); + } + + }else + goto Exit; + + if(udp_start_server && (NULL == udpserver_task)){ + if(xTaskCreate(Udpserverhandler, "udp_server", BSD_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &udpserver_task) != pdPASS) + printf("\r\nUDP ERROR: Create udp server task failed."); + } + + if(udp_start_client && (NULL == udpcllient_task)){ + if(xTaskCreate(Udpclienthandler, "udp_client", BSD_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, &udpcllient_task) != pdPASS) + printf("\r\nUDP ERROR: Create udp client task failed."); + } + + return; +Exit: + printf("\r\nUDP: udp test command format error!"); + printf("\r\nPlease Enter: \"udp -s\" to start udp server or \"udp -c to start udp client\r\n"); + return; +} + diff --git a/component/common/utilities/udpecho.c b/component/common/utilities/udpecho.c new file mode 100644 index 0000000..57674a0 --- /dev/null +++ b/component/common/utilities/udpecho.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2001-2003 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 + +#include "lwip/api.h" +#include "lwip/sys.h" + + +#define UDPECHO_THREAD_PRIO ( tskIDLE_PRIORITY + 3 ) + +static struct netconn *conn; +static struct netbuf *buf; +static struct ip_addr *addr; +static unsigned short port; +/*-----------------------------------------------------------------------------------*/ +static void udpecho_thread(void *arg) +{ + err_t err; + + LWIP_UNUSED_ARG(arg); + + conn = netconn_new(NETCONN_UDP); + if (conn!= NULL) + { + err = netconn_bind(conn, IP_ADDR_ANY, 7); + if (err == ERR_OK) + { + while (1) + { + buf = netconn_recv(conn); + + if (buf!= NULL) + { + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + netconn_connect(conn, addr, port); + buf->addr = NULL; + netconn_send(conn,buf); + netbuf_delete(buf); + } + } + } + else + { + printf("can not bind netconn"); + } + } + else + { + printf("can create new UDP netconn"); + } +} +/*-----------------------------------------------------------------------------------*/ +void udpecho_init(void) +{ + sys_thread_new("udpecho_thread", udpecho_thread, NULL, DEFAULT_THREAD_STACKSIZE,UDPECHO_THREAD_PRIO ); +} + +#endif /* LWIP_NETCONN */ diff --git a/component/common/utilities/update.c b/component/common/utilities/update.c new file mode 100644 index 0000000..0df7bf2 --- /dev/null +++ b/component/common/utilities/update.c @@ -0,0 +1,971 @@ +#include +#include +#include +#include +#include +#include + +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +#include +#if defined(STM32F2XX) +#include +#elif defined(STM32F4XX) +#include +#elif defined(STM32f1xx) +#include +#endif +#include "cloud_updater.h" +#else +#include "flash_api.h" +#endif +#include "update.h" +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#define OFFSET_DATA FLASH_SYSTEM_DATA_ADDR +#define IMAGE_2 0x0000B000 +#define WRITE_OTA_ADDR 0 +#define CONFIG_CUSTOM_SIGNATURE 1 +#define SWAP_UPDATE 0 + +#if WRITE_OTA_ADDR +#define BACKUP_SECTOR (FLASH_SYSTEM_DATA_ADDR - 0x1000) +#endif + +#if CONFIG_CUSTOM_SIGNATURE +/* --------------------------------------------------- + * Customized Signature + * ---------------------------------------------------*/ +// This signature can be used to verify the correctness of the image +// It will be located in fixed location in application image +#pragma location=".custom.validate.rodata" +const unsigned char cus_sig[32] = "Customer Signature-modelxxx"; +#endif + +#else +#define CONFIG_SECTOR FLASH_Sector_1 +#define APPLICATION_SECTOR FLASH_Sector_2 +#define UPDATE_SECTOR FLASH_Sector_8 +#endif +#define STACK_SIZE 1024 +#define TASK_PRIORITY tskIDLE_PRIORITY + 1 +#define BUF_SIZE 512 +#define ETH_ALEN 6 + +#define SERVER_LOCAL 1 +#define SERVER_CLOUD 2 +#define SERVER_TYPE SERVER_LOCAL +#define UPDATE_DBG 1 + +#if (SERVER_TYPE == SERVER_LOCAL) +typedef struct +{ + uint32_t ip_addr; + uint16_t port; +}update_cfg_local_t; +#endif + +#if (SERVER_TYPE == SERVER_CLOUD) +#define REPOSITORY_LEN 16 +#define FILE_PATH_LEN 64 +typedef struct +{ + uint8_t repository[REPOSITORY_LEN]; + uint8_t file_path[FILE_PATH_LEN]; +}update_cfg_cloud_t; +#endif + +sys_thread_t TaskOTA = NULL; +//--------------------------------------------------------------------- +static void* update_malloc(unsigned int size) +{ + return pvPortMalloc(size); +} + +//--------------------------------------------------------------------- +static void update_free(void *buf) +{ + vPortFree(buf); +} + +//--------------------------------------------------------------------- +#if (SERVER_TYPE == SERVER_LOCAL) +#if defined(STM32F2XX) ||(STM32F4XX) +static void update_ota_local_task(void *param) +{ + int server_socket = 0; + struct sockaddr_in server_addr; + char *buf; + int read_bytes, size = 0, i; + update_cfg_local_t *cfg = (update_cfg_local_t*)param; + uint32_t address, checksum = 0; +#if CONFIG_WRITE_MAC_TO_FLASH + char mac[ETH_ALEN]; +#endif + printf("\n\r[%s] Update task start", __FUNCTION__); + buf = update_malloc(BUF_SIZE); + if(!buf){ + printf("\n\r[%s] Alloc buffer failed", __FUNCTION__); + goto update_ota_exit; + } + // Connect socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if(server_socket < 0){ + printf("\n\r[%s] Create socket failed", __FUNCTION__); + goto update_ota_exit; + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = cfg->ip_addr; + server_addr.sin_port = cfg->port; + + if(connect(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1){ + printf("\n\r[%s] socket connect failed", __FUNCTION__); + goto update_ota_exit; + } + // Erase config sectors + if(flash_EraseSector(CONFIG_SECTOR) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } +#if CONFIG_WRITE_MAC_TO_FLASH + // Read MAC address + if(flash_Read(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Read MAC error", __FUNCTION__); + goto update_ota_exit; + } +#endif + // Erase update sectors + for(i = UPDATE_SECTOR; i <= FLASH_Sector_11; i += 8){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } + // Write update sectors + address = flash_SectorAddress(UPDATE_SECTOR); + printf("\n\r"); + while(1){ + read_bytes = read(server_socket, buf, BUF_SIZE); + if(read_bytes == 0) break; // Read end + if(read_bytes < 0){ + printf("\n\r[%s] Read socket failed", __FUNCTION__); + goto update_ota_exit; + } + if(flash_Wrtie(address + size, buf, read_bytes) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes; + for(i = 0; i < read_bytes; i ++) + checksum += buf[i]; + printf("\rUpdate file size = %d ", size); + } +#if CONFIG_WRITE_MAC_TO_FLASH + //Write MAC address + if(!(mac[0]==0xff&&mac[1]==0xff&&mac[2]==0xff&&mac[3]==0xff&&mac[4]==0xff&&mac[5]==0xff)){ + if(flash_Wrtie(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Write MAC failed", __FUNCTION__); + goto update_ota_exit; + } + } +#endif + // Write config sectors + address = flash_SectorAddress(CONFIG_SECTOR); + if( (flash_Wrtie(address, (char*)&size, 4) < 0) || + (flash_Wrtie(address+4, (char*)&checksum, 4) < 0) ){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + printf("\n\r[%s] Update OTA success!", __FUNCTION__); +update_ota_exit: + if(buf) + update_free(buf); + if(server_socket >= 0) + close(server_socket); + if(param) + update_free(param); + TaskOTA = NULL; + printf("\n\r[%s] Update task exit", __FUNCTION__); + vTaskDelete(NULL); + return; +} +#elif defined(STM32f1xx) +static void update_ota_local_task(void *param) +{ + int server_socket; + struct sockaddr_in server_addr; + char *buf, flag_a = 0; + int read_bytes, size = 0, i; + update_cfg_local_t *cfg = (update_cfg_local_t*)param; + uint32_t address, checksum = 0; + uint16_t a = 0; +#if CONFIG_WRITE_MAC_TO_FLASH + char mac[ETH_ALEN]; +#endif + + printf("\n\r[%s] Update task start", __FUNCTION__); + buf = update_malloc(BUF_SIZE); + if(!buf){ + printf("\n\r[%s] Alloc buffer failed", __FUNCTION__); + goto update_ota_exit; + } + // Connect socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if(server_socket < 0){ + printf("\n\r[%s] Create socket failed", __FUNCTION__); + goto update_ota_exit; + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = cfg->ip_addr; + server_addr.sin_port = cfg->port; + + if(connect(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1){ + printf("\n\r[%s] socket connect failed", __FUNCTION__); + goto update_ota_exit; + } + // Erase config sectors + for(i = CONFIG_SECTOR; i < APPLICATION_SECTOR; i += FLASH_PAGE_SIZE){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } +#if CONFIG_WRITE_MAC_TO_FLASH + // Read MAC address + if(flash_Read(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Read MAC error", __FUNCTION__); + goto update_ota_exit; + } +#endif + + // Erase update sectors + for(i = UPDATE_SECTOR; i < FLASH_Sector_0 + FLASH_SIZE; i += FLASH_PAGE_SIZE){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } + // Write update sectors + address = UPDATE_SECTOR; + printf("\n\r"); + while(1){ + read_bytes = read(server_socket, buf, BUF_SIZE); + if(read_bytes == 0) break; // Read end + if(read_bytes < 0){ + printf("\n\r[%s] Read socket failed", __FUNCTION__); + goto update_ota_exit; + } + if(flag_a == 0){ + if(read_bytes % 2 != 0){ + a = buf[read_bytes - 1]; + flag_a = 1; + if(flash_Wrtie(address + size, buf, read_bytes - 1) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 1; + } + else{ + if(flash_Wrtie(address + size, buf, read_bytes) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes; + } + } + else{ + a = buf[0] << 8 | a; + if(flash_Wrtie(address + size, (char*)(&a), 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += 2; + a = 0; + flag_a = 0; + if((read_bytes - 1) % 2 != 0){ + a = buf[read_bytes - 1]; + flag_a = 1; + if(flash_Wrtie(address + size, buf + 1, read_bytes - 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 2; + } + else{ + if(flash_Wrtie(address + size, buf + 1, read_bytes - 1) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 1; + } + } + for(i = 0; i < read_bytes; i ++) + checksum += buf[i]; + printf("\rUpdate file size = %d ", size); + } + if(flag_a){ + if(flash_Wrtie(address + size, (char*)(&a), 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += 1; + } +#if CONFIG_WRITE_MAC_TO_FLASH + //Write MAC address + if(!(mac[0]==0xff&&mac[1]==0xff&&mac[2]==0xff&&mac[3]==0xff&&mac[4]==0xff&&mac[5]==0xff)){ + if(flash_Wrtie(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Write MAC failed", __FUNCTION__); + goto update_ota_exit; + } + } +#endif + + // Write config sectors + address = CONFIG_SECTOR; + if( (flash_Wrtie(address, (char*)&size, 4) < 0) || + (flash_Wrtie(address+4, (char*)&checksum, 4) < 0) ){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + printf("\n\r[%s] Update OTA success!", __FUNCTION__); +update_ota_exit: + if(buf) + update_free(buf); + if(server_socket >= 0) + close(server_socket); + if(param) + update_free(param); + TaskOTA = NULL; + printf("\n\r[%s] Update task exit", __FUNCTION__); + vTaskDelete(NULL); + return; +} + +#elif defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) + +void ota_platform_reset(void) +{ + //wifi_off(); + + // Set processor clock to default before system reset + HAL_WRITE32(SYSTEM_CTRL_BASE, 0x14, 0x00000021); + osDelay(100); + + // Cortex-M3 SCB->AIRCR + HAL_WRITE32(0xE000ED00, 0x0C, (0x5FA << 16) | // VECTKEY + (HAL_READ32(0xE000ED00, 0x0C) & (7 << 8)) | // PRIGROUP + (1 << 2)); // SYSRESETREQ + while(1) osDelay(1000); +} +#if WRITE_OTA_ADDR +int write_ota_addr_to_system_data(flash_t *flash, uint32_t ota_addr) +{ + uint32_t data, i = 0; + //Get upgraded image 2 addr from offset + flash_read_word(flash, OFFSET_DATA, &data); + printf("\n\r[%s] data 0x%x ota_addr 0x%x", __FUNCTION__, data, ota_addr); + if(data == ~0x0){ + flash_write_word(flash, OFFSET_DATA, ota_addr); + }else{ + //erase backup sector + flash_erase_sector(flash, BACKUP_SECTOR); + //backup system data to backup sector + for(i = 0; i < 0x1000; i+= 4){ + flash_read_word(flash, OFFSET_DATA + i, &data); + if(i == 0) + data = ota_addr; + flash_write_word(flash, BACKUP_SECTOR + i,data); + } + //erase system data + flash_erase_sector(flash, OFFSET_DATA); + //write data back to system data + for(i = 0; i < 0x1000; i+= 4){ + flash_read_word(flash, BACKUP_SECTOR + i, &data); + flash_write_word(flash, OFFSET_DATA + i,data); + } + //erase backup sector + flash_erase_sector(flash, BACKUP_SECTOR); + } + return 0; +} +#endif +static void update_ota_local_task(void *param) +{ + int server_socket; + struct sockaddr_in server_addr; + unsigned char *buf; + int read_bytes = 0, size = 0, i = 0; + update_cfg_local_t *cfg = (update_cfg_local_t *)param; + uint32_t address, checksum = 0; + flash_t flash; + uint32_t NewImg2BlkSize = 0, NewImg2Len = 0, NewImg2Addr = 0, file_checksum[3]; + uint32_t Img2Len = 0; + int ret = -1 ; + //uint8_t signature[8] = {0x38,0x31,0x39,0x35,0x38,0x37,0x31,0x31}; + uint32_t IMAGE_x = 0, ImgxLen = 0, ImgxAddr = 0; +#if WRITE_OTA_ADDR + uint32_t ota_addr = 0x80000; +#endif +#if CONFIG_CUSTOM_SIGNATURE + char custom_sig[32] = "Customer Signature-modelxxx"; + uint32_t read_custom_sig[8]; +#endif + printf("\n\r[%s] Update task start", __FUNCTION__); + buf = update_malloc(BUF_SIZE); + if(!buf){ + printf("\n\r[%s] Alloc buffer failed", __FUNCTION__); + goto update_ota_exit; + } + // Connect socket + server_socket = socket(AF_INET, SOCK_STREAM, 0); + if(server_socket < 0){ + printf("\n\r[%s] Create socket failed", __FUNCTION__); + goto update_ota_exit; + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = cfg->ip_addr; + server_addr.sin_port = cfg->port; + + if(connect(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1){ + printf("\n\r[%s] socket connect failed", __FUNCTION__); + goto update_ota_exit; + } + DBG_INFO_MSG_OFF(_DBG_SPI_FLASH_); + +#if 1 + // The upgraded image2 pointer must 4K aligned and should not overlap with Default Image2 + flash_read_word(&flash, IMAGE_2, &Img2Len); + IMAGE_x = IMAGE_2 + Img2Len + 0x10; + flash_read_word(&flash, IMAGE_x, &ImgxLen); + flash_read_word(&flash, IMAGE_x+4, &ImgxAddr); + if(ImgxAddr==0x30000000){ + printf("\n\r[%s] IMAGE_3 0x%x Img3Len 0x%x", __FUNCTION__, IMAGE_x, ImgxLen); + }else{ + printf("\n\r[%s] no IMAGE_3", __FUNCTION__); + // no image3 + IMAGE_x = IMAGE_2; + ImgxLen = Img2Len; + } +#if WRITE_OTA_ADDR + if((ota_addr > IMAGE_x) && ((ota_addr < (IMAGE_x+ImgxLen))) || + (ota_addr < IMAGE_x) || + ((ota_addr & 0xfff) != 0)|| + (ota_addr == ~0x0)){ + printf("\n\r[%s] illegal ota addr 0x%x", __FUNCTION__, ota_addr); + goto update_ota_exit; + }else + write_ota_addr_to_system_data( &flash, ota_addr); +#endif + //Get upgraded image 2 addr from offset + flash_read_word(&flash, OFFSET_DATA, &NewImg2Addr); + if((NewImg2Addr > IMAGE_x) && ((NewImg2Addr < (IMAGE_x+ImgxLen))) || + (NewImg2Addr < IMAGE_x) || + ((NewImg2Addr & 0xfff) != 0)|| + (NewImg2Addr == ~0x0)){ + printf("\n\r[%s] after read, NewImg2Addr 0x%x, Img2Len 0x%x", __FUNCTION__, NewImg2Addr, Img2Len); + goto update_ota_exit; + } +#else + //For test, hard code addr + NewImg2Addr = 0x80000; +#endif + + //Clear file_checksum + memset(file_checksum, 0, sizeof(file_checksum)); + + if(file_checksum[0] == 0){ + printf("\n\r[%s] Read chechsum first", __FUNCTION__); + read_bytes = read(server_socket, file_checksum, sizeof(file_checksum)); + // !X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X + // !W checksum !W padding 0 !W file size !W + // !X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X!X + printf("\n\r[%s] chechsum read_bytes %d", __FUNCTION__, read_bytes); + printf("\n\r[%s] chechsum 0x%x, file size 0x%x", __FUNCTION__, file_checksum[0],file_checksum[2]); + if(file_checksum[2] == 0){ + printf("\n\r[%s] No checksum and file size", __FUNCTION__); + goto update_ota_exit; + } + } + +#if SWAP_UPDATE + uint32_t SigImage0,SigImage1; + uint32_t Part1Addr=0xFFFFFFFF, Part2Addr=0xFFFFFFFF; + uint32_t OldImg2Addr; + flash_read_word(&flash, 0x18, &Part1Addr); + Part1Addr = (Part1Addr&0xFFFF)*1024; // first partition + Part2Addr = NewImg2Addr; + + // read Part1/Part2 signature + flash_read_word(&flash, Part1Addr+8, &SigImage0); + flash_read_word(&flash, Part1Addr+12, &SigImage1); + printf("\n\r[%s] Part1 Sig %x", __FUNCTION__, SigImage0); + if(SigImage0==0x35393138 && SigImage1==0x31313738) + OldImg2Addr = Part1Addr; // newer version, change to older version + else + NewImg2Addr = Part1Addr; // update to older version + flash_read_word(&flash, Part2Addr+8, &SigImage0); + flash_read_word(&flash, Part2Addr+12, &SigImage1); + printf("\n\r[%s] Part2 Sig %x", __FUNCTION__, SigImage0); + if(SigImage0==0x35393138 && SigImage1==0x31313738) + OldImg2Addr = Part2Addr; + else + NewImg2Addr = Part2Addr; + + printf("\n\r[%s] New %x, Old %x", __FUNCTION__, NewImg2Addr, OldImg2Addr); + + if( NewImg2Addr==Part1Addr ){ + if( file_checksum[2] > (Part2Addr-Part1Addr) ){ // firmware size too large + printf("\n\r[%s] Part1 size < OTA size", __FUNCTION__); + goto update_ota_exit; + // or update to partition2 + // NewImg2Addr = Part2Addr; + } + } + +#endif + + //Erase upgraded image 2 region + if(NewImg2Len == 0){ + NewImg2Len = file_checksum[2]; + printf("\n\r[%s] NewImg2Len %d ", __FUNCTION__, NewImg2Len); + if((int)NewImg2Len > 0){ + NewImg2BlkSize = ((NewImg2Len - 1)/4096) + 1; + printf("\n\r[%s] NewImg2BlkSize %d 0x%8x", __FUNCTION__, NewImg2BlkSize, NewImg2BlkSize); + for( i = 0; i < NewImg2BlkSize; i++) + flash_erase_sector(&flash, NewImg2Addr + i * 4096); + }else{ + printf("\n\r[%s] Size INVALID", __FUNCTION__); + goto update_ota_exit; + } + } + + printf("\n\r[%s] NewImg2Addr 0x%x", __FUNCTION__, NewImg2Addr); + // Write New Image 2 sector + if(NewImg2Addr != ~0x0){ + address = NewImg2Addr; + printf("\n\r"); + while(1){ + memset(buf, 0, BUF_SIZE); + read_bytes = read(server_socket, buf, BUF_SIZE); + if(read_bytes == 0) break; // Read end + if(read_bytes < 0){ + printf("\n\r[%s] Read socket failed", __FUNCTION__); + goto update_ota_exit; + } + //printf("\n\r[%s] read_bytes %d", __FUNCTION__, read_bytes); + + #if 1 + if(flash_stream_write(&flash, address + size, read_bytes, buf) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes; + for(i = 0; i < read_bytes; i ++) + checksum += buf[i]; + #else + size += read_bytes; + for(i = 0; i < read_bytes; i ++){ + checksum += buf[i]; + } + #endif + } + printf("\n\r"); + printf("\n\rUpdate file size = %d checksum 0x%x ", size, checksum); +#if CONFIG_WRITE_MAC_TO_FLASH + //Write MAC address + if(!(mac[0]==0xff&&mac[1]==0xff&&mac[2]==0xff&&mac[3]==0xff&&mac[4]==0xff&&mac[5]==0xff)){ + if(flash_write_word(&flash, FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Write MAC failed", __FUNCTION__); + goto update_ota_exit; + } + } +#endif + printf("\n\r checksum 0x%x file_checksum 0x%x ", checksum, *(file_checksum)); +#if CONFIG_CUSTOM_SIGNATURE + for(i = 0; i < 8; i ++){ + flash_read_word(&flash, NewImg2Addr + 0x28 + i *4, read_custom_sig + i); + } + printf("\n\r[%s] read_custom_sig %s", __FUNCTION__ , (char*)read_custom_sig); +#endif + // compare checksum with received checksum + if(!memcmp(&checksum,file_checksum,sizeof(checksum)) +#if CONFIG_CUSTOM_SIGNATURE + && !strcmp((char*)read_custom_sig,custom_sig) +#endif + ){ + + //Set signature in New Image 2 addr + 8 and + 12 + uint32_t sig_readback0,sig_readback1; + flash_write_word(&flash,NewImg2Addr + 8, 0x35393138); + flash_write_word(&flash,NewImg2Addr + 12, 0x31313738); + flash_read_word(&flash, NewImg2Addr + 8, &sig_readback0); + flash_read_word(&flash, NewImg2Addr + 12, &sig_readback1); + printf("\n\r[%s] signature %x,%x, checksum 0x%x", __FUNCTION__ , sig_readback0, sig_readback1, checksum); +#if SWAP_UPDATE + flash_write_word(&flash,OldImg2Addr + 8, 0x35393130); + flash_write_word(&flash,OldImg2Addr + 12, 0x31313738); + flash_read_word(&flash, OldImg2Addr + 8, &sig_readback0); + flash_read_word(&flash, OldImg2Addr + 12, &sig_readback1); + printf("\n\r[%s] old signature %x,%x", __FUNCTION__ , sig_readback0, sig_readback1); +#endif + printf("\n\r[%s] Update OTA success!", __FUNCTION__); + + ret = 0; + } + } +update_ota_exit: + if(buf) + update_free(buf); + if(server_socket >= 0) + close(server_socket); + if(param) + update_free(param); + TaskOTA = NULL; + printf("\n\r[%s] Update task exit", __FUNCTION__); + if(!ret){ + printf("\n\r[%s] Ready to reboot", __FUNCTION__); + ota_platform_reset(); + } + vTaskDelete(NULL); + return; + +} +#endif + +//--------------------------------------------------------------------- +int update_ota_local(char *ip, int port) +{ + update_cfg_local_t *pUpdateCfg; + + if(TaskOTA){ + printf("\n\r[%s] Update task has created.", __FUNCTION__); + return 0; + } + pUpdateCfg = update_malloc(sizeof(update_cfg_local_t)); + if(pUpdateCfg == NULL){ + printf("\n\r[%s] Alloc update cfg failed", __FUNCTION__); + return -1; + } + pUpdateCfg->ip_addr = inet_addr(ip); + pUpdateCfg->port = ntohs(port); + + TaskOTA = sys_thread_new("OTA_server", update_ota_local_task, pUpdateCfg, STACK_SIZE, TASK_PRIORITY); + if(TaskOTA == NULL){ + update_free(pUpdateCfg); + printf("\n\r[%s] Create update task failed", __FUNCTION__); + } + return 0; +} +#endif // #if (SERVER_TYPE == SERVER_LOCAL) + +//--------------------------------------------------------------------- +#if (SERVER_TYPE == SERVER_CLOUD) +#if defined(STM32F2XX) ||(STM32F4XX) +static void update_ota_cloud_task(void *param) +{ + struct updater_ctx ctx; + char *buf; + int read_bytes, size = 0, i; + uint32_t address, checksum = 0; + update_cfg_cloud_t *cfg = (update_cfg_cloud_t*)param; +#if CONFIG_WRITE_MAC_TO_FLASH + char mac[ETH_ALEN]; +#endif + printf("\n\r[%s] Update task start", __FUNCTION__); + buf = update_malloc(BUF_SIZE); + if(!buf){ + printf("\n\r[%s] Alloc buffer failed", __FUNCTION__); + goto update_ota_exit_1; + } + // Init ctx + if(updater_init_ctx(&ctx, (char*)cfg->repository, (char*)cfg->file_path) != 0) { + printf("\n\r[%s] Cloud ctx init failed", __FUNCTION__); + goto update_ota_exit_1; + } + printf("\n\r[%s] Firmware link: %s, size = %d bytes, checksum = 0x%08x, version = %s\n", __FUNCTION__, + ctx.link, ctx.size, ctx.checksum, ctx.version); + + // Erase config sectors + if(flash_EraseSector(CONFIG_SECTOR) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } +#if CONFIG_WRITE_MAC_TO_FLASH + // Read MAC address + if(flash_Read(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Read MAC error", __FUNCTION__); + goto update_ota_exit; + } +#endif + // Erase update sectors + for(i = UPDATE_SECTOR; i <= FLASH_Sector_11; i += 8){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } + // Write update sectors + address = flash_SectorAddress(UPDATE_SECTOR); + printf("\n\r"); + while(ctx.bytes < ctx.size){ + read_bytes = updater_read_bytes(&ctx, (unsigned char*)buf, BUF_SIZE); + if(read_bytes == 0) break; // Read end + if(read_bytes < 0){ + printf("\n\r[%s] Read socket failed", __FUNCTION__); + goto update_ota_exit; + } + if(flash_Wrtie(address + size, buf, read_bytes) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes; + for(i = 0; i < read_bytes; i ++) + checksum += buf[i]; + printf("\rUpdate file size = %d/%d bytes ", ctx.bytes, ctx.size); + } + printf("\n\r[%s] ctx checksum = %08x, computed checksum = %08x\n", __FUNCTION__, ctx.checksum, checksum); + if(checksum != ctx.checksum){ + printf("\n\r[%s] Checksum error", __FUNCTION__); + goto update_ota_exit; + } +#if CONFIG_WRITE_MAC_TO_FLASH + //Write MAC address + if(!(mac[0]==0xff&&mac[1]==0xff&&mac[2]==0xff&&mac[3]==0xff&&mac[4]==0xff&&mac[5]==0xff)){ + if(flash_Wrtie(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Write MAC failed", __FUNCTION__); + goto update_ota_exit; + } + } +#endif + // Write config sectors + address = flash_SectorAddress(CONFIG_SECTOR); + if( (flash_Wrtie(address, (char*)&size, 4) < 0) || + (flash_Wrtie(address+4, (char*)&checksum, 4) < 0) ){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + printf("\n\r[%s] Update OTA success!", __FUNCTION__); + +update_ota_exit: + updater_free_ctx(&ctx); +update_ota_exit_1: + if(buf) + update_free(buf); + if(param) + update_free(param); + TaskOTA = NULL; + printf("\n\r[%s] Update task exit", __FUNCTION__); + vTaskDelete(NULL); + return; +} +#elif defined(STM32f1xx) +static void update_ota_cloud_task(void *param) +{ + struct updater_ctx ctx; + char *buf, flag_a = 0; + int read_bytes, size = 0, i; + uint32_t address, checksum = 0; + update_cfg_cloud_t *cfg = (update_cfg_cloud_t*)param; + uint16_t a = 0; +#if CONFIG_WRITE_MAC_TO_FLASH + char mac[ETH_ALEN]; +#endif + printf("\n\r[%s] Update task start", __FUNCTION__); + buf = update_malloc(BUF_SIZE); + if(!buf){ + printf("\n\r[%s] Alloc buffer failed", __FUNCTION__); + goto update_ota_exit_1; + } + // Init ctx + if(updater_init_ctx(&ctx, (char*)cfg->repository, (char*)cfg->file_path) != 0) { + printf("\n\r[%s] Cloud ctx init failed", __FUNCTION__); + goto update_ota_exit_1; + } + printf("\n\r[%s] Firmware link: %s, size = %d bytes, checksum = 0x%08x, version = %s\n", __FUNCTION__, + ctx.link, ctx.size, ctx.checksum, ctx.version); + + // Erase config sectors + for(i = CONFIG_SECTOR; i < APPLICATION_SECTOR; i += FLASH_PAGE_SIZE){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } +#if CONFIG_WRITE_MAC_TO_FLASH + // Read MAC address + if(flash_Read(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Read MAC error", __FUNCTION__); + goto update_ota_exit; + } +#endif + + // Erase update sectors + for(i = UPDATE_SECTOR; i < FLASH_Sector_0 + FLASH_SIZE; i += FLASH_PAGE_SIZE){ + if(flash_EraseSector(i) < 0){ + printf("\n\r[%s] Erase sector failed", __FUNCTION__); + goto update_ota_exit; + } + } + // Write update sectors + address = UPDATE_SECTOR; + printf("\n\r"); + while(ctx.bytes < ctx.size){ + read_bytes = updater_read_bytes(&ctx, (unsigned char*)buf, BUF_SIZE); + if(read_bytes == 0) break; // Read end + if(read_bytes < 0){ + printf("\n\r[%s] Read socket failed", __FUNCTION__); + goto update_ota_exit; + } + if(flag_a == 0){ + if(read_bytes % 2 != 0){ + a = buf[read_bytes - 1]; + flag_a = 1; + if(flash_Wrtie(address + size, buf, read_bytes - 1) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 1; + } + else{ + if(flash_Wrtie(address + size, buf, read_bytes) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes; + } + } + else{ + a = buf[0]<< 8 |a; + if(flash_Wrtie(address + size, (char*)(&a), 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += 2; + a = 0; + flag_a = 0; + if((read_bytes - 1) % 2 != 0){ + a = buf[read_bytes - 1]; + flag_a = 1; + if(flash_Wrtie(address + size, buf + 1, read_bytes - 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 2; + } + else{ + if(flash_Wrtie(address + size, buf + 1, read_bytes - 1) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += read_bytes - 1; + } + } + for(i = 0; i < read_bytes; i ++) + checksum += buf[i]; + printf("\rUpdate file size = %d/%d bytes ", ctx.bytes, ctx.size); + } + if(flag_a){ + if(flash_Wrtie(address + size, (char*)(&a), 2) < 0){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + size += 1; + } + printf("\n\r[%s] ctx checksum = %08x, computed checksum = %08x\n", __FUNCTION__, ctx.checksum, checksum); + if(checksum != ctx.checksum){ + printf("\n\r[%s] Checksum error", __FUNCTION__); + goto update_ota_exit; + } +#if CONFIG_WRITE_MAC_TO_FLASH + //Write MAC address + if(!(mac[0]==0xff&&mac[1]==0xff&&mac[2]==0xff&&mac[3]==0xff&&mac[4]==0xff&&mac[5]==0xff)){ + if(flash_Wrtie(FLASH_ADD_STORE_MAC, mac, ETH_ALEN) < 0){ + printf("\n\r[%s] Write MAC failed", __FUNCTION__); + goto update_ota_exit; + } + } +#endif + + // Write config sectors + address = CONFIG_SECTOR; + if( (flash_Wrtie(address, (char*)&size, 4) < 0) || + (flash_Wrtie(address+4, (char*)&checksum, 4) < 0) ){ + printf("\n\r[%s] Write sector failed", __FUNCTION__); + goto update_ota_exit; + } + printf("\n\r[%s] Update OTA success!", __FUNCTION__); + +update_ota_exit: + updater_free_ctx(&ctx); +update_ota_exit_1: + if(buf) + update_free(buf); + if(param) + update_free(param); + TaskOTA = NULL; + printf("\n\r[%s] Update task exit", __FUNCTION__); + vTaskDelete(NULL); + return; +} + +#endif + +//--------------------------------------------------------------------- +int update_ota_cloud(char *repository, char *file_path) +{ + update_cfg_cloud_t *pUpdateCfg; + + if(TaskOTA){ + printf("\n\r[%s] Update task has created.", __FUNCTION__); + return 0; + } + pUpdateCfg = update_malloc(sizeof(update_cfg_cloud_t)); + if(pUpdateCfg == NULL){ + printf("\n\r[%s] Alloc update cfg failed.", __FUNCTION__); + goto exit; + } + if(strlen(repository) > (REPOSITORY_LEN-1)){ + printf("\n\r[%s] Repository length is too long.", __FUNCTION__); + goto exit; + } + if(strlen(file_path) > (FILE_PATH_LEN-1)){ + printf("\n\r[%s] File path length is too long.", __FUNCTION__); + goto exit; + } + strcpy((char*)pUpdateCfg->repository, repository); + strcpy((char*)pUpdateCfg->file_path, file_path); + + TaskOTA = sys_thread_new("OTA_server", update_ota_cloud_task, pUpdateCfg, STACK_SIZE, TASK_PRIORITY); + if(TaskOTA == NULL){ + printf("\n\r[%s] Create update task failed", __FUNCTION__); + goto exit; + } + +exit: + update_free(pUpdateCfg); + return 0; +} +#endif // #if (SERVER_TYPE == SERVER_CLOUD) + +//--------------------------------------------------------------------- +void cmd_update(int argc, char **argv) +{ +// printf("\n\r[%s] Firmware A", __FUNCTION__); +#if (SERVER_TYPE == SERVER_LOCAL) + int port; + if(argc != 3){ + printf("\n\r[%s] Usage: update IP PORT", __FUNCTION__); + return; + } + port = atoi(argv[2]); + update_ota_local(argv[1], port); +#endif +#if (SERVER_TYPE == SERVER_CLOUD) + if(argc != 3){ + printf("\n\r[%s] Usage: update REPOSITORY FILE_PATH", __FUNCTION__); + return; + } + update_ota_cloud(argv[1], argv[2]); +#endif +} + +//--------------------------------------------------------------------- + diff --git a/component/common/utilities/update.h b/component/common/utilities/update.h new file mode 100644 index 0000000..64c965a --- /dev/null +++ b/component/common/utilities/update.h @@ -0,0 +1,10 @@ +#ifndef UPDATE_H +#define UPDATE_H + +//-------------------------------------------------------------------------- +int update_ota_local(char *ip, int port); +int update_ota_cloud(char *repository, char *file_path); +void cmd_update(int argc, char **argv); + +//---------------------------------------------------------------------------- +#endif diff --git a/component/common/utilities/webserver.c b/component/common/utilities/webserver.c new file mode 100644 index 0000000..3f44ab2 --- /dev/null +++ b/component/common/utilities/webserver.c @@ -0,0 +1,1197 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/* + Implements a simplistic WEB server. Every time a connection is made and + data is received a dynamic page that shows the current TCP/IP statistics + is generated and returned. The connection is then closed. + + This file was adapted from a FreeRTOS lwIP slip demo supplied by a third + party. +*/ + +/* ------------------------ System includes ------------------------------- */ + + +/* ------------------------ FreeRTOS includes ----------------------------- */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* ------------------------ lwIP includes --------------------------------- */ +#include "lwip/api.h" +#include "lwip/tcpip.h" +#include "lwip/memp.h" +#include "lwip/stats.h" +#include "netif/loopif.h" + +/* ------------------------ Project includes ------------------------------ */ +#include +#include "main.h" + +#include "webserver.h" +#include "wlan_intf.h" + + + +#include "cJSON.h" + + + +#define CONFIG_READ_FLASH 1 + + +#ifdef CONFIG_READ_FLASH + +#ifndef CONFIG_PLATFORM_8195A + +#include +#if defined(STM32F2XX) +#include +#elif defined(STM32F4XX) +#include +#elif defined(STM32f1xx) +#include +#endif + +#else +#include "flash_api.h" +#define DATA_SECTOR (0x000FE000) +#define BACKUP_SECTOR (0x00008000) + +#endif +#endif +/* ------------------------ Defines --------------------------------------- */ +/* The size of the buffer in which the dynamic WEB page is created. */ +#define webMAX_PAGE_SIZE ( 2800 ) /*FSL: buffer containing array*/ +#define LOCAL_BUF_SIZE 800 +#define AP_SETTING_ADDR 0x000FE000; +/* Standard GET response. */ +#define webHTTP_OK "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n" + +/* The port on which we listen. */ +#define webHTTP_PORT ( 80 ) + +/* Delay on close error. */ +#define webSHORT_DELAY ( 10 ) + + +/* Format of the dynamic page that is returned on each connection. */ +#define webHTML_HEAD_START \ +"\ +\ +\ +" +/* +\ +\ +\ +*/ + +#define webHTML_BODY_START \ +"\ +\ +\r\n\r\n
\ +\ +\ +\ +" + +#define webHTML_END \ +"\ +\ +\ +\ +\ +\ +
\ +

Realtek SoftAP Configuration

\ +
\ +
\ +Copyright ?realtek.com
\ +\r\n
" \ +"\r\n" \ +"" + +#define webWaitHTML_START \ +"\ +\ +" +#define webWaitHTML_END \ +"\ +\ +

\ +

SoftAP is now restarting!

\ +

Please wait a moment and reconnect!

\ +

"\ +"\r\n" \ +"" + +#define onChangeSecType \ +"" + +#define onSubmitForm \ +"" + +/* +alert(\"Please enter your password!\");\ +return false;\ +}\ +if(z.value.length < 8)\ +{\ +alert(\"Your password is too short!(8-32)\");\ +return false;\ +}\ +if(z.value.length>32)\ +{\ +alert(\"Your password is too long!(8-32)\");\ +*/ + +#define MAX_SOFTAP_SSID_LEN 32 +#define MAX_PASSWORD_LEN 32 +#define MAX_CHANNEL_NUM 13 + +#if INCLUDE_uxTaskGetStackHighWaterMark + static volatile unsigned portBASE_TYPE uxHighWaterMark_web = 0; +#endif + +/* ------------------------ Prototypes ------------------------------------ */ +static void vProcessConnection( struct netconn *pxNetCon ); + +/*------------------------------------------------------------------------------*/ +/* GLOBALS */ +/*------------------------------------------------------------------------------*/ +rtw_wifi_setting_t wifi_setting = {RTW_MODE_NONE, {0}, 0, RTW_SECURITY_OPEN, {0}}; + +#ifndef WLAN0_NAME + #define WLAN0_NAME "wlan0" +#endif + +#ifndef WLAN1_NAME + #define WLAN1_NAME "wlan1" +#endif + + + +#define STRCMP(a,b) (strcmp(a,b) == 0) + + + +static void LoadWifiSetting() +{ + const char *ifname = WLAN0_NAME; + + if(rltk_wlan_running(WLAN1_IDX)) + {//STA_AP_MODE + ifname = WLAN1_NAME; + } + + wifi_get_setting(ifname, &wifi_setting); + + //printf("\r\nLoadWifiSetting(): wifi_setting.ssid=%s\n", wifi_setting.ssid); + //printf("\r\nLoadWifiSetting(): wifi_setting.channel=%d\n", wifi_setting.channel); + //printf("\r\nLoadWifiSetting(): wifi_setting.security_type=%d\n", wifi_setting.security_type); + //printf("\r\nLoadWifiSetting(): wifi_setting.password=%s\n", wifi_setting.password); +} + +#if CONFIG_READ_FLASH +#ifndef CONFIG_PLATFORM_8195A +void LoadWifiConfig() +{ + rtw_wifi_config_t local_config; + uint32_t address; +#ifdef STM32F10X_XL + address = 0x08080000; //bank2 domain +#else + uint16_t sector_nb = FLASH_Sector_11; + address = flash_SectorAddress(sector_nb); +#endif + printf("\r\nLoadWifiConfig(): Read from FLASH!\n"); + flash_Read(address, (char *)&local_config, sizeof(local_config)); + + printf("\r\nLoadWifiConfig(): local_config.boot_mode=0x%x\n", local_config.boot_mode); + printf("\r\nLoadWifiConfig(): local_config.ssid=%s\n", local_config.ssid); + printf("\r\nLoadWifiConfig(): local_config.channel=%d\n", local_config.channel); + printf("\r\nLoadWifiConfig(): local_config.security_type=%d\n", local_config.security_type); + printf("\r\nLoadWifiConfig(): local_config.password=%s\n", local_config.password); + + if(local_config.boot_mode == 0x77665502) + { + wifi_setting.mode = RTW_MODE_AP; + if(local_config.ssid_len > 32) + local_config.ssid_len = 32; + memcpy(wifi_setting.ssid, local_config.ssid, local_config.ssid_len); + wifi_setting.ssid[local_config.ssid_len] = '\0'; + wifi_setting.channel = local_config.channel; + wifi_setting.security_type = local_config.security_type; + if(local_config.password_len > 32) + local_config.password_len = 32; + memcpy(wifi_setting.password, local_config.password, local_config.password_len); + wifi_setting.password[local_config.password_len] = '\0'; + } + else + { + LoadWifiSetting(); + } +} + +int StoreApInfo() +{ + rtw_wifi_config_t wifi_config; + uint32_t address; +#ifdef STM32F10X_XL + address = 0x08080000; //bank2 domain +#else + uint16_t sector_nb = FLASH_Sector_11; + address = flash_SectorAddress(sector_nb); +#endif + wifi_config.boot_mode = 0x77665502; + memcpy(wifi_config.ssid, wifi_setting.ssid, strlen((char*)wifi_setting.ssid)); + wifi_config.ssid_len = strlen((char*)wifi_setting.ssid); + wifi_config.security_type = wifi_setting.security_type; + memcpy(wifi_config.password, wifi_setting.password, strlen((char*)wifi_setting.password)); + wifi_config.password_len= strlen((char*)wifi_setting.password); + wifi_config.channel = wifi_setting.channel; + + printf("\n\rWritting boot mode 0x77665502 and Wi-Fi setting to flash ..."); +#ifdef STM32F10X_XL + FLASH_ErasePage(address); +#else + flash_EraseSector(sector_nb); +#endif + flash_Wrtie(address, (char *)&wifi_config, sizeof(rtw_wifi_config_t)); + + return 0; +} + + +#else + +void LoadWifiConfig() +{ + + + flash_t flash; + + rtw_wifi_config_t local_config; + uint32_t address; + + address = DATA_SECTOR; + + + //memset(&local_config,0,sizeof(rtw_wifi_config_t)); + printf("\r\nLoadWifiConfig(): Read from FLASH!\n"); + // flash_Read(address, &local_config, sizeof(local_config)); + + flash_stream_read(&flash, address, sizeof(rtw_wifi_config_t),(uint8_t *)(&local_config)); + + + printf("\r\nLoadWifiConfig(): local_config.boot_mode=0x%x\n", local_config.boot_mode); + printf("\r\nLoadWifiConfig(): local_config.ssid=%s\n", local_config.ssid); + printf("\r\nLoadWifiConfig(): local_config.channel=%d\n", local_config.channel); + printf("\r\nLoadWifiConfig(): local_config.security_type=%d\n", local_config.security_type); + printf("\r\nLoadWifiConfig(): local_config.password=%s\n", local_config.password); + + if(local_config.boot_mode == 0x77665502) + { + wifi_setting.mode = RTW_MODE_AP; + if(local_config.ssid_len > 32) + local_config.ssid_len = 32; + memcpy(wifi_setting.ssid, local_config.ssid, local_config.ssid_len); + wifi_setting.ssid[local_config.ssid_len] = '\0'; + wifi_setting.channel = local_config.channel; + if(local_config.security_type == 1) + wifi_setting.security_type = RTW_SECURITY_WPA2_AES_PSK; + else + wifi_setting.security_type = RTW_SECURITY_OPEN; + if(local_config.password_len > 32) + local_config.password_len = 32; + memcpy(wifi_setting.password, local_config.password, local_config.password_len); + wifi_setting.password[local_config.password_len] = '\0'; + } + else + { + LoadWifiSetting(); + } + +} + +int StoreApInfo() +{ + + flash_t flash; + + rtw_wifi_config_t wifi_config; + uint32_t address; + uint32_t data,i = 0; + + + address = DATA_SECTOR; + + wifi_config.boot_mode = 0x77665502; + memcpy(wifi_config.ssid, wifi_setting.ssid, strlen((char*)wifi_setting.ssid)); + wifi_config.ssid_len = strlen((char*)wifi_setting.ssid); + wifi_config.security_type = wifi_setting.security_type; + if(wifi_setting.security_type !=0) + wifi_config.security_type = 1; + else + wifi_config.security_type = 0; + memcpy(wifi_config.password, wifi_setting.password, strlen((char*)wifi_setting.password)); + wifi_config.password_len= strlen((char*)wifi_setting.password); + wifi_config.channel = wifi_setting.channel; + printf("\n\rWritting boot mode 0x77665502 and Wi-Fi setting to flash ..."); + //printf("\n\r &wifi_config = 0x%x",&wifi_config); + + flash_read_word(&flash,address,&data); + + + if(data == ~0x0) + + flash_stream_write(&flash, address,sizeof(rtw_wifi_config_t), (uint8_t *)&wifi_config); + + else{ + //flash_EraseSector(sector_nb); + + + flash_erase_sector(&flash,BACKUP_SECTOR); + for(i = 0; i < 0x1000; i+= 4){ + flash_read_word(&flash, DATA_SECTOR + i, &data); + if(i < sizeof(rtw_wifi_config_t)) + { + memcpy(&data,(char *)(&wifi_config) + i,4); + //printf("\n\r Wifi_config + %d = 0x%x",i,(void *)(&wifi_config + i)); + //printf("\n\r Data = %d",data); + } + flash_write_word(&flash, BACKUP_SECTOR + i,data); + } + flash_read_word(&flash,BACKUP_SECTOR + 68,&data); + //printf("\n\r Base + BACKUP_SECTOR + 68 wifi channel = %d",data); + //erase system data + flash_erase_sector(&flash, DATA_SECTOR); + //write data back to system data + for(i = 0; i < 0x1000; i+= 4){ + flash_read_word(&flash, BACKUP_SECTOR + i, &data); + flash_write_word(&flash, DATA_SECTOR + i,data); + } + //erase backup sector + flash_erase_sector(&flash, BACKUP_SECTOR); + } + + //flash_Wrtie(address, (char *)&wifi_config, sizeof(rtw_wifi_config_t)); + //flash_stream_write(&flash, address,sizeof(rtw_wifi_config_t), (uint8_t *)&wifi_config); + //flash_stream_read(&flash, address, sizeof(rtw_wifi_config_t),data); + //flash_stream_read(&flash, address, sizeof(rtw_wifi_config_t),data); + //printf("\n\r Base + 0x000FF000 +4 wifi config = %s",data[4]); + //printf("\n\r Base + 0x000FF000 +71 wifi channel = %d",data[71]); + + return 0; +} +#endif + + +static void RestartSoftAP() +{ + //printf("\r\nRestartAP: ssid=%s", wifi_setting.ssid); + //printf("\r\nRestartAP: ssid_len=%d", strlen((char*)wifi_setting.ssid)); + //printf("\r\nRestartAP: security_type=%d", wifi_setting.security_type); + //printf("\r\nRestartAP: password=%s", wifi_setting.password); + //printf("\r\nRestartAP: password_len=%d", strlen((char*)wifi_setting.password)); + //printf("\r\nRestartAP: channel=%d\n", wifi_setting.channel); + wifi_restart_ap(wifi_setting.ssid, + wifi_setting.security_type, + wifi_setting.password, + strlen((char*)wifi_setting.ssid), + strlen((char*)wifi_setting.password), + wifi_setting.channel); +} +#endif + + +u32 web_atoi(char* s) +{ + int num=0,flag=0; + int i; + + for(i=0;i<=strlen(s);i++) + { + if(s[i] >= '0' && s[i] <= '9') + num = num * 10 + s[i] -'0'; + else if(s[0] == '-' && i==0) + flag =1; + else + break; + } + + if(flag == 1) + num = num * -1; + + return(num); +} + +static void CreateSsidTableItem(char *pbuf, u8_t *ssid, u8_t ssid_len) +{ + char local_ssid[MAX_SOFTAP_SSID_LEN+1]; + + if(ssid_len > MAX_SOFTAP_SSID_LEN) + ssid_len = MAX_SOFTAP_SSID_LEN; + memcpy(local_ssid, ssid, ssid_len); + local_ssid[ssid_len] = '\0'; + sprintf(pbuf, "" + "" + "SoftAP SSID:
" + "" + "" + "
" + "" + "", + local_ssid); + //printf("\r\nstrlen(SsidTableItem)=%d\n", strlen(pbuf)); +} + +static void CreateSecTypeTableItem(char *pbuf, u32_t sectype) +{ + u8_t flag[2] = {0, 0}; + + if(sectype == RTW_SECURITY_OPEN) + flag[0] = 1; + else if(sectype == RTW_SECURITY_WPA2_AES_PSK) + flag[1] = 1; + else + return; + + sprintf(pbuf, "" + "" + "Security Type:
" + "" + "" + "" + "" + "", + flag[0]?"selected":"", + flag[1]?"selected":""); + //printf("\r\nstrlen(SecTypeTableItem)=%d\n", strlen(pbuf)); +} + +static void CreatePasswdTableItem(char *pbuf, u8_t *password, u8_t passwd_len) +{ + char local_passwd[MAX_PASSWORD_LEN+1]; + + if(passwd_len > MAX_PASSWORD_LEN) + passwd_len = MAX_PASSWORD_LEN; + if(passwd_len > 0) + { + memcpy(local_passwd, password, passwd_len); + local_passwd[passwd_len] = '\0'; + } + sprintf(pbuf, "" + "" + "Password:
" + "" + "" + "
" + "" + "", + passwd_len?local_passwd:""); + //printf("\r\nstrlen(passwordTableItem)=%d\n", strlen(pbuf)); +} + +static void CreateChannelTableItem(char *pbuf, u8_t channel) +{ + u8_t flag[MAX_CHANNEL_NUM+1] = {0}; + + if(channel > MAX_CHANNEL_NUM){ + printf("Channel(%d) is out of range!\n", channel); + channel = 1; + } + flag[channel] = 1; + + sprintf(pbuf, "" + "" + "Channel:
" + "" + "" + "" + "" + "", + flag[1]?"selected":"", + flag[2]?"selected":"", + flag[3]?"selected":"", + flag[4]?"selected":"", + flag[5]?"selected":"", + flag[6]?"selected":"", + flag[7]?"selected":"", + flag[8]?"selected":"", + flag[9]?"selected":"", + flag[10]?"selected":"", + flag[11]?"selected":""); + //printf("\r\nstrlen(ChannelTableItem)=%d\n", strlen(pbuf)); +} + +static void GenerateIndexHtmlPage(portCHAR* cDynamicPage, portCHAR *LocalBuf) +{ + /* Generate the page index.html... + ... First the page header. */ + strcpy( cDynamicPage, webHTML_HEAD_START ); + + /* Add script */ + strcat( cDynamicPage, onChangeSecType ); + strcat( cDynamicPage, onSubmitForm); + + /* Add Body start */ + strcat( cDynamicPage, webHTML_BODY_START ); + + /* Add SSID */ + CreateSsidTableItem(LocalBuf, wifi_setting.ssid, strlen((char*)wifi_setting.ssid)); + strcat( cDynamicPage, LocalBuf ); + + /* Add SECURITY TYPE */ + CreateSecTypeTableItem(LocalBuf, wifi_setting.security_type); + strcat( cDynamicPage, LocalBuf ); + + /* Add PASSWORD */ + CreatePasswdTableItem(LocalBuf, wifi_setting.password, strlen((char*)wifi_setting.password)); + strcat( cDynamicPage, LocalBuf ); + + /* Add CHANNEL */ + CreateChannelTableItem(LocalBuf, wifi_setting.channel); + strcat( cDynamicPage, LocalBuf ); + + /* ... Finally the page footer. */ + strcat( cDynamicPage, webHTML_END ); + //printf("\r\nGenerateIndexHtmlPage(): %s\n", cDynamicPage); + printf("\r\nGenerateIndexHtmlPage Len: %d\n", strlen( cDynamicPage )); +} + +static void GenerateWaitHtmlPage(portCHAR* cDynamicPage) +{ + /* Generate the dynamic page... + ... First the page header. */ + strcpy( cDynamicPage, webWaitHTML_START ); + + /* ... Finally the page footer. */ + strcat( cDynamicPage, webWaitHTML_END); + + //printf("\r\nGenerateWaitHtmlPage(): %s\n", cDynamicPage); + //printf("\r\nGenerateWaitHtmlPage Len: %d\n", strlen( cDynamicPage )); +} + +static u8_t ProcessPostMessage(struct netbuf *pxRxBuffer, portCHAR *LocalBuf) +{ + struct pbuf *p; + portCHAR *pcRxString, *ptr; + unsigned portSHORT usLength; + u8_t bChanged = 0; + rtw_security_t secType; + u8_t channel; + u8_t len = 0; + + pcRxString = LocalBuf; + p = pxRxBuffer->p; + usLength = p->tot_len; + //printf("\r\n !!!!!!!!!POST!p->tot_len =%d p->len=%d\n", p->tot_len, p->len); + while(p) + { + memcpy(pcRxString, p->payload, p->len); + pcRxString += p->len; + p = p->next; + } + pcRxString = LocalBuf; + pcRxString[usLength] = '\0'; + //printf("\r\n usLength=%d pcRxString = %s\n", usLength, pcRxString); + + ptr = (char*)strstr(pcRxString, "Ssid="); + if(ptr) + { + pcRxString = (char*)strstr(ptr, "&"); + *pcRxString++ = '\0'; + ptr += 5; + if(strcmp((char*)wifi_setting.ssid, ptr)) + { + bChanged = 1; + len = strlen(ptr); + if(len > MAX_SOFTAP_SSID_LEN){ + len = MAX_SOFTAP_SSID_LEN; + ptr[len] = '\0'; + } + strcpy((char*)wifi_setting.ssid, ptr); + } + } + + //printf("\r\n wifi_config.ssid = %s\n", wifi_setting.ssid); + ptr = (char*)strstr(pcRxString, "Security+Type="); + if(ptr) + { + pcRxString = (char*)strstr(ptr, "&"); + *pcRxString++ = '\0'; + ptr += 14; + if(!strcmp(ptr, "open")) + secType = RTW_SECURITY_OPEN; + else if(!strcmp(ptr, "wpa2-aes")) + secType = RTW_SECURITY_WPA2_AES_PSK; + else + secType = RTW_SECURITY_OPEN; + if(wifi_setting.security_type != secType) + { + bChanged = 1; + wifi_setting.security_type = secType; + } + } + + //printf("\r\n wifi_config.security_type = %d\n", wifi_setting.security_type); + if(wifi_setting.security_type > RTW_SECURITY_OPEN) + { + ptr = (char*)strstr(pcRxString, "Password="); + if(ptr) + { + pcRxString = (char*)strstr(ptr, "&"); + *pcRxString++ = '\0'; + ptr += 9; + if(strcmp((char*)wifi_setting.password, ptr)) + { + bChanged = 1; + len = strlen(ptr); + if(len > MAX_PASSWORD_LEN){ + len = MAX_PASSWORD_LEN; + ptr[len] = '\0'; + } + strcpy((char*)wifi_setting.password, ptr); + } + } + //printf("\r\n wifi_config.password = %s\n", wifi_setting.password); + } + ptr = (char*)strstr(pcRxString, "Channel="); + if(ptr) + { + ptr += 8; + channel = web_atoi(ptr); + if((channel>MAX_CHANNEL_NUM)||(channel < 1)) + channel = 1; + if(wifi_setting.channel !=channel) + { + bChanged = 1; + wifi_setting.channel = channel; + } + } + //printf("\r\n wifi_config.channel = %d\n", wifi_setting.channel); + + return bChanged; +} + +struct netconn *pxHTTPListener = NULL; +static void vProcessConnection( struct netconn *pxNetCon ) +{ + static portCHAR cDynamicPage[webMAX_PAGE_SIZE]; + struct netbuf *pxRxBuffer; + portCHAR *pcRxString; + unsigned portSHORT usLength; + static portCHAR LocalBuf[LOCAL_BUF_SIZE]; + u8_t bChanged = 0; + int ret_recv = ERR_OK; + int ret_accept = ERR_OK; + + /* Load WiFi Setting*/ + LoadWifiSetting(); + + /* We expect to immediately get data. */ + // Evan mopdified for adapt two version lwip api diff + port_netconn_recv( pxNetCon , pxRxBuffer, ret_recv); + + if( pxRxBuffer != NULL && ret_recv == ERR_OK) + { + /* Where is the data? */ + netbuf_data( pxRxBuffer, ( void * )&pcRxString, &usLength ); + + /* Is this a GET? We don't handle anything else. */ + if( !strncmp( pcRxString, "GET", 3 ) ) + { + //printf("\r\nusLength=%d pcRxString=%s \n", usLength, pcRxString); + pcRxString = cDynamicPage; + + /* Write out the HTTP OK header. */ + netconn_write( pxNetCon, webHTTP_OK, ( u16_t ) strlen( webHTTP_OK ), NETCONN_COPY ); + + /* Generate index.html page. */ + GenerateIndexHtmlPage(cDynamicPage, LocalBuf); + + /* Write out the dynamically generated page. */ + netconn_write( pxNetCon, cDynamicPage, ( u16_t ) strlen( cDynamicPage ), NETCONN_COPY ); + } + else if(!strncmp( pcRxString, "POST", 4 ) ) + { + /* Write out the HTTP OK header. */ + netconn_write( pxNetCon, webHTTP_OK, ( u16_t ) strlen( webHTTP_OK ), NETCONN_COPY ); + bChanged = ProcessPostMessage(pxRxBuffer, LocalBuf); + if(bChanged) + { + GenerateWaitHtmlPage(cDynamicPage); + + /* Write out the generated page. */ + netconn_write( pxNetCon, cDynamicPage, ( u16_t ) strlen( cDynamicPage ), NETCONN_COPY ); + +#if CONFIG_READ_FLASH + StoreApInfo(); +#endif + } + else + { + /* Generate index.html page. */ + GenerateIndexHtmlPage(cDynamicPage, LocalBuf); + + /* Write out the generated page. */ + netconn_write( pxNetCon, cDynamicPage, ( u16_t ) strlen( cDynamicPage ), NETCONN_COPY ); + } + } + netbuf_delete( pxRxBuffer ); + } + netconn_close( pxNetCon ); + + if(bChanged) + { + struct netconn *pxNewConnection; + vTaskDelay(200/portTICK_RATE_MS); + //printf("\r\n%d:before restart ap\n", xTaskGetTickCount()); + RestartSoftAP(); + //printf("\r\n%d:after restart ap\n", xTaskGetTickCount()); + pxHTTPListener->recv_timeout = 1; + // Evan mopdified for adapt two version lwip api diff + port_netconn_accept( pxHTTPListener , pxNewConnection, ret_accept); + if( pxNewConnection != NULL && ret_accept == ERR_OK) + { + //printf("\r\n%d: got a conn\n", xTaskGetTickCount()); + netconn_close( pxNewConnection ); + while( netconn_delete( pxNewConnection ) != ERR_OK ) + { + vTaskDelay( webSHORT_DELAY ); + } + } + //printf("\r\n%d:end\n", xTaskGetTickCount()); + pxHTTPListener->recv_timeout = 0; + } +} + + + + + + +/*************************************************************** +** PROSCEND implement +***************************************************************/ + + +#define HTTP_OK_JSON "HTTP/1.0 200 OK\r\nContent-type: application/json\r\n\r\n" +#define STRNCMP(a,b,c) (strncmp(a,b,c) == 0) + + + + + + +cJSON_Hooks memoryHook; + + + + + + +void rgb_mode_handle_off() { + + Ai_WS2811_setColor(0, 0, 0); + sendLEDs(); + +} + + + + +void rgb_mode_handle_on(int r, int g, int b) { + + Ai_WS2811_setColor(r, g, b); + sendLEDs(); + +} + + + + +static void _handle_post_rgb_json(char *j) { + + extern u16 Blink_ms; + extern u8 Config_r, Config_g, Config_b; + extern u8 IsBlink; + extern u8 IsMeteor; + + cJSON *obj = cJSON_Parse(j); + if (obj) { + cJSON *R = cJSON_GetObjectItem(obj, "R"); + cJSON *G = cJSON_GetObjectItem(obj, "G"); + cJSON *B = cJSON_GetObjectItem(obj, "B"); + + cJSON *Mode = cJSON_GetObjectItem(obj, "Mode"); + cJSON *Speed = cJSON_GetObjectItem(obj, "Speed"); + + printf("R: %d\n", R->valueint); + printf("G: %d\n", G->valueint); + printf("B: %d\n", B->valueint); + + printf("Mode: %s\n", Mode->valuestring); + printf("Speed: %d\n", Speed->valueint); + + Config_r = R->valueint; + Config_g = G->valueint; + Config_b = B->valueint; + + if (Speed->valueint < 10) { + Blink_ms = 10; + } else { + Blink_ms = Speed->valueint; + } + + if (STRCMP(Mode->valuestring, "OFF")) { + printf("rgb_mode_handle_off\n"); + rgb_mode_handle_off(); + } + + if (STRCMP(Mode->valuestring, "ON")) { + printf("rgb_mode_handle_on\n"); + rgb_mode_handle_on(R->valueint, G->valueint, B->valueint); + } + + if (STRCMP(Mode->valuestring, "Blink")) { + printf("rgb_mode_handle_blink\n"); + IsBlink = 1; + } else { + IsBlink = 0; + } + + if (STRCMP(Mode->valuestring, "Meteor")) { + printf("rgb_mode_handle_meteor\n"); + IsMeteor = 1; + } else { + IsMeteor = 0; + } + + + + // wait for implementing + + + + cJSON_Delete(obj); + } +} + + + +void _handle_post_rgb(struct netbuf *netbuf, char *localbuf) { + struct pbuf *p; + portCHAR *pcRxString, *ptr; + unsigned portSHORT usLength; + u8_t bChanged = 0; + rtw_security_t secType; + u8_t channel; + u8_t len = 0; + + pcRxString = localbuf; + p = netbuf->p; + usLength = p->tot_len; + + while (p) { + memcpy(pcRxString, p->payload, p->len); + pcRxString += p->len; + p = p->next; + } + pcRxString = localbuf; + pcRxString[usLength] = '\0'; + + ptr = (char*)strstr(pcRxString, "\r\n\r\n"); + if (ptr) { + ptr += 4; + _handle_post_rgb_json(ptr); + } + +} + + +/* +** set the configuration about the smart lighting +** POST /rgb/ HTTP/1.1 +** +** { + "R" : 0, + "G" : 255, + "B" : 27, + "Speed" : 50, + "Mode" : "ON" + } +** +** HTTP/1.1 200 OK +** +** +*/ +static void _handle_post(struct netconn *netconn, const char *data, struct netbuf *netbuf) { + + static char buf[LOCAL_BUF_SIZE]; + + netconn_write(netconn, HTTP_OK_JSON, strlen(HTTP_OK_JSON), NETCONN_COPY); + if (strstr(data, "/rgb")) { + printf("POST /rgb\n"); + _handle_post_rgb(netbuf, buf); + } else { + printf("POST Unknown\n"); + printf("pcRxString: %s\n", data); + } + +} + + + +void restful_api_handler(struct netconn *netconn) { + struct netbuf *netbuf; + char *data; + u16_t len; + int ret = ERR_OK; + + port_netconn_recv(netconn, netbuf, ret); + + if (netbuf != NULL && ret == ERR_OK) { + netbuf_data(netbuf, (void *)&data, &len); + if (STRNCMP(data, "POST", 4)) _handle_post(netconn, data, netbuf); + netbuf_delete(netbuf); + } + + netconn_close(netconn); +} + + + +static void _cjson_init(void) { + memoryHook.malloc_fn = malloc; + memoryHook.free_fn = free; + cJSON_InitHooks(&memoryHook); +} + + + + + + +/*------------------------------------------------------------*/ +xTaskHandle webs_task = NULL; +xSemaphoreHandle webs_sema = NULL; +u8_t webs_terminate = 0; +void vBasicWEBServer( void *pvParameters ) +{ + struct netconn *pxNewConnection; + //struct ip_addr xIpAddr, xNetMast, xGateway; + extern err_t ethernetif_init( struct netif *netif ); + int ret = ERR_OK; + /* Parameters are not used - suppress compiler error. */ + ( void )pvParameters; + + /* Create a new tcp connection handle */ + pxHTTPListener = netconn_new( NETCONN_TCP ); + netconn_bind( pxHTTPListener, NULL, webHTTP_PORT ); + netconn_listen( pxHTTPListener ); + +#if CONFIG_READ_FLASH + /* Load wifi_config */ + LoadWifiConfig(); + RestartSoftAP(); +#endif + //printf("\r\n-0\n"); + + + + /* init once */ + _cjson_init(); + + + + /* Loop forever */ + for( ;; ) + { + if(webs_terminate) + break; + + //printf("\r\n%d:-1\n", xTaskGetTickCount()); + /* Wait for connection. */ + // Evan mopdified for adapt two version lwip api diff + port_netconn_accept( pxHTTPListener , pxNewConnection, ret); + //printf("\r\n%d:-2\n", xTaskGetTickCount()); + + if( pxNewConnection != NULL && ret == ERR_OK) + { + /* Service connection. */ + + #if 0 + vProcessConnection( pxNewConnection ); + #else // RESTful + restful_api_handler(pxNewConnection); + #endif + + while( netconn_delete( pxNewConnection ) != ERR_OK ) + { + vTaskDelay( webSHORT_DELAY ); + } + } + //printf("\r\n%d:-3\n", xTaskGetTickCount()); + } + //printf("\r\n-4\n"); + if(pxHTTPListener) + { + netconn_close(pxHTTPListener); + netconn_delete(pxHTTPListener); + pxHTTPListener = NULL; + } + + //printf("\r\nExit Web Server Thread!\n"); + xSemaphoreGive(webs_sema); +} + +#define STACKSIZE 512 +void start_web_server() +{ + printf("\r\nWEB:Enter start web server!\n"); + webs_terminate = 0; + if(webs_task == NULL) + { + if(xTaskCreate(vBasicWEBServer, (const char *)"web_server", STACKSIZE, NULL, tskIDLE_PRIORITY + 1, &webs_task) != pdPASS) + printf("\n\rWEB: Create webserver task failed!\n"); + } + if(webs_sema == NULL) + { + webs_sema = xSemaphoreCreateCounting(0xffffffff, 0); //Set max count 0xffffffff + } + //printf("\r\nWEB:Exit start web server!\n"); +} + +void stop_web_server() +{ + //printf("\r\nWEB:Enter stop web server!\n"); + webs_terminate = 1; + if(pxHTTPListener) + netconn_abort(pxHTTPListener); + if(webs_sema) + { + if(xSemaphoreTake(webs_sema, 15 * configTICK_RATE_HZ) != pdTRUE) + { + if(pxHTTPListener) + { + netconn_close(pxHTTPListener); + netconn_delete(pxHTTPListener); + pxHTTPListener = NULL; + } + printf("\r\nWEB: Take webs sema(%p) failed!!!!!!!!!!!\n", webs_sema); + } + vSemaphoreDelete(webs_sema); + webs_sema = NULL; + } + if(webs_task) + { + vTaskDelete(webs_task); + webs_task = NULL; + } + printf("\r\nWEB:Exit stop web server!\n"); +} diff --git a/component/common/utilities/webserver.h b/component/common/utilities/webserver.h new file mode 100644 index 0000000..46b1cce --- /dev/null +++ b/component/common/utilities/webserver.h @@ -0,0 +1,71 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef BASIC_WEB_SERVER_H +#define BASIC_WEB_SERVER_H +#include +/*------------------------------------------------------------------------------*/ +/* MACROS */ +/*------------------------------------------------------------------------------*/ +#define basicwebWEBSERVER_PRIORITY ( tskIDLE_PRIORITY + 2 ) + +#define lwipBASIC_SERVER_STACK_SIZE 256 + +/*------------------------------------------------------------------------------*/ + +/* The function that implements the WEB server task. */ +extern void start_web_server(void); + +#endif /* + */ + diff --git a/component/os/freertos/cmsis_os.c b/component/os/freertos/cmsis_os.c new file mode 100644 index 0000000..5ceea59 --- /dev/null +++ b/component/os/freertos/cmsis_os.c @@ -0,0 +1,1394 @@ + + +#include "cmsis_os.h" +#include "diag.h" + +#define CMSIS_OS_ERR(fmt, args...) DBG_8195A("\n\r%s: " fmt, __FUNCTION__, ## args) + +extern void *_memset( void *s, int c, SIZE_T n ); +#define os_memset _memset + +#if configSignalManagementSupport // the older FreeRTOS version didn't support Signal Management functions +#if 0 +#define THREAD_SIGNAL_MAP_SIZE 32 +typedef struct thread_signal_map { + uint8_t is_in_use; + osThreadId thread_id; + EventGroupHandle_t signals; +} ThreadSignalEntity; + + +ThreadSignalEntity ThreadSignalMapTable[THREAD_SIGNAL_MAP_SIZE]={0}; +#endif + +typedef struct thread_signal_map { + osThreadId thread_id; + EventGroupHandle_t signals; + void *pnext; +} ThreadSignalRec, *pThreadSignalRec; + +ThreadSignalRec *pThreadSignalMapHead; +ThreadSignalRec *pThreadSignalMapTail; +#endif + +/* Convert from CMSIS type osPriority to FreeRTOS priority number */ +static unsigned portBASE_TYPE makeFreeRtosPriority (osPriority priority) +{ + unsigned portBASE_TYPE fpriority = tskIDLE_PRIORITY; + + if (priority != osPriorityError) { + fpriority += (priority - osPriorityIdle); + } + + return fpriority; +} + + +/* Convert from FreeRTOS priority number to CMSIS type osPriority */ +static osPriority makeCmsisPriority (unsigned portBASE_TYPE fpriority) +{ + osPriority priority = osPriorityError; + + if ((fpriority - tskIDLE_PRIORITY) <= (osPriorityRealtime - osPriorityIdle)) { + priority = (osPriority)((int)osPriorityIdle + (int)(fpriority - tskIDLE_PRIORITY)); + } + + return priority; +} + + +/* Determine whether we are in thread mode or handler mode. */ +static int inHandlerMode (void) +{ + return __get_IPSR() != 0; +} + +#if configSignalManagementSupport // the older FreeRTOS version didn't support Signal Management functions +static void add_thread_signal_map (osThreadId thread_id, EventGroupHandle_t signals) +{ + int dummy; +// uint32_t i; + ThreadSignalRec *prec_entity; + + if (inHandlerMode()) { + dummy = portSET_INTERRUPT_MASK_FROM_ISR(); + } + else { + vPortEnterCritical(); + } + + prec_entity = (ThreadSignalRec*) malloc(sizeof(ThreadSignalRec)); + + if (prec_entity != NULL) { + prec_entity->thread_id = thread_id; + prec_entity->signals = signals; + prec_entity->pnext = NULL; + if (pThreadSignalMapHead == NULL) { + pThreadSignalMapHead = prec_entity; + pThreadSignalMapTail = prec_entity; + } + else { + pThreadSignalMapTail->pnext = prec_entity; + pThreadSignalMapTail = prec_entity; + } + } + else { + CMSIS_OS_ERR("No Free Thread-Signal entity\n"); + } + +#if 0 + for (i=0;i= THREAD_SIGNAL_MAP_SIZE) { + // No free Thread-Signals map entity + CMSIS_OS_ERR("No Free Thread-Signal entity\n"); + } + +#endif + + if (inHandlerMode()) { + portCLEAR_INTERRUPT_MASK_FROM_ISR(dummy); + } + else { + vPortExitCritical(); + } + +} + +static EventGroupHandle_t find_signal_by_thread (osThreadId thread_id) +{ + EventGroupHandle_t signals_hdl=NULL; +// uint32_t i; + int dummy; + ThreadSignalRec *prec_entity; + + if (inHandlerMode()) { + dummy = portSET_INTERRUPT_MASK_FROM_ISR(); + } + else { + vPortEnterCritical(); + } + + prec_entity = pThreadSignalMapHead; + while (prec_entity != NULL) { + if (prec_entity->thread_id == thread_id) { + signals_hdl = prec_entity->signals; + break; + } + else { + prec_entity = prec_entity->pnext; + } + } + +#if 0 + for (i=0;ithread_id == thread_id) { + signals_hdl = prec_entity->signals; + if (prec_entity == pThreadSignalMapHead) { + if (prec_entity->pnext != NULL) { + pThreadSignalMapHead = prec_entity->pnext; + } + else { + pThreadSignalMapHead = NULL; + pThreadSignalMapTail = NULL; + } + } + else if (prec_entity == pThreadSignalMapTail) { + pprev_entity->pnext = NULL; + pThreadSignalMapTail = pprev_entity; + } + else { + pprev_entity->pnext = prec_entity->pnext; + } + free((void *)prec_entity); + break; + } + else { + pprev_entity = prec_entity; + prec_entity = prec_entity->pnext; + } + } + +#if 0 + for (i=0;ipthread, + (const portCHAR *)thread_def->name, + (thread_def->stacksize/4), + argument, + makeFreeRtosPriority(thread_def->tpriority), + &handle); + if (pdPASS == result) { +#if configSignalManagementSupport + EventGroupHandle_t signals; + + signals = xEventGroupCreate(); + if (signals == NULL) { + /* The event group was not created because there was insufficient + FreeRTOS heap available. */ + CMSIS_OS_ERR("Create a EventGroup for a new thread failed\n"); + } + else + { + add_thread_signal_map(handle, signals); + } +#endif + } + else + { + CMSIS_OS_ERR("Create a new thread(%s) failed\r\n", thread_def->name); + } + + return handle; +} + + +/// Return the thread ID of the current running thread. +/// \return thread ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetId shall be consistent in every CMSIS-RTOS. +osThreadId osThreadGetId (void) +{ + return xTaskGetCurrentTaskHandle(); +} + + +/// Terminate execution of a thread and remove it from Active Threads. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadTerminate shall be consistent in every CMSIS-RTOS. +osStatus osThreadTerminate (osThreadId thread_id) +{ +#if configSignalManagementSupport + EventGroupHandle_t EventHandle; + + EventHandle = remove_thread_signal_map (thread_id); + if (EventHandle) { + vEventGroupDelete (EventHandle); + } +#endif + + vTaskDelete(thread_id); + + return osOK; +} + + +/// Pass control to next thread that is in state \b READY. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadYield shall be consistent in every CMSIS-RTOS. +osStatus osThreadYield (void) +{ + taskYIELD(); + + return osOK; +} + + +/// Change priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] priority new priority value for the thread function. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadSetPriority shall be consistent in every CMSIS-RTOS. +osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority) +{ + vTaskPrioritySet(thread_id, makeFreeRtosPriority(priority)); + + return osOK; +} + + +/// Get current priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return current priority value of the thread function. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetPriority shall be consistent in every CMSIS-RTOS. +osPriority osThreadGetPriority (osThreadId thread_id) +{ + return makeCmsisPriority(uxTaskPriorityGet(thread_id)); +} + + + +// ==== Generic Wait Functions ==== + +/// Wait for Timeout (Time Delay) +/// \param[in] millisec time delay value +/// \return status code that indicates the execution status of the function. +osStatus osDelay (uint32_t millisec) +{ +#if INCLUDE_vTaskDelay + portTickType ticks = (millisec * configTICK_RATE_HZ) / 1000; + + vTaskDelay(ticks ? ticks : 1); /* Minimum delay = 1 tick */ + + return osOK; +#else + (void) millisec; + + return osErrorResource; +#endif +} + + +#if (defined (osFeature_Wait) && (osFeature_Wait != 0)) // Generic Wait available + +/// Wait for Signal, Message, Mail, or Timeout +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains signal, message, or mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osWait shall be consistent in every CMSIS-RTOS. +osEvent osWait (uint32_t millisec); + +#endif // Generic Wait available + + +// ==== Timer Management Functions ==== + +static void _osTimerCallbackFreeRTOS (xTimerHandle handle) +{ + osTimerDef_t *timer = (osTimerDef_t *)(pvTimerGetTimerID(handle)); + + timer->ptimer(timer->custom->argument); +} + + +/// Create a timer. +/// \param[in] timer_def timer object referenced with \ref osTimer. +/// \param[in] type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. +/// \param[in] argument argument to the timer call back function. +/// \return timer ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osTimerCreate shall be consistent in every CMSIS-RTOS. +osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument) +{ + timer_def->custom->argument = argument; + + return xTimerCreate((const portCHAR *)"", + 1, //Set later when timer is started + (type == osTimerPeriodic) ? pdTRUE : pdFALSE, + (void *)timer_def, + _osTimerCallbackFreeRTOS + ); +} + + + +/// Start or restart a timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \param[in] millisec time delay value of the timer. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStart shall be consistent in every CMSIS-RTOS. +osStatus osTimerStart (osTimerId timer_id, uint32_t millisec) +{ + portBASE_TYPE taskWoken = pdFALSE; + osStatus result = osOK; + portTickType ticks = millisec / portTICK_RATE_MS; + if (ticks == 0) { + ticks = 1; + } + + if (inHandlerMode()) { + if (xTimerChangePeriodFromISR(timer_id, ticks, &taskWoken) == pdPASS) { + xTimerStartFromISR(timer_id, &taskWoken); + portEND_SWITCHING_ISR(taskWoken); + } + } + else { + //TODO: add timeout support + if (xTimerChangePeriod(timer_id, ticks, 0) != pdPASS) { + result = osErrorOS; + } + else { + if (xTimerStart(timer_id, 0) != pdPASS) { + result = osErrorOS; + } + } + } + + return result; +} + + + +/// Stop the timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStop shall be consistent in every CMSIS-RTOS. +osStatus osTimerStop (osTimerId timer_id) +{ + portBASE_TYPE taskWoken = pdFALSE; + osStatus result = osOK; + + if (inHandlerMode()) { + xTimerStopFromISR(timer_id, &taskWoken); + portEND_SWITCHING_ISR(taskWoken); + } + else { + if (xTimerStop(timer_id, 0) != pdPASS) { //TODO: add timeout support + result = osErrorOS; + } + } + + return result; +} + + +/// Delete a timer that was created by \ref osTimerCreate. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerDelete shall be consistent in every CMSIS-RTOS. +osStatus osTimerDelete (osTimerId timer_id) +{ + osStatus result = osOK; + + /* try to delete the soft timer and wait max RTL_TIMER_API_MAX_BLOCK_TICKS + to send the delete command to the timer command queue */ + if (xTimerDelete(timer_id, portMAX_DELAY ) != pdPASS) { + result = osErrorOS; + } + + return result; +} + +// ==== Signal Management ==== +#if configSignalManagementSupport +/// Set the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that should be set. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalSet shall be consistent in every CMSIS-RTOS. +int32_t osSignalSet (osThreadId thread_id, int32_t signals) +{ + EventGroupHandle_t event_handle; + portBASE_TYPE taskWoken = pdFALSE; + portBASE_TYPE xResult; + EventBits_t uxBits_ret=0x80000000; +#ifdef CHECK_VALUE_OF_EVENT_GROUP + EventBits_t uxBits; +#endif + + if (signals & (0xFFFFFFFF << osFeature_Signals)) { + return 0x80000000; + } + + event_handle = find_signal_by_thread(thread_id); + if (event_handle) { + if (inHandlerMode()) { + uxBits_ret = xEventGroupGetBitsFromISR(event_handle); + xResult = xEventGroupSetBitsFromISR( + event_handle, /* The event group being updated. */ + signals, /* The bits being set. */ + &taskWoken ); + if( xResult != pdFAIL ) + { + portYIELD_FROM_ISR(taskWoken); + } + } + else { + uxBits_ret = xEventGroupGetBits(event_handle); +#ifdef CHECK_VALUE_OF_EVENT_GROUP + uxBits = +#endif + xEventGroupSetBits( + event_handle, /* The event group being updated. */ + signals );/* The bits being set. */ + } + } + + return uxBits_ret; +} + +/// Clear the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that shall be cleared. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalClear shall be consistent in every CMSIS-RTOS. +int32_t osSignalClear (osThreadId thread_id, int32_t signals) +{ + EventGroupHandle_t event_handle; + //portBASE_TYPE taskWoken = pdFALSE; + EventBits_t uxBits_ret=0x80000000; +#ifdef CHECK_VALUE_OF_EVENT_GROUP + EventBits_t uxBits; +#endif + + if (signals & (0xFFFFFFFF << osFeature_Signals)) { + return 0x80000000; + } + + event_handle = find_signal_by_thread(thread_id); + if (event_handle) { + if (inHandlerMode()) { + uxBits_ret = xEventGroupGetBitsFromISR(event_handle); +#ifdef CHECK_VALUE_OF_EVENT_GROUP + uxBits = +#endif + xEventGroupClearBitsFromISR( + event_handle, /* The event group being updated. */ + signals);/* The bits being cleared. */ + } + else { + uxBits_ret = xEventGroupGetBits(event_handle); +#ifdef CHECK_VALUE_OF_EVENT_GROUP + uxBits = +#endif + xEventGroupClearBits( + event_handle, /* The event group being updated. */ + signals);/* The bits being cleared. */ + } + } + + return uxBits_ret; +} + + +/// Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread. +/// \param[in] signals wait until all specified signal flags set or 0 for any single signal flag. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event flag information or error code. +/// \note MUST REMAIN UNCHANGED: \b osSignalWait shall be consistent in every CMSIS-RTOS. +osEvent osSignalWait (int32_t signals, uint32_t millisec) +{ + TaskHandle_t thread_id; + EventGroupHandle_t event_handle; + EventBits_t uxBits=0x80000000; + osEvent ret; + uint32_t wait_ticks; + + if (signals & (0xFFFFFFFF << osFeature_Signals)) { + ret.status = osErrorValue; + ret.value.signals = 0; + return ret; + } + + thread_id = xTaskGetCurrentTaskHandle(); + event_handle = find_signal_by_thread(thread_id); + if (event_handle) { + if (signals == 0) { + // if signals is 0, then wait any signal + signals = (1 << osFeature_Signals) - 1; + } + + wait_ticks = millisec_to_ticks(millisec); + uxBits = xEventGroupWaitBits( + event_handle, /* The event group being tested. */ + signals, /* The bits within the event group to wait for. */ + pdTRUE, /* the signals should be cleared before returning. */ + pdFALSE, /* Don't wait for both bits, either bit will do. */ + wait_ticks );/* Wait for either bit to be set. */ + if (uxBits == 0) { + ret.status = millisec ? osEventTimeout : osOK; + ret.value.signals = 0; + } + else { + ret.status = osEventSignal; + ret.value.signals = uxBits; + } + } + + return ret; +} +#else +// The older FreeRTOS version didn't support Signal Management functions + +int32_t osSignalSet (osThreadId thread_id, int32_t signals) +{ + return 0; +} + +int32_t osSignalClear (osThreadId thread_id, int32_t signals) +{ + return 0; +} + +osEvent osSignalWait (int32_t signals, uint32_t millisec) +{ + osEvent ret; + + ret.status = osErrorOS; + return ret; +} + +#endif // end of "else of #if configSignalManagementSupport + +// ==== Mutex Management ==== + + +/// Create and Initialize a Mutex object +/// \param[in] mutex_def mutex definition referenced with \ref osMutex. +/// \return mutex ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMutexCreate shall be consistent in every CMSIS-RTOS. +osMutexId osMutexCreate (const osMutexDef_t *mutex_def) +{ + (void) mutex_def; + + return xSemaphoreCreateMutex(); +} + + + +/// Wait until a Mutex becomes available +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexWait shall be consistent in every CMSIS-RTOS. +osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec) +{ + portTickType ticks; + + + if (mutex_id == NULL) { + return osErrorParameter; + } + + if (inHandlerMode()) { + return osErrorISR; + } + + ticks = millisec_to_ticks(millisec); + + if (xSemaphoreTake(mutex_id, ticks) != pdTRUE) { + return osErrorOS; + } + + return osOK; +} + + + +/// Release a Mutex that was obtained by \ref osMutexWait +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexRelease shall be consistent in every CMSIS-RTOS. +osStatus osMutexRelease (osMutexId mutex_id) +{ + osStatus result = osOK; + portBASE_TYPE taskWoken = pdFALSE; + + + if (inHandlerMode()) { + if (xSemaphoreGiveFromISR(mutex_id, &taskWoken) != pdTRUE) { + result = osErrorOS; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + if (xSemaphoreGive(mutex_id) != pdTRUE) { + result = osErrorOS; + } + } + + return result; +} + +/// Delete a Mutex that was created by \ref osMutexCreate. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexDelete shall be consistent in every CMSIS-RTOS. +osStatus osMutexDelete (osMutexId mutex_id) +{ + if (!mutex_id) { + return osErrorValue; + } + + vSemaphoreDelete(mutex_id); + return osOK; +} + + +// ==== Semaphore Management Functions ==== + +#if (defined (osFeature_Semaphore) && (osFeature_Semaphore != 0)) // Semaphore available + +/// Create and Initialize a Semaphore object used for managing resources +/// \param[in] semaphore_def semaphore definition referenced with \ref osSemaphore. +/// \param[in] count number of available resources. +/// \return semaphore ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreCreate shall be consistent in every CMSIS-RTOS. +osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count) +{ + (void) semaphore_def; + osSemaphoreId sema; + + if (count == 1) { + vSemaphoreCreateBinary(sema); + return sema; + } + + return xSemaphoreCreateCounting(count, count); +} + + + +/// Wait until a Semaphore token becomes available +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphore. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return number of available tokens, or -1 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreWait shall be consistent in every CMSIS-RTOS. +int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec) +{ + portTickType ticks; + + + if (semaphore_id == NULL) { + return osErrorParameter; + } + + ticks = millisec_to_ticks(millisec); + + if (inHandlerMode()) { + return osErrorISR; + } + + if (xSemaphoreTake(semaphore_id, ticks) != pdTRUE) { + return osErrorOS; + } + + return osOK; +} + + +/// Release a Semaphore token +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphore. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreRelease shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreRelease (osSemaphoreId semaphore_id) +{ + osStatus result = osOK; + portBASE_TYPE taskWoken = pdFALSE; + + + if (inHandlerMode()) { + if (xSemaphoreGiveFromISR(semaphore_id, &taskWoken) != pdTRUE) { + result = osErrorOS; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + if (xSemaphoreGive(semaphore_id) != pdTRUE) { + result = osErrorOS; + } + } + + return result; +} + + +/// Delete a Semaphore that was created by \ref osSemaphoreCreate. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreDelete shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreDelete (osSemaphoreId semaphore_id) +{ + if (!semaphore_id) { + return osErrorValue; + } + + vSemaphoreDelete(semaphore_id); + return osOK; +} + + +#endif // Semaphore available + +// ==== Memory Pool Management Functions ==== + +#if (defined (osFeature_Pool) && (osFeature_Pool != 0)) // Memory Pool Management available + +#if 0 +/// \brief Define a Memory Pool. +/// \param name name of the memory pool. +/// \param no maximum number of objects (elements) in the memory pool. +/// \param type data type of a single object (element). +/// \note CAN BE CHANGED: The parameter to \b osPoolDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osPoolDef(name, no, type) \ +extern osPoolDef_t os_pool_def_##name; +#else // define the object +#define osPoolDef(name, no, type) \ +osPoolDef_t os_pool_def_##name = \ +{ (no), sizeof(type), NULL }; +#endif +#endif + +//TODO +//This is a primitive and inefficient wrapper around the existing FreeRTOS memory management. +//A better implementation will have to modify heap_x.c! + + +typedef struct os_pool_cb { + void *pool; + uint8_t *markers; + uint32_t pool_sz; + uint32_t item_sz; + uint32_t currentIndex; +} os_pool_cb_t; + +#if 0 +/// \brief Access a Memory Pool definition. +/// \param name name of the memory pool +/// \note CAN BE CHANGED: The parameter to \b osPool shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osPool(name) \ +&os_pool_def_##name +#endif + +/// Create and Initialize a memory pool +/// \param[in] pool_def memory pool definition referenced with \ref osPool. +/// \return memory pool ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osPoolCreate shall be consistent in every CMSIS-RTOS. +osPoolId osPoolCreate (const osPoolDef_t *pool_def) +{ + osPoolId thePool; + int itemSize = 4 * ((pool_def->item_sz + 3) / 4); + uint32_t i; + + /* First have to allocate memory for the pool control block. */ + thePool = pvPortMalloc(sizeof(os_pool_cb_t)); + if (thePool) { + thePool->pool_sz = pool_def->pool_sz; + thePool->item_sz = itemSize; + thePool->currentIndex = 0; + + /* Memory for markers */ + thePool->markers = pvPortMalloc(pool_def->pool_sz); + if (thePool->markers) { + /* Now allocate the pool itself. */ + thePool->pool = pvPortMalloc(pool_def->pool_sz * itemSize); + + if (thePool->pool) { + for (i = 0; i < pool_def->pool_sz; i++) { + thePool->markers[i] = 0; + } + } + else { + vPortFree(thePool->markers); + vPortFree(thePool); + thePool = NULL; + } + } + else { + vPortFree(thePool); + thePool = NULL; + } + } + + return thePool; +} + + +/// Allocate a memory block from a memory pool +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolAlloc (osPoolId pool_id) +{ + int dummy; + void *p = NULL; + uint32_t i; + uint32_t index; + + if (inHandlerMode()) { + dummy = portSET_INTERRUPT_MASK_FROM_ISR(); + } + else { + vPortEnterCritical(); + } + + for (i = 0; i < pool_id->pool_sz; i++) { + index = pool_id->currentIndex + i; + if (index >= pool_id->pool_sz) { + index = 0; + } + + if (pool_id->markers[index] == 0) { + pool_id->markers[index] = 1; + p = (void *)((uint32_t)(pool_id->pool) + (index * pool_id->item_sz)); + pool_id->currentIndex = index; + break; + } + } + + if (inHandlerMode()) { + portCLEAR_INTERRUPT_MASK_FROM_ISR(dummy); + } + else { + vPortExitCritical(); + } + + return p; +} + + +/// Allocate a memory block from a memory pool and set memory block to zero +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolCAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolCAlloc (osPoolId pool_id) +{ + void *p = osPoolAlloc(pool_id); + + os_memset(p, 0, pool_id->item_sz); + + return p; +} + + +/// Return an allocated memory block back to a specific memory pool +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \param[in] block address of the allocated memory block that is returned to the memory pool. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osPoolFree shall be consistent in every CMSIS-RTOS. +osStatus osPoolFree (osPoolId pool_id, void *block) +{ + int dummy; + uint32_t index; + + if (pool_id == NULL) { + return osErrorParameter; + } + + if (block == NULL) { + return osErrorParameter; + } + + if (block < pool_id->pool) { + return osErrorParameter; + } + + index = (uint32_t)block - (uint32_t)(pool_id->pool); + if (index % pool_id->item_sz) { + return osErrorParameter; + } + index = index / pool_id->item_sz; + if (index >= pool_id->pool_sz) { + return osErrorParameter; + } + + if (inHandlerMode()) { + dummy = portSET_INTERRUPT_MASK_FROM_ISR(); + } + else { + vPortEnterCritical(); + } + + pool_id->markers[index] = 0; + + if (inHandlerMode()) { + portCLEAR_INTERRUPT_MASK_FROM_ISR(dummy); + } + else { + vPortExitCritical(); + } + + return osOK; +} + + +#endif // Memory Pool Management available + + +// ==== Message Queue Management Functions ==== + +#if (defined (osFeature_MessageQ) && (osFeature_MessageQ != 0)) // Message Queues available + +/// Create and Initialize a Message Queue. +/// \param[in] queue_def queue definition referenced with \ref osMessageQ. +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return message queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMessageCreate shall be consistent in every CMSIS-RTOS. +osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id) +{ + (void) thread_id; + + return xQueueCreate(queue_def->queue_sz, queue_def->item_sz); +} + + +/// Put a Message to a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] info message information. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS. +osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec) +{ + portBASE_TYPE taskWoken = pdFALSE; + portTickType ticks; + + if (inHandlerMode()) { + if (xQueueSendFromISR(queue_id, (const void *)info, &taskWoken) != pdTRUE) { + return osErrorOS; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + ticks = millisec_to_ticks(millisec); + + if (xQueueSend(queue_id, (const void *)info, ticks) != pdTRUE) { + return osErrorOS; + } + } + + return osOK; +} + + + +/// Get a Message or Wait for a Message from a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event information that includes status code. +/// \note MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS. +osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec) +{ + portBASE_TYPE taskWoken = pdFALSE; + portTickType ticks; + osEvent retEvent; + + retEvent.def.message_id = queue_id; + if (inHandlerMode()) { + if (xQueueReceiveFromISR(queue_id, (void *)retEvent.value.p, &taskWoken) != pdTRUE) { + retEvent.status = osErrorOS; + return retEvent; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + ticks = millisec_to_ticks(millisec); + + if (xQueueReceive(queue_id, (void *)retEvent.value.p, ticks) != pdTRUE) { + retEvent.status = osErrorOS; + return retEvent; + } + } + + retEvent.status = osOK; + + return retEvent; +} + +#endif // Message Queues available + + +// ==== Mail Queue Management Functions ==== + +#if (defined (osFeature_MailQ) && (osFeature_MailQ != 0)) // Mail Queues available + + +typedef struct os_mailQ_cb { + osMailQDef_t *queue_def; + xQueueHandle handle; + osPoolId pool; +} os_mailQ_cb_t; + + +/// Create and Initialize mail queue +/// \param[in] queue_def reference to the mail queue definition obtain with \ref osMailQ +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return mail queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailCreate shall be consistent in every CMSIS-RTOS. +osMailQId osMailCreate (const osMailQDef_t *queue_def, osThreadId thread_id) +{ + (void) thread_id; + + osPoolDef_t pool_def = {queue_def->queue_sz, queue_def->item_sz}; + + + /* Create a mail queue control block */ + *(queue_def->cb) = pvPortMalloc(sizeof(struct os_mailQ_cb)); + if (*(queue_def->cb) == NULL) { + return NULL; + } + (*(queue_def->cb))->queue_def = (osMailQDef_t *)queue_def; + + /* Create a queue in FreeRTOS */ + (*(queue_def->cb))->handle = xQueueCreate(queue_def->queue_sz, sizeof(void *)); + if ((*(queue_def->cb))->handle == NULL) { + vPortFree(*(queue_def->cb)); + return NULL; + } + + /* Create a mail pool */ + (*(queue_def->cb))->pool = osPoolCreate(&pool_def); + if ((*(queue_def->cb))->pool == NULL) { + vQueueDelete((*(queue_def->cb))->handle); + vPortFree(*(queue_def->cb)); + return NULL; + } + + return *(queue_def->cb); +} + + + +/// Allocate a memory block from a mail +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can be filled with mail or NULL in case error. +/// \note MUST REMAIN UNCHANGED: \b osMailAlloc shall be consistent in every CMSIS-RTOS. +void *osMailAlloc (osMailQId queue_id, uint32_t millisec) +{ + (void) millisec; + void *p; + uint32_t retried=0; + + + if (queue_id == NULL) { + return NULL; + } + + do { + p = osPoolAlloc(queue_id->pool); + if (NULL == p) { + // sleep a while and then try again + if (millisec == osWaitForever) { + osDelay(2); + } + else { + if (!retried) { + osDelay(millisec); + retried = 1; + } + else { + break; + } + } + } + } while (NULL == p); + + + return p; +} + + + +/// Allocate a memory block from a mail and set memory block to zero +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can shall filled with mail or NULL in case error. +/// \note MUST REMAIN UNCHANGED: \b osMailCAlloc shall be consistent in every CMSIS-RTOS. +void *osMailCAlloc (osMailQId queue_id, uint32_t millisec) +{ +// uint32_t i; + void *p = osMailAlloc(queue_id, millisec); + + if (p) { + os_memset(p, 0, queue_id->queue_def->item_sz); + } + + return p; +} + + + +/// Put a mail to a queue +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail memory block previously allocated with \ref osMailAlloc or \ref osMailCAlloc. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailPut shall be consistent in every CMSIS-RTOS. +osStatus osMailPut (osMailQId queue_id, void *mail) +{ + portBASE_TYPE taskWoken; + portTickType ticks=1000/portTICK_RATE_MS; // No timeout is defined for this function, so we just wait 1 sec + + + if (queue_id == NULL) { + return osErrorParameter; + } + + taskWoken = pdFALSE; + + if (inHandlerMode()) { + if (xQueueSendFromISR(queue_id->handle, &mail, &taskWoken) != pdTRUE) { + return osErrorOS; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + if (xQueueSend(queue_id->handle, &mail, ticks) != pdTRUE) { //TODO where to get timeout value? + return osErrorOS; + } + } + + return osOK; +} + + + +/// Get a mail from a queue +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osMailGet shall be consistent in every CMSIS-RTOS. +osEvent osMailGet (osMailQId queue_id, uint32_t millisec) +{ + portBASE_TYPE taskWoken; + portTickType ticks; + osEvent event; + + event.def.mail_id = queue_id; + + if (queue_id == NULL) { + event.status = osErrorParameter; + return event; + } + + taskWoken = pdFALSE; + + ticks = millisec_to_ticks(millisec); + + if (inHandlerMode()) { + if (xQueueReceiveFromISR(queue_id->handle, &event.value.p, &taskWoken) == pdTRUE) { + /* We have mail */ + event.status = osEventMail; + } + else { + event.status = osOK; + } + portEND_SWITCHING_ISR(taskWoken); + } + else { + if (xQueueReceive(queue_id->handle, &event.value.p, ticks) == pdTRUE) { + /* We have mail */ + event.status = osEventMail; + } + else { + event.status = (ticks == 0) ? osOK : osEventTimeout; + } + } + + return event; +} + + + +/// Free a memory block from a mail +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail pointer to the memory block that was obtained with \ref osMailGet. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailFree shall be consistent in every CMSIS-RTOS. +osStatus osMailFree (osMailQId queue_id, void *mail) +{ + if (queue_id == NULL) { + return osErrorParameter; + } + + osPoolFree(queue_id->pool, mail); + + return osOK; +} + + +void *calloc_freertos(size_t nelements, size_t elementSize) +{ + void *pbuf; + uint32_t size; + + size = nelements*elementSize; + pbuf = pvPortMalloc(size); + os_memset(pbuf, 0, size); + + return pbuf; +} +#endif // Mail Queues available + + diff --git a/component/os/freertos/cmsis_os.h b/component/os/freertos/cmsis_os.h new file mode 100644 index 0000000..11e8112 --- /dev/null +++ b/component/os/freertos/cmsis_os.h @@ -0,0 +1,816 @@ +/* ---------------------------------------------------------------------- + * $Date: 5. February 2013 + * $Revision: V1.02 + * + * Project: CMSIS-RTOS API + * Title: cmsis_os.h template header file + * + * Version 0.02 + * Initial Proposal Phase + * Version 0.03 + * osKernelStart added, optional feature: main started as thread + * osSemaphores have standard behavior + * osTimerCreate does not start the timer, added osTimerStart + * osThreadPass is renamed to osThreadYield + * Version 1.01 + * Support for C++ interface + * - const attribute removed from the osXxxxDef_t typedef's + * - const attribute added to the osXxxxDef macros + * Added: osTimerDelete, osMutexDelete, osSemaphoreDelete + * Added: osKernelInitialize + * Version 1.02 + * Control functions for short timeouts in microsecond resolution: + * Added: osKernelSysTick, osKernelSysTickFrequency, osKernelSysTickMicroSec + * Removed: osSignalGet + *---------------------------------------------------------------------------- + * + * Copyright (c) 2013 ARM LIMITED + * 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 ARM 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 COPYRIGHT HOLDERS AND 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 "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "queue.h" +#include "semphr.h" + + +#define FREERTOS_VERSION 0x00080001 // bits[31:16] main version, bits[15:0] sub-version + +#if FREERTOS_VERSION >= 0x00080000 +#define configSignalManagementSupport 1 +#else +#define configSignalManagementSupport 0 +#endif + +#if configSignalManagementSupport +#include "event_groups.h" +#endif + +/** +\page cmsis_os_h Header File Template: cmsis_os.h + +The file \b cmsis_os.h is a template header file for a CMSIS-RTOS compliant Real-Time Operating System (RTOS). +Each RTOS that is compliant with CMSIS-RTOS shall provide a specific \b cmsis_os.h header file that represents +its implementation. + +The file cmsis_os.h contains: + - CMSIS-RTOS API function definitions + - struct definitions for parameters and return types + - status and priority values used by CMSIS-RTOS API functions + - macros for defining threads and other kernel objects + + +Name conventions and header file modifications + +All definitions are prefixed with \b os to give an unique name space for CMSIS-RTOS functions. +Definitions that are prefixed \b os_ are not used in the application code but local to this header file. +All definitions and functions that belong to a module are grouped and have a common prefix, i.e. \b osThread. + +Definitions that are marked with CAN BE CHANGED can be adapted towards the needs of the actual CMSIS-RTOS implementation. +These definitions can be specific to the underlying RTOS kernel. + +Definitions that are marked with MUST REMAIN UNCHANGED cannot be altered. Otherwise the CMSIS-RTOS implementation is no longer +compliant to the standard. Note that some functions are optional and need not to be provided by every CMSIS-RTOS implementation. + + +Function calls from interrupt service routines + +The following CMSIS-RTOS functions can be called from threads and interrupt service routines (ISR): + - \ref osSignalSet + - \ref osSemaphoreRelease + - \ref osPoolAlloc, \ref osPoolCAlloc, \ref osPoolFree + - \ref osMessagePut, \ref osMessageGet + - \ref osMailAlloc, \ref osMailCAlloc, \ref osMailGet, \ref osMailPut, \ref osMailFree + +Functions that cannot be called from an ISR are verifying the interrupt status and return in case that they are called +from an ISR context the status code \b osErrorISR. In some implementations this condition might be caught using the HARD FAULT vector. + +Some CMSIS-RTOS implementations support CMSIS-RTOS function calls from multiple ISR at the same time. +If this is impossible, the CMSIS-RTOS rejects calls by nested ISR functions with the status code \b osErrorISRRecursive. + + +Define and reference object definitions + +With \#define osObjectsExternal objects are defined as external symbols. This allows to create a consistent header file +that is used throughout a project as shown below: + +Header File +\code +#include // CMSIS RTOS header file + +// Thread definition +extern void thread_sample (void const *argument); // function prototype +osThreadDef (thread_sample, osPriorityBelowNormal, 1, 100); + +// Pool definition +osPoolDef(MyPool, 10, long); +\endcode + + +This header file defines all objects when included in a C/C++ source file. When \#define osObjectsExternal is +present before the header file, the objects are defined as external symbols. A single consistent header file can therefore be +used throughout the whole project. + +Example +\code +#include "osObjects.h" // Definition of the CMSIS-RTOS objects +\endcode + +\code +#define osObjectExternal // Objects will be defined as external symbols +#include "osObjects.h" // Reference to the CMSIS-RTOS objects +\endcode + +*/ + +#ifndef _CMSIS_OS_H +#define _CMSIS_OS_H + +/// \note MUST REMAIN UNCHANGED: \b osCMSIS identifies the CMSIS-RTOS API version. +#define osCMSIS 0x10002 ///< API version (main [31:16] .sub [15:0]) + +/// \note CAN BE CHANGED: \b osCMSIS_KERNEL identifies the underlying RTOS kernel and version number. +#define osCMSIS_KERNEL 0x10000 ///< RTOS identification and version (main [31:16] .sub [15:0]) + +/// \note MUST REMAIN UNCHANGED: \b osKernelSystemId shall be consistent in every CMSIS-RTOS. +#define osKernelSystemId "KERNEL V1.00" ///< RTOS identification string + +/// \note MUST REMAIN UNCHANGED: \b osFeature_xxx shall be consistent in every CMSIS-RTOS. +#define osFeature_MainThread 1 ///< main thread 1=main can be thread, 0=not available +#define osFeature_Pool 1 ///< Memory Pools: 1=available, 0=not available +#define osFeature_MailQ 1 ///< Mail Queues: 1=available, 0=not available +#define osFeature_MessageQ 1 ///< Message Queues: 1=available, 0=not available +#define osFeature_Signals 8 ///< maximum number of Signal Flags available per thread +#define osFeature_Semaphore 30 ///< maximum count for \ref osSemaphoreCreate function +#define osFeature_Wait 1 ///< osWait function: 1=available, 0=not available +#define osFeature_SysTick 1 ///< osKernelSysTick functions: 1=available, 0=not available + +//#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + +// ==== Enumeration, structures, defines ==== + +/// Priority used for thread control. +/// \note MUST REMAIN UNCHANGED: \b osPriority shall be consistent in every CMSIS-RTOS. +typedef enum { + osPriorityIdle = -3, ///< priority: idle (lowest) + osPriorityLow = -2, ///< priority: low + osPriorityBelowNormal = -1, ///< priority: below normal + osPriorityNormal = 0, ///< priority: normal (default) + osPriorityAboveNormal = +1, ///< priority: above normal + osPriorityHigh = +2, ///< priority: high + osPriorityRealtime = +3, ///< priority: realtime (highest) + osPriorityError = 0x84 ///< system cannot determine priority or thread has illegal priority +} osPriority; + +/// Timeout value. +/// \note MUST REMAIN UNCHANGED: \b osWaitForever shall be consistent in every CMSIS-RTOS. +#define osWaitForever 0xFFFFFFFF ///< wait forever timeout value + +/// Status code values returned by CMSIS-RTOS functions. +/// \note MUST REMAIN UNCHANGED: \b osStatus shall be consistent in every CMSIS-RTOS. +typedef enum { + osOK = 0, ///< function completed; no error or event occurred. + osEventSignal = 0x08, ///< function completed; signal event occurred. + osEventMessage = 0x10, ///< function completed; message event occurred. + osEventMail = 0x20, ///< function completed; mail event occurred. + osEventTimeout = 0x40, ///< function completed; timeout occurred. + osErrorParameter = 0x80, ///< parameter error: a mandatory parameter was missing or specified an incorrect object. + osErrorResource = 0x81, ///< resource not available: a specified resource was not available. + osErrorTimeoutResource = 0xC1, ///< resource not available within given time: a specified resource was not available within the timeout period. + osErrorISR = 0x82, ///< not allowed in ISR context: the function cannot be called from interrupt service routines. + osErrorISRRecursive = 0x83, ///< function called multiple times from ISR with same object. + osErrorPriority = 0x84, ///< system cannot determine priority or thread has illegal priority. + osErrorNoMemory = 0x85, ///< system is out of memory: it was impossible to allocate or reserve memory for the operation. + osErrorValue = 0x86, ///< value of a parameter is out of range. + osErrorOS = 0xFF, ///< unspecified RTOS error: run-time error but no other error message fits. + os_status_reserved = 0x7FFFFFFF ///< prevent from enum down-size compiler optimization. +} osStatus; + + +/// Timer type value for the timer definition. +/// \note MUST REMAIN UNCHANGED: \b os_timer_type shall be consistent in every CMSIS-RTOS. +typedef enum { + osTimerOnce = 0, ///< one-shot timer + osTimerPeriodic = 1 ///< repeating timer +} os_timer_type; + +/// Entry point of a thread. +/// \note MUST REMAIN UNCHANGED: \b os_pthread shall be consistent in every CMSIS-RTOS. +typedef void (*os_pthread) (void const *argument); + +/// Entry point of a timer call back function. +/// \note MUST REMAIN UNCHANGED: \b os_ptimer shall be consistent in every CMSIS-RTOS. +typedef void (*os_ptimer) (void const *argument); + +// >>> the following data type definitions may shall adapted towards a specific RTOS + +/// Thread ID identifies the thread (pointer to a thread control block). +/// \note CAN BE CHANGED: \b os_thread_cb is implementation specific in every CMSIS-RTOS. +typedef xTaskHandle osThreadId; + +/// Timer ID identifies the timer (pointer to a timer control block). +/// \note CAN BE CHANGED: \b os_timer_cb is implementation specific in every CMSIS-RTOS. +typedef xTimerHandle osTimerId; + +/// Mutex ID identifies the mutex (pointer to a mutex control block). +/// \note CAN BE CHANGED: \b os_mutex_cb is implementation specific in every CMSIS-RTOS. +typedef xSemaphoreHandle osMutexId; + +/// Semaphore ID identifies the semaphore (pointer to a semaphore control block). +/// \note CAN BE CHANGED: \b os_semaphore_cb is implementation specific in every CMSIS-RTOS. +typedef xSemaphoreHandle osSemaphoreId; + +/// Pool ID identifies the memory pool (pointer to a memory pool control block). +/// \note CAN BE CHANGED: \b os_pool_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_pool_cb *osPoolId; + +/// Message ID identifies the message queue (pointer to a message queue control block). +/// \note CAN BE CHANGED: \b os_messageQ_cb is implementation specific in every CMSIS-RTOS. +typedef xQueueHandle osMessageQId; + +/// Mail ID identifies the mail queue (pointer to a mail queue control block). +/// \note CAN BE CHANGED: \b os_mailQ_cb is implementation specific in every CMSIS-RTOS. +typedef struct os_mailQ_cb *osMailQId; + + +/// Thread Definition structure contains startup information of a thread. +/// \note CAN BE CHANGED: \b os_thread_def is implementation specific in every CMSIS-RTOS. +typedef struct os_thread_def { + os_pthread pthread; ///< start address of thread function + osPriority tpriority; ///< initial thread priority + uint32_t instances; ///< maximum number of instances of that thread function + uint32_t stacksize; ///< stack size requirements in bytes; 0 is default stack size + char * name; +} osThreadDef_t; + +/// Timer Definition structure contains timer parameters. +/// \note CAN BE CHANGED: \b os_timer_def is implementation specific in every CMSIS-RTOS. +struct os_timer_custom { + void *argument; +}; + +typedef struct os_timer_def { + os_ptimer ptimer; ///< start address of a timer function + struct os_timer_custom *custom; +} osTimerDef_t; + +/// Mutex Definition structure contains setup information for a mutex. +/// \note CAN BE CHANGED: \b os_mutex_def is implementation specific in every CMSIS-RTOS. +typedef struct os_mutex_def { + uint32_t dummy; ///< dummy value. +} osMutexDef_t; + +/// Semaphore Definition structure contains setup information for a semaphore. +/// \note CAN BE CHANGED: \b os_semaphore_def is implementation specific in every CMSIS-RTOS. +typedef struct os_semaphore_def { + uint32_t dummy; ///< dummy value. +} osSemaphoreDef_t; + +/// Definition structure for memory block allocation +/// \note CAN BE CHANGED: \b os_pool_def is implementation specific in every CMSIS-RTOS. +typedef struct os_pool_def { + uint32_t pool_sz; ///< number of items (elements) in the pool + uint32_t item_sz; ///< size of an item + void *pool; ///< pointer to memory for pool +} osPoolDef_t; + +/// Definition structure for message queue. +/// \note CAN BE CHANGED: \b os_messageQ_def is implementation specific in every CMSIS-RTOS. +typedef struct os_messageQ_def { + uint32_t queue_sz; ///< number of elements in the queue + uint32_t item_sz; ///< size of an item + void *pool; ///< memory array for messages +} osMessageQDef_t; + +/// Definition structure for mail queue +/// \note CAN BE CHANGED: \b os_mailQ_def is implementation specific in every CMSIS-RTOS. +typedef struct os_mailQ_def { + uint32_t queue_sz; ///< number of elements in the queue + uint32_t item_sz; ///< size of an item + struct os_mailQ_cb **cb; +} osMailQDef_t; + +/// Event structure contains detailed information about an event. +/// \note MUST REMAIN UNCHANGED: \b os_event shall be consistent in every CMSIS-RTOS. +/// However the struct may be extended at the end. +typedef struct { + osStatus status; ///< status code: event or error information + union { + uint32_t v; ///< message as 32-bit value + void *p; ///< message or mail as void pointer + int32_t signals; ///< signal flags + } value; ///< event value + union { + osMailQId mail_id; ///< mail id obtained by \ref osMailCreate + osMessageQId message_id; ///< message id obtained by \ref osMessageCreate + } def; ///< event definition +} osEvent; + + +// ==== Kernel Control Functions ==== + +/// Initialize the RTOS Kernel for creating objects. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osKernelInitialize shall be consistent in every CMSIS-RTOS. +osStatus osKernelInitialize (void); + +/// Start the RTOS Kernel. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osKernelStart shall be consistent in every CMSIS-RTOS. +osStatus osKernelStart (void); + +/// Check if the RTOS kernel is already started. +/// \note MUST REMAIN UNCHANGED: \b osKernelRunning shall be consistent in every CMSIS-RTOS. +/// \return 0 RTOS is not started, 1 RTOS is started. +int32_t osKernelRunning(void); + +#if (defined (osFeature_SysTick) && (osFeature_SysTick != 0)) // System Timer available + +/// Get the RTOS kernel system timer counter +/// \note MUST REMAIN UNCHANGED: \b osKernelSysTick shall be consistent in every CMSIS-RTOS. +/// \return RTOS kernel system timer as 32-bit value +uint32_t osKernelSysTick (void); + +/// The RTOS kernel system timer frequency in Hz +/// \note Reflects the system timer setting and is typically defined in a configuration file. +#define osKernelSysTickFrequency configTICK_RATE_HZ + +/// Convert a microseconds value to a RTOS kernel system timer value. +/// \param microsec time value in microseconds. +/// \return time value normalized to the \ref osKernelSysTickFrequency +#define osKernelSysTickMicroSec(microsec) (((uint64_t)microsec * (osKernelSysTickFrequency)) / 1000000) + +#endif // System Timer available + +// ==== Thread Management ==== + +/// Create a Thread Definition with function, priority, and stack requirements. +/// \param name name of the thread function. +/// \param priority initial priority of the thread function. +/// \param instances number of possible thread instances. +/// \param stacksz stack size (in bytes) requirements for the thread function. +/// \note CAN BE CHANGED: The parameters to \b osThreadDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osThreadDef(name, priority, instances, stacksz) \ +extern const osThreadDef_t os_thread_def_##name +#else // define the object +#define osThreadDef(name, priority, instances, stacksz) \ +const osThreadDef_t os_thread_def_##name = \ +{ (name), (priority), (instances), (stacksz), #name } +#endif + +/// Access a Thread definition. +/// \param name name of the thread definition object. +/// \note CAN BE CHANGED: The parameter to \b osThread shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osThread(name) \ +&os_thread_def_##name + +/// Create a thread and add it to Active Threads and set it to state READY. +/// \param[in] thread_def thread definition referenced with \ref osThread. +/// \param[in] argument pointer that is passed to the thread function as start argument. +/// \return thread ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osThreadCreate shall be consistent in every CMSIS-RTOS. +osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument); + +/// Return the thread ID of the current running thread. +/// \return thread ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetId shall be consistent in every CMSIS-RTOS. +osThreadId osThreadGetId (void); + +/// Terminate execution of a thread and remove it from Active Threads. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadTerminate shall be consistent in every CMSIS-RTOS. +osStatus osThreadTerminate (osThreadId thread_id); + +/// Pass control to next thread that is in state \b READY. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadYield shall be consistent in every CMSIS-RTOS. +osStatus osThreadYield (void); + +/// Change priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] priority new priority value for the thread function. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osThreadSetPriority shall be consistent in every CMSIS-RTOS. +osStatus osThreadSetPriority (osThreadId thread_id, osPriority priority); + +/// Get current priority of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \return current priority value of the thread function. +/// \note MUST REMAIN UNCHANGED: \b osThreadGetPriority shall be consistent in every CMSIS-RTOS. +osPriority osThreadGetPriority (osThreadId thread_id); + + +// ==== Generic Wait Functions ==== + +/// Wait for Timeout (Time Delay). +/// \param[in] millisec time delay value +/// \return status code that indicates the execution status of the function. +osStatus osDelay (uint32_t millisec); + +#if (defined (osFeature_Wait) && (osFeature_Wait != 0)) // Generic Wait available + +/// Wait for Signal, Message, Mail, or Timeout. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains signal, message, or mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osWait shall be consistent in every CMSIS-RTOS. +osEvent osWait (uint32_t millisec); + +#endif // Generic Wait available + + +// ==== Timer Management Functions ==== +/// Define a Timer object. +/// \param name name of the timer object. +/// \param function name of the timer call back function. +/// \note CAN BE CHANGED: The parameter to \b osTimerDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osTimerDef(name, function) \ +extern const osTimerDef_t os_timer_def_##name; \ +extern struct os_timer_custom os_timer_custome_##name +#else // define the object +#define osTimerDef(name, function) \ +struct os_timer_custom os_timer_custom_##name; \ +const osTimerDef_t os_timer_def_##name = \ +{ (function), (&os_timer_custom_##name) } +#endif + +/// Access a Timer definition. +/// \param name name of the timer object. +/// \note CAN BE CHANGED: The parameter to \b osTimer shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osTimer(name) \ +&os_timer_def_##name + +/// Create a timer. +/// \param[in] timer_def timer object referenced with \ref osTimer. +/// \param[in] type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. +/// \param[in] argument argument to the timer call back function. +/// \return timer ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osTimerCreate shall be consistent in every CMSIS-RTOS. +osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument); + +/// Start or restart a timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \param[in] millisec time delay value of the timer. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStart shall be consistent in every CMSIS-RTOS. +osStatus osTimerStart (osTimerId timer_id, uint32_t millisec); + +/// Stop the timer. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerStop shall be consistent in every CMSIS-RTOS. +osStatus osTimerStop (osTimerId timer_id); + +/// Delete a timer that was created by \ref osTimerCreate. +/// \param[in] timer_id timer ID obtained by \ref osTimerCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osTimerDelete shall be consistent in every CMSIS-RTOS. +osStatus osTimerDelete (osTimerId timer_id); + + +// ==== Signal Management ==== + +/// Set the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that should be set. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalSet shall be consistent in every CMSIS-RTOS. +int32_t osSignalSet (osThreadId thread_id, int32_t signals); + +/// Clear the specified Signal Flags of an active thread. +/// \param[in] thread_id thread ID obtained by \ref osThreadCreate or \ref osThreadGetId. +/// \param[in] signals specifies the signal flags of the thread that shall be cleared. +/// \return previous signal flags of the specified thread or 0x80000000 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSignalClear shall be consistent in every CMSIS-RTOS. +int32_t osSignalClear (osThreadId thread_id, int32_t signals); + +/// Wait for one or more Signal Flags to become signaled for the current \b RUNNING thread. +/// \param[in] signals wait until all specified signal flags set or 0 for any single signal flag. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event flag information or error code. +/// \note MUST REMAIN UNCHANGED: \b osSignalWait shall be consistent in every CMSIS-RTOS. +osEvent osSignalWait (int32_t signals, uint32_t millisec); + + +// ==== Mutex Management ==== + +/// Define a Mutex. +/// \param name name of the mutex object. +/// \note CAN BE CHANGED: The parameter to \b osMutexDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMutexDef(name) \ +extern const osMutexDef_t os_mutex_def_##name +#else // define the object +#define osMutexDef(name) \ +const osMutexDef_t os_mutex_def_##name = { 0 } +#endif + +/// Access a Mutex definition. +/// \param name name of the mutex object. +/// \note CAN BE CHANGED: The parameter to \b osMutex shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMutex(name) \ +&os_mutex_def_##name + +/// Create and Initialize a Mutex object. +/// \param[in] mutex_def mutex definition referenced with \ref osMutex. +/// \return mutex ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMutexCreate shall be consistent in every CMSIS-RTOS. +osMutexId osMutexCreate (const osMutexDef_t *mutex_def); + +/// Wait until a Mutex becomes available. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexWait shall be consistent in every CMSIS-RTOS. +osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec); + +/// Release a Mutex that was obtained by \ref osMutexWait. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexRelease shall be consistent in every CMSIS-RTOS. +osStatus osMutexRelease (osMutexId mutex_id); + +/// Delete a Mutex that was created by \ref osMutexCreate. +/// \param[in] mutex_id mutex ID obtained by \ref osMutexCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMutexDelete shall be consistent in every CMSIS-RTOS. +osStatus osMutexDelete (osMutexId mutex_id); + + +// ==== Semaphore Management Functions ==== + +#if (defined (osFeature_Semaphore) && (osFeature_Semaphore != 0)) // Semaphore available + +/// Define a Semaphore object. +/// \param name name of the semaphore object. +/// \note CAN BE CHANGED: The parameter to \b osSemaphoreDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osSemaphoreDef(name) \ +extern const osSemaphoreDef_t os_semaphore_def_##name +#else // define the object +#define osSemaphoreDef(name) \ +const osSemaphoreDef_t os_semaphore_def_##name = { 0 } +#endif + +/// Access a Semaphore definition. +/// \param name name of the semaphore object. +/// \note CAN BE CHANGED: The parameter to \b osSemaphore shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osSemaphore(name) \ +&os_semaphore_def_##name + +/// Create and Initialize a Semaphore object used for managing resources. +/// \param[in] semaphore_def semaphore definition referenced with \ref osSemaphore. +/// \param[in] count number of available resources. +/// \return semaphore ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreCreate shall be consistent in every CMSIS-RTOS. +osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count); + +/// Wait until a Semaphore token becomes available. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return number of available tokens, or -1 in case of incorrect parameters. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreWait shall be consistent in every CMSIS-RTOS. +int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec); + +/// Release a Semaphore token. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreRelease shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreRelease (osSemaphoreId semaphore_id); + +/// Delete a Semaphore that was created by \ref osSemaphoreCreate. +/// \param[in] semaphore_id semaphore object referenced with \ref osSemaphoreCreate. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osSemaphoreDelete shall be consistent in every CMSIS-RTOS. +osStatus osSemaphoreDelete (osSemaphoreId semaphore_id); + +#endif // Semaphore available + + +// ==== Memory Pool Management Functions ==== + +#if (defined (osFeature_Pool) && (osFeature_Pool != 0)) // Memory Pool Management available + +/// \brief Define a Memory Pool. +/// \param name name of the memory pool. +/// \param no maximum number of blocks (objects) in the memory pool. +/// \param type data type of a single block (object). +/// \note CAN BE CHANGED: The parameter to \b osPoolDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osPoolDef(name, no, type) \ +extern const osPoolDef_t os_pool_def_##name +#else // define the object +#define osPoolDef(name, no, type) \ +const osPoolDef_t os_pool_def_##name = \ +{ (no), sizeof(type), NULL } +#endif + +/// \brief Access a Memory Pool definition. +/// \param name name of the memory pool +/// \note CAN BE CHANGED: The parameter to \b osPool shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osPool(name) \ +&os_pool_def_##name + +/// Create and Initialize a memory pool. +/// \param[in] pool_def memory pool definition referenced with \ref osPool. +/// \return memory pool ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osPoolCreate shall be consistent in every CMSIS-RTOS. +osPoolId osPoolCreate (const osPoolDef_t *pool_def); + +/// Allocate a memory block from a memory pool. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolAlloc (osPoolId pool_id); + +/// Allocate a memory block from a memory pool and set memory block to zero. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \return address of the allocated memory block or NULL in case of no memory available. +/// \note MUST REMAIN UNCHANGED: \b osPoolCAlloc shall be consistent in every CMSIS-RTOS. +void *osPoolCAlloc (osPoolId pool_id); + +/// Return an allocated memory block back to a specific memory pool. +/// \param[in] pool_id memory pool ID obtain referenced with \ref osPoolCreate. +/// \param[in] block address of the allocated memory block that is returned to the memory pool. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osPoolFree shall be consistent in every CMSIS-RTOS. +osStatus osPoolFree (osPoolId pool_id, void *block); + +#endif // Memory Pool Management available + + +// ==== Message Queue Management Functions ==== + +#if (defined (osFeature_MessageQ) && (osFeature_MessageQ != 0)) // Message Queues available + +/// \brief Create a Message Queue Definition. +/// \param name name of the queue. +/// \param queue_sz maximum number of messages in the queue. +/// \param type data type of a single message element (for debugger). +/// \note CAN BE CHANGED: The parameter to \b osMessageQDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMessageQDef(name, queue_sz, type) \ +extern const osMessageQDef_t os_messageQ_def_##name +#else // define the object +#define osMessageQDef(name, queue_sz, type) \ +const osMessageQDef_t os_messageQ_def_##name = \ +{ (queue_sz), sizeof (type) } +#endif + +/// \brief Access a Message Queue Definition. +/// \param name name of the queue +/// \note CAN BE CHANGED: The parameter to \b osMessageQ shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMessageQ(name) \ +&os_messageQ_def_##name + +/// Create and Initialize a Message Queue. +/// \param[in] queue_def queue definition referenced with \ref osMessageQ. +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return message queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMessageCreate shall be consistent in every CMSIS-RTOS. +osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id); + +/// Put a Message to a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] info message information. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMessagePut shall be consistent in every CMSIS-RTOS. +osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec); + +/// Get a Message or Wait for a Message from a Queue. +/// \param[in] queue_id message queue ID obtained with \ref osMessageCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out. +/// \return event information that includes status code. +/// \note MUST REMAIN UNCHANGED: \b osMessageGet shall be consistent in every CMSIS-RTOS. +osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec); + +#endif // Message Queues available + + +// ==== Mail Queue Management Functions ==== + +#if (defined (osFeature_MailQ) && (osFeature_MailQ != 0)) // Mail Queues available + +/// \brief Create a Mail Queue Definition. +/// \param name name of the queue +/// \param queue_sz maximum number of messages in queue +/// \param type data type of a single message element +/// \note CAN BE CHANGED: The parameter to \b osMailQDef shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#if defined (osObjectsExternal) // object is external +#define osMailQDef(name, queue_sz, type) \ +extern struct os_mailQ_cb *os_mailQ_cb_##name; \ +extern const osMailQDef_t os_mailQ_def_##name; +#else // define the object +#define osMailQDef(name, queue_sz, type) \ +struct os_mailQ_cb *os_mailQ_cb_##name; \ +const osMailQDef_t os_mailQ_def_##name = \ +{ (queue_sz), sizeof (type), (&os_mailQ_cb_##name) } +#endif + +/// \brief Access a Mail Queue Definition. +/// \param name name of the queue +/// \note CAN BE CHANGED: The parameter to \b osMailQ shall be consistent but the +/// macro body is implementation specific in every CMSIS-RTOS. +#define osMailQ(name) \ +&os_mailQ_def_##name + +/// Create and Initialize mail queue. +/// \param[in] queue_def reference to the mail queue definition obtain with \ref osMailQ +/// \param[in] thread_id thread ID (obtained by \ref osThreadCreate or \ref osThreadGetId) or NULL. +/// \return mail queue ID for reference by other functions or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailCreate shall be consistent in every CMSIS-RTOS. +osMailQId osMailCreate (const osMailQDef_t *queue_def, osThreadId thread_id); + +/// Allocate a memory block from a mail. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can be filled with mail or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailAlloc shall be consistent in every CMSIS-RTOS. +void *osMailAlloc (osMailQId queue_id, uint32_t millisec); + +/// Allocate a memory block from a mail and set memory block to zero. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return pointer to memory block that can be filled with mail or NULL in case of error. +/// \note MUST REMAIN UNCHANGED: \b osMailCAlloc shall be consistent in every CMSIS-RTOS. +void *osMailCAlloc (osMailQId queue_id, uint32_t millisec); + +/// Put a mail to a queue. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail memory block previously allocated with \ref osMailAlloc or \ref osMailCAlloc. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailPut shall be consistent in every CMSIS-RTOS. +osStatus osMailPut (osMailQId queue_id, void *mail); + +/// Get a mail from a queue. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] millisec timeout value or 0 in case of no time-out +/// \return event that contains mail information or error code. +/// \note MUST REMAIN UNCHANGED: \b osMailGet shall be consistent in every CMSIS-RTOS. +osEvent osMailGet (osMailQId queue_id, uint32_t millisec); + +/// Free a memory block from a mail. +/// \param[in] queue_id mail queue ID obtained with \ref osMailCreate. +/// \param[in] mail pointer to the memory block that was obtained with \ref osMailGet. +/// \return status code that indicates the execution status of the function. +/// \note MUST REMAIN UNCHANGED: \b osMailFree shall be consistent in every CMSIS-RTOS. +osStatus osMailFree (osMailQId queue_id, void *mail); + +#endif // Mail Queues available + +#define malloc(size) pvPortMalloc(size) +#define free(pbuf) vPortFree(pbuf) +extern void *calloc_freertos(size_t nelements, size_t elementSize); +#define calloc(nelements, elementSize) calloc_freertos(nelements, elementSize) + +#ifdef __cplusplus +} +#endif + +#endif // _CMSIS_OS_H diff --git a/component/os/freertos/freertos_pmu.c b/component/os/freertos/freertos_pmu.c new file mode 100644 index 0000000..2b80fe2 --- /dev/null +++ b/component/os/freertos/freertos_pmu.c @@ -0,0 +1,96 @@ +#include "FreeRTOS.h" + +#include "freertos_pmu.h" + +#include "platform_autoconf.h" +#include "sleep_ex_api.h" +#include "wlan_intf.h" + +static uint32_t wakelock = DEFAULT_WAKELOCK; +static uint32_t wakeup_event = DEFAULT_WAKEUP_EVENT; + +_WEAK void rltk_wlan_pre_sleep_processing (void) { + // Add dummy function for MP +} + +_WEAK void rltk_wlan_post_sleep_processing (void) { + // Add dummy function for MP +} + +/* ++++++++ FreeRTOS macro implementation ++++++++ */ + +/* + * It is called in idle task. + * + * @return true : System is ready to check conditions that if it can enter sleep. + * false : System keep awake. + **/ +int freertos_ready_to_sleep() { + return wakelock == 0; +} + +/* + * It is called when freertos is going to sleep. + * At this moment, all sleep conditons are satisfied. All freertos' sleep pre-processing are done. + * + * @param expected_idle_time : The time that FreeRTOS expect to sleep. + * If we set this value to 0 then FreeRTOS will do nothing in its sleep function. + **/ +void freertos_pre_sleep_processing(unsigned int *expected_idle_time) { + +#ifdef CONFIG_SOC_PS_MODULE + + unsigned int stime; + + /* To disable freertos sleep function and use our sleep function, + * we can set original expected idle time to 0. */ + stime = *expected_idle_time; + *expected_idle_time = 0; + +#ifdef CONFIG_LITTLE_WIFI_MCU_FUNCTION_THREAD +#ifdef CONFIG_POWER_SAVING + if (wakeup_event & SLEEP_WAKEUP_BY_WLAN) { + rltk_wlan_pre_sleep_processing(); + } +#endif +#endif + + sleep_ex(wakeup_event, stime); + +#ifdef CONFIG_LITTLE_WIFI_MCU_FUNCTION_THREAD +#ifdef CONFIG_POWER_SAVING + if (wakeup_event & SLEEP_WAKEUP_BY_WLAN) { + rltk_wlan_post_sleep_processing(); + } +#endif +#endif + +#else + // If PS is not enabled, then use freertos sleep function +#endif +} + +/* -------- FreeRTOS macro implementation -------- */ + +void acquire_wakelock(uint32_t lock_id) { + wakelock |= lock_id; +} + +void release_wakelock(uint32_t lock_id) { + wakelock &= ~lock_id; +} + +uint32_t get_wakelock_status() { + return wakelock; +} + +void add_wakeup_event(uint32_t event) { + wakeup_event |= event; +} + +void del_wakeup_event(uint32_t event) { + wakeup_event &= ~event; + // To fulfill tickless design, system timer is required to be wakeup event + wakeup_event |= SLEEP_WAKEUP_BY_STIMER; +} + diff --git a/component/os/freertos/freertos_pmu.h b/component/os/freertos/freertos_pmu.h new file mode 100644 index 0000000..7e74e90 --- /dev/null +++ b/component/os/freertos/freertos_pmu.h @@ -0,0 +1,25 @@ +#ifndef __FREERTOS_PMU_H_ +#define __FREERTOS_PMU_H_ + +#include "sleep_ex_api.h" + +#define BIT(n) (1<>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Creates six tasks that operate on three queues as follows: + * + * The first two tasks send and receive an incrementing number to/from a queue. + * One task acts as a producer and the other as the consumer. The consumer is a + * higher priority than the producer and is set to block on queue reads. The queue + * only has space for one item - as soon as the producer posts a message on the + * queue the consumer will unblock, pre-empt the producer, and remove the item. + * + * The second two tasks work the other way around. Again the queue used only has + * enough space for one item. This time the consumer has a lower priority than the + * producer. The producer will try to post on the queue blocking when the queue is + * full. When the consumer wakes it will remove the item from the queue, causing + * the producer to unblock, pre-empt the consumer, and immediately re-fill the + * queue. + * + * The last two tasks use the same queue producer and consumer functions. This time the queue has + * enough space for lots of items and the tasks operate at the same priority. The + * producer will execute, placing items into the queue. The consumer will start + * executing when either the queue becomes full (causing the producer to block) or + * a context switch occurs (tasks of the same priority will time slice). + * + * \page BlockQC blockQ.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V1.00: + + + Reversed the priority and block times of the second two demo tasks so + they operate as per the description above. + +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. + +Changes from V4.0.2 + + + The second set of tasks were created the wrong way around. This has been + corrected. +*/ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "BlockQ.h" +#include "print.h" + +#define blckqSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE ) +#define blckqNUM_TASK_SETS ( 3 ) + +/* Structure used to pass parameters to the blocking queue tasks. */ +typedef struct BLOCKING_QUEUE_PARAMETERS +{ + QueueHandle_t xQueue; /*< The queue to be used by the task. */ + TickType_t xBlockTime; /*< The block time to use on queue reads/writes. */ + volatile short *psCheckVariable; /*< Incremented on each successful cycle to check the task is still running. */ +} xBlockingQueueParameters; + +/* Task function that creates an incrementing number and posts it on a queue. */ +static void vBlockingQueueProducer( void *pvParameters ); + +/* Task function that removes the incrementing number from a queue and checks that +it is the expected number. */ +static void vBlockingQueueConsumer( void *pvParameters ); + +/* Variables which are incremented each time an item is removed from a queue, and +found to be the expected value. +These are used to check that the tasks are still running. */ +static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 }; + +/* Variable which are incremented each time an item is posted on a queue. These +are used to check that the tasks are still running. */ +static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartBlockingQueueTasks( unsigned portBASE_TYPE uxPriority ) +{ +xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2; +xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4; +xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6; +const unsigned portBASE_TYPE uxQueueSize1 = 1, uxQueueSize5 = 5; +const TickType_t xBlockTime = ( TickType_t ) 1000 / portTICK_PERIOD_MS; +const TickType_t xDontBlock = ( TickType_t ) 0; + + /* Create the first two tasks as described at the top of the file. */ + + /* First create the structure used to pass parameters to the consumer tasks. */ + pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Create the queue used by the first two tasks to pass the incrementing number. + Pass a pointer to the queue in the parameter structure. */ + pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) ); + + /* The consumer is created first so gets a block time as described above. */ + pxQueueParameters1->xBlockTime = xBlockTime; + + /* Pass in the variable that this task is going to increment so we can check it + is still running. */ + pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] ); + + /* Create the structure used to pass parameters to the producer task. */ + pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Pass the queue to this task also, using the parameter structure. */ + pxQueueParameters2->xQueue = pxQueueParameters1->xQueue; + + /* The producer is not going to block - as soon as it posts the consumer will + wake and remove the item so the producer should always have room to post. */ + pxQueueParameters2->xBlockTime = xDontBlock; + + /* Pass in the variable that this task is going to increment so we can check + it is still running. */ + pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] ); + + + /* Note the producer has a lower priority than the consumer when the tasks are + spawned. */ + xTaskCreate( vBlockingQueueConsumer, "QConsB1", blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL ); + xTaskCreate( vBlockingQueueProducer, "QProdB2", blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL ); + + + + /* Create the second two tasks as described at the top of the file. This uses + the same mechanism but reverses the task priorities. */ + + pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) ); + pxQueueParameters3->xBlockTime = xDontBlock; + pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] ); + + pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters4->xQueue = pxQueueParameters3->xQueue; + pxQueueParameters4->xBlockTime = xBlockTime; + pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] ); + + xTaskCreate( vBlockingQueueProducer, "QProdB3", blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueConsumer, "QConsB4", blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL ); + + + + /* Create the last two tasks as described above. The mechanism is again just + the same. This time both parameter structures are given a block time. */ + pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) ); + pxQueueParameters5->xBlockTime = xBlockTime; + pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] ); + + pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters6->xQueue = pxQueueParameters5->xQueue; + pxQueueParameters6->xBlockTime = xBlockTime; + pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] ); + + xTaskCreate( vBlockingQueueProducer, "QProdB5", blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueConsumer, "QConsB6", blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL ); +} +/*-----------------------------------------------------------*/ + +static void vBlockingQueueProducer( void *pvParameters ) +{ +unsigned short usValue = 0; +xBlockingQueueParameters *pxQueueParameters; +const char * const pcTaskStartMsg = "Blocking queue producer started.\r\n"; +const char * const pcTaskErrorMsg = "Could not post on blocking queue\r\n"; +short sErrorEverOccurred = pdFALSE; + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + if( xQueueSendToBack( pxQueueParameters->xQueue, ( void * ) &usValue, pxQueueParameters->xBlockTime ) != pdPASS ) + { + vPrintDisplayMessage( &pcTaskErrorMsg ); + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully posted a message, so increment the variable + used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the variable we are going to post next time round. The + consumer will expect the numbers to follow in numerical order. */ + ++usValue; + } + } +} +/*-----------------------------------------------------------*/ + +static void vBlockingQueueConsumer( void *pvParameters ) +{ +unsigned short usData, usExpectedValue = 0; +xBlockingQueueParameters *pxQueueParameters; +const char * const pcTaskStartMsg = "Blocking queue consumer started.\r\n"; +const char * const pcTaskErrorMsg = "Incorrect value received on blocking queue.\r\n"; +short sErrorEverOccurred = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + for( ;; ) + { + if( xQueueReceive( pxQueueParameters->xQueue, &usData, pxQueueParameters->xBlockTime ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + vPrintDisplayMessage( &pcTaskErrorMsg ); + + /* Catch-up. */ + usExpectedValue = usData; + + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully received a message, so increment the + variable used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the value we expect to remove from the queue next time + round. */ + ++usExpectedValue; + } + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +portBASE_TYPE xAreBlockingQueuesStillRunning( void ) +{ +static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 }; +static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( short ) 0, ( short ) 0, ( short ) 0 }; +portBASE_TYPE xReturn = pdPASS, xTasks; + + /* Not too worried about mutual exclusion on these variables as they are 16 + bits and we are only reading them. We also only care to see if they have + changed or not. + + Loop through each check variable and return pdFALSE if any are found not + to have changed since the last call. */ + + for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ ) + { + if( sBlockingConsumerCount[ xTasks ] == sLastBlockingConsumerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingConsumerCount[ xTasks ] = sBlockingConsumerCount[ xTasks ]; + + + if( sBlockingProducerCount[ xTasks ] == sLastBlockingProducerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ]; + } + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/PollQ.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/PollQ.c new file mode 100644 index 0000000..6148a80 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/PollQ.c @@ -0,0 +1,258 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/** + * This is a very simple queue test. See the BlockQ. c documentation for a more + * comprehensive version. + * + * Creates two tasks that communicate over a single queue. One task acts as a + * producer, the other a consumer. + * + * The producer loops for three iteration, posting an incrementing number onto the + * queue each cycle. It then delays for a fixed period before doing exactly the + * same again. + * + * The consumer loops emptying the queue. Each item removed from the queue is + * checked to ensure it contains the expected value. When the queue is empty it + * blocks for a fixed period, then does the same again. + * + * All queue access is performed without blocking. The consumer completely empties + * the queue each time it runs so the producer should never find the queue full. + * + * An error is flagged if the consumer obtains an unexpected value or the producer + * find the queue is full. + * + * \page PollQC pollQ.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "print.h" + +/* Demo program include files. */ +#include "PollQ.h" + +#define pollqSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE ) + +/* The task that posts the incrementing number onto the queue. */ +static void vPolledQueueProducer( void *pvParameters ); + +/* The task that empties the queue. */ +static void vPolledQueueConsumer( void *pvParameters ); + +/* Variables that are used to check that the tasks are still running with no errors. */ +static volatile short sPollingConsumerCount = 0, sPollingProducerCount = 0; +/*-----------------------------------------------------------*/ + +void vStartPolledQueueTasks( unsigned portBASE_TYPE uxPriority ) +{ +static QueueHandle_t xPolledQueue; +const unsigned portBASE_TYPE uxQueueSize = 10; + + /* Create the queue used by the producer and consumer. */ + xPolledQueue = xQueueCreate( uxQueueSize, ( unsigned portBASE_TYPE ) sizeof( unsigned short ) ); + + /* Spawn the producer and consumer. */ + xTaskCreate( vPolledQueueConsumer, "QConsNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, NULL ); + xTaskCreate( vPolledQueueProducer, "QProdNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, NULL ); +} +/*-----------------------------------------------------------*/ + +static void vPolledQueueProducer( void *pvParameters ) +{ +unsigned short usValue = 0, usLoop; +QueueHandle_t *pxQueue; +const TickType_t xDelay = ( TickType_t ) 200 / portTICK_PERIOD_MS; +const unsigned short usNumToProduce = 3; +const char * const pcTaskStartMsg = "Polled queue producer started.\r\n"; +const char * const pcTaskErrorMsg = "Could not post on polled queue.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The queue being used is passed in as the parameter. */ + pxQueue = ( QueueHandle_t * ) pvParameters; + + for( ;; ) + { + for( usLoop = 0; usLoop < usNumToProduce; ++usLoop ) + { + /* Send an incrementing number on the queue without blocking. */ + if( xQueueSendToBack( *pxQueue, ( void * ) &usValue, ( TickType_t ) 0 ) != pdPASS ) + { + /* We should never find the queue full - this is an error. */ + vPrintDisplayMessage( &pcTaskErrorMsg ); + sError = pdTRUE; + } + else + { + if( sError == pdFALSE ) + { + /* If an error has ever been recorded we stop incrementing the + check variable. */ + ++sPollingProducerCount; + } + + /* Update the value we are going to post next time around. */ + ++usValue; + } + } + + /* Wait before we start posting again to ensure the consumer runs and + empties the queue. */ + vTaskDelay( xDelay ); + } +} +/*-----------------------------------------------------------*/ + +static void vPolledQueueConsumer( void *pvParameters ) +{ +unsigned short usData, usExpectedValue = 0; +QueueHandle_t *pxQueue; +const TickType_t xDelay = ( TickType_t ) 200 / portTICK_PERIOD_MS; +const char * const pcTaskStartMsg = "Polled queue consumer started.\r\n"; +const char * const pcTaskErrorMsg = "Incorrect value received on polled queue.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The queue being used is passed in as the parameter. */ + pxQueue = ( QueueHandle_t * ) pvParameters; + + for( ;; ) + { + /* Loop until the queue is empty. */ + while( uxQueueMessagesWaiting( *pxQueue ) ) + { + if( xQueueReceive( *pxQueue, &usData, ( TickType_t ) 0 ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + /* This is not what we expected to receive so an error has + occurred. */ + vPrintDisplayMessage( &pcTaskErrorMsg ); + sError = pdTRUE; + /* Catch-up to the value we received so our next expected value + should again be correct. */ + usExpectedValue = usData; + } + else + { + if( sError == pdFALSE ) + { + /* Only increment the check variable if no errors have + occurred. */ + ++sPollingConsumerCount; + } + } + ++usExpectedValue; + } + } + + /* Now the queue is empty we block, allowing the producer to place more + items in the queue. */ + vTaskDelay( xDelay ); + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running with no errors. */ +portBASE_TYPE xArePollingQueuesStillRunning( void ) +{ +static short sLastPollingConsumerCount = 0, sLastPollingProducerCount = 0; +portBASE_TYPE xReturn; + + if( ( sLastPollingConsumerCount == sPollingConsumerCount ) || + ( sLastPollingProducerCount == sPollingProducerCount ) + ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + sLastPollingConsumerCount = sPollingConsumerCount; + sLastPollingProducerCount = sPollingProducerCount; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/comtest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/comtest.c new file mode 100644 index 0000000..92c1e5d --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/comtest.c @@ -0,0 +1,384 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Creates two tasks that operate on an interrupt driven serial port. A loopback + * connector should be used so that everything that is transmitted is also received. + * The serial port does not use any flow control. On a standard 9way 'D' connector + * pins two and three should be connected together. + * + * The first task repeatedly sends a string to a queue, character at a time. The + * serial port interrupt will empty the queue and transmit the characters. The + * task blocks for a pseudo random period before resending the string. + * + * The second task blocks on a queue waiting for a character to be received. + * Characters received by the serial port interrupt routine are posted onto the + * queue - unblocking the task making it ready to execute. If this is then the + * highest priority task ready to run it will run immediately - with a context + * switch occurring at the end of the interrupt service routine. The task + * receiving characters is spawned with a higher priority than the task + * transmitting the characters. + * + * With the loop back connector in place, one task will transmit a string and the + * other will immediately receive it. The receiving task knows the string it + * expects to receive so can detect an error. + * + * This also creates a third task. This is used to test semaphore usage from an + * ISR and does nothing interesting. + * + * \page ComTestC comtest.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V1.00: + + + The priority of the Rx task has been lowered. Received characters are + now processed (read from the queue) at the idle priority, allowing low + priority tasks to run evenly at times of a high communications overhead. + +Changes from V1.01: + + + The Tx task now waits a pseudo random time between transissions. + Previously a fixed period was used but this was not such a good test as + interrupts fired at regular intervals. + +Changes From V1.2.0: + + + Use vSerialPutString() instead of single character puts. + + Only stop the check variable incrementing after two consecutive errors. + +Changed from V1.2.5 + + + Made the Rx task 2 priorities higher than the Tx task. Previously it was + only 1. This is done to tie in better with the other demo application + tasks. + +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. + + Slight modification to task priorities. + +*/ + + +/* Scheduler include files. */ +#include +#include +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "serial.h" +#include "comtest.h" +#include "print.h" + +/* The Tx task will transmit the sequence of characters at a pseudo random +interval. This is the maximum and minimum block time between sends. */ +#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x15e ) +#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0xc8 ) + +#define comMAX_CONSECUTIVE_ERRORS ( 2 ) + +#define comSTACK_SIZE ( ( unsigned short ) 256 ) + +#define comRX_RELATIVE_PRIORITY ( 1 ) + +/* Handle to the com port used by both tasks. */ +static xComPortHandle xPort; + +/* The transmit function as described at the top of the file. */ +static void vComTxTask( void *pvParameters ); + +/* The receive function as described at the top of the file. */ +static void vComRxTask( void *pvParameters ); + +/* The semaphore test function as described at the top of the file. */ +static void vSemTestTask( void * pvParameters ); + +/* The string that is repeatedly transmitted. */ +const char * const pcMessageToExchange = "Send this message over and over again to check communications interrupts. " + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n"; + +/* Variables that are incremented on each cycle of each task. These are used to +check that both tasks are still executing. */ +volatile short sTxCount = 0, sRxCount = 0, sSemCount = 0; + +/* The handle to the semaphore test task. */ +static TaskHandle_t xSemTestTaskHandle = NULL; + +/*-----------------------------------------------------------*/ + +void vStartComTestTasks( unsigned portBASE_TYPE uxPriority, eCOMPort ePort, eBaud eBaudRate ) +{ +const unsigned portBASE_TYPE uxBufferLength = 255; + + /* Initialise the com port then spawn both tasks. */ + xPort = xSerialPortInit( ePort, eBaudRate, serNO_PARITY, serBITS_8, serSTOP_1, uxBufferLength ); + xTaskCreate( vComTxTask, "COMTx", comSTACK_SIZE, NULL, uxPriority, NULL ); + xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority + comRX_RELATIVE_PRIORITY, NULL ); + xTaskCreate( vSemTestTask, "ISRSem", comSTACK_SIZE, NULL, tskIDLE_PRIORITY, &xSemTestTaskHandle ); +} +/*-----------------------------------------------------------*/ + +static void vComTxTask( void *pvParameters ) +{ +const char * const pcTaskStartMsg = "COM Tx task started.\r\n"; +TickType_t xTimeToWait; + + /* Stop warnings. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + /* Send the string to the serial port. */ + vSerialPutString( xPort, pcMessageToExchange, strlen( pcMessageToExchange ) ); + + /* We have posted all the characters in the string - increment the variable + used to check that this task is still running, then wait before re-sending + the string. */ + sTxCount++; + + xTimeToWait = xTaskGetTickCount(); + + /* Make sure we don't wait too long... */ + xTimeToWait %= comTX_MAX_BLOCK_TIME; + + /* ...but we do want to wait. */ + if( xTimeToWait < comTX_MIN_BLOCK_TIME ) + { + xTimeToWait = comTX_MIN_BLOCK_TIME; + } + + vTaskDelay( xTimeToWait ); + } +} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */ +/*-----------------------------------------------------------*/ + +static void vComRxTask( void *pvParameters ) +{ +const char * const pcTaskStartMsg = "COM Rx task started.\r\n"; +const char * const pcTaskErrorMsg = "COM read error\r\n"; +const char * const pcTaskRestartMsg = "COM resynced\r\n"; +const char * const pcTaskTimeoutMsg = "COM Rx timed out\r\n"; +const TickType_t xBlockTime = ( TickType_t ) 0xffff / portTICK_PERIOD_MS; +const char *pcExpectedChar; +portBASE_TYPE xGotChar; +char cRxedChar; +short sResyncRequired, sConsecutiveErrors, sLatchedError; + + /* Stop warnings. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The first expected character is the first character in the string. */ + pcExpectedChar = pcMessageToExchange; + sResyncRequired = pdFALSE; + sConsecutiveErrors = 0; + sLatchedError = pdFALSE; + + for( ;; ) + { + /* Receive a message from the com port interrupt routine. If a message is + not yet available the call will block the task. */ + xGotChar = xSerialGetChar( xPort, &cRxedChar, xBlockTime ); + if( xGotChar == pdTRUE ) + { + if( sResyncRequired == pdTRUE ) + { + /* We got out of sequence and are waiting for the start of the next + transmission of the string. */ + if( cRxedChar == '\n' ) + { + /* This is the end of the message so we can start again - with + the first character in the string being the next thing we expect + to receive. */ + pcExpectedChar = pcMessageToExchange; + sResyncRequired = pdFALSE; + + /* Queue a message for printing to say that we are going to try + again. */ + vPrintDisplayMessage( &pcTaskRestartMsg ); + + /* Stop incrementing the check variable, if consecutive errors occur. */ + sConsecutiveErrors++; + if( sConsecutiveErrors >= comMAX_CONSECUTIVE_ERRORS ) + { + sLatchedError = pdTRUE; + } + } + } + else + { + /* We have received a character, but is it the expected character? */ + if( cRxedChar != *pcExpectedChar ) + { + /* This was not the expected character so post a message for + printing to say that an error has occurred. We will then wait + to resynchronise. */ + vPrintDisplayMessage( &pcTaskErrorMsg ); + sResyncRequired = pdTRUE; + } + else + { + /* This was the expected character so next time we will expect + the next character in the string. Wrap back to the beginning + of the string when the null terminator has been reached. */ + pcExpectedChar++; + if( *pcExpectedChar == '\0' ) + { + pcExpectedChar = pcMessageToExchange; + + /* We have got through the entire string without error. */ + sConsecutiveErrors = 0; + } + } + } + + /* Increment the count that is used to check that this task is still + running. This is only done if an error has never occurred. */ + if( sLatchedError == pdFALSE ) + { + sRxCount++; + } + } + else + { + vPrintDisplayMessage( &pcTaskTimeoutMsg ); + } + } +} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */ +/*-----------------------------------------------------------*/ + +static void vSemTestTask( void * pvParameters ) +{ +const char * const pcTaskStartMsg = "ISR Semaphore test started.\r\n"; +portBASE_TYPE xError = pdFALSE; + + /* Stop warnings. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + if( xSerialWaitForSemaphore( xPort ) ) + { + if( xError == pdFALSE ) + { + sSemCount++; + } + } + else + { + xError = pdTRUE; + } + } +} /*lint !e715 !e830 !e818 pvParameters not used but function prototype must be standard for task function. */ +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +portBASE_TYPE xAreComTestTasksStillRunning( void ) +{ +static short sLastTxCount = 0, sLastRxCount = 0, sLastSemCount = 0; +portBASE_TYPE xReturn; + + /* Not too worried about mutual exclusion on these variables as they are 16 + bits and we are only reading them. We also only care to see if they have + changed or not. */ + + if( ( sTxCount == sLastTxCount ) || ( sRxCount == sLastRxCount ) || ( sSemCount == sLastSemCount ) ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + sLastTxCount = sTxCount; + sLastRxCount = sRxCount; + sLastSemCount = sSemCount; + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vComTestUnsuspendTask( void ) +{ + /* The task that is suspended on the semaphore will be referenced from the + Suspended list as it is blocking indefinitely. This call just checks that + the kernel correctly detects this and does not attempt to unsuspend the + task. */ + xTaskResumeFromISR( xSemTestTaskHandle ); +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/death.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/death.c new file mode 100644 index 0000000..aaf4cff --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/death.c @@ -0,0 +1,241 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Create a single persistent task which periodically dynamically creates another + * four tasks. The original task is called the creator task, the four tasks it + * creates are called suicidal tasks. + * + * Two of the created suicidal tasks kill one other suicidal task before killing + * themselves - leaving just the original task remaining. + * + * The creator task must be spawned after all of the other demo application tasks + * as it keeps a check on the number of tasks under the scheduler control. The + * number of tasks it expects to see running should never be greater than the + * number of tasks that were in existence when the creator task was spawned, plus + * one set of four suicidal tasks. If this number is exceeded an error is flagged. + * + * \page DeathC death.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "death.h" +#include "print.h" + +#define deathSTACK_SIZE ( ( unsigned short ) 512 ) + +/* The task originally created which is responsible for periodically dynamically +creating another four tasks. */ +static void vCreateTasks( void *pvParameters ); + +/* The task function of the dynamically created tasks. */ +static void vSuicidalTask( void *pvParameters ); + +/* A variable which is incremented every time the dynamic tasks are created. This +is used to check that the task is still running. */ +static volatile short sCreationCount = 0; + +/* Used to store the number of tasks that were originally running so the creator +task can tell if any of the suicidal tasks have failed to die. */ +static volatile unsigned portBASE_TYPE uxTasksRunningAtStart = 0; +static const unsigned portBASE_TYPE uxMaxNumberOfExtraTasksRunning = 5; + +/* Used to store a handle to the tasks that should be killed by a suicidal task, +before it kills itself. */ +TaskHandle_t xCreatedTask1, xCreatedTask2; + +/*-----------------------------------------------------------*/ + +void vCreateSuicidalTasks( unsigned portBASE_TYPE uxPriority ) +{ +unsigned portBASE_TYPE *puxPriority; + + /* Create the Creator tasks - passing in as a parameter the priority at which + the suicidal tasks should be created. */ + puxPriority = ( unsigned portBASE_TYPE * ) pvPortMalloc( sizeof( unsigned portBASE_TYPE ) ); + *puxPriority = uxPriority; + + xTaskCreate( vCreateTasks, "CREATOR", deathSTACK_SIZE, ( void * ) puxPriority, uxPriority, NULL ); + + /* Record the number of tasks that are running now so we know if any of the + suicidal tasks have failed to be killed. */ + uxTasksRunningAtStart = uxTaskGetNumberOfTasks(); +} +/*-----------------------------------------------------------*/ + +static void vSuicidalTask( void *pvParameters ) +{ +portDOUBLE d1, d2; +TaskHandle_t xTaskToKill; +const TickType_t xDelay = ( TickType_t ) 500 / portTICK_PERIOD_MS; + + if( pvParameters != NULL ) + { + /* This task is periodically created four times. Tow created tasks are + passed a handle to the other task so it can kill it before killing itself. + The other task is passed in null. */ + xTaskToKill = *( TaskHandle_t* )pvParameters; + } + else + { + xTaskToKill = NULL; + } + + for( ;; ) + { + /* Do something random just to use some stack and registers. */ + d1 = 2.4; + d2 = 89.2; + d2 *= d1; + vTaskDelay( xDelay ); + + if( xTaskToKill != NULL ) + { + /* Make sure the other task has a go before we delete it. */ + vTaskDelay( ( TickType_t ) 0 ); + /* Kill the other task that was created by vCreateTasks(). */ + vTaskDelete( xTaskToKill ); + /* Kill ourselves. */ + vTaskDelete( NULL ); + } + } +}/*lint !e818 !e550 Function prototype must be as per standard for task functions. */ +/*-----------------------------------------------------------*/ + +static void vCreateTasks( void *pvParameters ) +{ +const TickType_t xDelay = ( TickType_t ) 1000 / portTICK_PERIOD_MS; +unsigned portBASE_TYPE uxPriority; +const char * const pcTaskStartMsg = "Create task started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + uxPriority = *( unsigned portBASE_TYPE * ) pvParameters; + vPortFree( pvParameters ); + + for( ;; ) + { + /* Just loop round, delaying then creating the four suicidal tasks. */ + vTaskDelay( xDelay ); + + xTaskCreate( vSuicidalTask, "SUICIDE1", deathSTACK_SIZE, NULL, uxPriority, &xCreatedTask1 ); + xTaskCreate( vSuicidalTask, "SUICIDE2", deathSTACK_SIZE, &xCreatedTask1, uxPriority, NULL ); + + xTaskCreate( vSuicidalTask, "SUICIDE1", deathSTACK_SIZE, NULL, uxPriority, &xCreatedTask2 ); + xTaskCreate( vSuicidalTask, "SUICIDE2", deathSTACK_SIZE, &xCreatedTask2, uxPriority, NULL ); + + ++sCreationCount; + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that the creator task is still running and that there +are not any more than four extra tasks. */ +portBASE_TYPE xIsCreateTaskStillRunning( void ) +{ +static short sLastCreationCount = 0; +short sReturn = pdTRUE; +unsigned portBASE_TYPE uxTasksRunningNow; + + if( sLastCreationCount == sCreationCount ) + { + sReturn = pdFALSE; + } + + uxTasksRunningNow = uxTaskGetNumberOfTasks(); + + if( uxTasksRunningNow < uxTasksRunningAtStart ) + { + sReturn = pdFALSE; + } + else if( ( uxTasksRunningNow - uxTasksRunningAtStart ) > uxMaxNumberOfExtraTasksRunning ) + { + sReturn = pdFALSE; + } + else + { + /* Everything is okay. */ + } + + return sReturn; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/dynamic.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/dynamic.c new file mode 100644 index 0000000..294743d --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/dynamic.c @@ -0,0 +1,616 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * The first test creates three tasks - two counter tasks (one continuous count + * and one limited count) and one controller. A "count" variable is shared + * between all three tasks. The two counter tasks should never be in a "ready" + * state at the same time. The controller task runs at the same priority as + * the continuous count task, and at a lower priority than the limited count + * task. + * + * One counter task loops indefinitely, incrementing the shared count variable + * on each iteration. To ensure it has exclusive access to the variable it + * raises it's priority above that of the controller task before each + * increment, lowering it again to it's original priority before starting the + * next iteration. + * + * The other counter task increments the shared count variable on each + * iteration of it's loop until the count has reached a limit of 0xff - at + * which point it suspends itself. It will not start a new loop until the + * controller task has made it "ready" again by calling vTaskResume (). + * This second counter task operates at a higher priority than controller + * task so does not need to worry about mutual exclusion of the counter + * variable. + * + * The controller task is in two sections. The first section controls and + * monitors the continuous count task. When this section is operational the + * limited count task is suspended. Likewise, the second section controls + * and monitors the limited count task. When this section is operational the + * continuous count task is suspended. + * + * In the first section the controller task first takes a copy of the shared + * count variable. To ensure mutual exclusion on the count variable it + * suspends the continuous count task, resuming it again when the copy has been + * taken. The controller task then sleeps for a fixed period - during which + * the continuous count task will execute and increment the shared variable. + * When the controller task wakes it checks that the continuous count task + * has executed by comparing the copy of the shared variable with its current + * value. This time, to ensure mutual exclusion, the scheduler itself is + * suspended with a call to vTaskSuspendAll (). This is for demonstration + * purposes only and is not a recommended technique due to its inefficiency. + * + * After a fixed number of iterations the controller task suspends the + * continuous count task, and moves on to its second section. + * + * At the start of the second section the shared variable is cleared to zero. + * The limited count task is then woken from it's suspension by a call to + * vTaskResume (). As this counter task operates at a higher priority than + * the controller task the controller task should not run again until the + * shared variable has been counted up to the limited value causing the counter + * task to suspend itself. The next line after vTaskResume () is therefore + * a check on the shared variable to ensure everything is as expected. + * + * + * The second test consists of a couple of very simple tasks that post onto a + * queue while the scheduler is suspended. This test was added to test parts + * of the scheduler not exercised by the first test. + * + * + * The final set of two tasks implements a third test. This simply raises the + * priority of a task while the scheduler is suspended. Again this test was + * added to exercise parts of the code not covered by the first test. + * + * \page Priorities dynamic.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. + + Added a second, simple test that uses the functions + vQueueReceiveWhenSuspendedTask() and vQueueSendWhenSuspendedTask(). + +Changes from V3.1.1 + + + Added a third simple test that uses the vTaskPrioritySet() function + while the scheduler is suspended. + + Modified the controller task slightly to test the calling of + vTaskResumeAll() while the scheduler is suspended. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "dynamic.h" +#include "print.h" + +/* Function that implements the "limited count" task as described above. */ +static void vLimitedIncrementTask( void * pvParameters ); + +/* Function that implements the "continuous count" task as described above. */ +static void vContinuousIncrementTask( void * pvParameters ); + +/* Function that implements the controller task as described above. */ +static void vCounterControlTask( void * pvParameters ); + +/* The simple test functions that check sending and receiving while the +scheduler is suspended. */ +static void vQueueReceiveWhenSuspendedTask( void *pvParameters ); +static void vQueueSendWhenSuspendedTask( void *pvParameters ); + +/* The simple test functions that check raising and lowering of task priorities +while the scheduler is suspended. */ +static void prvChangePriorityWhenSuspendedTask( void *pvParameters ); +static void prvChangePriorityHelperTask( void *pvParameters ); + + +/* Demo task specific constants. */ +#define priSTACK_SIZE ( ( unsigned short ) configMINIMAL_STACK_SIZE ) +#define priSLEEP_TIME ( ( TickType_t ) 50 ) +#define priLOOPS ( 5 ) +#define priMAX_COUNT ( ( unsigned long ) 0xff ) +#define priNO_BLOCK ( ( TickType_t ) 0 ) +#define priSUSPENDED_QUEUE_LENGTH ( 1 ) + +/*-----------------------------------------------------------*/ + +/* Handles to the two counter tasks. These could be passed in as parameters +to the controller task to prevent them having to be file scope. */ +static TaskHandle_t xContinuousIncrementHandle, xLimitedIncrementHandle, xChangePriorityWhenSuspendedHandle; + +/* The shared counter variable. This is passed in as a parameter to the two +counter variables for demonstration purposes. */ +static unsigned long ulCounter; + +/* Variable used in a similar way by the test that checks the raising and +lowering of task priorities while the scheduler is suspended. */ +static unsigned long ulPrioritySetCounter; + +/* Variables used to check that the tasks are still operating without error. +Each complete iteration of the controller task increments this variable +provided no errors have been found. The variable maintaining the same value +is therefore indication of an error. */ +static unsigned short usCheckVariable = ( unsigned short ) 0; +static portBASE_TYPE xSuspendedQueueSendError = pdFALSE; +static portBASE_TYPE xSuspendedQueueReceiveError = pdFALSE; +static portBASE_TYPE xPriorityRaiseWhenSuspendedError = pdFALSE; + +/* Queue used by the second test. */ +QueueHandle_t xSuspendedTestQueue; + +/*-----------------------------------------------------------*/ +/* + * Start the seven tasks as described at the top of the file. + * Note that the limited count task is given a higher priority. + */ +void vStartDynamicPriorityTasks( void ) +{ + xSuspendedTestQueue = xQueueCreate( priSUSPENDED_QUEUE_LENGTH, sizeof( unsigned long ) ); + xTaskCreate( vContinuousIncrementTask, "CONT_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY, &xContinuousIncrementHandle ); + xTaskCreate( vLimitedIncrementTask, "LIM_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY + 1, &xLimitedIncrementHandle ); + xTaskCreate( vCounterControlTask, "C_CTRL", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vQueueSendWhenSuspendedTask, "SUSP_SEND", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vQueueReceiveWhenSuspendedTask, "SUSP_RECV", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + xTaskCreate( prvChangePriorityWhenSuspendedTask, "1st_P_CHANGE", priSTACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL ); + xTaskCreate( prvChangePriorityHelperTask, "2nd_P_CHANGE", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, &xChangePriorityWhenSuspendedHandle ); +} +/*-----------------------------------------------------------*/ + +/* + * Just loops around incrementing the shared variable until the limit has been + * reached. Once the limit has been reached it suspends itself. + */ +static void vLimitedIncrementTask( void * pvParameters ) +{ +unsigned long *pulCounter; + + /* Take a pointer to the shared variable from the parameters passed into + the task. */ + pulCounter = ( unsigned long * ) pvParameters; + + /* This will run before the control task, so the first thing it does is + suspend - the control task will resume it when ready. */ + vTaskSuspend( NULL ); + + for( ;; ) + { + /* Just count up to a value then suspend. */ + ( *pulCounter )++; + + if( *pulCounter >= priMAX_COUNT ) + { + vTaskSuspend( NULL ); + } + } +} +/*-----------------------------------------------------------*/ + +/* + * Just keep counting the shared variable up. The control task will suspend + * this task when it wants. + */ +static void vContinuousIncrementTask( void * pvParameters ) +{ +unsigned long *pulCounter; +unsigned portBASE_TYPE uxOurPriority; + + /* Take a pointer to the shared variable from the parameters passed into + the task. */ + pulCounter = ( unsigned long * ) pvParameters; + + /* Query our priority so we can raise it when exclusive access to the + shared variable is required. */ + uxOurPriority = uxTaskPriorityGet( NULL ); + + for( ;; ) + { + /* Raise our priority above the controller task to ensure a context + switch does not occur while we are accessing this variable. */ + vTaskPrioritySet( NULL, uxOurPriority + 1 ); + ( *pulCounter )++; + vTaskPrioritySet( NULL, uxOurPriority ); + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +/* + * Controller task as described above. + */ +static void vCounterControlTask( void * pvParameters ) +{ +unsigned long ulLastCounter; +short sLoops; +short sError = pdFALSE; +const char * const pcTaskStartMsg = "Priority manipulation tasks started.\r\n"; +const char * const pcTaskFailMsg = "Priority manipulation Task Failed\r\n"; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + /* Start with the counter at zero. */ + ulCounter = ( unsigned long ) 0; + + /* First section : */ + + /* Check the continuous count task is running. */ + for( sLoops = 0; sLoops < priLOOPS; sLoops++ ) + { + /* Suspend the continuous count task so we can take a mirror of the + shared variable without risk of corruption. */ + vTaskSuspend( xContinuousIncrementHandle ); + ulLastCounter = ulCounter; + vTaskResume( xContinuousIncrementHandle ); + + /* Now delay to ensure the other task has processor time. */ + vTaskDelay( priSLEEP_TIME ); + + /* Check the shared variable again. This time to ensure mutual + exclusion the whole scheduler will be locked. This is just for + demo purposes! */ + vTaskSuspendAll(); + { + if( ulLastCounter == ulCounter ) + { + /* The shared variable has not changed. There is a problem + with the continuous count task so flag an error. */ + sError = pdTRUE; + xTaskResumeAll(); + vPrintDisplayMessage( &pcTaskFailMsg ); + vTaskSuspendAll(); + } + } + xTaskResumeAll(); + } + + + /* Second section: */ + + /* Suspend the continuous counter task so it stops accessing the shared variable. */ + vTaskSuspend( xContinuousIncrementHandle ); + + /* Reset the variable. */ + ulCounter = ( unsigned long ) 0; + + /* Resume the limited count task which has a higher priority than us. + We should therefore not return from this call until the limited count + task has suspended itself with a known value in the counter variable. + The scheduler suspension is not necessary but is included for test + purposes. */ + vTaskSuspendAll(); + vTaskResume( xLimitedIncrementHandle ); + xTaskResumeAll(); + + /* Does the counter variable have the expected value? */ + if( ulCounter != priMAX_COUNT ) + { + sError = pdTRUE; + vPrintDisplayMessage( &pcTaskFailMsg ); + } + + if( sError == pdFALSE ) + { + /* If no errors have occurred then increment the check variable. */ + portENTER_CRITICAL(); + usCheckVariable++; + portEXIT_CRITICAL(); + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Resume the continuous count task and do it all again. */ + vTaskResume( xContinuousIncrementHandle ); + } +} +/*-----------------------------------------------------------*/ + +static void vQueueSendWhenSuspendedTask( void *pvParameters ) +{ +static unsigned long ulValueToSend = ( unsigned long ) 0; +const char * const pcTaskStartMsg = "Queue send while suspended task started.\r\n"; +const char * const pcTaskFailMsg = "Queue send while suspended failed.\r\n"; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + vTaskSuspendAll(); + { + /* We must not block while the scheduler is suspended! */ + if( xQueueSend( xSuspendedTestQueue, ( void * ) &ulValueToSend, priNO_BLOCK ) != pdTRUE ) + { + if( xSuspendedQueueSendError == pdFALSE ) + { + xTaskResumeAll(); + vPrintDisplayMessage( &pcTaskFailMsg ); + vTaskSuspendAll(); + } + + xSuspendedQueueSendError = pdTRUE; + } + } + xTaskResumeAll(); + + vTaskDelay( priSLEEP_TIME ); + + ++ulValueToSend; + } +} +/*-----------------------------------------------------------*/ + +static void vQueueReceiveWhenSuspendedTask( void *pvParameters ) +{ +static unsigned long ulExpectedValue = ( unsigned long ) 0, ulReceivedValue; +const char * const pcTaskStartMsg = "Queue receive while suspended task started.\r\n"; +const char * const pcTaskFailMsg = "Queue receive while suspended failed.\r\n"; +portBASE_TYPE xGotValue; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + do + { + /* Suspending the scheduler here is fairly pointless and + undesirable for a normal application. It is done here purely + to test the scheduler. The inner xTaskResumeAll() should + never return pdTRUE as the scheduler is still locked by the + outer call. */ + vTaskSuspendAll(); + { + vTaskSuspendAll(); + { + xGotValue = xQueueReceive( xSuspendedTestQueue, ( void * ) &ulReceivedValue, priNO_BLOCK ); + } + if( xTaskResumeAll() ) + { + xSuspendedQueueReceiveError = pdTRUE; + } + } + xTaskResumeAll(); + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + } while( xGotValue == pdFALSE ); + + if( ulReceivedValue != ulExpectedValue ) + { + if( xSuspendedQueueReceiveError == pdFALSE ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + } + xSuspendedQueueReceiveError = pdTRUE; + } + + ++ulExpectedValue; + } +} +/*-----------------------------------------------------------*/ + +static void prvChangePriorityWhenSuspendedTask( void *pvParameters ) +{ +const char * const pcTaskStartMsg = "Priority change when suspended task started.\r\n"; +const char * const pcTaskFailMsg = "Priority change when suspended task failed.\r\n"; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + /* Start with the counter at 0 so we know what the counter should be + when we check it next. */ + ulPrioritySetCounter = ( unsigned long ) 0; + + /* Resume the helper task. At this time it has a priority lower than + ours so no context switch should occur. */ + vTaskResume( xChangePriorityWhenSuspendedHandle ); + + /* Check to ensure the task just resumed has not executed. */ + portENTER_CRITICAL(); + { + if( ulPrioritySetCounter != ( unsigned long ) 0 ) + { + xPriorityRaiseWhenSuspendedError = pdTRUE; + vPrintDisplayMessage( &pcTaskFailMsg ); + } + } + portEXIT_CRITICAL(); + + /* Now try raising the priority while the scheduler is suspended. */ + vTaskSuspendAll(); + { + vTaskPrioritySet( xChangePriorityWhenSuspendedHandle, ( configMAX_PRIORITIES - 1 ) ); + + /* Again, even though the helper task has a priority greater than + ours, it should not have executed yet because the scheduler is + suspended. */ + portENTER_CRITICAL(); + { + if( ulPrioritySetCounter != ( unsigned long ) 0 ) + { + xPriorityRaiseWhenSuspendedError = pdTRUE; + vPrintDisplayMessage( &pcTaskFailMsg ); + } + } + portEXIT_CRITICAL(); + } + xTaskResumeAll(); + + /* Now the scheduler has been resumed the helper task should + immediately preempt us and execute. When it executes it will increment + the ulPrioritySetCounter exactly once before suspending itself. + + We should now always find the counter set to 1. */ + portENTER_CRITICAL(); + { + if( ulPrioritySetCounter != ( unsigned long ) 1 ) + { + xPriorityRaiseWhenSuspendedError = pdTRUE; + vPrintDisplayMessage( &pcTaskFailMsg ); + } + } + portEXIT_CRITICAL(); + + /* Delay until we try this again. */ + vTaskDelay( priSLEEP_TIME * 2 ); + + /* Set the priority of the helper task back ready for the next + execution of this task. */ + vTaskSuspendAll(); + vTaskPrioritySet( xChangePriorityWhenSuspendedHandle, tskIDLE_PRIORITY ); + xTaskResumeAll(); + } +} +/*-----------------------------------------------------------*/ + +static void prvChangePriorityHelperTask( void *pvParameters ) +{ + /* Just to stop warning messages. */ + ( void ) pvParameters; + + for( ;; ) + { + /* This is the helper task for prvChangePriorityWhenSuspendedTask(). + It has it's priority raised and lowered. When it runs it simply + increments the counter then suspends itself again. This allows + prvChangePriorityWhenSuspendedTask() to know how many times it has + executed. */ + ulPrioritySetCounter++; + vTaskSuspend( NULL ); + } +} +/*-----------------------------------------------------------*/ + +/* Called to check that all the created tasks are still running without error. */ +portBASE_TYPE xAreDynamicPriorityTasksStillRunning( void ) +{ +/* Keep a history of the check variables so we know if it has been incremented +since the last call. */ +static unsigned short usLastTaskCheck = ( unsigned short ) 0; +portBASE_TYPE xReturn = pdTRUE; + + /* Check the tasks are still running by ensuring the check variable + is still incrementing. */ + + if( usCheckVariable == usLastTaskCheck ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + if( xSuspendedQueueSendError == pdTRUE ) + { + xReturn = pdFALSE; + } + + if( xSuspendedQueueReceiveError == pdTRUE ) + { + xReturn = pdFALSE; + } + + if( xPriorityRaiseWhenSuspendedError == pdTRUE ) + { + xReturn = pdFALSE; + } + + usLastTaskCheck = usCheckVariable; + return xReturn; +} + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/events.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/events.c new file mode 100644 index 0000000..307568e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/events.c @@ -0,0 +1,406 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * This file exercises the event mechanism whereby more than one task is + * blocked waiting for the same event. + * + * The demo creates five tasks - four 'event' tasks, and a controlling task. + * The event tasks have various different priorities and all block on reading + * the same queue. The controlling task writes data to the queue, then checks + * to see which of the event tasks read the data from the queue. The + * controlling task has the lowest priority of all the tasks so is guaranteed + * to always get preempted immediately upon writing to the queue. + * + * By selectively suspending and resuming the event tasks the controlling task + * can check that the highest priority task that is blocked on the queue is the + * task that reads the posted data from the queue. + * + * Two of the event tasks share the same priority. When neither of these tasks + * are suspended they should alternate - one reading one message from the queue, + * the other the next message, etc. + */ + +/* Standard includes. */ +#include +#include +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "mevents.h" +#include "print.h" + +/* Demo specific constants. */ +#define evtSTACK_SIZE ( ( unsigned portBASE_TYPE ) configMINIMAL_STACK_SIZE ) +#define evtNUM_TASKS ( 4 ) +#define evtQUEUE_LENGTH ( ( unsigned portBASE_TYPE ) 3 ) +#define evtNO_DELAY 0 + +/* Just indexes used to uniquely identify the tasks. Note that two tasks are +'highest' priority. */ +#define evtHIGHEST_PRIORITY_INDEX_2 3 +#define evtHIGHEST_PRIORITY_INDEX_1 2 +#define evtMEDIUM_PRIORITY_INDEX 1 +#define evtLOWEST_PRIORITY_INDEX 0 + +/* Each event task increments one of these counters each time it reads data +from the queue. */ +static volatile portBASE_TYPE xTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 }; + +/* Each time the controlling task posts onto the queue it increments the +expected count of the task that it expected to read the data from the queue +(i.e. the task with the highest priority that should be blocked on the queue). + +xExpectedTaskCounters are incremented from the controlling task, and +xTaskCounters are incremented from the individual event tasks - therefore +comparing xTaskCounters to xExpectedTaskCounters shows whether or not the +correct task was unblocked by the post. */ +static portBASE_TYPE xExpectedTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 }; + +/* Handles to the four event tasks. These are required to suspend and resume +the tasks. */ +static TaskHandle_t xCreatedTasks[ evtNUM_TASKS ]; + +/* The single queue onto which the controlling task posts, and the four event +tasks block. */ +static QueueHandle_t xQueue; + +/* Flag used to indicate whether or not an error has occurred at any time. +An error is either the queue being full when not expected, or an unexpected +task reading data from the queue. */ +static portBASE_TYPE xHealthStatus = pdPASS; + +/*-----------------------------------------------------------*/ + +/* Function that implements the event task. This is created four times. */ +static void prvMultiEventTask( void *pvParameters ); + +/* Function that implements the controlling task. */ +static void prvEventControllerTask( void *pvParameters ); + +/* This is a utility function that posts data to the queue, then compares +xExpectedTaskCounters with xTaskCounters to ensure everything worked as +expected. + +The event tasks all have higher priorities the controlling task. Therefore +the controlling task will always get preempted between writhing to the queue +and checking the task counters. + +@param xExpectedTask The index to the task that the controlling task thinks + should be the highest priority task waiting for data, and + therefore the task that will unblock. + +@param xIncrement The number of items that should be written to the queue. +*/ +static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement ); + +/* This is just incremented each cycle of the controlling tasks function so +the main application can ensure the test is still running. */ +static portBASE_TYPE xCheckVariable = 0; + +/*-----------------------------------------------------------*/ + +void vStartMultiEventTasks( void ) +{ + /* Create the queue to be used for all the communications. */ + xQueue = xQueueCreate( evtQUEUE_LENGTH, ( unsigned portBASE_TYPE ) sizeof( unsigned portBASE_TYPE ) ); + + /* Start the controlling task. This has the idle priority to ensure it is + always preempted by the event tasks. */ + xTaskCreate( prvEventControllerTask, "EvntCTRL", evtSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + + /* Start the four event tasks. Note that two have priority 3, one + priority 2 and the other priority 1. */ + xTaskCreate( prvMultiEventTask, "Event0", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 0 ] ), 1, &( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ) ); + xTaskCreate( prvMultiEventTask, "Event1", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 1 ] ), 2, &( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ) ); + xTaskCreate( prvMultiEventTask, "Event2", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 2 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ) ); + xTaskCreate( prvMultiEventTask, "Event3", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 3 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ) ); +} +/*-----------------------------------------------------------*/ + +static void prvMultiEventTask( void *pvParameters ) +{ +portBASE_TYPE *pxCounter; +unsigned portBASE_TYPE uxDummy; +const char * const pcTaskStartMsg = "Multi event task started.\r\n"; + + /* The variable this task will increment is passed in as a parameter. */ + pxCounter = ( portBASE_TYPE * ) pvParameters; + + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + /* Block on the queue. */ + if( xQueueReceive( xQueue, &uxDummy, portMAX_DELAY ) ) + { + /* We unblocked by reading the queue - so simply increment + the counter specific to this task instance. */ + ( *pxCounter )++; + } + else + { + xHealthStatus = pdFAIL; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvEventControllerTask( void *pvParameters ) +{ +const char * const pcTaskStartMsg = "Multi event controller task started.\r\n"; +portBASE_TYPE xDummy = 0; + + /* Just to stop warnings. */ + ( void ) pvParameters; + + vPrintDisplayMessage( &pcTaskStartMsg ); + + for( ;; ) + { + /* All tasks are blocked on the queue. When a message is posted one of + the two tasks that share the highest priority should unblock to read + the queue. The next message written should unblock the other task with + the same high priority, and so on in order. No other task should + unblock to read data as they have lower priorities. */ + + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + + /* For the rest of these tests we don't need the second 'highest' + priority task - so it is suspended. */ + vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ); + + + + /* Now suspend the other highest priority task. The medium priority + task will then be the task with the highest priority that remains + blocked on the queue. */ + vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + + /* This time, when we post onto the queue we will expect the medium + priority task to unblock and preempt us. */ + prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 ); + + /* Now try resuming the highest priority task while the scheduler is + suspended. The task should start executing as soon as the scheduler + is resumed - therefore when we post to the queue again, the highest + priority task should again preempt us. */ + vTaskSuspendAll(); + vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + xTaskResumeAll(); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + + /* Now we are going to suspend the high and medium priority tasks. The + low priority task should then preempt us. Again the task suspension is + done with the whole scheduler suspended just for test purposes. */ + vTaskSuspendAll(); + vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ); + xTaskResumeAll(); + prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 ); + + /* Do the same basic test another few times - selectively suspending + and resuming tasks and each time calling prvCheckTaskCounters() passing + to the function the number of the task we expected to be unblocked by + the post. */ + + vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + + vTaskSuspendAll(); /* Just for test. */ + vTaskSuspendAll(); /* Just for test. */ + vTaskSuspendAll(); /* Just for even more test. */ + vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + xTaskResumeAll(); + xTaskResumeAll(); + xTaskResumeAll(); + prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 ); + + vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ); + prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 ); + + vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 ); + + /* Now a slight change, first suspend all tasks. */ + vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ); + vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ); + + /* Now when we resume the low priority task and write to the queue 3 + times. We expect the low priority task to service the queue three + times. */ + vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ); + prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, evtQUEUE_LENGTH ); + + /* Again suspend all tasks (only the low priority task is not suspended + already). */ + vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ); + + /* This time we are going to suspend the scheduler, resume the low + priority task, then resume the high priority task. In this state we + will write to the queue three times. When the scheduler is resumed + we expect the high priority task to service all three messages. */ + vTaskSuspendAll(); + { + vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ); + vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ); + + for( xDummy = 0; xDummy < evtQUEUE_LENGTH; xDummy++ ) + { + if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE ) + { + xHealthStatus = pdFAIL; + } + } + + /* The queue should not have been serviced yet!. The scheduler + is still suspended. */ + if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) ) + { + xHealthStatus = pdFAIL; + } + } + xTaskResumeAll(); + + /* We should have been preempted by resuming the scheduler - so by the + time we are running again we expect the high priority task to have + removed three items from the queue. */ + xExpectedTaskCounters[ evtHIGHEST_PRIORITY_INDEX_1 ] += evtQUEUE_LENGTH; + if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) ) + { + xHealthStatus = pdFAIL; + } + + /* The medium priority and second high priority tasks are still + suspended. Make sure to resume them before starting again. */ + vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ); + vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ); + + /* Just keep incrementing to show the task is still executing. */ + xCheckVariable++; + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement ) +{ +portBASE_TYPE xDummy = 0; + + /* Write to the queue the requested number of times. The data written is + not important. */ + for( xDummy = 0; xDummy < xIncrement; xDummy++ ) + { + if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE ) + { + /* Did not expect to ever find the queue full. */ + xHealthStatus = pdFAIL; + } + } + + /* All the tasks blocked on the queue have a priority higher than the + controlling task. Writing to the queue will therefore have caused this + task to be preempted. By the time this line executes the event task will + have executed and incremented its counter. Increment the expected counter + to the same value. */ + ( xExpectedTaskCounters[ xExpectedTask ] ) += xIncrement; + + /* Check the actual counts and expected counts really are the same. */ + if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) ) + { + /* The counters were not the same. This means a task we did not expect + to unblock actually did unblock. */ + xHealthStatus = pdFAIL; + } +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xAreMultiEventTasksStillRunning( void ) +{ +static portBASE_TYPE xPreviousCheckVariable = 0; + + /* Called externally to periodically check that this test is still + operational. */ + + if( xPreviousCheckVariable == xCheckVariable ) + { + xHealthStatus = pdFAIL; + } + + xPreviousCheckVariable = xCheckVariable; + + return xHealthStatus; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flash.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flash.c new file mode 100644 index 0000000..db94755 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flash.c @@ -0,0 +1,166 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/** + * Creates eight tasks, each of which flash an LED at a different rate. The first + * LED flashes every 125ms, the second every 250ms, the third every 375ms, etc. + * + * The LED flash tasks provide instant visual feedback. They show that the scheduler + * is still operational. + * + * The PC port uses the standard parallel port for outputs, the Flashlite 186 port + * uses IO port F. + * + * \page flashC flash.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. + +Changes from V2.1.1 + + + The stack size now uses configMINIMAL_STACK_SIZE. + + String constants made file scope to decrease stack depth on 8051 port. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "partest.h" +#include "flash.h" +#include "print.h" + +#define ledSTACK_SIZE configMINIMAL_STACK_SIZE + +/* Structure used to pass parameters to the LED tasks. */ +typedef struct LED_PARAMETERS +{ + unsigned portBASE_TYPE uxLED; /*< The output the task should use. */ + TickType_t xFlashRate; /*< The rate at which the LED should flash. */ +} xLEDParameters; + +/* The task that is created eight times - each time with a different xLEDParaemtes +structure passed in as the parameter. */ +static void vLEDFlashTask( void *pvParameters ); + +/* String to print if USE_STDIO is defined. */ +const char * const pcTaskStartMsg = "LED flash task started.\r\n"; + +/*-----------------------------------------------------------*/ + +void vStartLEDFlashTasks( unsigned portBASE_TYPE uxPriority ) +{ +unsigned portBASE_TYPE uxLEDTask; +xLEDParameters *pxLEDParameters; +const unsigned portBASE_TYPE uxNumOfLEDs = 8; +const TickType_t xFlashRate = 125; + + /* Create the eight tasks. */ + for( uxLEDTask = 0; uxLEDTask < uxNumOfLEDs; ++uxLEDTask ) + { + /* Create and complete the structure used to pass parameters to the next + created task. */ + pxLEDParameters = ( xLEDParameters * ) pvPortMalloc( sizeof( xLEDParameters ) ); + pxLEDParameters->uxLED = uxLEDTask; + pxLEDParameters->xFlashRate = ( xFlashRate + ( xFlashRate * ( TickType_t ) uxLEDTask ) ); + pxLEDParameters->xFlashRate /= portTICK_PERIOD_MS; + + /* Spawn the task. */ + xTaskCreate( vLEDFlashTask, "LEDx", ledSTACK_SIZE, ( void * ) pxLEDParameters, uxPriority, ( TaskHandle_t * ) NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void vLEDFlashTask( void *pvParameters ) +{ +xLEDParameters *pxParameters; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + pxParameters = ( xLEDParameters * ) pvParameters; + + for(;;) + { + /* Delay for half the flash period then turn the LED on. */ + vTaskDelay( pxParameters->xFlashRate / ( TickType_t ) 2 ); + vParTestToggleLED( pxParameters->uxLED ); + + /* Delay for half the flash period then turn the LED off. */ + vTaskDelay( pxParameters->xFlashRate / ( TickType_t ) 2 ); + vParTestToggleLED( pxParameters->uxLED ); + } +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flop.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flop.c new file mode 100644 index 0000000..c516ff6 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/flop.c @@ -0,0 +1,369 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* +Changes from V1.2.3 + + + The created tasks now include calls to tskYIELD(), allowing them to be used + with the cooperative scheduler. +*/ + +/** + * Creates eight tasks, each of which loops continuously performing an (emulated) + * floating point calculation. + * + * All the tasks run at the idle priority and never block or yield. This causes + * all eight tasks to time slice with the idle task. Running at the idle priority + * means that these tasks will get pre-empted any time another task is ready to run + * or a time slice occurs. More often than not the pre-emption will occur mid + * calculation, creating a good test of the schedulers context switch mechanism - a + * calculation producing an unexpected result could be a symptom of a corruption in + * the context of a task. + * + * \page FlopC flop.c + * \ingroup DemoFiles + *
+ */ + +#include +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "print.h" + +/* Demo program include files. */ +#include "flop.h" + +#define mathSTACK_SIZE ( ( unsigned short ) 512 ) +#define mathNUMBER_OF_TASKS ( 8 ) + +/* Four tasks, each of which performs a different floating point calculation. +Each of the four is created twice. */ +static void vCompetingMathTask1( void *pvParameters ); +static void vCompetingMathTask2( void *pvParameters ); +static void vCompetingMathTask3( void *pvParameters ); +static void vCompetingMathTask4( void *pvParameters ); + +/* These variables are used to check that all the tasks are still running. If a +task gets a calculation wrong it will +stop incrementing its check variable. */ +static volatile unsigned short usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( unsigned short ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartMathTasks( unsigned portBASE_TYPE uxPriority ) +{ + xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask1, "Math5", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask2, "Math6", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask3, "Math7", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask4, "Math8", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL ); +} +/*-----------------------------------------------------------*/ + +static void vCompetingMathTask1( void *pvParameters ) +{ +portDOUBLE d1, d2, d3, d4; +volatile unsigned short *pusTaskCheckVariable; +const portDOUBLE dAnswer = ( 123.4567 + 2345.6789 ) * -918.222; +const char * const pcTaskStartMsg = "Math task 1 started.\r\n"; +const char * const pcTaskFailMsg = "Math task 1 failed.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for(;;) + { + d1 = 123.4567; + d2 = 2345.6789; + d3 = -918.222; + + d4 = ( d1 + d2 ) * d3; + + taskYIELD(); + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( d4 - dAnswer ) > 0.001 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + + taskYIELD(); + } +} +/*-----------------------------------------------------------*/ + +static void vCompetingMathTask2( void *pvParameters ) +{ +portDOUBLE d1, d2, d3, d4; +volatile unsigned short *pusTaskCheckVariable; +const portDOUBLE dAnswer = ( -389.38 / 32498.2 ) * -2.0001; +const char * const pcTaskStartMsg = "Math task 2 started.\r\n"; +const char * const pcTaskFailMsg = "Math task 2 failed.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for( ;; ) + { + d1 = -389.38; + d2 = 32498.2; + d3 = -2.0001; + + d4 = ( d1 / d2 ) * d3; + + taskYIELD(); + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( d4 - dAnswer ) > 0.001 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know + this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + + taskYIELD(); + } +} +/*-----------------------------------------------------------*/ + +static void vCompetingMathTask3( void *pvParameters ) +{ +portDOUBLE *pdArray, dTotal1, dTotal2, dDifference; +volatile unsigned short *pusTaskCheckVariable; +const unsigned short usArraySize = 250; +unsigned short usPosition; +const char * const pcTaskStartMsg = "Math task 3 started.\r\n"; +const char * const pcTaskFailMsg = "Math task 3 failed.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + pdArray = ( portDOUBLE * ) pvPortMalloc( ( size_t ) 250 * sizeof( portDOUBLE ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + dTotal1 = 0.0; + dTotal2 = 0.0; + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + pdArray[ usPosition ] = ( portDOUBLE ) usPosition + 5.5; + dTotal1 += ( portDOUBLE ) usPosition + 5.5; + } + + taskYIELD(); + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + dTotal2 += pdArray[ usPosition ]; + } + + dDifference = dTotal1 - dTotal2; + if( fabs( dDifference ) > 0.001 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + taskYIELD(); + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +static void vCompetingMathTask4( void *pvParameters ) +{ +portDOUBLE *pdArray, dTotal1, dTotal2, dDifference; +volatile unsigned short *pusTaskCheckVariable; +const unsigned short usArraySize = 250; +unsigned short usPosition; +const char * const pcTaskStartMsg = "Math task 4 started.\r\n"; +const char * const pcTaskFailMsg = "Math task 4 failed.\r\n"; +short sError = pdFALSE; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + pdArray = ( portDOUBLE * ) pvPortMalloc( ( size_t ) 250 * sizeof( portDOUBLE ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + dTotal1 = 0.0; + dTotal2 = 0.0; + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + pdArray[ usPosition ] = ( portDOUBLE ) usPosition * 12.123; + dTotal1 += ( portDOUBLE ) usPosition * 12.123; + } + + taskYIELD(); + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + dTotal2 += pdArray[ usPosition ]; + } + + dDifference = dTotal1 - dTotal2; + if( fabs( dDifference ) > 0.001 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + taskYIELD(); + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +portBASE_TYPE xAreMathsTaskStillRunning( void ) +{ +/* Keep a history of the check variables so we know if they have been incremented +since the last call. */ +static unsigned short usLastTaskCheck[ mathNUMBER_OF_TASKS ] = { ( unsigned short ) 0 }; +portBASE_TYPE xReturn = pdTRUE, xTask; + + /* Check the maths tasks are still running by ensuring their check variables + are still incrementing. */ + for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ ) + { + if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ]; + } + + return xReturn; +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/integer.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/integer.c new file mode 100644 index 0000000..8a16e04 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/integer.c @@ -0,0 +1,365 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* +Changes from V1.2.3 + + + The created tasks now include calls to tskYIELD(), allowing them to be used + with the cooperative scheduler. +*/ + +/** + * This does the same as flop. c, but uses variables of type long instead of + * type double. + * + * As with flop. c, the tasks created in this file are a good test of the + * scheduler context switch mechanism. The processor has to access 32bit + * variables in two or four chunks (depending on the processor). The low + * priority of these tasks means there is a high probability that a context + * switch will occur mid calculation. See the flop. c documentation for + * more information. + * + * \page IntegerC integer.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V1.2.1 + + + The constants used in the calculations are larger to ensure the + optimiser does not truncate them to 16 bits. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "print.h" + +/* Demo program include files. */ +#include "integer.h" + +#define intgSTACK_SIZE ( ( unsigned short ) 256 ) +#define intgNUMBER_OF_TASKS ( 8 ) + +/* Four tasks, each of which performs a different calculation on four byte +variables. Each of the four is created twice. */ +static void vCompeteingIntMathTask1( void *pvParameters ); +static void vCompeteingIntMathTask2( void *pvParameters ); +static void vCompeteingIntMathTask3( void *pvParameters ); +static void vCompeteingIntMathTask4( void *pvParameters ); + +/* These variables are used to check that all the tasks are still running. If a +task gets a calculation wrong it will stop incrementing its check variable. */ +static volatile unsigned short usTaskCheck[ intgNUMBER_OF_TASKS ] = { ( unsigned short ) 0 }; +/*-----------------------------------------------------------*/ + +void vStartIntegerMathTasks( unsigned portBASE_TYPE uxPriority ) +{ + xTaskCreate( vCompeteingIntMathTask1, "IntMath1", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask2, "IntMath2", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask3, "IntMath3", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask4, "IntMath4", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask1, "IntMath5", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask2, "IntMath6", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask3, "IntMath7", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL ); + xTaskCreate( vCompeteingIntMathTask4, "IntMath8", intgSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL ); +} +/*-----------------------------------------------------------*/ + +static void vCompeteingIntMathTask1( void *pvParameters ) +{ +long l1, l2, l3, l4; +short sError = pdFALSE; +volatile unsigned short *pusTaskCheckVariable; +const long lAnswer = ( ( long ) 74565L + ( long ) 1234567L ) * ( long ) -918L; +const char * const pcTaskStartMsg = "Integer math task 1 started.\r\n"; +const char * const pcTaskFailMsg = "Integer math task 1 failed.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for(;;) + { + l1 = ( long ) 74565L; + l2 = ( long ) 1234567L; + l3 = ( long ) -918L; + + l4 = ( l1 + l2 ) * l3; + + taskYIELD(); + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( l4 != lAnswer ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +static void vCompeteingIntMathTask2( void *pvParameters ) +{ +long l1, l2, l3, l4; +short sError = pdFALSE; +volatile unsigned short *pusTaskCheckVariable; +const long lAnswer = ( ( long ) -389000L / ( long ) 329999L ) * ( long ) -89L; +const char * const pcTaskStartMsg = "Integer math task 2 started.\r\n"; +const char * const pcTaskFailMsg = "Integer math task 2 failed.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for( ;; ) + { + l1 = -389000L; + l2 = 329999L; + l3 = -89L; + + l4 = ( l1 / l2 ) * l3; + + taskYIELD(); + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( l4 != lAnswer ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +static void vCompeteingIntMathTask3( void *pvParameters ) +{ +long *plArray, lTotal1, lTotal2; +short sError = pdFALSE; +volatile unsigned short *pusTaskCheckVariable; +const unsigned short usArraySize = ( unsigned short ) 250; +unsigned short usPosition; +const char * const pcTaskStartMsg = "Integer math task 3 started.\r\n"; +const char * const pcTaskFailMsg = "Integer math task 3 failed.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Create the array we are going to use for our check calculation. */ + plArray = ( long * ) pvPortMalloc( ( size_t ) 250 * sizeof( long ) ); + + /* Keep filling the array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + lTotal1 = ( long ) 0; + lTotal2 = ( long ) 0; + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + plArray[ usPosition ] = ( long ) usPosition + ( long ) 5; + lTotal1 += ( long ) usPosition + ( long ) 5; + } + + taskYIELD(); + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + lTotal2 += plArray[ usPosition ]; + } + + if( lTotal1 != lTotal2 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + taskYIELD(); + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +static void vCompeteingIntMathTask4( void *pvParameters ) +{ +long *plArray, lTotal1, lTotal2; +short sError = pdFALSE; +volatile unsigned short *pusTaskCheckVariable; +const unsigned short usArraySize = 250; +unsigned short usPosition; +const char * const pcTaskStartMsg = "Integer math task 4 started.\r\n"; +const char * const pcTaskFailMsg = "Integer math task 4 failed.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( unsigned short * ) pvParameters; + + /* Create the array we are going to use for our check calculation. */ + plArray = ( long * ) pvPortMalloc( ( size_t ) 250 * sizeof( long ) ); + + /* Keep filling the array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + lTotal1 = ( long ) 0; + lTotal2 = ( long ) 0; + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + plArray[ usPosition ] = ( long ) usPosition * ( long ) 12; + lTotal1 += ( long ) usPosition * ( long ) 12; + } + + taskYIELD(); + + for( usPosition = 0; usPosition < usArraySize; usPosition++ ) + { + lTotal2 += plArray[ usPosition ]; + } + + + if( lTotal1 != lTotal2 ) + { + vPrintDisplayMessage( &pcTaskFailMsg ); + sError = pdTRUE; + } + + taskYIELD(); + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +portBASE_TYPE xAreIntegerMathsTaskStillRunning( void ) +{ +/* Keep a history of the check variables so we know if they have been incremented +since the last call. */ +static unsigned short usLastTaskCheck[ intgNUMBER_OF_TASKS ] = { ( unsigned short ) 0 }; +portBASE_TYPE xReturn = pdTRUE, xTask; + + /* Check the maths tasks are still running by ensuring their check variables + are still incrementing. */ + for( xTask = 0; xTask < intgNUMBER_OF_TASKS; xTask++ ) + { + if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ]; + } + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/print.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/print.c new file mode 100644 index 0000000..3d554a1 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/print.c @@ -0,0 +1,144 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Manages a queue of strings that are waiting to be displayed. This is used to + * ensure mutual exclusion of console output. + * + * A task wishing to display a message will call vPrintDisplayMessage (), with a + * pointer to the string as the parameter. The pointer is posted onto the + * xPrintQueue queue. + * + * The task spawned in main. c blocks on xPrintQueue. When a message becomes + * available it calls pcPrintGetNextMessage () to obtain a pointer to the next + * string, then uses the functions defined in the portable layer FileIO. c to + * display the message. + * + * NOTE: + * Using console IO can disrupt real time performance - depending on the port. + * Standard C IO routines are not designed for real time applications. While + * standard IO is useful for demonstration and debugging an alternative method + * should be used if you actually require console IO as part of your application. + * + * \page PrintC print.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "queue.h" + +/* Demo program include files. */ +#include "print.h" + +static QueueHandle_t xPrintQueue; + +/*-----------------------------------------------------------*/ + +void vPrintInitialise( void ) +{ +const unsigned portBASE_TYPE uxQueueSize = 20; + + /* Create the queue on which errors will be reported. */ + xPrintQueue = xQueueCreate( uxQueueSize, ( unsigned portBASE_TYPE ) sizeof( char * ) ); +} +/*-----------------------------------------------------------*/ + +void vPrintDisplayMessage( const char * const * ppcMessageToSend ) +{ + #ifdef USE_STDIO + xQueueSend( xPrintQueue, ( void * ) ppcMessageToSend, ( TickType_t ) 0 ); + #else + /* Stop warnings. */ + ( void ) ppcMessageToSend; + #endif +} +/*-----------------------------------------------------------*/ + +const char *pcPrintGetNextMessage( TickType_t xPrintRate ) +{ +char *pcMessage; + + if( xQueueReceive( xPrintQueue, &pcMessage, xPrintRate ) == pdPASS ) + { + return pcMessage; + } + else + { + return NULL; + } +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/semtest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/semtest.c new file mode 100644 index 0000000..f874162 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Full/semtest.c @@ -0,0 +1,323 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Creates two sets of two tasks. The tasks within a set share a variable, access + * to which is guarded by a semaphore. + * + * Each task starts by attempting to obtain the semaphore. On obtaining a + * semaphore a task checks to ensure that the guarded variable has an expected + * value. It then clears the variable to zero before counting it back up to the + * expected value in increments of 1. After each increment the variable is checked + * to ensure it contains the value to which it was just set. When the starting + * value is again reached the task releases the semaphore giving the other task in + * the set a chance to do exactly the same thing. The starting value is high + * enough to ensure that a tick is likely to occur during the incrementing loop. + * + * An error is flagged if at any time during the process a shared variable is + * found to have a value other than that expected. Such an occurrence would + * suggest an error in the mutual exclusion mechanism by which access to the + * variable is restricted. + * + * The first set of two tasks poll their semaphore. The second set use blocking + * calls. + * + * \page SemTestC semtest.c + * \ingroup DemoFiles + *
+ */ + +/* +Changes from V1.2.0: + + + The tasks that operate at the idle priority now use a lower expected + count than those running at a higher priority. This prevents the low + priority tasks from signaling an error because they have not been + scheduled enough time for each of them to count the shared variable to + the high value. + +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than unsigned long. + +Changes from V2.1.1 + + + The stack size now uses configMINIMAL_STACK_SIZE. + + String constants made file scope to decrease stack depth on 8051 port. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "semtest.h" +#include "print.h" + +/* The value to which the shared variables are counted. */ +#define semtstBLOCKING_EXPECTED_VALUE ( ( unsigned long ) 0xfff ) +#define semtstNON_BLOCKING_EXPECTED_VALUE ( ( unsigned long ) 0xff ) + +#define semtstSTACK_SIZE configMINIMAL_STACK_SIZE + +#define semtstNUM_TASKS ( 4 ) + +#define semtstDELAY_FACTOR ( ( TickType_t ) 10 ) + +/* The task function as described at the top of the file. */ +static void prvSemaphoreTest( void *pvParameters ); + +/* Structure used to pass parameters to each task. */ +typedef struct SEMAPHORE_PARAMETERS +{ + SemaphoreHandle_t xSemaphore; + volatile unsigned long *pulSharedVariable; + TickType_t xBlockTime; +} xSemaphoreParameters; + +/* Variables used to check that all the tasks are still running without errors. */ +static volatile short sCheckVariables[ semtstNUM_TASKS ] = { 0 }; +static volatile short sNextCheckVariable = 0; + +/* Strings to print if USE_STDIO is defined. */ +const char * const pcPollingSemaphoreTaskError = "Guarded shared variable in unexpected state.\r\n"; +const char * const pcSemaphoreTaskStart = "Guarded shared variable task started.\r\n"; + +/*-----------------------------------------------------------*/ + +void vStartSemaphoreTasks( unsigned portBASE_TYPE uxPriority ) +{ +xSemaphoreParameters *pxFirstSemaphoreParameters, *pxSecondSemaphoreParameters; +const TickType_t xBlockTime = ( TickType_t ) 100; + + /* Create the structure used to pass parameters to the first two tasks. */ + pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) ); + + if( pxFirstSemaphoreParameters != NULL ) + { + /* Create the semaphore used by the first two tasks. */ + vSemaphoreCreateBinary( pxFirstSemaphoreParameters->xSemaphore ); + + if( pxFirstSemaphoreParameters->xSemaphore != NULL ) + { + /* Create the variable which is to be shared by the first two tasks. */ + pxFirstSemaphoreParameters->pulSharedVariable = ( unsigned long * ) pvPortMalloc( sizeof( unsigned long ) ); + + /* Initialise the share variable to the value the tasks expect. */ + *( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE; + + /* The first two tasks do not block on semaphore calls. */ + pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0; + + /* Spawn the first two tasks. As they poll they operate at the idle priority. */ + xTaskCreate( prvSemaphoreTest, "PolSEM1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL ); + xTaskCreate( prvSemaphoreTest, "PolSEM2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL ); + } + } + + /* Do exactly the same to create the second set of tasks, only this time + provide a block time for the semaphore calls. */ + pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) ); + if( pxSecondSemaphoreParameters != NULL ) + { + vSemaphoreCreateBinary( pxSecondSemaphoreParameters->xSemaphore ); + + if( pxSecondSemaphoreParameters->xSemaphore != NULL ) + { + pxSecondSemaphoreParameters->pulSharedVariable = ( unsigned long * ) pvPortMalloc( sizeof( unsigned long ) ); + *( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE; + pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS; + + xTaskCreate( prvSemaphoreTest, "BlkSEM1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL ); + xTaskCreate( prvSemaphoreTest, "BlkSEM2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL ); + } + } +} +/*-----------------------------------------------------------*/ + +static void prvSemaphoreTest( void *pvParameters ) +{ +xSemaphoreParameters *pxParameters; +volatile unsigned long *pulSharedVariable, ulExpectedValue; +unsigned long ulCounter; +short sError = pdFALSE, sCheckVariableToUse; + + /* See which check variable to use. sNextCheckVariable is not semaphore + protected! */ + portENTER_CRITICAL(); + sCheckVariableToUse = sNextCheckVariable; + sNextCheckVariable++; + portEXIT_CRITICAL(); + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcSemaphoreTaskStart ); + + /* A structure is passed in as the parameter. This contains the shared + variable being guarded. */ + pxParameters = ( xSemaphoreParameters * ) pvParameters; + pulSharedVariable = pxParameters->pulSharedVariable; + + /* If we are blocking we use a much higher count to ensure loads of context + switches occur during the count. */ + if( pxParameters->xBlockTime > ( TickType_t ) 0 ) + { + ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE; + } + else + { + ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE; + } + + for( ;; ) + { + /* Try to obtain the semaphore. */ + if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS ) + { + /* We have the semaphore and so expect any other tasks using the + shared variable to have left it in the state we expect to find + it. */ + if( *pulSharedVariable != ulExpectedValue ) + { + vPrintDisplayMessage( &pcPollingSemaphoreTaskError ); + sError = pdTRUE; + } + + /* Clear the variable, then count it back up to the expected value + before releasing the semaphore. Would expect a context switch or + two during this time. */ + for( ulCounter = ( unsigned long ) 0; ulCounter <= ulExpectedValue; ulCounter++ ) + { + *pulSharedVariable = ulCounter; + if( *pulSharedVariable != ulCounter ) + { + if( sError == pdFALSE ) + { + vPrintDisplayMessage( &pcPollingSemaphoreTaskError ); + } + sError = pdTRUE; + } + } + + /* Release the semaphore, and if no errors have occurred increment the check + variable. */ + if( xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE ) + { + vPrintDisplayMessage( &pcPollingSemaphoreTaskError ); + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + if( sCheckVariableToUse < semtstNUM_TASKS ) + { + ( sCheckVariables[ sCheckVariableToUse ] )++; + } + } + + /* If we have a block time then we are running at a priority higher + than the idle priority. This task takes a long time to complete + a cycle (deliberately so to test the guarding) so will be starving + out lower priority tasks. Block for some time to allow give lower + priority tasks some processor time. */ + vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR ); + } + else + { + if( pxParameters->xBlockTime == ( TickType_t ) 0 ) + { + /* We have not got the semaphore yet, so no point using the + processor. We are not blocking when attempting to obtain the + semaphore. */ + taskYIELD(); + } + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +portBASE_TYPE xAreSemaphoreTasksStillRunning( void ) +{ +static short sLastCheckVariables[ semtstNUM_TASKS ] = { 0 }; +portBASE_TYPE xTask, xReturn = pdTRUE; + + for( xTask = 0; xTask < semtstNUM_TASKS; xTask++ ) + { + if( sLastCheckVariables[ xTask ] == sCheckVariables[ xTask ] ) + { + xReturn = pdFALSE; + } + + sLastCheckVariables[ xTask ] = sCheckVariables[ xTask ]; + } + + return xReturn; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlckQ.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlckQ.c new file mode 100644 index 0000000..a1d2cd0 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlckQ.c @@ -0,0 +1,332 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This is a version of BlockQ.c that uses the alternative (Alt) API. + * + * Creates six tasks that operate on three queues as follows: + * + * The first two tasks send and receive an incrementing number to/from a queue. + * One task acts as a producer and the other as the consumer. The consumer is a + * higher priority than the producer and is set to block on queue reads. The queue + * only has space for one item - as soon as the producer posts a message on the + * queue the consumer will unblock, pre-empt the producer, and remove the item. + * + * The second two tasks work the other way around. Again the queue used only has + * enough space for one item. This time the consumer has a lower priority than the + * producer. The producer will try to post on the queue blocking when the queue is + * full. When the consumer wakes it will remove the item from the queue, causing + * the producer to unblock, pre-empt the consumer, and immediately re-fill the + * queue. + * + * The last two tasks use the same queue producer and consumer functions. This time the queue has + * enough space for lots of items and the tasks operate at the same priority. The + * producer will execute, placing items into the queue. The consumer will start + * executing when either the queue becomes full (causing the producer to block) or + * a context switch occurs (tasks of the same priority will time slice). + * + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "AltBlckQ.h" + +#define blckqSTACK_SIZE configMINIMAL_STACK_SIZE +#define blckqNUM_TASK_SETS ( 3 ) + +/* Structure used to pass parameters to the blocking queue tasks. */ +typedef struct BLOCKING_QUEUE_PARAMETERS +{ + QueueHandle_t xQueue; /*< The queue to be used by the task. */ + TickType_t xBlockTime; /*< The block time to use on queue reads/writes. */ + volatile short *psCheckVariable; /*< Incremented on each successful cycle to check the task is still running. */ +} xBlockingQueueParameters; + +/* Task function that creates an incrementing number and posts it on a queue. */ +static portTASK_FUNCTION_PROTO( vBlockingQueueProducer, pvParameters ); + +/* Task function that removes the incrementing number from a queue and checks that +it is the expected number. */ +static portTASK_FUNCTION_PROTO( vBlockingQueueConsumer, pvParameters ); + +/* Variables which are incremented each time an item is removed from a queue, and +found to be the expected value. +These are used to check that the tasks are still running. */ +static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; + +/* Variable which are incremented each time an item is posted on a queue. These +are used to check that the tasks are still running. */ +static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartAltBlockingQueueTasks( UBaseType_t uxPriority ) +{ +xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2; +xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4; +xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6; +const UBaseType_t uxQueueSize1 = 1, uxQueueSize5 = 5; +const TickType_t xBlockTime = ( TickType_t ) 1000 / portTICK_PERIOD_MS; +const TickType_t xDontBlock = ( TickType_t ) 0; + + /* Create the first two tasks as described at the top of the file. */ + + /* First create the structure used to pass parameters to the consumer tasks. */ + pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Create the queue used by the first two tasks to pass the incrementing number. + Pass a pointer to the queue in the parameter structure. */ + pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) ); + + /* The consumer is created first so gets a block time as described above. */ + pxQueueParameters1->xBlockTime = xBlockTime; + + /* Pass in the variable that this task is going to increment so we can check it + is still running. */ + pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] ); + + /* Create the structure used to pass parameters to the producer task. */ + pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Pass the queue to this task also, using the parameter structure. */ + pxQueueParameters2->xQueue = pxQueueParameters1->xQueue; + + /* The producer is not going to block - as soon as it posts the consumer will + wake and remove the item so the producer should always have room to post. */ + pxQueueParameters2->xBlockTime = xDontBlock; + + /* Pass in the variable that this task is going to increment so we can check + it is still running. */ + pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] ); + + + /* Note the producer has a lower priority than the consumer when the tasks are + spawned. */ + xTaskCreate( vBlockingQueueConsumer, "QConsB1", blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL ); + xTaskCreate( vBlockingQueueProducer, "QProdB2", blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL ); + + + + /* Create the second two tasks as described at the top of the file. This uses + the same mechanism but reverses the task priorities. */ + + pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) ); + pxQueueParameters3->xBlockTime = xDontBlock; + pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] ); + + pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters4->xQueue = pxQueueParameters3->xQueue; + pxQueueParameters4->xBlockTime = xBlockTime; + pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] ); + + xTaskCreate( vBlockingQueueConsumer, "QProdB3", blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueProducer, "QConsB4", blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL ); + + + + /* Create the last two tasks as described above. The mechanism is again just + the same. This time both parameter structures are given a block time. */ + pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( UBaseType_t ) sizeof( uint16_t ) ); + pxQueueParameters5->xBlockTime = xBlockTime; + pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] ); + + pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters6->xQueue = pxQueueParameters5->xQueue; + pxQueueParameters6->xBlockTime = xBlockTime; + pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] ); + + xTaskCreate( vBlockingQueueProducer, "QProdB5", blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueConsumer, "QConsB6", blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vBlockingQueueProducer, pvParameters ) +{ +uint16_t usValue = 0; +xBlockingQueueParameters *pxQueueParameters; +short sErrorEverOccurred = pdFALSE; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt blocking queue producer task started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + for( ;; ) + { + if( xQueueAltSendToBack( pxQueueParameters->xQueue, ( void * ) &usValue, pxQueueParameters->xBlockTime ) != pdPASS ) + { + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully posted a message, so increment the variable + used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the variable we are going to post next time round. The + consumer will expect the numbers to follow in numerical order. */ + ++usValue; + } + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vBlockingQueueConsumer, pvParameters ) +{ +uint16_t usData, usExpectedValue = 0; +xBlockingQueueParameters *pxQueueParameters; +short sErrorEverOccurred = pdFALSE; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt blocking queue consumer task started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + for( ;; ) + { + if( xQueueAltReceive( pxQueueParameters->xQueue, &usData, pxQueueParameters->xBlockTime ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + /* Catch-up. */ + usExpectedValue = usData; + + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully received a message, so increment the + variable used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the value we expect to remove from the queue next time + round. */ + ++usExpectedValue; + } + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreAltBlockingQueuesStillRunning( void ) +{ +static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; +static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; +BaseType_t xReturn = pdPASS, xTasks; + + /* Not too worried about mutual exclusion on these variables as they are 16 + bits and we are only reading them. We also only care to see if they have + changed or not. + + Loop through each check variable to and return pdFALSE if any are found not + to have changed since the last call. */ + + for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ ) + { + if( sBlockingConsumerCount[ xTasks ] == sLastBlockingConsumerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingConsumerCount[ xTasks ] = sBlockingConsumerCount[ xTasks ]; + + + if( sBlockingProducerCount[ xTasks ] == sLastBlockingProducerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ]; + } + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlock.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlock.c new file mode 100644 index 0000000..f19e3f4 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltBlock.c @@ -0,0 +1,549 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This is a version of BlockTim.c that uses the light weight API. + * + * This file contains some test scenarios that ensure tasks do not exit queue + * send or receive functions prematurely. A description of the tests is + * included within the code. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo includes. */ +#include "AltBlock.h" + +/* Task priorities. */ +#define bktPRIMARY_PRIORITY ( 3 ) +#define bktSECONDARY_PRIORITY ( 2 ) + +/* Task behaviour. */ +#define bktQUEUE_LENGTH ( 5 ) +#define bktSHORT_WAIT ( ( ( TickType_t ) 20 ) / portTICK_PERIOD_MS ) +#define bktPRIMARY_BLOCK_TIME ( 10 ) +#define bktALLOWABLE_MARGIN ( 12 ) +#define bktTIME_TO_BLOCK ( 175 ) +#define bktDONT_BLOCK ( ( TickType_t ) 0 ) +#define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 ) + +/* The queue on which the tasks block. */ +static QueueHandle_t xTestQueue; + +/* Handle to the secondary task is required by the primary task for calls +to vTaskSuspend/Resume(). */ +static TaskHandle_t xSecondary; + +/* Used to ensure that tasks are still executing without error. */ +static BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0; +static BaseType_t xErrorOccurred = pdFALSE; + +/* Provides a simple mechanism for the primary task to know when the +secondary task has executed. */ +static volatile UBaseType_t xRunIndicator; + +/* The two test tasks. Their behaviour is commented within the files. */ +static void vPrimaryBlockTimeTestTask( void *pvParameters ); +static void vSecondaryBlockTimeTestTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +void vCreateAltBlockTimeTasks( void ) +{ + /* Create the queue on which the two tasks block. */ + xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xTestQueue, "AltBlockQueue" ); + + + /* Create the two test tasks. */ + xTaskCreate( vPrimaryBlockTimeTestTask, "FBTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL ); + xTaskCreate( vSecondaryBlockTimeTestTask, "FBTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary ); +} +/*-----------------------------------------------------------*/ + +static void vPrimaryBlockTimeTestTask( void *pvParameters ) +{ +BaseType_t xItem, xData; +TickType_t xTimeWhenBlocking; +TickType_t xTimeToBlock, xBlockedTime; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt primary block time test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + ( void ) pvParameters; + + for( ;; ) + { + /********************************************************************* + Test 1 + + Simple block time wakeup test on queue receives. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is empty. Attempt to read from the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem; + + /* A critical section is used to minimise the jitter in the time + measurements. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueAltReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + /********************************************************************* + Test 2 + + Simple block time wakeup test on queue sends. + + First fill the queue. It should be empty so all sends should pass. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is full. Attempt to write to the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem; + + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueAltSendToBack( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + /********************************************************************* + Test 3 + + Wake the other task, it will block attempting to post to the queue. + When we read from the queue the other task will wake, but before it + can run we will post to the queue again. When the other task runs it + will find the queue still full, even though it was woken. It should + recognise that its block time has not expired and return to block for + the remains of its block time. + + Wake the other task so it blocks attempting to post to the already + full queue. */ + xRunIndicator = 0; + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + /* The other task has not yet executed. */ + vTaskDelay( bktSHORT_WAIT ); + } + /* Make sure the other task is blocked on the queue. */ + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we make space on the queue the other task should wake + but not execute as this task has higher priority. */ + if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now fill the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + full ourselves, and the other task have set xRunIndicator. */ + if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + + /* Set the priority back down. */ + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /********************************************************************* + Test 4 + + As per test 3 - but with the send and receive the other way around. + The other task blocks attempting to read from the queue. + + Empty the queue. We should find that it is full. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + /* Wake the other task so it blocks attempting to read from the + already empty queue. */ + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we place an item on the queue the other task should + wake but not execute as this task has higher priority. */ + if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now empty the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + empty ourselves, and the other task would be suspended. */ + if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + + xPrimaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void vSecondaryBlockTimeTestTask( void *pvParameters ) +{ +TickType_t xTimeWhenBlocking, xBlockedTime; +BaseType_t xData; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt secondary block time test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + ( void ) pvParameters; + + for( ;; ) + { + /********************************************************************* + Test 1 and 2 + + This task does does not participate in these tests. */ + vTaskSuspend( NULL ); + + /********************************************************************* + Test 3 + + The first thing we do is attempt to read from the queue. It should be + full so we block. Note the time before we block so we can check the + wake time is as per that expected. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + anything on the queue. */ + xData = 0; + xRunIndicator = bktRUN_INDICATOR; + if( xQueueAltSendToBack( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we inside the send function? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as + soon as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + /* Suspend ready for test 3. */ + xRunIndicator = bktRUN_INDICATOR; + vTaskSuspend( NULL ); + + /********************************************************************* + Test 4 + + As per test three, but with the send and receive reversed. */ + portENTER_CRITICAL(); + { + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + anything on the queue. */ + xRunIndicator = bktRUN_INDICATOR; + if( xQueueAltReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + } + portEXIT_CRITICAL(); + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as soon + as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + xRunIndicator = bktRUN_INDICATOR; + + xSecondaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreAltBlockTimeTestTasksStillRunning( void ) +{ +static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0; +BaseType_t xReturn = pdPASS; + + /* Have both tasks performed at least one cycle since this function was + last called? */ + if( xPrimaryCycles == xLastPrimaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xSecondaryCycles == xLastSecondaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xErrorOccurred == pdTRUE ) + { + xReturn = pdFAIL; + } + + xLastSecondaryCycleCount = xSecondaryCycles; + xLastPrimaryCycleCount = xPrimaryCycles; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltPollQ.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltPollQ.c new file mode 100644 index 0000000..57b7322 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltPollQ.c @@ -0,0 +1,275 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This is a version of PollQ.c that uses the alternative (Alt) API. + * + * Creates two tasks that communicate over a single queue. One task acts as a + * producer, the other a consumer. + * + * The producer loops for three iteration, posting an incrementing number onto the + * queue each cycle. It then delays for a fixed period before doing exactly the + * same again. + * + * The consumer loops emptying the queue. Each item removed from the queue is + * checked to ensure it contains the expected value. When the queue is empty it + * blocks for a fixed period, then does the same again. + * + * All queue access is performed without blocking. The consumer completely empties + * the queue each time it runs so the producer should never find the queue full. + * + * An error is flagged if the consumer obtains an unexpected value or the producer + * find the queue is full. + */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than uint32_t. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "AltPollQ.h" + +#define pollqSTACK_SIZE configMINIMAL_STACK_SIZE +#define pollqQUEUE_SIZE ( 10 ) +#define pollqPRODUCER_DELAY ( ( TickType_t ) 200 / portTICK_PERIOD_MS ) +#define pollqCONSUMER_DELAY ( pollqPRODUCER_DELAY - ( TickType_t ) ( 20 / portTICK_PERIOD_MS ) ) +#define pollqNO_DELAY ( ( TickType_t ) 0 ) +#define pollqVALUES_TO_PRODUCE ( ( BaseType_t ) 3 ) +#define pollqINITIAL_VALUE ( ( BaseType_t ) 0 ) + +/* The task that posts the incrementing number onto the queue. */ +static portTASK_FUNCTION_PROTO( vPolledQueueProducer, pvParameters ); + +/* The task that empties the queue. */ +static portTASK_FUNCTION_PROTO( vPolledQueueConsumer, pvParameters ); + +/* Variables that are used to check that the tasks are still running with no +errors. */ +static volatile BaseType_t xPollingConsumerCount = pollqINITIAL_VALUE, xPollingProducerCount = pollqINITIAL_VALUE; + +/*-----------------------------------------------------------*/ + +void vStartAltPolledQueueTasks( UBaseType_t uxPriority ) +{ +static QueueHandle_t xPolledQueue; + + /* Create the queue used by the producer and consumer. */ + xPolledQueue = xQueueCreate( pollqQUEUE_SIZE, ( UBaseType_t ) sizeof( uint16_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xPolledQueue, "AltPollQueue" ); + + + /* Spawn the producer and consumer. */ + xTaskCreate( vPolledQueueConsumer, "QConsNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL ); + xTaskCreate( vPolledQueueProducer, "QProdNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vPolledQueueProducer, pvParameters ) +{ +uint16_t usValue = ( uint16_t ) 0; +BaseType_t xError = pdFALSE, xLoop; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt polling queue producer task started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + for( ;; ) + { + for( xLoop = 0; xLoop < pollqVALUES_TO_PRODUCE; xLoop++ ) + { + /* Send an incrementing number on the queue without blocking. */ + if( xQueueAltSendToBack( *( ( QueueHandle_t * ) pvParameters ), ( void * ) &usValue, pollqNO_DELAY ) != pdPASS ) + { + /* We should never find the queue full so if we get here there + has been an error. */ + xError = pdTRUE; + } + else + { + if( xError == pdFALSE ) + { + /* If an error has ever been recorded we stop incrementing the + check variable. */ + portENTER_CRITICAL(); + xPollingProducerCount++; + portEXIT_CRITICAL(); + } + + /* Update the value we are going to post next time around. */ + usValue++; + } + } + + /* Wait before we start posting again to ensure the consumer runs and + empties the queue. */ + vTaskDelay( pollqPRODUCER_DELAY ); + } +} /*lint !e818 Function prototype must conform to API. */ +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vPolledQueueConsumer, pvParameters ) +{ +uint16_t usData, usExpectedValue = ( uint16_t ) 0; +BaseType_t xError = pdFALSE; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt blocking queue consumer task started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + for( ;; ) + { + /* Loop until the queue is empty. */ + while( uxQueueMessagesWaiting( *( ( QueueHandle_t * ) pvParameters ) ) ) + { + if( xQueueAltReceive( *( ( QueueHandle_t * ) pvParameters ), &usData, pollqNO_DELAY ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + /* This is not what we expected to receive so an error has + occurred. */ + xError = pdTRUE; + + /* Catch-up to the value we received so our next expected + value should again be correct. */ + usExpectedValue = usData; + } + else + { + if( xError == pdFALSE ) + { + /* Only increment the check variable if no errors have + occurred. */ + portENTER_CRITICAL(); + xPollingConsumerCount++; + portEXIT_CRITICAL(); + } + } + + /* Next time round we would expect the number to be one higher. */ + usExpectedValue++; + } + } + + /* Now the queue is empty we block, allowing the producer to place more + items in the queue. */ + vTaskDelay( pollqCONSUMER_DELAY ); + } +} /*lint !e818 Function prototype must conform to API. */ +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running with no errors. */ +BaseType_t xAreAltPollingQueuesStillRunning( void ) +{ +BaseType_t xReturn; + + /* Check both the consumer and producer poll count to check they have both + been changed since out last trip round. We do not need a critical section + around the check variables as this is called from a higher priority than + the other tasks that access the same variables. */ + if( ( xPollingConsumerCount == pollqINITIAL_VALUE ) || + ( xPollingProducerCount == pollqINITIAL_VALUE ) + ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + /* Set the check variables back down so we know if they have been + incremented the next time around. */ + xPollingConsumerCount = pollqINITIAL_VALUE; + xPollingProducerCount = pollqINITIAL_VALUE; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltQTest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltQTest.c new file mode 100644 index 0000000..188d3b0 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/AltQTest.c @@ -0,0 +1,587 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * This file implements the same demo and test as GenQTest.c, but uses the + * light weight API in place of the fully featured API. + * + * See the comments at the top of GenQTest.c for a description. + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* Demo program include files. */ +#include "AltQTest.h" + +#define genqQUEUE_LENGTH ( 5 ) +#define genqNO_BLOCK ( 0 ) + +#define genqMUTEX_LOW_PRIORITY ( tskIDLE_PRIORITY ) +#define genqMUTEX_TEST_PRIORITY ( tskIDLE_PRIORITY + 1 ) +#define genqMUTEX_MEDIUM_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define genqMUTEX_HIGH_PRIORITY ( tskIDLE_PRIORITY + 3 ) + +/*-----------------------------------------------------------*/ + +/* + * Tests the behaviour of the xQueueAltSendToFront() and xQueueAltSendToBack() + * macros by using both to fill a queue, then reading from the queue to + * check the resultant queue order is as expected. Queue data is also + * peeked. + */ +static void prvSendFrontAndBackTest( void *pvParameters ); + +/* + * The following three tasks are used to demonstrate the mutex behaviour. + * Each task is given a different priority to demonstrate the priority + * inheritance mechanism. + * + * The low priority task obtains a mutex. After this a high priority task + * attempts to obtain the same mutex, causing its priority to be inherited + * by the low priority task. The task with the inherited high priority then + * resumes a medium priority task to ensure it is not blocked by the medium + * priority task while it holds the inherited high priority. Once the mutex + * is returned the task with the inherited priority returns to its original + * low priority, and is therefore immediately preempted by first the high + * priority task and then the medium prioroity task before it can continue. + */ +static void prvLowPriorityMutexTask( void *pvParameters ); +static void prvMediumPriorityMutexTask( void *pvParameters ); +static void prvHighPriorityMutexTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +/* Flag that will be latched to pdTRUE should any unexpected behaviour be +detected in any of the tasks. */ +static BaseType_t xErrorDetected = pdFALSE; + +/* Counters that are incremented on each cycle of a test. This is used to +detect a stalled task - a test that is no longer running. */ +static volatile uint32_t ulLoopCounter = 0; +static volatile uint32_t ulLoopCounter2 = 0; + +/* The variable that is guarded by the mutex in the mutex demo tasks. */ +static volatile uint32_t ulGuardedVariable = 0; + +/* Handles used in the mutext test to suspend and resume the high and medium +priority mutex test tasks. */ +static TaskHandle_t xHighPriorityMutexTask, xMediumPriorityMutexTask; + +/*-----------------------------------------------------------*/ + +void vStartAltGenericQueueTasks( UBaseType_t uxPriority ) +{ +QueueHandle_t xQueue; +SemaphoreHandle_t xMutex; + + /* Create the queue that we are going to use for the + prvSendFrontAndBackTest demo. */ + xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( uint32_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xQueue, "Alt_Gen_Test_Queue" ); + + /* Create the demo task and pass it the queue just created. We are + passing the queue handle by value so it does not matter that it is + declared on the stack here. */ + xTaskCreate( prvSendFrontAndBackTest, "FGenQ", configMINIMAL_STACK_SIZE, ( void * ) xQueue, uxPriority, NULL ); + + /* Create the mutex used by the prvMutexTest task. */ + xMutex = xSemaphoreCreateMutex(); + + /* vQueueAddToRegistry() adds the mutex to the registry, if one is + in use. The registry is provided as a means for kernel aware + debuggers to locate mutex and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Alt_Q_Mutex" ); + + /* Create the mutex demo tasks and pass it the mutex just created. We are + passing the mutex handle by value so it does not matter that it is declared + on the stack here. */ + xTaskCreate( prvLowPriorityMutexTask, "FMuLow", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL ); + xTaskCreate( prvMediumPriorityMutexTask, "FMuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask ); + xTaskCreate( prvHighPriorityMutexTask, "FMuHigh", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask ); +} +/*-----------------------------------------------------------*/ + +static void prvSendFrontAndBackTest( void *pvParameters ) +{ +uint32_t ulData, ulData2; +QueueHandle_t xQueue; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Alt queue SendToFront/SendToBack/Peek test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + xQueue = ( QueueHandle_t ) pvParameters; + + for( ;; ) + { + /* The queue is empty, so sending an item to the back of the queue + should have the same efect as sending it to the front of the queue. + + First send to the front and check everything is as expected. */ + xQueueAltSendToFront( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK ); + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* The data we sent to the queue should equal the data we just received + from the queue. */ + if( ulLoopCounter != ulData ) + { + xErrorDetected = pdTRUE; + } + + /* Then do the same, sending the data to the back, checking everything + is as expected. */ + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + xQueueAltSendToBack( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK ); + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* The data we sent to the queue should equal the data we just received + from the queue. */ + if( ulLoopCounter != ulData ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + + /* Place 2, 3, 4 into the queue, adding items to the back of the queue. */ + for( ulData = 2; ulData < 5; ulData++ ) + { + xQueueAltSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + } + + /* Now the order in the queue should be 2, 3, 4, with 2 being the first + thing to be read out. Now add 1 then 0 to the front of the queue. */ + if( uxQueueMessagesWaiting( xQueue ) != 3 ) + { + xErrorDetected = pdTRUE; + } + ulData = 1; + xQueueAltSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + ulData = 0; + xQueueAltSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + + /* Now the queue should be full, and when we read the data out we + should receive 0, 1, 2, 3, 4. */ + if( uxQueueMessagesWaiting( xQueue ) != 5 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the data we read out is in the expected order. */ + for( ulData = 0; ulData < genqQUEUE_LENGTH; ulData++ ) + { + /* Try peeking the data first. */ + if( xQueueAltPeek( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + + + /* Now try receiving the data for real. The value should be the + same. Clobber the value first so we know we really received it. */ + ulData2 = ~ulData2; + if( xQueueAltReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + } + + /* The queue should now be empty again. */ + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + /* Our queue is empty once more, add 10, 11 to the back. */ + ulData = 10; + if( xQueueAltSendToBack( xQueue, &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + ulData = 11; + if( xQueueAltSendToBack( xQueue, &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 2 ) + { + xErrorDetected = pdTRUE; + } + + /* Now we should have 10, 11 in the queue. Add 7, 8, 9 to the + front. */ + for( ulData = 9; ulData >= 7; ulData-- ) + { + if( xQueueAltSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + } + + /* Now check that the queue is full, and that receiving data provides + the expected sequence of 7, 8, 9, 10, 11. */ + if( uxQueueMessagesWaiting( xQueue ) != 5 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueAltSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the data we read out is in the expected order. */ + for( ulData = 7; ulData < ( 7 + genqQUEUE_LENGTH ); ulData++ ) + { + if( xQueueAltReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + } + + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvLowPriorityMutexTask( void *pvParameters ) +{ +SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Fast mutex with priority inheritance test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + ( void ) pvParameters; + + + for( ;; ) + { + /* Take the mutex. It should be available now. */ + if( xSemaphoreAltTake( xMutex, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* Set our guarded variable to a known start value. */ + ulGuardedVariable = 0; + + /* Our priority should be as per that assigned when the task was + created. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the high priority task. This will attempt to take the + mutex, and block when it finds it cannot obtain it. */ + vTaskResume( xHighPriorityMutexTask ); + + /* We should now have inherited the prioritoy of the high priority task, + as by now it will have attempted to get the mutex. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* We can attempt to set our priority to the test priority - between the + idle priority and the medium/high test priorities, but our actual + prioroity should remain at the high priority. */ + vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY ); + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the medium priority task. This should not run as our + inherited priority is above that of the medium priority task. */ + vTaskResume( xMediumPriorityMutexTask ); + + /* If the did run then it will have incremented our guarded variable. */ + if( ulGuardedVariable != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* When we give back the semaphore our priority should be disinherited + back to the priority to which we attempted to set ourselves. This means + that when the high priority task next blocks, the medium priority task + should execute and increment the guarded variable. When we next run + both the high and medium priority tasks will have been suspended again. */ + if( xSemaphoreAltGive( xMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* Check that the guarded variable did indeed increment... */ + if( ulGuardedVariable != 1 ) + { + xErrorDetected = pdTRUE; + } + + /* ... and that our priority has been disinherited to + genqMUTEX_TEST_PRIORITY. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Set our priority back to our original priority ready for the next + loop around this test. */ + vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY ); + + /* Just to show we are still running. */ + ulLoopCounter2++; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +static void prvMediumPriorityMutexTask( void *pvParameters ) +{ + ( void ) pvParameters; + + for( ;; ) + { + /* The medium priority task starts by suspending itself. The low + priority task will unsuspend this task when required. */ + vTaskSuspend( NULL ); + + /* When this task unsuspends all it does is increment the guarded + variable, this is so the low priority task knows that it has + executed. */ + ulGuardedVariable++; + } +} +/*-----------------------------------------------------------*/ + +static void prvHighPriorityMutexTask( void *pvParameters ) +{ +SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters; + + ( void ) pvParameters; + + for( ;; ) + { + /* The high priority task starts by suspending itself. The low + priority task will unsuspend this task when required. */ + vTaskSuspend( NULL ); + + /* When this task unsuspends all it does is attempt to obtain + the mutex. It should find the mutex is not available so a + block time is specified. */ + if( xSemaphoreAltTake( xMutex, portMAX_DELAY ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* When we eventually obtain the mutex we just give it back then + return to suspend ready for the next test. */ + if( xSemaphoreAltGive( xMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreAltGenericQueueTasksStillRunning( void ) +{ +static uint32_t ulLastLoopCounter = 0, ulLastLoopCounter2 = 0; + + /* If the demo task is still running then we expect the loopcounters to + have incremented since this function was last called. */ + if( ulLastLoopCounter == ulLoopCounter ) + { + xErrorDetected = pdTRUE; + } + + if( ulLastLoopCounter2 == ulLoopCounter2 ) + { + xErrorDetected = pdTRUE; + } + + ulLastLoopCounter = ulLoopCounter; + ulLastLoopCounter2 = ulLoopCounter2; + + /* Errors detected in the task itself will have latched xErrorDetected + to true. */ + + return !xErrorDetected; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/BlockQ.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/BlockQ.c new file mode 100644 index 0000000..b8db390 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/BlockQ.c @@ -0,0 +1,324 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Creates six tasks that operate on three queues as follows: + * + * The first two tasks send and receive an incrementing number to/from a queue. + * One task acts as a producer and the other as the consumer. The consumer is a + * higher priority than the producer and is set to block on queue reads. The queue + * only has space for one item - as soon as the producer posts a message on the + * queue the consumer will unblock, pre-empt the producer, and remove the item. + * + * The second two tasks work the other way around. Again the queue used only has + * enough space for one item. This time the consumer has a lower priority than the + * producer. The producer will try to post on the queue blocking when the queue is + * full. When the consumer wakes it will remove the item from the queue, causing + * the producer to unblock, pre-empt the consumer, and immediately re-fill the + * queue. + * + * The last two tasks use the same queue producer and consumer functions. This time the queue has + * enough space for lots of items and the tasks operate at the same priority. The + * producer will execute, placing items into the queue. The consumer will start + * executing when either the queue becomes full (causing the producer to block) or + * a context switch occurs (tasks of the same priority will time slice). + * + */ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "BlockQ.h" + +#define blckqSTACK_SIZE configMINIMAL_STACK_SIZE +#define blckqNUM_TASK_SETS ( 3 ) + +/* Structure used to pass parameters to the blocking queue tasks. */ +typedef struct BLOCKING_QUEUE_PARAMETERS +{ + QueueHandle_t xQueue; /*< The queue to be used by the task. */ + TickType_t xBlockTime; /*< The block time to use on queue reads/writes. */ + volatile short *psCheckVariable; /*< Incremented on each successful cycle to check the task is still running. */ +} xBlockingQueueParameters; + +/* Task function that creates an incrementing number and posts it on a queue. */ +static portTASK_FUNCTION_PROTO( vBlockingQueueProducer, pvParameters ); + +/* Task function that removes the incrementing number from a queue and checks that +it is the expected number. */ +static portTASK_FUNCTION_PROTO( vBlockingQueueConsumer, pvParameters ); + +/* Variables which are incremented each time an item is removed from a queue, and +found to be the expected value. +These are used to check that the tasks are still running. */ +static volatile short sBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; + +/* Variable which are incremented each time an item is posted on a queue. These +are used to check that the tasks are still running. */ +static volatile short sBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartBlockingQueueTasks( UBaseType_t uxPriority ) +{ +xBlockingQueueParameters *pxQueueParameters1, *pxQueueParameters2; +xBlockingQueueParameters *pxQueueParameters3, *pxQueueParameters4; +xBlockingQueueParameters *pxQueueParameters5, *pxQueueParameters6; +const UBaseType_t uxQueueSize1 = 1, uxQueueSize5 = 5; +const TickType_t xBlockTime = ( TickType_t ) 1000 / portTICK_PERIOD_MS; +const TickType_t xDontBlock = ( TickType_t ) 0; + + /* Create the first two tasks as described at the top of the file. */ + + /* First create the structure used to pass parameters to the consumer tasks. */ + pxQueueParameters1 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Create the queue used by the first two tasks to pass the incrementing number. + Pass a pointer to the queue in the parameter structure. */ + pxQueueParameters1->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) ); + + /* The consumer is created first so gets a block time as described above. */ + pxQueueParameters1->xBlockTime = xBlockTime; + + /* Pass in the variable that this task is going to increment so we can check it + is still running. */ + pxQueueParameters1->psCheckVariable = &( sBlockingConsumerCount[ 0 ] ); + + /* Create the structure used to pass parameters to the producer task. */ + pxQueueParameters2 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + + /* Pass the queue to this task also, using the parameter structure. */ + pxQueueParameters2->xQueue = pxQueueParameters1->xQueue; + + /* The producer is not going to block - as soon as it posts the consumer will + wake and remove the item so the producer should always have room to post. */ + pxQueueParameters2->xBlockTime = xDontBlock; + + /* Pass in the variable that this task is going to increment so we can check + it is still running. */ + pxQueueParameters2->psCheckVariable = &( sBlockingProducerCount[ 0 ] ); + + + /* Note the producer has a lower priority than the consumer when the tasks are + spawned. */ + xTaskCreate( vBlockingQueueConsumer, "QConsB1", blckqSTACK_SIZE, ( void * ) pxQueueParameters1, uxPriority, NULL ); + xTaskCreate( vBlockingQueueProducer, "QProdB2", blckqSTACK_SIZE, ( void * ) pxQueueParameters2, tskIDLE_PRIORITY, NULL ); + + + + /* Create the second two tasks as described at the top of the file. This uses + the same mechanism but reverses the task priorities. */ + + pxQueueParameters3 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters3->xQueue = xQueueCreate( uxQueueSize1, ( UBaseType_t ) sizeof( uint16_t ) ); + pxQueueParameters3->xBlockTime = xDontBlock; + pxQueueParameters3->psCheckVariable = &( sBlockingProducerCount[ 1 ] ); + + pxQueueParameters4 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters4->xQueue = pxQueueParameters3->xQueue; + pxQueueParameters4->xBlockTime = xBlockTime; + pxQueueParameters4->psCheckVariable = &( sBlockingConsumerCount[ 1 ] ); + + xTaskCreate( vBlockingQueueConsumer, "QConsB3", blckqSTACK_SIZE, ( void * ) pxQueueParameters3, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueProducer, "QProdB4", blckqSTACK_SIZE, ( void * ) pxQueueParameters4, uxPriority, NULL ); + + + + /* Create the last two tasks as described above. The mechanism is again just + the same. This time both parameter structures are given a block time. */ + pxQueueParameters5 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters5->xQueue = xQueueCreate( uxQueueSize5, ( UBaseType_t ) sizeof( uint16_t ) ); + pxQueueParameters5->xBlockTime = xBlockTime; + pxQueueParameters5->psCheckVariable = &( sBlockingProducerCount[ 2 ] ); + + pxQueueParameters6 = ( xBlockingQueueParameters * ) pvPortMalloc( sizeof( xBlockingQueueParameters ) ); + pxQueueParameters6->xQueue = pxQueueParameters5->xQueue; + pxQueueParameters6->xBlockTime = xBlockTime; + pxQueueParameters6->psCheckVariable = &( sBlockingConsumerCount[ 2 ] ); + + xTaskCreate( vBlockingQueueProducer, "QProdB5", blckqSTACK_SIZE, ( void * ) pxQueueParameters5, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vBlockingQueueConsumer, "QConsB6", blckqSTACK_SIZE, ( void * ) pxQueueParameters6, tskIDLE_PRIORITY, NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vBlockingQueueProducer, pvParameters ) +{ +uint16_t usValue = 0; +xBlockingQueueParameters *pxQueueParameters; +short sErrorEverOccurred = pdFALSE; + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + for( ;; ) + { + if( xQueueSend( pxQueueParameters->xQueue, ( void * ) &usValue, pxQueueParameters->xBlockTime ) != pdPASS ) + { + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully posted a message, so increment the variable + used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the variable we are going to post next time round. The + consumer will expect the numbers to follow in numerical order. */ + ++usValue; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vBlockingQueueConsumer, pvParameters ) +{ +uint16_t usData, usExpectedValue = 0; +xBlockingQueueParameters *pxQueueParameters; +short sErrorEverOccurred = pdFALSE; + + pxQueueParameters = ( xBlockingQueueParameters * ) pvParameters; + + for( ;; ) + { + if( xQueueReceive( pxQueueParameters->xQueue, &usData, pxQueueParameters->xBlockTime ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + /* Catch-up. */ + usExpectedValue = usData; + + sErrorEverOccurred = pdTRUE; + } + else + { + /* We have successfully received a message, so increment the + variable used to check we are still running. */ + if( sErrorEverOccurred == pdFALSE ) + { + ( *pxQueueParameters->psCheckVariable )++; + } + + /* Increment the value we expect to remove from the queue next time + round. */ + ++usExpectedValue; + } + + #if configUSE_PREEMPTION == 0 + { + if( pxQueueParameters->xBlockTime == 0 ) + { + taskYIELD(); + } + } + #endif + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreBlockingQueuesStillRunning( void ) +{ +static short sLastBlockingConsumerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; +static short sLastBlockingProducerCount[ blckqNUM_TASK_SETS ] = { ( uint16_t ) 0, ( uint16_t ) 0, ( uint16_t ) 0 }; +BaseType_t xReturn = pdPASS, xTasks; + + /* Not too worried about mutual exclusion on these variables as they are 16 + bits and we are only reading them. We also only care to see if they have + changed or not. + + Loop through each check variable to and return pdFALSE if any are found not + to have changed since the last call. */ + + for( xTasks = 0; xTasks < blckqNUM_TASK_SETS; xTasks++ ) + { + if( sBlockingConsumerCount[ xTasks ] == sLastBlockingConsumerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingConsumerCount[ xTasks ] = sBlockingConsumerCount[ xTasks ]; + + + if( sBlockingProducerCount[ xTasks ] == sLastBlockingProducerCount[ xTasks ] ) + { + xReturn = pdFALSE; + } + sLastBlockingProducerCount[ xTasks ] = sBlockingProducerCount[ xTasks ]; + } + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/EventGroupsDemo.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/EventGroupsDemo.c new file mode 100644 index 0000000..64d7430 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/EventGroupsDemo.c @@ -0,0 +1,1074 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + + +/* +* This file contains fairly comprehensive checks on the behaviour of event +* groups. It is not intended to be a user friendly demonstration of the +* event groups API. +* +* NOTE: The tests implemented in this file are informal 'sanity' tests +* only and are not part of the module tests that make use of the +* mtCOVERAGE_TEST_MARKER macro within the event groups implementation. +*/ + + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "event_groups.h" + +/* Demo app includes. */ +#include "EventGroupsDemo.h" + +#if( INCLUDE_eTaskGetState != 1 ) + #error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file. +#endif + +/* Priorities used by the tasks. */ +#define ebSET_BIT_TASK_PRIORITY ( tskIDLE_PRIORITY ) +#define ebWAIT_BIT_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +/* Generic bit definitions. */ +#define ebBIT_0 ( 0x01UL ) +#define ebBIT_1 ( 0x02UL ) +#define ebBIT_2 ( 0x04UL ) +#define ebBIT_3 ( 0x08UL ) +#define ebBIT_4 ( 0x10UL ) +#define ebBIT_5 ( 0x20UL ) +#define ebBIT_6 ( 0x40UL ) +#define ebBIT_7 ( 0x80UL ) + +/* Combinations of bits used in the demo. */ +#define ebCOMBINED_BITS ( ebBIT_1 | ebBIT_5 | ebBIT_7 ) +#define ebALL_BITS ( ebBIT_0 | ebBIT_1 | ebBIT_2 | ebBIT_3 | ebBIT_4 | ebBIT_5 | ebBIT_6 | ebBIT_7 ) + +/* Associate a bit to each task. These bits are used to identify all the tasks +that synchronise with the xEventGroupSync() function. */ +#define ebSET_BIT_TASK_SYNC_BIT ebBIT_0 +#define ebWAIT_BIT_TASK_SYNC_BIT ebBIT_1 +#define ebRENDESVOUS_TASK_1_SYNC_BIT ebBIT_2 +#define ebRENDESVOUS_TASK_2_SYNC_BIT ebBIT_3 +#define ebALL_SYNC_BITS ( ebSET_BIT_TASK_SYNC_BIT | ebWAIT_BIT_TASK_SYNC_BIT | ebRENDESVOUS_TASK_1_SYNC_BIT | ebRENDESVOUS_TASK_2_SYNC_BIT ) + +/* A block time of zero simply means "don't block". */ +#define ebDONT_BLOCK ( 0 ) + +/* A 5ms delay. */ +#define ebSHORT_DELAY ( 5 / portTICK_PERIOD_MS ) + +/* Used in the selective bits test which checks no, one or both tasks blocked on +event bits in a group are unblocked as appropriate as different bits get set. */ +#define ebSELECTIVE_BITS_1 0x03 +#define ebSELECTIVE_BITS_2 0x05 + +/*-----------------------------------------------------------*/ + +/* + * NOTE: The tests implemented in this function are informal 'sanity' tests + * only and are not part of the module tests that make use of the + * mtCOVERAGE_TEST_MARKER macro within the event groups implementation. + * + * The master test task. This task: + * + * 1) Calls prvSelectiveBitsTestMasterFunction() to test the behaviour when two + * tasks are blocked on different bits in an event group. The counterpart of + * this test is implemented by the prvSelectiveBitsTestSlaveFunction() + * function (which is called by the two tasks that block on the event group). + * + * 2) Calls prvBitCombinationTestMasterFunction() to test the behaviour when + * just one task is blocked on various combinations of bits within an event + * group. The counterpart of this test is implemented within the 'test + * slave' task. + * + * 3) Calls prvPerformTaskSyncTests() to test task synchronisation behaviour. + */ +static void prvTestMasterTask( void *pvParameters ); + +/* + * A helper task that enables the 'test master' task to perform several + * behavioural tests. See the comments above the prvTestMasterTask() prototype + * above. + */ +static void prvTestSlaveTask( void *pvParameters ); + +/* + * The part of the test that is performed between the 'test master' task and the + * 'test slave' task to test the behaviour when the slave blocks on various + * event bit combinations. + */ +static BaseType_t prvBitCombinationTestMasterFunction( BaseType_t xError, TaskHandle_t xTestSlaveTaskHandle ); + +/* + * The part of the test that uses all the tasks to test the task synchronisation + * behaviour. + */ +static BaseType_t prvPerformTaskSyncTests( BaseType_t xError, TaskHandle_t xTestSlaveTaskHandle ); + +/* + * Two instances of prvSyncTask() are created. They start by calling + * prvSelectiveBitsTestSlaveFunction() to act as slaves when the test master is + * executing the prvSelectiveBitsTestMasterFunction() function. They then loop + * to test the task synchronisation (rendezvous) behaviour. + */ +static void prvSyncTask( void *pvParameters ); + +/* + * Functions used in a test that blocks two tasks on various different bits + * within an event group - then sets each bit in turn and checks that the + * correct tasks unblock at the correct times. + */ +static BaseType_t prvSelectiveBitsTestMasterFunction( void ); +static void prvSelectiveBitsTestSlaveFunction( void ); + +/*-----------------------------------------------------------*/ + +/* Variables that are incremented by the tasks on each cycle provided no errors +have been found. Used to detect an error or stall in the test cycling. */ +static volatile uint32_t ulTestMasterCycles = 0, ulTestSlaveCycles = 0, ulISRCycles = 0; + +/* The event group used by all the task based tests. */ +static EventGroupHandle_t xEventGroup = NULL; + +/* The event group used by the interrupt based tests. */ +static EventGroupHandle_t xISREventGroup = NULL; + +/* Handles to the tasks that only take part in the synchronisation calls. */ +static TaskHandle_t xSyncTask1 = NULL, xSyncTask2 = NULL; + +/*-----------------------------------------------------------*/ + +void vStartEventGroupTasks( void ) +{ +TaskHandle_t xTestSlaveTaskHandle; + + /* + * This file contains fairly comprehensive checks on the behaviour of event + * groups. It is not intended to be a user friendly demonstration of the + * event groups API. + * + * NOTE: The tests implemented in this file are informal 'sanity' tests + * only and are not part of the module tests that make use of the + * mtCOVERAGE_TEST_MARKER macro within the event groups implementation. + * + * Create the test tasks as described at the top of this file. + */ + xTaskCreate( prvTestSlaveTask, "WaitO", configMINIMAL_STACK_SIZE, NULL, ebWAIT_BIT_TASK_PRIORITY, &xTestSlaveTaskHandle ); + xTaskCreate( prvTestMasterTask, "SetB", configMINIMAL_STACK_SIZE, ( void * ) xTestSlaveTaskHandle, ebSET_BIT_TASK_PRIORITY, NULL ); + xTaskCreate( prvSyncTask, "Rndv", configMINIMAL_STACK_SIZE, ( void * ) ebRENDESVOUS_TASK_1_SYNC_BIT, ebWAIT_BIT_TASK_PRIORITY, &xSyncTask1 ); + xTaskCreate( prvSyncTask, "Rndv", configMINIMAL_STACK_SIZE, ( void * ) ebRENDESVOUS_TASK_2_SYNC_BIT, ebWAIT_BIT_TASK_PRIORITY, &xSyncTask2 ); + + /* If the last task was created then the others will have been too. */ + configASSERT( xSyncTask2 ); + + /* Create the event group used by the ISR tests. The event group used by + the tasks is created by the tasks themselves. */ + xISREventGroup = xEventGroupCreate(); + configASSERT( xISREventGroup ); +} +/*-----------------------------------------------------------*/ + +static void prvTestMasterTask( void *pvParameters ) +{ +BaseType_t xError; + +/* The handle to the slave task is passed in as the task parameter. */ +TaskHandle_t xTestSlaveTaskHandle = ( TaskHandle_t ) pvParameters; + + /* Avoid compiler warnings. */ + ( void ) pvParameters; + + /* Create the event group used by the tasks ready for the initial tests. */ + xEventGroup = xEventGroupCreate(); + configASSERT( xEventGroup ); + + /* Perform the tests that block two tasks on different combinations of bits, + then set each bit in turn and check the correct tasks unblock at the correct + times. */ + xError = prvSelectiveBitsTestMasterFunction(); + + for( ;; ) + { + /* Recreate the event group ready for the next cycle. */ + xEventGroup = xEventGroupCreate(); + configASSERT( xEventGroup ); + + /* Perform the tests that check the behaviour when a single task is + blocked on various combinations of event bits. */ + xError = prvBitCombinationTestMasterFunction( xError, xTestSlaveTaskHandle ); + + /* Perform the task synchronisation tests. */ + xError = prvPerformTaskSyncTests( xError, xTestSlaveTaskHandle ); + + /* Delete the event group. */ + vEventGroupDelete( xEventGroup ); + + /* Now all the other tasks should have completed and suspended + themselves ready for the next go around the loop. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eSuspended ) + { + xError = pdTRUE; + } + + /* Only increment the cycle variable if no errors have been detected. */ + if( xError == pdFALSE ) + { + ulTestMasterCycles++; + } + + configASSERT( xError == pdFALSE ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSyncTask( void *pvParameters ) +{ +EventBits_t uxSynchronisationBit, uxReturned; + + /* A few tests that check the behaviour when two tasks are blocked on + various different bits within an event group are performed before this task + enters its infinite loop to carry out its main demo function. */ + prvSelectiveBitsTestSlaveFunction(); + + /* The bit to use to indicate this task is at the synchronisation point is + passed in as the task parameter. */ + uxSynchronisationBit = ( EventBits_t ) pvParameters; + + for( ;; ) + { + /* Now this task takes part in a task synchronisation - sometimes known + as a 'rendezvous'. Its execution pattern is controlled by the 'test + master' task, which is responsible for taking this task out of the + Suspended state when it is time to test the synchronisation behaviour. + See: http://www.freertos.org/xEventGroupSync.html. */ + vTaskSuspend( NULL ); + + /* Set the bit that indicates this task is at the synchronisation + point. The first time this is done the 'test master' task has a lower + priority than this task so this task will get to the sync point before + the set bits task. */ + uxReturned = xEventGroupSync( xEventGroup, /* The event group used for the synchronisation. */ + uxSynchronisationBit, /* The bit to set in the event group to indicate this task is at the sync point. */ + ebALL_SYNC_BITS,/* The bits to wait for - these bits are set by the other tasks taking part in the sync. */ + portMAX_DELAY );/* The maximum time to wait for the sync condition to be met before giving up. */ + + /* A max delay was used, so this task should only exit the above + function call when the sync condition is met. Check this is the + case. */ + configASSERT( ( uxReturned & ebALL_SYNC_BITS ) == ebALL_SYNC_BITS ); + + /* Remove compiler warning if configASSERT() is not defined. */ + ( void ) uxReturned; + + /* Wait until the 'test master' task unsuspends this task again. */ + vTaskSuspend( NULL ); + + /* Set the bit that indicates this task is at the synchronisation + point again. This time the 'test master' task has a higher priority + than this task so will get to the sync point before this task. */ + uxReturned = xEventGroupSync( xEventGroup, uxSynchronisationBit, ebALL_SYNC_BITS, portMAX_DELAY ); + + /* Again a max delay was used, so this task should only exit the above + function call when the sync condition is met. Check this is the + case. */ + configASSERT( ( uxReturned & ebALL_SYNC_BITS ) == ebALL_SYNC_BITS ); + + /* Block on the event group again. This time the event group is going + to be deleted while this task is blocked on it so it is expected that 0 + be returned. */ + uxReturned = xEventGroupWaitBits( xEventGroup, ebALL_SYNC_BITS, pdFALSE, pdTRUE, portMAX_DELAY ); + configASSERT( uxReturned == 0 ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTestSlaveTask( void *pvParameters ) +{ +EventBits_t uxReturned; +BaseType_t xError = pdFALSE; + + /* Avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /********************************************************************** + * Part 1: This section is the counterpart to the + * prvBitCombinationTestMasterFunction() function which is called by the + * test master task. + *********************************************************************** + + This task is controller by the 'test master' task (which is + implemented by prvTestMasterTask()). Suspend until resumed by the + 'test master' task. */ + vTaskSuspend( NULL ); + + /* Wait indefinitely for one of the bits in ebCOMBINED_BITS to get + set. Clear the bit on exit. */ + uxReturned = xEventGroupWaitBits( xEventGroup, /* The event group that contains the event bits being queried. */ + ebBIT_1, /* The bit to wait for. */ + pdTRUE, /* Clear the bit on exit. */ + pdTRUE, /* Wait for all the bits (only one in this case anyway). */ + portMAX_DELAY ); /* Block indefinitely to wait for the condition to be met. */ + + /* The 'test master' task set all the bits defined by ebCOMBINED_BITS, + only one of which was being waited for by this task. The return value + shows the state of the event bits when the task was unblocked, however + because the task was waiting for ebBIT_1 and 'clear on exit' was set to + the current state of the event bits will have ebBIT_1 clear. */ + if( uxReturned != ebCOMBINED_BITS ) + { + xError = pdTRUE; + } + + /* Now call xEventGroupWaitBits() again, this time waiting for all the + bits in ebCOMBINED_BITS to be set. This call should block until the + 'test master' task sets ebBIT_1 - which was the bit cleared in the call + to xEventGroupWaitBits() above. */ + uxReturned = xEventGroupWaitBits( xEventGroup, + ebCOMBINED_BITS, /* The bits being waited on. */ + pdFALSE, /* Don't clear the bits on exit. */ + pdTRUE, /* All the bits must be set to unblock. */ + portMAX_DELAY ); + + /* Were all the bits set? */ + if( ( uxReturned & ebCOMBINED_BITS ) != ebCOMBINED_BITS ) + { + xError = pdTRUE; + } + + /* Suspend again to wait for the 'test master' task. */ + vTaskSuspend( NULL ); + + /* Now call xEventGroupWaitBits() again, again waiting for all the bits + in ebCOMBINED_BITS to be set, but this time clearing the bits when the + task is unblocked. */ + uxReturned = xEventGroupWaitBits( xEventGroup, + ebCOMBINED_BITS, /* The bits being waited on. */ + pdTRUE, /* Clear the bits on exit. */ + pdTRUE, /* All the bits must be set to unblock. */ + portMAX_DELAY ); + + /* The 'test master' task set all the bits in the event group, so that + is the value that should have been returned. The bits defined by + ebCOMBINED_BITS will have been clear again in the current value though + as 'clear on exit' was set to pdTRUE. */ + if( uxReturned != ebALL_BITS ) + { + xError = pdTRUE; + } + + + + + + /********************************************************************** + * Part 2: This section is the counterpart to the + * prvPerformTaskSyncTests() function which is called by the + * test master task. + *********************************************************************** + + + Once again wait for the 'test master' task to unsuspend this task + when it is time for the next test. */ + vTaskSuspend( NULL ); + + /* Now peform a synchronisation with all the other tasks. At this point + the 'test master' task has the lowest priority so will get to the sync + point after all the other synchronising tasks. */ + uxReturned = xEventGroupSync( xEventGroup, /* The event group used for the sync. */ + ebWAIT_BIT_TASK_SYNC_BIT, /* The bit in the event group used to indicate this task is at the sync point. */ + ebALL_SYNC_BITS, /* The bits to wait for. These bits are set by the other tasks taking part in the sync. */ + portMAX_DELAY ); /* The maximum time to wait for the sync condition to be met before giving up. */ + + /* A sync with a max delay should only exit when all the synchronisation + bits are set... */ + if( ( uxReturned & ebALL_SYNC_BITS ) != ebALL_SYNC_BITS ) + { + xError = pdTRUE; + } + + /* ...but now the synchronisation bits should be clear again. Read back + the current value of the bits within the event group to check that is + the case. Setting the bits to zero will return the bits previous value + then leave all the bits clear. */ + if( xEventGroupSetBits( xEventGroup, 0x00 ) != 0 ) + { + xError = pdTRUE; + } + + /* Check the bits are indeed 0 now by simply reading then. */ + if( xEventGroupGetBits( xEventGroup ) != 0 ) + { + xError = pdTRUE; + } + + if( xError == pdFALSE ) + { + /* This task is still cycling without finding an error. */ + ulTestSlaveCycles++; + } + + vTaskSuspend( NULL ); + + /* This time sync when the 'test master' task has the highest priority + at the point where it sets its sync bit - so this time the 'test master' + task will get to the sync point before this task. */ + uxReturned = xEventGroupSync( xEventGroup, ebWAIT_BIT_TASK_SYNC_BIT, ebALL_SYNC_BITS, portMAX_DELAY ); + + /* A sync with a max delay should only exit when all the synchronisation + bits are set... */ + if( ( uxReturned & ebALL_SYNC_BITS ) != ebALL_SYNC_BITS ) + { + xError = pdTRUE; + } + + /* ...but now the sync bits should be clear again. */ + if( xEventGroupSetBits( xEventGroup, 0x00 ) != 0 ) + { + xError = pdTRUE; + } + + /* Block on the event group again. This time the event group is going + to be deleted while this task is blocked on it, so it is expected that 0 + will be returned. */ + uxReturned = xEventGroupWaitBits( xEventGroup, ebALL_SYNC_BITS, pdFALSE, pdTRUE, portMAX_DELAY ); + + if( uxReturned != 0 ) + { + xError = pdTRUE; + } + + if( xError == pdFALSE ) + { + /* This task is still cycling without finding an error. */ + ulTestSlaveCycles++; + } + + configASSERT( xError == pdFALSE ); + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvPerformTaskSyncTests( BaseType_t xError, TaskHandle_t xTestSlaveTaskHandle ) +{ +EventBits_t uxBits; + + /* The three tasks that take part in the synchronisation (rendezvous) are + expected to be in the suspended state at the start of the test. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eSuspended ) + { + xError = pdTRUE; + } + + /* Try a synch with no other tasks involved. First set all the bits other + than this task's bit. */ + xEventGroupSetBits( xEventGroup, ( ebALL_SYNC_BITS & ~ebSET_BIT_TASK_SYNC_BIT ) ); + + /* Then wait on just one bit - the bit that is being set. */ + uxBits = xEventGroupSync( xEventGroup, /* The event group used for the synchronisation. */ + ebSET_BIT_TASK_SYNC_BIT,/* The bit set by this task when it reaches the sync point. */ + ebSET_BIT_TASK_SYNC_BIT,/* The bits to wait for - in this case it is just waiting for itself. */ + portMAX_DELAY ); /* The maximum time to wait for the sync condition to be met. */ + + /* A sync with a max delay should only exit when all the synchronise + bits are set...check that is the case. In this case there is only one + sync bit anyway. */ + if( ( uxBits & ebSET_BIT_TASK_SYNC_BIT ) != ebSET_BIT_TASK_SYNC_BIT ) + { + xError = pdTRUE; + } + + /* ...but now the sync bits should be clear again, leaving all the other + bits set (as only one bit was being waited for). */ + if( xEventGroupGetBits( xEventGroup ) != ( ebALL_SYNC_BITS & ~ebSET_BIT_TASK_SYNC_BIT ) ) + { + xError = pdTRUE; + } + + /* Clear all the bits to zero again. */ + xEventGroupClearBits( xEventGroup, ( ebALL_SYNC_BITS & ~ebSET_BIT_TASK_SYNC_BIT ) ); + if( xEventGroupGetBits( xEventGroup ) != 0 ) + { + xError = pdTRUE; + } + + /* Unsuspend the other tasks then check they have executed up to the + synchronisation point. */ + vTaskResume( xTestSlaveTaskHandle ); + vTaskResume( xSyncTask1 ); + vTaskResume( xSyncTask2 ); + + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Set this task's sync bit. */ + uxBits = xEventGroupSync( xEventGroup, /* The event group used for the synchronisation. */ + ebSET_BIT_TASK_SYNC_BIT,/* The bit set by this task when it reaches the sync point. */ + ebALL_SYNC_BITS, /* The bits to wait for - these bits are set by the other tasks that take part in the sync. */ + portMAX_DELAY ); /* The maximum time to wait for the sync condition to be met. */ + + /* A sync with a max delay should only exit when all the synchronise + bits are set...check that is the case. */ + if( ( uxBits & ebALL_SYNC_BITS ) != ebALL_SYNC_BITS ) + { + xError = pdTRUE; + } + + /* ...but now the sync bits should be clear again. */ + if( xEventGroupGetBits( xEventGroup ) != 0 ) + { + xError = pdTRUE; + } + + + /* The other tasks should now all be suspended again, ready for the next + synchronisation. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eSuspended ) + { + xError = pdTRUE; + } + + + /* Sync again - but this time set the last necessary bit as the + highest priority task, rather than the lowest priority task. Unsuspend + the other tasks then check they have executed up to the synchronisation + point. */ + vTaskResume( xTestSlaveTaskHandle ); + vTaskResume( xSyncTask1 ); + vTaskResume( xSyncTask2 ); + + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Raise the priority of this task above that of the other tasks. */ + vTaskPrioritySet( NULL, ebWAIT_BIT_TASK_PRIORITY + 1 ); + + /* Set this task's sync bit. */ + uxBits = xEventGroupSync( xEventGroup, ebSET_BIT_TASK_SYNC_BIT, ebALL_SYNC_BITS, portMAX_DELAY ); + + /* A sync with a max delay should only exit when all the synchronisation + bits are set... */ + if( ( uxBits & ebALL_SYNC_BITS ) != ebALL_SYNC_BITS ) + { + xError = pdTRUE; + } + + /* ...but now the sync bits should be clear again. */ + if( xEventGroupGetBits( xEventGroup ) != 0 ) + { + xError = pdTRUE; + } + + + /* The other tasks should now all be in the ready state again, but not + executed yet as this task still has a higher relative priority. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eReady ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eReady ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eReady ) + { + xError = pdTRUE; + } + + + /* Reset the priority of this task back to its original value. */ + vTaskPrioritySet( NULL, ebSET_BIT_TASK_PRIORITY ); + + /* Now all the other tasks should have reblocked on the event bits + to test the behaviour when the event bits are deleted. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask1 ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eBlocked ) + { + xError = pdTRUE; + } + + return xError; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvBitCombinationTestMasterFunction( BaseType_t xError, TaskHandle_t xTestSlaveTaskHandle ) +{ +EventBits_t uxBits; + + /* Resume the other task. It will block, pending a single bit from + within ebCOMBINED_BITS. */ + vTaskResume( xTestSlaveTaskHandle ); + + /* Ensure the other task is blocked on the task. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Set all the bits in ebCOMBINED_BITS - the 'test slave' task is only + blocked waiting for one of them. */ + xEventGroupSetBits( xEventGroup, ebCOMBINED_BITS ); + + /* The 'test slave' task should now have executed, clearing ebBIT_1 (the + bit it was blocked on), then re-entered the Blocked state to wait for + all the other bits in ebCOMBINED_BITS to be set again. First check + ebBIT_1 is clear. */ + uxBits = xEventGroupWaitBits( xEventGroup, ebALL_BITS, pdFALSE, pdFALSE, ebDONT_BLOCK ); + + if( uxBits != ( ebCOMBINED_BITS & ~ebBIT_1 ) ) + { + xError = pdTRUE; + } + + /* Ensure the other task is still in the blocked state. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Set all the bits other than ebBIT_1 - which is the bit that must be + set before the other task unblocks. */ + xEventGroupSetBits( xEventGroup, ebALL_BITS & ~ebBIT_1 ); + + /* Ensure all the expected bits are still set. */ + uxBits = xEventGroupWaitBits( xEventGroup, ebALL_BITS, pdFALSE, pdFALSE, ebDONT_BLOCK ); + + if( uxBits != ( ebALL_BITS & ~ebBIT_1 ) ) + { + xError = pdTRUE; + } + + /* Ensure the other task is still in the blocked state. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Now also set ebBIT_1, which should unblock the other task, which will + then suspend itself. */ + xEventGroupSetBits( xEventGroup, ebBIT_1 ); + + /* Ensure the other task is suspended. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eSuspended ) + { + xError = pdTRUE; + } + + /* The other task should not have cleared the bits - so all the bits + should still be set. */ + if( xEventGroupSetBits( xEventGroup, 0x00 ) != ebALL_BITS ) + { + xError = pdTRUE; + } + + /* Clear ebBIT_1 again. */ + if( xEventGroupClearBits( xEventGroup, ebBIT_1 ) != ebALL_BITS ) + { + xError = pdTRUE; + } + + /* Resume the other task - which will wait on all the ebCOMBINED_BITS + again - this time clearing the bits when it is unblocked. */ + vTaskResume( xTestSlaveTaskHandle ); + + /* Ensure the other task is blocked once again. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Set the bit the other task is waiting for. */ + xEventGroupSetBits( xEventGroup, ebBIT_1 ); + + /* Ensure the other task is suspended once again. */ + if( eTaskGetState( xTestSlaveTaskHandle ) != eSuspended ) + { + xError = pdTRUE; + } + + /* The other task should have cleared the bits in ebCOMBINED_BITS. + Clear the remaining bits. */ + uxBits = xEventGroupWaitBits( xEventGroup, ebALL_BITS, pdFALSE, pdFALSE, ebDONT_BLOCK ); + + if( uxBits != ( ebALL_BITS & ~ebCOMBINED_BITS ) ) + { + xError = pdTRUE; + } + + /* Clear all bits ready for the sync with the other three tasks. The + value returned is the value prior to the bits being cleared. */ + if( xEventGroupClearBits( xEventGroup, ebALL_BITS ) != ( ebALL_BITS & ~ebCOMBINED_BITS ) ) + { + xError = pdTRUE; + } + + /* The bits should be clear now. */ + if( xEventGroupGetBits( xEventGroup ) != 0x00 ) + { + xError = pdTRUE; + } + + return xError; +} +/*-----------------------------------------------------------*/ + +static void prvSelectiveBitsTestSlaveFunction( void ) +{ +EventBits_t uxPendBits, uxReturned; + + /* Used in a test that blocks two tasks on various different bits within an + event group - then sets each bit in turn and checks that the correct tasks + unblock at the correct times. + + This function is called by two different tasks - each of which will use a + different bit. Check the task handle to see which task the function was + called by. */ + if( xTaskGetCurrentTaskHandle() == xSyncTask1 ) + { + uxPendBits = ebSELECTIVE_BITS_1; + } + else + { + uxPendBits = ebSELECTIVE_BITS_2; + } + + for( ;; ) + { + /* Wait until it is time to perform the next cycle of the test. The + task is unsuspended by the tests implemented in the + prvSelectiveBitsTestMasterFunction() function. */ + vTaskSuspend( NULL ); + uxReturned = xEventGroupWaitBits( xEventGroup, uxPendBits, pdTRUE, pdFALSE, portMAX_DELAY ); + + if( uxReturned == ( EventBits_t ) 0 ) + { + break; + } + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvSelectiveBitsTestMasterFunction( void ) +{ +BaseType_t xError = pdFALSE; +EventBits_t uxBit; + + /* Used in a test that blocks two tasks on various different bits within an + event group - then sets each bit in turn and checks that the correct tasks + unblock at the correct times. The two other tasks (xSyncTask1 and + xSyncTask2) call prvSelectiveBitsTestSlaveFunction() to perform their parts in + this test. + + Both other tasks should start in the suspended state. */ + if( eTaskGetState( xSyncTask1 ) != eSuspended ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eSuspended ) + { + xError = pdTRUE; + } + + /* Test each bit in the byte individually. */ + for( uxBit = 0x01; uxBit < 0x100; uxBit <<= 1 ) + { + /* Resume both tasks. */ + vTaskResume( xSyncTask1 ); + vTaskResume( xSyncTask2 ); + + /* Now both tasks should be blocked on the event group. */ + if( eTaskGetState( xSyncTask1 ) != eBlocked ) + { + xError = pdTRUE; + } + + if( eTaskGetState( xSyncTask2 ) != eBlocked ) + { + xError = pdTRUE; + } + + /* Set one bit. */ + xEventGroupSetBits( xEventGroup, uxBit ); + + /* Is the bit set in the first set of selective bits? If so the first + sync task should have unblocked and returned to the suspended state. */ + if( ( uxBit & ebSELECTIVE_BITS_1 ) == 0 ) + { + /* Task should not have unblocked. */ + if( eTaskGetState( xSyncTask1 ) != eBlocked ) + { + xError = pdTRUE; + } + } + else + { + /* Task should have unblocked and returned to the suspended state. */ + if( eTaskGetState( xSyncTask1 ) != eSuspended ) + { + xError = pdTRUE; + } + } + + /* Same checks for the second sync task. */ + if( ( uxBit & ebSELECTIVE_BITS_2 ) == 0 ) + { + /* Task should not have unblocked. */ + if( eTaskGetState( xSyncTask2 ) != eBlocked ) + { + xError = pdTRUE; + } + } + else + { + /* Task should have unblocked and returned to the suspended state. */ + if( eTaskGetState( xSyncTask2 ) != eSuspended ) + { + xError = pdTRUE; + } + } + } + + /* Ensure both tasks are blocked on the event group again, then delete the + event group so the other tasks leave this portion of the test. */ + vTaskResume( xSyncTask1 ); + vTaskResume( xSyncTask2 ); + + /* Deleting the event group is the signal that the two other tasks should + leave the prvSelectiveBitsTestSlaveFunction() function and continue to the main + part of their functionality. */ + vEventGroupDelete( xEventGroup ); + + return xError; +} +/*-----------------------------------------------------------*/ + +void vPeriodicEventGroupsProcessing( void ) +{ +static BaseType_t xCallCount = 0, xISRTestError = pdFALSE; +const BaseType_t xSetBitCount = 100, xGetBitsCount = 200, xClearBitsCount = 300; +const EventBits_t uxBitsToSet = 0x12U; +EventBits_t uxReturned; +BaseType_t xMessagePosted; + + /* Called periodically from the tick hook to exercise the "FromISR" + functions. */ + + xCallCount++; + + if( xCallCount == xSetBitCount ) + { + /* All the event bits should start clear. */ + uxReturned = xEventGroupGetBitsFromISR( xISREventGroup ); + if( uxReturned != 0x00 ) + { + xISRTestError = pdTRUE; + } + else + { + /* Set the bits. This is called from the tick hook so it is not + necessary to use the last parameter to ensure a context switch + occurs immediately. */ + xMessagePosted = xEventGroupSetBitsFromISR( xISREventGroup, uxBitsToSet, NULL ); + if( xMessagePosted != pdPASS ) + { + xISRTestError = pdTRUE; + } + } + } + else if( xCallCount == xGetBitsCount ) + { + /* Check the bits were set as expected. */ + uxReturned = xEventGroupGetBitsFromISR( xISREventGroup ); + if( uxReturned != uxBitsToSet ) + { + xISRTestError = pdTRUE; + } + } + else if( xCallCount == xClearBitsCount ) + { + /* Clear the bits again. */ + uxReturned = xEventGroupClearBitsFromISR( xISREventGroup, uxBitsToSet ); + + /* Check the message was posted. */ + if( uxReturned != pdPASS ) + { + xISRTestError = pdTRUE; + } + + /* Go back to the start. */ + xCallCount = 0; + + /* If no errors have been detected then increment the count of test + cycles. */ + if( xISRTestError == pdFALSE ) + { + ulISRCycles++; + } + } + else + { + /* Nothing else to do. */ + } +} + +/*-----------------------------------------------------------*/ +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreEventGroupTasksStillRunning( void ) +{ +static uint32_t ulPreviousWaitBitCycles = 0, ulPreviousSetBitCycles = 0, ulPreviousISRCycles = 0; +BaseType_t xStatus = pdPASS; + + /* Check the tasks are still cycling without finding any errors. */ + if( ulPreviousSetBitCycles == ulTestMasterCycles ) + { + xStatus = pdFAIL; + } + ulPreviousSetBitCycles = ulTestMasterCycles; + + if( ulPreviousWaitBitCycles == ulTestSlaveCycles ) + { + xStatus = pdFAIL; + } + ulPreviousWaitBitCycles = ulTestSlaveCycles; + + if( ulPreviousISRCycles == ulISRCycles ) + { + xStatus = pdFAIL; + } + ulPreviousISRCycles = ulISRCycles; + + return xStatus; +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/GenQTest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/GenQTest.c new file mode 100644 index 0000000..3da052a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/GenQTest.c @@ -0,0 +1,857 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 - + * including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and + * mutex behaviour. + * + * See the comments above the prvSendFrontAndBackTest() and + * prvLowPriorityMutexTask() prototypes below for more information. + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* Demo program include files. */ +#include "GenQTest.h" + +#define genqQUEUE_LENGTH ( 5 ) +#define genqNO_BLOCK ( 0 ) + +#define genqMUTEX_LOW_PRIORITY ( tskIDLE_PRIORITY ) +#define genqMUTEX_TEST_PRIORITY ( tskIDLE_PRIORITY + 1 ) +#define genqMUTEX_MEDIUM_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define genqMUTEX_HIGH_PRIORITY ( tskIDLE_PRIORITY + 3 ) + +#define genqINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 ) +/*-----------------------------------------------------------*/ + +/* + * Tests the behaviour of the xQueueSendToFront() and xQueueSendToBack() + * macros by using both to fill a queue, then reading from the queue to + * check the resultant queue order is as expected. Queue data is also + * peeked. + */ +static void prvSendFrontAndBackTest( void *pvParameters ); + +/* + * The following three tasks are used to demonstrate the mutex behaviour. + * Each task is given a different priority to demonstrate the priority + * inheritance mechanism. + * + * The low priority task obtains a mutex. After this a high priority task + * attempts to obtain the same mutex, causing its priority to be inherited + * by the low priority task. The task with the inherited high priority then + * resumes a medium priority task to ensure it is not blocked by the medium + * priority task while it holds the inherited high priority. Once the mutex + * is returned the task with the inherited priority returns to its original + * low priority, and is therefore immediately preempted by first the high + * priority task and then the medium prioroity task before it can continue. + */ +static void prvLowPriorityMutexTask( void *pvParameters ); +static void prvMediumPriorityMutexTask( void *pvParameters ); +static void prvHighPriorityMutexTask( void *pvParameters ); + +/* + * Exercises the priority inheritance when a task takes two mutexes, returning + * them in a different order to which they were taken. + */ +static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex ); + +/* + * Exercises the priority inheritance when a task takes two mutexes, returning + * them in the same order in which they were taken. + */ +static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex ); + +/* + * Task that receives an a mutex that is given from an interrupt - although + * generally mutexes should not be used given in interrupts (and definitely + * never taken in an interrupt) there are some circumstances when it may be + * desirable. NOTE: This function is not declared static to prevent compiler + * warnings being generated in demos where the function is declared but not + * used. + */ +void vInterruptMutexTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +/* Flag that will be latched to pdTRUE should any unexpected behaviour be +detected in any of the tasks. */ +static volatile BaseType_t xErrorDetected = pdFALSE; + +/* Counters that are incremented on each cycle of a test. This is used to +detect a stalled task - a test that is no longer running. */ +static volatile uint32_t ulLoopCounter = 0; +static volatile uint32_t ulLoopCounter2 = 0; + +/* The variable that is guarded by the mutex in the mutex demo tasks. */ +static volatile uint32_t ulGuardedVariable = 0; + +/* Handles used in the mutext test to suspend and resume the high and medium +priority mutex test tasks. */ +static TaskHandle_t xHighPriorityMutexTask, xMediumPriorityMutexTask; + +/* A mutex which is given from an interrupt - although generally mutexes should +not be used given in interrupts (and definitely never taken in an interrupt) +there are some circumstances when it may be desirable. */ +static SemaphoreHandle_t xISRMutex = NULL; + +/*-----------------------------------------------------------*/ + +void vStartGenericQueueTasks( UBaseType_t uxPriority ) +{ +QueueHandle_t xQueue; +SemaphoreHandle_t xMutex; + + xISRMutex = xSemaphoreCreateMutex(); + configASSERT( xISRMutex ); + + /* Create the queue that we are going to use for the + prvSendFrontAndBackTest demo. */ + xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( uint32_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xQueue, "Gen_Queue_Test" ); + + /* Create the demo task and pass it the queue just created. We are + passing the queue handle by value so it does not matter that it is + declared on the stack here. */ + xTaskCreate( prvSendFrontAndBackTest, "GenQ", configMINIMAL_STACK_SIZE, ( void * ) xQueue, uxPriority, NULL ); + + /* Create the mutex used by the prvMutexTest task. */ + xMutex = xSemaphoreCreateMutex(); + + /* vQueueAddToRegistry() adds the mutex to the registry, if one is + in use. The registry is provided as a means for kernel aware + debuggers to locate mutexes and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Gen_Queue_Mutex" ); + + /* Create the mutex demo tasks and pass it the mutex just created. We are + passing the mutex handle by value so it does not matter that it is declared + on the stack here. */ + xTaskCreate( prvLowPriorityMutexTask, "MuLow", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL ); + xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask ); + xTaskCreate( prvHighPriorityMutexTask, "MuHigh", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask ); + + /* Only when the windows simulator is being used - create the task that + receives a mutex from an interrupt. */ + #ifdef _WINDOWS_ + { + xTaskCreate( vInterruptMutexTask, "IntMu", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, NULL ); + } + #endif /* __WINDOWS__ */ +} +/*-----------------------------------------------------------*/ + +static void prvSendFrontAndBackTest( void *pvParameters ) +{ +uint32_t ulData, ulData2; +QueueHandle_t xQueue; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Queue SendToFront/SendToBack/Peek test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + xQueue = ( QueueHandle_t ) pvParameters; + + for( ;; ) + { + /* The queue is empty, so sending an item to the back of the queue + should have the same efect as sending it to the front of the queue. + + First send to the front and check everything is as expected. */ + xQueueSendToFront( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK ); + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* The data we sent to the queue should equal the data we just received + from the queue. */ + if( ulLoopCounter != ulData ) + { + xErrorDetected = pdTRUE; + } + + /* Then do the same, sending the data to the back, checking everything + is as expected. */ + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + xQueueSendToBack( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK ); + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* The data we sent to the queue should equal the data we just received + from the queue. */ + if( ulLoopCounter != ulData ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + + /* Place 2, 3, 4 into the queue, adding items to the back of the queue. */ + for( ulData = 2; ulData < 5; ulData++ ) + { + xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + } + + /* Now the order in the queue should be 2, 3, 4, with 2 being the first + thing to be read out. Now add 1 then 0 to the front of the queue. */ + if( uxQueueMessagesWaiting( xQueue ) != 3 ) + { + xErrorDetected = pdTRUE; + } + ulData = 1; + xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + ulData = 0; + xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ); + + /* Now the queue should be full, and when we read the data out we + should receive 0, 1, 2, 3, 4. */ + if( uxQueueMessagesWaiting( xQueue ) != 5 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the data we read out is in the expected order. */ + for( ulData = 0; ulData < genqQUEUE_LENGTH; ulData++ ) + { + /* Try peeking the data first. */ + if( xQueuePeek( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + + + /* Now try receiving the data for real. The value should be the + same. Clobber the value first so we know we really received it. */ + ulData2 = ~ulData2; + if( xQueueReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + } + + /* The queue should now be empty again. */ + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + + /* Our queue is empty once more, add 10, 11 to the back. */ + ulData = 10; + if( xQueueSend( xQueue, &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + ulData = 11; + if( xQueueSend( xQueue, &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 2 ) + { + xErrorDetected = pdTRUE; + } + + /* Now we should have 10, 11 in the queue. Add 7, 8, 9 to the + front. */ + for( ulData = 9; ulData >= 7; ulData-- ) + { + if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + } + + /* Now check that the queue is full, and that receiving data provides + the expected sequence of 7, 8, 9, 10, 11. */ + if( uxQueueMessagesWaiting( xQueue ) != 5 ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + if( xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the data we read out is in the expected order. */ + for( ulData = 7; ulData < ( 7 + genqQUEUE_LENGTH ); ulData++ ) + { + if( xQueueReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulData != ulData2 ) + { + xErrorDetected = pdTRUE; + } + } + + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex ) +{ + /* Take the mutex. It should be available now. */ + if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* Set the guarded variable to a known start value. */ + ulGuardedVariable = 0; + + /* This task's priority should be as per that assigned when the task was + created. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the high priority task. This will attempt to take the + mutex, and block when it finds it cannot obtain it. */ + vTaskResume( xHighPriorityMutexTask ); + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Ensure the task is reporting its priority as blocked and not + suspended (as it would have done in versions up to V7.5.3). */ + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* The priority of the high priority task should now have been inherited + as by now it will have attempted to get the mutex. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Attempt to set the priority of this task to the test priority - + between the idle priority and the medium/high test priorities, but the + actual priority should remain at the high priority. */ + vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY ); + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the medium priority task. This should not run as the + inherited priority of this task is above that of the medium priority + task. */ + vTaskResume( xMediumPriorityMutexTask ); + + /* If the medium priority task did run then it will have incremented the + guarded variable. */ + if( ulGuardedVariable != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* Take the local mutex too, so two mutexes are now held. */ + if( xSemaphoreTake( xLocalMutex, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* When the semaphore is given back the priority of this task should not + yet be disinherited because the local mutex is still held. This is a + simplification to allow FreeRTOS to be integrated with middleware that + attempts to hold multiple mutexes without bloating the code with complex + algorithms. It is possible that the high priority mutex task will + execute as it shares a priority with this task. */ + if( xSemaphoreGive( xMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* The guarded variable is only incremented by the medium priority task, + which still should not have executed as this task should remain at the + higher priority, ensure this is the case. */ + if( ulGuardedVariable != 0 ) + { + xErrorDetected = pdTRUE; + } + + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now also give back the local mutex, taking the held count back to 0. + This time the priority of this task should be disinherited back to the + priority to which it was set while the mutex was held. This means + the medium priority task should execute and increment the guarded + variable. When this task next runs both the high and medium priority + tasks will have been suspended again. */ + if( xSemaphoreGive( xLocalMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the guarded variable did indeed increment... */ + if( ulGuardedVariable != 1 ) + { + xErrorDetected = pdTRUE; + } + + /* ... and that the priority of this task has been disinherited to + genqMUTEX_TEST_PRIORITY. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Set the priority of this task back to its original value, ready for + the next loop around this test. */ + vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY ); +} +/*-----------------------------------------------------------*/ + +static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex ) +{ + /* Take the mutex. It should be available now. */ + if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* Set the guarded variable to a known start value. */ + ulGuardedVariable = 0; + + /* This task's priority should be as per that assigned when the task was + created. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the high priority task. This will attempt to take the + mutex, and block when it finds it cannot obtain it. */ + vTaskResume( xHighPriorityMutexTask ); + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Ensure the task is reporting its priority as blocked and not + suspended (as it would have done in versions up to V7.5.3). */ + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* The priority of the high priority task should now have been inherited + as by now it will have attempted to get the mutex. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now unsuspend the medium priority task. This should not run as the + inherited priority of this task is above that of the medium priority + task. */ + vTaskResume( xMediumPriorityMutexTask ); + + /* If the medium priority task did run then it will have incremented the + guarded variable. */ + if( ulGuardedVariable != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* Take the local mutex too, so two mutexes are now held. */ + if( xSemaphoreTake( xLocalMutex, genqNO_BLOCK ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* When the local semaphore is given back the priority of this task should + not yet be disinherited because the shared mutex is still held. This is a + simplification to allow FreeRTOS to be integrated with middleware that + attempts to hold multiple mutexes without bloating the code with complex + algorithms. It is possible that the high priority mutex task will + execute as it shares a priority with this task. */ + if( xSemaphoreGive( xLocalMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* The guarded variable is only incremented by the medium priority task, + which still should not have executed as this task should remain at the + higher priority, ensure this is the case. */ + if( ulGuardedVariable != 0 ) + { + xErrorDetected = pdTRUE; + } + + if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY ) + { + xErrorDetected = pdTRUE; + } + + /* Now also give back the shared mutex, taking the held count back to 0. + This time the priority of this task should be disinherited back to the + priority at which it was created. This means the medium priority task + should execute and increment the guarded variable. When this task next runs + both the high and medium priority tasks will have been suspended again. */ + if( xSemaphoreGive( xMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* Check the guarded variable did indeed increment... */ + if( ulGuardedVariable != 1 ) + { + xErrorDetected = pdTRUE; + } + + /* ... and that the priority of this task has been disinherited to + genqMUTEX_LOW_PRIORITY. */ + if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY ) + { + xErrorDetected = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +static void prvLowPriorityMutexTask( void *pvParameters ) +{ +SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + /* The local mutex is used to check the 'mutexs held' count. */ + xLocalMutex = xSemaphoreCreateMutex(); + configASSERT( xLocalMutex ); + + for( ;; ) + { + /* The first tests exercise the priority inheritance when two mutexes + are taken then returned in a different order to which they were + taken. */ + prvTakeTwoMutexesReturnInDifferentOrder( xMutex, xLocalMutex ); + + /* Just to show this task is still running. */ + ulLoopCounter2++; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* The second tests exercise the priority inheritance when two mutexes + are taken then returned in the same order in which they were taken. */ + prvTakeTwoMutexesReturnInSameOrder( xMutex, xLocalMutex ); + + /* Just to show this task is still running. */ + ulLoopCounter2++; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +static void prvMediumPriorityMutexTask( void *pvParameters ) +{ + ( void ) pvParameters; + + for( ;; ) + { + /* The medium priority task starts by suspending itself. The low + priority task will unsuspend this task when required. */ + vTaskSuspend( NULL ); + + /* When this task unsuspends all it does is increment the guarded + variable, this is so the low priority task knows that it has + executed. */ + ulGuardedVariable++; + } +} +/*-----------------------------------------------------------*/ + +static void prvHighPriorityMutexTask( void *pvParameters ) +{ +SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters; + + for( ;; ) + { + /* The high priority task starts by suspending itself. The low + priority task will unsuspend this task when required. */ + vTaskSuspend( NULL ); + + /* When this task unsuspends all it does is attempt to obtain + the mutex. It should find the mutex is not available so a + block time is specified. */ + if( xSemaphoreTake( xMutex, portMAX_DELAY ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* When the mutex is eventually obtained it is just given back before + returning to suspend ready for the next cycle. */ + if( xSemaphoreGive( xMutex ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + } +} +/*-----------------------------------------------------------*/ + +/* NOTE: This function is not declared static to prevent compiler warnings in +demos where the function is declared but not used. */ +void vInterruptMutexTask( void *pvParameters ) +{ +const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( genqINTERRUPT_MUTEX_GIVE_PERIOD_MS ); +volatile uint32_t ulLoops = 0; + + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Has to wait longer than the time between gives to make sure it + should definitely have received the mutex. */ + if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + else + { + ulLoops++; + } + } +} +/*-----------------------------------------------------------*/ + +void vMutexISRInteractionTest( void ) +{ +static TickType_t xLastGiveTime = 0; +TickType_t xTimeNow; + + xTimeNow = xTaskGetTickCountFromISR(); + if( ( xTimeNow - xLastGiveTime ) >= pdMS_TO_TICKS( genqINTERRUPT_MUTEX_GIVE_PERIOD_MS ) ) + { + configASSERT( xISRMutex ); + xSemaphoreGiveFromISR( xISRMutex, NULL ); + xLastGiveTime = xTimeNow; + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreGenericQueueTasksStillRunning( void ) +{ +static uint32_t ulLastLoopCounter = 0, ulLastLoopCounter2 = 0; + + /* If the demo task is still running then we expect the loop counters to + have incremented since this function was last called. */ + if( ulLastLoopCounter == ulLoopCounter ) + { + xErrorDetected = pdTRUE; + } + + if( ulLastLoopCounter2 == ulLoopCounter2 ) + { + xErrorDetected = pdTRUE; + } + + ulLastLoopCounter = ulLoopCounter; + ulLastLoopCounter2 = ulLoopCounter2; + + /* Errors detected in the task itself will have latched xErrorDetected + to true. */ + + return ( BaseType_t ) !xErrorDetected; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/IntQueue.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/IntQueue.c new file mode 100644 index 0000000..fcb7157 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/IntQueue.c @@ -0,0 +1,760 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This file defines one of the more complex set of demo/test tasks. They are + * designed to stress test the queue implementation though pseudo simultaneous + * multiple reads and multiple writes from both tasks of varying priority and + * interrupts. The interrupts are prioritised such to ensure that nesting + * occurs (for those ports that support it). + * + * The test ensures that, while being accessed from three tasks and two + * interrupts, all the data sent to the queues is also received from + * the same queue, and that no duplicate items are either sent or received. + * The tests also ensure that a low priority task is never able to successfully + * read from or write to a queue when a task of higher priority is attempting + * the same operation. + */ + +/* Standard includes. */ +#include + +/* SafeRTOS includes. */ +#include "FreeRTOS.h" +#include "queue.h" +#include "task.h" + +/* Demo app includes. */ +#include "IntQueue.h" +#include "IntQueueTimer.h" + +#if( INCLUDE_eTaskGetState != 1 ) + #error INCLUDE_eTaskGetState must be set to 1 in FreeRTOSConfig.h to use this demo file. +#endif + +/* Priorities used by test tasks. */ +#ifndef intqHIGHER_PRIORITY + #define intqHIGHER_PRIORITY ( configMAX_PRIORITIES - 2 ) +#endif +#define intqLOWER_PRIORITY ( tskIDLE_PRIORITY ) + +/* The number of values to send/receive before checking that all values were +processed as expected. */ +#define intqNUM_VALUES_TO_LOG ( 200 ) +#define intqSHORT_DELAY ( 140 ) + +/* The value by which the value being sent to or received from a queue should +increment past intqNUM_VALUES_TO_LOG before we check that all values have been +sent/received correctly. This is done to ensure that all tasks and interrupts +accessing the queue have completed their accesses with the +intqNUM_VALUES_TO_LOG range. */ +#define intqVALUE_OVERRUN ( 50 ) + +/* The delay used by the polling task. A short delay is used for code +coverage. */ +#define intqONE_TICK_DELAY ( 1 ) + +/* Each task and interrupt is given a unique identifier. This value is used to +identify which task sent or received each value. The identifier is also used +to distinguish between two tasks that are running the same task function. */ +#define intqHIGH_PRIORITY_TASK1 ( ( UBaseType_t ) 1 ) +#define intqHIGH_PRIORITY_TASK2 ( ( UBaseType_t ) 2 ) +#define intqLOW_PRIORITY_TASK ( ( UBaseType_t ) 3 ) +#define intqFIRST_INTERRUPT ( ( UBaseType_t ) 4 ) +#define intqSECOND_INTERRUPT ( ( UBaseType_t ) 5 ) +#define intqQUEUE_LENGTH ( ( UBaseType_t ) 10 ) + +/* At least intqMIN_ACCEPTABLE_TASK_COUNT values should be sent to/received +from each queue by each task, otherwise an error is detected. */ +#define intqMIN_ACCEPTABLE_TASK_COUNT ( 5 ) + +/* Send the next value to the queue that is normally empty. This is called +from within the interrupts. */ +#define timerNORMALLY_EMPTY_TX() \ + if( xQueueIsQueueFullFromISR( xNormallyEmptyQueue ) != pdTRUE ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + uxValueForNormallyEmptyQueue++; \ + xQueueSendFromISR( xNormallyEmptyQueue, ( void * ) &uxValueForNormallyEmptyQueue, &xHigherPriorityTaskWoken ); \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } \ + +/* Send the next value to the queue that is normally full. This is called +from within the interrupts. */ +#define timerNORMALLY_FULL_TX() \ + if( xQueueIsQueueFullFromISR( xNormallyFullQueue ) != pdTRUE ) \ + { \ + UBaseType_t uxSavedInterruptStatus; \ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); \ + { \ + uxValueForNormallyFullQueue++; \ + xQueueSendFromISR( xNormallyFullQueue, ( void * ) &uxValueForNormallyFullQueue, &xHigherPriorityTaskWoken ); \ + } \ + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); \ + } \ + +/* Receive a value from the normally empty queue. This is called from within +an interrupt. */ +#define timerNORMALLY_EMPTY_RX() \ + if( xQueueReceiveFromISR( xNormallyEmptyQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) != pdPASS ) \ + { \ + prvQueueAccessLogError( __LINE__ ); \ + } \ + else \ + { \ + prvRecordValue_NormallyEmpty( uxRxedValue, intqSECOND_INTERRUPT ); \ + } + +/* Receive a value from the normally full queue. This is called from within +an interrupt. */ +#define timerNORMALLY_FULL_RX() \ + if( xQueueReceiveFromISR( xNormallyFullQueue, &uxRxedValue, &xHigherPriorityTaskWoken ) == pdPASS ) \ + { \ + prvRecordValue_NormallyFull( uxRxedValue, intqSECOND_INTERRUPT ); \ + } \ + + +/*-----------------------------------------------------------*/ + +/* The two queues used by the test. */ +static QueueHandle_t xNormallyEmptyQueue, xNormallyFullQueue; + +/* Variables used to detect a stall in one of the tasks. */ +static UBaseType_t uxHighPriorityLoops1 = 0, uxHighPriorityLoops2 = 0, uxLowPriorityLoops1 = 0, uxLowPriorityLoops2 = 0; + +/* Any unexpected behaviour sets xErrorStatus to fail and log the line that +caused the error in xErrorLine. */ +static BaseType_t xErrorStatus = pdPASS; +static volatile UBaseType_t xErrorLine = ( UBaseType_t ) 0; + +/* Used for sequencing between tasks. */ +static BaseType_t xWasSuspended = pdFALSE; + +/* The values that are sent to the queues. An incremented value is sent each +time to each queue. */ +volatile UBaseType_t uxValueForNormallyEmptyQueue = 0, uxValueForNormallyFullQueue = 0; + +/* A handle to some of the tasks is required so they can be suspended/resumed. */ +TaskHandle_t xHighPriorityNormallyEmptyTask1, xHighPriorityNormallyEmptyTask2, xHighPriorityNormallyFullTask1, xHighPriorityNormallyFullTask2; + +/* When a value is received in a queue the value is ticked off in the array +the array position of the value is set to a the identifier of the task or +interrupt that accessed the queue. This way missing or duplicate values can be +detected. */ +static uint8_t ucNormallyEmptyReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 }; +static uint8_t ucNormallyFullReceivedValues[ intqNUM_VALUES_TO_LOG ] = { 0 }; + +/* The test tasks themselves. */ +static void prvLowerPriorityNormallyEmptyTask( void *pvParameters ); +static void prvLowerPriorityNormallyFullTask( void *pvParameters ); +static void prvHigherPriorityNormallyEmptyTask( void *pvParameters ); +static void prv1stHigherPriorityNormallyFullTask( void *pvParameters ); +static void prv2ndHigherPriorityNormallyFullTask( void *pvParameters ); + +/* Used to mark the positions within the ucNormallyEmptyReceivedValues and +ucNormallyFullReceivedValues arrays, while checking for duplicates. */ +static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue, UBaseType_t uxSource ); +static void prvRecordValue_NormallyFull( UBaseType_t uxValue, UBaseType_t uxSource ); + +/* Logs the line on which an error occurred. */ +static void prvQueueAccessLogError( UBaseType_t uxLine ); + +/*-----------------------------------------------------------*/ + +void vStartInterruptQueueTasks( void ) +{ + /* Start the test tasks. */ + xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H1QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask1 ); + xTaskCreate( prvHigherPriorityNormallyEmptyTask, "H2QRx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyEmptyTask2 ); + xTaskCreate( prvLowerPriorityNormallyEmptyTask, "L1QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL ); + xTaskCreate( prv1stHigherPriorityNormallyFullTask, "H1QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK1, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask1 ); + xTaskCreate( prv2ndHigherPriorityNormallyFullTask, "H2QTx", configMINIMAL_STACK_SIZE, ( void * ) intqHIGH_PRIORITY_TASK2, intqHIGHER_PRIORITY, &xHighPriorityNormallyFullTask2 ); + xTaskCreate( prvLowerPriorityNormallyFullTask, "L2QRx", configMINIMAL_STACK_SIZE, NULL, intqLOWER_PRIORITY, NULL ); + + /* Create the queues that are accessed by multiple tasks and multiple + interrupts. */ + xNormallyFullQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) ); + xNormallyEmptyQueue = xQueueCreate( intqQUEUE_LENGTH, ( UBaseType_t ) sizeof( UBaseType_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xNormallyFullQueue, "NormallyFull" ); + vQueueAddToRegistry( xNormallyEmptyQueue, "NormallyEmpty" ); +} +/*-----------------------------------------------------------*/ + +static void prvRecordValue_NormallyFull( UBaseType_t uxValue, UBaseType_t uxSource ) +{ + if( uxValue < intqNUM_VALUES_TO_LOG ) + { + /* We don't expect to receive the same value twice, so if the value + has already been marked as received an error has occurred. */ + if( ucNormallyFullReceivedValues[ uxValue ] != 0x00 ) + { + prvQueueAccessLogError( __LINE__ ); + } + + /* Log that this value has been received. */ + ucNormallyFullReceivedValues[ uxValue ] = ( uint8_t ) uxSource; + } +} +/*-----------------------------------------------------------*/ + +static void prvRecordValue_NormallyEmpty( UBaseType_t uxValue, UBaseType_t uxSource ) +{ + if( uxValue < intqNUM_VALUES_TO_LOG ) + { + /* We don't expect to receive the same value twice, so if the value + has already been marked as received an error has occurred. */ + if( ucNormallyEmptyReceivedValues[ uxValue ] != 0x00 ) + { + prvQueueAccessLogError( __LINE__ ); + } + + /* Log that this value has been received. */ + ucNormallyEmptyReceivedValues[ uxValue ] = ( uint8_t ) uxSource; + } +} +/*-----------------------------------------------------------*/ + +static void prvQueueAccessLogError( UBaseType_t uxLine ) +{ + /* Latch the line number that caused the error. */ + xErrorLine = uxLine; + xErrorStatus = pdFAIL; +} +/*-----------------------------------------------------------*/ + +static void prvHigherPriorityNormallyEmptyTask( void *pvParameters ) +{ +UBaseType_t uxRxed, ux, uxTask1, uxTask2, uxInterrupts, uxErrorCount1 = 0, uxErrorCount2 = 0; + + /* The timer should not be started until after the scheduler has started. + More than one task is running this code so we check the parameter value + to determine which task should start the timer. */ + if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 ) + { + vInitialiseTimerForIntQueueTest(); + } + + for( ;; ) + { + /* Block waiting to receive a value from the normally empty queue. + Interrupts will write to the queue so we should receive a value. */ + if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqSHORT_DELAY ) != pdPASS ) + { + prvQueueAccessLogError( __LINE__ ); + } + else + { + /* Note which value was received so we can check all expected + values are received and no values are duplicated. */ + prvRecordValue_NormallyEmpty( uxRxed, ( UBaseType_t ) pvParameters ); + } + + /* Ensure the other task running this code gets a chance to execute. */ + taskYIELD(); + + if( ( UBaseType_t ) pvParameters == intqHIGH_PRIORITY_TASK1 ) + { + /* Have we received all the expected values? */ + if( uxValueForNormallyEmptyQueue > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) ) + { + vTaskSuspend( xHighPriorityNormallyEmptyTask2 ); + + uxTask1 = 0; + uxTask2 = 0; + uxInterrupts = 0; + + /* Loop through the array, checking that both tasks have + placed values into the array, and that no values are missing. + Start at 1 as we expect position 0 to be unused. */ + for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ ) + { + if( ucNormallyEmptyReceivedValues[ ux ] == 0 ) + { + /* A value is missing. */ + prvQueueAccessLogError( __LINE__ ); + } + else + { + if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK1 ) + { + /* Value was placed into the array by task 1. */ + uxTask1++; + } + else if( ucNormallyEmptyReceivedValues[ ux ] == intqHIGH_PRIORITY_TASK2 ) + { + /* Value was placed into the array by task 2. */ + uxTask2++; + } + else if( ucNormallyEmptyReceivedValues[ ux ] == intqSECOND_INTERRUPT ) + { + uxInterrupts++; + } + } + } + + if( uxTask1 < intqMIN_ACCEPTABLE_TASK_COUNT ) + { + /* Only task 2 seemed to log any values. */ + uxErrorCount1++; + if( uxErrorCount1 > 2 ) + { + prvQueueAccessLogError( __LINE__ ); + } + } + else + { + uxErrorCount1 = 0; + } + + if( uxTask2 < intqMIN_ACCEPTABLE_TASK_COUNT ) + { + /* Only task 1 seemed to log any values. */ + uxErrorCount2++; + if( uxErrorCount2 > 2 ) + { + prvQueueAccessLogError( __LINE__ ); + } + } + else + { + uxErrorCount2 = 0; + } + + if( uxInterrupts == 0 ) + { + prvQueueAccessLogError( __LINE__ ); + } + + /* Clear the array again, ready to start a new cycle. */ + memset( ucNormallyEmptyReceivedValues, 0x00, sizeof( ucNormallyEmptyReceivedValues ) ); + + uxHighPriorityLoops1++; + uxValueForNormallyEmptyQueue = 0; + + /* Suspend ourselves, allowing the lower priority task to + actually receive something from the queue. Until now it + will have been prevented from doing so by the higher + priority tasks. The lower priority task will resume us + if it receives something. We will then resume the other + higher priority task. */ + vTaskSuspend( NULL ); + vTaskResume( xHighPriorityNormallyEmptyTask2 ); + } + } + } +} +/*-----------------------------------------------------------*/ + +static void prvLowerPriorityNormallyEmptyTask( void *pvParameters ) +{ +UBaseType_t uxValue, uxRxed; + + /* The parameters are not being used so avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + if( xQueueReceive( xNormallyEmptyQueue, &uxRxed, intqONE_TICK_DELAY ) != errQUEUE_EMPTY ) + { + /* A value should only be obtained when the high priority task is + suspended. */ + if( eTaskGetState( xHighPriorityNormallyEmptyTask1 ) != eSuspended ) + { + prvQueueAccessLogError( __LINE__ ); + } + + prvRecordValue_NormallyEmpty( uxRxed, intqLOW_PRIORITY_TASK ); + + /* Wake the higher priority task again. */ + vTaskResume( xHighPriorityNormallyEmptyTask1 ); + uxLowPriorityLoops1++; + } + else + { + /* Raise our priority while we send so we can preempt the higher + priority task, and ensure we get the Tx value into the queue. */ + vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 ); + + portENTER_CRITICAL(); + { + uxValueForNormallyEmptyQueue++; + uxValue = uxValueForNormallyEmptyQueue; + } + portEXIT_CRITICAL(); + + if( xQueueSend( xNormallyEmptyQueue, &uxValue, portMAX_DELAY ) != pdPASS ) + { + prvQueueAccessLogError( __LINE__ ); + } + + vTaskPrioritySet( NULL, intqLOWER_PRIORITY ); + } + } +} +/*-----------------------------------------------------------*/ + +static void prv1stHigherPriorityNormallyFullTask( void *pvParameters ) +{ +UBaseType_t uxValueToTx, ux, uxInterrupts; + + /* The parameters are not being used so avoid compiler warnings. */ + ( void ) pvParameters; + + /* Make sure the queue starts full or near full. >> 1 as there are two + high priority tasks. */ + for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ ) + { + portENTER_CRITICAL(); + { + uxValueForNormallyFullQueue++; + uxValueToTx = uxValueForNormallyFullQueue; + } + portEXIT_CRITICAL(); + + xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ); + } + + for( ;; ) + { + portENTER_CRITICAL(); + { + uxValueForNormallyFullQueue++; + uxValueToTx = uxValueForNormallyFullQueue; + } + portEXIT_CRITICAL(); + + if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS ) + { + /* intqHIGH_PRIORITY_TASK2 is never suspended so we would not + expect it to ever time out. */ + prvQueueAccessLogError( __LINE__ ); + } + + /* Allow the other task running this code to run. */ + taskYIELD(); + + /* Have all the expected values been sent to the queue? */ + if( uxValueToTx > ( intqNUM_VALUES_TO_LOG + intqVALUE_OVERRUN ) ) + { + /* Make sure the other high priority task completes its send of + any values below intqNUM_VALUE_TO_LOG. */ + vTaskDelay( intqSHORT_DELAY ); + + vTaskSuspend( xHighPriorityNormallyFullTask2 ); + + if( xWasSuspended == pdTRUE ) + { + /* We would have expected the other high priority task to have + set this back to false by now. */ + prvQueueAccessLogError( __LINE__ ); + } + + /* Set the suspended flag so an error is not logged if the other + task recognises a time out when it is unsuspended. */ + xWasSuspended = pdTRUE; + + /* Check interrupts are also sending. */ + uxInterrupts = 0U; + + /* Start at 1 as we expect position 0 to be unused. */ + for( ux = 1; ux < intqNUM_VALUES_TO_LOG; ux++ ) + { + if( ucNormallyFullReceivedValues[ ux ] == 0 ) + { + /* A value was missing. */ + prvQueueAccessLogError( __LINE__ ); + } + else if( ucNormallyFullReceivedValues[ ux ] == intqSECOND_INTERRUPT ) + { + uxInterrupts++; + } + } + + if( uxInterrupts == 0 ) + { + /* No writes from interrupts were found. Are interrupts + actually running? */ + prvQueueAccessLogError( __LINE__ ); + } + + /* Reset the array ready for the next cycle. */ + memset( ucNormallyFullReceivedValues, 0x00, sizeof( ucNormallyFullReceivedValues ) ); + + uxHighPriorityLoops2++; + uxValueForNormallyFullQueue = 0; + + /* Suspend ourselves, allowing the lower priority task to + actually receive something from the queue. Until now it + will have been prevented from doing so by the higher + priority tasks. The lower priority task will resume us + if it receives something. We will then resume the other + higher priority task. */ + vTaskSuspend( NULL ); + vTaskResume( xHighPriorityNormallyFullTask2 ); + } + } +} +/*-----------------------------------------------------------*/ + +static void prv2ndHigherPriorityNormallyFullTask( void *pvParameters ) +{ +UBaseType_t uxValueToTx, ux; + + /* The parameters are not being used so avoid compiler warnings. */ + ( void ) pvParameters; + + /* Make sure the queue starts full or near full. >> 1 as there are two + high priority tasks. */ + for( ux = 0; ux < ( intqQUEUE_LENGTH >> 1 ); ux++ ) + { + portENTER_CRITICAL(); + { + uxValueForNormallyFullQueue++; + uxValueToTx = uxValueForNormallyFullQueue; + } + portEXIT_CRITICAL(); + + xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ); + } + + for( ;; ) + { + portENTER_CRITICAL(); + { + uxValueForNormallyFullQueue++; + uxValueToTx = uxValueForNormallyFullQueue; + } + portEXIT_CRITICAL(); + + if( xQueueSend( xNormallyFullQueue, &uxValueToTx, intqSHORT_DELAY ) != pdPASS ) + { + if( xWasSuspended != pdTRUE ) + { + /* It is ok to time out if the task has been suspended. */ + prvQueueAccessLogError( __LINE__ ); + } + } + + xWasSuspended = pdFALSE; + + taskYIELD(); + } +} +/*-----------------------------------------------------------*/ + +static void prvLowerPriorityNormallyFullTask( void *pvParameters ) +{ +UBaseType_t uxValue, uxTxed = 9999; + + /* The parameters are not being used so avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + if( xQueueSend( xNormallyFullQueue, &uxTxed, intqONE_TICK_DELAY ) != errQUEUE_FULL ) + { + /* Should only succeed when the higher priority task is suspended */ + if( eTaskGetState( xHighPriorityNormallyFullTask1 ) != eSuspended ) + { + prvQueueAccessLogError( __LINE__ ); + } + + vTaskResume( xHighPriorityNormallyFullTask1 ); + uxLowPriorityLoops2++; + } + else + { + /* Raise our priority while we receive so we can preempt the higher + priority task, and ensure we get the value from the queue. */ + vTaskPrioritySet( NULL, intqHIGHER_PRIORITY + 1 ); + + if( xQueueReceive( xNormallyFullQueue, &uxValue, portMAX_DELAY ) != pdPASS ) + { + prvQueueAccessLogError( __LINE__ ); + } + else + { + prvRecordValue_NormallyFull( uxValue, intqLOW_PRIORITY_TASK ); + } + + vTaskPrioritySet( NULL, intqLOWER_PRIORITY ); + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xFirstTimerHandler( void ) +{ +BaseType_t xHigherPriorityTaskWoken = pdFALSE; +UBaseType_t uxRxedValue; +static UBaseType_t uxNextOperation = 0; + + /* Called from a timer interrupt. Perform various read and write + accesses on the queues. */ + + uxNextOperation++; + + if( uxNextOperation & ( UBaseType_t ) 0x01 ) + { + timerNORMALLY_EMPTY_TX(); + timerNORMALLY_EMPTY_TX(); + timerNORMALLY_EMPTY_TX(); + } + else + { + timerNORMALLY_FULL_RX(); + timerNORMALLY_FULL_RX(); + timerNORMALLY_FULL_RX(); + } + + return xHigherPriorityTaskWoken; +} +/*-----------------------------------------------------------*/ + +BaseType_t xSecondTimerHandler( void ) +{ +UBaseType_t uxRxedValue; +BaseType_t xHigherPriorityTaskWoken = pdFALSE; +static UBaseType_t uxNextOperation = 0; + + /* Called from a timer interrupt. Perform various read and write + accesses on the queues. */ + + uxNextOperation++; + + if( uxNextOperation & ( UBaseType_t ) 0x01 ) + { + timerNORMALLY_EMPTY_TX(); + timerNORMALLY_EMPTY_TX(); + + timerNORMALLY_EMPTY_RX(); + timerNORMALLY_EMPTY_RX(); + } + else + { + timerNORMALLY_FULL_RX(); + timerNORMALLY_FULL_TX(); + timerNORMALLY_FULL_TX(); + timerNORMALLY_FULL_TX(); + timerNORMALLY_FULL_TX(); + } + + return xHigherPriorityTaskWoken; +} +/*-----------------------------------------------------------*/ + + +BaseType_t xAreIntQueueTasksStillRunning( void ) +{ +static UBaseType_t uxLastHighPriorityLoops1 = 0, uxLastHighPriorityLoops2 = 0, uxLastLowPriorityLoops1 = 0, uxLastLowPriorityLoops2 = 0; + + /* xErrorStatus can be set outside of this function. This function just + checks that all the tasks are still cycling. */ + + if( uxHighPriorityLoops1 == uxLastHighPriorityLoops1 ) + { + /* The high priority 1 task has stalled. */ + prvQueueAccessLogError( __LINE__ ); + } + + uxLastHighPriorityLoops1 = uxHighPriorityLoops1; + + if( uxHighPriorityLoops2 == uxLastHighPriorityLoops2 ) + { + /* The high priority 2 task has stalled. */ + prvQueueAccessLogError( __LINE__ ); + } + + uxLastHighPriorityLoops2 = uxHighPriorityLoops2; + + if( uxLowPriorityLoops1 == uxLastLowPriorityLoops1 ) + { + /* The low priority 1 task has stalled. */ + prvQueueAccessLogError( __LINE__ ); + } + + uxLastLowPriorityLoops1 = uxLowPriorityLoops1; + + if( uxLowPriorityLoops2 == uxLastLowPriorityLoops2 ) + { + /* The low priority 2 task has stalled. */ + prvQueueAccessLogError( __LINE__ ); + } + + uxLastLowPriorityLoops2 = uxLowPriorityLoops2; + + return xErrorStatus; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/PollQ.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/PollQ.c new file mode 100644 index 0000000..4782f61 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/PollQ.c @@ -0,0 +1,258 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This version of PollQ. c is for use on systems that have limited stack + * space and no display facilities. The complete version can be found in + * the Demo/Common/Full directory. + * + * Creates two tasks that communicate over a single queue. One task acts as a + * producer, the other a consumer. + * + * The producer loops for three iteration, posting an incrementing number onto the + * queue each cycle. It then delays for a fixed period before doing exactly the + * same again. + * + * The consumer loops emptying the queue. Each item removed from the queue is + * checked to ensure it contains the expected value. When the queue is empty it + * blocks for a fixed period, then does the same again. + * + * All queue access is performed without blocking. The consumer completely empties + * the queue each time it runs so the producer should never find the queue full. + * + * An error is flagged if the consumer obtains an unexpected value or the producer + * find the queue is full. + */ + +/* +Changes from V2.0.0 + + + Delay periods are now specified using variables and constants of + TickType_t rather than uint32_t. +*/ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "PollQ.h" + +#define pollqSTACK_SIZE configMINIMAL_STACK_SIZE +#define pollqQUEUE_SIZE ( 10 ) +#define pollqPRODUCER_DELAY ( ( TickType_t ) 200 / portTICK_PERIOD_MS ) +#define pollqCONSUMER_DELAY ( pollqPRODUCER_DELAY - ( TickType_t ) ( 20 / portTICK_PERIOD_MS ) ) +#define pollqNO_DELAY ( ( TickType_t ) 0 ) +#define pollqVALUES_TO_PRODUCE ( ( BaseType_t ) 3 ) +#define pollqINITIAL_VALUE ( ( BaseType_t ) 0 ) + +/* The task that posts the incrementing number onto the queue. */ +static portTASK_FUNCTION_PROTO( vPolledQueueProducer, pvParameters ); + +/* The task that empties the queue. */ +static portTASK_FUNCTION_PROTO( vPolledQueueConsumer, pvParameters ); + +/* Variables that are used to check that the tasks are still running with no +errors. */ +static volatile BaseType_t xPollingConsumerCount = pollqINITIAL_VALUE, xPollingProducerCount = pollqINITIAL_VALUE; + +/*-----------------------------------------------------------*/ + +void vStartPolledQueueTasks( UBaseType_t uxPriority ) +{ +static QueueHandle_t xPolledQueue; + + /* Create the queue used by the producer and consumer. */ + xPolledQueue = xQueueCreate( pollqQUEUE_SIZE, ( UBaseType_t ) sizeof( uint16_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xPolledQueue, "Poll_Test_Queue" ); + + /* Spawn the producer and consumer. */ + xTaskCreate( vPolledQueueConsumer, "QConsNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL ); + xTaskCreate( vPolledQueueProducer, "QProdNB", pollqSTACK_SIZE, ( void * ) &xPolledQueue, uxPriority, ( TaskHandle_t * ) NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vPolledQueueProducer, pvParameters ) +{ +uint16_t usValue = ( uint16_t ) 0; +BaseType_t xError = pdFALSE, xLoop; + + for( ;; ) + { + for( xLoop = 0; xLoop < pollqVALUES_TO_PRODUCE; xLoop++ ) + { + /* Send an incrementing number on the queue without blocking. */ + if( xQueueSend( *( ( QueueHandle_t * ) pvParameters ), ( void * ) &usValue, pollqNO_DELAY ) != pdPASS ) + { + /* We should never find the queue full so if we get here there + has been an error. */ + xError = pdTRUE; + } + else + { + if( xError == pdFALSE ) + { + /* If an error has ever been recorded we stop incrementing the + check variable. */ + portENTER_CRITICAL(); + xPollingProducerCount++; + portEXIT_CRITICAL(); + } + + /* Update the value we are going to post next time around. */ + usValue++; + } + } + + /* Wait before we start posting again to ensure the consumer runs and + empties the queue. */ + vTaskDelay( pollqPRODUCER_DELAY ); + } +} /*lint !e818 Function prototype must conform to API. */ +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vPolledQueueConsumer, pvParameters ) +{ +uint16_t usData, usExpectedValue = ( uint16_t ) 0; +BaseType_t xError = pdFALSE; + + for( ;; ) + { + /* Loop until the queue is empty. */ + while( uxQueueMessagesWaiting( *( ( QueueHandle_t * ) pvParameters ) ) ) + { + if( xQueueReceive( *( ( QueueHandle_t * ) pvParameters ), &usData, pollqNO_DELAY ) == pdPASS ) + { + if( usData != usExpectedValue ) + { + /* This is not what we expected to receive so an error has + occurred. */ + xError = pdTRUE; + + /* Catch-up to the value we received so our next expected + value should again be correct. */ + usExpectedValue = usData; + } + else + { + if( xError == pdFALSE ) + { + /* Only increment the check variable if no errors have + occurred. */ + portENTER_CRITICAL(); + xPollingConsumerCount++; + portEXIT_CRITICAL(); + } + } + + /* Next time round we would expect the number to be one higher. */ + usExpectedValue++; + } + } + + /* Now the queue is empty we block, allowing the producer to place more + items in the queue. */ + vTaskDelay( pollqCONSUMER_DELAY ); + } +} /*lint !e818 Function prototype must conform to API. */ +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running with no errors. */ +BaseType_t xArePollingQueuesStillRunning( void ) +{ +BaseType_t xReturn; + + /* Check both the consumer and producer poll count to check they have both + been changed since out last trip round. We do not need a critical section + around the check variables as this is called from a higher priority than + the other tasks that access the same variables. */ + if( ( xPollingConsumerCount == pollqINITIAL_VALUE ) || + ( xPollingProducerCount == pollqINITIAL_VALUE ) + ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + /* Set the check variables back down so we know if they have been + incremented the next time around. */ + xPollingConsumerCount = pollqINITIAL_VALUE; + xPollingProducerCount = pollqINITIAL_VALUE; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QPeek.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QPeek.c new file mode 100644 index 0000000..4f4caa9 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QPeek.c @@ -0,0 +1,474 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Tests the behaviour when data is peeked from a queue when there are + * multiple tasks blocked on the queue. + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" + +/* Demo program include files. */ +#include "QPeek.h" + +#define qpeekQUEUE_LENGTH ( 5 ) +#define qpeekNO_BLOCK ( 0 ) +#define qpeekSHORT_DELAY ( 10 ) + +#define qpeekLOW_PRIORITY ( tskIDLE_PRIORITY + 0 ) +#define qpeekMEDIUM_PRIORITY ( tskIDLE_PRIORITY + 1 ) +#define qpeekHIGH_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define qpeekHIGHEST_PRIORITY ( tskIDLE_PRIORITY + 3 ) + +/*-----------------------------------------------------------*/ + +/* + * The following three tasks are used to demonstrate the peeking behaviour. + * Each task is given a different priority to demonstrate the order in which + * tasks are woken as data is peeked from a queue. + */ +static void prvLowPriorityPeekTask( void *pvParameters ); +static void prvMediumPriorityPeekTask( void *pvParameters ); +static void prvHighPriorityPeekTask( void *pvParameters ); +static void prvHighestPriorityPeekTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +/* Flag that will be latched to pdTRUE should any unexpected behaviour be +detected in any of the tasks. */ +static volatile BaseType_t xErrorDetected = pdFALSE; + +/* Counter that is incremented on each cycle of a test. This is used to +detect a stalled task - a test that is no longer running. */ +static volatile uint32_t ulLoopCounter = 0; + +/* Handles to the test tasks. */ +TaskHandle_t xMediumPriorityTask, xHighPriorityTask, xHighestPriorityTask; +/*-----------------------------------------------------------*/ + +void vStartQueuePeekTasks( void ) +{ +QueueHandle_t xQueue; + + /* Create the queue that we are going to use for the test/demo. */ + xQueue = xQueueCreate( qpeekQUEUE_LENGTH, sizeof( uint32_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xQueue, "QPeek_Test_Queue" ); + + /* Create the demo tasks and pass it the queue just created. We are + passing the queue handle by value so it does not matter that it is declared + on the stack here. */ + xTaskCreate( prvLowPriorityPeekTask, "PeekL", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekLOW_PRIORITY, NULL ); + xTaskCreate( prvMediumPriorityPeekTask, "PeekM", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekMEDIUM_PRIORITY, &xMediumPriorityTask ); + xTaskCreate( prvHighPriorityPeekTask, "PeekH1", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGH_PRIORITY, &xHighPriorityTask ); + xTaskCreate( prvHighestPriorityPeekTask, "PeekH2", configMINIMAL_STACK_SIZE, ( void * ) xQueue, qpeekHIGHEST_PRIORITY, &xHighestPriorityTask ); +} +/*-----------------------------------------------------------*/ + +static void prvHighestPriorityPeekTask( void *pvParameters ) +{ +QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; +uint32_t ulValue; + + #ifdef USE_STDIO + { + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Queue peek test started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + } + #endif + + for( ;; ) + { + /* Try peeking from the queue. The queue should be empty so we will + block, allowing the high priority task to execute. */ + if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + /* We expected to have received something by the time we unblock. */ + xErrorDetected = pdTRUE; + } + + /* When we reach here the high and medium priority tasks should still + be blocked on the queue. We unblocked because the low priority task + wrote a value to the queue, which we should have peeked. Peeking the + data (rather than receiving it) will leave the data on the queue, so + the high priority task should then have also been unblocked, but not + yet executed. */ + if( ulValue != 0x11223344 ) + { + /* We did not receive the expected value. */ + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + /* The message should have been left on the queue. */ + xErrorDetected = pdTRUE; + } + + /* Now we are going to actually receive the data, so when the high + priority task runs it will find the queue empty and return to the + blocked state. */ + ulValue = 0; + if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) + { + /* We expected to receive the value. */ + xErrorDetected = pdTRUE; + } + + if( ulValue != 0x11223344 ) + { + /* We did not receive the expected value - which should have been + the same value as was peeked. */ + xErrorDetected = pdTRUE; + } + + /* Now we will block again as the queue is once more empty. The low + priority task can then execute again. */ + if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + /* We expected to have received something by the time we unblock. */ + xErrorDetected = pdTRUE; + } + + /* When we get here the low priority task should have again written to the + queue. */ + if( ulValue != 0x01234567 ) + { + /* We did not receive the expected value. */ + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + /* The message should have been left on the queue. */ + xErrorDetected = pdTRUE; + } + + /* We only peeked the data, so suspending ourselves now should enable + the high priority task to also peek the data. The high priority task + will have been unblocked when we peeked the data as we left the data + in the queue. */ + vTaskSuspend( NULL ); + + + + /* This time we are going to do the same as the above test, but the + high priority task is going to receive the data, rather than peek it. + This means that the medium priority task should never peek the value. */ + if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulValue != 0xaabbaabb ) + { + xErrorDetected = pdTRUE; + } + + vTaskSuspend( NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvHighPriorityPeekTask( void *pvParameters ) +{ +QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; +uint32_t ulValue; + + for( ;; ) + { + /* Try peeking from the queue. The queue should be empty so we will + block, allowing the medium priority task to execute. Both the high + and highest priority tasks will then be blocked on the queue. */ + if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + /* We expected to have received something by the time we unblock. */ + xErrorDetected = pdTRUE; + } + + /* When we get here the highest priority task should have peeked the data + (unblocking this task) then suspended (allowing this task to also peek + the data). */ + if( ulValue != 0x01234567 ) + { + /* We did not receive the expected value. */ + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + /* The message should have been left on the queue. */ + xErrorDetected = pdTRUE; + } + + /* We only peeked the data, so suspending ourselves now should enable + the medium priority task to also peek the data. The medium priority task + will have been unblocked when we peeked the data as we left the data + in the queue. */ + vTaskSuspend( NULL ); + + + /* This time we are going actually receive the value, so the medium + priority task will never peek the data - we removed it from the queue. */ + if( xQueueReceive( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + xErrorDetected = pdTRUE; + } + + if( ulValue != 0xaabbaabb ) + { + xErrorDetected = pdTRUE; + } + + vTaskSuspend( NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvMediumPriorityPeekTask( void *pvParameters ) +{ +QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; +uint32_t ulValue; + + for( ;; ) + { + /* Try peeking from the queue. The queue should be empty so we will + block, allowing the low priority task to execute. The highest, high + and medium priority tasks will then all be blocked on the queue. */ + if( xQueuePeek( xQueue, &ulValue, portMAX_DELAY ) != pdPASS ) + { + /* We expected to have received something by the time we unblock. */ + xErrorDetected = pdTRUE; + } + + /* When we get here the high priority task should have peeked the data + (unblocking this task) then suspended (allowing this task to also peek + the data). */ + if( ulValue != 0x01234567 ) + { + /* We did not receive the expected value. */ + xErrorDetected = pdTRUE; + } + + if( uxQueueMessagesWaiting( xQueue ) != 1 ) + { + /* The message should have been left on the queue. */ + xErrorDetected = pdTRUE; + } + + /* Just so we know the test is still running. */ + ulLoopCounter++; + + /* Now we can suspend ourselves so the low priority task can execute + again. */ + vTaskSuspend( NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvLowPriorityPeekTask( void *pvParameters ) +{ +QueueHandle_t xQueue = ( QueueHandle_t ) pvParameters; +uint32_t ulValue; + + for( ;; ) + { + /* Write some data to the queue. This should unblock the highest + priority task that is waiting to peek data from the queue. */ + ulValue = 0x11223344; + if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) + { + /* We were expecting the queue to be empty so we should not of + had a problem writing to the queue. */ + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* By the time we get here the data should have been removed from + the queue. */ + if( uxQueueMessagesWaiting( xQueue ) != 0 ) + { + xErrorDetected = pdTRUE; + } + + /* Write another value to the queue, again waking the highest priority + task that is blocked on the queue. */ + ulValue = 0x01234567; + if( xQueueSendToBack( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) + { + /* We were expecting the queue to be empty so we should not of + had a problem writing to the queue. */ + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* All the other tasks should now have successfully peeked the data. + The data is still in the queue so we should be able to receive it. */ + ulValue = 0; + if( xQueueReceive( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) + { + /* We expected to receive the data. */ + xErrorDetected = pdTRUE; + } + + if( ulValue != 0x01234567 ) + { + /* We did not receive the expected value. */ + } + + /* Lets just delay a while as this is an intensive test as we don't + want to starve other tests of processing time. */ + vTaskDelay( qpeekSHORT_DELAY ); + + /* Unsuspend the other tasks so we can repeat the test - this time + however not all the other tasks will peek the data as the high + priority task is actually going to remove it from the queue. Send + to front is used just to be different. As the queue is empty it + makes no difference to the result. */ + vTaskResume( xMediumPriorityTask ); + vTaskResume( xHighPriorityTask ); + vTaskResume( xHighestPriorityTask ); + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + ulValue = 0xaabbaabb; + if( xQueueSendToFront( xQueue, &ulValue, qpeekNO_BLOCK ) != pdPASS ) + { + /* We were expecting the queue to be empty so we should not of + had a problem writing to the queue. */ + xErrorDetected = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* This time we should find that the queue is empty. The high priority + task actually removed the data rather than just peeking it. */ + if( xQueuePeek( xQueue, &ulValue, qpeekNO_BLOCK ) != errQUEUE_EMPTY ) + { + /* We expected to receive the data. */ + xErrorDetected = pdTRUE; + } + + /* Unsuspend the highest and high priority tasks so we can go back + and repeat the whole thing. The medium priority task should not be + suspended as it was not able to peek the data in this last case. */ + vTaskResume( xHighPriorityTask ); + vTaskResume( xHighestPriorityTask ); + + /* Lets just delay a while as this is an intensive test as we don't + want to starve other tests of processing time. */ + vTaskDelay( qpeekSHORT_DELAY ); + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreQueuePeekTasksStillRunning( void ) +{ +static uint32_t ulLastLoopCounter = 0; + + /* If the demo task is still running then we expect the loopcounter to + have incremented since this function was last called. */ + if( ulLastLoopCounter == ulLoopCounter ) + { + xErrorDetected = pdTRUE; + } + + ulLastLoopCounter = ulLoopCounter; + + /* Errors detected in the task itself will have latched xErrorDetected + to true. */ + + return ( BaseType_t ) !xErrorDetected; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueOverwrite.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueOverwrite.c new file mode 100644 index 0000000..15ffb34 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueOverwrite.c @@ -0,0 +1,268 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Basic task to demonstrate the xQueueOverwrite() function. See the comments + * in the function itself. + */ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo program include files. */ +#include "QueueOverwrite.h" + +/* A block time of 0 just means "don't block". */ +#define qoDONT_BLOCK 0 + +/* Number of times to overwrite the value in the queue. */ +#define qoLOOPS 5 + +/* The task that uses the queue. */ +static void prvQueueOverwriteTask( void *pvParameters ); + +/* Variable that is incremented on each loop of prvQueueOverwriteTask() provided +prvQueueOverwriteTask() has not found any errors. */ +static uint32_t ulLoopCounter = 0; + +/* Set to pdFALSE if an error is discovered by the +vQueueOverwritePeriodicISRDemo() function. */ +static BaseType_t xISRTestStatus = pdPASS; + +/* The queue that is accessed from the ISR. The queue accessed by the task is +created inside the task itself. */ +static QueueHandle_t xISRQueue = NULL; + +/*-----------------------------------------------------------*/ + +void vStartQueueOverwriteTask( UBaseType_t uxPriority ) +{ +const UBaseType_t uxQueueLength = 1; + + /* Create the queue used by the ISR. xQueueOverwriteFromISR() should only + be used on queues that have a length of 1. */ + xISRQueue = xQueueCreate( uxQueueLength, ( UBaseType_t ) sizeof( uint32_t ) ); + + /* Create the test task. The queue used by the test task is created inside + the task itself. */ + xTaskCreate( prvQueueOverwriteTask, "QOver", configMINIMAL_STACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL ); +} +/*-----------------------------------------------------------*/ + +static void prvQueueOverwriteTask( void *pvParameters ) +{ +QueueHandle_t xTaskQueue; +const UBaseType_t uxQueueLength = 1; +uint32_t ulValue, ulStatus = pdPASS, x; + + /* The parameter is not used. */ + ( void ) pvParameters; + + /* Create the queue. xQueueOverwrite() should only be used on queues that + have a length of 1. */ + xTaskQueue = xQueueCreate( uxQueueLength, ( UBaseType_t ) sizeof( uint32_t ) ); + configASSERT( xTaskQueue ); + + for( ;; ) + { + /* The queue is empty. Writing to the queue then reading from the queue + should return the item written. */ + ulValue = 10; + xQueueOverwrite( xTaskQueue, &ulValue ); + + ulValue = 0; + xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK ); + + if( ulValue != 10 ) + { + ulStatus = pdFAIL; + } + + /* Now try writing to the queue several times. Each time the value + in the queue should get overwritten. */ + for( x = 0; x < qoLOOPS; x++ ) + { + /* Write to the queue. */ + xQueueOverwrite( xTaskQueue, &x ); + + /* Check the value in the queue is that written, even though the + queue was not necessarily empty. */ + xQueuePeek( xTaskQueue, &ulValue, qoDONT_BLOCK ); + if( ulValue != x ) + { + ulStatus = pdFAIL; + } + + /* There should always be one item in the queue. */ + if( uxQueueMessagesWaiting( xTaskQueue ) != uxQueueLength ) + { + ulStatus = pdFAIL; + } + } + + /* Empty the queue again. */ + xQueueReceive( xTaskQueue, &ulValue, qoDONT_BLOCK ); + + if( uxQueueMessagesWaiting( xTaskQueue ) != 0 ) + { + ulStatus = pdFAIL; + } + + if( ulStatus != pdFAIL ) + { + /* Increment a counter to show this task is still running without + error. */ + ulLoopCounter++; + } + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xIsQueueOverwriteTaskStillRunning( void ) +{ +BaseType_t xReturn; + + if( xISRTestStatus != pdPASS ) + { + xReturn = pdFAIL; + } + else if( ulLoopCounter > 0 ) + { + xReturn = pdPASS; + } + else + { + /* The task has either stalled of discovered an error. */ + xReturn = pdFAIL; + } + + ulLoopCounter = 0; + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vQueueOverwritePeriodicISRDemo( void ) +{ +static uint32_t ulCallCount = 0; +const uint32_t ulTx1 = 10UL, ulTx2 = 20UL, ulNumberOfSwitchCases = 3UL; +uint32_t ulRx; + + /* This function should be called from an interrupt, such as the tick hook + function vApplicationTickHook(). */ + + configASSERT( xISRQueue ); + + switch( ulCallCount ) + { + case 0: + /* The queue is empty. Write ulTx1 to the queue. In this demo the + last parameter is not used because there are no tasks blocked on + this queue. */ + xQueueOverwriteFromISR( xISRQueue, &ulTx1, NULL ); + + /* Peek the queue to check it holds the expected value. */ + xQueuePeekFromISR( xISRQueue, &ulRx ); + if( ulRx != ulTx1 ) + { + xISRTestStatus = pdFAIL; + } + break; + + case 1: + /* The queue already holds ulTx1. Overwrite the value in the queue + with ulTx2. */ + xQueueOverwriteFromISR( xISRQueue, &ulTx2, NULL ); + break; + + case 2: + /* Read from the queue to empty the queue again. The value read + should be ulTx2. */ + xQueueReceiveFromISR( xISRQueue, &ulRx, NULL ); + + if( ulRx != ulTx2 ) + { + xISRTestStatus = pdFAIL; + } + break; + } + + /* Run the next case in the switch statement above next time this function + is called. */ + ulCallCount++; + + if( ulCallCount >= ulNumberOfSwitchCases ) + { + /* Go back to the start. */ + ulCallCount = 0; + } +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueSet.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueSet.c new file mode 100644 index 0000000..bc8f0e7 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/QueueSet.c @@ -0,0 +1,715 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Tests the use of queue sets. + * + * A receive task creates a number of queues and adds them to a queue set before + * blocking on the queue set receive. A transmit task and (optionally) an + * interrupt repeatedly unblocks the receive task by sending messages to the + * queues in a pseudo random order. The receive task removes the messages from + * the queues and flags an error if the received message does not match that + * expected. The task sends values in the range 0 to + * queuesetINITIAL_ISR_TX_VALUE, and the ISR sends value in the range + * queuesetINITIAL_ISR_TX_VALUE to ULONG_MAX. + */ + + +/* Standard includes. */ +#include +#include + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo includes. */ +#include "QueueSet.h" + +/* The number of queues that are created and added to the queue set. */ +#define queuesetNUM_QUEUES_IN_SET 3 + +/* The length of each created queue. */ +#define queuesetQUEUE_LENGTH 3 + +/* Block times used in this demo. A block time or 0 means "don't block". */ +#define queuesetSHORT_DELAY 200 +#define queuesetDONT_BLOCK 0 + +/* Messages are sent in incrementing order from both a task and an interrupt. +The task sends values in the range 0 to 0xfffe, and the interrupt sends values +in the range of 0xffff to ULONG_MAX. */ +#define queuesetINITIAL_ISR_TX_VALUE 0xffffUL + +/* The priorities used in this demo. */ +#define queuesetLOW_PRIORITY ( tskIDLE_PRIORITY ) +#define queuesetMEDIUM_PRIORITY ( queuesetLOW_PRIORITY + 1 ) + +/* For test purposes the priority of the sending task is changed after every +queuesetPRIORITY_CHANGE_LOOPS number of values are sent to a queue. */ +#define queuesetPRIORITY_CHANGE_LOOPS ( ( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH ) * 2 ) + +/* The ISR sends to the queue every queuesetISR_TX_PERIOD ticks. */ +#define queuesetISR_TX_PERIOD ( 100UL ) + +/* A delay inserted when the Tx task changes its priority to be above the idle +task priority to ensure the idle priority tasks get some CPU time before the +next iteration of the queue set Tx task. */ +#define queuesetTX_LOOP_DELAY ( 200 / portTICK_PERIOD_MS ) + +/* The allowable maximum deviation between a received value and the expected +received value. A deviation will occur when data is received from a queue +inside an ISR in between a task receiving from a queue and the task checking +the received value. */ +#define queuesetALLOWABLE_RX_DEVIATION 3 + +/* Ignore values that are at the boundaries of allowable values to make the +testing of limits easier (don't have to deal with wrapping values). */ +#define queuesetIGNORED_BOUNDARY ( queuesetALLOWABLE_RX_DEVIATION * 2 ) + +typedef enum +{ + eEqualPriority = 0, /* Tx and Rx tasks have the same priority. */ + eTxHigherPriority, /* The priority of the Tx task is above that of the Rx task. */ + eTxLowerPriority /* The priority of the Tx task is below that of the Rx task. */ +} eRelativePriorities; + +/* + * The task that periodically sends to the queue set. + */ +static void prvQueueSetSendingTask( void *pvParameters ); + +/* + * The task that reads from the queue set. + */ +static void prvQueueSetReceivingTask( void *pvParameters ); + +/* + * Check the value received from a queue is the expected value. Some values + * originate from the send task, some values originate from the ISR, with the + * range of the value being used to distinguish between the two message + * sources. + */ +static void prvCheckReceivedValue( uint32_t ulReceived ); + +/* + * For purposes of test coverage, functions that read from and write to a + * queue set from an ISR respectively. + */ +static void prvReceiveFromQueueInSetFromISR( void ); +static void prvSendToQueueInSetFromISR( void ); + +/* + * Create the queues and add them to a queue set before resuming the Tx + * task. + */ +static void prvSetupTest( void ); + +/* + * Checks a value received from a queue falls within the range of expected + * values. + */ +static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived, uint32_t ulExpectedReceived ); + +/* + * Increase test coverage by occasionally change the priorities of the two tasks + * relative to each other. */ +static void prvChangeRelativePriorities( void ); + +/* + * Local pseudo random number seed and return functions. Used to avoid calls + * to the standard library. + */ +static uint32_t prvRand( void ); +static void prvSRand( uint32_t ulSeed ); + +/*-----------------------------------------------------------*/ + +/* The queues that are added to the set. */ +static QueueHandle_t xQueues[ queuesetNUM_QUEUES_IN_SET ] = { 0 }; + +/* Counts how many times each queue in the set is used to ensure all the +queues are used. */ +static uint32_t ulQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 }; + +/* The handle of the queue set to which the queues are added. */ +static QueueSetHandle_t xQueueSet; + +/* If the prvQueueSetReceivingTask() task has not detected any errors then +it increments ulCycleCounter on each iteration. +xAreQueueSetTasksStillRunning() returns pdPASS if the value of +ulCycleCounter has changed between consecutive calls, and pdFALSE if +ulCycleCounter has stopped incrementing (indicating an error condition). */ +static volatile uint32_t ulCycleCounter = 0UL; + +/* Set to pdFAIL if an error is detected by any queue set task. +ulCycleCounter will only be incremented if xQueueSetTasksSatus equals pdPASS. */ +static volatile BaseType_t xQueueSetTasksStatus = pdPASS; + +/* Just a flag to let the function that writes to a queue from an ISR know that +the queues are setup and can be used. */ +static volatile BaseType_t xSetupComplete = pdFALSE; + +/* The value sent to the queue from the ISR is file scope so the +xAreQueeuSetTasksStillRunning() function can check it is incrementing as +expected. */ +static volatile uint32_t ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE; + +/* Used by the pseudo random number generator. */ +static uint32_t ulNextRand = 0; + +/* The task handles are stored so their priorities can be changed. */ +TaskHandle_t xQueueSetSendingTask, xQueueSetReceivingTask; + +/*-----------------------------------------------------------*/ + +void vStartQueueSetTasks( void ) +{ + /* Create the tasks. */ + xTaskCreate( prvQueueSetSendingTask, "SetTx", configMINIMAL_STACK_SIZE, NULL, queuesetMEDIUM_PRIORITY, &xQueueSetSendingTask ); + xTaskCreate( prvQueueSetReceivingTask, "SetRx", configMINIMAL_STACK_SIZE, ( void * ) xQueueSetSendingTask, queuesetMEDIUM_PRIORITY, &xQueueSetReceivingTask ); + + /* It is important that the sending task does not attempt to write to a + queue before the queue has been created. It is therefore placed into the + suspended state before the scheduler has started. It is resumed by the + receiving task after the receiving task has created the queues and added the + queues to the queue set. */ + vTaskSuspend( xQueueSetSendingTask ); +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreQueueSetTasksStillRunning( void ) +{ +static uint32_t ulLastCycleCounter, ulLastISRTxValue = 0; +static uint32_t ulLastQueueUsedCounter[ queuesetNUM_QUEUES_IN_SET ] = { 0 }; +BaseType_t xReturn = pdPASS, x; + + if( ulLastCycleCounter == ulCycleCounter ) + { + /* The cycle counter is no longer being incremented. Either one of the + tasks is stalled or an error has been detected. */ + xReturn = pdFAIL; + } + + ulLastCycleCounter = ulCycleCounter; + + /* Ensure that all the queues in the set have been used. This ensures the + test is working as intended and guards against the rand() in the Tx task + missing some values. */ + for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ ) + { + if( ulLastQueueUsedCounter[ x ] == ulQueueUsedCounter[ x ] ) + { + xReturn = pdFAIL; + } + + ulLastQueueUsedCounter[ x ] = ulQueueUsedCounter[ x ]; + } + + /* Check the global status flag. */ + if( xQueueSetTasksStatus != pdPASS ) + { + xReturn = pdFAIL; + } + + /* Check that the ISR is still sending values to the queues too. */ + if( ulISRTxValue == ulLastISRTxValue ) + { + xReturn = pdFAIL; + } + else + { + ulLastISRTxValue = ulISRTxValue; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvQueueSetSendingTask( void *pvParameters ) +{ +uint32_t ulTaskTxValue = 0, ulQueueToWriteTo; +QueueHandle_t xQueueInUse; + + /* Remove compiler warning about the unused parameter. */ + ( void ) pvParameters; + + /* Seed mini pseudo random number generator. */ + prvSRand( ( uint32_t ) &ulTaskTxValue ); + + for( ;; ) + { + /* Generate the index for the queue to which a value is to be sent. */ + ulQueueToWriteTo = prvRand() % queuesetNUM_QUEUES_IN_SET; + xQueueInUse = xQueues[ ulQueueToWriteTo ]; + + /* Note which index is being written to to ensure all the queues are + used. */ + ( ulQueueUsedCounter[ ulQueueToWriteTo ] )++; + + /* Send to the queue to unblock the task that is waiting for data to + arrive on a queue within the queue set to which this queue belongs. */ + if( xQueueSendToBack( xQueueInUse, &ulTaskTxValue, portMAX_DELAY ) != pdPASS ) + { + /* The send should always pass as an infinite block time was + used. */ + xQueueSetTasksStatus = pdFAIL; + } + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + ulTaskTxValue++; + + /* If the Tx value has reached the range used by the ISR then set it + back to 0. */ + if( ulTaskTxValue == queuesetINITIAL_ISR_TX_VALUE ) + { + ulTaskTxValue = 0; + } + + /* Increase test coverage by occasionally change the priorities of the + two tasks relative to each other. */ + prvChangeRelativePriorities(); + } +} +/*-----------------------------------------------------------*/ + +static void prvChangeRelativePriorities( void ) +{ +static UBaseType_t ulLoops = 0; +static eRelativePriorities ePriorities = eEqualPriority; + + /* Occasionally change the task priority relative to the priority of + the receiving task. */ + ulLoops++; + if( ulLoops >= queuesetPRIORITY_CHANGE_LOOPS ) + { + ulLoops = 0; + + switch( ePriorities ) + { + case eEqualPriority: + /* Both tasks are running with medium priority. Now lower the + priority of the receiving task so the Tx task has the higher + relative priority. */ + vTaskPrioritySet( xQueueSetReceivingTask, queuesetLOW_PRIORITY ); + ePriorities = eTxHigherPriority; + break; + + case eTxHigherPriority: + /* The Tx task is running with a higher priority than the Rx + task. Switch the priorities around so the Rx task has the + higher relative priority. */ + vTaskPrioritySet( xQueueSetReceivingTask, queuesetMEDIUM_PRIORITY ); + vTaskPrioritySet( xQueueSetSendingTask, queuesetLOW_PRIORITY ); + ePriorities = eTxLowerPriority; + break; + + case eTxLowerPriority: + /* The Tx task is running with a lower priority than the Rx + task. Make the priorities equal again. */ + vTaskPrioritySet( xQueueSetSendingTask, queuesetMEDIUM_PRIORITY ); + ePriorities = eEqualPriority; + + /* When both tasks are using a non-idle priority the queue set + tasks will starve idle priority tasks of execution time - so + relax a bit before the next iteration to minimise the impact. */ + vTaskDelay( queuesetTX_LOOP_DELAY ); + + break; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvQueueSetReceivingTask( void *pvParameters ) +{ +uint32_t ulReceived; +QueueHandle_t xActivatedQueue; + + /* Remove compiler warnings. */ + ( void ) pvParameters; + + /* Create the queues and add them to the queue set before resuming the Tx + task. */ + prvSetupTest(); + + for( ;; ) + { + /* Wait for a message to arrive on one of the queues in the set. */ + xActivatedQueue = xQueueSelectFromSet( xQueueSet, portMAX_DELAY ); + configASSERT( xActivatedQueue ); + + if( xActivatedQueue == NULL ) + { + /* This should not happen as an infinite delay was used. */ + xQueueSetTasksStatus = pdFAIL; + } + else + { + /* Reading from the queue should pass with a zero block time as + this task will only run when something has been posted to a task + in the queue set. */ + if( xQueueReceive( xActivatedQueue, &ulReceived, queuesetDONT_BLOCK ) != pdPASS ) + { + xQueueSetTasksStatus = pdFAIL; + } + + /* Ensure the value received was the value expected. This function + manipulates file scope data and is also called from an ISR, hence + the critical section. */ + taskENTER_CRITICAL(); + { + prvCheckReceivedValue( ulReceived ); + } + taskEXIT_CRITICAL(); + } + + if( xQueueSetTasksStatus == pdPASS ) + { + ulCycleCounter++; + } + } +} +/*-----------------------------------------------------------*/ + +void vQueueSetAccessQueueSetFromISR( void ) +{ +static uint32_t ulCallCount = 0; + + /* xSetupComplete is set to pdTRUE when the queues have been created and + are available for use. */ + if( xSetupComplete == pdTRUE ) + { + /* It is intended that this function is called from the tick hook + function, so each call is one tick period apart. */ + ulCallCount++; + if( ulCallCount > queuesetISR_TX_PERIOD ) + { + ulCallCount = 0; + + /* First attempt to read from the queue set. */ + prvReceiveFromQueueInSetFromISR(); + + /* Then write to the queue set. */ + prvSendToQueueInSetFromISR(); + } + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckReceivedValue( uint32_t ulReceived ) +{ +static uint32_t ulExpectedReceivedFromTask = 0, ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE; + + /* Values are received in tasks and interrupts. It is likely that the + receiving task will sometimes get preempted by the receiving interrupt + between reading a value from the queue and calling this function. When + that happens, if the receiving interrupt calls this function the values + will get passed into this function slightly out of order. For that + reason the value passed in is tested against a small range of expected + values, rather than a single absolute value. To make the range testing + easier values in the range limits are ignored. */ + + /* If the received value is equal to or greater than + queuesetINITIAL_ISR_TX_VALUE then it was sent by an ISR. */ + if( ulReceived >= queuesetINITIAL_ISR_TX_VALUE ) + { + /* The value was sent from the ISR. */ + if( ( ulReceived - queuesetINITIAL_ISR_TX_VALUE ) < queuesetIGNORED_BOUNDARY ) + { + /* The value received is at the lower limit of the expected range. + Don't test it and expect to receive one higher next time. */ + } + else if( ( ULONG_MAX - ulReceived ) <= queuesetIGNORED_BOUNDARY ) + { + /* The value received is at the higher limit of the expected range. + Don't test it and expect to wrap soon. */ + } + else + { + /* Check the value against its expected value range. */ + if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromISR ) != pdPASS ) + { + xQueueSetTasksStatus = pdFAIL; + } + } + + configASSERT( xQueueSetTasksStatus ); + + /* It is expected to receive an incrementing number. */ + ulExpectedReceivedFromISR++; + if( ulExpectedReceivedFromISR == 0 ) + { + ulExpectedReceivedFromISR = queuesetINITIAL_ISR_TX_VALUE; + } + } + else + { + /* The value was sent from the Tx task. */ + if( ulReceived < queuesetIGNORED_BOUNDARY ) + { + /* The value received is at the lower limit of the expected range. + Don't test it, and expect to receive one higher next time. */ + } + else if( ( ( queuesetINITIAL_ISR_TX_VALUE - 1 ) - ulReceived ) <= queuesetIGNORED_BOUNDARY ) + { + /* The value received is at the higher limit of the expected range. + Don't test it and expect to wrap soon. */ + } + else + { + /* Check the value against its expected value range. */ + if( prvCheckReceivedValueWithinExpectedRange( ulReceived, ulExpectedReceivedFromTask ) != pdPASS ) + { + xQueueSetTasksStatus = pdFAIL; + } + } + + configASSERT( xQueueSetTasksStatus ); + + /* It is expected to receive an incrementing number. */ + ulExpectedReceivedFromTask++; + if( ulExpectedReceivedFromTask >= queuesetINITIAL_ISR_TX_VALUE ) + { + ulExpectedReceivedFromTask = 0; + } + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvCheckReceivedValueWithinExpectedRange( uint32_t ulReceived, uint32_t ulExpectedReceived ) +{ +BaseType_t xReturn = pdPASS; + + if( ulReceived > ulExpectedReceived ) + { + configASSERT( ( ulReceived - ulExpectedReceived ) <= queuesetALLOWABLE_RX_DEVIATION ); + if( ( ulReceived - ulExpectedReceived ) > queuesetALLOWABLE_RX_DEVIATION ) + { + xReturn = pdFALSE; + } + } + else + { + configASSERT( ( ulExpectedReceived - ulReceived ) <= queuesetALLOWABLE_RX_DEVIATION ); + if( ( ulExpectedReceived - ulReceived ) > queuesetALLOWABLE_RX_DEVIATION ) + { + xReturn = pdFALSE; + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvReceiveFromQueueInSetFromISR( void ) +{ +QueueSetMemberHandle_t xActivatedQueue; +uint32_t ulReceived; + + /* See if any of the queues in the set contain data. */ + xActivatedQueue = xQueueSelectFromSetFromISR( xQueueSet ); + + if( xActivatedQueue != NULL ) + { + /* Reading from the queue for test purposes only. */ + if( xQueueReceiveFromISR( xActivatedQueue, &ulReceived, NULL ) != pdPASS ) + { + /* Data should have been available as the handle was returned from + xQueueSelectFromSetFromISR(). */ + xQueueSetTasksStatus = pdFAIL; + } + + /* Ensure the value received was the value expected. */ + prvCheckReceivedValue( ulReceived ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSendToQueueInSetFromISR( void ) +{ +static BaseType_t xQueueToWriteTo = 0; + + if( xQueueSendFromISR( xQueues[ xQueueToWriteTo ], ( void * ) &ulISRTxValue, NULL ) == pdPASS ) + { + ulISRTxValue++; + + /* If the Tx value has wrapped then set it back to its + initial value. */ + if( ulISRTxValue == 0UL ) + { + ulISRTxValue = queuesetINITIAL_ISR_TX_VALUE; + } + + /* Use a different queue next time. */ + xQueueToWriteTo++; + if( xQueueToWriteTo >= queuesetNUM_QUEUES_IN_SET ) + { + xQueueToWriteTo = 0; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvSetupTest( void ) +{ +BaseType_t x; +uint32_t ulValueToSend = 0; + + /* Ensure the queues are created and the queue set configured before the + sending task is unsuspended. + + First Create the queue set such that it will be able to hold a message for + every space in every queue in the set. */ + xQueueSet = xQueueCreateSet( queuesetNUM_QUEUES_IN_SET * queuesetQUEUE_LENGTH ); + + for( x = 0; x < queuesetNUM_QUEUES_IN_SET; x++ ) + { + /* Create the queue and add it to the set. The queue is just holding + uint32_t value. */ + xQueues[ x ] = xQueueCreate( queuesetQUEUE_LENGTH, sizeof( uint32_t ) ); + configASSERT( xQueues[ x ] ); + if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdPASS ) + { + xQueueSetTasksStatus = pdFAIL; + } + else + { + /* The queue has now been added to the queue set and cannot be added to + another. */ + if( xQueueAddToSet( xQueues[ x ], xQueueSet ) != pdFAIL ) + { + xQueueSetTasksStatus = pdFAIL; + } + } + } + + /* Attempt to remove a queue from a queue set it does not belong + to (NULL being passed as the queue set in this case). */ + if( xQueueRemoveFromSet( xQueues[ 0 ], NULL ) != pdFAIL ) + { + /* It is not possible to successfully remove a queue from a queue + set it does not belong to. */ + xQueueSetTasksStatus = pdFAIL; + } + + /* Attempt to remove a queue from the queue set it does belong to. */ + if( xQueueRemoveFromSet( xQueues[ 0 ], xQueueSet ) != pdPASS ) + { + /* It should be possible to remove the queue from the queue set it + does belong to. */ + xQueueSetTasksStatus = pdFAIL; + } + + /* Add an item to the queue before attempting to add it back into the + set. */ + xQueueSend( xQueues[ 0 ], ( void * ) &ulValueToSend, 0 ); + if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdFAIL ) + { + /* Should not be able to add a non-empty queue to a set. */ + xQueueSetTasksStatus = pdFAIL; + } + + /* Remove the item from the queue before adding the queue back into the + set so the dynamic tests can begin. */ + xQueueReceive( xQueues[ 0 ], &ulValueToSend, 0 ); + if( xQueueAddToSet( xQueues[ 0 ], xQueueSet ) != pdPASS ) + { + /* If the queue was successfully removed from the queue set then it + should be possible to add it back in again. */ + xQueueSetTasksStatus = pdFAIL; + } + + /* The task that sends to the queues is not running yet, so attempting to + read from the queue set should fail. */ + if( xQueueSelectFromSet( xQueueSet, queuesetSHORT_DELAY ) != NULL ) + { + xQueueSetTasksStatus = pdFAIL; + } + + /* Resume the task that writes to the queues. */ + vTaskResume( xQueueSetSendingTask ); + + /* Let the ISR access the queues also. */ + xSetupComplete = pdTRUE; +} +/*-----------------------------------------------------------*/ + +static uint32_t prvRand( void ) +{ + ulNextRand = ( ulNextRand * 1103515245UL ) + 12345UL; + return ( ulNextRand / 65536UL ) % 32768UL; +} +/*-----------------------------------------------------------*/ + +static void prvSRand( uint32_t ulSeed ) +{ + ulNextRand = ulSeed; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/TimerDemo.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/TimerDemo.c new file mode 100644 index 0000000..68ece37 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/TimerDemo.c @@ -0,0 +1,1079 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Tests the behaviour of timers. Some timers are created before the scheduler + * is started, and some after. + */ + +/* Standard includes. */ +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" + +/* Demo program include files. */ +#include "TimerDemo.h" + +#if ( configTIMER_TASK_PRIORITY < 1 ) + #error configTIMER_TASK_PRIORITY must be set to at least 1 for this test/demo to function correctly. +#endif + +#define tmrdemoDONT_BLOCK ( ( TickType_t ) 0 ) +#define tmrdemoONE_SHOT_TIMER_PERIOD ( xBasePeriod * ( TickType_t ) 3 ) +#define trmdemoNUM_TIMER_RESETS ( ( uint8_t ) 10 ) + +/*-----------------------------------------------------------*/ + +/* The callback functions used by the timers. These each increment a counter +to indicate which timer has expired. The auto-reload timers that are used by +the test task (as opposed to being used from an ISR) all share the same +prvAutoReloadTimerCallback() callback function, and use the ID of the +pxExpiredTimer parameter passed into that function to know which counter to +increment. The other timers all have their own unique callback function and +simply increment their counters without using the callback function parameter. */ +static void prvAutoReloadTimerCallback( TimerHandle_t pxExpiredTimer ); +static void prvOneShotTimerCallback( TimerHandle_t pxExpiredTimer ); +static void prvTimerTestTask( void *pvParameters ); +static void prvISRAutoReloadTimerCallback( TimerHandle_t pxExpiredTimer ); +static void prvISROneShotTimerCallback( TimerHandle_t pxExpiredTimer ); + +/* The test functions used by the timer test task. These manipulate the auto +reload and one shot timers in various ways, then delay, then inspect the timers +to ensure they have behaved as expected. */ +static void prvTest1_CreateTimersWithoutSchedulerRunning( void ); +static void prvTest2_CheckTaskAndTimersInitialState( void ); +static void prvTest3_CheckAutoReloadExpireRates( void ); +static void prvTest4_CheckAutoReloadTimersCanBeStopped( void ); +static void prvTest5_CheckBasicOneShotTimerBehaviour( void ); +static void prvTest6_CheckAutoReloadResetBehaviour( void ); +static void prvResetStartConditionsForNextIteration( void ); + +/*-----------------------------------------------------------*/ + +/* Flag that will be latched to pdFAIL should any unexpected behaviour be +detected in any of the demo tests. */ +static volatile BaseType_t xTestStatus = pdPASS; + +/* Counter that is incremented on each cycle of a test. This is used to +detect a stalled task - a test that is no longer running. */ +static volatile uint32_t ulLoopCounter = 0; + +/* A set of auto reload timers - each of which use the same callback function. +The callback function uses the timer ID to index into, and then increment, a +counter in the ucAutoReloadTimerCounters[] array. The auto reload timers +referenced from xAutoReloadTimers[] are used by the prvTimerTestTask task. */ +static TimerHandle_t xAutoReloadTimers[ configTIMER_QUEUE_LENGTH + 1 ] = { 0 }; +static uint8_t ucAutoReloadTimerCounters[ configTIMER_QUEUE_LENGTH + 1 ] = { 0 }; + +/* The one shot timer is configured to use a callback function that increments +ucOneShotTimerCounter each time it gets called. */ +static TimerHandle_t xOneShotTimer = NULL; +static uint8_t ucOneShotTimerCounter = ( uint8_t ) 0; + +/* The ISR reload timer is controlled from the tick hook to exercise the timer +API functions that can be used from an ISR. It is configured to increment +ucISRReloadTimerCounter each time its callback function is executed. */ +static TimerHandle_t xISRAutoReloadTimer = NULL; +static uint8_t ucISRAutoReloadTimerCounter = ( uint8_t ) 0; + +/* The ISR one shot timer is controlled from the tick hook to exercise the timer +API functions that can be used from an ISR. It is configured to increment +ucISRReloadTimerCounter each time its callback function is executed. */ +static TimerHandle_t xISROneShotTimer = NULL; +static uint8_t ucISROneShotTimerCounter = ( uint8_t ) 0; + +/* The period of all the timers are a multiple of the base period. The base +period is configured by the parameter to vStartTimerDemoTask(). */ +static TickType_t xBasePeriod = 0; + +/*-----------------------------------------------------------*/ + +void vStartTimerDemoTask( TickType_t xBasePeriodIn ) +{ + /* Start with the timer and counter arrays clear - this is only necessary + where the compiler does not clear them automatically on start up. */ + memset( ucAutoReloadTimerCounters, 0x00, sizeof( ucAutoReloadTimerCounters ) ); + memset( xAutoReloadTimers, 0x00, sizeof( xAutoReloadTimers ) ); + + /* Store the period from which all the timer periods will be generated from + (multiples of). */ + xBasePeriod = xBasePeriodIn; + + /* Create a set of timers for use by this demo/test. */ + prvTest1_CreateTimersWithoutSchedulerRunning(); + + /* Create the task that will control and monitor the timers. This is + created at a lower priority than the timer service task to ensure, as + far as it is concerned, commands on timers are actioned immediately + (sending a command to the timer service task will unblock the timer service + task, which will then preempt this task). */ + if( xTestStatus != pdFAIL ) + { + xTaskCreate( prvTimerTestTask, "Tmr Tst", configMINIMAL_STACK_SIZE, NULL, configTIMER_TASK_PRIORITY - 1, NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTimerTestTask( void *pvParameters ) +{ + ( void ) pvParameters; + + /* Create a one-shot timer for use later on in this test. */ + xOneShotTimer = xTimerCreate( "Oneshot Timer", /* Text name to facilitate debugging. The kernel does not use this itself. */ + tmrdemoONE_SHOT_TIMER_PERIOD, /* The period for the timer. */ + pdFALSE, /* Don't auto-reload - hence a one shot timer. */ + ( void * ) 0, /* The timer identifier. In this case this is not used as the timer has its own callback. */ + prvOneShotTimerCallback ); /* The callback to be called when the timer expires. */ + + if( xOneShotTimer == NULL ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + + /* Ensure all the timers are in their expected initial state. This + depends on the timer service task having a higher priority than this task. */ + prvTest2_CheckTaskAndTimersInitialState(); + + for( ;; ) + { + /* Check the auto reload timers expire at the expected/correct rates. */ + prvTest3_CheckAutoReloadExpireRates(); + + /* Check the auto reload timers can be stopped correctly, and correctly + report their state. */ + prvTest4_CheckAutoReloadTimersCanBeStopped(); + + /* Check the one shot timer only calls its callback once after it has been + started, and that it reports its state correctly. */ + prvTest5_CheckBasicOneShotTimerBehaviour(); + + /* Check timer reset behaviour. */ + prvTest6_CheckAutoReloadResetBehaviour(); + + /* Start the timers again to restart all the tests over again. */ + prvResetStartConditionsForNextIteration(); + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that the created task is still running and has not +detected any errors. */ +BaseType_t xAreTimerDemoTasksStillRunning( TickType_t xCycleFrequency ) +{ +static uint32_t ulLastLoopCounter = 0UL; +TickType_t xMaxBlockTimeUsedByTheseTests, xLoopCounterIncrementTimeMax; +static TickType_t xIterationsWithoutCounterIncrement = ( TickType_t ) 0, xLastCycleFrequency; + + if( xLastCycleFrequency != xCycleFrequency ) + { + /* The cycle frequency has probably become much faster due to an error + elsewhere. Start counting Iterations again. */ + xIterationsWithoutCounterIncrement = ( TickType_t ) 0; + xLastCycleFrequency = xCycleFrequency; + } + + /* Calculate the maximum number of times that it is permissible for this + function to be called without ulLoopCounter being incremented. This is + necessary because the tests in this file block for extended periods, and the + block period might be longer than the time between calls to this function. */ + xMaxBlockTimeUsedByTheseTests = ( ( TickType_t ) configTIMER_QUEUE_LENGTH ) * xBasePeriod; + xLoopCounterIncrementTimeMax = ( xMaxBlockTimeUsedByTheseTests / xCycleFrequency ) + 1; + + /* If the demo task is still running then the loop counter is expected to + have incremented every xLoopCounterIncrementTimeMax calls. */ + if( ulLastLoopCounter == ulLoopCounter ) + { + xIterationsWithoutCounterIncrement++; + if( xIterationsWithoutCounterIncrement > xLoopCounterIncrementTimeMax ) + { + /* The tests appear to be no longer running (stalled). */ + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else + { + /* ulLoopCounter changed, so the count of times this function was called + without a change can be reset to zero. */ + xIterationsWithoutCounterIncrement = ( TickType_t ) 0; + } + + ulLastLoopCounter = ulLoopCounter; + + /* Errors detected in the task itself will have latched xTestStatus + to pdFAIL. */ + + return xTestStatus; +} +/*-----------------------------------------------------------*/ + +static void prvTest1_CreateTimersWithoutSchedulerRunning( void ) +{ +UBaseType_t xTimer; + + for( xTimer = 0; xTimer < configTIMER_QUEUE_LENGTH; xTimer++ ) + { + /* As the timer queue is not yet full, it should be possible to both create + and start a timer. These timers are being started before the scheduler has + been started, so their block times should get set to zero within the timer + API itself. */ + xAutoReloadTimers[ xTimer ] = xTimerCreate( "FR Timer", /* Text name to facilitate debugging. The kernel does not use this itself. */ + ( ( xTimer + ( TickType_t ) 1 ) * xBasePeriod ),/* The period for the timer. The plus 1 ensures a period of zero is not specified. */ + pdTRUE, /* Auto-reload is set to true. */ + ( void * ) xTimer, /* An identifier for the timer as all the auto reload timers use the same callback. */ + prvAutoReloadTimerCallback ); /* The callback to be called when the timer expires. */ + + configASSERT( strcmp( pcTimerGetTimerName( xAutoReloadTimers[ xTimer ] ), "FR Timer" ) == 0 ); + + if( xAutoReloadTimers[ xTimer ] == NULL ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + else + { + /* The scheduler has not yet started, so the block period of + portMAX_DELAY should just get set to zero in xTimerStart(). Also, + the timer queue is not yet full so xTimerStart() should return + pdPASS. */ + if( xTimerStart( xAutoReloadTimers[ xTimer ], portMAX_DELAY ) != pdPASS ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + } + + /* The timers queue should now be full, so it should be possible to create + another timer, but not possible to start it (the timer queue will not get + drained until the scheduler has been started. */ + xAutoReloadTimers[ configTIMER_QUEUE_LENGTH ] = xTimerCreate( "FR Timer", /* Text name to facilitate debugging. The kernel does not use this itself. */ + ( configTIMER_QUEUE_LENGTH * xBasePeriod ), /* The period for the timer. */ + pdTRUE, /* Auto-reload is set to true. */ + ( void * ) xTimer, /* An identifier for the timer as all the auto reload timers use the same callback. */ + prvAutoReloadTimerCallback ); /* The callback executed when the timer expires. */ + + if( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH ] == NULL ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + else + { + if( xTimerStart( xAutoReloadTimers[ xTimer ], portMAX_DELAY ) == pdPASS ) + { + /* This time it would not be expected that the timer could be + started at this point. */ + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + /* Create the timers that are used from the tick interrupt to test the timer + API functions that can be called from an ISR. */ + xISRAutoReloadTimer = xTimerCreate( "ISR AR", /* The text name given to the timer. */ + 0xffff, /* The timer is not given a period yet - this will be done from the tick hook, but a period of 0 is invalid. */ + pdTRUE, /* This is an auto reload timer. */ + ( void * ) NULL, /* The identifier is not required. */ + prvISRAutoReloadTimerCallback ); /* The callback that is executed when the timer expires. */ + + xISROneShotTimer = xTimerCreate( "ISR OS", /* The text name given to the timer. */ + 0xffff, /* The timer is not given a period yet - this will be done from the tick hook, but a period of 0 is invalid. */ + pdFALSE, /* This is a one shot timer. */ + ( void * ) NULL, /* The identifier is not required. */ + prvISROneShotTimerCallback ); /* The callback that is executed when the timer expires. */ + + if( ( xISRAutoReloadTimer == NULL ) || ( xISROneShotTimer == NULL ) ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTest2_CheckTaskAndTimersInitialState( void ) +{ +uint8_t ucTimer; + + /* Ensure all the timers are in their expected initial state. This depends + on the timer service task having a higher priority than this task. + + auto reload timers 0 to ( configTIMER_QUEUE_LENGTH - 1 ) should now be active, + and auto reload timer configTIMER_QUEUE_LENGTH should not yet be active (it + could not be started prior to the scheduler being started when it was + created). */ + for( ucTimer = 0; ucTimer < ( uint8_t ) configTIMER_QUEUE_LENGTH; ucTimer++ ) + { + if( xTimerIsTimerActive( xAutoReloadTimers[ ucTimer ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + if( xTimerIsTimerActive( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH ] ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } +} +/*-----------------------------------------------------------*/ + +static void prvTest3_CheckAutoReloadExpireRates( void ) +{ +uint8_t ucMaxAllowableValue, ucMinAllowableValue, ucTimer; +TickType_t xBlockPeriod, xTimerPeriod, xExpectedNumber; + + /* Check the auto reload timers expire at the expected rates. */ + + + /* Delaying for configTIMER_QUEUE_LENGTH * xBasePeriod ticks should allow + all the auto reload timers to expire at least once. */ + xBlockPeriod = ( ( TickType_t ) configTIMER_QUEUE_LENGTH ) * xBasePeriod; + vTaskDelay( xBlockPeriod ); + + /* Check that all the auto reload timers have called their callback + function the expected number of times. */ + for( ucTimer = 0; ucTimer < ( uint8_t ) configTIMER_QUEUE_LENGTH; ucTimer++ ) + { + /* The expected number of expiries is equal to the block period divided + by the timer period. */ + xTimerPeriod = ( ( ( TickType_t ) ucTimer + ( TickType_t ) 1 ) * xBasePeriod ); + xExpectedNumber = xBlockPeriod / xTimerPeriod; + + ucMaxAllowableValue = ( ( uint8_t ) xExpectedNumber ) ; + ucMinAllowableValue = ( uint8_t ) ( ( uint8_t ) xExpectedNumber - ( uint8_t ) 1 ); /* Weird casting to try and please all compilers. */ + + if( ( ucAutoReloadTimerCounters[ ucTimer ] < ucMinAllowableValue ) || + ( ucAutoReloadTimerCounters[ ucTimer ] > ucMaxAllowableValue ) + ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so the + check task knows this task is still running. */ + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvTest4_CheckAutoReloadTimersCanBeStopped( void ) +{ +uint8_t ucTimer; + + /* Check the auto reload timers can be stopped correctly, and correctly + report their state. */ + + /* Stop all the active timers. */ + for( ucTimer = 0; ucTimer < ( uint8_t ) configTIMER_QUEUE_LENGTH; ucTimer++ ) + { + /* The timer has not been stopped yet! */ + if( xTimerIsTimerActive( xAutoReloadTimers[ ucTimer ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Now stop the timer. This will appear to happen immediately to + this task because this task is running at a priority below the + timer service task. */ + xTimerStop( xAutoReloadTimers[ ucTimer ], tmrdemoDONT_BLOCK ); + + /* The timer should now be inactive. */ + if( xTimerIsTimerActive( xAutoReloadTimers[ ucTimer ] ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + taskENTER_CRITICAL(); + { + /* The timer in array position configTIMER_QUEUE_LENGTH should not + be active. The critical section is used to ensure the timer does + not call its callback between the next line running and the array + being cleared back to zero, as that would mask an error condition. */ + if( ucAutoReloadTimerCounters[ configTIMER_QUEUE_LENGTH ] != ( uint8_t ) 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Clear the timer callback count. */ + memset( ( void * ) ucAutoReloadTimerCounters, 0, sizeof( ucAutoReloadTimerCounters ) ); + } + taskEXIT_CRITICAL(); + + /* The timers are now all inactive, so this time, after delaying, none + of the callback counters should have incremented. */ + vTaskDelay( ( ( TickType_t ) configTIMER_QUEUE_LENGTH ) * xBasePeriod ); + for( ucTimer = 0; ucTimer < ( uint8_t ) configTIMER_QUEUE_LENGTH; ucTimer++ ) + { + if( ucAutoReloadTimerCounters[ ucTimer ] != ( uint8_t ) 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so + the check task knows this task is still running. */ + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvTest5_CheckBasicOneShotTimerBehaviour( void ) +{ + /* Check the one shot timer only calls its callback once after it has been + started, and that it reports its state correctly. */ + + /* The one shot timer should not be active yet. */ + if( xTimerIsTimerActive( xOneShotTimer ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucOneShotTimerCounter != ( uint8_t ) 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Start the one shot timer and check that it reports its state correctly. */ + xTimerStart( xOneShotTimer, tmrdemoDONT_BLOCK ); + if( xTimerIsTimerActive( xOneShotTimer ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Delay for three times as long as the one shot timer period, then check + to ensure it has only called its callback once, and is now not in the + active state. */ + vTaskDelay( tmrdemoONE_SHOT_TIMER_PERIOD * ( TickType_t ) 3 ); + + if( xTimerIsTimerActive( xOneShotTimer ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucOneShotTimerCounter != ( uint8_t ) 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + else + { + /* Reset the one shot timer callback count. */ + ucOneShotTimerCounter = ( uint8_t ) 0; + } + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so the + check task knows this task is still running. */ + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvTest6_CheckAutoReloadResetBehaviour( void ) +{ +uint8_t ucTimer; + + /* Check timer reset behaviour. */ + + /* Restart the one shot timer and check it reports its status correctly. */ + xTimerStart( xOneShotTimer, tmrdemoDONT_BLOCK ); + if( xTimerIsTimerActive( xOneShotTimer ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Restart one of the auto reload timers and check that it reports its + status correctly. */ + xTimerStart( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ], tmrdemoDONT_BLOCK ); + if( xTimerIsTimerActive( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + for( ucTimer = 0; ucTimer < trmdemoNUM_TIMER_RESETS; ucTimer++ ) + { + /* Delay for half as long as the one shot timer period, then reset it. + It should never expire while this is done, so its callback count should + never increment. */ + vTaskDelay( tmrdemoONE_SHOT_TIMER_PERIOD / 2 ); + + /* Check both running timers are still active, but have not called their + callback functions. */ + if( xTimerIsTimerActive( xOneShotTimer ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucOneShotTimerCounter != ( uint8_t ) 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( xTimerIsTimerActive( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucAutoReloadTimerCounters[ configTIMER_QUEUE_LENGTH - 1 ] != ( uint8_t ) 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Reset both running timers. */ + xTimerReset( xOneShotTimer, tmrdemoDONT_BLOCK ); + xTimerReset( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ], tmrdemoDONT_BLOCK ); + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so + the check task knows this task is still running. */ + ulLoopCounter++; + } + } + + /* Finally delay long enough for both running timers to expire. */ + vTaskDelay( ( ( TickType_t ) configTIMER_QUEUE_LENGTH ) * xBasePeriod ); + + /* The timers were not reset during the above delay period so should now + both have called their callback functions. */ + if( ucOneShotTimerCounter != ( uint8_t ) 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucAutoReloadTimerCounters[ configTIMER_QUEUE_LENGTH - 1 ] == 0 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* The one shot timer should no longer be active, while the auto reload + timer should still be active. */ + if( xTimerIsTimerActive( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( xTimerIsTimerActive( xOneShotTimer ) == pdTRUE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Stop the auto reload timer again. */ + xTimerStop( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ], tmrdemoDONT_BLOCK ); + + if( xTimerIsTimerActive( xAutoReloadTimers[ configTIMER_QUEUE_LENGTH - 1 ] ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Clear the timer callback counts, ready for another iteration of these + tests. */ + ucAutoReloadTimerCounters[ configTIMER_QUEUE_LENGTH - 1 ] = ( uint8_t ) 0; + ucOneShotTimerCounter = ( uint8_t ) 0; + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so the check + task knows this task is still running. */ + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +static void prvResetStartConditionsForNextIteration( void ) +{ +uint8_t ucTimer; + + /* Start the timers again to start all the tests over again. */ + + /* Start the timers again. */ + for( ucTimer = 0; ucTimer < ( uint8_t ) configTIMER_QUEUE_LENGTH; ucTimer++ ) + { + /* The timer has not been started yet! */ + if( xTimerIsTimerActive( xAutoReloadTimers[ ucTimer ] ) != pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Now start the timer. This will appear to happen immediately to + this task because this task is running at a priority below the timer + service task. */ + xTimerStart( xAutoReloadTimers[ ucTimer ], tmrdemoDONT_BLOCK ); + + /* The timer should now be active. */ + if( xTimerIsTimerActive( xAutoReloadTimers[ ucTimer ] ) == pdFALSE ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + + if( xTestStatus == pdPASS ) + { + /* No errors have been reported so increment the loop counter so the + check task knows this task is still running. */ + ulLoopCounter++; + } +} +/*-----------------------------------------------------------*/ + +void vTimerPeriodicISRTests( void ) +{ +static TickType_t uxTick = ( TickType_t ) -1; + +#if( configTIMER_TASK_PRIORITY != ( configMAX_PRIORITIES - 1 ) ) + /* The timer service task is not the highest priority task, so it cannot + be assumed that timings will be exact. Timers should never call their + callback before their expiry time, but a margin is permissible for calling + their callback after their expiry time. If exact timing is required then + configTIMER_TASK_PRIORITY must be set to ensure the timer service task + is the highest priority task in the system. + + This function is called from the tick hook. The tick hook is called + even when the scheduler is suspended. Therefore it is possible that the + uxTick count maintained in this function is temporarily ahead of the tick + count maintained by the kernel. When this is the case a message posted from + this function will assume a time stamp in advance of the real time stamp, + which can result in a timer being processed before this function expects it + to. For example, if the kernel's tick count was 100, and uxTick was 102, + then this function will not expect the timer to have expired until the + kernel's tick count is (102 + xBasePeriod), whereas in reality the timer + will expire when the kernel's tick count is (100 + xBasePeriod). For this + reason xMargin is used as an allowable margin for premature timer expiries + as well as late timer expiries. */ + const TickType_t xMargin = 6; +#else + #ifdef _WINDOWS_ + /* Windows is not real real time. */ + const TickType_t xMargin = 8; + #else + const TickType_t xMargin = 4; + #endif /* _WINDOWS_ */ +#endif + + + uxTick++; + + if( uxTick == 0 ) + { + /* The timers will have been created, but not started. Start them now + by setting their period. */ + ucISRAutoReloadTimerCounter = 0; + ucISROneShotTimerCounter = 0; + + /* It is possible that the timer task has not yet made room in the + timer queue. If the timers cannot be started then reset uxTick so + another attempt is made later. */ + uxTick = ( TickType_t ) -1; + + /* Try starting first timer. */ + if( xTimerChangePeriodFromISR( xISRAutoReloadTimer, xBasePeriod, NULL ) == pdPASS ) + { + /* First timer was started, try starting the second timer. */ + if( xTimerChangePeriodFromISR( xISROneShotTimer, xBasePeriod, NULL ) == pdPASS ) + { + /* Both timers were started, so set the uxTick back to its + proper value. */ + uxTick = 0; + } + else + { + /* Second timer could not be started, so stop the first one + again. */ + xTimerStopFromISR( xISRAutoReloadTimer, NULL ); + } + } + } + else if( uxTick == ( xBasePeriod - xMargin ) ) + { + /* Neither timer should have expired yet. */ + if( ( ucISRAutoReloadTimerCounter != 0 ) || ( ucISROneShotTimerCounter != 0 ) ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( xBasePeriod + xMargin ) ) + { + /* Both timers should now have expired once. The auto reload timer will + still be active, but the one shot timer should now have stopped. */ + if( ( ucISRAutoReloadTimerCounter != 1 ) || ( ucISROneShotTimerCounter != 1 ) ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( ( 2 * xBasePeriod ) - xMargin ) ) + { + /* The auto reload timer will still be active, but the one shot timer + should now have stopped - however, at this time neither of the timers + should have expired again since the last test. */ + if( ( ucISRAutoReloadTimerCounter != 1 ) || ( ucISROneShotTimerCounter != 1 ) ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( ( 2 * xBasePeriod ) + xMargin ) ) + { + /* The auto reload timer will still be active, but the one shot timer + should now have stopped. At this time the auto reload timer should have + expired again, but the one shot timer count should not have changed. */ + if( ucISRAutoReloadTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( ( 2 * xBasePeriod ) + ( xBasePeriod >> ( TickType_t ) 2U ) ) ) + { + /* The auto reload timer will still be active, but the one shot timer + should now have stopped. Again though, at this time, neither timer call + back should have been called since the last test. */ + if( ucISRAutoReloadTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( 3 * xBasePeriod ) ) + { + /* Start the one shot timer again. */ + xTimerStartFromISR( xISROneShotTimer, NULL ); + } + else if( uxTick == ( ( 3 * xBasePeriod ) + xMargin ) ) + { + /* The auto reload timer and one shot timer will be active. At + this time the auto reload timer should have expired again, but the one + shot timer count should not have changed yet. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Now stop the auto reload timer. The one shot timer was started + a few ticks ago. */ + xTimerStopFromISR( xISRAutoReloadTimer, NULL ); + } + else if( uxTick == ( 4 * ( xBasePeriod - xMargin ) ) ) + { + /* The auto reload timer is now stopped, and the one shot timer is + active, but at this time neither timer should have expired since the + last test. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 1 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( ( 4 * xBasePeriod ) + xMargin ) ) + { + /* The auto reload timer is now stopped, and the one shot timer is + active. The one shot timer should have expired again, but the auto + reload timer should not have executed its callback. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( 8 * xBasePeriod ) ) + { + /* The auto reload timer is now stopped, and the one shot timer has + already expired and then stopped itself. Both callback counters should + not have incremented since the last test. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + /* Now reset the one shot timer. */ + xTimerResetFromISR( xISROneShotTimer, NULL ); + } + else if( uxTick == ( ( 9 * xBasePeriod ) - xMargin ) ) + { + /* Only the one shot timer should be running, but it should not have + expired since the last test. Check the callback counters have not + incremented, then reset the one shot timer again. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + xTimerResetFromISR( xISROneShotTimer, NULL ); + } + else if( uxTick == ( ( 10 * xBasePeriod ) - ( 2 * xMargin ) ) ) + { + /* Only the one shot timer should be running, but it should not have + expired since the last test. Check the callback counters have not + incremented, then reset the one shot timer again. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + xTimerResetFromISR( xISROneShotTimer, NULL ); + } + else if( uxTick == ( ( 11 * xBasePeriod ) - ( 3 * xMargin ) ) ) + { + /* Only the one shot timer should be running, but it should not have + expired since the last test. Check the callback counters have not + incremented, then reset the one shot timer once again. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 2 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + xTimerResetFromISR( xISROneShotTimer, NULL ); + } + else if( uxTick == ( ( 12 * xBasePeriod ) - ( 2 * xMargin ) ) ) + { + /* Only the one shot timer should have been running and this time it + should have expired. Check its callback count has been incremented. + The auto reload timer is still not running so should still have the same + count value. This time the one shot timer is not reset so should not + restart from its expiry period again. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + } + else if( uxTick == ( 15 * xBasePeriod ) ) + { + /* Neither timer should be running now. Check neither callback count + has incremented, then go back to the start to run these tests all + over again. */ + if( ucISRAutoReloadTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + if( ucISROneShotTimerCounter != 3 ) + { + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } + + uxTick = ( TickType_t ) -1; + } +} +/*-----------------------------------------------------------*/ + +/*** Timer callback functions are defined below here. ***/ + +static void prvAutoReloadTimerCallback( TimerHandle_t pxExpiredTimer ) +{ +uint32_t ulTimerID; + + ulTimerID = ( uint32_t ) pvTimerGetTimerID( pxExpiredTimer ); + if( ulTimerID <= ( configTIMER_QUEUE_LENGTH + 1 ) ) + { + ( ucAutoReloadTimerCounters[ ulTimerID ] )++; + } + else + { + /* The timer ID appears to be unexpected (invalid). */ + xTestStatus = pdFAIL; + configASSERT( xTestStatus ); + } +} +/*-----------------------------------------------------------*/ + +static void prvOneShotTimerCallback( TimerHandle_t pxExpiredTimer ) +{ + /* The parameter is not used in this case as only one timer uses this + callback function. */ + ( void ) pxExpiredTimer; + + ucOneShotTimerCounter++; +} +/*-----------------------------------------------------------*/ + +static void prvISRAutoReloadTimerCallback( TimerHandle_t pxExpiredTimer ) +{ + /* The parameter is not used in this case as only one timer uses this + callback function. */ + ( void ) pxExpiredTimer; + + ucISRAutoReloadTimerCounter++; +} +/*-----------------------------------------------------------*/ + +static void prvISROneShotTimerCallback( TimerHandle_t pxExpiredTimer ) +{ + /* The parameter is not used in this case as only one timer uses this + callback function. */ + ( void ) pxExpiredTimer; + + ucISROneShotTimerCounter++; +} +/*-----------------------------------------------------------*/ + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/blocktim.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/blocktim.c new file mode 100644 index 0000000..2528317 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/blocktim.c @@ -0,0 +1,505 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This file contains some test scenarios that ensure tasks do not exit queue + * send or receive functions prematurely. A description of the tests is + * included within the code. + */ + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* Demo includes. */ +#include "blocktim.h" + +/* Task priorities. Allow these to be overridden. */ +#ifndef bktPRIMARY_PRIORITY + #define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 ) +#endif + +#ifndef bktSECONDARY_PRIORITY + #define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 ) +#endif + +/* Task behaviour. */ +#define bktQUEUE_LENGTH ( 5 ) +#define bktSHORT_WAIT ( ( ( TickType_t ) 20 ) / portTICK_PERIOD_MS ) +#define bktPRIMARY_BLOCK_TIME ( 10 ) +#define bktALLOWABLE_MARGIN ( 15 ) +#define bktTIME_TO_BLOCK ( 175 ) +#define bktDONT_BLOCK ( ( TickType_t ) 0 ) +#define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 ) + +/* The queue on which the tasks block. */ +static QueueHandle_t xTestQueue; + +/* Handle to the secondary task is required by the primary task for calls +to vTaskSuspend/Resume(). */ +static TaskHandle_t xSecondary; + +/* Used to ensure that tasks are still executing without error. */ +static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0; +static volatile BaseType_t xErrorOccurred = pdFALSE; + +/* Provides a simple mechanism for the primary task to know when the +secondary task has executed. */ +static volatile UBaseType_t xRunIndicator; + +/* The two test tasks. Their behaviour is commented within the files. */ +static void vPrimaryBlockTimeTestTask( void *pvParameters ); +static void vSecondaryBlockTimeTestTask( void *pvParameters ); + +/*-----------------------------------------------------------*/ + +void vCreateBlockTimeTasks( void ) +{ + /* Create the queue on which the two tasks block. */ + xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" ); + + /* Create the two test tasks. */ + xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL ); + xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary ); +} +/*-----------------------------------------------------------*/ + +static void vPrimaryBlockTimeTestTask( void *pvParameters ) +{ +BaseType_t xItem, xData; +TickType_t xTimeWhenBlocking; +TickType_t xTimeToBlock, xBlockedTime; + + ( void ) pvParameters; + + for( ;; ) + { + /********************************************************************* + Test 1 + + Simple block time wakeup test on queue receives. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is empty. Attempt to read from the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); + + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + /********************************************************************* + Test 2 + + Simple block time wakeup test on queue sends. + + First fill the queue. It should be empty so all sends should pass. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* The queue is full. Attempt to write to the queue using a block + time. When we wake, ensure the delta in time is as expected. */ + xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem ); + + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after xTimeToBlock having not received + anything on the queue. */ + if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we blocked for? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + if( xBlockedTime < xTimeToBlock ) + { + /* Should not have blocked for less than we requested. */ + xErrorOccurred = pdTRUE; + } + + if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) ) + { + /* Should not have blocked for longer than we requested, + although we would not necessarily run as soon as we were + unblocked so a margin is allowed. */ + xErrorOccurred = pdTRUE; + } + } + + /********************************************************************* + Test 3 + + Wake the other task, it will block attempting to post to the queue. + When we read from the queue the other task will wake, but before it + can run we will post to the queue again. When the other task runs it + will find the queue still full, even though it was woken. It should + recognise that its block time has not expired and return to block for + the remains of its block time. + + Wake the other task so it blocks attempting to post to the already + full queue. */ + xRunIndicator = 0; + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + /* The other task has not yet executed. */ + vTaskDelay( bktSHORT_WAIT ); + } + /* Make sure the other task is blocked on the queue. */ + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we make space on the queue the other task should wake + but not execute as this task has higher priority. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now fill the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + full ourselves, and the other task have set xRunIndicator. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + + /* Set the priority back down. */ + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + + /********************************************************************* + Test 4 + + As per test 3 - but with the send and receive the other way around. + The other task blocks attempting to read from the queue. + + Empty the queue. We should find that it is full. */ + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + /* Wake the other task so it blocks attempting to read from the + already empty queue. */ + vTaskResume( xSecondary ); + + /* We need to wait a little to ensure the other task executes. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + xRunIndicator = 0; + + for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ ) + { + /* Now when we place an item on the queue the other task should + wake but not execute as this task has higher priority. */ + if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Now empty the queue again before the other task gets a chance to + execute. If the other task had executed we would find the queue + empty ourselves, and the other task would be suspended. */ + if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed. */ + xErrorOccurred = pdTRUE; + } + + /* Raise the priority of the other task so it executes and blocks + on the queue again. */ + vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 ); + + /* The other task should now have re-blocked without exiting the + queue function. */ + if( xRunIndicator == bktRUN_INDICATOR ) + { + /* The other task should not have executed outside of the + queue function. */ + xErrorOccurred = pdTRUE; + } + vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY ); + } + + /* Let the other task timeout. When it unblockes it will check that it + unblocked at the correct time, then suspend itself. */ + while( xRunIndicator != bktRUN_INDICATOR ) + { + vTaskDelay( bktSHORT_WAIT ); + } + vTaskDelay( bktSHORT_WAIT ); + + xPrimaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void vSecondaryBlockTimeTestTask( void *pvParameters ) +{ +TickType_t xTimeWhenBlocking, xBlockedTime; +BaseType_t xData; + + ( void ) pvParameters; + + for( ;; ) + { + /********************************************************************* + Test 1 and 2 + + This task does does not participate in these tests. */ + vTaskSuspend( NULL ); + + /********************************************************************* + Test 3 + + The first thing we do is attempt to read from the queue. It should be + full so we block. Note the time before we block so we can check the + wake time is as per that expected. */ + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not sent + anything to the queue. */ + xData = 0; + xRunIndicator = bktRUN_INDICATOR; + if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL ) + { + xErrorOccurred = pdTRUE; + } + + /* How long were we inside the send function? */ + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as + soon as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + /* Suspend ready for test 3. */ + xRunIndicator = bktRUN_INDICATOR; + vTaskSuspend( NULL ); + + /********************************************************************* + Test 4 + + As per test three, but with the send and receive reversed. */ + xTimeWhenBlocking = xTaskGetTickCount(); + + /* We should unblock after bktTIME_TO_BLOCK having not received + anything on the queue. */ + xRunIndicator = bktRUN_INDICATOR; + if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY ) + { + xErrorOccurred = pdTRUE; + } + + xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking; + + /* We should not have blocked for less time than bktTIME_TO_BLOCK. */ + if( xBlockedTime < bktTIME_TO_BLOCK ) + { + xErrorOccurred = pdTRUE; + } + + /* We should of not blocked for much longer than bktALLOWABLE_MARGIN + either. A margin is permitted as we would not necessarily run as soon + as we unblocked. */ + if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) ) + { + xErrorOccurred = pdTRUE; + } + + xRunIndicator = bktRUN_INDICATOR; + + xSecondaryCycles++; + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreBlockTimeTestTasksStillRunning( void ) +{ +static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0; +BaseType_t xReturn = pdPASS; + + /* Have both tasks performed at least one cycle since this function was + last called? */ + if( xPrimaryCycles == xLastPrimaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xSecondaryCycles == xLastSecondaryCycleCount ) + { + xReturn = pdFAIL; + } + + if( xErrorOccurred == pdTRUE ) + { + xReturn = pdFAIL; + } + + xLastSecondaryCycleCount = xSecondaryCycles; + xLastPrimaryCycleCount = xPrimaryCycles; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest.c new file mode 100644 index 0000000..2a1542a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest.c @@ -0,0 +1,303 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * This version of comtest. c is for use on systems that have limited stack + * space and no display facilities. The complete version can be found in + * the Demo/Common/Full directory. + * + * Creates two tasks that operate on an interrupt driven serial port. A + * loopback connector should be used so that everything that is transmitted is + * also received. The serial port does not use any flow control. On a + * standard 9way 'D' connector pins two and three should be connected together. + * + * The first task posts a sequence of characters to the Tx queue, toggling an + * LED on each successful post. At the end of the sequence it sleeps for a + * pseudo-random period before resending the same sequence. + * + * The UART Tx end interrupt is enabled whenever data is available in the Tx + * queue. The Tx end ISR removes a single character from the Tx queue and + * passes it to the UART for transmission. + * + * The second task blocks on the Rx queue waiting for a character to become + * available. When the UART Rx end interrupt receives a character it places + * it in the Rx queue, waking the second task. The second task checks that the + * characters removed from the Rx queue form the same sequence as those posted + * to the Tx queue, and toggles an LED for each correct character. + * + * The receiving task is spawned with a higher priority than the transmitting + * task. The receiver will therefore wake every time a character is + * transmitted so neither the Tx or Rx queue should ever hold more than a few + * characters. + * + */ + +/* Scheduler include files. */ +#include +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "serial.h" +#include "comtest.h" +#include "partest.h" + +#define comSTACK_SIZE configMINIMAL_STACK_SIZE +#define comTX_LED_OFFSET ( 0 ) +#define comRX_LED_OFFSET ( 1 ) +#define comTOTAL_PERMISSIBLE_ERRORS ( 2 ) + +/* The Tx task will transmit the sequence of characters at a pseudo random +interval. This is the maximum and minimum block time between sends. */ +#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 ) +#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 ) +#define comOFFSET_TIME ( ( TickType_t ) 3 ) + +/* We should find that each character can be queued for Tx immediately and we +don't have to block to send. */ +#define comNO_BLOCK ( ( TickType_t ) 0 ) + +/* The Rx task will block on the Rx queue for a long period. */ +#define comRX_BLOCK_TIME ( ( TickType_t ) 0xffff ) + +/* The sequence transmitted is from comFIRST_BYTE to and including comLAST_BYTE. */ +#define comFIRST_BYTE ( 'A' ) +#define comLAST_BYTE ( 'X' ) + +#define comBUFFER_LEN ( ( UBaseType_t ) ( comLAST_BYTE - comFIRST_BYTE ) + ( UBaseType_t ) 1 ) +#define comINITIAL_RX_COUNT_VALUE ( 0 ) + +/* Handle to the com port used by both tasks. */ +static xComPortHandle xPort = NULL; + +/* The transmit task as described at the top of the file. */ +static portTASK_FUNCTION_PROTO( vComTxTask, pvParameters ); + +/* The receive task as described at the top of the file. */ +static portTASK_FUNCTION_PROTO( vComRxTask, pvParameters ); + +/* The LED that should be toggled by the Rx and Tx tasks. The Rx task will +toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task will toggle LED +( uxBaseLED + comTX_LED_OFFSET ). */ +static UBaseType_t uxBaseLED = 0; + +/* Check variable used to ensure no error have occurred. The Rx task will +increment this variable after every successfully received sequence. If at any +time the sequence is incorrect the the variable will stop being incremented. */ +static volatile UBaseType_t uxRxLoops = comINITIAL_RX_COUNT_VALUE; + +/*-----------------------------------------------------------*/ + +void vAltStartComTestTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ) +{ + /* Initialise the com port then spawn the Rx and Tx tasks. */ + uxBaseLED = uxLED; + xSerialPortInitMinimal( ulBaudRate, comBUFFER_LEN ); + + /* The Tx task is spawned with a lower priority than the Rx task. */ + xTaskCreate( vComTxTask, "COMTx", comSTACK_SIZE, NULL, uxPriority - 1, ( TaskHandle_t * ) NULL ); + xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vComTxTask, pvParameters ) +{ +char cByteToSend; +TickType_t xTimeToWait; + + /* Just to stop compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Simply transmit a sequence of characters from comFIRST_BYTE to + comLAST_BYTE. */ + for( cByteToSend = comFIRST_BYTE; cByteToSend <= comLAST_BYTE; cByteToSend++ ) + { + if( xSerialPutChar( xPort, cByteToSend, comNO_BLOCK ) == pdPASS ) + { + vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET ); + } + } + + /* Turn the LED off while we are not doing anything. */ + vParTestSetLED( uxBaseLED + comTX_LED_OFFSET, pdFALSE ); + + /* We have posted all the characters in the string - wait before + re-sending. Wait a pseudo-random time as this will provide a better + test. */ + xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME; + + /* Make sure we don't wait too long... */ + xTimeToWait %= comTX_MAX_BLOCK_TIME; + + /* ...but we do want to wait. */ + if( xTimeToWait < comTX_MIN_BLOCK_TIME ) + { + xTimeToWait = comTX_MIN_BLOCK_TIME; + } + + vTaskDelay( xTimeToWait ); + } +} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */ +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vComRxTask, pvParameters ) +{ +signed char cExpectedByte, cByteRxed; +BaseType_t xResyncRequired = pdFALSE, xErrorOccurred = pdFALSE; + + /* Just to stop compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* We expect to receive the characters from comFIRST_BYTE to + comLAST_BYTE in an incrementing order. Loop to receive each byte. */ + for( cExpectedByte = comFIRST_BYTE; cExpectedByte <= comLAST_BYTE; cExpectedByte++ ) + { + /* Block on the queue that contains received bytes until a byte is + available. */ + if( xSerialGetChar( xPort, &cByteRxed, comRX_BLOCK_TIME ) ) + { + /* Was this the byte we were expecting? If so, toggle the LED, + otherwise we are out on sync and should break out of the loop + until the expected character sequence is about to restart. */ + if( cByteRxed == cExpectedByte ) + { + vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET ); + } + else + { + xResyncRequired = pdTRUE; + break; /*lint !e960 Non-switch break allowed. */ + } + } + } + + /* Turn the LED off while we are not doing anything. */ + vParTestSetLED( uxBaseLED + comRX_LED_OFFSET, pdFALSE ); + + /* Did we break out of the loop because the characters were received in + an unexpected order? If so wait here until the character sequence is + about to restart. */ + if( xResyncRequired == pdTRUE ) + { + while( cByteRxed != comLAST_BYTE ) + { + /* Block until the next char is available. */ + xSerialGetChar( xPort, &cByteRxed, comRX_BLOCK_TIME ); + } + + /* Note that an error occurred which caused us to have to resync. + We use this to stop incrementing the loop counter so + sAreComTestTasksStillRunning() will return false - indicating an + error. */ + xErrorOccurred++; + + /* We have now resynced with the Tx task and can continue. */ + xResyncRequired = pdFALSE; + } + else + { + if( xErrorOccurred < comTOTAL_PERMISSIBLE_ERRORS ) + { + /* Increment the count of successful loops. As error + occurring (i.e. an unexpected character being received) will + prevent this counter being incremented for the rest of the + execution. Don't worry about mutual exclusion on this + variable - it doesn't really matter as we just want it + to change. */ + uxRxLoops++; + } + } + } +} /*lint !e715 !e818 pvParameters is required for a task function even if it is not referenced. */ +/*-----------------------------------------------------------*/ + +BaseType_t xAreComTestTasksStillRunning( void ) +{ +BaseType_t xReturn; + + /* If the count of successful reception loops has not changed than at + some time an error occurred (i.e. a character was received out of sequence) + and we will return false. */ + if( uxRxLoops == comINITIAL_RX_COUNT_VALUE ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + /* Reset the count of successful Rx loops. When this function is called + again we expect this to have been incremented. */ + uxRxLoops = comINITIAL_RX_COUNT_VALUE; + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest_strings.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest_strings.c new file mode 100644 index 0000000..0e84b12 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/comtest_strings.c @@ -0,0 +1,349 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Creates a task and a timer that operate on an interrupt driven serial port. + * This demo assumes that the characters transmitted on a port will also be + * received on the same port. Therefore, the UART must either be connected to + * an echo server, or the uart connector must have a loopback connector fitted. + * See http://www.serialporttool.com/CommEcho.htm for a suitable echo server + * for Windows hosts. + * + * The timer sends a string to the UART, toggles an LED, then resets itself by + * changing its own period. The period is calculated as a pseudo random number + * between comTX_MAX_BLOCK_TIME and comTX_MIN_BLOCK_TIME. + * + * The task blocks on an Rx queue waiting for a character to become available. + * Received characters are checked to ensure they match those transmitted by the + * Tx timer. An error is latched if characters are missing, incorrect, or + * arrive too slowly. + * + * How characters are actually transmitted and received is port specific. Demos + * that include this test/demo file will provide example drivers. The Tx timer + * executes in the context of the timer service (daemon) task, and must + * therefore never attempt to block. + * + */ + +/* Scheduler include files. */ +#include +#include +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" + +#ifndef configUSE_TIMERS + #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h. +#endif + +#if configUSE_TIMERS != 1 + #error This demo uses timers. configUSE_TIMERS must be set to 1 in FreeRTOSConfig.h. +#endif + + +/* Demo program include files. */ +#include "serial.h" +#include "comtest_strings.h" +#include "partest.h" + +/* The size of the stack given to the Rx task. */ +#define comSTACK_SIZE configMINIMAL_STACK_SIZE + +/* See the comment above the declaraction of the uxBaseLED variable. */ +#define comTX_LED_OFFSET ( 0 ) +#define comRX_LED_OFFSET ( 1 ) + +/* The Tx timer transmits the sequence of characters at a pseudo random +interval that is capped between comTX_MAX_BLOCK_TIME and +comTX_MIN_BLOCK_TIME. */ +#define comTX_MAX_BLOCK_TIME ( ( TickType_t ) 0x96 ) +#define comTX_MIN_BLOCK_TIME ( ( TickType_t ) 0x32 ) +#define comOFFSET_TIME ( ( TickType_t ) 3 ) + +/* States for the simple state machine implemented in the Rx task. */ +#define comtstWAITING_START_OF_STRING 0 +#define comtstWAITING_END_OF_STRING 1 + +/* A short delay in ticks - this delay is used to allow the Rx queue to fill up +a bit so more than one character can be processed at a time. This is relative +to comTX_MIN_BLOCK_TIME to ensure it is never longer than the shortest gap +between transmissions. It could be worked out more scientifically from the +baud rate being used. */ +#define comSHORT_DELAY ( comTX_MIN_BLOCK_TIME >> ( TickType_t ) 2 ) + +/* The string that is transmitted and received. */ +#define comTRANSACTED_STRING "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" + +/* A block time of 0 simply means "don't block". */ +#define comtstDONT_BLOCK ( TickType_t ) 0 + +/* Handle to the com port used by both tasks. */ +static xComPortHandle xPort = NULL; + +/* The callback function allocated to the transmit timer, as described in the +comments at the top of this file. */ +static void prvComTxTimerCallback( TimerHandle_t xTimer ); + +/* The receive task as described in the comments at the top of this file. */ +static void vComRxTask( void *pvParameters ); + +/* The Rx task will toggle LED ( uxBaseLED + comRX_LED_OFFSET). The Tx task +will toggle LED ( uxBaseLED + comTX_LED_OFFSET ). */ +static UBaseType_t uxBaseLED = 0; + +/* The Rx task toggles uxRxLoops on each successful iteration of its defined +function - provided no errors have ever been latched. If this variable stops +incrementing, then an error has occurred. */ +static volatile UBaseType_t uxRxLoops = 0UL; + +/* The timer used to periodically transmit the string. This is the timer that +has prvComTxTimerCallback allocated to it as its callback function. */ +static TimerHandle_t xTxTimer = NULL; + +/* The string length is held at file scope so the Tx timer does not need to +calculate it each time it executes. */ +static size_t xStringLength = 0U; + +/*-----------------------------------------------------------*/ + +void vStartComTestStringsTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ) +{ + /* Store values that are used at run time. */ + uxBaseLED = uxLED; + + /* Calculate the string length here, rather than each time the Tx timer + executes. */ + xStringLength = strlen( comTRANSACTED_STRING ); + + /* Include the null terminator in the string length as this is used to + detect the end of the string in the Rx task. */ + xStringLength++; + + /* Initialise the com port, then spawn the Rx task and create the Tx + timer. */ + xSerialPortInitMinimal( ulBaudRate, ( xStringLength * 2U ) ); + + /* Create the Rx task and the Tx timer. The timer is started from the + Rx task. */ + xTaskCreate( vComRxTask, "COMRx", comSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL ); + xTxTimer = xTimerCreate( "TxTimer", comTX_MIN_BLOCK_TIME, pdFALSE, NULL, prvComTxTimerCallback ); + configASSERT( xTxTimer ); +} +/*-----------------------------------------------------------*/ + +static void prvComTxTimerCallback( TimerHandle_t xTimer ) +{ +TickType_t xTimeToWait; + + /* The parameter is not used in this case. */ + ( void ) xTimer; + + /* Send the string. How this is actually performed depends on the + sample driver provided with this demo. However - as this is a timer, + it executes in the context of the timer task and therefore must not + block. */ + vSerialPutString( xPort, comTRANSACTED_STRING, xStringLength ); + + /* Toggle an LED to give a visible indication that another transmission + has been performed. */ + vParTestToggleLED( uxBaseLED + comTX_LED_OFFSET ); + + /* Wait a pseudo random time before sending the string again. */ + xTimeToWait = xTaskGetTickCount() + comOFFSET_TIME; + + /* Ensure the time to wait is not greater than comTX_MAX_BLOCK_TIME. */ + xTimeToWait %= comTX_MAX_BLOCK_TIME; + + /* Ensure the time to wait is not less than comTX_MIN_BLOCK_TIME. */ + if( xTimeToWait < comTX_MIN_BLOCK_TIME ) + { + xTimeToWait = comTX_MIN_BLOCK_TIME; + } + + /* Reset the timer to run again xTimeToWait ticks from now. This function + is called from the context of the timer task, so the block time must not + be anything other than zero. */ + xTimerChangePeriod( xTxTimer, xTimeToWait, comtstDONT_BLOCK ); +} +/*-----------------------------------------------------------*/ + +static void vComRxTask( void *pvParameters ) +{ +BaseType_t xState = comtstWAITING_START_OF_STRING, xErrorOccurred = pdFALSE; +char *pcExpectedByte, cRxedChar; +const xComPortHandle xPort = NULL; + + /* The parameter is not used in this example. */ + ( void ) pvParameters; + + /* Start the Tx timer. This only needs to be started once, as it will + reset itself thereafter. */ + xTimerStart( xTxTimer, portMAX_DELAY ); + + /* The first expected Rx character is the first in the string that is + transmitted. */ + pcExpectedByte = comTRANSACTED_STRING; + + for( ;; ) + { + /* Wait for the next character. */ + if( xSerialGetChar( xPort, &cRxedChar, ( comTX_MAX_BLOCK_TIME * 2 ) ) == pdFALSE ) + { + /* A character definitely should have been received by now. As a + character was not received an error must have occurred (which might + just be that the loopback connector is not fitted). */ + xErrorOccurred = pdTRUE; + } + + switch( xState ) + { + case comtstWAITING_START_OF_STRING: + if( cRxedChar == *pcExpectedByte ) + { + /* The received character was the first character of the + string. Move to the next state to check each character + as it comes in until the entire string has been received. */ + xState = comtstWAITING_END_OF_STRING; + pcExpectedByte++; + + /* Block for a short period. This just allows the Rx queue + to contain more than one character, and therefore prevent + thrashing reads to the queue, and repetitive context + switches as each character is received. */ + vTaskDelay( comSHORT_DELAY ); + } + break; + + case comtstWAITING_END_OF_STRING: + if( cRxedChar == *pcExpectedByte ) + { + /* The received character was the expected character. Was + it the last character in the string - i.e. the null + terminator? */ + if( cRxedChar == 0x00 ) + { + /* The entire string has been received. If no errors + have been latched, then increment the loop counter to + show this task is still healthy. */ + if( xErrorOccurred == pdFALSE ) + { + uxRxLoops++; + + /* Toggle an LED to give a visible sign that a + complete string has been received. */ + vParTestToggleLED( uxBaseLED + comRX_LED_OFFSET ); + } + + /* Go back to wait for the start of the next string. */ + pcExpectedByte = comTRANSACTED_STRING; + xState = comtstWAITING_START_OF_STRING; + } + else + { + /* Wait for the next character in the string. */ + pcExpectedByte++; + } + } + else + { + /* The character received was not that expected. */ + xErrorOccurred = pdTRUE; + } + break; + + default: + /* Should not get here. Stop the Rx loop counter from + incrementing to latch the error. */ + xErrorOccurred = pdTRUE; + break; + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreComTestTasksStillRunning( void ) +{ +BaseType_t xReturn; + + /* If the count of successful reception loops has not changed than at + some time an error occurred (i.e. a character was received out of sequence) + and false is returned. */ + if( uxRxLoops == 0UL ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + /* Reset the count of successful Rx loops. When this function is called + again it should have been incremented again. */ + uxRxLoops = 0UL; + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/countsem.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/countsem.c new file mode 100644 index 0000000..aa33933 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/countsem.c @@ -0,0 +1,322 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Simple demonstration of the usage of counting semaphore. + */ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo program include files. */ +#include "countsem.h" + +/* The maximum count value that the semaphore used for the demo can hold. */ +#define countMAX_COUNT_VALUE ( 200 ) + +/* Constants used to indicate whether or not the semaphore should have been +created with its maximum count value, or its minimum count value. These +numbers are used to ensure that the pointers passed in as the task parameters +are valid. */ +#define countSTART_AT_MAX_COUNT ( 0xaa ) +#define countSTART_AT_ZERO ( 0x55 ) + +/* Two tasks are created for the test. One uses a semaphore created with its +count value set to the maximum, and one with the count value set to zero. */ +#define countNUM_TEST_TASKS ( 2 ) +#define countDONT_BLOCK ( 0 ) + +/*-----------------------------------------------------------*/ + +/* Flag that will be latched to pdTRUE should any unexpected behaviour be +detected in any of the tasks. */ +static volatile BaseType_t xErrorDetected = pdFALSE; + +/*-----------------------------------------------------------*/ + +/* + * The demo task. This simply counts the semaphore up to its maximum value, + * the counts it back down again. The result of each semaphore 'give' and + * 'take' is inspected, with an error being flagged if it is found not to be + * the expected result. + */ +static void prvCountingSemaphoreTask( void *pvParameters ); + +/* + * Utility function to increment the semaphore count value up from zero to + * countMAX_COUNT_VALUE. + */ +static void prvIncrementSemaphoreCount( SemaphoreHandle_t xSemaphore, UBaseType_t *puxLoopCounter ); + +/* + * Utility function to decrement the semaphore count value up from + * countMAX_COUNT_VALUE to zero. + */ +static void prvDecrementSemaphoreCount( SemaphoreHandle_t xSemaphore, UBaseType_t *puxLoopCounter ); + +/*-----------------------------------------------------------*/ + +/* The structure that is passed into the task as the task parameter. */ +typedef struct COUNT_SEM_STRUCT +{ + /* The semaphore to be used for the demo. */ + SemaphoreHandle_t xSemaphore; + + /* Set to countSTART_AT_MAX_COUNT if the semaphore should be created with + its count value set to its max count value, or countSTART_AT_ZERO if it + should have been created with its count value set to 0. */ + UBaseType_t uxExpectedStartCount; + + /* Incremented on each cycle of the demo task. Used to detect a stalled + task. */ + UBaseType_t uxLoopCounter; +} xCountSemStruct; + +/* Two structures are defined, one is passed to each test task. */ +static volatile xCountSemStruct xParameters[ countNUM_TEST_TASKS ]; + +/*-----------------------------------------------------------*/ + +void vStartCountingSemaphoreTasks( void ) +{ + /* Create the semaphores that we are going to use for the test/demo. The + first should be created such that it starts at its maximum count value, + the second should be created such that it starts with a count value of zero. */ + xParameters[ 0 ].xSemaphore = xSemaphoreCreateCounting( countMAX_COUNT_VALUE, countMAX_COUNT_VALUE ); + xParameters[ 0 ].uxExpectedStartCount = countSTART_AT_MAX_COUNT; + xParameters[ 0 ].uxLoopCounter = 0; + + xParameters[ 1 ].xSemaphore = xSemaphoreCreateCounting( countMAX_COUNT_VALUE, 0 ); + xParameters[ 1 ].uxExpectedStartCount = 0; + xParameters[ 1 ].uxLoopCounter = 0; + + /* vQueueAddToRegistry() adds the semaphore to the registry, if one is + in use. The registry is provided as a means for kernel aware + debuggers to locate semaphores and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( ( QueueHandle_t ) xParameters[ 0 ].xSemaphore, "Counting_Sem_1" ); + vQueueAddToRegistry( ( QueueHandle_t ) xParameters[ 1 ].xSemaphore, "Counting_Sem_2" ); + + + /* Were the semaphores created? */ + if( ( xParameters[ 0 ].xSemaphore != NULL ) || ( xParameters[ 1 ].xSemaphore != NULL ) ) + { + /* Create the demo tasks, passing in the semaphore to use as the parameter. */ + xTaskCreate( prvCountingSemaphoreTask, "CNT1", configMINIMAL_STACK_SIZE, ( void * ) &( xParameters[ 0 ] ), tskIDLE_PRIORITY, NULL ); + xTaskCreate( prvCountingSemaphoreTask, "CNT2", configMINIMAL_STACK_SIZE, ( void * ) &( xParameters[ 1 ] ), tskIDLE_PRIORITY, NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvDecrementSemaphoreCount( SemaphoreHandle_t xSemaphore, UBaseType_t *puxLoopCounter ) +{ +UBaseType_t ux; + + /* If the semaphore count is at its maximum then we should not be able to + 'give' the semaphore. */ + if( xSemaphoreGive( xSemaphore ) == pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* We should be able to 'take' the semaphore countMAX_COUNT_VALUE times. */ + for( ux = 0; ux < countMAX_COUNT_VALUE; ux++ ) + { + if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) != pdPASS ) + { + /* We expected to be able to take the semaphore. */ + xErrorDetected = pdTRUE; + } + + ( *puxLoopCounter )++; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the semaphore count is zero then we should not be able to 'take' + the semaphore. */ + if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) == pdPASS ) + { + xErrorDetected = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +static void prvIncrementSemaphoreCount( SemaphoreHandle_t xSemaphore, UBaseType_t *puxLoopCounter ) +{ +UBaseType_t ux; + + /* If the semaphore count is zero then we should not be able to 'take' + the semaphore. */ + if( xSemaphoreTake( xSemaphore, countDONT_BLOCK ) == pdPASS ) + { + xErrorDetected = pdTRUE; + } + + /* We should be able to 'give' the semaphore countMAX_COUNT_VALUE times. */ + for( ux = 0; ux < countMAX_COUNT_VALUE; ux++ ) + { + if( xSemaphoreGive( xSemaphore ) != pdPASS ) + { + /* We expected to be able to take the semaphore. */ + xErrorDetected = pdTRUE; + } + + ( *puxLoopCounter )++; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the semaphore count is at its maximum then we should not be able to + 'give' the semaphore. */ + if( xSemaphoreGive( xSemaphore ) == pdPASS ) + { + xErrorDetected = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +static void prvCountingSemaphoreTask( void *pvParameters ) +{ +xCountSemStruct *pxParameter; + + #ifdef USE_STDIO + void vPrintDisplayMessage( const char * const * ppcMessageToSend ); + + const char * const pcTaskStartMsg = "Counting semaphore demo started.\r\n"; + + /* Queue a message for printing to say the task has started. */ + vPrintDisplayMessage( &pcTaskStartMsg ); + #endif + + /* The semaphore to be used was passed as the parameter. */ + pxParameter = ( xCountSemStruct * ) pvParameters; + + /* Did we expect to find the semaphore already at its max count value, or + at zero? */ + if( pxParameter->uxExpectedStartCount == countSTART_AT_MAX_COUNT ) + { + prvDecrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) ); + } + + /* Now we expect the semaphore count to be 0, so this time there is an + error if we can take the semaphore. */ + if( xSemaphoreTake( pxParameter->xSemaphore, 0 ) == pdPASS ) + { + xErrorDetected = pdTRUE; + } + + for( ;; ) + { + prvIncrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) ); + prvDecrementSemaphoreCount( pxParameter->xSemaphore, &( pxParameter->uxLoopCounter ) ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreCountingSemaphoreTasksStillRunning( void ) +{ +static UBaseType_t uxLastCount0 = 0, uxLastCount1 = 0; +BaseType_t xReturn = pdPASS; + + /* Return fail if any 'give' or 'take' did not result in the expected + behaviour. */ + if( xErrorDetected != pdFALSE ) + { + xReturn = pdFAIL; + } + + /* Return fail if either task is not still incrementing its loop counter. */ + if( uxLastCount0 == xParameters[ 0 ].uxLoopCounter ) + { + xReturn = pdFAIL; + } + else + { + uxLastCount0 = xParameters[ 0 ].uxLoopCounter; + } + + if( uxLastCount1 == xParameters[ 1 ].uxLoopCounter ) + { + xReturn = pdFAIL; + } + else + { + uxLastCount1 = xParameters[ 1 ].uxLoopCounter; + } + + return xReturn; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crflash.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crflash.c new file mode 100644 index 0000000..4507f1c --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crflash.c @@ -0,0 +1,246 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This demo application file demonstrates the use of queues to pass data + * between co-routines. + * + * N represents the number of 'fixed delay' co-routines that are created and + * is set during initialisation. + * + * N 'fixed delay' co-routines are created that just block for a fixed + * period then post the number of an LED onto a queue. Each such co-routine + * uses a different block period. A single 'flash' co-routine is also created + * that blocks on the same queue, waiting for the number of the next LED it + * should flash. Upon receiving a number it simply toggle the instructed LED + * then blocks on the queue once more. In this manner each LED from LED 0 to + * LED N-1 is caused to flash at a different rate. + * + * The 'fixed delay' co-routines are created with co-routine priority 0. The + * flash co-routine is created with co-routine priority 1. This means that + * the queue should never contain more than a single item. This is because + * posting to the queue will unblock the 'flash' co-routine, and as this has + * a priority greater than the tasks posting to the queue it is guaranteed to + * have emptied the queue and blocked once again before the queue can contain + * any more date. An error is indicated if an attempt to post data to the + * queue fails - indicating that the queue is already full. + * + */ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "croutine.h" +#include "queue.h" + +/* Demo application includes. */ +#include "partest.h" +#include "crflash.h" + +/* The queue should only need to be of length 1. See the description at the +top of the file. */ +#define crfQUEUE_LENGTH 1 + +#define crfFIXED_DELAY_PRIORITY 0 +#define crfFLASH_PRIORITY 1 + +/* Only one flash co-routine is created so the index is not significant. */ +#define crfFLASH_INDEX 0 + +/* Don't allow more than crfMAX_FLASH_TASKS 'fixed delay' co-routines to be +created. */ +#define crfMAX_FLASH_TASKS 8 + +/* We don't want to block when posting to the queue. */ +#define crfPOSTING_BLOCK_TIME 0 + +/* + * The 'fixed delay' co-routine as described at the top of the file. + */ +static void prvFixedDelayCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ); + +/* + * The 'flash' co-routine as described at the top of the file. + */ +static void prvFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ); + +/* The queue used to pass data between the 'fixed delay' co-routines and the +'flash' co-routine. */ +static QueueHandle_t xFlashQueue; + +/* This will be set to pdFALSE if we detect an error. */ +static BaseType_t xCoRoutineFlashStatus = pdPASS; + +/*-----------------------------------------------------------*/ + +/* + * See the header file for details. + */ +void vStartFlashCoRoutines( UBaseType_t uxNumberToCreate ) +{ +UBaseType_t uxIndex; + + if( uxNumberToCreate > crfMAX_FLASH_TASKS ) + { + uxNumberToCreate = crfMAX_FLASH_TASKS; + } + + /* Create the queue used to pass data between the co-routines. */ + xFlashQueue = xQueueCreate( crfQUEUE_LENGTH, sizeof( UBaseType_t ) ); + + if( xFlashQueue ) + { + /* Create uxNumberToCreate 'fixed delay' co-routines. */ + for( uxIndex = 0; uxIndex < uxNumberToCreate; uxIndex++ ) + { + xCoRoutineCreate( prvFixedDelayCoRoutine, crfFIXED_DELAY_PRIORITY, uxIndex ); + } + + /* Create the 'flash' co-routine. */ + xCoRoutineCreate( prvFlashCoRoutine, crfFLASH_PRIORITY, crfFLASH_INDEX ); + } +} +/*-----------------------------------------------------------*/ + +static void prvFixedDelayCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ) +{ +/* Even though this is a co-routine the xResult variable does not need to be +static as we do not need it to maintain its state between blocks. */ +BaseType_t xResult; +/* The uxIndex parameter of the co-routine function is used as an index into +the xFlashRates array to obtain the delay period to use. */ +static const TickType_t xFlashRates[ crfMAX_FLASH_TASKS ] = { 150 / portTICK_PERIOD_MS, + 200 / portTICK_PERIOD_MS, + 250 / portTICK_PERIOD_MS, + 300 / portTICK_PERIOD_MS, + 350 / portTICK_PERIOD_MS, + 400 / portTICK_PERIOD_MS, + 450 / portTICK_PERIOD_MS, + 500 / portTICK_PERIOD_MS }; + + /* Co-routines MUST start with a call to crSTART. */ + crSTART( xHandle ); + + for( ;; ) + { + /* Post our uxIndex value onto the queue. This is used as the LED to + flash. */ + crQUEUE_SEND( xHandle, xFlashQueue, ( void * ) &uxIndex, crfPOSTING_BLOCK_TIME, &xResult ); + + if( xResult != pdPASS ) + { + /* For the reasons stated at the top of the file we should always + find that we can post to the queue. If we could not then an error + has occurred. */ + xCoRoutineFlashStatus = pdFAIL; + } + + crDELAY( xHandle, xFlashRates[ uxIndex ] ); + } + + /* Co-routines MUST end with a call to crEND. */ + crEND(); +} +/*-----------------------------------------------------------*/ + +static void prvFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ) +{ +/* Even though this is a co-routine the variable do not need to be +static as we do not need it to maintain their state between blocks. */ +BaseType_t xResult; +UBaseType_t uxLEDToFlash; + + /* Co-routines MUST start with a call to crSTART. */ + crSTART( xHandle ); + ( void ) uxIndex; + + for( ;; ) + { + /* Block to wait for the number of the LED to flash. */ + crQUEUE_RECEIVE( xHandle, xFlashQueue, &uxLEDToFlash, portMAX_DELAY, &xResult ); + + if( xResult != pdPASS ) + { + /* We would not expect to wake unless we received something. */ + xCoRoutineFlashStatus = pdFAIL; + } + else + { + /* We received the number of an LED to flash - flash it! */ + vParTestToggleLED( uxLEDToFlash ); + } + } + + /* Co-routines MUST end with a call to crEND. */ + crEND(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreFlashCoRoutinesStillRunning( void ) +{ + /* Return pdPASS or pdFAIL depending on whether an error has been detected + or not. */ + return xCoRoutineFlashStatus; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crhook.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crhook.c new file mode 100644 index 0000000..1e3a18e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/crhook.c @@ -0,0 +1,270 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This demo file demonstrates how to send data between an ISR and a + * co-routine. A tick hook function is used to periodically pass data between + * the RTOS tick and a set of 'hook' co-routines. + * + * hookNUM_HOOK_CO_ROUTINES co-routines are created. Each co-routine blocks + * to wait for a character to be received on a queue from the tick ISR, checks + * to ensure the character received was that expected, then sends the number + * back to the tick ISR on a different queue. + * + * The tick ISR checks the numbers received back from the 'hook' co-routines + * matches the number previously sent. + * + * If at any time a queue function returns unexpectedly, or an incorrect value + * is received either by the tick hook or a co-routine then an error is + * latched. + * + * This demo relies on each 'hook' co-routine to execute between each + * hookTICK_CALLS_BEFORE_POST tick interrupts. This and the heavy use of + * queues from within an interrupt may result in an error being detected on + * slower targets simply due to timing. + */ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "croutine.h" +#include "queue.h" + +/* Demo application includes. */ +#include "crhook.h" + +/* The number of 'hook' co-routines that are to be created. */ +#define hookNUM_HOOK_CO_ROUTINES ( 4 ) + +/* The number of times the tick hook should be called before a character is +posted to the 'hook' co-routines. */ +#define hookTICK_CALLS_BEFORE_POST ( 500 ) + +/* There should never be more than one item in any queue at any time. */ +#define hookHOOK_QUEUE_LENGTH ( 1 ) + +/* Don't block when initially posting to the queue. */ +#define hookNO_BLOCK_TIME ( 0 ) + +/* The priority relative to other co-routines (rather than tasks) that the +'hook' co-routines should take. */ +#define mainHOOK_CR_PRIORITY ( 1 ) +/*-----------------------------------------------------------*/ + +/* + * The co-routine function itself. + */ +static void prvHookCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ); + + +/* + * The tick hook function. This receives a number from each 'hook' co-routine + * then sends a number to each co-routine. An error is flagged if a send or + * receive fails, or an unexpected number is received. + */ +void vApplicationTickHook( void ); + +/*-----------------------------------------------------------*/ + +/* Queues used to send data FROM a co-routine TO the tick hook function. +The hook functions received (Rx's) on these queues. One queue per +'hook' co-routine. */ +static QueueHandle_t xHookRxQueues[ hookNUM_HOOK_CO_ROUTINES ]; + +/* Queues used to send data FROM the tick hook TO a co-routine function. +The hood function transmits (Tx's) on these queues. One queue per +'hook' co-routine. */ +static QueueHandle_t xHookTxQueues[ hookNUM_HOOK_CO_ROUTINES ]; + +/* Set to true if an error is detected at any time. */ +static BaseType_t xCoRoutineErrorDetected = pdFALSE; + +/*-----------------------------------------------------------*/ + +void vStartHookCoRoutines( void ) +{ +UBaseType_t uxIndex, uxValueToPost = 0; + + for( uxIndex = 0; uxIndex < hookNUM_HOOK_CO_ROUTINES; uxIndex++ ) + { + /* Create a queue to transmit to and receive from each 'hook' + co-routine. */ + xHookRxQueues[ uxIndex ] = xQueueCreate( hookHOOK_QUEUE_LENGTH, sizeof( UBaseType_t ) ); + xHookTxQueues[ uxIndex ] = xQueueCreate( hookHOOK_QUEUE_LENGTH, sizeof( UBaseType_t ) ); + + /* To start things off the tick hook function expects the queue it + uses to receive data to contain a value. */ + xQueueSend( xHookRxQueues[ uxIndex ], &uxValueToPost, hookNO_BLOCK_TIME ); + + /* Create the 'hook' co-routine itself. */ + xCoRoutineCreate( prvHookCoRoutine, mainHOOK_CR_PRIORITY, uxIndex ); + } +} +/*-----------------------------------------------------------*/ + +static UBaseType_t uxCallCounter = 0, uxNumberToPost = 0; +void vApplicationTickHook( void ) +{ +UBaseType_t uxReceivedNumber; +BaseType_t xIndex, xCoRoutineWoken; + + /* Is it time to talk to the 'hook' co-routines again? */ + uxCallCounter++; + if( uxCallCounter >= hookTICK_CALLS_BEFORE_POST ) + { + uxCallCounter = 0; + + for( xIndex = 0; xIndex < hookNUM_HOOK_CO_ROUTINES; xIndex++ ) + { + xCoRoutineWoken = pdFALSE; + if( crQUEUE_RECEIVE_FROM_ISR( xHookRxQueues[ xIndex ], &uxReceivedNumber, &xCoRoutineWoken ) != pdPASS ) + { + /* There is no reason why we would not expect the queue to + contain a value. */ + xCoRoutineErrorDetected = pdTRUE; + } + else + { + /* Each queue used to receive data from the 'hook' co-routines + should contain the number we last posted to the same co-routine. */ + if( uxReceivedNumber != uxNumberToPost ) + { + xCoRoutineErrorDetected = pdTRUE; + } + + /* Nothing should be blocked waiting to post to the queue. */ + if( xCoRoutineWoken != pdFALSE ) + { + xCoRoutineErrorDetected = pdTRUE; + } + } + } + + /* Start the next cycle by posting the next number onto each Tx queue. */ + uxNumberToPost++; + + for( xIndex = 0; xIndex < hookNUM_HOOK_CO_ROUTINES; xIndex++ ) + { + if( crQUEUE_SEND_FROM_ISR( xHookTxQueues[ xIndex ], &uxNumberToPost, pdFALSE ) != pdTRUE ) + { + /* Posting to the queue should have woken the co-routine that + was blocked on the queue. */ + xCoRoutineErrorDetected = pdTRUE; + } + } + } +} +/*-----------------------------------------------------------*/ + +static void prvHookCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex ) +{ +static UBaseType_t uxReceivedValue[ hookNUM_HOOK_CO_ROUTINES ]; +BaseType_t xResult; + + /* Each co-routine MUST start with a call to crSTART(); */ + crSTART( xHandle ); + + for( ;; ) + { + /* Wait to receive a value from the tick hook. */ + xResult = pdFAIL; + crQUEUE_RECEIVE( xHandle, xHookTxQueues[ uxIndex ], &( uxReceivedValue[ uxIndex ] ), portMAX_DELAY, &xResult ); + + /* There is no reason why we should not have received something on + the queue. */ + if( xResult != pdPASS ) + { + xCoRoutineErrorDetected = pdTRUE; + } + + /* Send the same number back to the idle hook so it can verify it. */ + xResult = pdFAIL; + crQUEUE_SEND( xHandle, xHookRxQueues[ uxIndex ], &( uxReceivedValue[ uxIndex ] ), hookNO_BLOCK_TIME, &xResult ); + if( xResult != pdPASS ) + { + /* There is no reason why we should not have been able to post to + the queue. */ + xCoRoutineErrorDetected = pdTRUE; + } + } + + /* Each co-routine MUST end with a call to crEND(). */ + crEND(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xAreHookCoRoutinesStillRunning( void ) +{ + if( xCoRoutineErrorDetected ) + { + return pdFALSE; + } + else + { + return pdTRUE; + } +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/death.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/death.c new file mode 100644 index 0000000..d567971 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/death.c @@ -0,0 +1,254 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Create a single persistent task which periodically dynamically creates another + * two tasks. The original task is called the creator task, the two tasks it + * creates are called suicidal tasks. + * + * One of the created suicidal tasks kill one other suicidal task before killing + * itself - leaving just the original task remaining. + * + * The creator task must be spawned after all of the other demo application tasks + * as it keeps a check on the number of tasks under the scheduler control. The + * number of tasks it expects to see running should never be greater than the + * number of tasks that were in existence when the creator task was spawned, plus + * one set of four suicidal tasks. If this number is exceeded an error is flagged. + * + * \page DeathC death.c + * \ingroup DemoFiles + *
+ */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "death.h" + +#define deathSTACK_SIZE ( configMINIMAL_STACK_SIZE + 60 ) + +/* The task originally created which is responsible for periodically dynamically +creating another four tasks. */ +static portTASK_FUNCTION_PROTO( vCreateTasks, pvParameters ); + +/* The task function of the dynamically created tasks. */ +static portTASK_FUNCTION_PROTO( vSuicidalTask, pvParameters ); + +/* A variable which is incremented every time the dynamic tasks are created. This +is used to check that the task is still running. */ +static volatile uint16_t usCreationCount = 0; + +/* Used to store the number of tasks that were originally running so the creator +task can tell if any of the suicidal tasks have failed to die. +*/ +static volatile UBaseType_t uxTasksRunningAtStart = 0; + +/* Tasks are deleted by the idle task. Under heavy load the idle task might +not get much processing time, so it would be legitimate for several tasks to +remain undeleted for a short period. */ +static const UBaseType_t uxMaxNumberOfExtraTasksRunning = 3; + +/* Used to store a handle to the task that should be killed by a suicidal task, +before it kills itself. */ +TaskHandle_t xCreatedTask; + +/*-----------------------------------------------------------*/ + +void vCreateSuicidalTasks( UBaseType_t uxPriority ) +{ +UBaseType_t *puxPriority; + + /* Create the Creator tasks - passing in as a parameter the priority at which + the suicidal tasks should be created. */ + puxPriority = ( UBaseType_t * ) pvPortMalloc( sizeof( UBaseType_t ) ); + *puxPriority = uxPriority; + + xTaskCreate( vCreateTasks, "CREATOR", deathSTACK_SIZE, ( void * ) puxPriority, uxPriority, NULL ); + + /* Record the number of tasks that are running now so we know if any of the + suicidal tasks have failed to be killed. */ + uxTasksRunningAtStart = ( UBaseType_t ) uxTaskGetNumberOfTasks(); + + /* FreeRTOS.org versions before V3.0 started the idle-task as the very + first task. The idle task was then already included in uxTasksRunningAtStart. + From FreeRTOS V3.0 on, the idle task is started when the scheduler is + started. Therefore the idle task is not yet accounted for. We correct + this by increasing uxTasksRunningAtStart by 1. */ + uxTasksRunningAtStart++; + + /* From FreeRTOS version 7.0.0 can optionally create a timer service task. + If this is done, then uxTasksRunningAtStart needs incrementing again as that + too is created when the scheduler is started. */ + #if configUSE_TIMERS == 1 + uxTasksRunningAtStart++; + #endif +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vSuicidalTask, pvParameters ) +{ +volatile long l1, l2; +TaskHandle_t xTaskToKill; +const TickType_t xDelay = ( TickType_t ) 200 / portTICK_PERIOD_MS; + + if( pvParameters != NULL ) + { + /* This task is periodically created four times. Two created tasks are + passed a handle to the other task so it can kill it before killing itself. + The other task is passed in null. */ + xTaskToKill = *( TaskHandle_t* )pvParameters; + } + else + { + xTaskToKill = NULL; + } + + for( ;; ) + { + /* Do something random just to use some stack and registers. */ + l1 = 2; + l2 = 89; + l2 *= l1; + vTaskDelay( xDelay ); + + if( xTaskToKill != NULL ) + { + /* Make sure the other task has a go before we delete it. */ + vTaskDelay( ( TickType_t ) 0 ); + + /* Kill the other task that was created by vCreateTasks(). */ + vTaskDelete( xTaskToKill ); + + /* Kill ourselves. */ + vTaskDelete( NULL ); + } + } +}/*lint !e818 !e550 Function prototype must be as per standard for task functions. */ +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCreateTasks, pvParameters ) +{ +const TickType_t xDelay = ( TickType_t ) 1000 / portTICK_PERIOD_MS; +UBaseType_t uxPriority; + + uxPriority = *( UBaseType_t * ) pvParameters; + vPortFree( pvParameters ); + + for( ;; ) + { + /* Just loop round, delaying then creating the four suicidal tasks. */ + vTaskDelay( xDelay ); + + xCreatedTask = NULL; + + xTaskCreate( vSuicidalTask, "SUICID1", configMINIMAL_STACK_SIZE, NULL, uxPriority, &xCreatedTask ); + xTaskCreate( vSuicidalTask, "SUICID2", configMINIMAL_STACK_SIZE, &xCreatedTask, uxPriority, NULL ); + + ++usCreationCount; + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that the creator task is still running and that there +are not any more than four extra tasks. */ +BaseType_t xIsCreateTaskStillRunning( void ) +{ +static uint16_t usLastCreationCount = 0xfff; +BaseType_t xReturn = pdTRUE; +static UBaseType_t uxTasksRunningNow; + + if( usLastCreationCount == usCreationCount ) + { + xReturn = pdFALSE; + } + else + { + usLastCreationCount = usCreationCount; + } + + uxTasksRunningNow = ( UBaseType_t ) uxTaskGetNumberOfTasks(); + + if( uxTasksRunningNow < uxTasksRunningAtStart ) + { + xReturn = pdFALSE; + } + else if( ( uxTasksRunningNow - uxTasksRunningAtStart ) > uxMaxNumberOfExtraTasksRunning ) + { + xReturn = pdFALSE; + } + else + { + /* Everything is okay. */ + } + + return xReturn; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/dynamic.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/dynamic.c new file mode 100644 index 0000000..29ef3ef --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/dynamic.c @@ -0,0 +1,511 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * The first test creates three tasks - two counter tasks (one continuous count + * and one limited count) and one controller. A "count" variable is shared + * between all three tasks. The two counter tasks should never be in a "ready" + * state at the same time. The controller task runs at the same priority as + * the continuous count task, and at a lower priority than the limited count + * task. + * + * One counter task loops indefinitely, incrementing the shared count variable + * on each iteration. To ensure it has exclusive access to the variable it + * raises its priority above that of the controller task before each + * increment, lowering it again to its original priority before starting the + * next iteration. + * + * The other counter task increments the shared count variable on each + * iteration of its loop until the count has reached a limit of 0xff - at + * which point it suspends itself. It will not start a new loop until the + * controller task has made it "ready" again by calling vTaskResume(). + * This second counter task operates at a higher priority than controller + * task so does not need to worry about mutual exclusion of the counter + * variable. + * + * The controller task is in two sections. The first section controls and + * monitors the continuous count task. When this section is operational the + * limited count task is suspended. Likewise, the second section controls + * and monitors the limited count task. When this section is operational the + * continuous count task is suspended. + * + * In the first section the controller task first takes a copy of the shared + * count variable. To ensure mutual exclusion on the count variable it + * suspends the continuous count task, resuming it again when the copy has been + * taken. The controller task then sleeps for a fixed period - during which + * the continuous count task will execute and increment the shared variable. + * When the controller task wakes it checks that the continuous count task + * has executed by comparing the copy of the shared variable with its current + * value. This time, to ensure mutual exclusion, the scheduler itself is + * suspended with a call to vTaskSuspendAll (). This is for demonstration + * purposes only and is not a recommended technique due to its inefficiency. + * + * After a fixed number of iterations the controller task suspends the + * continuous count task, and moves on to its second section. + * + * At the start of the second section the shared variable is cleared to zero. + * The limited count task is then woken from its suspension by a call to + * vTaskResume (). As this counter task operates at a higher priority than + * the controller task the controller task should not run again until the + * shared variable has been counted up to the limited value causing the counter + * task to suspend itself. The next line after vTaskResume () is therefore + * a check on the shared variable to ensure everything is as expected. + * + * + * The second test consists of a couple of very simple tasks that post onto a + * queue while the scheduler is suspended. This test was added to test parts + * of the scheduler not exercised by the first test. + * + */ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "dynamic.h" + +/* Function that implements the "limited count" task as described above. */ +static portTASK_FUNCTION_PROTO( vLimitedIncrementTask, pvParameters ); + +/* Function that implements the "continuous count" task as described above. */ +static portTASK_FUNCTION_PROTO( vContinuousIncrementTask, pvParameters ); + +/* Function that implements the controller task as described above. */ +static portTASK_FUNCTION_PROTO( vCounterControlTask, pvParameters ); + +static portTASK_FUNCTION_PROTO( vQueueReceiveWhenSuspendedTask, pvParameters ); +static portTASK_FUNCTION_PROTO( vQueueSendWhenSuspendedTask, pvParameters ); + +/* Demo task specific constants. */ +#define priSTACK_SIZE ( configMINIMAL_STACK_SIZE ) +#define priSLEEP_TIME ( ( TickType_t ) 128 / portTICK_PERIOD_MS ) +#define priLOOPS ( 5 ) +#define priMAX_COUNT ( ( uint32_t ) 0xff ) +#define priNO_BLOCK ( ( TickType_t ) 0 ) +#define priSUSPENDED_QUEUE_LENGTH ( 1 ) + +/*-----------------------------------------------------------*/ + +/* Handles to the two counter tasks. These could be passed in as parameters +to the controller task to prevent them having to be file scope. */ +static TaskHandle_t xContinuousIncrementHandle, xLimitedIncrementHandle; + +/* The shared counter variable. This is passed in as a parameter to the two +counter variables for demonstration purposes. */ +static volatile uint32_t ulCounter; + +/* Variables used to check that the tasks are still operating without error. +Each complete iteration of the controller task increments this variable +provided no errors have been found. The variable maintaining the same value +is therefore indication of an error. */ +static volatile uint16_t usCheckVariable = ( uint16_t ) 0; +static volatile BaseType_t xSuspendedQueueSendError = pdFALSE; +static volatile BaseType_t xSuspendedQueueReceiveError = pdFALSE; + +/* Queue used by the second test. */ +QueueHandle_t xSuspendedTestQueue; + +/* The value the queue receive task expects to receive next. This is file +scope so xAreDynamicPriorityTasksStillRunning() can ensure it is still +incrementing. */ +static uint32_t ulExpectedValue = ( uint32_t ) 0; + +/*-----------------------------------------------------------*/ +/* + * Start the three tasks as described at the top of the file. + * Note that the limited count task is given a higher priority. + */ +void vStartDynamicPriorityTasks( void ) +{ + xSuspendedTestQueue = xQueueCreate( priSUSPENDED_QUEUE_LENGTH, sizeof( uint32_t ) ); + + /* vQueueAddToRegistry() adds the queue to the queue registry, if one is + in use. The queue registry is provided as a means for kernel aware + debuggers to locate queues and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( xSuspendedTestQueue, "Suspended_Test_Queue" ); + + xTaskCreate( vContinuousIncrementTask, "CNT_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY, &xContinuousIncrementHandle ); + xTaskCreate( vLimitedIncrementTask, "LIM_INC", priSTACK_SIZE, ( void * ) &ulCounter, tskIDLE_PRIORITY + 1, &xLimitedIncrementHandle ); + xTaskCreate( vCounterControlTask, "C_CTRL", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vQueueSendWhenSuspendedTask, "SUSP_TX", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); + xTaskCreate( vQueueReceiveWhenSuspendedTask, "SUSP_RX", priSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); +} +/*-----------------------------------------------------------*/ + +/* + * Just loops around incrementing the shared variable until the limit has been + * reached. Once the limit has been reached it suspends itself. + */ +static portTASK_FUNCTION( vLimitedIncrementTask, pvParameters ) +{ +uint32_t *pulCounter; + + /* Take a pointer to the shared variable from the parameters passed into + the task. */ + pulCounter = ( uint32_t * ) pvParameters; + + /* This will run before the control task, so the first thing it does is + suspend - the control task will resume it when ready. */ + vTaskSuspend( NULL ); + + for( ;; ) + { + /* Just count up to a value then suspend. */ + ( *pulCounter )++; + + if( *pulCounter >= priMAX_COUNT ) + { + vTaskSuspend( NULL ); + } + } +} +/*-----------------------------------------------------------*/ + +/* + * Just keep counting the shared variable up. The control task will suspend + * this task when it wants. + */ +static portTASK_FUNCTION( vContinuousIncrementTask, pvParameters ) +{ +volatile uint32_t *pulCounter; +UBaseType_t uxOurPriority; + + /* Take a pointer to the shared variable from the parameters passed into + the task. */ + pulCounter = ( uint32_t * ) pvParameters; + + /* Query our priority so we can raise it when exclusive access to the + shared variable is required. */ + uxOurPriority = uxTaskPriorityGet( NULL ); + + for( ;; ) + { + /* Raise the priority above the controller task to ensure a context + switch does not occur while the variable is being accessed. */ + vTaskPrioritySet( NULL, uxOurPriority + 1 ); + { + configASSERT( ( uxTaskPriorityGet( NULL ) == ( uxOurPriority + 1 ) ) ); + ( *pulCounter )++; + } + vTaskPrioritySet( NULL, uxOurPriority ); + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + configASSERT( ( uxTaskPriorityGet( NULL ) == uxOurPriority ) ); + } +} +/*-----------------------------------------------------------*/ + +/* + * Controller task as described above. + */ +static portTASK_FUNCTION( vCounterControlTask, pvParameters ) +{ +uint32_t ulLastCounter; +short sLoops; +short sError = pdFALSE; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Start with the counter at zero. */ + ulCounter = ( uint32_t ) 0; + + /* First section : */ + + /* Check the continuous count task is running. */ + for( sLoops = 0; sLoops < priLOOPS; sLoops++ ) + { + /* Suspend the continuous count task so we can take a mirror of the + shared variable without risk of corruption. This is not really + needed as the other task raises its priority above this task's + priority. */ + vTaskSuspend( xContinuousIncrementHandle ); + { + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + ulLastCounter = ulCounter; + } + vTaskResume( xContinuousIncrementHandle ); + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xContinuousIncrementHandle ) == eReady ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Now delay to ensure the other task has processor time. */ + vTaskDelay( priSLEEP_TIME ); + + /* Check the shared variable again. This time to ensure mutual + exclusion the whole scheduler will be locked. This is just for + demo purposes! */ + vTaskSuspendAll(); + { + if( ulLastCounter == ulCounter ) + { + /* The shared variable has not changed. There is a problem + with the continuous count task so flag an error. */ + sError = pdTRUE; + } + } + xTaskResumeAll(); + } + + /* Second section: */ + + /* Suspend the continuous counter task so it stops accessing the shared + variable. */ + vTaskSuspend( xContinuousIncrementHandle ); + + /* Reset the variable. */ + ulCounter = ( uint32_t ) 0; + + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Resume the limited count task which has a higher priority than us. + We should therefore not return from this call until the limited count + task has suspended itself with a known value in the counter variable. */ + vTaskResume( xLimitedIncrementHandle ); + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + + /* This task should not run again until xLimitedIncrementHandle has + suspended itself. */ + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xLimitedIncrementHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Does the counter variable have the expected value? */ + if( ulCounter != priMAX_COUNT ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If no errors have occurred then increment the check variable. */ + portENTER_CRITICAL(); + usCheckVariable++; + portEXIT_CRITICAL(); + } + + /* Resume the continuous count task and do it all again. */ + vTaskResume( xContinuousIncrementHandle ); + + #if( configUSE_PREEMPTION == 0 ) + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vQueueSendWhenSuspendedTask, pvParameters ) +{ +static uint32_t ulValueToSend = ( uint32_t ) 0; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + for( ;; ) + { + vTaskSuspendAll(); + { + /* We must not block while the scheduler is suspended! */ + if( xQueueSend( xSuspendedTestQueue, ( void * ) &ulValueToSend, priNO_BLOCK ) != pdTRUE ) + { + xSuspendedQueueSendError = pdTRUE; + } + } + xTaskResumeAll(); + + vTaskDelay( priSLEEP_TIME ); + + ++ulValueToSend; + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vQueueReceiveWhenSuspendedTask, pvParameters ) +{ +uint32_t ulReceivedValue; +BaseType_t xGotValue; + + /* Just to stop warning messages. */ + ( void ) pvParameters; + + for( ;; ) + { + do + { + /* Suspending the scheduler here is fairly pointless and + undesirable for a normal application. It is done here purely + to test the scheduler. The inner xTaskResumeAll() should + never return pdTRUE as the scheduler is still locked by the + outer call. */ + vTaskSuspendAll(); + { + vTaskSuspendAll(); + { + xGotValue = xQueueReceive( xSuspendedTestQueue, ( void * ) &ulReceivedValue, priNO_BLOCK ); + } + if( xTaskResumeAll() != pdFALSE ) + { + xSuspendedQueueReceiveError = pdTRUE; + } + } + xTaskResumeAll(); + + #if configUSE_PREEMPTION == 0 + { + taskYIELD(); + } + #endif + + } while( xGotValue == pdFALSE ); + + if( ulReceivedValue != ulExpectedValue ) + { + xSuspendedQueueReceiveError = pdTRUE; + } + + if( xSuspendedQueueReceiveError != pdTRUE ) + { + /* Only increment the variable if an error has not occurred. This + allows xAreDynamicPriorityTasksStillRunning() to check for stalled + tasks as well as explicit errors. */ + ++ulExpectedValue; + } + } +} +/*-----------------------------------------------------------*/ + +/* Called to check that all the created tasks are still running without error. */ +BaseType_t xAreDynamicPriorityTasksStillRunning( void ) +{ +/* Keep a history of the check variables so we know if it has been incremented +since the last call. */ +static uint16_t usLastTaskCheck = ( uint16_t ) 0; +static uint32_t ulLastExpectedValue = ( uint32_t ) 0U; +BaseType_t xReturn = pdTRUE; + + /* Check the tasks are still running by ensuring the check variable + is still incrementing. */ + + if( usCheckVariable == usLastTaskCheck ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + if( ulExpectedValue == ulLastExpectedValue ) + { + /* The value being received by the queue receive task has not + incremented so an error exists. */ + xReturn = pdFALSE; + } + + if( xSuspendedQueueSendError == pdTRUE ) + { + xReturn = pdFALSE; + } + + if( xSuspendedQueueReceiveError == pdTRUE ) + { + xReturn = pdFALSE; + } + + usLastTaskCheck = usCheckVariable; + ulLastExpectedValue = ulExpectedValue; + + return xReturn; +} diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash.c new file mode 100644 index 0000000..8689707 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash.c @@ -0,0 +1,157 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * This version of flash .c is for use on systems that have limited stack space + * and no display facilities. The complete version can be found in the + * Demo/Common/Full directory. + * + * Three tasks are created, each of which flash an LED at a different rate. The first + * LED flashes every 200ms, the second every 400ms, the third every 600ms. + * + * The LED flash tasks provide instant visual feedback. They show that the scheduler + * is still operational. + * + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "partest.h" +#include "flash.h" + +#define ledSTACK_SIZE configMINIMAL_STACK_SIZE +#define ledNUMBER_OF_LEDS ( 3 ) +#define ledFLASH_RATE_BASE ( ( TickType_t ) 333 ) + +/* Variable used by the created tasks to calculate the LED number to use, and +the rate at which they should flash the LED. */ +static volatile UBaseType_t uxFlashTaskNumber = 0; + +/* The task that is created three times. */ +static portTASK_FUNCTION_PROTO( vLEDFlashTask, pvParameters ); + +/*-----------------------------------------------------------*/ + +void vStartLEDFlashTasks( UBaseType_t uxPriority ) +{ +BaseType_t xLEDTask; + + /* Create the three tasks. */ + for( xLEDTask = 0; xLEDTask < ledNUMBER_OF_LEDS; ++xLEDTask ) + { + /* Spawn the task. */ + xTaskCreate( vLEDFlashTask, "LEDx", ledSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) NULL ); + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vLEDFlashTask, pvParameters ) +{ +TickType_t xFlashRate, xLastFlashTime; +UBaseType_t uxLED; + + /* The parameters are not used. */ + ( void ) pvParameters; + + /* Calculate the LED and flash rate. */ + portENTER_CRITICAL(); + { + /* See which of the eight LED's we should use. */ + uxLED = uxFlashTaskNumber; + + /* Update so the next task uses the next LED. */ + uxFlashTaskNumber++; + } + portEXIT_CRITICAL(); + + xFlashRate = ledFLASH_RATE_BASE + ( ledFLASH_RATE_BASE * ( TickType_t ) uxLED ); + xFlashRate /= portTICK_PERIOD_MS; + + /* We will turn the LED on and off again in the delay period, so each + delay is only half the total period. */ + xFlashRate /= ( TickType_t ) 2; + + /* We need to initialise xLastFlashTime prior to the first call to + vTaskDelayUntil(). */ + xLastFlashTime = xTaskGetTickCount(); + + for(;;) + { + /* Delay for half the flash period then turn the LED on. */ + vTaskDelayUntil( &xLastFlashTime, xFlashRate ); + vParTestToggleLED( uxLED ); + + /* Delay for half the flash period then turn the LED off. */ + vTaskDelayUntil( &xLastFlashTime, xFlashRate ); + vParTestToggleLED( uxLED ); + } +} /*lint !e715 !e818 !e830 Function definition must be standard for task creation. */ + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash_timer.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash_timer.c new file mode 100644 index 0000000..b54a866 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flash_timer.c @@ -0,0 +1,136 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/** + * Repeatedly toggles one or more LEDs using software timers - one timer per + * LED. + */ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "timers.h" + +/* Demo program include files. */ +#include "partest.h" +#include "flash_timer.h" + +/* The toggle rates are all a multple of ledFLASH_RATE_BASE. */ +#define ledFLASH_RATE_BASE ( ( ( TickType_t ) 333 ) / portTICK_PERIOD_MS ) + +/* A block time of zero simple means "don't block". */ +#define ledDONT_BLOCK ( ( TickType_t ) 0 ) + +/*-----------------------------------------------------------*/ + +/* + * The callback function used by each LED flashing timer. All the timers use + * this function, and the timer ID is used within the function to determine + * which timer has actually expired. + */ +static void prvLEDTimerCallback( TimerHandle_t xTimer ); + +/*-----------------------------------------------------------*/ + +void vStartLEDFlashTimers( UBaseType_t uxNumberOfLEDs ) +{ +UBaseType_t uxLEDTimer; +TimerHandle_t xTimer; + + /* Create and start the requested number of timers. */ + for( uxLEDTimer = 0; uxLEDTimer < uxNumberOfLEDs; ++uxLEDTimer ) + { + /* Create the timer. */ + xTimer = xTimerCreate( "Flasher", /* A text name, purely to help debugging. */ + ledFLASH_RATE_BASE * ( uxLEDTimer + 1 ),/* The timer period, which is a multiple of ledFLASH_RATE_BASE. */ + pdTRUE, /* This is an auto-reload timer, so xAutoReload is set to pdTRUE. */ + ( void * ) uxLEDTimer, /* The ID is used to identify the timer within the timer callback function, as each timer uses the same callback. */ + prvLEDTimerCallback /* Each timer uses the same callback. */ + ); + + /* If the timer was created successfully, attempt to start it. If the + scheduler has not yet been started then the timer command queue must + be long enough to hold each command sent to it until such time that the + scheduler is started. The timer command queue length is set by + configTIMER_QUEUE_LENGTH in FreeRTOSConfig.h. */ + if( xTimer != NULL ) + { + xTimerStart( xTimer, ledDONT_BLOCK ); + } + } +} +/*-----------------------------------------------------------*/ + +static void prvLEDTimerCallback( TimerHandle_t xTimer ) +{ +BaseType_t xTimerID; + + /* The timer ID is used to identify the timer that has actually expired as + each timer uses the same callback. The ID is then also used as the number + of the LED that is to be toggled. */ + xTimerID = ( BaseType_t ) pvTimerGetTimerID( xTimer ); + vParTestToggleLED( xTimerID ); +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flop.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flop.c new file mode 100644 index 0000000..438cf02 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/flop.c @@ -0,0 +1,383 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Creates eight tasks, each of which loops continuously performing a floating + * point calculation. + * + * All the tasks run at the idle priority and never block or yield. This causes + * all eight tasks to time slice with the idle task. Running at the idle + * priority means that these tasks will get pre-empted any time another task is + * ready to run or a time slice occurs. More often than not the pre-emption + * will occur mid calculation, creating a good test of the schedulers context + * switch mechanism - a calculation producing an unexpected result could be a + * symptom of a corruption in the context of a task. + */ + +#include +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "flop.h" + +#define mathSTACK_SIZE configMINIMAL_STACK_SIZE +#define mathNUMBER_OF_TASKS ( 4 ) + +/* Four tasks, each of which performs a different floating point calculation. +Each of the four is created twice. */ +static portTASK_FUNCTION_PROTO( vCompetingMathTask1, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask2, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask3, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask4, pvParameters ); + +/* These variables are used to check that all the tasks are still running. If a +task gets a calculation wrong it will stop setting its check variable. */ +static volatile uint16_t usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartMathTasks( UBaseType_t uxPriority ) +{ + xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask1, pvParameters ) +{ +volatile portDOUBLE d1, d2, d3, d4; +volatile uint16_t *pusTaskCheckVariable; +volatile portDOUBLE dAnswer; +short sError = pdFALSE; + + /* Some ports require that tasks that use a hardware floating point unit + tell the kernel that they require a floating point context before any + floating point instructions are executed. */ + portTASK_USES_FLOATING_POINT(); + + d1 = 123.4567; + d2 = 2345.6789; + d3 = -918.222; + + dAnswer = ( d1 + d2 ) * d3; + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for(;;) + { + d1 = 123.4567; + d2 = 2345.6789; + d3 = -918.222; + + d4 = ( d1 + d2 ) * d3; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( d4 - dAnswer ) > 0.001 ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct then set set the check + variable. The check variable will get set to pdFALSE each time + xAreMathsTaskStillRunning() is executed. */ + ( *pusTaskCheckVariable ) = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask2, pvParameters ) +{ +volatile portDOUBLE d1, d2, d3, d4; +volatile uint16_t *pusTaskCheckVariable; +volatile portDOUBLE dAnswer; +short sError = pdFALSE; + + /* Some ports require that tasks that use a hardware floating point unit + tell the kernel that they require a floating point context before any + floating point instructions are executed. */ + portTASK_USES_FLOATING_POINT(); + + d1 = -389.38; + d2 = 32498.2; + d3 = -2.0001; + + dAnswer = ( d1 / d2 ) * d3; + + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for( ;; ) + { + d1 = -389.38; + d2 = 32498.2; + d3 = -2.0001; + + d4 = ( d1 / d2 ) * d3; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( d4 - dAnswer ) > 0.001 ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct then set set the check + variable. The check variable will get set to pdFALSE each time + xAreMathsTaskStillRunning() is executed. */ + ( *pusTaskCheckVariable ) = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask3, pvParameters ) +{ +volatile portDOUBLE *pdArray, dTotal1, dTotal2, dDifference; +volatile uint16_t *pusTaskCheckVariable; +const size_t xArraySize = 10; +size_t xPosition; +short sError = pdFALSE; + + /* Some ports require that tasks that use a hardware floating point unit + tell the kernel that they require a floating point context before any + floating point instructions are executed. */ + portTASK_USES_FLOATING_POINT(); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + pdArray = ( portDOUBLE * ) pvPortMalloc( xArraySize * sizeof( portDOUBLE ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + dTotal1 = 0.0; + dTotal2 = 0.0; + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + pdArray[ xPosition ] = ( portDOUBLE ) xPosition + 5.5; + dTotal1 += ( portDOUBLE ) xPosition + 5.5; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + dTotal2 += pdArray[ xPosition ]; + } + + dDifference = dTotal1 - dTotal2; + if( fabs( dDifference ) > 0.001 ) + { + sError = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct then set set the check + variable. The check variable will get set to pdFALSE each time + xAreMathsTaskStillRunning() is executed. */ + ( *pusTaskCheckVariable ) = pdTRUE; + } + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask4, pvParameters ) +{ +volatile portDOUBLE *pdArray, dTotal1, dTotal2, dDifference; +volatile uint16_t *pusTaskCheckVariable; +const size_t xArraySize = 10; +size_t xPosition; +short sError = pdFALSE; + + /* Some ports require that tasks that use a hardware floating point unit + tell the kernel that they require a floating point context before any + floating point instructions are executed. */ + portTASK_USES_FLOATING_POINT(); + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + pdArray = ( portDOUBLE * ) pvPortMalloc( xArraySize * sizeof( portDOUBLE ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + dTotal1 = 0.0; + dTotal2 = 0.0; + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + pdArray[ xPosition ] = ( portDOUBLE ) xPosition * 12.123; + dTotal1 += ( portDOUBLE ) xPosition * 12.123; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + dTotal2 += pdArray[ xPosition ]; + } + + dDifference = dTotal1 - dTotal2; + if( fabs( dDifference ) > 0.001 ) + { + sError = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct then set set the check + variable. The check variable will get set to pdFALSE each time + xAreMathsTaskStillRunning() is executed. */ + ( *pusTaskCheckVariable ) = pdTRUE; + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreMathsTaskStillRunning( void ) +{ +BaseType_t xReturn = pdPASS, xTask; + + /* Check the maths tasks are still running by ensuring their check variables + have been set to pdPASS. */ + for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ ) + { + if( usTaskCheck[ xTask ] != pdTRUE ) + { + /* The check has not been set so the associated task has either + stalled or detected an error. */ + xReturn = pdFAIL; + } + else + { + /* Reset the variable so it can be checked again the next time this + function is executed. */ + usTaskCheck[ xTask ] = pdFALSE; + } + } + + return xReturn; +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/integer.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/integer.c new file mode 100644 index 0000000..a276613 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/integer.c @@ -0,0 +1,201 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Creates one or more tasks that repeatedly perform a set of integer + * calculations. The result of each run-time calculation is compared to the + * known expected result - with a mismatch being indicative of an error in the + * context switch mechanism. + */ + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "integer.h" + +/* The constants used in the calculation. */ +#define intgCONST1 ( ( long ) 123 ) +#define intgCONST2 ( ( long ) 234567 ) +#define intgCONST3 ( ( long ) -3 ) +#define intgCONST4 ( ( long ) 7 ) +#define intgEXPECTED_ANSWER ( ( ( intgCONST1 + intgCONST2 ) * intgCONST3 ) / intgCONST4 ) + +#define intgSTACK_SIZE configMINIMAL_STACK_SIZE + +/* As this is the minimal version, we will only create one task. */ +#define intgNUMBER_OF_TASKS ( 1 ) + +/* The task function. Repeatedly performs a 32 bit calculation, checking the +result against the expected result. If the result is incorrect then the +context switch must have caused some corruption. */ +static portTASK_FUNCTION_PROTO( vCompeteingIntMathTask, pvParameters ); + +/* Variables that are set to true within the calculation task to indicate +that the task is still executing. The check task sets the variable back to +false, flagging an error if the variable is still false the next time it +is called. */ +static volatile BaseType_t xTaskCheck[ intgNUMBER_OF_TASKS ] = { ( BaseType_t ) pdFALSE }; + +/*-----------------------------------------------------------*/ + +void vStartIntegerMathTasks( UBaseType_t uxPriority ) +{ +short sTask; + + for( sTask = 0; sTask < intgNUMBER_OF_TASKS; sTask++ ) + { + xTaskCreate( vCompeteingIntMathTask, "IntMath", intgSTACK_SIZE, ( void * ) &( xTaskCheck[ sTask ] ), uxPriority, ( TaskHandle_t * ) NULL ); + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompeteingIntMathTask, pvParameters ) +{ +/* These variables are all effectively set to constants so they are volatile to +ensure the compiler does not just get rid of them. */ +volatile long lValue; +short sError = pdFALSE; +volatile BaseType_t *pxTaskHasExecuted; + + /* Set a pointer to the variable we are going to set to true each + iteration. This is also a good test of the parameter passing mechanism + within each port. */ + pxTaskHasExecuted = ( volatile BaseType_t * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for( ;; ) + { + /* Perform the calculation. This will store partial value in + registers, resulting in a good test of the context switch mechanism. */ + lValue = intgCONST1; + lValue += intgCONST2; + + /* Yield in case cooperative scheduling is being used. */ + #if configUSE_PREEMPTION == 0 + { + taskYIELD(); + } + #endif + + /* Finish off the calculation. */ + lValue *= intgCONST3; + lValue /= intgCONST4; + + /* If the calculation is found to be incorrect we stop setting the + TaskHasExecuted variable so the check task can see an error has + occurred. */ + if( lValue != intgEXPECTED_ANSWER ) /*lint !e774 volatile used to prevent this being optimised out. */ + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* We have not encountered any errors, so set the flag that show + we are still executing. This will be periodically cleared by + the check task. */ + portENTER_CRITICAL(); + *pxTaskHasExecuted = pdTRUE; + portEXIT_CRITICAL(); + } + + /* Yield in case cooperative scheduling is being used. */ + #if configUSE_PREEMPTION == 0 + { + taskYIELD(); + } + #endif + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreIntegerMathsTaskStillRunning( void ) +{ +BaseType_t xReturn = pdTRUE; +short sTask; + + /* Check the maths tasks are still running by ensuring their check variables + are still being set to true. */ + for( sTask = 0; sTask < intgNUMBER_OF_TASKS; sTask++ ) + { + if( xTaskCheck[ sTask ] == pdFALSE ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + /* Reset the check variable so we can tell if it has been set by + the next time around. */ + xTaskCheck[ sTask ] = pdFALSE; + } + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/recmutex.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/recmutex.c new file mode 100644 index 0000000..69a6f9d --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/recmutex.c @@ -0,0 +1,440 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + The tasks defined on this page demonstrate the use of recursive mutexes. + + For recursive mutex functionality the created mutex should be created using + xSemaphoreCreateRecursiveMutex(), then be manipulated + using the xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() API + functions. + + This demo creates three tasks all of which access the same recursive mutex: + + prvRecursiveMutexControllingTask() has the highest priority so executes + first and grabs the mutex. It then performs some recursive accesses - + between each of which it sleeps for a short period to let the lower + priority tasks execute. When it has completed its demo functionality + it gives the mutex back before suspending itself. + + prvRecursiveMutexBlockingTask() attempts to access the mutex by performing + a blocking 'take'. The blocking task has a lower priority than the + controlling task so by the time it executes the mutex has already been + taken by the controlling task, causing the blocking task to block. It + does not unblock until the controlling task has given the mutex back, + and it does not actually run until the controlling task has suspended + itself (due to the relative priorities). When it eventually does obtain + the mutex all it does is give the mutex back prior to also suspending + itself. At this point both the controlling task and the blocking task are + suspended. + + prvRecursiveMutexPollingTask() runs at the idle priority. It spins round + a tight loop attempting to obtain the mutex with a non-blocking call. As + the lowest priority task it will not successfully obtain the mutex until + both the controlling and blocking tasks are suspended. Once it eventually + does obtain the mutex it first unsuspends both the controlling task and + blocking task prior to giving the mutex back - resulting in the polling + task temporarily inheriting the controlling tasks priority. +*/ + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "recmutex.h" + +/* Priorities assigned to the three tasks. recmuCONTROLLING_TASK_PRIORITY can +be overridden by a definition in FreeRTOSConfig.h. */ +#ifndef recmuCONTROLLING_TASK_PRIORITY + #define recmuCONTROLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#endif +#define recmuBLOCKING_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) +#define recmuPOLLING_TASK_PRIORITY ( tskIDLE_PRIORITY + 0 ) + +/* The recursive call depth. */ +#define recmuMAX_COUNT ( 10 ) + +/* Misc. */ +#define recmuSHORT_DELAY ( 20 / portTICK_PERIOD_MS ) +#define recmuNO_DELAY ( ( TickType_t ) 0 ) +#define recmuEIGHT_TICK_DELAY ( ( TickType_t ) 8 ) + +/* The three tasks as described at the top of this file. */ +static void prvRecursiveMutexControllingTask( void *pvParameters ); +static void prvRecursiveMutexBlockingTask( void *pvParameters ); +static void prvRecursiveMutexPollingTask( void *pvParameters ); + +/* The mutex used by the demo. */ +static SemaphoreHandle_t xMutex; + +/* Variables used to detect and latch errors. */ +static volatile BaseType_t xErrorOccurred = pdFALSE, xControllingIsSuspended = pdFALSE, xBlockingIsSuspended = pdFALSE; +static volatile UBaseType_t uxControllingCycles = 0, uxBlockingCycles = 0, uxPollingCycles = 0; + +/* Handles of the two higher priority tasks, required so they can be resumed +(unsuspended). */ +static TaskHandle_t xControllingTaskHandle, xBlockingTaskHandle; + +/*-----------------------------------------------------------*/ + +void vStartRecursiveMutexTasks( void ) +{ + /* Just creates the mutex and the three tasks. */ + + xMutex = xSemaphoreCreateRecursiveMutex(); + + /* vQueueAddToRegistry() adds the mutex to the registry, if one is + in use. The registry is provided as a means for kernel aware + debuggers to locate mutex and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Recursive_Mutex" ); + + + if( xMutex != NULL ) + { + xTaskCreate( prvRecursiveMutexControllingTask, "Rec1", configMINIMAL_STACK_SIZE, NULL, recmuCONTROLLING_TASK_PRIORITY, &xControllingTaskHandle ); + xTaskCreate( prvRecursiveMutexBlockingTask, "Rec2", configMINIMAL_STACK_SIZE, NULL, recmuBLOCKING_TASK_PRIORITY, &xBlockingTaskHandle ); + xTaskCreate( prvRecursiveMutexPollingTask, "Rec3", configMINIMAL_STACK_SIZE, NULL, recmuPOLLING_TASK_PRIORITY, NULL ); + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexControllingTask( void *pvParameters ) +{ +UBaseType_t ux; + + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Should not be able to 'give' the mutex, as we have not yet 'taken' + it. The first time through, the mutex will not have been used yet, + subsequent times through, at this point the mutex will be held by the + polling task. */ + if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + for( ux = 0; ux < recmuMAX_COUNT; ux++ ) + { + /* We should now be able to take the mutex as many times as + we like. + + The first time through the mutex will be immediately available, on + subsequent times through the mutex will be held by the polling task + at this point and this Take will cause the polling task to inherit + the priority of this task. In this case the block time must be + long enough to ensure the polling task will execute again before the + block time expires. If the block time does expire then the error + flag will be set here. */ + if( xSemaphoreTakeRecursive( xMutex, recmuEIGHT_TICK_DELAY ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Ensure the other task attempting to access the mutex (and the + other demo tasks) are able to execute to ensure they either block + (where a block time is specified) or return an error (where no + block time is specified) as the mutex is held by this task. */ + vTaskDelay( recmuSHORT_DELAY ); + } + + /* For each time we took the mutex, give it back. */ + for( ux = 0; ux < recmuMAX_COUNT; ux++ ) + { + /* Ensure the other task attempting to access the mutex (and the + other demo tasks) are able to execute. */ + vTaskDelay( recmuSHORT_DELAY ); + + /* We should now be able to give the mutex as many times as we + took it. When the mutex is available again the Blocking task + should be unblocked but not run because it has a lower priority + than this task. The polling task should also not run at this point + as it too has a lower priority than this task. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + } + + /* Having given it back the same number of times as it was taken, we + should no longer be the mutex owner, so the next give should fail. */ + if( xSemaphoreGiveRecursive( xMutex ) == pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + /* Keep count of the number of cycles this task has performed so a + stall can be detected. */ + uxControllingCycles++; + + /* Suspend ourselves so the blocking task can execute. */ + xControllingIsSuspended = pdTRUE; + vTaskSuspend( NULL ); + xControllingIsSuspended = pdFALSE; + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexBlockingTask( void *pvParameters ) +{ + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ;; ) + { + /* This task will run while the controlling task is blocked, and the + controlling task will block only once it has the mutex - therefore + this call should block until the controlling task has given up the + mutex, and not actually execute past this call until the controlling + task is suspended. portMAX_DELAY - 1 is used instead of portMAX_DELAY + to ensure the task's state is reported as Blocked and not Suspended in + a later call to configASSERT() (within the polling task). */ + if( xSemaphoreTakeRecursive( xMutex, ( portMAX_DELAY - 1 ) ) == pdPASS ) + { + if( xControllingIsSuspended != pdTRUE ) + { + /* Did not expect to execute until the controlling task was + suspended. */ + xErrorOccurred = pdTRUE; + } + else + { + /* Give the mutex back before suspending ourselves to allow + the polling task to obtain the mutex. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + xBlockingIsSuspended = pdTRUE; + vTaskSuspend( NULL ); + xBlockingIsSuspended = pdFALSE; + } + } + else + { + /* We should not leave the xSemaphoreTakeRecursive() function + until the mutex was obtained. */ + xErrorOccurred = pdTRUE; + } + + /* The controlling and blocking tasks should be in lock step. */ + if( uxControllingCycles != ( uxBlockingCycles + 1 ) ) + { + xErrorOccurred = pdTRUE; + } + + /* Keep count of the number of cycles this task has performed so a + stall can be detected. */ + uxBlockingCycles++; + } +} +/*-----------------------------------------------------------*/ + +static void prvRecursiveMutexPollingTask( void *pvParameters ) +{ + /* Just to remove compiler warning. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Keep attempting to obtain the mutex. We should only obtain it when + the blocking task has suspended itself, which in turn should only + happen when the controlling task is also suspended. */ + if( xSemaphoreTakeRecursive( xMutex, recmuNO_DELAY ) == pdPASS ) + { + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xControllingTaskHandle ) == eSuspended ); + configASSERT( eTaskGetState( xBlockingTaskHandle ) == eSuspended ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Is the blocking task suspended? */ + if( ( xBlockingIsSuspended != pdTRUE ) || ( xControllingIsSuspended != pdTRUE ) ) + { + xErrorOccurred = pdTRUE; + } + else + { + /* Keep count of the number of cycles this task has performed + so a stall can be detected. */ + uxPollingCycles++; + + /* We can resume the other tasks here even though they have a + higher priority than the polling task. When they execute they + will attempt to obtain the mutex but fail because the polling + task is still the mutex holder. The polling task (this task) + will then inherit the higher priority. The Blocking task will + block indefinitely when it attempts to obtain the mutex, the + Controlling task will only block for a fixed period and an + error will be latched if the polling task has not returned the + mutex by the time this fixed period has expired. */ + vTaskResume( xBlockingTaskHandle ); + vTaskResume( xControllingTaskHandle ); + + /* The other two tasks should now have executed and no longer + be suspended. */ + if( ( xBlockingIsSuspended == pdTRUE ) || ( xControllingIsSuspended == pdTRUE ) ) + { + xErrorOccurred = pdTRUE; + } + + #if( INCLUDE_uxTaskPriorityGet == 1 ) + { + /* Check priority inherited. */ + configASSERT( uxTaskPriorityGet( NULL ) == recmuCONTROLLING_TASK_PRIORITY ); + } + #endif /* INCLUDE_uxTaskPriorityGet */ + + #if( INCLUDE_eTaskGetState == 1 ) + { + configASSERT( eTaskGetState( xControllingTaskHandle ) == eBlocked ); + configASSERT( eTaskGetState( xBlockingTaskHandle ) == eBlocked ); + } + #endif /* INCLUDE_eTaskGetState */ + + /* Release the mutex, disinheriting the higher priority again. */ + if( xSemaphoreGiveRecursive( xMutex ) != pdPASS ) + { + xErrorOccurred = pdTRUE; + } + + #if( INCLUDE_uxTaskPriorityGet == 1 ) + { + /* Check priority disinherited. */ + configASSERT( uxTaskPriorityGet( NULL ) == recmuPOLLING_TASK_PRIORITY ); + } + #endif /* INCLUDE_uxTaskPriorityGet */ + } + } + + #if configUSE_PREEMPTION == 0 + { + taskYIELD(); + } + #endif + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreRecursiveMutexTasksStillRunning( void ) +{ +BaseType_t xReturn; +static UBaseType_t uxLastControllingCycles = 0, uxLastBlockingCycles = 0, uxLastPollingCycles = 0; + + /* Is the controlling task still cycling? */ + if( uxLastControllingCycles == uxControllingCycles ) + { + xErrorOccurred = pdTRUE; + } + else + { + uxLastControllingCycles = uxControllingCycles; + } + + /* Is the blocking task still cycling? */ + if( uxLastBlockingCycles == uxBlockingCycles ) + { + xErrorOccurred = pdTRUE; + } + else + { + uxLastBlockingCycles = uxBlockingCycles; + } + + /* Is the polling task still cycling? */ + if( uxLastPollingCycles == uxPollingCycles ) + { + xErrorOccurred = pdTRUE; + } + else + { + uxLastPollingCycles = uxPollingCycles; + } + + if( xErrorOccurred == pdTRUE ) + { + xReturn = pdFAIL; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; +} + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/semtest.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/semtest.c new file mode 100644 index 0000000..5e43e4a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/semtest.c @@ -0,0 +1,298 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Creates two sets of two tasks. The tasks within a set share a variable, access + * to which is guarded by a semaphore. + * + * Each task starts by attempting to obtain the semaphore. On obtaining a + * semaphore a task checks to ensure that the guarded variable has an expected + * value. It then clears the variable to zero before counting it back up to the + * expected value in increments of 1. After each increment the variable is checked + * to ensure it contains the value to which it was just set. When the starting + * value is again reached the task releases the semaphore giving the other task in + * the set a chance to do exactly the same thing. The starting value is high + * enough to ensure that a tick is likely to occur during the incrementing loop. + * + * An error is flagged if at any time during the process a shared variable is + * found to have a value other than that expected. Such an occurrence would + * suggest an error in the mutual exclusion mechanism by which access to the + * variable is restricted. + * + * The first set of two tasks poll their semaphore. The second set use blocking + * calls. + * + */ + + +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo app include files. */ +#include "semtest.h" + +/* The value to which the shared variables are counted. */ +#define semtstBLOCKING_EXPECTED_VALUE ( ( uint32_t ) 0xfff ) +#define semtstNON_BLOCKING_EXPECTED_VALUE ( ( uint32_t ) 0xff ) + +#define semtstSTACK_SIZE configMINIMAL_STACK_SIZE + +#define semtstNUM_TASKS ( 4 ) + +#define semtstDELAY_FACTOR ( ( TickType_t ) 10 ) + +/* The task function as described at the top of the file. */ +static portTASK_FUNCTION_PROTO( prvSemaphoreTest, pvParameters ); + +/* Structure used to pass parameters to each task. */ +typedef struct SEMAPHORE_PARAMETERS +{ + SemaphoreHandle_t xSemaphore; + volatile uint32_t *pulSharedVariable; + TickType_t xBlockTime; +} xSemaphoreParameters; + +/* Variables used to check that all the tasks are still running without errors. */ +static volatile short sCheckVariables[ semtstNUM_TASKS ] = { 0 }; +static volatile short sNextCheckVariable = 0; + +/*-----------------------------------------------------------*/ + +void vStartSemaphoreTasks( UBaseType_t uxPriority ) +{ +xSemaphoreParameters *pxFirstSemaphoreParameters, *pxSecondSemaphoreParameters; +const TickType_t xBlockTime = ( TickType_t ) 100; + + /* Create the structure used to pass parameters to the first two tasks. */ + pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) ); + + if( pxFirstSemaphoreParameters != NULL ) + { + /* Create the semaphore used by the first two tasks. */ + pxFirstSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary(); + xSemaphoreGive( pxFirstSemaphoreParameters->xSemaphore ); + + if( pxFirstSemaphoreParameters->xSemaphore != NULL ) + { + /* Create the variable which is to be shared by the first two tasks. */ + pxFirstSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) ); + + /* Initialise the share variable to the value the tasks expect. */ + *( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE; + + /* The first two tasks do not block on semaphore calls. */ + pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0; + + /* Spawn the first two tasks. As they poll they operate at the idle priority. */ + xTaskCreate( prvSemaphoreTest, "PolSEM1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL ); + xTaskCreate( prvSemaphoreTest, "PolSEM2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL ); + } + } + + /* Do exactly the same to create the second set of tasks, only this time + provide a block time for the semaphore calls. */ + pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) ); + if( pxSecondSemaphoreParameters != NULL ) + { + pxSecondSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary(); + xSemaphoreGive( pxSecondSemaphoreParameters->xSemaphore ); + + if( pxSecondSemaphoreParameters->xSemaphore != NULL ) + { + pxSecondSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) ); + *( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE; + pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS; + + xTaskCreate( prvSemaphoreTest, "BlkSEM1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL ); + xTaskCreate( prvSemaphoreTest, "BlkSEM2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL ); + } + } + + /* vQueueAddToRegistry() adds the semaphore to the registry, if one is + in use. The registry is provided as a means for kernel aware + debuggers to locate semaphores and has no purpose if a kernel aware debugger + is not being used. The call to vQueueAddToRegistry() will be removed + by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is + defined to be less than 1. */ + vQueueAddToRegistry( ( QueueHandle_t ) pxFirstSemaphoreParameters->xSemaphore, "Counting_Sem_1" ); + vQueueAddToRegistry( ( QueueHandle_t ) pxSecondSemaphoreParameters->xSemaphore, "Counting_Sem_2" ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( prvSemaphoreTest, pvParameters ) +{ +xSemaphoreParameters *pxParameters; +volatile uint32_t *pulSharedVariable, ulExpectedValue; +uint32_t ulCounter; +short sError = pdFALSE, sCheckVariableToUse; + + /* See which check variable to use. sNextCheckVariable is not semaphore + protected! */ + portENTER_CRITICAL(); + sCheckVariableToUse = sNextCheckVariable; + sNextCheckVariable++; + portEXIT_CRITICAL(); + + /* A structure is passed in as the parameter. This contains the shared + variable being guarded. */ + pxParameters = ( xSemaphoreParameters * ) pvParameters; + pulSharedVariable = pxParameters->pulSharedVariable; + + /* If we are blocking we use a much higher count to ensure loads of context + switches occur during the count. */ + if( pxParameters->xBlockTime > ( TickType_t ) 0 ) + { + ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE; + } + else + { + ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE; + } + + for( ;; ) + { + /* Try to obtain the semaphore. */ + if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS ) + { + /* We have the semaphore and so expect any other tasks using the + shared variable to have left it in the state we expect to find + it. */ + if( *pulSharedVariable != ulExpectedValue ) + { + sError = pdTRUE; + } + + /* Clear the variable, then count it back up to the expected value + before releasing the semaphore. Would expect a context switch or + two during this time. */ + for( ulCounter = ( uint32_t ) 0; ulCounter <= ulExpectedValue; ulCounter++ ) + { + *pulSharedVariable = ulCounter; + if( *pulSharedVariable != ulCounter ) + { + sError = pdTRUE; + } + } + + /* Release the semaphore, and if no errors have occurred increment the check + variable. */ + if( xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + if( sCheckVariableToUse < semtstNUM_TASKS ) + { + ( sCheckVariables[ sCheckVariableToUse ] )++; + } + } + + /* If we have a block time then we are running at a priority higher + than the idle priority. This task takes a long time to complete + a cycle (deliberately so to test the guarding) so will be starving + out lower priority tasks. Block for some time to allow give lower + priority tasks some processor time. */ + vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR ); + } + else + { + if( pxParameters->xBlockTime == ( TickType_t ) 0 ) + { + /* We have not got the semaphore yet, so no point using the + processor. We are not blocking when attempting to obtain the + semaphore. */ + taskYIELD(); + } + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreSemaphoreTasksStillRunning( void ) +{ +static short sLastCheckVariables[ semtstNUM_TASKS ] = { 0 }; +BaseType_t xTask, xReturn = pdTRUE; + + for( xTask = 0; xTask < semtstNUM_TASKS; xTask++ ) + { + if( sLastCheckVariables[ xTask ] == sCheckVariables[ xTask ] ) + { + xReturn = pdFALSE; + } + + sLastCheckVariables[ xTask ] = sCheckVariables[ xTask ]; + } + + return xReturn; +} + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/sp_flop.c b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/sp_flop.c new file mode 100644 index 0000000..44cdd6f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/Minimal/sp_flop.c @@ -0,0 +1,365 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * Creates eight tasks, each of which loops continuously performing a floating + * point calculation - using single precision variables. + * + * All the tasks run at the idle priority and never block or yield. This causes + * all eight tasks to time slice with the idle task. Running at the idle priority + * means that these tasks will get pre-empted any time another task is ready to run + * or a time slice occurs. More often than not the pre-emption will occur mid + * calculation, creating a good test of the schedulers context switch mechanism - a + * calculation producing an unexpected result could be a symptom of a corruption in + * the context of a task. + */ + +#include +#include + +/* Scheduler include files. */ +#include "FreeRTOS.h" +#include "task.h" + +/* Demo program include files. */ +#include "flop.h" + +#define mathSTACK_SIZE configMINIMAL_STACK_SIZE +#define mathNUMBER_OF_TASKS ( 8 ) + +/* Four tasks, each of which performs a different floating point calculation. +Each of the four is created twice. */ +static portTASK_FUNCTION_PROTO( vCompetingMathTask1, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask2, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask3, pvParameters ); +static portTASK_FUNCTION_PROTO( vCompetingMathTask4, pvParameters ); + +/* These variables are used to check that all the tasks are still running. If a +task gets a calculation wrong it will +stop incrementing its check variable. */ +static volatile uint16_t usTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 }; + +/*-----------------------------------------------------------*/ + +void vStartMathTasks( UBaseType_t uxPriority ) +{ + xTaskCreate( vCompetingMathTask1, "Math1", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 0 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask2, "Math2", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 1 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask3, "Math3", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 2 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask4, "Math4", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 3 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask1, "Math5", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 4 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask2, "Math6", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 5 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask3, "Math7", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 6 ] ), uxPriority, NULL ); + xTaskCreate( vCompetingMathTask4, "Math8", mathSTACK_SIZE, ( void * ) &( usTaskCheck[ 7 ] ), uxPriority, NULL ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask1, pvParameters ) +{ +volatile float f1, f2, f3, f4; +volatile uint16_t *pusTaskCheckVariable; +volatile float fAnswer; +short sError = pdFALSE; + + f1 = 123.4567F; + f2 = 2345.6789F; + f3 = -918.222F; + + fAnswer = ( f1 + f2 ) * f3; + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for(;;) + { + f1 = 123.4567F; + f2 = 2345.6789F; + f3 = -918.222F; + + f4 = ( f1 + f2 ) * f3; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( f4 - fAnswer ) > 0.001F ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask2, pvParameters ) +{ +volatile float f1, f2, f3, f4; +volatile uint16_t *pusTaskCheckVariable; +volatile float fAnswer; +short sError = pdFALSE; + + f1 = -389.38F; + f2 = 32498.2F; + f3 = -2.0001F; + + fAnswer = ( f1 / f2 ) * f3; + + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + /* Keep performing a calculation and checking the result against a constant. */ + for( ;; ) + { + f1 = -389.38F; + f2 = 32498.2F; + f3 = -2.0001F; + + f4 = ( f1 / f2 ) * f3; + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + /* If the calculation does not match the expected constant, stop the + increment of the check variable. */ + if( fabs( f4 - fAnswer ) > 0.001F ) + { + sError = pdTRUE; + } + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know + this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask3, pvParameters ) +{ +volatile float *pfArray, fTotal1, fTotal2, fDifference, fPosition; +volatile uint16_t *pusTaskCheckVariable; +const size_t xArraySize = 10; +size_t xPosition; +short sError = pdFALSE; + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + pfArray = ( float * ) pvPortMalloc( xArraySize * sizeof( float ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + fTotal1 = 0.0F; + fTotal2 = 0.0F; + fPosition = 0.0F; + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + pfArray[ xPosition ] = fPosition + 5.5F; + fTotal1 += fPosition + 5.5F; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + fTotal2 += pfArray[ xPosition ]; + } + + fDifference = fTotal1 - fTotal2; + if( fabs( fDifference ) > 0.001F ) + { + sError = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( vCompetingMathTask4, pvParameters ) +{ +volatile float *pfArray, fTotal1, fTotal2, fDifference, fPosition; +volatile uint16_t *pusTaskCheckVariable; +const size_t xArraySize = 10; +size_t xPosition; +short sError = pdFALSE; + + /* The variable this task increments to show it is still running is passed in + as the parameter. */ + pusTaskCheckVariable = ( uint16_t * ) pvParameters; + + pfArray = ( float * ) pvPortMalloc( xArraySize * sizeof( float ) ); + + /* Keep filling an array, keeping a running total of the values placed in the + array. Then run through the array adding up all the values. If the two totals + do not match, stop the check variable from incrementing. */ + for( ;; ) + { + fTotal1 = 0.0F; + fTotal2 = 0.0F; + fPosition = 0.0F; + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + pfArray[ xPosition ] = fPosition * 12.123F; + fTotal1 += fPosition * 12.123F; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + for( xPosition = 0; xPosition < xArraySize; xPosition++ ) + { + fTotal2 += pfArray[ xPosition ]; + } + + fDifference = fTotal1 - fTotal2; + if( fabs( fDifference ) > 0.001F ) + { + sError = pdTRUE; + } + + #if configUSE_PREEMPTION == 0 + taskYIELD(); + #endif + + if( sError == pdFALSE ) + { + /* If the calculation has always been correct, increment the check + variable so we know this task is still running okay. */ + ( *pusTaskCheckVariable )++; + } + } +} +/*-----------------------------------------------------------*/ + +/* This is called to check that all the created tasks are still running. */ +BaseType_t xAreMathsTaskStillRunning( void ) +{ +/* Keep a history of the check variables so we know if they have been incremented +since the last call. */ +static uint16_t usLastTaskCheck[ mathNUMBER_OF_TASKS ] = { ( uint16_t ) 0 }; +BaseType_t xReturn = pdTRUE, xTask; + + /* Check the maths tasks are still running by ensuring their check variables + are still incrementing. */ + for( xTask = 0; xTask < mathNUMBER_OF_TASKS; xTask++ ) + { + if( usTaskCheck[ xTask ] == usLastTaskCheck[ xTask ] ) + { + /* The check has not incremented so an error exists. */ + xReturn = pdFALSE; + } + + usLastTaskCheck[ xTask ] = usTaskCheck[ xTask ]; + } + + return xReturn; +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlckQ.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlckQ.h new file mode 100644 index 0000000..5e5eb3f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlckQ.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef ALT_BLOCK_Q_H +#define ALT_BLOCK_Q_H + +void vStartAltBlockingQueueTasks( UBaseType_t uxPriority ); +BaseType_t xAreAltBlockingQueuesStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlock.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlock.h new file mode 100644 index 0000000..7946734 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltBlock.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FAST_BLOCK_TIME_TEST_H +#define FAST_BLOCK_TIME_TEST_H + +void vCreateAltBlockTimeTasks( void ); +BaseType_t xAreAltBlockTimeTestTasksStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltPollQ.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltPollQ.h new file mode 100644 index 0000000..ce6af62 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltPollQ.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef ALT_POLLED_Q_H +#define ALT_POLLED_Q_H + +void vStartAltPolledQueueTasks( UBaseType_t uxPriority ); +BaseType_t xAreAltPollingQueuesStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltQTest.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltQTest.h new file mode 100644 index 0000000..cf50026 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/AltQTest.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FAST_GEN_Q_TEST_H +#define FAST_GEN_Q_TEST_H + +void vStartAltGenericQueueTasks( UBaseType_t uxPriority ); +BaseType_t xAreAltGenericQueueTasksStillRunning( void ); + +#endif /* GEN_Q_TEST_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/BlockQ.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/BlockQ.h new file mode 100644 index 0000000..7a6ea01 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/BlockQ.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef BLOCK_Q_H +#define BLOCK_Q_H + +void vStartBlockingQueueTasks( UBaseType_t uxPriority ); +BaseType_t xAreBlockingQueuesStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/EventGroupsDemo.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/EventGroupsDemo.h new file mode 100644 index 0000000..1b5c49c --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/EventGroupsDemo.h @@ -0,0 +1,82 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + + +/* + * This file contains fairly comprehensive checks on the behaviour of event + * groups. It is not intended to be a user friendly demonstration of the event + * groups API. + */ + +#ifndef EVENT_GROUPS_DEMO_H +#define EVENT_GROUPS_DEMO_H + +void vStartEventGroupTasks( void ); +BaseType_t xAreEventGroupTasksStillRunning( void ); +void vPeriodicEventGroupsProcessing( void ); + +#endif /* EVENT_GROUPS_DEMO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/GenQTest.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/GenQTest.h new file mode 100644 index 0000000..a832900 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/GenQTest.h @@ -0,0 +1,76 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef GEN_Q_TEST_H +#define GEN_Q_TEST_H + +void vStartGenericQueueTasks( UBaseType_t uxPriority ); +BaseType_t xAreGenericQueueTasksStillRunning( void ); +void vMutexISRInteractionTest( void ); + +#endif /* GEN_Q_TEST_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/IntQueue.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/IntQueue.h new file mode 100644 index 0000000..77fae82 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/IntQueue.h @@ -0,0 +1,80 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef QUEUE_ACCESS_TEST +#define QUEUE_ACCESS_TEST + +void vStartInterruptQueueTasks( void ); +BaseType_t xAreIntQueueTasksStillRunning( void ); +BaseType_t xFirstTimerHandler( void ); +BaseType_t xSecondTimerHandler( void ); + +#endif /* QUEUE_ACCESS_TEST */ + + + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/PollQ.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/PollQ.h new file mode 100644 index 0000000..5a6dc8e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/PollQ.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef POLLED_Q_H +#define POLLED_Q_H + +void vStartPolledQueueTasks( UBaseType_t uxPriority ); +BaseType_t xArePollingQueuesStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QPeek.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QPeek.h new file mode 100644 index 0000000..b9b8525 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QPeek.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef Q_PEEK_TEST_H +#define Q_PEEK_TEST_H + +void vStartQueuePeekTasks( void ); +BaseType_t xAreQueuePeekTasksStillRunning( void ); + +#endif /* Q_PEEK_TEST_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueOverwrite.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueOverwrite.h new file mode 100644 index 0000000..1d79247 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueOverwrite.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef QUEUE_OVERWRITE_H +#define QUEUE_OVERWRITE_H + +void vStartQueueOverwriteTask( UBaseType_t uxPriority ); +BaseType_t xIsQueueOverwriteTaskStillRunning( void ); +void vQueueOverwritePeriodicISRDemo( void ); + +#endif /* QUEUE_OVERWRITE_H */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueSet.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueSet.h new file mode 100644 index 0000000..16c4623 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/QueueSet.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef QUEUE_WAIT_MULTIPLE_H +#define QUEUE_WAIT_MULTIPLE_H + +void vStartQueueSetTasks( void ); +BaseType_t xAreQueueSetTasksStillRunning( void ); +void vQueueSetAccessQueueSetFromISR( void ); + +#endif /* QUEUE_WAIT_MULTIPLE_H */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/TimerDemo.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/TimerDemo.h new file mode 100644 index 0000000..d005a64 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/TimerDemo.h @@ -0,0 +1,76 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef TIMER_DEMO_H +#define TIMER_DEMO_H + +void vStartTimerDemoTask( TickType_t xBaseFrequencyIn ); +BaseType_t xAreTimerDemoTasksStillRunning( TickType_t xCycleFrequency ); +void vTimerPeriodicISRTests( void ); + +#endif /* TIMER_DEMO_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/blocktim.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/blocktim.h new file mode 100644 index 0000000..fb56df4 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/blocktim.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef BLOCK_TIME_TEST_H +#define BLOCK_TIME_TEST_H + +void vCreateBlockTimeTasks( void ); +BaseType_t xAreBlockTimeTestTasksStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest.h new file mode 100644 index 0000000..43d92c8 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef COMTEST_H +#define COMTEST_H + +void vAltStartComTestTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ); +void vStartComTestTasks( UBaseType_t uxPriority, eCOMPort ePort, eBaud eBaudRate ); +BaseType_t xAreComTestTasksStillRunning( void ); +void vComTestUnsuspendTask( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest2.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest2.h new file mode 100644 index 0000000..43e9993 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest2.h @@ -0,0 +1,73 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef COMTEST_H +#define COMTEST_H + +void vAltStartComTestTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ); +BaseType_t xAreComTestTasksStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest_strings.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest_strings.h new file mode 100644 index 0000000..0d72615 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/comtest_strings.h @@ -0,0 +1,73 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef COMTEST_STRINGS_H +#define COMTEST_STRINGS_H + +void vStartComTestStringsTasks( UBaseType_t uxPriority, uint32_t ulBaudRate, UBaseType_t uxLED ); +BaseType_t xAreComTestTasksStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/countsem.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/countsem.h new file mode 100644 index 0000000..484a23a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/countsem.h @@ -0,0 +1,73 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef COUNT_SEMAPHORE_TEST_H +#define COUNT_SEMAPHORE_TEST_H + +void vStartCountingSemaphoreTasks( void ); +BaseType_t xAreCountingSemaphoreTasksStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crflash.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crflash.h new file mode 100644 index 0000000..3d51635 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crflash.h @@ -0,0 +1,85 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef CRFLASH_LED_H +#define CRFLASH_LED_H + +/* + * Create the co-routines used to flash the LED's at different rates. + * + * @param uxPriority The number of 'fixed delay' co-routines to create. This + * also effects the number of LED's that will be utilised. For example, + * passing in 3 will cause LED's 0 to 2 to be utilised. + */ +void vStartFlashCoRoutines( UBaseType_t uxPriority ); + +/* + * Return pdPASS or pdFAIL depending on whether an error has been detected + * or not. + */ +BaseType_t xAreFlashCoRoutinesStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crhook.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crhook.h new file mode 100644 index 0000000..d56ee65 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/crhook.h @@ -0,0 +1,81 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef CRHOOK_H +#define CRHOOK_H + +/* + * Create the co-routines used to communicate wit the tick hook. + */ +void vStartHookCoRoutines( void ); + +/* + * Return pdPASS or pdFAIL depending on whether an error has been detected + * or not. + */ +BaseType_t xAreHookCoRoutinesStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/death.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/death.h new file mode 100644 index 0000000..5d08038 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/death.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef SUICIDE_TASK_H +#define SUICIDE_TASK_H + +void vCreateSuicidalTasks( UBaseType_t uxPriority ); +BaseType_t xIsCreateTaskStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/dynamic.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/dynamic.h new file mode 100644 index 0000000..a923c20 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/dynamic.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef DYNAMIC_MANIPULATION_H +#define DYNAMIC_MANIPULATION_H + +void vStartDynamicPriorityTasks( void ); +BaseType_t xAreDynamicPriorityTasksStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/fileIO.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/fileIO.h new file mode 100644 index 0000000..afcf005 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/fileIO.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FILE_IO_H +#define FILE_OI_H + +void vDisplayMessage( const char * const pcMessageToPrint ); +void vWriteMessageToDisk( const char * const pcMessage ); +void vWriteBufferToDisk( const char * const pcBuffer, uint32_t ulBufferLength ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash.h new file mode 100644 index 0000000..41ea2f6 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash.h @@ -0,0 +1,72 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FLASH_LED_H +#define FLASH_LED_H + +void vStartLEDFlashTasks( UBaseType_t uxPriority ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash_timer.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash_timer.h new file mode 100644 index 0000000..2e7a230 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flash_timer.h @@ -0,0 +1,79 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FLASH_TIMER_H +#define FLASH_TIMER_H + +/* + * Creates the LED flashing timers. xNumberOfLEDs specifies how many timers to + * create, with each timer toggling a different LED. The first LED to be + * toggled is LED 0, with subsequent LEDs following on in numerical order. Each + * timer uses the exact same callback function, with the timer ID being used + * within the callback function to determine which timer has actually expired + * (and therefore which LED to toggle). + */ +void vStartLEDFlashTimers( UBaseType_t uxNumberOfLEDs ); + +#endif /* FLASH_TIMER_H */ diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flop.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flop.h new file mode 100644 index 0000000..ef6fe64 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/flop.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef FLOP_TASKS_H +#define FLOP_TASKS_H + +void vStartMathTasks( UBaseType_t uxPriority ); +BaseType_t xAreMathsTaskStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/integer.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/integer.h new file mode 100644 index 0000000..8b0d960 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/integer.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef INTEGER_TASKS_H +#define INTEGER_TASKS_H + +void vStartIntegerMathTasks( UBaseType_t uxPriority ); +BaseType_t xAreIntegerMathsTaskStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/mevents.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/mevents.h new file mode 100644 index 0000000..fed650e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/mevents.h @@ -0,0 +1,74 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef EVENTS_TEST_H +#define EVENTS_TEST_H + +void vStartMultiEventTasks( void ); +BaseType_t xAreMultiEventTasksStillRunning( void ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/partest.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/partest.h new file mode 100644 index 0000000..0886481 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/partest.h @@ -0,0 +1,76 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef PARTEST_H +#define PARTEST_H + +#define partstDEFAULT_PORT_ADDRESS ( ( uint16_t ) 0x378 ) + +void vParTestInitialise( void ); +void vParTestSetLED( UBaseType_t uxLED, BaseType_t xValue ); +void vParTestToggleLED( UBaseType_t uxLED ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/print.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/print.h new file mode 100644 index 0000000..562b1e7 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/print.h @@ -0,0 +1,75 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef PRINT_H +#define PRINT_H + +void vPrintInitialise( void ); +void vPrintDisplayMessage( const char * const * pcMessageToSend ); +const char *pcPrintGetNextMessage( TickType_t xPrintRate ); + +#endif + + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/recmutex.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/recmutex.h new file mode 100644 index 0000000..82eee1f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/recmutex.h @@ -0,0 +1,73 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef RECURSIVE_MUTEX_TEST_H +#define RECURSIVE_MUTEX_TEST_H + +void vStartRecursiveMutexTasks( void ); +BaseType_t xAreRecursiveMutexTasksStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/semtest.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/semtest.h new file mode 100644 index 0000000..4c7ffb3 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/semtest.h @@ -0,0 +1,73 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef SEMAPHORE_TEST_H +#define SEMAPHORE_TEST_H + +void vStartSemaphoreTasks( UBaseType_t uxPriority ); +BaseType_t xAreSemaphoreTasksStillRunning( void ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Demo/Common/include/serial.h b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/serial.h new file mode 100644 index 0000000..838ccd3 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Demo/Common/include/serial.h @@ -0,0 +1,136 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef SERIAL_COMMS_H +#define SERIAL_COMMS_H + +typedef void * xComPortHandle; + +typedef enum +{ + serCOM1, + serCOM2, + serCOM3, + serCOM4, + serCOM5, + serCOM6, + serCOM7, + serCOM8 +} eCOMPort; + +typedef enum +{ + serNO_PARITY, + serODD_PARITY, + serEVEN_PARITY, + serMARK_PARITY, + serSPACE_PARITY +} eParity; + +typedef enum +{ + serSTOP_1, + serSTOP_2 +} eStopBits; + +typedef enum +{ + serBITS_5, + serBITS_6, + serBITS_7, + serBITS_8 +} eDataBits; + +typedef enum +{ + ser50, + ser75, + ser110, + ser134, + ser150, + ser200, + ser300, + ser600, + ser1200, + ser1800, + ser2400, + ser4800, + ser9600, + ser19200, + ser38400, + ser57600, + ser115200 +} eBaud; + +xComPortHandle xSerialPortInitMinimal( unsigned long ulWantedBaud, unsigned portBASE_TYPE uxQueueLength ); +xComPortHandle xSerialPortInit( eCOMPort ePort, eBaud eWantedBaud, eParity eWantedParity, eDataBits eWantedDataBits, eStopBits eWantedStopBits, unsigned portBASE_TYPE uxBufferLength ); +void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength ); +signed portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedChar, TickType_t xBlockTime ); +signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, TickType_t xBlockTime ); +portBASE_TYPE xSerialWaitForSemaphore( xComPortHandle xPort ); +void vSerialClose( xComPortHandle xPort ); + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/License/license.txt b/component/os/freertos/freertos_v8.1.2/License/license.txt new file mode 100644 index 0000000..be959bb --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/License/license.txt @@ -0,0 +1,394 @@ +The FreeRTOS source code is licensed by a *modified* GNU General Public +License (GPL). The modification is provided in the form of an exception. + +NOTE: The modification to the GPL is included to allow you to distribute a +combined work that includes FreeRTOS without being obliged to provide the source +code for proprietary components outside of the FreeRTOS kernel. + + + +---------------------------------------------------------------------------- + +The FreeRTOS GPL Exception Text: + +Any FreeRTOS source code, whether modified or in it's original release form, +or whether in whole or in part, can only be distributed by you under the terms +of the GNU General Public License plus this exception. An independent module is +a module which is not derived from or based on FreeRTOS. + +Clause 1: + +Linking FreeRTOS statically or dynamically with other modules is making a +combined work based on FreeRTOS. Thus, the terms and conditions of the GNU +General Public License cover the whole combination. + +As a special exception, the copyright holder of FreeRTOS gives you permission +to link FreeRTOS with independent modules that communicate with FreeRTOS +solely through the FreeRTOS API interface, regardless of the license terms of +these independent modules, and to copy and distribute the resulting combined +work under terms of your choice, provided that + + + Every copy of the combined work is accompanied by a written statement that + details to the recipient the version of FreeRTOS used and an offer by yourself + to provide the FreeRTOS source code (including any modifications you may have + made) should the recipient request it. + + + The combined work is not itself an RTOS, scheduler, kernel or related product. + + + The independent modules add significant and primary functionality to FreeRTOS + and do not merely extend the existing functionality already present in FreeRTOS. + +Clause 2: + +FreeRTOS may not be used for any competitive or comparative purpose, including the +publication of any form of run time or compile time metric, without the express +permission of Real Time Engineers Ltd. (this is the norm within the industry and +is intended to ensure information accuracy). + + +-------------------------------------------------------------------- + +The standard GPL exception text: + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License** as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/component/os/freertos/freertos_v8.1.2/Source/Makefile b/component/os/freertos/freertos_v8.1.2/Source/Makefile new file mode 100644 index 0000000..85694d5 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/Makefile @@ -0,0 +1,54 @@ + +include $(MAKE_INCLUDE_GEN) + +.PHONY: all clean + +MODULE_IFLAGS = -I./include + + +#*****************************************************************************# +# Object FILE LIST # +#*****************************************************************************# +OBJS = tasks.o list.o croutine.o queue.o timers.o event_groups.o +ifeq ($(CONFIG_RELEASE_BUILD),y) + OBJS = +else +endif + + +#*****************************************************************************# +# RULES TO GENERATE TARGETS # +#*****************************************************************************# + +# Define the Rules to build the core targets +#all: CORE_TARGETS COPY_RAM_OBJS +all: CORE_TARGETS COPY_RAM_OBJS + make -C portable/MemMang all + make -C portable/GCC/ARM_CM3 all + + + +#*****************************************************************************# +# GENERATE OBJECT FILE +#*****************************************************************************# +CORE_TARGETS: $(OBJS) + + +#*****************************************************************************# +# RULES TO CLEAN TARGETS # +#*****************************************************************************# +clean: + $(REMOVE) portable/MemMang/*.o + $(REMOVE) portable/MemMang/*.i + $(REMOVE) portable/MemMang/*.s + $(REMOVE) portable/MemMang/*.d + $(REMOVE) portable/GCC/ARM_CM3/*.o + $(REMOVE) portable/GCC/ARM_CM3/*.i + $(REMOVE) portable/GCC/ARM_CM3/*.s + $(REMOVE) portable/GCC/ARM_CM3/*.d + $(REMOVE) *.o + $(REMOVE) *.i + $(REMOVE) *.s + $(REMOVE) *.d + +-include $(DEPS) diff --git a/component/os/freertos/freertos_v8.1.2/Source/croutine.c b/component/os/freertos/freertos_v8.1.2/Source/croutine.c new file mode 100644 index 0000000..3c5bd6f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/croutine.c @@ -0,0 +1,386 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" + +/* + * Some kernel aware debuggers require data to be viewed to be global, rather + * than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + + +/* Lists for ready and blocked co-routines. --------------------*/ +static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ +static List_t xDelayedCoRoutineList1; /*< Delayed co-routines. */ +static List_t xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ +static List_t * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */ +static List_t * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ +static List_t xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ + +/* Other file private variables. --------------------------------*/ +CRCB_t * pxCurrentCoRoutine = NULL; +static UBaseType_t uxTopCoRoutineReadyPriority = 0; +static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; + +/* The initial state of the co-routine when it is created. */ +#define corINITIAL_STATE ( 0 ) + +/* + * Place the co-routine represented by pxCRCB into the appropriate ready queue + * for the priority. It is inserted at the end of the list. + * + * This macro accesses the co-routine ready lists and therefore must not be + * used from within an ISR. + */ +#define prvAddCoRoutineToReadyQueue( pxCRCB ) \ +{ \ + if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ + { \ + uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ + } \ + vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ +} + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first co-routine. + */ +static void prvInitialiseCoRoutineLists( void ); + +/* + * Co-routines that are readied by an interrupt cannot be placed directly into + * the ready lists (there is no mutual exclusion). Instead they are placed in + * in the pending ready list in order that they can later be moved to the ready + * list by the co-routine scheduler. + */ +static void prvCheckPendingReadyList( void ); + +/* + * Macro that looks at the list of co-routines that are currently delayed to + * see if any require waking. + * + * Co-routines are stored in the queue in the order of their wake time - + * meaning once one co-routine has been found whose timer has not expired + * we need not look any further down the list. + */ +static void prvCheckDelayedList( void ); + +/*-----------------------------------------------------------*/ + +BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex ) +{ +BaseType_t xReturn; +CRCB_t *pxCoRoutine; + + /* Allocate the memory that will store the co-routine control block. */ + pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) ); + if( pxCoRoutine ) + { + /* If pxCurrentCoRoutine is NULL then this is the first co-routine to + be created and the co-routine data structures need initialising. */ + if( pxCurrentCoRoutine == NULL ) + { + pxCurrentCoRoutine = pxCoRoutine; + prvInitialiseCoRoutineLists(); + } + + /* Check the priority is within limits. */ + if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) + { + uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; + } + + /* Fill out the co-routine control block from the function parameters. */ + pxCoRoutine->uxState = corINITIAL_STATE; + pxCoRoutine->uxPriority = uxPriority; + pxCoRoutine->uxIndex = uxIndex; + pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; + + /* Initialise all the other co-routine control block parameters. */ + vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); + vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); + + /* Set the co-routine control block as a link back from the ListItem_t. + This is so we can get back to the containing CRCB from a generic item + in a list. */ + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) ); + + /* Now the co-routine has been initialised it can be added to the ready + list at the correct priority. */ + prvAddCoRoutineToReadyQueue( pxCoRoutine ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList ) +{ +TickType_t xTimeToWake; + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xCoRoutineTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + ( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xCoRoutineTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + + if( pxEventList ) + { + /* Also add the co-routine to an event list. If this is done then the + function must be called with interrupts disabled. */ + vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckPendingReadyList( void ) +{ + /* Are there any co-routines waiting to get moved to the ready list? These + are co-routines that have been readied by an ISR. The ISR cannot access + the ready lists itself. */ + while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE ) + { + CRCB_t *pxUnblockedCRCB; + + /* The pending ready list can be accessed by an ISR. */ + portDISABLE_INTERRUPTS(); + { + pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) ); + ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + } + portENABLE_INTERRUPTS(); + + ( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); + prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckDelayedList( void ) +{ +CRCB_t *pxCRCB; + + xPassedTicks = xTaskGetTickCount() - xLastTickCount; + while( xPassedTicks ) + { + xCoRoutineTickCount++; + xPassedTicks--; + + /* If the tick count has overflowed we need to swap the ready lists. */ + if( xCoRoutineTickCount == 0 ) + { + List_t * pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. If there are + any items in pxDelayedCoRoutineList here then there is an error! */ + pxTemp = pxDelayedCoRoutineList; + pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; + pxOverflowDelayedCoRoutineList = pxTemp; + } + + /* See if this tick has made a timeout expire. */ + while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE ) + { + pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ); + + if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) + { + /* Timeout not yet expired. */ + break; + } + + portDISABLE_INTERRUPTS(); + { + /* The event could have occurred just before this critical + section. If this is the case then the generic list item will + have been moved to the pending ready list and the following + line is still valid. Also the pvContainer parameter will have + been set to NULL so the following lines are also valid. */ + ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) ); + + /* Is the co-routine waiting on an event also? */ + if( pxCRCB->xEventListItem.pvContainer ) + { + ( void ) uxListRemove( &( pxCRCB->xEventListItem ) ); + } + } + portENABLE_INTERRUPTS(); + + prvAddCoRoutineToReadyQueue( pxCRCB ); + } + } + + xLastTickCount = xCoRoutineTickCount; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineSchedule( void ) +{ + /* See if any co-routines readied by events need moving to the ready lists. */ + prvCheckPendingReadyList(); + + /* See if any delayed co-routines have timed out. */ + prvCheckDelayedList(); + + /* Find the highest priority queue that contains ready co-routines. */ + while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) + { + if( uxTopCoRoutineReadyPriority == 0 ) + { + /* No more co-routines to check. */ + return; + } + --uxTopCoRoutineReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines + of the same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); + + /* Call the co-routine. */ + ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); + + return; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseCoRoutineLists( void ) +{ +UBaseType_t uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); + } + + vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 ); + vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 ); + vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList ); + + /* Start with pxDelayedCoRoutineList using list1 and the + pxOverflowDelayedCoRoutineList using list2. */ + pxDelayedCoRoutineList = &xDelayedCoRoutineList1; + pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; +} +/*-----------------------------------------------------------*/ + +BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList ) +{ +CRCB_t *pxUnblockedCRCB; +BaseType_t xReturn; + + /* This function is called from within an interrupt. It can only access + event lists and the pending ready list. This function assumes that a + check has already been made to ensure pxEventList is not empty. */ + pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) ); + + if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + diff --git a/component/os/freertos/freertos_v8.1.2/Source/event_groups.c b/component/os/freertos/freertos_v8.1.2/Source/event_groups.c new file mode 100644 index 0000000..caecbbb --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/event_groups.c @@ -0,0 +1,676 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* Standard includes. */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "event_groups.h" + +/* Lint e961 and e750 are suppressed as a MISRA exception justified because the +MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the +header files above, but not in this file, in order to generate the correct +privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ + +#if ( INCLUDE_xEventGroupSetBitFromISR == 1 ) && ( configUSE_TIMERS == 0 ) + #error configUSE_TIMERS must be set to 1 to make the xEventGroupSetBitFromISR() function available. +#endif + +#if ( INCLUDE_xEventGroupSetBitFromISR == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 0 ) + #error INCLUDE_xTimerPendFunctionCall must also be set to one to make the xEventGroupSetBitFromISR() function available. +#endif + +/* The following bit fields convey control information in a task's event list +item value. It is important they don't clash with the +taskEVENT_LIST_ITEM_VALUE_IN_USE definition. */ +#if configUSE_16_BIT_TICKS == 1 + #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x0100U + #define eventUNBLOCKED_DUE_TO_BIT_SET 0x0200U + #define eventWAIT_FOR_ALL_BITS 0x0400U + #define eventEVENT_BITS_CONTROL_BYTES 0xff00U +#else + #define eventCLEAR_EVENTS_ON_EXIT_BIT 0x01000000UL + #define eventUNBLOCKED_DUE_TO_BIT_SET 0x02000000UL + #define eventWAIT_FOR_ALL_BITS 0x04000000UL + #define eventEVENT_BITS_CONTROL_BYTES 0xff000000UL +#endif + +typedef struct xEventGroupDefinition +{ + EventBits_t uxEventBits; + List_t xTasksWaitingForBits; /*< List of tasks waiting for a bit to be set. */ + + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxEventGroupNumber; + #endif + +} EventGroup_t; + +/*-----------------------------------------------------------*/ + +/* + * Test the bits set in uxCurrentEventBits to see if the wait condition is met. + * The wait condition is defined by xWaitForAllBits. If xWaitForAllBits is + * pdTRUE then the wait condition is met if all the bits set in uxBitsToWaitFor + * are also set in uxCurrentEventBits. If xWaitForAllBits is pdFALSE then the + * wait condition is met if any of the bits set in uxBitsToWait for are also set + * in uxCurrentEventBits. + */ +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ); + +/*-----------------------------------------------------------*/ + +EventGroupHandle_t xEventGroupCreate( void ) +{ +EventGroup_t *pxEventBits; + + pxEventBits = pvPortMalloc( sizeof( EventGroup_t ) ); + if( pxEventBits != NULL ) + { + pxEventBits->uxEventBits = 0; + vListInitialise( &( pxEventBits->xTasksWaitingForBits ) ); + traceEVENT_GROUP_CREATE( pxEventBits ); + } + else + { + traceEVENT_GROUP_CREATE_FAILED(); + } + + return ( EventGroupHandle_t ) pxEventBits; +} +/*-----------------------------------------------------------*/ + +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) +{ +EventBits_t uxOriginalBitValue, uxReturn; +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +BaseType_t xAlreadyYielded; +BaseType_t xTimeoutOccurred = pdFALSE; + + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + uxOriginalBitValue = pxEventBits->uxEventBits; + + ( void ) xEventGroupSetBits( xEventGroup, uxBitsToSet ); + + if( ( ( uxOriginalBitValue | uxBitsToSet ) & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + /* All the rendezvous bits are now set - no need to block. */ + uxReturn = ( uxOriginalBitValue | uxBitsToSet ); + + /* Rendezvous always clear the bits. They will have been cleared + already unless this is the only task in the rendezvous. */ + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + + xTicksToWait = 0; + } + else + { + if( xTicksToWait != ( TickType_t ) 0 ) + { + traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ); + + /* Store the bits that the calling task is waiting for in the + task's event list item so the kernel knows when a match is + found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | eventCLEAR_EVENTS_ON_EXIT_BIT | eventWAIT_FOR_ALL_BITS ), xTicksToWait ); + + /* This assignment is obsolete as uxReturn will get set after + the task unblocks, but some compilers mistakenly generate a + warning about uxReturn being returned without being set if the + assignment is omitted. */ + uxReturn = 0; + } + else + { + /* The rendezvous bits were not set, but no block time was + specified - just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + } + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + point either the required bits were set or the block time expired. If + the required bits were set they will have been stored in the task's + event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + /* The task timed out, just return the current event bit value. */ + taskENTER_CRITICAL(); + { + uxReturn = pxEventBits->uxEventBits; + + /* Although the task got here because it timed out before the + bits it was waiting for were set, it is possible that since it + unblocked another task has set the bits. If this is the case + then it needs to clear the bits before exiting. */ + if( ( uxReturn & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + xTimeoutOccurred = pdTRUE; + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* Control bits might be set as the task had blocked should not be + returned. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + + traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) +{ +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventBits_t uxReturn, uxControlBits = 0; +BaseType_t xWaitConditionMet, xAlreadyYielded; +BaseType_t xTimeoutOccurred = pdFALSE; + + /* Check the user is not attempting to wait on the bits used by the kernel + itself, and that at least one bit is being requested. */ + configASSERT( ( uxBitsToWaitFor & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + configASSERT( uxBitsToWaitFor != 0 ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + vTaskSuspendAll(); + { + const EventBits_t uxCurrentEventBits = pxEventBits->uxEventBits; + + /* Check to see if the wait condition is already met or not. */ + xWaitConditionMet = prvTestWaitCondition( uxCurrentEventBits, uxBitsToWaitFor, xWaitForAllBits ); + + if( xWaitConditionMet != pdFALSE ) + { + /* The wait condition has already been met so there is no need to + block. */ + uxReturn = uxCurrentEventBits; + xTicksToWait = ( TickType_t ) 0; + + /* Clear the wait bits if requested to do so. */ + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The wait condition has not been met, but no block time was + specified, so just return the current value. */ + uxReturn = uxCurrentEventBits; + } + else + { + /* The task is going to block to wait for its required bits to be + set. uxControlBits are used to remember the specified behaviour of + this call to xEventGroupWaitBits() - for use when the event bits + unblock the task. */ + if( xClearOnExit != pdFALSE ) + { + uxControlBits |= eventCLEAR_EVENTS_ON_EXIT_BIT; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xWaitForAllBits != pdFALSE ) + { + uxControlBits |= eventWAIT_FOR_ALL_BITS; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the bits that the calling task is waiting for in the + task's event list item so the kernel knows when a match is + found. Then enter the blocked state. */ + vTaskPlaceOnUnorderedEventList( &( pxEventBits->xTasksWaitingForBits ), ( uxBitsToWaitFor | uxControlBits ), xTicksToWait ); + + /* This is obsolete as it will get set after the task unblocks, but + some compilers mistakenly generate a warning about the variable + being returned without being set if it is not done. */ + uxReturn = 0; + + traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ); + } + } + xAlreadyYielded = xTaskResumeAll(); + + if( xTicksToWait != ( TickType_t ) 0 ) + { + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The task blocked to wait for its required bits to be set - at this + point either the required bits were set or the block time expired. If + the required bits were set they will have been stored in the task's + event list item, and they should now be retrieved then cleared. */ + uxReturn = uxTaskResetEventItemValue(); + + if( ( uxReturn & eventUNBLOCKED_DUE_TO_BIT_SET ) == ( EventBits_t ) 0 ) + { + taskENTER_CRITICAL(); + { + /* The task timed out, just return the current event bit value. */ + uxReturn = pxEventBits->uxEventBits; + + /* It is possible that the event bits were updated between this + task leaving the Blocked state and running again. */ + if( prvTestWaitCondition( uxReturn, uxBitsToWaitFor, xWaitForAllBits ) != pdFALSE ) + { + if( xClearOnExit != pdFALSE ) + { + pxEventBits->uxEventBits &= ~uxBitsToWaitFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* Prevent compiler warnings when trace macros are not used. */ + xTimeoutOccurred = pdFALSE; + } + else + { + /* The task unblocked because the bits were set. */ + } + + /* The task blocked so control bits may have been set. */ + uxReturn &= ~eventEVENT_BITS_CONTROL_BYTES; + } + traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) +{ +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventBits_t uxReturn; + + /* Check the user is not attempting to clear the bits used by the kernel + itself. */ + configASSERT( ( uxBitsToClear & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + + taskENTER_CRITICAL(); + { + traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ); + + /* The value returned is the event group value prior to the bits being + cleared. */ + uxReturn = pxEventBits->uxEventBits; + + /* Clear the bits. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + } + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) + + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) + { + BaseType_t xReturn; + + traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ); + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ) +{ +UBaseType_t uxSavedInterruptStatus; +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +EventBits_t uxReturn; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + uxReturn = pxEventBits->uxEventBits; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) +{ +ListItem_t *pxListItem, *pxNext; +ListItem_t const *pxListEnd; +List_t *pxList; +EventBits_t uxBitsToClear = 0, uxBitsWaitedFor, uxControlBits; +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +BaseType_t xMatchFound = pdFALSE; + + /* Check the user is not attempting to set the bits used by the kernel + itself. */ + configASSERT( ( uxBitsToSet & eventEVENT_BITS_CONTROL_BYTES ) == 0 ); + + pxList = &( pxEventBits->xTasksWaitingForBits ); + pxListEnd = listGET_END_MARKER( pxList ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + vTaskSuspendAll(); + { + traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ); + + pxListItem = listGET_HEAD_ENTRY( pxList ); + + /* Set the bits. */ + pxEventBits->uxEventBits |= uxBitsToSet; + + /* See if the new bit value should unblock any tasks. */ + while( pxListItem != pxListEnd ) + { + pxNext = listGET_NEXT( pxListItem ); + uxBitsWaitedFor = listGET_LIST_ITEM_VALUE( pxListItem ); + xMatchFound = pdFALSE; + + /* Split the bits waited for from the control bits. */ + uxControlBits = uxBitsWaitedFor & eventEVENT_BITS_CONTROL_BYTES; + uxBitsWaitedFor &= ~eventEVENT_BITS_CONTROL_BYTES; + + if( ( uxControlBits & eventWAIT_FOR_ALL_BITS ) == ( EventBits_t ) 0 ) + { + /* Just looking for single bit being set. */ + if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) != ( EventBits_t ) 0 ) + { + xMatchFound = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( ( uxBitsWaitedFor & pxEventBits->uxEventBits ) == uxBitsWaitedFor ) + { + /* All bits are set. */ + xMatchFound = pdTRUE; + } + else + { + /* Need all bits to be set, but not all the bits were set. */ + } + + if( xMatchFound != pdFALSE ) + { + /* The bits match. Should the bits be cleared on exit? */ + if( ( uxControlBits & eventCLEAR_EVENTS_ON_EXIT_BIT ) != ( EventBits_t ) 0 ) + { + uxBitsToClear |= uxBitsWaitedFor; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Store the actual event flag value in the task's event list + item before removing the task from the event list. The + eventUNBLOCKED_DUE_TO_BIT_SET bit is set so the task knows + that is was unblocked due to its required bits matching, rather + than because it timed out. */ + ( void ) xTaskRemoveFromUnorderedEventList( pxListItem, pxEventBits->uxEventBits | eventUNBLOCKED_DUE_TO_BIT_SET ); + } + + /* Move onto the next list item. Note pxListItem->pxNext is not + used here as the list item may have been removed from the event list + and inserted into the ready/pending reading list. */ + pxListItem = pxNext; + } + + /* Clear any bits that matched when the eventCLEAR_EVENTS_ON_EXIT_BIT + bit was set in the control word. */ + pxEventBits->uxEventBits &= ~uxBitsToClear; + } + ( void ) xTaskResumeAll(); + + return pxEventBits->uxEventBits; +} +/*-----------------------------------------------------------*/ + +void vEventGroupDelete( EventGroupHandle_t xEventGroup ) +{ +EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; +const List_t *pxTasksWaitingForBits = &( pxEventBits->xTasksWaitingForBits ); + + vTaskSuspendAll(); + { + traceEVENT_GROUP_DELETE( xEventGroup ); + + while( listCURRENT_LIST_LENGTH( pxTasksWaitingForBits ) > ( UBaseType_t ) 0 ) + { + /* Unblock the task, returning 0 as the event list is being deleted + and cannot therefore have any bits set. */ + configASSERT( pxTasksWaitingForBits->xListEnd.pxNext != ( ListItem_t * ) &( pxTasksWaitingForBits->xListEnd ) ); + ( void ) xTaskRemoveFromUnorderedEventList( pxTasksWaitingForBits->xListEnd.pxNext, eventUNBLOCKED_DUE_TO_BIT_SET ); + } + + vPortFree( pxEventBits ); + } + ( void ) xTaskResumeAll(); +} +/*-----------------------------------------------------------*/ + +/* For internal use only - execute a 'set bits' command that was pended from +an interrupt. */ +void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ) +{ + ( void ) xEventGroupSetBits( pvEventGroup, ( EventBits_t ) ulBitsToSet ); +} +/*-----------------------------------------------------------*/ + +/* For internal use only - execute a 'clear bits' command that was pended from +an interrupt. */ +void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ) +{ + ( void ) xEventGroupClearBits( pvEventGroup, ( EventBits_t ) ulBitsToClear ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvTestWaitCondition( const EventBits_t uxCurrentEventBits, const EventBits_t uxBitsToWaitFor, const BaseType_t xWaitForAllBits ) +{ +BaseType_t xWaitConditionMet = pdFALSE; + + if( xWaitForAllBits == pdFALSE ) + { + /* Task only has to wait for one bit within uxBitsToWaitFor to be + set. Is one already set? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) != ( EventBits_t ) 0 ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Task has to wait for all the bits in uxBitsToWaitFor to be set. + Are they set already? */ + if( ( uxCurrentEventBits & uxBitsToWaitFor ) == uxBitsToWaitFor ) + { + xWaitConditionMet = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return xWaitConditionMet; +} +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 1 ) ) + + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ) + { + BaseType_t xReturn; + + traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ); + xReturn = xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ); + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if (configUSE_TRACE_FACILITY == 1) + + UBaseType_t uxEventGroupGetNumber( void* xEventGroup ) + { + UBaseType_t xReturn; + EventGroup_t *pxEventBits = ( EventGroup_t * ) xEventGroup; + + if( xEventGroup == NULL ) + { + xReturn = 0; + } + else + { + xReturn = pxEventBits->uxEventGroupNumber; + } + + return xReturn; + } + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/FreeRTOS.h b/component/os/freertos/freertos_v8.1.2/Source/include/FreeRTOS.h new file mode 100644 index 0000000..eaa41cb --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/FreeRTOS.h @@ -0,0 +1,762 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef INC_FREERTOS_H +#define INC_FREERTOS_H + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* + * If stdint.h cannot be located then: + * + If using GCC ensure the -nostdint options is *not* being used. + * + Ensure the project's include path includes the directory in which your + * compiler stores stdint.h. + * + Set any compiler options necessary for it to support C99, as technically + * stdint.h is only mandatory with C99 (FreeRTOS does not require C99 in any + * other way). + * + The FreeRTOS download includes a simple stdint.h definition that can be + * used in cases where none is provided by the compiler. The files only + * contains the typedefs required to build FreeRTOS. Read the instructions + * in FreeRTOS/source/stdint.readme for more information. + */ +#include /* READ COMMENT ABOVE. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configMINIMAL_STACK_SIZE + #error Missing definition: configMINIMAL_STACK_SIZE must be defined in FreeRTOSConfig.h. configMINIMAL_STACK_SIZE defines the size (in words) of the stack allocated to the idle task. Refer to the demo project provided for your port for a suitable value. +#endif + +#ifndef configMAX_PRIORITIES + #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #error Missing definition: configUSE_CO_ROUTINES must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #error Missing definition: INCLUDE_vTaskPrioritySet must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #error Missing definition: INCLUDE_uxTaskPriorityGet must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelete + #error Missing definition: INCLUDE_vTaskDelete must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskSuspend + #error Missing definition: INCLUDE_vTaskSuspend must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelayUntil + #error Missing definition: INCLUDE_vTaskDelayUntil must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelay + #error Missing definition: INCLUDE_vTaskDelay must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_16_BIT_TICKS + #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if configUSE_CO_ROUTINES != 0 + #ifndef configMAX_CO_ROUTINE_PRIORITIES + #error configMAX_CO_ROUTINE_PRIORITIES must be greater than or equal to 1. + #endif +#endif + +#ifndef configMAX_PRIORITIES + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. +#endif + +#ifndef INCLUDE_xTaskGetIdleTaskHandle + #define INCLUDE_xTaskGetIdleTaskHandle 0 +#endif + +#ifndef INCLUDE_xTimerGetTimerDaemonTaskHandle + #define INCLUDE_xTimerGetTimerDaemonTaskHandle 0 +#endif + +#ifndef INCLUDE_xQueueGetMutexHolder + #define INCLUDE_xQueueGetMutexHolder 0 +#endif + +#ifndef INCLUDE_xSemaphoreGetMutexHolder + #define INCLUDE_xSemaphoreGetMutexHolder INCLUDE_xQueueGetMutexHolder +#endif + +#ifndef INCLUDE_pcTaskGetTaskName + #define INCLUDE_pcTaskGetTaskName 0 +#endif + +#ifndef configUSE_APPLICATION_TASK_TAG + #define configUSE_APPLICATION_TASK_TAG 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark + #define INCLUDE_uxTaskGetStackHighWaterMark 0 +#endif + +#ifndef INCLUDE_eTaskGetState + #define INCLUDE_eTaskGetState 0 +#endif + +#ifndef configUSE_RECURSIVE_MUTEXES + #define configUSE_RECURSIVE_MUTEXES 0 +#endif + +#ifndef configUSE_MUTEXES + #define configUSE_MUTEXES 0 +#endif + +#ifndef configUSE_TIMERS + #define configUSE_TIMERS 0 +#endif + +#ifndef configUSE_COUNTING_SEMAPHORES + #define configUSE_COUNTING_SEMAPHORES 0 +#endif + +#ifndef configUSE_ALTERNATIVE_API + #define configUSE_ALTERNATIVE_API 0 +#endif + +#ifndef portCRITICAL_NESTING_IN_TCB + #define portCRITICAL_NESTING_IN_TCB 0 +#endif + +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef INCLUDE_xEventGroupSetBitFromISR + #define INCLUDE_xEventGroupSetBitFromISR 0 +#endif + +#ifndef INCLUDE_xTimerPendFunctionCall + #define INCLUDE_xTimerPendFunctionCall 0 +#endif + +#ifndef configASSERT + #define configASSERT( x ) + #define configASSERT_DEFINED 0 +#else + #define configASSERT_DEFINED 1 +#endif + +/* The timers module relies on xTaskGetSchedulerState(). */ +#if configUSE_TIMERS == 1 + + #ifndef configTIMER_TASK_PRIORITY + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. + #endif /* configTIMER_TASK_PRIORITY */ + + #ifndef configTIMER_QUEUE_LENGTH + #error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. + #endif /* configTIMER_QUEUE_LENGTH */ + + #ifndef configTIMER_TASK_STACK_DEPTH + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. + #endif /* configTIMER_TASK_STACK_DEPTH */ + +#endif /* configUSE_TIMERS */ + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +#ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 0 +#endif + + +#ifndef portSET_INTERRUPT_MASK_FROM_ISR + #define portSET_INTERRUPT_MASK_FROM_ISR() 0 +#endif + +#ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue +#endif + +#ifndef portCLEAN_UP_TCB + #define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB +#endif + +#ifndef portPRE_TASK_DELETE_HOOK + #define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxYieldPending ) +#endif + +#ifndef portSETUP_TCB + #define portSETUP_TCB( pxTCB ) ( void ) pxTCB +#endif + +#ifndef configQUEUE_REGISTRY_SIZE + #define configQUEUE_REGISTRY_SIZE 0U +#endif + +#if ( configQUEUE_REGISTRY_SIZE < 1 ) + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) +#endif + +#ifndef portPOINTER_SIZE_TYPE + #define portPOINTER_SIZE_TYPE uint32_t +#endif + +/* Remove any unused trace macros. */ +#ifndef traceSTART + /* Used to perform any necessary initialisation - for example, open a file + into which trace is to be written. */ + #define traceSTART() +#endif + +#ifndef traceEND + /* Use to close a trace, for example close a file into which trace has been + written. */ + #define traceEND() +#endif + +#ifndef traceTASK_SWITCHED_IN + /* Called after a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() +#endif + +#ifndef traceINCREASE_TICK_COUNT + /* Called before stepping the tick count after waking from tickless idle + sleep. */ + #define traceINCREASE_TICK_COUNT( x ) +#endif + +#ifndef traceLOW_POWER_IDLE_BEGIN + /* Called immediately before entering tickless idle. */ + #define traceLOW_POWER_IDLE_BEGIN() +#endif + +#ifndef traceLOW_POWER_IDLE_END + /* Called when returning to the Idle task after a tickless idle. */ + #define traceLOW_POWER_IDLE_END() +#endif + +#ifndef traceTASK_SWITCHED_OUT + /* Called before a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() +#endif + +#ifndef traceTASK_PRIORITY_INHERIT + /* Called when a task attempts to take a mutex that is already held by a + lower priority task. pxTCBOfMutexHolder is a pointer to the TCB of the task + that holds the mutex. uxInheritedPriority is the priority the mutex holder + will inherit (the priority of the task that is attempting to obtain the + muted. */ + #define traceTASK_PRIORITY_INHERIT( pxTCBOfMutexHolder, uxInheritedPriority ) +#endif + +#ifndef traceTASK_PRIORITY_DISINHERIT + /* Called when a task releases a mutex, the holding of which had resulted in + the task inheriting the priority of a higher priority task. + pxTCBOfMutexHolder is a pointer to the TCB of the task that is releasing the + mutex. uxOriginalPriority is the task's configured (base) priority. */ + #define traceTASK_PRIORITY_DISINHERIT( pxTCBOfMutexHolder, uxOriginalPriority ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_RECEIVE + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_SEND + /* Task is about to block because it cannot write to a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the write was attempted. pxCurrentTCB points to the TCB of the + task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) +#endif + +#ifndef configCHECK_FOR_STACK_OVERFLOW + #define configCHECK_FOR_STACK_OVERFLOW 0 +#endif + +/* The following event macros are embedded in the kernel API calls. */ + +#ifndef traceMOVED_TASK_TO_READY_STATE + #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) +#endif + +#ifndef traceQUEUE_CREATE + #define traceQUEUE_CREATE( pxNewQueue ) +#endif + +#ifndef traceQUEUE_CREATE_FAILED + #define traceQUEUE_CREATE_FAILED( ucQueueType ) +#endif + +#ifndef traceCREATE_MUTEX + #define traceCREATE_MUTEX( pxNewQueue ) +#endif + +#ifndef traceCREATE_MUTEX_FAILED + #define traceCREATE_MUTEX_FAILED() +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE_FAILED + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE_FAILED + #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE + #define traceCREATE_COUNTING_SEMAPHORE() +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SEND + #define traceQUEUE_SEND( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FAILED + #define traceQUEUE_SEND_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE + #define traceQUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK + #define traceQUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR + #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FAILED + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR_FAILED + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR_FAILED + #define traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_DELETE + #define traceQUEUE_DELETE( pxQueue ) +#endif + +#ifndef traceTASK_CREATE + #define traceTASK_CREATE( pxNewTCB ) +#endif + +#ifndef traceTASK_CREATE_FAILED + #define traceTASK_CREATE_FAILED() +#endif + +#ifndef traceTASK_DELETE + #define traceTASK_DELETE( pxTaskToDelete ) +#endif + +#ifndef traceTASK_DELAY_UNTIL + #define traceTASK_DELAY_UNTIL() +#endif + +#ifndef traceTASK_DELAY + #define traceTASK_DELAY() +#endif + +#ifndef traceTASK_PRIORITY_SET + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) +#endif + +#ifndef traceTASK_SUSPEND + #define traceTASK_SUSPEND( pxTaskToSuspend ) +#endif + +#ifndef traceTASK_RESUME + #define traceTASK_RESUME( pxTaskToResume ) +#endif + +#ifndef traceTASK_RESUME_FROM_ISR + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) +#endif + +#ifndef traceTASK_INCREMENT_TICK + #define traceTASK_INCREMENT_TICK( xTickCount ) +#endif + +#ifndef traceTIMER_CREATE + #define traceTIMER_CREATE( pxNewTimer ) +#endif + +#ifndef traceTIMER_CREATE_FAILED + #define traceTIMER_CREATE_FAILED() +#endif + +#ifndef traceTIMER_COMMAND_SEND + #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) +#endif + +#ifndef traceTIMER_EXPIRED + #define traceTIMER_EXPIRED( pxTimer ) +#endif + +#ifndef traceTIMER_COMMAND_RECEIVED + #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) +#endif + +#ifndef traceMALLOC + #define traceMALLOC( pvAddress, uiSize ) +#endif + +#ifndef traceFREE + #define traceFREE( pvAddress, uiSize ) +#endif + +#ifndef traceEVENT_GROUP_CREATE + #define traceEVENT_GROUP_CREATE( xEventGroup ) +#endif + +#ifndef traceEVENT_GROUP_CREATE_FAILED + #define traceEVENT_GROUP_CREATE_FAILED() +#endif + +#ifndef traceEVENT_GROUP_SYNC_BLOCK + #define traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_SYNC_END + #define traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_BLOCK + #define traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_END + #define traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS + #define traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS_FROM_ISR + #define traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS + #define traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS_FROM_ISR + #define traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_DELETE + #define traceEVENT_GROUP_DELETE( xEventGroup ) +#endif + +#ifndef tracePEND_FUNC_CALL + #define tracePEND_FUNC_CALL(xFunctionToPend, pvParameter1, ulParameter2, ret) +#endif + +#ifndef tracePEND_FUNC_CALL_FROM_ISR + #define tracePEND_FUNC_CALL_FROM_ISR(xFunctionToPend, pvParameter1, ulParameter2, ret) +#endif + +#ifndef traceQUEUE_REGISTRY_ADD + #define traceQUEUE_REGISTRY_ADD(xQueue, pcQueueName) +#endif + +#ifndef configGENERATE_RUN_TIME_STATS + #define configGENERATE_RUN_TIME_STATS 0 +#endif + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + #ifndef configUSE_STATS_FORMATTING_FUNCTIONS + #define configUSE_STATS_FORMATTING_FUNCTIONS 1 + #endif + + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. + #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + +#endif /* configGENERATE_RUN_TIME_STATS */ + +#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#endif + +#ifndef configUSE_MALLOC_FAILED_HOOK + #define configUSE_MALLOC_FAILED_HOOK 0 +#endif + +#ifndef portPRIVILEGE_BIT + #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) +#endif + +#ifndef portYIELD_WITHIN_API + #define portYIELD_WITHIN_API portYIELD +#endif + +#ifndef pvPortMallocAligned + #define pvPortMallocAligned( x, puxStackBuffer ) ( ( ( puxStackBuffer ) == NULL ) ? ( pvPortMalloc( ( x ) ) ) : ( puxStackBuffer ) ) +#endif + +#ifndef vPortFreeAligned + #define vPortFreeAligned( pvBlockToFree ) vPortFree( pvBlockToFree ) +#endif + +#ifndef portSUPPRESS_TICKS_AND_SLEEP + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) +#endif + +#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP + #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#endif + +#if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2 + #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2 +#endif + +#ifndef configUSE_TICKLESS_IDLE + #define configUSE_TICKLESS_IDLE 0 +#endif + +#ifndef configPRE_SLEEP_PROCESSING + #define configPRE_SLEEP_PROCESSING( x ) +#endif + +#ifndef configPOST_SLEEP_PROCESSING + #define configPOST_SLEEP_PROCESSING( x ) +#endif + +#ifndef configUSE_QUEUE_SETS + #define configUSE_QUEUE_SETS 0 +#endif + +#ifndef portTASK_USES_FLOATING_POINT + #define portTASK_USES_FLOATING_POINT() +#endif + +#ifndef configUSE_TIME_SLICING + #define configUSE_TIME_SLICING 1 +#endif + +#ifndef configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS + #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 +#endif + +#ifndef configUSE_NEWLIB_REENTRANT + #define configUSE_NEWLIB_REENTRANT 0 +#endif + +#ifndef configUSE_STATS_FORMATTING_FUNCTIONS + #define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#endif + +#ifndef portASSERT_IF_INTERRUPT_PRIORITY_INVALID + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() +#endif + +#ifndef configUSE_TRACE_FACILITY + #define configUSE_TRACE_FACILITY 0 +#endif + +#ifndef mtCOVERAGE_TEST_MARKER + #define mtCOVERAGE_TEST_MARKER() +#endif + +#ifndef portASSERT_IF_IN_ISR + #define portASSERT_IF_IN_ISR() +#endif + +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#endif + +/* Definitions to allow backward compatibility with FreeRTOS versions prior to +V8 if desired. */ +#ifndef configENABLE_BACKWARD_COMPATIBILITY + #define configENABLE_BACKWARD_COMPATIBILITY 1 +#endif + +#if configENABLE_BACKWARD_COMPATIBILITY == 1 + #define eTaskStateGet eTaskGetState + #define portTickType TickType_t + #define xTaskHandle TaskHandle_t + #define xQueueHandle QueueHandle_t + #define xSemaphoreHandle SemaphoreHandle_t + #define xQueueSetHandle QueueSetHandle_t + #define xQueueSetMemberHandle QueueSetMemberHandle_t + #define xTimeOutType TimeOut_t + #define xMemoryRegion MemoryRegion_t + #define xTaskParameters TaskParameters_t + #define xTaskStatusType TaskStatus_t + #define xTimerHandle TimerHandle_t + #define xCoRoutineHandle CoRoutineHandle_t + #define pdTASK_HOOK_CODE TaskHookFunction_t + #define portTICK_RATE_MS portTICK_PERIOD_MS + + /* Backward compatibility within the scheduler code only - these definitions + are not really required but are included for completeness. */ + #define tmrTIMER_CALLBACK TimerCallbackFunction_t + #define pdTASK_CODE TaskFunction_t + #define xListItem ListItem_t + #define xList List_t +#endif /* configENABLE_BACKWARD_COMPATIBILITY */ + +#ifdef __cplusplus +} +#endif + +#endif /* INC_FREERTOS_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/StackMacros.h b/component/os/freertos/freertos_v8.1.2/Source/include/StackMacros.h new file mode 100644 index 0000000..c7cfb28 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/StackMacros.h @@ -0,0 +1,180 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 0 ) + + /* FreeRTOSConfig.h is not set to check for stack overflows. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 0 */ +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 1 ) + + /* FreeRTOSConfig.h is only set to use the first method of + overflow checking. */ + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW > 0 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pxCurrentTCB->pxStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#endif /* STACK_MACROS_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/croutine.h b/component/os/freertos/freertos_v8.1.2/Source/include/croutine.h new file mode 100644 index 0000000..e9a86d0 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/croutine.h @@ -0,0 +1,758 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef CO_ROUTINE_H +#define CO_ROUTINE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include croutine.h" +#endif + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Used to hide the implementation of the co-routine control block. The +control block structure however has to be included in the header due to +the macro implementation of the co-routine functionality. */ +typedef void * CoRoutineHandle_t; + +/* Defines the prototype to which co-routine functions must conform. */ +typedef void (*crCOROUTINE_CODE)( CoRoutineHandle_t, UBaseType_t ); + +typedef struct corCoRoutineControlBlock +{ + crCOROUTINE_CODE pxCoRoutineFunction; + ListItem_t xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ + ListItem_t xEventListItem; /*< List item used to place the CRCB in event lists. */ + UBaseType_t uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ + UBaseType_t uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ + uint16_t uxState; /*< Used internally by the co-routine implementation. */ +} CRCB_t; /* Co-routine control block. Note must be identical in size down to uxPriority with TCB_t. */ + +/** + * croutine. h + *
+ BaseType_t xCoRoutineCreate(
+                                 crCOROUTINE_CODE pxCoRoutineCode,
+                                 UBaseType_t uxPriority,
+                                 UBaseType_t uxIndex
+                               );
+ * + * Create a new co-routine and add it to the list of co-routines that are + * ready to run. + * + * @param pxCoRoutineCode Pointer to the co-routine function. Co-routine + * functions require special syntax - see the co-routine section of the WEB + * documentation for more information. + * + * @param uxPriority The priority with respect to other co-routines at which + * the co-routine will run. + * + * @param uxIndex Used to distinguish between different co-routines that + * execute the same function. See the example below and the co-routine section + * of the WEB documentation for further information. + * + * @return pdPASS if the co-routine was successfully created and added to a ready + * list, otherwise an error code defined with ProjDefs.h. + * + * Example usage: +
+ // Co-routine to be created.
+ void vFlashCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ static const char cLedToFlash[ 2 ] = { 5, 6 };
+ static const TickType_t uxFlashRates[ 2 ] = { 200, 400 };
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // This co-routine just delays for a fixed period, then toggles
+         // an LED.  Two co-routines are created using this function, so
+         // the uxIndex parameter is used to tell the co-routine which
+         // LED to flash and how int32_t to delay.  This assumes xQueue has
+         // already been created.
+         vParTestToggleLED( cLedToFlash[ uxIndex ] );
+         crDELAY( xHandle, uxFlashRates[ uxIndex ] );
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+
+ // Function that creates two co-routines.
+ void vOtherFunction( void )
+ {
+ uint8_t ucParameterToPass;
+ TaskHandle_t xHandle;
+
+     // Create two co-routines at priority 0.  The first is given index 0
+     // so (from the code above) toggles LED 5 every 200 ticks.  The second
+     // is given index 1 so toggles LED 6 every 400 ticks.
+     for( uxIndex = 0; uxIndex < 2; uxIndex++ )
+     {
+         xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
+     }
+ }
+   
+ * \defgroup xCoRoutineCreate xCoRoutineCreate + * \ingroup Tasks + */ +BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, UBaseType_t uxPriority, UBaseType_t uxIndex ); + + +/** + * croutine. h + *
+ void vCoRoutineSchedule( void );
+ * + * Run a co-routine. + * + * vCoRoutineSchedule() executes the highest priority co-routine that is able + * to run. The co-routine will execute until it either blocks, yields or is + * preempted by a task. Co-routines execute cooperatively so one + * co-routine cannot be preempted by another, but can be preempted by a task. + * + * If an application comprises of both tasks and co-routines then + * vCoRoutineSchedule should be called from the idle task (in an idle task + * hook). + * + * Example usage: +
+ // This idle task hook will schedule a co-routine each time it is called.
+ // The rest of the idle task will execute between co-routine calls.
+ void vApplicationIdleHook( void )
+ {
+	vCoRoutineSchedule();
+ }
+
+ // Alternatively, if you do not require any other part of the idle task to
+ // execute, the idle task hook can call vCoRoutineScheduler() within an
+ // infinite loop.
+ void vApplicationIdleHook( void )
+ {
+    for( ;; )
+    {
+        vCoRoutineSchedule();
+    }
+ }
+ 
+ * \defgroup vCoRoutineSchedule vCoRoutineSchedule + * \ingroup Tasks + */ +void vCoRoutineSchedule( void ); + +/** + * croutine. h + *
+ crSTART( CoRoutineHandle_t xHandle );
+ * + * This macro MUST always be called at the start of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static int32_t ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crSTART( pxCRCB ) switch( ( ( CRCB_t * )( pxCRCB ) )->uxState ) { case 0: + +/** + * croutine. h + *
+ crEND();
+ * + * This macro MUST always be called at the end of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static int32_t ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crEND() } + +/* + * These macros are intended for internal use by the co-routine implementation + * only. The macros should not be used directly by application writers. + */ +#define crSET_STATE0( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = (__LINE__ * 2); return; case (__LINE__ * 2): +#define crSET_STATE1( xHandle ) ( ( CRCB_t * )( xHandle ) )->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1): + +/** + * croutine. h + *
+ crDELAY( CoRoutineHandle_t xHandle, TickType_t xTicksToDelay );
+ * + * Delay a co-routine for a fixed period of time. + * + * crDELAY can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * @param xHandle The handle of the co-routine to delay. This is the xHandle + * parameter of the co-routine function. + * + * @param xTickToDelay The number of ticks that the co-routine should delay + * for. The actual amount of time this equates to is defined by + * configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_PERIOD_MS + * can be used to convert ticks to milliseconds. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ // We are to delay for 200ms.
+ static const xTickType xDelayTime = 200 / portTICK_PERIOD_MS;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+        // Delay for 200ms.
+        crDELAY( xHandle, xDelayTime );
+
+        // Do something here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crDELAY crDELAY + * \ingroup Tasks + */ +#define crDELAY( xHandle, xTicksToDelay ) \ + if( ( xTicksToDelay ) > 0 ) \ + { \ + vCoRoutineAddToDelayedList( ( xTicksToDelay ), NULL ); \ + } \ + crSET_STATE0( ( xHandle ) ); + +/** + *
+ crQUEUE_SEND(
+                  CoRoutineHandle_t xHandle,
+                  QueueHandle_t pxQueue,
+                  void *pvItemToQueue,
+                  TickType_t xTicksToWait,
+                  BaseType_t *pxResult
+             )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_SEND can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue on which the data will be posted. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvItemToQueue A pointer to the data being posted onto the queue. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied from pvItemToQueue into the queue + * itself. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for space to become available on the queue, should space not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see example + * below). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully posted onto the queue, otherwise it will be set to an + * error defined within ProjDefs.h. + * + * Example usage: +
+ // Co-routine function that blocks for a fixed period then posts a number onto
+ // a queue.
+ static void prvCoRoutineFlashTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static BaseType_t xNumberToPost = 0;
+ static BaseType_t xResult;
+
+    // Co-routines must begin with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // This assumes the queue has already been created.
+        crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
+
+        if( xResult != pdPASS )
+        {
+            // The message was not posted!
+        }
+
+        // Increment the number to be posted onto the queue.
+        xNumberToPost++;
+
+        // Delay for 100 ticks.
+        crDELAY( xHandle, 100 );
+    }
+
+    // Co-routines must end with a call to crEND().
+    crEND();
+ }
+ * \defgroup crQUEUE_SEND crQUEUE_SEND + * \ingroup Tasks + */ +#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ +{ \ + *( pxResult ) = xQueueCRSend( ( pxQueue) , ( pvItemToQueue) , ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *pxResult = xQueueCRSend( ( pxQueue ), ( pvItemToQueue ), 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_RECEIVE(
+                     CoRoutineHandle_t xHandle,
+                     QueueHandle_t pxQueue,
+                     void *pvBuffer,
+                     TickType_t xTicksToWait,
+                     BaseType_t *pxResult
+                 )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_RECEIVE can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue from which the data will be received. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvBuffer The buffer into which the received item is to be copied. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied into pvBuffer. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for data to become available from the queue, should data not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_PERIOD_MS can be used to convert ticks to milliseconds (see the + * crQUEUE_SEND example). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully retrieved from the queue, otherwise it will be set to + * an error code as defined within ProjDefs.h. + * + * Example usage: +
+ // A co-routine receives the number of an LED to flash from a queue.  It
+ // blocks on the queue until the number is received.
+ static void prvCoRoutineFlashWorkTask( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static BaseType_t xResult;
+ static UBaseType_t uxLEDToFlash;
+
+    // All co-routines must start with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // Wait for data to become available on the queue.
+        crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+        if( xResult == pdPASS )
+        {
+            // We received the LED to flash - flash it!
+            vParTestToggleLED( uxLEDToFlash );
+        }
+    }
+
+    crEND();
+ }
+ * \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ +{ \ + *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), ( xTicksToWait ) ); \ + if( *( pxResult ) == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( ( xHandle ) ); \ + *( pxResult ) = xQueueCRReceive( ( pxQueue) , ( pvBuffer ), 0 ); \ + } \ + if( *( pxResult ) == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( ( xHandle ) ); \ + *( pxResult ) = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            QueueHandle_t pxQueue,
+                            void *pvItemToQueue,
+                            BaseType_t xCoRoutinePreviouslyWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue + * that is being used from within a co-routine. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto + * the same queue multiple times from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. + * + * @return pdTRUE if a co-routine was woken by posting onto the queue. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage: +
+ // A co-routine that blocks on a queue waiting for characters to be received.
+ static void vReceivingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ char cRxedChar;
+ BaseType_t xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Wait for data to become available on the queue.  This assumes the
+         // queue xCommsRxQueue has already been created!
+         crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+         // Was a character received?
+         if( xResult == pdPASS )
+         {
+             // Process the character here.
+         }
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to send characters received on a serial port to
+ // a co-routine.
+ void vUART_ISR( void )
+ {
+ char cRxedChar;
+ BaseType_t xCRWokenByPost = pdFALSE;
+
+     // We loop around reading characters until there are none left in the UART.
+     while( UART_RX_REG_NOT_EMPTY() )
+     {
+         // Obtain the character from the UART.
+         cRxedChar = UART_RX_REG;
+
+         // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
+         // the first time around the loop.  If the post causes a co-routine
+         // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
+         // In this manner we can ensure that if more than one co-routine is
+         // blocked on the queue only one is woken by this ISR no matter how
+         // many characters are posted to the queue.
+         xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
+     }
+ }
+ * \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( ( pxQueue ), ( pvItemToQueue ), ( xCoRoutinePreviouslyWoken ) ) + + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            QueueHandle_t pxQueue,
+                            void *pvBuffer,
+                            BaseType_t * pxCoRoutineWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data + * from a queue that is being used from within a co-routine (a co-routine + * posted to the queue). + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvBuffer A pointer to a buffer into which the received item will be + * placed. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from the queue into + * pvBuffer. + * + * @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become + * available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a + * co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise + * *pxCoRoutineWoken will remain unchanged. + * + * @return pdTRUE an item was successfully received from the queue, otherwise + * pdFALSE. + * + * Example usage: +
+ // A co-routine that posts a character to a queue then blocks for a fixed
+ // period.  The character is incremented each time.
+ static void vSendingCoRoutine( CoRoutineHandle_t xHandle, UBaseType_t uxIndex )
+ {
+ // cChar holds its value while this co-routine is blocked and must therefore
+ // be declared static.
+ static char cCharToTx = 'a';
+ BaseType_t xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Send the next character to the queue.
+         crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
+
+         if( xResult == pdPASS )
+         {
+             // The character was successfully posted to the queue.
+         }
+		 else
+		 {
+			// Could not post the character to the queue.
+		 }
+
+         // Enable the UART Tx interrupt to cause an interrupt in this
+		 // hypothetical UART.  The interrupt will obtain the character
+		 // from the queue and send it.
+		 ENABLE_RX_INTERRUPT();
+
+		 // Increment to the next character then block for a fixed period.
+		 // cCharToTx will maintain its value across the delay as it is
+		 // declared static.
+		 cCharToTx++;
+		 if( cCharToTx > 'x' )
+		 {
+			cCharToTx = 'a';
+		 }
+		 crDELAY( 100 );
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to receive characters to send on a UART.
+ void vUART_ISR( void )
+ {
+ char cCharToTx;
+ BaseType_t xCRWokenByPost = pdFALSE;
+
+     while( UART_TX_REG_EMPTY() )
+     {
+         // Are there any characters in the queue waiting to be sent?
+		 // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
+		 // is woken by the post - ensuring that only a single co-routine is
+		 // woken no matter how many times we go around this loop.
+         if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
+		 {
+			 SEND_CHARACTER( cCharToTx );
+		 }
+     }
+ }
+ * \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( ( pxQueue ), ( pvBuffer ), ( pxCoRoutineWoken ) ) + +/* + * This function is intended for internal use by the co-routine macros only. + * The macro nature of the co-routine implementation requires that the + * prototype appears here. The function should not be used by application + * writers. + * + * Removes the current co-routine from its ready list and places it in the + * appropriate delayed list. + */ +void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay, List_t *pxEventList ); + +/* + * This function is intended for internal use by the queue implementation only. + * The function should not be used by application writers. + * + * Removes the highest priority co-routine from the event list and places it in + * the pending ready list. + */ +BaseType_t xCoRoutineRemoveFromEventList( const List_t *pxEventList ); + +#ifdef __cplusplus +} +#endif + +#endif /* CO_ROUTINE_H */ diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/event_groups.h b/component/os/freertos/freertos_v8.1.2/Source/include/event_groups.h new file mode 100644 index 0000000..51e7ea2 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/event_groups.h @@ -0,0 +1,726 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef EVENT_GROUPS_H +#define EVENT_GROUPS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include event_groups.h" +#endif + +#include "timers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * An event group is a collection of bits to which an application can assign a + * meaning. For example, an application may create an event group to convey + * the status of various CAN bus related events in which bit 0 might mean "A CAN + * message has been received and is ready for processing", bit 1 might mean "The + * application has queued a message that is ready for sending onto the CAN + * network", and bit 2 might mean "It is time to send a SYNC message onto the + * CAN network" etc. A task can then test the bit values to see which events + * are active, and optionally enter the Blocked state to wait for a specified + * bit or a group of specified bits to be active. To continue the CAN bus + * example, a CAN controlling task can enter the Blocked state (and therefore + * not consume any processing time) until either bit 0, bit 1 or bit 2 are + * active, at which time the bit that was actually active would inform the task + * which action it had to take (process a received message, send a message, or + * send a SYNC). + * + * The event groups implementation contains intelligence to avoid race + * conditions that would otherwise occur were an application to use a simple + * variable for the same purpose. This is particularly important with respect + * to when a bit within an event group is to be cleared, and when bits have to + * be set and then tested atomically - as is the case where event groups are + * used to create a synchronisation point between multiple tasks (a + * 'rendezvous'). + * + * \defgroup EventGroup + */ + + + +/** + * event_groups.h + * + * Type by which event groups are referenced. For example, a call to + * xEventGroupCreate() returns an EventGroupHandle_t variable that can then + * be used as a parameter to other event group functions. + * + * \defgroup EventGroupHandle_t EventGroupHandle_t + * \ingroup EventGroup + */ +typedef void * EventGroupHandle_t; + +/* + * The type that holds event bits always matches TickType_t - therefore the + * number of bits it holds is set by configUSE_16_BIT_TICKS (16 bits if set to 1, + * 32 bits if set to 0. + * + * \defgroup EventBits_t EventBits_t + * \ingroup EventGroup + */ +typedef TickType_t EventBits_t; + +/** + * event_groups.h + *
+ EventGroupHandle_t xEventGroupCreate( void );
+ 
+ * + * Create a new event group. This function cannot be called from an interrupt. + * + * Although event groups are not related to ticks, for internal implementation + * reasons the number of bits available for use in an event group is dependent + * on the configUSE_16_BIT_TICKS setting in FreeRTOSConfig.h. If + * configUSE_16_BIT_TICKS is 1 then each event group contains 8 usable bits (bit + * 0 to bit 7). If configUSE_16_BIT_TICKS is set to 0 then each event group has + * 24 usable bits (bit 0 to bit 23). The EventBits_t type is used to store + * event bits within an event group. + * + * @return If the event group was created then a handle to the event group is + * returned. If there was insufficient FreeRTOS heap available to create the + * event group then NULL is returned. See http://www.freertos.org/a00111.html + * + * Example usage: +
+	// Declare a variable to hold the created event group.
+	EventGroupHandle_t xCreatedEventGroup;
+
+	// Attempt to create the event group.
+	xCreatedEventGroup = xEventGroupCreate();
+
+	// Was the event group created successfully?
+	if( xCreatedEventGroup == NULL )
+	{
+		// The event group was not created because there was insufficient
+		// FreeRTOS heap available.
+	}
+	else
+	{
+		// The event group was created.
+	}
+   
+ * \defgroup xEventGroupCreate xEventGroupCreate + * \ingroup EventGroup + */ +EventGroupHandle_t xEventGroupCreate( void ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+	EventBits_t xEventGroupWaitBits( 	EventGroupHandle_t xEventGroup,
+										const EventBits_t uxBitsToWaitFor,
+										const BaseType_t xClearOnExit,
+										const BaseType_t xWaitForAllBits,
+										const TickType_t xTicksToWait );
+ 
+ * + * [Potentially] block to wait for one or more bits to be set within a + * previously created event group. + * + * This function cannot be called from an interrupt. + * + * @param xEventGroup The event group in which the bits are being tested. The + * event group must have previously been created using a call to + * xEventGroupCreate(). + * + * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test + * inside the event group. For example, to wait for bit 0 and/or bit 2 set + * uxBitsToWaitFor to 0x05. To wait for bits 0 and/or bit 1 and/or bit 2 set + * uxBitsToWaitFor to 0x07. Etc. + * + * @param xClearOnExit If xClearOnExit is set to pdTRUE then any bits within + * uxBitsToWaitFor that are set within the event group will be cleared before + * xEventGroupWaitBits() returns if the wait condition was met (if the function + * returns for a reason other than a timeout). If xClearOnExit is set to + * pdFALSE then the bits set in the event group are not altered when the call to + * xEventGroupWaitBits() returns. + * + * @param xWaitForAllBits If xWaitForAllBits is set to pdTRUE then + * xEventGroupWaitBits() will return when either all the bits in uxBitsToWaitFor + * are set or the specified block time expires. If xWaitForAllBits is set to + * pdFALSE then xEventGroupWaitBits() will return when any one of the bits set + * in uxBitsToWaitFor is set or the specified block time expires. The block + * time is specified by the xTicksToWait parameter. + * + * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait + * for one/all (depending on the xWaitForAllBits value) of the bits specified by + * uxBitsToWaitFor to become set. + * + * @return The value of the event group at the time either the bits being waited + * for became set, or the block time expired. Test the return value to know + * which bits were set. If xEventGroupWaitBits() returned because its timeout + * expired then not all the bits being waited for will be set. If + * xEventGroupWaitBits() returned because the bits it was waiting for were set + * then the returned value is the event group value before any bits were + * automatically cleared in the case that xClearOnExit parameter was set to + * pdTRUE. + * + * Example usage: +
+   #define BIT_0	( 1 << 0 )
+   #define BIT_4	( 1 << 4 )
+
+   void aFunction( EventGroupHandle_t xEventGroup )
+   {
+   EventBits_t uxBits;
+   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+
+		// Wait a maximum of 100ms for either bit 0 or bit 4 to be set within
+		// the event group.  Clear the bits before exiting.
+		uxBits = xEventGroupWaitBits(
+					xEventGroup,	// The event group being tested.
+					BIT_0 | BIT_4,	// The bits within the event group to wait for.
+					pdTRUE,			// BIT_0 and BIT_4 should be cleared before returning.
+					pdFALSE,		// Don't wait for both bits, either bit will do.
+					xTicksToWait );	// Wait a maximum of 100ms for either bit to be set.
+
+		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+		{
+			// xEventGroupWaitBits() returned because both bits were set.
+		}
+		else if( ( uxBits & BIT_0 ) != 0 )
+		{
+			// xEventGroupWaitBits() returned because just BIT_0 was set.
+		}
+		else if( ( uxBits & BIT_4 ) != 0 )
+		{
+			// xEventGroupWaitBits() returned because just BIT_4 was set.
+		}
+		else
+		{
+			// xEventGroupWaitBits() returned because xTicksToWait ticks passed
+			// without either BIT_0 or BIT_4 becoming set.
+		}
+   }
+   
+ * \defgroup xEventGroupWaitBits xEventGroupWaitBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+	EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear );
+ 
+ * + * Clear bits within an event group. This function cannot be called from an + * interrupt. + * + * @param xEventGroup The event group in which the bits are to be cleared. + * + * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear + * in the event group. For example, to clear bit 3 only, set uxBitsToClear to + * 0x08. To clear bit 3 and bit 0 set uxBitsToClear to 0x09. + * + * @return The value of the event group before the specified bits were cleared. + * + * Example usage: +
+   #define BIT_0	( 1 << 0 )
+   #define BIT_4	( 1 << 4 )
+
+   void aFunction( EventGroupHandle_t xEventGroup )
+   {
+   EventBits_t uxBits;
+
+		// Clear bit 0 and bit 4 in xEventGroup.
+		uxBits = xEventGroupClearBits(
+								xEventGroup,	// The event group being updated.
+								BIT_0 | BIT_4 );// The bits being cleared.
+
+		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+		{
+			// Both bit 0 and bit 4 were set before xEventGroupClearBits() was
+			// called.  Both will now be clear (not set).
+		}
+		else if( ( uxBits & BIT_0 ) != 0 )
+		{
+			// Bit 0 was set before xEventGroupClearBits() was called.  It will
+			// now be clear.
+		}
+		else if( ( uxBits & BIT_4 ) != 0 )
+		{
+			// Bit 4 was set before xEventGroupClearBits() was called.  It will
+			// now be clear.
+		}
+		else
+		{
+			// Neither bit 0 nor bit 4 were set in the first place.
+		}
+   }
+   
+ * \defgroup xEventGroupClearBits xEventGroupClearBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+	BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
+ 
+ * + * A version of xEventGroupClearBits() that can be called from an interrupt. + * + * Setting bits in an event group is not a deterministic operation because there + * are an unknown number of tasks that may be waiting for the bit or bits being + * set. FreeRTOS does not allow nondeterministic operations to be performed + * while interrupts are disabled, so protects event groups that are accessed + * from tasks by suspending the scheduler rather than disabling interrupts. As + * a result event groups cannot be accessed directly from an interrupt service + * routine. Therefore xEventGroupClearBitsFromISR() sends a message to the + * timer task to have the clear operation performed in the context of the timer + * task. + * + * @param xEventGroup The event group in which the bits are to be cleared. + * + * @param uxBitsToClear A bitwise value that indicates the bit or bits to clear. + * For example, to clear bit 3 only, set uxBitsToClear to 0x08. To clear bit 3 + * and bit 0 set uxBitsToClear to 0x09. + * + * @return If the request to execute the function was posted successfully then + * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned + * if the timer service queue was full. + * + * Example usage: +
+   #define BIT_0	( 1 << 0 )
+   #define BIT_4	( 1 << 4 )
+
+   // An event group which it is assumed has already been created by a call to
+   // xEventGroupCreate().
+   EventGroupHandle_t xEventGroup;
+
+   void anInterruptHandler( void )
+   {
+		// Clear bit 0 and bit 4 in xEventGroup.
+		xResult = xEventGroupClearBitsFromISR(
+							xEventGroup,	 // The event group being updated.
+							BIT_0 | BIT_4 ); // The bits being set.
+
+		if( xResult == pdPASS )
+		{
+			// The message was posted successfully.
+		}
+  }
+   
+ * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * \ingroup EventGroup + */ +#if( configUSE_TRACE_FACILITY == 1 ) + BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); +#else + #define xEventGroupClearBitsFromISR( xEventGroup, uxBitsToClear ) xTimerPendFunctionCallFromISR( vEventGroupClearBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToClear, NULL ) +#endif + +/** + * event_groups.h + *
+	EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet );
+ 
+ * + * Set bits within an event group. + * This function cannot be called from an interrupt. xEventGroupSetBitsFromISR() + * is a version that can be called from an interrupt. + * + * Setting bits in an event group will automatically unblock tasks that are + * blocked waiting for the bits. + * + * @param xEventGroup The event group in which the bits are to be set. + * + * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. + * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 + * and bit 0 set uxBitsToSet to 0x09. + * + * @return The value of the event group at the time the call to + * xEventGroupSetBits() returns. There are two reasons why the returned value + * might have the bits specified by the uxBitsToSet parameter cleared. First, + * if setting a bit results in a task that was waiting for the bit leaving the + * blocked state then it is possible the bit will be cleared automatically + * (see the xClearBitOnExit parameter of xEventGroupWaitBits()). Second, any + * unblocked (or otherwise Ready state) task that has a priority above that of + * the task that called xEventGroupSetBits() will execute and may change the + * event group value before the call to xEventGroupSetBits() returns. + * + * Example usage: +
+   #define BIT_0	( 1 << 0 )
+   #define BIT_4	( 1 << 4 )
+
+   void aFunction( EventGroupHandle_t xEventGroup )
+   {
+   EventBits_t uxBits;
+
+		// Set bit 0 and bit 4 in xEventGroup.
+		uxBits = xEventGroupSetBits(
+							xEventGroup,	// The event group being updated.
+							BIT_0 | BIT_4 );// The bits being set.
+
+		if( ( uxBits & ( BIT_0 | BIT_4 ) ) == ( BIT_0 | BIT_4 ) )
+		{
+			// Both bit 0 and bit 4 remained set when the function returned.
+		}
+		else if( ( uxBits & BIT_0 ) != 0 )
+		{
+			// Bit 0 remained set when the function returned, but bit 4 was
+			// cleared.  It might be that bit 4 was cleared automatically as a
+			// task that was waiting for bit 4 was removed from the Blocked
+			// state.
+		}
+		else if( ( uxBits & BIT_4 ) != 0 )
+		{
+			// Bit 4 remained set when the function returned, but bit 0 was
+			// cleared.  It might be that bit 0 was cleared automatically as a
+			// task that was waiting for bit 0 was removed from the Blocked
+			// state.
+		}
+		else
+		{
+			// Neither bit 0 nor bit 4 remained set.  It might be that a task
+			// was waiting for both of the bits to be set, and the bits were
+			// cleared as the task left the Blocked state.
+		}
+   }
+   
+ * \defgroup xEventGroupSetBits xEventGroupSetBits + * \ingroup EventGroup + */ +EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ) PRIVILEGED_FUNCTION; + +/** + * event_groups.h + *
+	BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken );
+ 
+ * + * A version of xEventGroupSetBits() that can be called from an interrupt. + * + * Setting bits in an event group is not a deterministic operation because there + * are an unknown number of tasks that may be waiting for the bit or bits being + * set. FreeRTOS does not allow nondeterministic operations to be performed in + * interrupts or from critical sections. Therefore xEventGroupSetBitFromISR() + * sends a message to the timer task to have the set operation performed in the + * context of the timer task - where a scheduler lock is used in place of a + * critical section. + * + * @param xEventGroup The event group in which the bits are to be set. + * + * @param uxBitsToSet A bitwise value that indicates the bit or bits to set. + * For example, to set bit 3 only, set uxBitsToSet to 0x08. To set bit 3 + * and bit 0 set uxBitsToSet to 0x09. + * + * @param pxHigherPriorityTaskWoken As mentioned above, calling this function + * will result in a message being sent to the timer daemon task. If the + * priority of the timer daemon task is higher than the priority of the + * currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE by + * xEventGroupSetBitsFromISR(), indicating that a context switch should be + * requested before the interrupt exits. For that reason + * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the + * example code below. + * + * @return If the request to execute the function was posted successfully then + * pdPASS is returned, otherwise pdFALSE is returned. pdFALSE will be returned + * if the timer service queue was full. + * + * Example usage: +
+   #define BIT_0	( 1 << 0 )
+   #define BIT_4	( 1 << 4 )
+
+   // An event group which it is assumed has already been created by a call to
+   // xEventGroupCreate().
+   EventGroupHandle_t xEventGroup;
+
+   void anInterruptHandler( void )
+   {
+   BaseType_t xHigherPriorityTaskWoken, xResult;
+
+		// xHigherPriorityTaskWoken must be initialised to pdFALSE.
+		xHigherPriorityTaskWoken = pdFALSE;
+
+		// Set bit 0 and bit 4 in xEventGroup.
+		xResult = xEventGroupSetBitsFromISR(
+							xEventGroup,	// The event group being updated.
+							BIT_0 | BIT_4   // The bits being set.
+							&xHigherPriorityTaskWoken );
+
+		// Was the message posted successfully?
+		if( xResult == pdPASS )
+		{
+			// If xHigherPriorityTaskWoken is now set to pdTRUE then a context
+			// switch should be requested.  The macro used is port specific and 
+			// will be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - 
+			// refer to the documentation page for the port being used.
+			portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
+		}
+  }
+   
+ * \defgroup xEventGroupSetBitsFromISR xEventGroupSetBitsFromISR + * \ingroup EventGroup + */ +#if( configUSE_TRACE_FACILITY == 1 ) + BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken ); +#else + #define xEventGroupSetBitsFromISR( xEventGroup, uxBitsToSet, pxHigherPriorityTaskWoken ) xTimerPendFunctionCallFromISR( vEventGroupSetBitsCallback, ( void * ) xEventGroup, ( uint32_t ) uxBitsToSet, pxHigherPriorityTaskWoken ) +#endif + +/** + * event_groups.h + *
+	EventBits_t xEventGroupSync(	EventGroupHandle_t xEventGroup,
+									const EventBits_t uxBitsToSet,
+									const EventBits_t uxBitsToWaitFor,
+									TickType_t xTicksToWait );
+ 
+ * + * Atomically set bits within an event group, then wait for a combination of + * bits to be set within the same event group. This functionality is typically + * used to synchronise multiple tasks, where each task has to wait for the other + * tasks to reach a synchronisation point before proceeding. + * + * This function cannot be used from an interrupt. + * + * The function will return before its block time expires if the bits specified + * by the uxBitsToWait parameter are set, or become set within that time. In + * this case all the bits specified by uxBitsToWait will be automatically + * cleared before the function returns. + * + * @param xEventGroup The event group in which the bits are being tested. The + * event group must have previously been created using a call to + * xEventGroupCreate(). + * + * @param uxBitsToSet The bits to set in the event group before determining + * if, and possibly waiting for, all the bits specified by the uxBitsToWait + * parameter are set. + * + * @param uxBitsToWaitFor A bitwise value that indicates the bit or bits to test + * inside the event group. For example, to wait for bit 0 and bit 2 set + * uxBitsToWaitFor to 0x05. To wait for bits 0 and bit 1 and bit 2 set + * uxBitsToWaitFor to 0x07. Etc. + * + * @param xTicksToWait The maximum amount of time (specified in 'ticks') to wait + * for all of the bits specified by uxBitsToWaitFor to become set. + * + * @return The value of the event group at the time either the bits being waited + * for became set, or the block time expired. Test the return value to know + * which bits were set. If xEventGroupSync() returned because its timeout + * expired then not all the bits being waited for will be set. If + * xEventGroupSync() returned because all the bits it was waiting for were + * set then the returned value is the event group value before any bits were + * automatically cleared. + * + * Example usage: +
+ // Bits used by the three tasks.
+ #define TASK_0_BIT		( 1 << 0 )
+ #define TASK_1_BIT		( 1 << 1 )
+ #define TASK_2_BIT		( 1 << 2 )
+
+ #define ALL_SYNC_BITS ( TASK_0_BIT | TASK_1_BIT | TASK_2_BIT )
+
+ // Use an event group to synchronise three tasks.  It is assumed this event
+ // group has already been created elsewhere.
+ EventGroupHandle_t xEventBits;
+
+ void vTask0( void *pvParameters )
+ {
+ EventBits_t uxReturn;
+ TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
+
+	 for( ;; )
+	 {
+		// Perform task functionality here.
+
+		// Set bit 0 in the event flag to note this task has reached the
+		// sync point.  The other two tasks will set the other two bits defined
+		// by ALL_SYNC_BITS.  All three tasks have reached the synchronisation
+		// point when all the ALL_SYNC_BITS are set.  Wait a maximum of 100ms
+		// for this to happen.
+		uxReturn = xEventGroupSync( xEventBits, TASK_0_BIT, ALL_SYNC_BITS, xTicksToWait );
+
+		if( ( uxReturn & ALL_SYNC_BITS ) == ALL_SYNC_BITS )
+		{
+			// All three tasks reached the synchronisation point before the call
+			// to xEventGroupSync() timed out.
+		}
+	}
+ }
+
+ void vTask1( void *pvParameters )
+ {
+	 for( ;; )
+	 {
+		// Perform task functionality here.
+
+		// Set bit 1 in the event flag to note this task has reached the
+		// synchronisation point.  The other two tasks will set the other two
+		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+		// indefinitely for this to happen.
+		xEventGroupSync( xEventBits, TASK_1_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+
+		// xEventGroupSync() was called with an indefinite block time, so
+		// this task will only reach here if the syncrhonisation was made by all
+		// three tasks, so there is no need to test the return value.
+	 }
+ }
+
+ void vTask2( void *pvParameters )
+ {
+	 for( ;; )
+	 {
+		// Perform task functionality here.
+
+		// Set bit 2 in the event flag to note this task has reached the
+		// synchronisation point.  The other two tasks will set the other two
+		// bits defined by ALL_SYNC_BITS.  All three tasks have reached the
+		// synchronisation point when all the ALL_SYNC_BITS are set.  Wait
+		// indefinitely for this to happen.
+		xEventGroupSync( xEventBits, TASK_2_BIT, ALL_SYNC_BITS, portMAX_DELAY );
+
+		// xEventGroupSync() was called with an indefinite block time, so
+		// this task will only reach here if the syncrhonisation was made by all
+		// three tasks, so there is no need to test the return value.
+	}
+ }
+
+ 
+ * \defgroup xEventGroupSync xEventGroupSync + * \ingroup EventGroup + */ +EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + + +/** + * event_groups.h + *
+	EventBits_t xEventGroupGetBits( EventGroupHandle_t xEventGroup );
+ 
+ * + * Returns the current value of the bits in an event group. This function + * cannot be used from an interrupt. + * + * @param xEventGroup The event group being queried. + * + * @return The event group bits at the time xEventGroupGetBits() was called. + * + * \defgroup xEventGroupGetBits xEventGroupGetBits + * \ingroup EventGroup + */ +#define xEventGroupGetBits( xEventGroup ) xEventGroupClearBits( xEventGroup, 0 ) + +/** + * event_groups.h + *
+	EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup );
+ 
+ * + * A version of xEventGroupGetBits() that can be called from an ISR. + * + * @param xEventGroup The event group being queried. + * + * @return The event group bits at the time xEventGroupGetBitsFromISR() was called. + * + * \defgroup xEventGroupGetBitsFromISR xEventGroupGetBitsFromISR + * \ingroup EventGroup + */ +EventBits_t xEventGroupGetBitsFromISR( EventGroupHandle_t xEventGroup ); + +/** + * event_groups.h + *
+	void xEventGroupDelete( EventGroupHandle_t xEventGroup );
+ 
+ * + * Delete an event group that was previously created by a call to + * xEventGroupCreate(). Tasks that are blocked on the event group will be + * unblocked and obtain 0 as the event group's value. + * + * @param xEventGroup The event group being deleted. + */ +void vEventGroupDelete( EventGroupHandle_t xEventGroup ); + +/* For internal use only. */ +void vEventGroupSetBitsCallback( void *pvEventGroup, const uint32_t ulBitsToSet ); +void vEventGroupClearBitsCallback( void *pvEventGroup, const uint32_t ulBitsToClear ); + +#if (configUSE_TRACE_FACILITY == 1) + UBaseType_t uxEventGroupGetNumber( void* xEventGroup ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* EVENT_GROUPS_H */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/list.h b/component/os/freertos/freertos_v8.1.2/Source/include/list.h new file mode 100644 index 0000000..c08b11d --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/list.h @@ -0,0 +1,403 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * This is the list implementation used by the scheduler. While it is tailored + * heavily for the schedulers needs, it is also available for use by + * application code. + * + * list_ts can only store pointers to list_item_ts. Each ListItem_t contains a + * numeric value (xItemValue). Most of the time the lists are sorted in + * descending item value order. + * + * Lists are created already containing one list item. The value of this + * item is the maximum possible that can be stored, it is therefore always at + * the end of the list and acts as a marker. The list member pxHead always + * points to this marker - even though it is at the tail of the list. This + * is because the tail contains a wrap back pointer to the true head of + * the list. + * + * In addition to it's value, each list item contains a pointer to the next + * item in the list (pxNext), a pointer to the list it is in (pxContainer) + * and a pointer to back to the object that contains it. These later two + * pointers are included for efficiency of list manipulation. There is + * effectively a two way link between the object containing the list item and + * the list item itself. + * + * + * \page ListIntroduction List Implementation + * \ingroup FreeRTOSIntro + */ + + +#ifndef LIST_H +#define LIST_H + +/* + * The list structure members are modified from within interrupts, and therefore + * by rights should be declared volatile. However, they are only modified in a + * functionally atomic way (within critical sections of with the scheduler + * suspended) and are either passed by reference into a function or indexed via + * a volatile variable. Therefore, in all use cases tested so far, the volatile + * qualifier can be omitted in order to provide a moderate performance + * improvement without adversely affecting functional behaviour. The assembly + * instructions generated by the IAR, ARM and GCC compilers when the respective + * compiler's options were set for maximum optimisation has been inspected and + * deemed to be as intended. That said, as compiler technology advances, and + * especially if aggressive cross module optimisation is used (a use case that + * has not been exercised to any great extend) then it is feasible that the + * volatile qualifier will be needed for correct optimisation. It is expected + * that a compiler removing essential code because, without the volatile + * qualifier on the list structure members and with aggressive cross module + * optimisation, the compiler deemed the code unnecessary will result in + * complete and obvious failure of the scheduler. If this is ever experienced + * then the volatile qualifier can be inserted in the relevant places within the + * list structures by simply defining configLIST_VOLATILE to volatile in + * FreeRTOSConfig.h (as per the example at the bottom of this comment block). + * If configLIST_VOLATILE is not defined then the preprocessor directives below + * will simply #define configLIST_VOLATILE away completely. + * + * To use volatile list structure members then add the following line to + * FreeRTOSConfig.h (without the quotes): + * "#define configLIST_VOLATILE volatile" + */ +#ifndef configLIST_VOLATILE + #define configLIST_VOLATILE +#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */ + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Definition of the only type of object that a list can contain. + */ +struct xLIST_ITEM +{ + configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + void * configLIST_VOLATILE pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ +}; +typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ + +struct xMINI_LIST_ITEM +{ + configLIST_VOLATILE TickType_t xItemValue; + struct xLIST_ITEM * configLIST_VOLATILE pxNext; + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; +}; +typedef struct xMINI_LIST_ITEM MiniListItem_t; + +/* + * Definition of the type of queue used by the scheduler. + */ +typedef struct xLIST +{ + configLIST_VOLATILE UBaseType_t uxNumberOfItems; + ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ + MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ +} List_t; + +/* + * Access macro to set the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) ) + +/* + * Access macro to get the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) + +/* + * Access macro to set the value of the list item. In most cases the value is + * used to sort the list in descending order. + * + * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) ) + +/* + * Access macro to retrieve the value of the list item. The value can + * represent anything - for example the priority of a task, or the time at + * which a task should be unblocked. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) + +/* + * Access macro to retrieve the value of the list item at the head of a given + * list. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue ) + +/* + * Return the list item at the head of the list. + * + * \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) + +/* + * Return the list item at the head of the list. + * + * \page listGET_NEXT listGET_NEXT + * \ingroup LinkedList + */ +#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext ) + +/* + * Return the list item that marks the end of the list + * + * \page listGET_END_MARKER listGET_END_MARKER + * \ingroup LinkedList + */ +#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) ) + +/* + * Access macro to determine if a list contains any items. The macro will + * only have the value true if the list is empty. + * + * \page listLIST_IS_EMPTY listLIST_IS_EMPTY + * \ingroup LinkedList + */ +#define listLIST_IS_EMPTY( pxList ) ( ( BaseType_t ) ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ) + +/* + * Access macro to return the number of items in the list. + */ +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) + +/* + * Access function to obtain the owner of the next entry in a list. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list + * and returns that entry's pxOwner parameter. Using multiple calls to this + * function it is therefore possible to move through every item contained in + * a list. + * + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxTCB pxTCB is set to the address of the owner of the next list item. + * @param pxList The list from which the next item owner is to be returned. + * + * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ +{ \ +List_t * const pxConstList = ( pxList ); \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ +} + + +/* + * Access function to obtain the owner of the first entry in a list. Lists + * are normally sorted in ascending item value order. + * + * This function returns the pxOwner member of the first item in the list. + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the owner of the head item is to be + * returned. + * + * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner ) + +/* + * Check to see if a list item is within a list. The list item maintains a + * "container" pointer that points to the list it is in. All this macro does + * is check to see if the container and the list match. + * + * @param pxList The list we want to know if the list item is within. + * @param pxListItem The list item we want to know if is in the list. + * @return pdTRUE if the list item is in the list, otherwise pdFALSE. + */ +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) ) + +/* + * Return the list a list item is contained within (referenced from). + * + * @param pxListItem The list item being queried. + * @return A pointer to the List_t object that references the pxListItem + */ +#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pvContainer ) + +/* + * This provides a crude means of knowing if a list has been initialised, as + * pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise() + * function. + */ +#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY ) + +/* + * Must be called before a list is used! This initialises all the members + * of the list structure and inserts the xListEnd item into the list as a + * marker to the back of the list. + * + * @param pxList Pointer to the list being initialised. + * + * \page vListInitialise vListInitialise + * \ingroup LinkedList + */ +void vListInitialise( List_t * const pxList ); + +/* + * Must be called before a list item is used. This sets the list container to + * null so the item does not think that it is already contained in a list. + * + * @param pxItem Pointer to the list item being initialised. + * + * \page vListInitialiseItem vListInitialiseItem + * \ingroup LinkedList + */ +void vListInitialiseItem( ListItem_t * const pxItem ); + +/* + * Insert a list item into a list. The item will be inserted into the list in + * a position determined by its item value (descending item value order). + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The item that is to be placed in the list. + * + * \page vListInsert vListInsert + * \ingroup LinkedList + */ +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ); + +/* + * Insert a list item into a list. The item will be inserted in a position + * such that it will be the last item within the list returned by multiple + * calls to listGET_OWNER_OF_NEXT_ENTRY. + * + * The list member pvIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pvIndex to the next item in the list. + * Placing an item in a list using vListInsertEnd effectively places the item + * in the list position pointed to by pvIndex. This means that every other + * item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before + * the pvIndex parameter again points to the item being inserted. + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The list item to be inserted into the list. + * + * \page vListInsertEnd vListInsertEnd + * \ingroup LinkedList + */ +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ); + +/* + * Remove an item from a list. The list item has a pointer to the list that + * it is in, so only the list item need be passed into the function. + * + * @param uxListRemove The item to be removed. The item will remove itself from + * the list pointed to by it's pxContainer parameter. + * + * @return The number of items that remain in the list after the list item has + * been removed. + * + * \page uxListRemove uxListRemove + * \ingroup LinkedList + */ +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/mpu_wrappers.h b/component/os/freertos/freertos_v8.1.2/Source/include/mpu_wrappers.h new file mode 100644 index 0000000..6777be5 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/mpu_wrappers.h @@ -0,0 +1,153 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef MPU_WRAPPERS_H +#define MPU_WRAPPERS_H + +/* This file redefines API functions to be called through a wrapper macro, but +only for ports that are using the MPU. */ +#ifdef portUSING_MPU_WRAPPERS + + /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is + included from queue.c or task.c to prevent it from having an effect within + those files. */ + #ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + + #define xTaskGenericCreate MPU_xTaskGenericCreate + #define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions + #define vTaskDelete MPU_vTaskDelete + #define vTaskDelayUntil MPU_vTaskDelayUntil + #define vTaskDelay MPU_vTaskDelay + #define uxTaskPriorityGet MPU_uxTaskPriorityGet + #define vTaskPrioritySet MPU_vTaskPrioritySet + #define eTaskGetState MPU_eTaskGetState + #define vTaskSuspend MPU_vTaskSuspend + #define vTaskResume MPU_vTaskResume + #define vTaskSuspendAll MPU_vTaskSuspendAll + #define xTaskResumeAll MPU_xTaskResumeAll + #define xTaskGetTickCount MPU_xTaskGetTickCount + #define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks + #define vTaskList MPU_vTaskList + #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag + #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag + #define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook + #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle + #define xTaskGetSchedulerState MPU_xTaskGetSchedulerState + #define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle + #define uxTaskGetSystemState MPU_uxTaskGetSystemState + + #define xQueueGenericCreate MPU_xQueueGenericCreate + #define xQueueCreateMutex MPU_xQueueCreateMutex + #define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive + #define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive + #define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore + #define xQueueGenericSend MPU_xQueueGenericSend + #define xQueueAltGenericSend MPU_xQueueAltGenericSend + #define xQueueAltGenericReceive MPU_xQueueAltGenericReceive + #define xQueueGenericReceive MPU_xQueueGenericReceive + #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting + #define vQueueDelete MPU_vQueueDelete + #define xQueueGenericReset MPU_xQueueGenericReset + #define xQueueCreateSet MPU_xQueueCreateSet + #define xQueueSelectFromSet MPU_xQueueSelectFromSet + #define xQueueAddToSet MPU_xQueueAddToSet + #define xQueueRemoveFromSet MPU_xQueueRemoveFromSet + #define xQueuePeekFromISR MPU_xQueuePeekFromISR + #define xQueueGetMutexHolder MPU_xQueueGetMutexHolder + + #define pvPortMalloc MPU_pvPortMalloc + #define vPortFree MPU_vPortFree + #define xPortGetFreeHeapSize MPU_xPortGetFreeHeapSize + #define vPortInitialiseBlocks MPU_vPortInitialiseBlocks + + #if configQUEUE_REGISTRY_SIZE > 0 + #define vQueueAddToRegistry MPU_vQueueAddToRegistry + #define vQueueUnregisterQueue MPU_vQueueUnregisterQueue + #endif + + /* Remove the privileged function macro. */ + #define PRIVILEGED_FUNCTION + + #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + + /* Ensure API functions go in the privileged execution section. */ + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + + #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +#else /* portUSING_MPU_WRAPPERS */ + + #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA + #define portUSING_MPU_WRAPPERS 0 + +#endif /* portUSING_MPU_WRAPPERS */ + + +#endif /* MPU_WRAPPERS_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/portable.h b/component/os/freertos/freertos_v8.1.2/Source/include/portable.h new file mode 100644 index 0000000..f63e99f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/portable.h @@ -0,0 +1,426 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Include the macro file relevant to the port being used. +NOTE: The following definitions are *DEPRECATED* as it is preferred to instead +just add the path to the correct portmacro.h header file to the compiler's +include path. */ +#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT + #include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT + #include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef GCC_MEGA_AVR + #include "../portable/GCC/ATMega323/portmacro.h" +#endif + +#ifdef IAR_MEGA_AVR + #include "../portable/IAR/ATMega323/portmacro.h" +#endif + +#ifdef MPLAB_PIC24_PORT + #include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h" +#endif + +#ifdef MPLAB_DSPIC_PORT + #include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h" +#endif + +#ifdef MPLAB_PIC18F_PORT + #include "../../Source/portable/MPLAB/PIC18F/portmacro.h" +#endif + +#ifdef MPLAB_PIC32MX_PORT + #include "../../Source/portable/MPLAB/PIC32MX/portmacro.h" +#endif + +#ifdef _FEDPICC + #include "libFreeRTOS/Include/portmacro.h" +#endif + +#ifdef SDCC_CYGNAL + #include "../../Source/portable/SDCC/Cygnal/portmacro.h" +#endif + +#ifdef GCC_ARM7 + #include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h" +#endif + +#ifdef GCC_ARM7_ECLIPSE + #include "portmacro.h" +#endif + +#ifdef ROWLEY_LPC23xx + #include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h" +#endif + +#ifdef IAR_MSP430 + #include "..\..\Source\portable\IAR\MSP430\portmacro.h" +#endif + +#ifdef GCC_MSP430 + #include "../../Source/portable/GCC/MSP430F449/portmacro.h" +#endif + +#ifdef ROWLEY_MSP430 + #include "../../Source/portable/Rowley/MSP430F449/portmacro.h" +#endif + +#ifdef ARM7_LPC21xx_KEIL_RVDS + #include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h" +#endif + +#ifdef SAM7_GCC + #include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h" +#endif + +#ifdef SAM7_IAR + #include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h" +#endif + +#ifdef SAM9XE_IAR + #include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h" +#endif + +#ifdef LPC2000_IAR + #include "..\..\Source\portable\IAR\LPC2000\portmacro.h" +#endif + +#ifdef STR71X_IAR + #include "..\..\Source\portable\IAR\STR71x\portmacro.h" +#endif + +#ifdef STR75X_IAR + #include "..\..\Source\portable\IAR\STR75x\portmacro.h" +#endif + +#ifdef STR75X_GCC + #include "..\..\Source\portable\GCC\STR75x\portmacro.h" +#endif + +#ifdef STR91X_IAR + #include "..\..\Source\portable\IAR\STR91x\portmacro.h" +#endif + +#ifdef GCC_H8S + #include "../../Source/portable/GCC/H8S2329/portmacro.h" +#endif + +#ifdef GCC_AT91FR40008 + #include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h" +#endif + +#ifdef RVDS_ARMCM3_LM3S102 + #include "../../Source/portable/RVDS/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3_LM3S102 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARM_CM3 + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARMCM3_LM + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef HCS12_CODE_WARRIOR + #include "../../Source/portable/CodeWarrior/HCS12/portmacro.h" +#endif + +#ifdef MICROBLAZE_GCC + #include "../../Source/portable/GCC/MicroBlaze/portmacro.h" +#endif + +#ifdef TERN_EE + #include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h" +#endif + +#ifdef GCC_HCS12 + #include "../../Source/portable/GCC/HCS12/portmacro.h" +#endif + +#ifdef GCC_MCF5235 + #include "../../Source/portable/GCC/MCF5235/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_GCC + #include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_CODEWARRIOR + #include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h" +#endif + +#ifdef GCC_PPC405 + #include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h" +#endif + +#ifdef GCC_PPC440 + #include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h" +#endif + +#ifdef _16FX_SOFTUNE + #include "..\..\Source\portable\Softune\MB96340\portmacro.h" +#endif + +#ifdef BCC_INDUSTRIAL_PC_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\PC\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef BCC_FLASH_LITE_186_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef __GNUC__ + #ifdef __AVR32_AVR32A__ + #include "portmacro.h" + #endif +#endif + +#ifdef __ICCAVR32__ + #ifdef __CORE__ + #if __CORE__ == __AVR32A__ + #include "portmacro.h" + #endif + #endif +#endif + +#ifdef __91467D + #include "portmacro.h" +#endif + +#ifdef __96340 + #include "portmacro.h" +#endif + + +#ifdef __IAR_V850ES_Fx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3_L__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Hx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3L__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +/* Catch all to ensure portmacro.h is included in the build. Newer demos +have the path as part of the project options, rather than as relative from +the project location. If portENTER_CRITICAL() has not been defined then +portmacro.h has not yet been included - as every portmacro.h provides a +portENTER_CRITICAL() definition. Check the demo application for your demo +to find the path to the correct portmacro.h file. */ +#ifndef portENTER_CRITICAL + #include "portmacro.h" +#endif + +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007U ) +#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 + +#ifndef portNUM_CONFIGURABLE_REGIONS + #define portNUM_CONFIGURABLE_REGIONS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpu_wrappers.h" + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; +#else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; +#endif + +/* Used by heap_5.c. */ +typedef struct HeapRegion +{ + uint8_t *pucStartAddress; + size_t xSizeInBytes; +} HeapRegion_t; + +/* + * Used to define multiple heap regions for use by heap_5.c. This function + * must be called before any calls to pvPortMalloc() - not creating a task, + * queue, semaphore, mutex, software timer, event group, etc. will result in + * pvPortMalloc being called. + * + * pxHeapRegions passes in an array of HeapRegion_t structures - each of which + * defines a region of memory that can be used as the heap. The array is + * terminated by a HeapRegions_t structure that has a size of 0. The region + * with the lowest start address must appear first in the array. + */ +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ); + + +/* + * Map to the memory management routines required for the port. + */ +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * The structures and methods of manipulating the MPU are contained within the + * port layer. + * + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint16_t usStackDepth ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/projdefs.h b/component/os/freertos/freertos_v8.1.2/Source/include/projdefs.h new file mode 100644 index 0000000..9eccedf --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/projdefs.h @@ -0,0 +1,94 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef PROJDEFS_H +#define PROJDEFS_H + +/* + * Defines the prototype to which task functions must conform. Defined in this + * file to ensure the type is known before portable.h is included. + */ +typedef void (*TaskFunction_t)( void * ); + +/* Converts a time in milliseconds to a time in ticks. */ +#define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) + +#define pdFALSE ( ( BaseType_t ) 0 ) +#define pdTRUE ( ( BaseType_t ) 1 ) + +#define pdPASS ( pdTRUE ) +#define pdFAIL ( pdFALSE ) +#define errQUEUE_EMPTY ( ( BaseType_t ) 0 ) +#define errQUEUE_FULL ( ( BaseType_t ) 0 ) + +/* Error definitions. */ +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) + +#endif /* PROJDEFS_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/queue.h b/component/os/freertos/freertos_v8.1.2/Source/include/queue.h new file mode 100644 index 0000000..a728a7c --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/queue.h @@ -0,0 +1,1687 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef QUEUE_H +#define QUEUE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include queue.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Type by which queues are referenced. For example, a call to xQueueCreate() + * returns an QueueHandle_t variable that can then be used as a parameter to + * xQueueSend(), xQueueReceive(), etc. + */ +typedef void * QueueHandle_t; + +/** + * Type by which queue sets are referenced. For example, a call to + * xQueueCreateSet() returns an xQueueSet variable that can then be used as a + * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. + */ +typedef void * QueueSetHandle_t; + +/** + * Queue sets can contain both queues and semaphores, so the + * QueueSetMemberHandle_t is defined as a type to be used where a parameter or + * return value can be either an QueueHandle_t or an SemaphoreHandle_t. + */ +typedef void * QueueSetMemberHandle_t; + +/* For internal use only. */ +#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) +#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) +#define queueOVERWRITE ( ( BaseType_t ) 2 ) + +/* For internal use only. These definitions *must* match those in queue.c. */ +#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) +#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) +#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) +#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) + +/** + * queue. h + *
+ QueueHandle_t xQueueCreate(
+							  UBaseType_t uxQueueLength,
+							  UBaseType_t uxItemSize
+						  );
+ * 
+ * + * Creates a new queue instance. This allocates the storage required by the + * new queue and returns a handle for the queue. + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+	if( xQueue1 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue2 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +#define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( uxQueueLength, uxItemSize, queueQUEUE_TYPE_BASE ) + +/** + * queue. h + *
+ BaseType_t xQueueSendToToFront(
+								   QueueHandle_t	xQueue,
+								   const void		*pvItemToQueue,
+								   TickType_t		xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) + +/** + * queue. h + *
+ BaseType_t xQueueSendToBack(
+								   QueueHandle_t	xQueue,
+								   const void		*pvItemToQueue,
+								   TickType_t		xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueSend(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue,
+							  TickType_t xTicksToWait
+						 );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueOverwrite(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue
+						 );
+ * 
+ * + * Only for use with queues that have a length of one - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * This function must not be called from an interrupt service routine. + * See xQueueOverwriteFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle of the queue to which the data is being sent. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @return xQueueOverwrite() is a macro that calls xQueueGenericSend(), and + * therefore has the same return values as xQueueSendToFront(). However, pdPASS + * is the only value that can be returned because xQueueOverwrite() will write + * to the queue even when the queue is already full. + * + * Example usage: +
+
+ void vFunction( void *pvParameters )
+ {
+ QueueHandle_t xQueue;
+ uint32_t ulVarToSend, ulValReceived;
+
+	// Create a queue to hold one uint32_t value.  It is strongly
+	// recommended *not* to use xQueueOverwrite() on queues that can
+	// contain more than one value, and doing so will trigger an assertion
+	// if configASSERT() is defined.
+	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+
+	// Write the value 10 to the queue using xQueueOverwrite().
+	ulVarToSend = 10;
+	xQueueOverwrite( xQueue, &ulVarToSend );
+
+	// Peeking the queue should now return 10, but leave the value 10 in
+	// the queue.  A block time of zero is used as it is known that the
+	// queue holds a value.
+	ulValReceived = 0;
+	xQueuePeek( xQueue, &ulValReceived, 0 );
+
+	if( ulValReceived != 10 )
+	{
+		// Error unless the item was removed by a different task.
+	}
+
+	// The queue is still full.  Use xQueueOverwrite() to overwrite the
+	// value held in the queue with 100.
+	ulVarToSend = 100;
+	xQueueOverwrite( xQueue, &ulVarToSend );
+
+	// This time read from the queue, leaving the queue empty once more.
+	// A block time of 0 is used again.
+	xQueueReceive( xQueue, &ulValReceived, 0 );
+
+	// The value read should be the last value written, even though the
+	// queue was already full when the value was written.
+	if( ulValReceived != 100 )
+	{
+		// Error!
+	}
+
+	// ...
+}
+ 
+ * \defgroup xQueueOverwrite xQueueOverwrite + * \ingroup QueueManagement + */ +#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) + + +/** + * queue. h + *
+ BaseType_t xQueueGenericSend(
+									QueueHandle_t xQueue,
+									const void * pvItemToQueue,
+									TickType_t xTicksToWait
+									BaseType_t xCopyPosition
+								);
+ * 
+ * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueuePeek(
+							 QueueHandle_t xQueue,
+							 void *pvBuffer,
+							 TickType_t xTicksToWait
+						 );
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. See + * xQueuePeekFromISR() for an alternative that can be called from an interrupt + * service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ QueueHandle_t xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to peek the data from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Peek a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask, but the item still remains on the queue.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) + +/** + * queue. h + *
+ BaseType_t xQueuePeekFromISR(
+									QueueHandle_t xQueue,
+									void *pvBuffer,
+								);
+ * + * A version of xQueuePeek() that can be called from an interrupt service + * routine (ISR). + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * \defgroup xQueuePeekFromISR xQueuePeekFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueReceive(
+								 QueueHandle_t xQueue,
+								 void *pvBuffer,
+								 TickType_t xTicksToWait
+							);
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_PERIOD_MS should be used to convert to real time if this is + * required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ QueueHandle_t xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) + + +/** + * queue. h + *
+ BaseType_t xQueueGenericReceive(
+									   QueueHandle_t	xQueue,
+									   void	*pvBuffer,
+									   TickType_t	xTicksToWait
+									   BaseType_t	xJustPeek
+									);
+ * + * It is preferred that the macro xQueueReceive() be used rather than calling + * this function directly. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * xQueueGenericReceive() will return immediately if the queue is empty and + * xTicksToWait is 0. + * + * @param xJustPeek When set to true, the item received from the queue is not + * actually removed from the queue - meaning a subsequent call to + * xQueueReceive() will return the same item. When set to false, the item + * being received from the queue is also removed from the queue. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ QueueHandle_t xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
+ * + * Return the number of free spaces available in a queue. This is equal to the + * number of items that can be sent to the queue before the queue becomes full + * if no items are removed. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of spaces available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
void vQueueDelete( QueueHandle_t xQueue );
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \defgroup vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueSendToFrontFromISR(
+										 QueueHandle_t xQueue,
+										 const void *pvItemToQueue,
+										 BaseType_t *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPrioritTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) + + +/** + * queue. h + *
+ BaseType_t xQueueSendToBackFromISR(
+										 QueueHandle_t xQueue,
+										 const void *pvItemToQueue,
+										 BaseType_t *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueOverwriteFromISR(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue,
+							  BaseType_t *pxHigherPriorityTaskWoken
+						 );
+ * 
+ * + * A version of xQueueOverwrite() that can be used in an interrupt service + * routine (ISR). + * + * Only for use with queues that can hold a single item - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return xQueueOverwriteFromISR() is a macro that calls + * xQueueGenericSendFromISR(), and therefore has the same return values as + * xQueueSendToFrontFromISR(). However, pdPASS is the only value that can be + * returned because xQueueOverwriteFromISR() will write to the queue even when + * the queue is already full. + * + * Example usage: +
+
+ QueueHandle_t xQueue;
+
+ void vFunction( void *pvParameters )
+ {
+ 	// Create a queue to hold one uint32_t value.  It is strongly
+	// recommended *not* to use xQueueOverwriteFromISR() on queues that can
+	// contain more than one value, and doing so will trigger an assertion
+	// if configASSERT() is defined.
+	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+}
+
+void vAnInterruptHandler( void )
+{
+// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+uint32_t ulVarToSend, ulValReceived;
+
+	// Write the value 10 to the queue using xQueueOverwriteFromISR().
+	ulVarToSend = 10;
+	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// The queue is full, but calling xQueueOverwriteFromISR() again will still
+	// pass because the value held in the queue will be overwritten with the
+	// new value.
+	ulVarToSend = 100;
+	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// Reading from the queue will now return 100.
+
+	// ...
+
+	if( xHigherPrioritytaskWoken == pdTRUE )
+	{
+		// Writing to the queue caused a task to unblock and the unblocked task
+		// has a priority higher than or equal to the priority of the currently
+		// executing task (the task this interrupt interrupted).  Perform a context
+		// switch so this interrupt returns directly to the unblocked task.
+		portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
+	}
+}
+ 
+ * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR + * \ingroup QueueManagement + */ +#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) + +/** + * queue. h + *
+ BaseType_t xQueueSendFromISR(
+									 QueueHandle_t xQueue,
+									 const void *pvItemToQueue,
+									 BaseType_t *pxHigherPriorityTaskWoken
+								);
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		// Actual macro used here is port specific.
+		portYIELD_FROM_ISR ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueGenericSendFromISR(
+										   QueueHandle_t		xQueue,
+										   const	void	*pvItemToQueue,
+										   BaseType_t	*pxHigherPriorityTaskWoken,
+										   BaseType_t	xCopyPosition
+									   );
+ 
+ * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWokenByPost;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWokenByPost = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post each byte.
+		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.  Note that the
+	// name of the yield function required is port specific.
+	if( xHigherPriorityTaskWokenByPost )
+	{
+		taskYIELD_YIELD_FROM_ISR();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueReceiveFromISR(
+									   QueueHandle_t	xQueue,
+									   void	*pvBuffer,
+									   BaseType_t *pxTaskWoken
+								   );
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+
+ QueueHandle_t xQueue;
+
+ // Function to create a queue and post some values.
+ void vAFunction( void *pvParameters )
+ {
+ char cValueToPost;
+ const TickType_t xTicksToWait = ( TickType_t )0xff;
+
+	// Create a queue capable of containing 10 characters.
+	xQueue = xQueueCreate( 10, sizeof( char ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Post some characters that will be used within an ISR.  If the queue
+	// is full then this task will block for xTicksToWait ticks.
+	cValueToPost = 'a';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+	cValueToPost = 'b';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+
+	// ... keep posting characters ... this task may block when the queue
+	// becomes full.
+
+	cValueToPost = 'c';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ }
+
+ // ISR that outputs all the characters received on the queue.
+ void vISR_Routine( void )
+ {
+ BaseType_t xTaskWokenByReceive = pdFALSE;
+ char cRxedChar;
+
+	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+	{
+		// A character was received.  Output the character now.
+		vOutputCharacter( cRxedChar );
+
+		// If removing the character from the queue woke the task that was
+		// posting onto the queue cTaskWokenByReceive will have been set to
+		// pdTRUE.  No matter how many times this loop iterates only one
+		// task will be woken.
+	}
+
+	if( cTaskWokenByPost != ( char ) pdFALSE;
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* + * Utilities to query queues that are safe to use from an ISR. These utilities + * should be used only from witin an ISR, or within a critical section. + */ +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + + +/* + * xQueueAltGenericSend() is an alternative version of xQueueGenericSend(). + * Likewise xQueueAltGenericReceive() is an alternative version of + * xQueueGenericReceive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +BaseType_t xQueueAltGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ); +BaseType_t xQueueAltGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ); +#define xQueueAltSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) +#define xQueueAltSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) +#define xQueueAltReceive( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdFALSE ) +#define xQueueAltPeek( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), pdTRUE ) + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ); +BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken ); +BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ); +BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ); + +/* + * For internal use only. Use xSemaphoreCreateMutex(), + * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling + * these functions directly. + */ +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Use xSemaphoreTakeMutexRecursive() or + * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. + */ +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t pxMutex ) PRIVILEGED_FUNCTION; + +/* + * Reset a queue back to its original empty state. pdPASS is returned if the + * queue is successfully reset. pdFAIL is returned if the queue could not be + * reset because there are tasks blocked on the queue waiting to either + * receive from the queue or send to the queue. + */ +#define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not effect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. The queue registry only + * stores a pointer to the string - so the string must be persistent (global or + * preferably in ROM/Flash), not on the stack. + */ +#if configQUEUE_REGISTRY_SIZE > 0 + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger, and vQueueUnregisterQueue() to + * remove the queue, semaphore or mutex from the register. If you are not using + * a kernel aware debugger then this function can be ignored. + * + * @param xQueue The handle of the queue being removed from the registry. + */ +#if configQUEUE_REGISTRY_SIZE > 0 + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the queue creation function, which is in turn called by + * any queue, semaphore or mutex creation function or macro. + */ +QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; + +/* + * Queue sets provide a mechanism to allow a task to block (pend) on a read + * operation from multiple queues or semaphores simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * A queue set must be explicitly created using a call to xQueueCreateSet() + * before it can be used. Once created, standard FreeRTOS queues and semaphores + * can be added to the set using calls to xQueueAddToSet(). + * xQueueSelectFromSet() is then used to determine which, if any, of the queues + * or semaphores contained in the set is in a state where a queue read or + * semaphore take operation would be successful. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: An additional 4 bytes of RAM is required for each space in a every + * queue added to a queue set. Therefore counting semaphores that have a high + * maximum count value should not be added to a queue set. + * + * Note 4: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param uxEventQueueLength Queue sets store events that occur on + * the queues and semaphores contained in the set. uxEventQueueLength specifies + * the maximum number of events that can be queued at once. To be absolutely + * certain that events are not lost uxEventQueueLength should be set to the + * total sum of the length of the queues added to the set, where binary + * semaphores and mutexes have a length of 1, and counting semaphores have a + * length set by their maximum count value. Examples: + * + If a queue set is to hold a queue of length 5, another queue of length 12, + * and a binary semaphore, then uxEventQueueLength should be set to + * (5 + 12 + 1), or 18. + * + If a queue set is to hold three binary semaphores then uxEventQueueLength + * should be set to (1 + 1 + 1 ), or 3. + * + If a queue set is to hold a counting semaphore that has a maximum count of + * 5, and a counting semaphore that has a maximum count of 3, then + * uxEventQueueLength should be set to (5 + 3), or 8. + * + * @return If the queue set is created successfully then a handle to the created + * queue set is returned. Otherwise NULL is returned. + */ +QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; + +/* + * Adds a queue or semaphore to a queue set that was previously created by a + * call to xQueueCreateSet(). + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being added to + * the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set to which the queue or semaphore + * is being added. + * + * @return If the queue or semaphore was successfully added to the queue set + * then pdPASS is returned. If the queue could not be successfully added to the + * queue set because it is already a member of a different queue set then pdFAIL + * is returned. + */ +BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * Removes a queue or semaphore from a queue set. A queue or semaphore can only + * be removed from a set if the queue or semaphore is empty. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being removed + * from the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set in which the queue or semaphore + * is included. + * + * @return If the queue or semaphore was successfully removed from the queue set + * then pdPASS is returned. If the queue was not in the queue set, or the + * queue (or semaphore) was not empty, then pdFAIL is returned. + */ +BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * xQueueSelectFromSet() selects from the members of a queue set a queue or + * semaphore that either contains data (in the case of a queue) or is available + * to take (in the case of a semaphore). xQueueSelectFromSet() effectively + * allows a task to block (pend) on a read operation on all the queues and + * semaphores in a queue set simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueSet The queue set on which the task will (potentially) block. + * + * @param xTicksToWait The maximum time, in ticks, that the calling task will + * remain in the Blocked state (with other tasks executing) to wait for a member + * of the queue set to be ready for a successful queue read or semaphore take + * operation. + * + * @return xQueueSelectFromSet() will return the handle of a queue (cast to + * a QueueSetMemberHandle_t type) contained in the queue set that contains data, + * or the handle of a semaphore (cast to a QueueSetMemberHandle_t type) contained + * in the queue set that is available, or NULL if no such queue or semaphore + * exists before before the specified block time expires. + */ +QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * A version of xQueueSelectFromSet() that can be used from an ISR. + */ +QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* Not public API functions. */ +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; +void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + + +#ifdef __cplusplus +} +#endif + +#endif /* QUEUE_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/semphr.h b/component/os/freertos/freertos_v8.1.2/Source/include/semphr.h new file mode 100644 index 0000000..5275895 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/semphr.h @@ -0,0 +1,840 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include semphr.h" +#endif + +#include "queue.h" + +typedef QueueHandle_t SemaphoreHandle_t; + +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( uint8_t ) 1U ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U ) +#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + + +/** + * semphr. h + *
vSemaphoreCreateBinary( SemaphoreHandle_t xSemaphore )
+ * + * This old vSemaphoreCreateBinary() macro is now deprecated in favour of the + * xSemaphoreCreateBinary() function. Note that binary semaphores created using + * the vSemaphoreCreateBinary() macro are created in a state such that the + * first call to 'take' the semaphore would pass, whereas binary semaphores + * created using xSemaphoreCreateBinary() are created in a state such that the + * the semaphore must first be 'given' before it can be 'taken'. + * + * Macro that implements a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as we don't want to actually store any data - we just want to know if the + * queue is empty or full. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param xSemaphore Handle to the created semaphore. Should be of type SemaphoreHandle_t. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+    // This is a macro so pass the variable in directly.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#define vSemaphoreCreateBinary( xSemaphore ) \ + { \ + ( xSemaphore ) = xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ); \ + if( ( xSemaphore ) != NULL ) \ + { \ + ( void ) xSemaphoreGive( ( xSemaphore ) ); \ + } \ + } + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateBinary( void )
+ * + * The old vSemaphoreCreateBinary() macro is now deprecated in favour of this + * xSemaphoreCreateBinary() function. Note that binary semaphores created using + * the vSemaphoreCreateBinary() macro are created in a state such that the + * first call to 'take' the semaphore would pass, whereas binary semaphores + * created using xSemaphoreCreateBinary() are created in a state such that the + * the semaphore must first be 'given' before it can be 'taken'. + * + * Function that creates a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as nothing is actually stored - all that is important is whether the queue is + * empty or full (the binary semaphore is available or not). + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @return Handle to the created semaphore. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateBinary();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE ) + +/** + * semphr. h + *
xSemaphoreTake(
+ *                   SemaphoreHandle_t xSemaphore,
+ *                   TickType_t xBlockTime
+ *               )
+ * + * Macro to obtain a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). + * + * @param xSemaphore A handle to the semaphore being taken - obtained when + * the semaphore was created. + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. A block + * time of portMAX_DELAY can be used to block indefinitely (provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h). + * + * @return pdTRUE if the semaphore was obtained. pdFALSE + * if xBlockTime expired without the semaphore becoming available. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+
+ // A task that creates a semaphore.
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+ }
+
+ // A task that uses the semaphore.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xSemaphore != NULL )
+    {
+        // See if we can obtain the semaphore.  If the semaphore is not available
+        // wait 10 ticks to see if it becomes free.
+        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the semaphore and can now access the
+            // shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource.  Release the
+            // semaphore.
+            xSemaphoreGive( xSemaphore );
+        }
+        else
+        {
+            // We could not obtain the semaphore and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTake xSemaphoreTake + * \ingroup Semaphores + */ +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) + +/** + * semphr. h + * xSemaphoreTakeRecursive( + * SemaphoreHandle_t xMutex, + * TickType_t xBlockTime + * ) + * + * Macro to recursively obtain, or 'take', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being obtained. This is the + * handle returned by xSemaphoreCreateRecursiveMutex(); + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_PERIOD_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. If + * the task already owns the semaphore then xSemaphoreTakeRecursive() will + * return immediately no matter what the value of xBlockTime. + * + * @return pdTRUE if the semaphore was obtained. pdFALSE if xBlockTime + * expired without the semaphore becoming available. + * + * Example usage: +
+ SemaphoreHandle_t xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.
+        if( xSemaphoreTakeRecursive( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, but instead buried in a more complex
+			// call structure.  This is just for illustrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * \ingroup Semaphores + */ +#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) ) + + +/* + * xSemaphoreAltTake() is an alternative version of xSemaphoreTake(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltTake( xSemaphore, xBlockTime ) xQueueAltGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE ) + +/** + * semphr. h + *
xSemaphoreGive( SemaphoreHandle_t xSemaphore )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). + * + * This macro must not be used from an ISR. See xSemaphoreGiveFromISR () for + * an alternative which can be used from an ISR. + * + * This macro must also not be used on semaphores created using + * xSemaphoreCreateRecursiveMutex(). + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @return pdTRUE if the semaphore was released. pdFALSE if an error occurred. + * Semaphores are implemented using queues. An error can occur if there is + * no space on the queue to post a message - indicating that the + * semaphore was not first obtained correctly. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+        {
+            // We would expect this call to fail because we cannot give
+            // a semaphore without first "taking" it!
+        }
+
+        // Obtain the semaphore - don't block if the semaphore is not
+        // immediately available.
+        if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) )
+        {
+            // We now have the semaphore and can access the shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource so can free the
+            // semaphore.
+            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+            {
+                // We would not expect this call to fail because we must have
+                // obtained the semaphore to get here.
+            }
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGive xSemaphoreGive + * \ingroup Semaphores + */ +#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
xSemaphoreGiveRecursive( SemaphoreHandle_t xMutex )
+ * + * Macro to recursively release, or 'give', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being released, or 'given'. This is the + * handle returned by xSemaphoreCreateMutex(); + * + * @return pdTRUE if the semaphore was given. + * + * Example usage: +
+ SemaphoreHandle_t xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.
+        if( xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, it would be more likely that the calls
+			// to xSemaphoreGiveRecursive() would be called as a call stack
+			// unwound.  This is just for demonstrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * \ingroup Semaphores + */ +#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) ) + +/* + * xSemaphoreAltGive() is an alternative version of xSemaphoreGive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltGive( xSemaphore ) xQueueAltGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
+ xSemaphoreGiveFromISR(
+                          SemaphoreHandle_t xSemaphore,
+                          BaseType_t *pxHigherPriorityTaskWoken
+                      )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary() or xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. + * + * Example usage: +
+ \#define LONG_TIME 0xffff
+ \#define TICKS_TO_WAIT	10
+ SemaphoreHandle_t xSemaphore = NULL;
+
+ // Repetitive task.
+ void vATask( void * pvParameters )
+ {
+    for( ;; )
+    {
+        // We want this task to run every 10 ticks of a timer.  The semaphore
+        // was created before this task was started.
+
+        // Block waiting for the semaphore to become available.
+        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
+        {
+            // It is time to execute.
+
+            // ...
+
+            // We have finished our task.  Return to the top of the loop where
+            // we will block on the semaphore until it is time to execute
+            // again.  Note when using the semaphore for synchronisation with an
+			// ISR in this manner there is no need to 'give' the semaphore back.
+        }
+    }
+ }
+
+ // Timer ISR
+ void vTimerISR( void * pvParameters )
+ {
+ static uint8_t ucLocalTickCount = 0;
+ static BaseType_t xHigherPriorityTaskWoken;
+
+    // A timer tick has occurred.
+
+    // ... Do other time functions.
+
+    // Is it time for vATask () to run?
+	xHigherPriorityTaskWoken = pdFALSE;
+    ucLocalTickCount++;
+    if( ucLocalTickCount >= TICKS_TO_WAIT )
+    {
+        // Unblock the task by releasing the semaphore.
+        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
+
+        // Reset the count so we release the semaphore again in 10 ticks time.
+        ucLocalTickCount = 0;
+    }
+
+    if( xHigherPriorityTaskWoken != pdFALSE )
+    {
+        // We can force a context switch here.  Context switching from an
+        // ISR uses port specific syntax.  Check the demo task for your port
+        // to find the syntax required.
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * \ingroup Semaphores + */ +#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * semphr. h + *
+ xSemaphoreTakeFromISR(
+                          SemaphoreHandle_t xSemaphore,
+                          BaseType_t *pxHigherPriorityTaskWoken
+                      )
+ * + * Macro to take a semaphore from an ISR. The semaphore must have + * previously been created with a call to vSemaphoreCreateBinary() or + * xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR, however taking a semaphore from an ISR + * is not a common operation. It is likely to only be useful when taking a + * counting semaphore when an interrupt is obtaining an object from a resource + * pool (when the semaphore count indicates the number of resources available). + * + * @param xSemaphore A handle to the semaphore being taken. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreTakeFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if taking the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreTakeFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully taken, otherwise + * pdFALSE + */ +#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) ) + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateMutex( void )
+ * + * Macro that implements a mutex semaphore by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros should not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * SemaphoreHandle_t. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ) + + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex( void )
+ * + * Macro that implements a recursive mutex by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros should not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * SemaphoreHandle_t. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateRecursiveMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX ) + +/** + * semphr. h + *
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount )
+ * + * Macro that creates a counting semaphore by using the existing + * queue mechanism. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @return Handle to the created semaphore. Null if the semaphore could not be + * created. + * + * Example usage: +
+ SemaphoreHandle_t xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+ SemaphoreHandle_t xSemaphore = NULL;
+
+    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
+    // The max value to which the semaphore can count should be 10, and the
+    // initial value assigned to the count should be 0.
+    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.
+    }
+ }
+ 
+ * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * \ingroup Semaphores + */ +#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) ) + +/** + * semphr. h + *
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );
+ * + * Delete a semaphore. This function must be used with care. For example, + * do not delete a mutex type semaphore if the mutex is held by a task. + * + * @param xSemaphore A handle to the semaphore to be deleted. + * + * \defgroup vSemaphoreDelete vSemaphoreDelete + * \ingroup Semaphores + */ +#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) ) + +/** + * semphr.h + *
TaskHandle_t xSemaphoreGetMutexHolder( SemaphoreHandle_t xMutex );
+ * + * If xMutex is indeed a mutex type semaphore, return the current mutex holder. + * If xMutex is not a mutex type semaphore, or the mutex is available (not held + * by a task), return NULL. + * + * Note: This is a good way of determining if the calling task is the mutex + * holder, but not a good way of determining the identity of the mutex holder as + * the holder may change between the function exiting and the returned value + * being tested. + */ +#define xSemaphoreGetMutexHolder( xSemaphore ) xQueueGetMutexHolder( ( xSemaphore ) ) + +#endif /* SEMAPHORE_H */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/stdint.readme b/component/os/freertos/freertos_v8.1.2/Source/include/stdint.readme new file mode 100644 index 0000000..4414c29 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/stdint.readme @@ -0,0 +1,27 @@ + +#ifndef FREERTOS_STDINT +#define FREERTOS_STDINT + +/******************************************************************************* + * THIS IS NOT A FULL stdint.h IMPLEMENTATION - It only contains the definitions + * necessary to build the FreeRTOS code. It is provided to allow FreeRTOS to be + * built using compilers that do not provide their own stdint.h definition. + * + * To use this file: + * + * 1) Copy this file into the directory that contains your FreeRTOSConfig.h + * header file, as that directory will already be in the compilers include + * path. + * + * 2) Rename the copied file stdint.h. + * + */ + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef long int32_t; +typedef unsigned long uint32_t; + +#endif /* FREERTOS_STDINT */ diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/task.h b/component/os/freertos/freertos_v8.1.2/Source/include/task.h new file mode 100644 index 0000000..ca71024 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/task.h @@ -0,0 +1,1570 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef INC_TASK_H +#define INC_TASK_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V8.1.2" +#define tskKERNEL_VERSION_MAJOR 8 +#define tskKERNEL_VERSION_MINOR 1 +#define tskKERNEL_VERSION_BUILD 2 + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an TaskHandle_t variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \defgroup TaskHandle_t TaskHandle_t + * \ingroup Tasks + */ +typedef void * TaskHandle_t; + +/* + * Defines the prototype to which the application task hook function must + * conform. + */ +typedef BaseType_t (*TaskHookFunction_t)( void * ); + +/* Task states returned by eTaskGetState. */ +typedef enum +{ + eRunning = 0, /* A task is querying the state of itself, so must be running. */ + eReady, /* The task being queried is in a read or pending ready list. */ + eBlocked, /* The task being queried is in the Blocked state. */ + eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted /* The task being queried has been deleted, but its TCB has not yet been freed. */ +} eTaskState; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + BaseType_t xOverflowCount; + TickType_t xTimeOnEntering; +} TimeOut_t; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void *pvBaseAddress; + uint32_t ulLengthInBytes; + uint32_t ulParameters; +} MemoryRegion_t; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMETERS +{ + TaskFunction_t pvTaskCode; + const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + uint16_t usStackDepth; + void *pvParameters; + UBaseType_t uxPriority; + StackType_t *puxStackBuffer; + MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; +} TaskParameters_t; + +/* Used with the uxTaskGetSystemState() function to return the state of each task +in the system. */ +typedef struct xTASK_STATUS +{ + TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ + const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /* A number unique to the task. */ + eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulDelataRunTimeCounterOfPeroid;/* The delta run time counter in a sample peroid*/ +#endif + uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ +} TaskStatus_t; + +/* Possible return values for eTaskConfirmSleepModeStatus(). */ +typedef enum +{ + eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ + eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ +} eSleepModeStatus; + + +/** + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \defgroup taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is +0 to generate more optimal code when configASSERT() is defined as the constant +is used in assert() statements. */ +#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) +#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) +#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) + + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + *
+ BaseType_t xTaskCreate(
+							  TaskFunction_t pvTaskCode,
+							  const char * const pcName,
+							  uint16_t usStackDepth,
+							  void *pvParameters,
+							  UBaseType_t uxPriority,
+							  TaskHandle_t *pvCreatedTask
+						  );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+ // Task to be created.
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+	 }
+ }
+
+ // Function that creates a task.
+ void vOtherFunction( void )
+ {
+ static uint8_t ucParameterToPass;
+ TaskHandle_t xHandle = NULL;
+
+	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
+	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
+	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
+	 // the new task attempts to access it.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+     configASSERT( xHandle );
+
+	 // Use the handle to delete the task.
+     if( xHandle != NULL )
+     {
+	     vTaskDelete( xHandle );
+     }
+ }
+   
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ) ) + +/** + * task. h + *
+ BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+// Create an TaskParameters_t structure that defines the task to be created.
+static const TaskParameters_t xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{
+		// Base address					Length	Parameters
+        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+};
+
+int main( void )
+{
+TaskHandle_t xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// and/or timer task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#define xTaskCreateRestricted( x, pxCreatedTask ) xTaskGenericCreate( ((x)->pvTaskCode), ((x)->pcName), ((x)->usStackDepth), ((x)->pvParameters), ((x)->uxPriority), (pxCreatedTask), ((x)->puxStackBuffer), ((x)->xRegions) ) + +/** + * task. h + *
+ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
+ * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param xRegions A pointer to an MemoryRegion_t structure that contains the + * new memory region definitions. + * + * Example usage: +
+// Define an array of MemoryRegion_t structures that configures an MPU region
+// allowing read/write access for 1024 bytes starting at the beginning of the
+// ucOneKByte array.  The other two of the maximum 3 definable regions are
+// unused so set to zero.
+static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
+{
+	// Base address		Length		Parameters
+	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
+	{ 0,				0,			0 },
+	{ 0,				0,			0 }
+};
+
+void vATask( void *pvParameters )
+{
+	// This task was created such that it has access to certain regions of
+	// memory as defined by the MPU configuration.  At some point it is
+	// desired that these MPU regions are replaced with that defined in the
+	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
+	// for this purpose.  NULL is used as the task handle to indicate that this
+	// function should modify the MPU regions of the calling task.
+	vTaskAllocateMPURegions( NULL, xAltRegions );
+
+	// Now the task can continue its function, but from this point on can only
+	// access its stack and the ucOneKByte array (unless any other statically
+	// defined or shared regions have been declared elsewhere).
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelete( TaskHandle_t xTask );
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernel's management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param xTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: +
+ void vOtherFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create the task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskDelay( const TickType_t xTicksToDelay );
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a periodic task as the path taken through the + * code, as well as other task and interrupt activity, will effect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See vTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + + void vTaskFunction( void * pvParameters ) + { + // Block for 500ms. + const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + + for( ;; ) + { + // Simply toggle the LED every 500ms, blocking between each toggle. + vToggleLED(); + vTaskDelay( xDelay ); + } + } + + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: +
+ // Perform an action every 10 ticks.
+ void vTaskFunction( void * pvParameters )
+ {
+ TickType_t xLastWakeTime;
+ const TickType_t xFrequency = 10;
+
+	 // Initialise the xLastWakeTime variable with the current time.
+	 xLastWakeTime = xTaskGetTickCount ();
+	 for( ;; )
+	 {
+		 // Wait for the next cycle.
+		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
+
+		 // Perform action here.
+	 }
+ }
+   
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to obtain the priority of the created task.
+	 // It was created with tskIDLE_PRIORITY, but may have changed
+	 // it itself.
+	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+	 {
+		 // The task has changed it's priority.
+	 }
+
+	 // ...
+
+	 // Is our priority higher than the created task?
+	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+	 {
+		 // Our priority (obtained using NULL handle) is higher.
+	 }
+ }
+   
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
eTaskState eTaskGetState( TaskHandle_t xTask );
+ * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the eTaskState + * enumerated type. + * + * @param xTask Handle of the task to be queried. + * + * @return The state of xTask at the time the function was called. Note the + * state of the task might change between the function being called, and the + * functions return value being tested by the calling task. + */ +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param xTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to raise the priority of the created task.
+	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+
+	 // ...
+
+	 // Use a NULL handle to raise our priority to the same value.
+	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ }
+   
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Suspend ourselves.
+	 vTaskSuspend( NULL );
+
+	 // We cannot get here unless another task calls vTaskResume
+	 // with our handle as the parameter.
+ }
+   
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskResume( TaskHandle_t xTaskToResume );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param xTaskToResume Handle to the task being readied. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Resume the suspended task ourselves.
+	 vTaskResume( xHandle );
+
+	 // The created task will once again get microcontroller processing
+	 // time in accordance with its priority within the system.
+ }
+   
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * xTaskResumeFromISR() should not be used to synchronise a task with an + * interrupt if there is a chance that the interrupt could arrive prior to the + * task being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @param xTaskToResume Handle to the task being readied. + * + * @return pdTRUE if resuming the task should result in a context switch, + * otherwise pdFALSE. This is used by the ISR to determine if a context switch + * may be required following the ISR. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskStartScheduler( void );
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: +
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will not get here unless a task calls vTaskEndScheduler ()
+ }
+   
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskEndScheduler( void );
+ * + * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: +
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // At some point we want to end the real time kernel processing
+		 // so call ...
+		 vTaskEndScheduler ();
+	 }
+ }
+
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will only get here when the vTaskCode () task has called
+	 // vTaskEndScheduler ().  When we get here we are back to single task
+	 // execution.
+ }
+   
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspendAll( void );
+ * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the kernel
+		 // tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.
+		 xTaskResumeAll ();
+	 }
+ }
+   
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskResumeAll( void );
+ * + * Resumes scheduler activity after it was suspended by a call to + * vTaskSuspendAll(). + * + * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks + * that were previously suspended by a call to vTaskSuspend(). + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the real
+		 // time kernel tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.  We want to force
+		 // a context switch - but there is no point if resuming the scheduler
+		 // caused a context switch already.
+		 if( !xTaskResumeAll () )
+		 {
+			  taskYIELD ();
+		 }
+	 }
+ }
+   
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + *
TickType_t xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \defgroup xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
TickType_t xTaskGetTickCountFromISR( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
uint16_t uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery );
+ * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQuery. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. INCLUDE_pcTaskGetTaskName must be + * set to 1 in FreeRTOSConfig.h for pcTaskGetTaskName() to be available. + * + * \defgroup pcTaskGetTaskName pcTaskGetTaskName + * \ingroup TaskUtils + */ +char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task.h + *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* When using trace macros it is sometimes necessary to include task.h before +FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, +so the following two prototypes will cause a compilation error. This can be +fixed by simply guarding against the inclusion of these two prototypes unless +they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration +constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + /** + * task.h + *
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
+ * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. + */ + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +/** + * task.h + *
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
+ * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. The return value is the value returned by the task hook function + * registered by the user. + */ +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * xTaskGetIdleTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTaskGetIdleTaskHandle( void ); + +/** + * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for + * uxTaskGetSystemState() to be available. + * + * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in + * the system. TaskStatus_t structures contain, among other things, members + * for the task handle, task name, task priority, task state, and total amount + * of run time consumed by the task. See the TaskStatus_t structure + * definition in this file for the full member list. + * + * NOTE: This function is intended for debugging use only as its use results in + * the scheduler remaining suspended for an extended period. + * + * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. + * The array must contain at least one TaskStatus_t structure for each task + * that is under the control of the RTOS. The number of tasks under the control + * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function. + * + * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray + * parameter. The size is specified as the number of indexes in the array, or + * the number of TaskStatus_t structures contained in the array, not by the + * number of bytes in the array. + * + * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in + * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the + * total run time (as defined by the run time stats clock, see + * http://www.freertos.org/rtos-run-time-stats.html) since the target booted. + * pulTotalRunTime can be set to NULL to omit the total run time information. + * + * @return The number of TaskStatus_t structures that were populated by + * uxTaskGetSystemState(). This should equal the number returned by the + * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed + * in the uxArraySize parameter was too small. + * + * Example usage: +
+    // This example demonstrates how a human readable table of run time stats
+	// information is generated from raw data provided by uxTaskGetSystemState().
+	// The human readable table is written to pcWriteBuffer
+	void vTaskGetRunTimeStats( char *pcWriteBuffer )
+	{
+	TaskStatus_t *pxTaskStatusArray;
+	volatile UBaseType_t uxArraySize, x;
+	uint32_t ulTotalRunTime, ulStatsAsPercentage;
+
+		// Make sure the write buffer does not contain a string.
+		*pcWriteBuffer = 0x00;
+
+		// Take a snapshot of the number of tasks in case it changes while this
+		// function is executing.
+		uxArraySize = uxTaskGetNumberOfTasks();
+
+		// Allocate a TaskStatus_t structure for each task.  An array could be
+		// allocated statically at compile time.
+		pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
+
+		if( pxTaskStatusArray != NULL )
+		{
+			// Generate raw status information about each task.
+			uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
+
+			// For percentage calculations.
+			ulTotalRunTime /= 100UL;
+
+			// Avoid divide by zero errors.
+			if( ulTotalRunTime > 0 )
+			{
+				// For each populated position in the pxTaskStatusArray array,
+				// format the raw data as human readable ASCII data
+				for( x = 0; x < uxArraySize; x++ )
+				{
+					// What percentage of the total run time has the task used?
+					// This will always be rounded down to the nearest integer.
+					// ulTotalRunTimeDiv100 has already been divided by 100.
+					ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
+
+					if( ulStatsAsPercentage > 0UL )
+					{
+						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
+					}
+					else
+					{
+						// If the percentage is zero here then the task has
+						// consumed less than 1% of the total run time.
+						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
+					}
+
+					pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
+				}
+			}
+
+			// The array is no longer needed, free the memory it consumes.
+			vPortFree( pxTaskStatusArray );
+		}
+	}
+	
+ */ +UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ); + +/** + * task. h + *
void vTaskList( char *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays task + * names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that might + * bloat the code size, use a lot of stack, and provide different results on + * different platforms. An alternative, tiny, third party, and limited + * functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly through a + * call to vTaskList(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \defgroup vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays the + * amount of time each task has spent in the Running state in both absolute and + * percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library function + * that might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, and + * limited functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() directly + * to get access to raw stats data, rather than indirectly through a call to + * vTaskGetRunTimeStats(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. If a non-zero value is returned then a context switch is + * required because either: + * + A task was removed from a blocked list because its timeout had expired, + * or + * + Time slicing is in use and there is a task of equal priority to the + * currently running task. + */ +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * The 'unordered' version replaces the event list item value with the + * xItemValue value, and inserts the list item at the end of the list. + * + * The 'ordered' version uses the existing event list item value (which is the + * owning tasks priority) to insert the list item into the event list is task + * priority order. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xItemValue The item value to use for the event list item when the + * event list is not ordered by task priority. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + */ +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList()/xTaskRemoveFromUnorderedEventList() will be called + * if either an event occurs to unblock a task, or the block timeout period + * expires. + * + * xTaskRemoveFromEventList() is used when the event list is in task priority + * order. It removes the list item from the head of the event list as that will + * have the highest priority owning task of all the tasks on the event list. + * xTaskRemoveFromUnorderedEventList() is used when the event list is not + * ordered and the event list items hold something other than the owning tasks + * priority. In this case the event list item value is updated to the value + * passed in the xItemValue parameter. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; + +/* + * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY + * THE EVENT BITS MODULE. + */ +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Generic version of the task creation function which is in turn called by the + * xTaskCreate() and xTaskCreateRestricted() macros. + */ +BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/* + * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. + */ +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * Set the uxTaskNumber of the task referenced by the xTask parameter to + * uxHandle. + */ +void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * If tickless mode is being used, or a low power mode is implemented, then + * the tick interrupt will not execute during idle periods. When this is the + * case, the tick count value maintained by the scheduler needs to be kept up + * to date with the actual execution time by being skipped forward by a time + * equal to the idle period. + */ +void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; + +/* + * Only avilable when configUSE_TICKLESS_IDLE is set to 1. + * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port + * specific sleep function to determine if it is ok to proceed with the sleep, + * and if it is ok to proceed, if it is ok to sleep indefinitely. + * + * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only + * called with the scheduler suspended, not from within a critical section. It + * is therefore possible for an interrupt to request a context switch between + * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being + * entered. eTaskConfirmSleepModeStatus() should be called from a short + * critical section between the timer being stopped and the sleep mode being + * entered to ensure it is ok to proceed into the sleep mode. + */ +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Increment the mutex held count when a mutex is + * taken and return the handle of the task that has taken the mutex. + */ +void *pvTaskIncrementMutexHeldCount( void ); + +#ifdef __cplusplus +} +#endif +#endif /* INC_TASK_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/include/timers.h b/component/os/freertos/freertos_v8.1.2/Source/include/timers.h new file mode 100644 index 0000000..3949135 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/include/timers.h @@ -0,0 +1,1121 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef TIMERS_H +#define TIMERS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include timers.h" +#endif + +/*lint -e537 This headers are only multiply included if the application code +happens to also be including task.h. */ +#include "task.h" +/*lint +e956 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +/* IDs for commands that can be sent/received on the timer queue. These are to +be used solely through the macros that make up the public software timer API, +as defined below. The commands that are sent from interrupts must use the +highest numbers as tmrFIRST_FROM_ISR_COMMAND is used to determine if the task +or interrupt version of the queue send function should be used. */ +#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 ) +#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 ) +#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 ) +#define tmrCOMMAND_START ( ( BaseType_t ) 1 ) +#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 ) +#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 ) +#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 ) +#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 ) + +#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 7 ) +#define tmrCOMMAND_STOP_FROM_ISR ( ( BaseType_t ) 8 ) +#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 ) + + +/** + * Type by which software timers are referenced. For example, a call to + * xTimerCreate() returns an TimerHandle_t variable that can then be used to + * reference the subject timer in calls to other software timer API functions + * (for example, xTimerStart(), xTimerReset(), etc.). + */ +typedef void * TimerHandle_t; + +/* + * Defines the prototype to which timer callback functions must conform. + */ +typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); + +/* + * Defines the prototype to which functions used with the + * xTimerPendFunctionCallFromISR() function must conform. + */ +typedef void (*PendedFunction_t)( void *, uint32_t ); + +/** + * TimerHandle_t xTimerCreate( const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction ); + * + * Creates a new software timer instance. This allocates the storage required + * by the new timer, initialises the new timers internal state, and returns a + * handle by which the new timer can be referenced. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @return If the timer is successfully created then a handle to the newly + * created timer is returned. If the timer cannot be created (because either + * there is insufficient FreeRTOS heap remaining to allocate the timer + * structures, or the timer period was set to 0) then NULL is returned. + * + * Example usage: + * @verbatim + * #define NUM_TIMERS 5 + * + * // An array to hold handles to the created timers. + * TimerHandle_t xTimers[ NUM_TIMERS ]; + * + * // An array to hold a count of the number of times each timer expires. + * int32_t lExpireCounters[ NUM_TIMERS ] = { 0 }; + * + * // Define a callback function that will be used by multiple timer instances. + * // The callback function does nothing but count the number of times the + * // associated timer expires, and stop the timer once the timer has expired + * // 10 times. + * void vTimerCallback( TimerHandle_t pxTimer ) + * { + * int32_t lArrayIndex; + * const int32_t xMaxExpiryCountBeforeStopping = 10; + * + * // Optionally do something if the pxTimer parameter is NULL. + * configASSERT( pxTimer ); + * + * // Which timer expired? + * lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer ); + * + * // Increment the number of times that pxTimer has expired. + * lExpireCounters[ lArrayIndex ] += 1; + * + * // If the timer has expired 10 times then stop it from running. + * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) + * { + * // Do not use a block time if calling a timer API function from a + * // timer callback function, as doing so could cause a deadlock! + * xTimerStop( pxTimer, 0 ); + * } + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start some timers. Starting the timers before the scheduler + * // has been started means the timers will start running immediately that + * // the scheduler starts. + * for( x = 0; x < NUM_TIMERS; x++ ) + * { + * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. + * ( 100 * x ), // The timer period in ticks. + * pdTRUE, // The timers will auto-reload themselves when they expire. + * ( void * ) x, // Assign each timer a unique id equal to its array index. + * vTimerCallback // Each timer calls the same callback when it expires. + * ); + * + * if( xTimers[ x ] == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * xTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * void *pvTimerGetTimerID( TimerHandle_t xTimer ); + * + * Returns the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used within the callback function to identify which timer actually + * expired. + * + * @param xTimer The timer being queried. + * + * @return The ID assigned to the timer being queried. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void *pvTimerGetTimerID( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); + * + * Queries a timer to see if it is active or dormant. + * + * A timer will be dormant if: + * 1) It has been created but not started, or + * 2) It is an expired one-shot timer that has not been restarted. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param xTimer The timer being queried. + * + * @return pdFALSE will be returned if the timer is dormant. A value other than + * pdFALSE will be returned if the timer is active. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is active, do something. + * } + * else + * { + * // xTimer is not active, do something else. + * } + * } + * @endverbatim + */ +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); + * + * xTimerGetTimerDaemonTaskHandle() is only available if + * INCLUDE_xTimerGetTimerDaemonTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the timer service/daemon task. It it not valid + * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); + +/** + * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStart() starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerStart() has equivalent functionality + * to the xTimerReset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerStart() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerStart() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerStart() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() + * to be available. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStart() was called. xTicksToWait is ignored if xTimerStart() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStop() stops a timer that was previously started using either of the + * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), + * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() + * to be available. + * + * @param xTimer The handle of the timer being stopped. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerChangePeriod() changes the period of a timer that was previously + * created using the xTimerCreate() API function. + * + * xTimerChangePeriod() can be called to change the period of an active or + * dormant state timer. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerChangePeriod() to be available. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerChangePeriod() was called. xTicksToWait is ignored if + * xTimerChangePeriod() is called before the scheduler is started. + * + * @return pdFAIL will be returned if the change period command could not be + * sent to the timer command queue even after xTicksToWait ticks had passed. + * pdPASS will be returned if the command was successfully sent to the timer + * command queue. When the command is actually processed will depend on the + * priority of the timer service/daemon task relative to other tasks in the + * system. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. If the timer + * // referenced by xTimer is already active when it is called, then the timer + * // is deleted. If the timer referenced by xTimer is not active when it is + * // called, then the period of the timer is set to 500ms and the timer is + * // started. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is already active - delete it. + * xTimerDelete( xTimer ); + * } + * else + * { + * // xTimer is not active, change its period to 500ms. This will also + * // cause the timer to start. Block for a maximum of 100 ticks if the + * // change period command cannot immediately be sent to the timer + * // command queue. + * if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS ) + * { + * // The command was successfully sent. + * } + * else + * { + * // The command could not be sent, even after waiting for 100 ticks + * // to pass. Take appropriate action here. + * } + * } + * } + * @endverbatim + */ + #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerDelete() deletes a timer that was previously created using the + * xTimerCreate() API function. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerDelete() to be available. + * + * @param xTimer The handle of the timer being deleted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerDelete() was called. xTicksToWait is ignored if xTimerDelete() + * is called before the scheduler is started. + * + * @return pdFAIL will be returned if the delete command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerChangePeriod() API function example usage scenario. + */ +#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerReset() re-starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerReset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when xTimerReset() was + * called. If the timer was in the dormant state then xTimerReset() has + * equivalent functionality to the xTimerStart() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerReset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerReset() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerReset() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() + * to be available. + * + * @param xTimer The handle of the timer being reset/started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerReset() was called. xTicksToWait is ignored if xTimerReset() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer. + * + * TimerHandle_t xBacklightTimer = NULL; + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press event handler. + * void vKeyPressEventHandler( char cKey ) + * { + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. Wait 10 ticks for the command to be successfully sent + * // if it cannot be sent immediately. + * vSetBacklightState( BACKLIGHT_ON ); + * if( xTimerReset( xBacklightTimer, 100 ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start the one-shot timer that is responsible for turning + * // the back-light off if no keys are pressed within a 5 second period. + * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. + * ( 5000 / portTICK_PERIOD_MS), // The timer period in ticks. + * pdFALSE, // The timer is a one-shot timer. + * 0, // The id is not used by the callback so can take any value. + * vBacklightTimerCallback // The callback function that switches the LCD back-light off. + * ); + * + * if( xBacklightTimer == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timer running as it has already + * // been set into the active state. + * xTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStart() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStartFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStartFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStartFromISR() function. If + * xTimerStartFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerStartFromISR() is actually called. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then restart the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The start command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStop() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being stopped. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStopFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStopFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStopFromISR() function. If + * xTimerStopFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the timer should be simply stopped. + * + * // The interrupt service routine that stops the timer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - simply stop the timer. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The stop command was not executed successfully. Take appropriate + * // action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerChangePeriod() that can be called from an interrupt + * service routine. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerChangePeriodFromISR() writes a message to the + * timer command queue, so has the potential to transition the timer service/ + * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() + * causes the timer service/daemon task to leave the Blocked state, and the + * timer service/daemon task has a priority equal to or greater than the + * currently executing task (the task that was interrupted), then + * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the + * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets + * this value to pdTRUE then a context switch should be performed before the + * interrupt exits. + * + * @return pdFAIL will be returned if the command to change the timers period + * could not be sent to the timer command queue. pdPASS will be returned if the + * command was successfully sent to the timer command queue. When the command + * is actually processed will depend on the priority of the timer service/daemon + * task relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the period of xTimer should be changed to 500ms. + * + * // The interrupt service routine that changes the period of xTimer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - change the period of xTimer to 500ms. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The command to change the timers period was not executed + * // successfully. Take appropriate action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerReset() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer that is to be started, reset, or + * restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerResetFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerResetFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerResetFromISR() function. If + * xTimerResetFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerResetFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + + +/** + * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * + * Used from application interrupt service routines to defer the execution of a + * function to the RTOS daemon task (the timer service task, hence this function + * is implemented in timers.c and is prefixed with 'Timer'). + * + * Ideally an interrupt service routine (ISR) is kept as short as possible, but + * sometimes an ISR either has a lot of processing to do, or needs to perform + * processing that is not deterministic. In these cases + * xTimerPendFunctionCallFromISR() can be used to defer processing of a function + * to the RTOS daemon task. + * + * A mechanism is provided that allows the interrupt to return directly to the + * task that will subsequently execute the pended callback function. This + * allows the callback function to execute contiguously in time with the + * interrupt - just as if the callback had executed in the interrupt itself. + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param pxHigherPriorityTaskWoken As mentioned above, calling this function + * will result in a message being sent to the timer daemon task. If the + * priority of the timer daemon task (which is set using + * configTIMER_TASK_PRIORITY in FreeRTOSConfig.h) is higher than the priority of + * the currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE within + * xTimerPendFunctionCallFromISR(), indicating that a context switch should be + * requested before the interrupt exits. For that reason + * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the + * example code below. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + * Example usage: + * @verbatim + * + * // The callback function that will execute in the context of the daemon task. + * // Note callback functions must all use this same prototype. + * void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 ) + * { + * BaseType_t xInterfaceToService; + * + * // The interface that requires servicing is passed in the second + * // parameter. The first parameter is not used in this case. + * xInterfaceToService = ( BaseType_t ) ulParameter2; + * + * // ...Perform the processing here... + * } + * + * // An ISR that receives data packets from multiple interfaces + * void vAnISR( void ) + * { + * BaseType_t xInterfaceToService, xHigherPriorityTaskWoken; + * + * // Query the hardware to determine which interface needs processing. + * xInterfaceToService = prvCheckInterfaces(); + * + * // The actual processing is to be deferred to a task. Request the + * // vProcessInterface() callback function is executed, passing in the + * // number of the interface that needs processing. The interface to + * // service is passed in the second parameter. The first parameter is + * // not used in this case. + * xHigherPriorityTaskWoken = pdFALSE; + * xTimerPendFunctionCallFromISR( vProcessInterface, NULL, ( uint32_t ) xInterfaceToService, &xHigherPriorityTaskWoken ); + * + * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context + * // switch should be requested. The macro used is port specific and will + * // be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to + * // the documentation page for the port being used. + * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + * + * } + * @endverbatim + */ +BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ); + + /** + * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * TickType_t xTicksToWait ); + * + * + * Used to defer the execution of a function to the RTOS daemon task (the timer + * service task, hence this function is implemented in timers.c and is prefixed + * with 'Timer'). + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param xTicksToWait Calling this function will result in a message being + * sent to the timer daemon task on a queue. xTicksToWait is the amount of + * time the calling task should remain in the Blocked state (so not using any + * processing time) for space to become available on the timer queue if the + * queue is found to be full. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + */ +BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ); + +/** + * const char * const pcTimerGetTimerName( TimerHandle_t xTimer ); + * + * Returns the name that was assigned to a timer when the timer was created. + * + * @param xTimer The handle of the timer being queried. + * + * @return The name assigned to the timer specified by the xTimer parameter. + */ +const char * pcTimerGetTimerName( TimerHandle_t xTimer ); /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/* + * Functions beyond this part are not part of the public API and are intended + * for use by the kernel only. + */ +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#ifdef __cplusplus +} +#endif +#endif /* TIMERS_H */ + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/list.c b/component/os/freertos/freertos_v8.1.2/Source/list.c new file mode 100644 index 0000000..701472a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/list.c @@ -0,0 +1,204 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#include +#include "FreeRTOS.h" +#include "list.h" + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +void vListInitialise( List_t * const pxList ) +{ + /* The list structure contains a list item which is used to mark the + end of the list. To initialise the list the list end is inserted + as the only list entry. */ + pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + /* The list end value is the highest possible value in the list to + ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + when the list is empty. */ + pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + pxList->uxNumberOfItems = ( UBaseType_t ) 0U; +} +/*-----------------------------------------------------------*/ + +void vListInitialiseItem( ListItem_t * const pxItem ) +{ + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pvContainer = NULL; +} +/*-----------------------------------------------------------*/ + +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) +{ +ListItem_t * const pxIndex = pxList->pxIndex; + + /* Insert a new list item into pxList, but rather than sort the list, + makes the new list item the last item to be removed by a call to + listGET_OWNER_OF_NEXT_ENTRY(). */ + pxNewListItem->pxNext = pxIndex; + pxNewListItem->pxPrevious = pxIndex->pxPrevious; + pxIndex->pxPrevious->pxNext = pxNewListItem; + pxIndex->pxPrevious = pxNewListItem; + + /* Remember which list the item is in. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) +{ +ListItem_t *pxIterator; +const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; + + /* Insert the new list item into the list, sorted in xItemValue order. + + If the list already contains a list item with the same item value then + the new list item should be placed after it. This ensures that TCB's which + are stored in ready lists (all of which have the same xItemValue value) + get an equal share of the CPU. However, if the xItemValue is the same as + the back marker the iteration loop below will not end. This means we need + to guard against this by checking the value first and modifying the + algorithm slightly if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + /* *** NOTE *********************************************************** + If you find your application is crashing here then likely causes are: + 1) Stack overflow - + see http://www.freertos.org/Stacks-and-stack-overflow-checking.html + 2) Incorrect interrupt priority assignment, especially on Cortex-M3 + parts where numerically high priority values denote low actual + interrupt priorities, which can seem counter intuitive. See + configMAX_SYSCALL_INTERRUPT_PRIORITY on http://www.freertos.org/a00110.html + 3) Calling an API function from within a critical section or when + the scheduler is suspended, or calling an API function that does + not end in "FromISR" from an interrupt. + 4) Using a queue or semaphore before it has been initialised or + before the scheduler has been started (are interrupts firing + before vTaskStartScheduler() has been called?). + See http://www.freertos.org/FAQHelp.html for more tips, and ensure + configASSERT() is defined! http://www.freertos.org/a00110.html#configASSERT + **********************************************************************/ + + for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + { + /* There is nothing to do here, we are just iterating to the + wanted insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + item later. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) +{ +/* The list item knows which list it is in. Obtain the list from the list +item. */ +List_t * const pxList = ( List_t * ) pxItemToRemove->pvContainer; + + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxItemToRemove->pvContainer = NULL; + ( pxList->uxNumberOfItems )--; + + return pxList->uxNumberOfItems; +} +/*-----------------------------------------------------------*/ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/Makefile b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/Makefile new file mode 100644 index 0000000..ce6da82 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/Makefile @@ -0,0 +1,32 @@ + +include $(MAKE_INCLUDE_GEN) + +.PHONY: all clean + +MODULE_IFLAGS = + + +#*****************************************************************************# +# Object FILE LIST # +#*****************************************************************************# +OBJS = port.o +ifeq ($(CONFIG_RELEASE_BUILD),y) + OBJS = +else +endif + + +#*****************************************************************************# +# RULES TO GENERATE TARGETS # +#*****************************************************************************# + +# Define the Rules to build the core targets +all: CORE_TARGETS COPY_RAM_OBJS + + +#*****************************************************************************# +# GENERATE OBJECT FILE +#*****************************************************************************# +CORE_TARGETS: $(OBJS) + +-include $(DEPS) diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/port.c new file mode 100644 index 0000000..972668f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/port.c @@ -0,0 +1,745 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM3 port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +/* For backward compatibility, ensure configKERNEL_INTERRUPT_PRIORITY is +defined. The value should also ensure backward compatibility. +FreeRTOS.org versions prior to V4.4.0 did not include this definition. */ +#ifndef configKERNEL_INTERRUPT_PRIORITY + #define configKERNEL_INTERRUPT_PRIORITY 255 +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000UL ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Let the user override the pre-loading of the initial LR with the address of +prvTaskExitError() in case is messes up unwinding of the stack in the +debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__ (( naked )); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__ (( naked )); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__ (( naked )); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " orr r14, #0xd \n" + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + __asm volatile( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. + See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); + + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + exit error function to prevent compiler warnings about a static function + not being called in the case that the application writer overrides this + functionality by defining configTASK_RETURN_ADDRESS. */ + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __asm volatile( "dsb" ); + __asm volatile( "isb" ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__attribute__(( naked )) uint32_t ulPortSetInterruptMask( void ) +{ + __asm volatile \ + ( \ + " mrs r0, basepri \n" \ + " mov r1, %0 \n" \ + " msr basepri, r1 \n" \ + " bx lr \n" \ + :: "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "r0", "r1" \ + ); + + /* This return will not be reached but is necessary to prevent compiler + warnings. */ + return 0; +} +/*-----------------------------------------------------------*/ + +__attribute__(( naked )) void vPortClearInterruptMask( uint32_t ulNewMaskValue ) +{ + __asm volatile \ + ( \ + " msr basepri, r0 \n" \ + " bx lr \n" \ + :::"r0" \ + ); + + /* Just to avoid compiler warnings. */ + ( void ) ulNewMaskValue; +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " stmdb r0!, {r4-r11} \n" /* Save the remaining registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r3, r14} \n" + " mov r0, %0 \n" + " msr basepri, r0 \n" + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r3, r14} \n" + " \n" /* Restore the context, including the critical nesting count. */ + " ldr r1, [r3] \n" + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11} \n" /* Pop the registers. */ + " msr psp, r0 \n" + " isb \n" + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __asm volatile( "cpsid i" ); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__(( weak )) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + + + + + + + + + + + + + + + + + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/portmacro.h new file mode 100644 index 0000000..8f51cfb --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/portmacro.h @@ -0,0 +1,195 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMaskValue ); +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask(0) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) ); + return ucReturn; + } + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/port.c new file mode 100644 index 0000000..1e1aa88 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/port.c @@ -0,0 +1,1246 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM3 port. + *----------------------------------------------------------*/ + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* Constants required to access and manipulate the NVIC. */ +#define portNVIC_SYSTICK_CTRL ( ( volatile uint32_t * ) 0xe000e010 ) +#define portNVIC_SYSTICK_LOAD ( ( volatile uint32_t * ) 0xe000e014 ) +#define portNVIC_SYSPRI2 ( ( volatile uint32_t * ) 0xe000ed20 ) +#define portNVIC_SYSPRI1 ( ( volatile uint32_t * ) 0xe000ed1c ) +#define portNVIC_SYS_CTRL_STATE ( ( volatile uint32_t * ) 0xe000ed24 ) +#define portNVIC_MEM_FAULT_ENABLE ( 1UL << 16UL ) + +/* Constants required to access and manipulate the MPU. */ +#define portMPU_TYPE ( ( volatile uint32_t * ) 0xe000ed90 ) +#define portMPU_REGION_BASE_ADDRESS ( ( volatile uint32_t * ) 0xe000ed9C ) +#define portMPU_REGION_ATTRIBUTE ( ( volatile uint32_t * ) 0xe000edA0 ) +#define portMPU_CTRL ( ( volatile uint32_t * ) 0xe000ed94 ) +#define portEXPECTED_MPU_TYPE_VALUE ( 8UL << 8UL ) /* 8 regions, unified. */ +#define portMPU_ENABLE ( 0x01UL ) +#define portMPU_BACKGROUND_ENABLE ( 1UL << 2UL ) +#define portPRIVILEGED_EXECUTION_START_ADDRESS ( 0UL ) +#define portMPU_REGION_VALID ( 0x10UL ) +#define portMPU_REGION_ENABLE ( 0x01UL ) +#define portPERIPHERALS_START_ADDRESS 0x40000000UL +#define portPERIPHERALS_END_ADDRESS 0x5FFFFFFFUL + +/* Constants required to access and manipulate the SysTick. */ +#define portNVIC_SYSTICK_CLK ( 0x00000004UL ) +#define portNVIC_SYSTICK_INT ( 0x00000002UL ) +#define portNVIC_SYSTICK_ENABLE ( 0x00000001UL ) +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) +#define portNVIC_SVC_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_CONTROL_IF_UNPRIVILEGED ( 0x03 ) +#define portINITIAL_CONTROL_IF_PRIVILEGED ( 0x02 ) + +/* Offsets in the stack to the parameters when inside the SVC handler. */ +#define portOFFSET_TO_PC ( 6 ) + +/* Set the privilege level to user mode if xRunningPrivileged is false. */ +#define portRESET_PRIVILEGE( xRunningPrivileged ) if( xRunningPrivileged != pdTRUE ) __asm volatile ( " mrs r0, control \n orr r0, #1 \n msr control, r0" :::"r0" ) + +/* Each task maintains its own interrupt status in the critical nesting +variable. Note this is not saved as part of the task context as context +switches can only occur when uxCriticalNesting is zero. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. + */ +static void prvSetupTimerInterrupt( void ) PRIVILEGED_FUNCTION; + +/* + * Configure a number of standard MPU regions that are used by all tasks. + */ +static void prvSetupMPU( void ) PRIVILEGED_FUNCTION; + +/* + * Return the smallest MPU region size that a given number of bytes will fit + * into. The region size is returned as the value that should be programmed + * into the region attribute register for that region. + */ +static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) PRIVILEGED_FUNCTION; + +/* + * Checks to see if being called from the context of an unprivileged task, and + * if so raises the privilege level and returns false - otherwise does nothing + * other than return true. + */ +static BaseType_t prvRaisePrivilege( void ) __attribute__(( naked )); + +/* + * Standard FreeRTOS exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__ (( naked )) PRIVILEGED_FUNCTION; +void xPortSysTickHandler( void ) __attribute__ ((optimize("3"))) PRIVILEGED_FUNCTION; +void vPortSVCHandler( void ) __attribute__ (( naked )) PRIVILEGED_FUNCTION; + +/* + * Starts the scheduler by restoring the context of the first task to run. + */ +static void prvRestoreContextOfFirstTask( void ) __attribute__(( naked )) PRIVILEGED_FUNCTION; + +/* + * C portion of the SVC handler. The SVC handler is split between an asm entry + * and a C wrapper for simplicity of coding and maintenance. + */ +static void prvSVCHandler( uint32_t *pulRegisters ) __attribute__(( noinline )) PRIVILEGED_FUNCTION; + +/* + * Prototypes for all the MPU wrappers. + */ +BaseType_t MPU_xTaskGenericCreate( TaskFunction_t pvTaskCode, const char * const pcName, uint16_t usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask, StackType_t *puxStackBuffer, const MemoryRegion_t * const xRegions ); +void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const xRegions ); +void MPU_vTaskDelete( TaskHandle_t pxTaskToDelete ); +void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, TickType_t xTimeIncrement ); +void MPU_vTaskDelay( TickType_t xTicksToDelay ); +UBaseType_t MPU_uxTaskPriorityGet( TaskHandle_t pxTask ); +void MPU_vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority ); +eTaskState MPU_eTaskGetState( TaskHandle_t pxTask ); +void MPU_vTaskSuspend( TaskHandle_t pxTaskToSuspend ); +void MPU_vTaskResume( TaskHandle_t pxTaskToResume ); +void MPU_vTaskSuspendAll( void ); +BaseType_t MPU_xTaskResumeAll( void ); +TickType_t MPU_xTaskGetTickCount( void ); +UBaseType_t MPU_uxTaskGetNumberOfTasks( void ); +void MPU_vTaskList( char *pcWriteBuffer ); +void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ); +void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxTagValue ); +TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ); +BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ); +UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ); +TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ); +BaseType_t MPU_xTaskGetSchedulerState( void ); +TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ); +UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t *pxTaskStatusArray, UBaseType_t uxArraySize, uint32_t *pulTotalRunTime ); +QueueHandle_t MPU_xQueueGenericCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t ucQueueType ); +BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ); +BaseType_t MPU_xQueueGenericReset( QueueHandle_t pxQueue, BaseType_t xNewQueue ); +UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t pxQueue ); +BaseType_t MPU_xQueueGenericReceive( QueueHandle_t pxQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ); +QueueHandle_t MPU_xQueueCreateMutex( void ); +QueueHandle_t MPU_xQueueCreateCountingSemaphore( UBaseType_t uxCountValue, UBaseType_t uxInitialCount ); +BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xBlockTime ); +BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t xMutex ); +BaseType_t MPU_xQueueAltGenericSend( QueueHandle_t pxQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ); +BaseType_t MPU_xQueueAltGenericReceive( QueueHandle_t pxQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ); +void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, char *pcName ); +void MPU_vQueueDelete( QueueHandle_t xQueue ); +void *MPU_pvPortMalloc( size_t xSize ); +void MPU_vPortFree( void *pv ); +void MPU_vPortInitialiseBlocks( void ); +size_t MPU_xPortGetFreeHeapSize( void ); +QueueSetHandle_t MPU_xQueueCreateSet( UBaseType_t uxEventQueueLength ); +QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks ); +BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ); +BaseType_t MPU_xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ); +void* MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ); + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = 0; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 9; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + if( xRunPrivileged == pdTRUE ) + { + *pxTopOfStack = portINITIAL_CONTROL_IF_PRIVILEGED; + } + else + { + *pxTopOfStack = portINITIAL_CONTROL_IF_UNPRIVILEGED; + } + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + /* Assumes psp was in use. */ + __asm volatile + ( + #ifndef USE_PROCESS_STACK /* Code should not be required if a main() is using the process stack. */ + " tst lr, #4 \n" + " ite eq \n" + " mrseq r0, msp \n" + " mrsne r0, psp \n" + #else + " mrs r0, psp \n" + #endif + " b %0 \n" + ::"i"(prvSVCHandler):"r0" + ); +} +/*-----------------------------------------------------------*/ + +static void prvSVCHandler( uint32_t *pulParam ) +{ +uint8_t ucSVCNumber; + + /* The stack contains: r0, r1, r2, r3, r12, r14, the return address and + xPSR. The first argument (r0) is pulParam[ 0 ]. */ + ucSVCNumber = ( ( uint8_t * ) pulParam[ portOFFSET_TO_PC ] )[ -2 ]; + switch( ucSVCNumber ) + { + case portSVC_START_SCHEDULER : *(portNVIC_SYSPRI1) |= portNVIC_SVC_PRI; + prvRestoreContextOfFirstTask(); + break; + + case portSVC_YIELD : *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; + /* Barriers are normally not required + but do ensure the code is completely + within the specified behaviour for the + architecture. */ + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + break; + + case portSVC_RAISE_PRIVILEGE : __asm volatile + ( + " mrs r1, control \n" /* Obtain current control value. */ + " bic r1, #1 \n" /* Set privilege bit. */ + " msr control, r1 \n" /* Write back new control value. */ + :::"r1" + ); + break; + + default : /* Unknown SVC call. */ + break; + } +} +/*-----------------------------------------------------------*/ + +static void prvRestoreContextOfFirstTask( void ) +{ + __asm volatile + ( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" + " ldr r0, [r1] \n" /* The first item in the TCB is the task top of stack. */ + " add r1, r1, #4 \n" /* Move onto the second item in the TCB... */ + " ldr r2, =0xe000ed9c \n" /* Region Base Address register. */ + " ldmia r1!, {r4-r11} \n" /* Read 4 sets of MPU registers. */ + " stmia r2!, {r4-r11} \n" /* Write 4 sets of MPU registers. */ + " ldmia r0!, {r3, r4-r11} \n" /* Pop the registers that are not automatically saved on exception entry. */ + " msr control, r3 \n" + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldr r14, =0xfffffffd \n" /* Load exec return code. */ + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See + http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) ); + + /* Make PendSV and SysTick the same priority as the kernel. */ + *(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI; + *(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI; + + /* Configure the regions in the MPU that are common to all tasks. */ + prvSetupMPU(); + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + __asm volatile( " svc %0 \n" + :: "i" (portSVC_START_SCHEDULER) ); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " mrs r1, control \n" + " stmdb r0!, {r1, r4-r11} \n" /* Save the remaining registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r3, r14} \n" + " mov r0, %0 \n" + " msr basepri, r0 \n" + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r3, r14} \n" + " \n" /* Restore the context. */ + " ldr r1, [r3] \n" + " ldr r0, [r1] \n" /* The first item in the TCB is the task top of stack. */ + " add r1, r1, #4 \n" /* Move onto the second item in the TCB... */ + " ldr r2, =0xe000ed9c \n" /* Region Base Address register. */ + " ldmia r1!, {r4-r11} \n" /* Read 4 sets of MPU registers. */ + " stmia r2!, {r4-r11} \n" /* Write 4 sets of MPU registers. */ + " ldmia r0!, {r3, r4-r11} \n" /* Pop the registers that are not automatically saved on exception entry. */ + " msr control, r3 \n" + " \n" + " msr psp, r0 \n" + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ +uint32_t ulDummy; + + ulDummy = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* Pend a context switch. */ + *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy ); +} +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +static void prvSetupTimerInterrupt( void ) +{ + /* Configure SysTick to interrupt at the requested rate. */ + *(portNVIC_SYSTICK_LOAD) = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + *(portNVIC_SYSTICK_CTRL) = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE; +} +/*-----------------------------------------------------------*/ + +static void prvSetupMPU( void ) +{ +extern uint32_t __privileged_functions_end__[]; +extern uint32_t __FLASH_segment_start__[]; +extern uint32_t __FLASH_segment_end__[]; +extern uint32_t __privileged_data_start__[]; +extern uint32_t __privileged_data_end__[]; + + /* Check the expected MPU is present. */ + if( *portMPU_TYPE == portEXPECTED_MPU_TYPE_VALUE ) + { + /* First setup the entire flash for unprivileged read only access. */ + *portMPU_REGION_BASE_ADDRESS = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + ( portMPU_REGION_VALID ) | + ( portUNPRIVILEGED_FLASH_REGION ); + + *portMPU_REGION_ATTRIBUTE = ( portMPU_REGION_READ_ONLY ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( portMPU_REGION_ENABLE ); + + /* Setup the first 16K for privileged only access (even though less + than 10K is actually being used). This is where the kernel code is + placed. */ + *portMPU_REGION_BASE_ADDRESS = ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */ + ( portMPU_REGION_VALID ) | + ( portPRIVILEGED_FLASH_REGION ); + + *portMPU_REGION_ATTRIBUTE = ( portMPU_REGION_PRIVILEGED_READ_ONLY ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) | + ( portMPU_REGION_ENABLE ); + + /* Setup the privileged data RAM region. This is where the kernel data + is placed. */ + *portMPU_REGION_BASE_ADDRESS = ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */ + ( portMPU_REGION_VALID ) | + ( portPRIVILEGED_RAM_REGION ); + + *portMPU_REGION_ATTRIBUTE = ( portMPU_REGION_PRIVILEGED_READ_WRITE ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) | + ( portMPU_REGION_ENABLE ); + + /* By default allow everything to access the general peripherals. The + system peripherals and registers are protected. */ + *portMPU_REGION_BASE_ADDRESS = ( portPERIPHERALS_START_ADDRESS ) | + ( portMPU_REGION_VALID ) | + ( portGENERAL_PERIPHERALS_REGION ); + + *portMPU_REGION_ATTRIBUTE = ( portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER ) | + ( prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS ) ) | + ( portMPU_REGION_ENABLE ); + + /* Enable the memory fault exception. */ + *portNVIC_SYS_CTRL_STATE |= portNVIC_MEM_FAULT_ENABLE; + + /* Enable the MPU with the background region configured. */ + *portMPU_CTRL |= ( portMPU_ENABLE | portMPU_BACKGROUND_ENABLE ); + } +} +/*-----------------------------------------------------------*/ + +static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) +{ +uint32_t ulRegionSize, ulReturnValue = 4; + + /* 32 is the smallest region size, 31 is the largest valid value for + ulReturnValue. */ + for( ulRegionSize = 32UL; ulReturnValue < 31UL; ( ulRegionSize <<= 1UL ) ) + { + if( ulActualSizeInBytes <= ulRegionSize ) + { + break; + } + else + { + ulReturnValue++; + } + } + + /* Shift the code by one before returning so it can be written directly + into the the correct bit position of the attribute register. */ + return ( ulReturnValue << 1UL ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvRaisePrivilege( void ) +{ + __asm volatile + ( + " mrs r0, control \n" + " tst r0, #1 \n" /* Is the task running privileged? */ + " itte ne \n" + " movne r0, #0 \n" /* CONTROL[0]!=0, return false. */ + " svcne %0 \n" /* Switch to privileged. */ + " moveq r0, #1 \n" /* CONTROL[0]==0, return true. */ + " bx lr \n" + :: "i" (portSVC_RAISE_PRIVILEGE) : "r0" + ); + + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint16_t usStackDepth ) +{ +extern uint32_t __SRAM_segment_start__[]; +extern uint32_t __SRAM_segment_end__[]; +extern uint32_t __privileged_data_start__[]; +extern uint32_t __privileged_data_end__[]; +int32_t lIndex; +uint32_t ul; + + if( xRegions == NULL ) + { + /* No MPU regions are specified so allow access to all RAM. */ + xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress = + ( ( uint32_t ) __SRAM_segment_start__ ) | /* Base address. */ + ( portMPU_REGION_VALID ) | + ( portSTACK_REGION ); + + xMPUSettings->xRegion[ 0 ].ulRegionAttribute = + ( portMPU_REGION_READ_WRITE ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + ( prvGetMPURegionSizeSetting( ( uint32_t ) __SRAM_segment_end__ - ( uint32_t ) __SRAM_segment_start__ ) ) | + ( portMPU_REGION_ENABLE ); + + /* Re-instate the privileged only RAM region as xRegion[ 0 ] will have + just removed the privileged only parameters. */ + xMPUSettings->xRegion[ 1 ].ulRegionBaseAddress = + ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */ + ( portMPU_REGION_VALID ) | + ( portSTACK_REGION + 1 ); + + xMPUSettings->xRegion[ 1 ].ulRegionAttribute = + ( portMPU_REGION_PRIVILEGED_READ_WRITE ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) | + ( portMPU_REGION_ENABLE ); + + /* Invalidate all other regions. */ + for( ul = 2; ul <= portNUM_CONFIGURABLE_REGIONS; ul++ ) + { + xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( portSTACK_REGION + ul ) | portMPU_REGION_VALID; + xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL; + } + } + else + { + /* This function is called automatically when the task is created - in + which case the stack region parameters will be valid. At all other + times the stack parameters will not be valid and it is assumed that the + stack region has already been configured. */ + if( usStackDepth > 0 ) + { + /* Define the region that allows access to the stack. */ + xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress = + ( ( uint32_t ) pxBottomOfStack ) | + ( portMPU_REGION_VALID ) | + ( portSTACK_REGION ); /* Region number. */ + + xMPUSettings->xRegion[ 0 ].ulRegionAttribute = + ( portMPU_REGION_READ_WRITE ) | /* Read and write. */ + ( prvGetMPURegionSizeSetting( ( uint32_t ) usStackDepth * ( uint32_t ) sizeof( StackType_t ) ) ) | + ( portMPU_REGION_CACHEABLE_BUFFERABLE ) | + ( portMPU_REGION_ENABLE ); + } + + lIndex = 0; + + for( ul = 1; ul <= portNUM_CONFIGURABLE_REGIONS; ul++ ) + { + if( ( xRegions[ lIndex ] ).ulLengthInBytes > 0UL ) + { + /* Translate the generic region definition contained in + xRegions into the CM3 specific MPU settings that are then + stored in xMPUSettings. */ + xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = + ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress ) | + ( portMPU_REGION_VALID ) | + ( portSTACK_REGION + ul ); /* Region number. */ + + xMPUSettings->xRegion[ ul ].ulRegionAttribute = + ( prvGetMPURegionSizeSetting( xRegions[ lIndex ].ulLengthInBytes ) ) | + ( xRegions[ lIndex ].ulParameters ) | + ( portMPU_REGION_ENABLE ); + } + else + { + /* Invalidate the region. */ + xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( portSTACK_REGION + ul ) | portMPU_REGION_VALID; + xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL; + } + + lIndex++; + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xTaskGenericCreate( TaskFunction_t pvTaskCode, const char * const pcName, uint16_t usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask, StackType_t *puxStackBuffer, const MemoryRegion_t * const xRegions ) +{ +BaseType_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGenericCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask, puxStackBuffer, xRegions ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +void MPU_vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const xRegions ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskAllocateMPURegions( xTask, xRegions ); + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + void MPU_vTaskDelete( TaskHandle_t pxTaskToDelete ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskDelete( pxTaskToDelete ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + void MPU_vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, TickType_t xTimeIncrement ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskDelayUntil( pxPreviousWakeTime, xTimeIncrement ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + void MPU_vTaskDelay( TickType_t xTicksToDelay ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskDelay( xTicksToDelay ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + UBaseType_t MPU_uxTaskPriorityGet( TaskHandle_t pxTask ) + { + UBaseType_t uxReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + uxReturn = uxTaskPriorityGet( pxTask ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return uxReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + void MPU_vTaskPrioritySet( TaskHandle_t pxTask, UBaseType_t uxNewPriority ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskPrioritySet( pxTask, uxNewPriority ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_eTaskGetState == 1 ) + eTaskState MPU_eTaskGetState( TaskHandle_t pxTask ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + eTaskState eReturn; + + eReturn = eTaskGetState( pxTask ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return eReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + TaskHandle_t MPU_xTaskGetIdleTaskHandle( void ) + { + TaskHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGetIdleTaskHandle(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return eReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + void MPU_vTaskSuspend( TaskHandle_t pxTaskToSuspend ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskSuspend( pxTaskToSuspend ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + void MPU_vTaskResume( TaskHandle_t pxTaskToResume ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskResume( pxTaskToResume ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +void MPU_vTaskSuspendAll( void ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskSuspendAll(); + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xTaskResumeAll( void ) +{ +BaseType_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskResumeAll(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +TickType_t MPU_xTaskGetTickCount( void ) +{ +TickType_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGetTickCount(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t MPU_uxTaskGetNumberOfTasks( void ) +{ +UBaseType_t uxReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + uxReturn = uxTaskGetNumberOfTasks(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + void MPU_vTaskList( char *pcWriteBuffer ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskList( pcWriteBuffer ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + void MPU_vTaskGetRunTimeStats( char *pcWriteBuffer ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskGetRunTimeStats( pcWriteBuffer ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void MPU_vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxTagValue ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vTaskSetApplicationTaskTag( xTask, pxTagValue ); + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t MPU_xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TaskHookFunction_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGetApplicationTaskTag( xTask ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + BaseType_t MPU_xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskCallApplicationTaskHook( xTask, pvParameter ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t MPU_uxTaskGetSystemState( TaskStatus_t *pxTaskStatusArray, UBaseType_t uxArraySize, uint32_t *pulTotalRunTime ) + { + UBaseType_t uxReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + uxReturn = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, pulTotalRunTime ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + UBaseType_t MPU_uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + uxReturn = uxTaskGetStackHighWaterMark( xTask ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return uxReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) + TaskHandle_t MPU_xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGetCurrentTaskHandle(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetSchedulerState == 1 ) + BaseType_t MPU_xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xTaskGetSchedulerState(); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +QueueHandle_t MPU_xQueueGenericCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t ucQueueType ) +{ +QueueHandle_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueGenericCreate( uxQueueLength, uxItemSize, ucQueueType ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xQueueGenericReset( QueueHandle_t pxQueue, BaseType_t xNewQueue ) +{ +BaseType_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueGenericReset( pxQueue, xNewQueue ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ) +{ +BaseType_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, xCopyPosition ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t MPU_uxQueueMessagesWaiting( const QueueHandle_t pxQueue ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); +UBaseType_t uxReturn; + + uxReturn = uxQueueMessagesWaiting( pxQueue ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return uxReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xQueueGenericReceive( QueueHandle_t pxQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); +BaseType_t xReturn; + + xReturn = xQueueGenericReceive( pxQueue, pvBuffer, xTicksToWait, xJustPeeking ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t MPU_xQueuePeekFromISR( QueueHandle_t pxQueue, void * const pvBuffer ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); +BaseType_t xReturn; + + xReturn = xQueuePeekFromISR( pxQueue, pvBuffer ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +void* MPU_xQueueGetMutexHolder( QueueHandle_t xSemaphore ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); +void * xReturn; + + xReturn = ( void * ) xQueueGetMutexHolder( xSemaphore ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + QueueHandle_t MPU_xQueueCreateMutex( void ) + { + QueueHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueCreateMutex( queueQUEUE_TYPE_MUTEX ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_COUNTING_SEMAPHORES == 1 + QueueHandle_t MPU_xQueueCreateCountingSemaphore( UBaseType_t uxCountValue, UBaseType_t uxInitialCount ) + { + QueueHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueCreateCountingSemaphore( uxCountValue, uxInitialCount ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + BaseType_t MPU_xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xBlockTime ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueTakeMutexRecursive( xMutex, xBlockTime ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + BaseType_t MPU_xQueueGiveMutexRecursive( QueueHandle_t xMutex ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueGiveMutexRecursive( xMutex ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + QueueSetHandle_t MPU_xQueueCreateSet( UBaseType_t uxEventQueueLength ) + { + QueueSetHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueCreateSet( uxEventQueueLength ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + QueueSetMemberHandle_t MPU_xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t xBlockTimeTicks ) + { + QueueSetMemberHandle_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueSelectFromSet( xQueueSet, xBlockTimeTicks ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + BaseType_t MPU_xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueAddToSet( xQueueOrSemaphore, xQueueSet ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + BaseType_t MPU_xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueRemoveFromSet( xQueueOrSemaphore, xQueueSet ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + BaseType_t MPU_xQueueAltGenericSend( QueueHandle_t pxQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = BaseType_t xQueueAltGenericSend( pxQueue, pvItemToQueue, xTicksToWait, xCopyPosition ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + BaseType_t MPU_xQueueAltGenericReceive( QueueHandle_t pxQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ) + { + BaseType_t xReturn; + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xQueueAltGenericReceive( pxQueue, pvBuffer, xTicksToWait, xJustPeeking ); + portRESET_PRIVILEGE( xRunningPrivileged ); + return xReturn; + } +#endif +/*-----------------------------------------------------------*/ + +#if configQUEUE_REGISTRY_SIZE > 0 + void MPU_vQueueAddToRegistry( QueueHandle_t xQueue, char *pcName ) + { + BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vQueueAddToRegistry( xQueue, pcName ); + + portRESET_PRIVILEGE( xRunningPrivileged ); + } +#endif +/*-----------------------------------------------------------*/ + +void MPU_vQueueDelete( QueueHandle_t xQueue ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vQueueDelete( xQueue ); + + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +void *MPU_pvPortMalloc( size_t xSize ) +{ +void *pvReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + pvReturn = pvPortMalloc( xSize ); + + portRESET_PRIVILEGE( xRunningPrivileged ); + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void MPU_vPortFree( void *pv ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vPortFree( pv ); + + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +void MPU_vPortInitialiseBlocks( void ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + vPortInitialiseBlocks(); + + portRESET_PRIVILEGE( xRunningPrivileged ); +} +/*-----------------------------------------------------------*/ + +size_t MPU_xPortGetFreeHeapSize( void ) +{ +size_t xReturn; +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + xReturn = xPortGetFreeHeapSize(); + + portRESET_PRIVILEGE( xRunningPrivileged ); + + return xReturn; +} + +/* Functions that the application writer wants to execute in privileged mode +can be defined in application_defined_privileged_functions.h. The functions +must take the same format as those above whereby the privilege state on exit +equals the privilege state on entry. For example: + +void MPU_FunctionName( [parameters ] ) +{ +BaseType_t xRunningPrivileged = prvRaisePrivilege(); + + FunctionName( [parameters ] ); + + portRESET_PRIVILEGE( xRunningPrivileged ); +} +*/ + +#if configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS == 1 + #include "application_defined_privileged_functions.h" +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/portmacro.h new file mode 100644 index 0000000..ee06be9 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3_MPU/portmacro.h @@ -0,0 +1,218 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* MPU specific constants. */ +#define portUSING_MPU_WRAPPERS 1 +#define portPRIVILEGE_BIT ( 0x80000000UL ) + +#define portMPU_REGION_READ_WRITE ( 0x03UL << 24UL ) +#define portMPU_REGION_PRIVILEGED_READ_ONLY ( 0x05UL << 24UL ) +#define portMPU_REGION_READ_ONLY ( 0x06UL << 24UL ) +#define portMPU_REGION_PRIVILEGED_READ_WRITE ( 0x01UL << 24UL ) +#define portMPU_REGION_CACHEABLE_BUFFERABLE ( 0x07UL << 16UL ) +#define portMPU_REGION_EXECUTE_NEVER ( 0x01UL << 28UL ) + +#define portUNPRIVILEGED_FLASH_REGION ( 0UL ) +#define portPRIVILEGED_FLASH_REGION ( 1UL ) +#define portPRIVILEGED_RAM_REGION ( 2UL ) +#define portGENERAL_PERIPHERALS_REGION ( 3UL ) +#define portSTACK_REGION ( 4UL ) +#define portFIRST_CONFIGURABLE_REGION ( 5UL ) +#define portLAST_CONFIGURABLE_REGION ( 7UL ) +#define portNUM_CONFIGURABLE_REGIONS ( ( portLAST_CONFIGURABLE_REGION - portFIRST_CONFIGURABLE_REGION ) + 1 ) +#define portTOTAL_NUM_REGIONS ( portNUM_CONFIGURABLE_REGIONS + 1 ) /* Plus one to make space for the stack region. */ + +#define portSWITCH_TO_USER_MODE() __asm volatile ( " mrs r0, control \n orr r0, #1 \n msr control, r0 " :::"r0" ) + +typedef struct MPU_REGION_REGISTERS +{ + uint32_t ulRegionBaseAddress; + uint32_t ulRegionAttribute; +} xMPU_REGION_REGISTERS; + +/* Plus 1 to create space for the stack region. */ +typedef struct MPU_SETTINGS +{ + xMPU_REGION_REGISTERS xRegion[ portTOTAL_NUM_REGIONS ]; +} xMPU_SETTINGS; + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* SVC numbers for various services. */ +#define portSVC_START_SCHEDULER 0 +#define portSVC_YIELD 1 +#define portSVC_RAISE_PRIVILEGE 2 + +/* Scheduler utilities. */ + +#define portYIELD() __asm volatile ( " SVC %0 \n" :: "i" (portSVC_YIELD) ) +#define portYIELD_WITHIN_API() *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET + +#define portNVIC_INT_CTRL ( ( volatile uint32_t *) 0xe000ed04 ) +#define portNVIC_PENDSVSET 0x10000000 +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) *(portNVIC_INT_CTRL) = portNVIC_PENDSVSET +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + + +/* Critical section management. */ + +/* + * Set basepri to portMAX_SYSCALL_INTERRUPT_PRIORITY without effecting other + * registers. r0 is clobbered. + */ +#define portSET_INTERRUPT_MASK() \ + __asm volatile \ + ( \ + " mov r0, %0 \n" \ + " msr basepri, r0 \n" \ + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY):"r0" \ + ) + +/* + * Set basepri back to 0 without effective other registers. + * r0 is clobbered. FAQ: Setting BASEPRI to 0 is not a bug. Please see + * http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html before disagreeing. + */ +#define portCLEAR_INTERRUPT_MASK() \ + __asm volatile \ + ( \ + " mov r0, #0 \n" \ + " msr basepri, r0 \n" \ + :::"r0" \ + ) + +/* FAQ: Setting BASEPRI to 0 is not a bug. Please see +http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html before disagreeing. */ +#define portSET_INTERRUPT_MASK_FROM_ISR() 0;portSET_INTERRUPT_MASK() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) portCLEAR_INTERRUPT_MASK();(void)x + + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK() +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) + +#define portNOP() + + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/port.c new file mode 100644 index 0000000..90f07d2 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/port.c @@ -0,0 +1,784 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM4F port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __VFP_FP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXEC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Let the user override the pre-loading of the initial LR with the address of +prvTaskExitError() in case is messes up unwinding of the stack in the +debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__ (( naked )); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__ (( naked )); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__ (( naked )); + +/* + * Function to enable the VFP. + */ + static void vPortEnableVFP( void ) __attribute__ (( naked )); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXEC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + __asm volatile( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. + See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); + + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + exit error function to prevent compiler warnings about a static function + not being called in the case that the application writer overrides this + functionality by defining configTASK_RETURN_ADDRESS. */ + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __asm volatile( "dsb" ); + __asm volatile( "isb" ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __asm volatile( "dsb" ); + __asm volatile( "isb" ); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__attribute__(( naked )) uint32_t ulPortSetInterruptMask( void ) +{ + __asm volatile \ + ( \ + " mrs r0, basepri \n" \ + " mov r1, %0 \n" \ + " msr basepri, r1 \n" \ + " bx lr \n" \ + :: "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "r0", "r1" \ + ); + + /* This return will not be reached but is necessary to prevent compiler + warnings. */ + return 0; +} +/*-----------------------------------------------------------*/ + +__attribute__(( naked )) void vPortClearInterruptMask( uint32_t ulNewMaskValue ) +{ + __asm volatile \ + ( \ + " msr basepri, r0 \n" \ + " bx lr \n" \ + :::"r0" \ + ); + + /* Just to avoid compiler warnings. */ + ( void ) ulNewMaskValue; +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */ + " it eq \n" + " vstmdbeq r0!, {s16-s31} \n" + " \n" + " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ + " \n" + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r3} \n" + " mov r0, %0 \n" + " msr basepri, r0 \n" + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r3} \n" + " \n" + " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldr r0, [r1] \n" + " \n" + " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */ + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */ + " it eq \n" + " vldmiaeq r0!, {s16-s31} \n" + " \n" + " msr psp, r0 \n" + " isb \n" + " \n" + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ + #if WORKAROUND_PMU_CM001 == 1 + " push { r14 } \n" + " pop { pc } \n" + #endif + #endif + " \n" + " bx r14 \n" + " \n" + " .align 2 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __attribute__((weak)) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __asm volatile( "cpsid i" ); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __asm volatile( "dsb" ); + __asm volatile( "wfi" ); + __asm volatile( "isb" ); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above the cpsid instruction() + above. */ + __asm volatile( "cpsie i" ); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__(( weak )) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +/* This is a naked function. */ +static void vPortEnableVFP( void ) +{ + __asm volatile + ( + " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */ + " ldr r1, [r0] \n" + " \n" + " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ + " str r1, [r0] \n" + " bx r14 " + ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/portmacro.h new file mode 100644 index 0000000..ea4a54d --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM4F/portmacro.h @@ -0,0 +1,196 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMaskValue ); +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask(0) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) ); + return ucReturn; + } + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/port.c new file mode 100644 index 0000000..ed4050a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/port.c @@ -0,0 +1,648 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM3 port. + *----------------------------------------------------------*/ + +/* IAR includes. */ +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0 + #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* For backward compatibility, ensure configKERNEL_INTERRUPT_PRIORITY is +defined. The value 255 should also ensure backward compatibility. +FreeRTOS.org versions prior to V4.3.0 did not include this definition. */ +#ifndef configKERNEL_INTERRUPT_PRIORITY + #define configKERNEL_INTERRUPT_PRIORITY 255 +#endif + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortSysTickHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +extern void vPortStartFirstTask( void ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __DSB(); + __ISB(); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __DSB(); + __ISB(); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __disable_interrupt(); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above __disable_interrupt() + call above. */ + __enable_interrupt(); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __DSB(); + __WFI(); + __ISB(); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above __disable_interrupt() + call above. */ + __enable_interrupt(); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__weak void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ +void vApplicationIdleHook( void ) +{ + /* Use the idle task to place the CPU into a low power mode. Greater power + saving could be achieved by not including any demo tasks that never block. */ +} + +#include "diag.h" +void vApplicationStackOverflowHook( xTaskHandle pxTask, signed char *pcTaskName ) +{ + /* This function will be called if a task overflows its stack, if + configCHECK_FOR_STACK_OVERFLOW != 0. It might be that the function + parameters have been corrupted, depending on the severity of the stack + overflow. When this is the case pxCurrentTCB can be inspected in the + debugger to find the offending task. */ + DiagPrintf("\n\r[%s] STACK OVERFLOW - TaskName(%s)\n\r", __FUNCTION__, pcTaskName); + for( ;; ); +} + +/*-----------------------------------------------------------*/ + + + + + + + + + + + + + + + + + + + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portasm.s b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portasm.s new file mode 100644 index 0000000..e98645c --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portasm.s @@ -0,0 +1,155 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#include + + RSEG CODE:CODE(2) + thumb + + EXTERN pxCurrentTCB + EXTERN vTaskSwitchContext + + PUBLIC xPortPendSVHandler + PUBLIC ulPortSetInterruptMask + PUBLIC vPortClearInterruptMask + PUBLIC vPortSVCHandler + PUBLIC vPortStartFirstTask + + + +/*-----------------------------------------------------------*/ + +xPortPendSVHandler: + mrs r0, psp + isb + ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */ + ldr r2, [r3] + + stmdb r0!, {r4-r11} /* Save the remaining registers. */ + str r0, [r2] /* Save the new top of stack into the first member of the TCB. */ + + stmdb sp!, {r3, r14} + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + bl vTaskSwitchContext + mov r0, #0 + msr basepri, r0 + ldmia sp!, {r3, r14} + + ldr r1, [r3] + ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ + ldmia r0!, {r4-r11} /* Pop the registers. */ + msr psp, r0 + isb + bx r14 + + +/*-----------------------------------------------------------*/ + +ulPortSetInterruptMask: + mrs r0, basepri + mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r1 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortClearInterruptMask: + msr basepri, r0 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortSVCHandler: + /* Get the location of the current TCB. */ + ldr r3, =pxCurrentTCB + ldr r1, [r3] + ldr r0, [r1] + /* Pop the core registers. */ + ldmia r0!, {r4-r11} + msr psp, r0 + isb + mov r0, #0 + msr basepri, r0 + orr r14, r14, #13 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortStartFirstTask + /* Use the NVIC offset register to locate the stack. */ + ldr r0, =0xE000ED08 + ldr r0, [r0] + ldr r0, [r0] + /* Set the msp back to the start of the stack. */ + msr msp, r0 + /* Call SVC to start the first task, ensuring interrupts are enabled. */ + cpsie i + cpsie f + dsb + isb + svc 0 + + END diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portmacro.h new file mode 100644 index 0000000..b4011df --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM3/portmacro.h @@ -0,0 +1,208 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +// to prevent marco define use in standard header file +#define _NO_DEFINITIONS_IN_HEADER_FILES +#include +//#include "basic_types.h" +#include "hal_misc.h" +#if !defined(__IARSTDLIB__) +#ifndef memcmp +#define memcmp(dst, src, sz) _memcmp(dst, src, sz) +#endif +#ifndef memset +#define memset(dst, val, sz) _memset(dst, val, sz) +#endif +#ifndef memcpy +#define memcpy(dst, src, sz) _memcpy(dst, src, sz) +#endif +#endif +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04UL ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) ) + + /*-----------------------------------------------------------*/ + + #include + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __CLZ( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMask ); + +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask( 0 ) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask( x ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +/* Suppress warnings that are generated by the IAR tools, but cannot be fixed in +the source code because to do so would cause other compilers to generate +warnings. */ +#pragma diag_suppress=Pe191 +#pragma diag_suppress=Pa082 + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/port.c new file mode 100644 index 0000000..101ed94 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/port.c @@ -0,0 +1,652 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM4F port. + *----------------------------------------------------------*/ + +/* Compiler includes. */ +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __ARMVFP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +#if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0 + #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXEC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortSysTickHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +extern void vPortStartFirstTask( void ); + +/* + * Turn the VFP on. + */ +extern void vPortEnableVFP( void ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXEC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __DSB(); + __ISB(); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __DSB(); + __ISB(); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __disable_interrupt(); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above __disable_interrupt() + call above. */ + __enable_interrupt(); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __DSB(); + __WFI(); + __ISB(); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above __disable_interrupt() + call above. */ + __enable_interrupt(); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__weak void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + + + + + + + + + + + + + + + + + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portasm.s b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portasm.s new file mode 100644 index 0000000..d67bc5e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portasm.s @@ -0,0 +1,195 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#include + + RSEG CODE:CODE(2) + thumb + + EXTERN pxCurrentTCB + EXTERN vTaskSwitchContext + + PUBLIC xPortPendSVHandler + PUBLIC ulPortSetInterruptMask + PUBLIC vPortClearInterruptMask + PUBLIC vPortSVCHandler + PUBLIC vPortStartFirstTask + PUBLIC vPortEnableVFP + + +/*-----------------------------------------------------------*/ + +xPortPendSVHandler: + mrs r0, psp + isb + /* Get the location of the current TCB. */ + ldr r3, =pxCurrentTCB + ldr r2, [r3] + + /* Is the task using the FPU context? If so, push high vfp registers. */ + tst r14, #0x10 + it eq + vstmdbeq r0!, {s16-s31} + + /* Save the core registers. */ + stmdb r0!, {r4-r11, r14} + + /* Save the new top of stack into the first member of the TCB. */ + str r0, [r2] + + stmdb sp!, {r3} + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + bl vTaskSwitchContext + mov r0, #0 + msr basepri, r0 + ldmia sp!, {r3} + + /* The first item in pxCurrentTCB is the task top of stack. */ + ldr r1, [r3] + ldr r0, [r1] + + /* Pop the core registers. */ + ldmia r0!, {r4-r11, r14} + + /* Is the task using the FPU context? If so, pop the high vfp registers + too. */ + tst r14, #0x10 + it eq + vldmiaeq r0!, {s16-s31} + + msr psp, r0 + isb + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */ + #if WORKAROUND_PMU_CM001 == 1 + push { r14 } + pop { pc } + #endif + #endif + + bx r14 + + +/*-----------------------------------------------------------*/ + +ulPortSetInterruptMask: + mrs r0, basepri + mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r1 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortClearInterruptMask: + msr basepri, r0 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortSVCHandler: + /* Get the location of the current TCB. */ + ldr r3, =pxCurrentTCB + ldr r1, [r3] + ldr r0, [r1] + /* Pop the core registers. */ + ldmia r0!, {r4-r11, r14} + msr psp, r0 + isb + mov r0, #0 + msr basepri, r0 + bx r14 + +/*-----------------------------------------------------------*/ + +vPortStartFirstTask + /* Use the NVIC offset register to locate the stack. */ + ldr r0, =0xE000ED08 + ldr r0, [r0] + ldr r0, [r0] + /* Set the msp back to the start of the stack. */ + msr msp, r0 + /* Call SVC to start the first task. */ + cpsie i + cpsie f + dsb + isb + svc 0 + +/*-----------------------------------------------------------*/ + +vPortEnableVFP: + /* The FPU enable bits are in the CPACR. */ + ldr.w r0, =0xE000ED88 + ldr r1, [r0] + + /* Enable CP10 and CP11 coprocessors, then save back. */ + orr r1, r1, #( 0xf << 20 ) + str r1, [r0] + bx r14 + + + + END + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portmacro.h new file mode 100644 index 0000000..24599a1 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/IAR/ARM_CM4F/portmacro.h @@ -0,0 +1,193 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #include + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __CLZ( ( uxReadyPriorities ) ) ) + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMask ); + +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask( 0 ) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask( x ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +/* Suppress warnings that are generated by the IAR tools, but cannot be fixed in +the source code because to do so would cause other compilers to generate +warnings. */ +#pragma diag_suppress=Pe191 +#pragma diag_suppress=Pa082 + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/Makefile b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/Makefile new file mode 100644 index 0000000..fcc7b3a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/Makefile @@ -0,0 +1,32 @@ + +include $(MAKE_INCLUDE_GEN) + +.PHONY: all clean + +MODULE_IFLAGS = -I../../include + + +#*****************************************************************************# +# Object FILE LIST # +#*****************************************************************************# +OBJS = heap_4.o +ifeq ($(CONFIG_RELEASE_BUILD),y) + OBJS = +else +endif + + +#*****************************************************************************# +# RULES TO GENERATE TARGETS # +#*****************************************************************************# + +# Define the Rules to build the core targets +all: CORE_TARGETS COPY_RAM_OBJS + + +#*****************************************************************************# +# GENERATE OBJECT FILE +#*****************************************************************************# +CORE_TARGETS: $(OBJS) + +-include $(DEPS) diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_1.c b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_1.c new file mode 100644 index 0000000..65d4f37 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_1.c @@ -0,0 +1,170 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * The simplest possible implementation of pvPortMalloc(). Note that this + * implementation does NOT allow allocated memory to be freed again. + * + * See heap_2.c, heap_3.c and heap_4.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* A few bytes might be lost to byte aligning the heap start address. */ +#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) + +/* Allocate the memory for the heap. */ +static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +static size_t xNextFreeByte = ( size_t ) 0; + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn = NULL; +static uint8_t *pucAlignedHeap = NULL; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + #if portBYTE_ALIGNMENT != 1 + if( xWantedSize & portBYTE_ALIGNMENT_MASK ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + #endif + + vTaskSuspendAll(); + { + if( pucAlignedHeap == NULL ) + { + /* Ensure the heap starts on a correctly aligned boundary. */ + pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); + } + + /* Check there is enough room left for the allocation. */ + if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && + ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ + { + /* Return the next free byte then increment the index past this + block. */ + pvReturn = pucAlignedHeap + xNextFreeByte; + xNextFreeByte += xWantedSize; + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + /* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c and + heap_4.c for alternative implementations, and the memory management pages of + http://www.FreeRTOS.org for more information. */ + ( void ) pv; + + /* Force an assert as it is invalid to call this function. */ + configASSERT( pv == NULL ); +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* Only required when static memory is not cleared. */ + xNextFreeByte = ( size_t ) 0; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return ( configADJUSTED_HEAP_SIZE - xNextFreeByte ); +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_2.c b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_2.c new file mode 100644 index 0000000..a28a560 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_2.c @@ -0,0 +1,299 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that permits + * allocated blocks to be freed, but does not combine adjacent free blocks + * into a single larger block (and so will fragment memory). See heap_4.c for + * an equivalent that does combine adjacent blocks into single larger blocks. + * + * See heap_1.c, heap_3.c and heap_4.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* A few bytes might be lost to byte aligning the heap start address. */ +#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) + +/* + * Initialises the heap structures before their first use. + */ +static void prvHeapInit( void ); + +/* Allocate the memory for the heap. */ +static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; + +/* Define the linked list structure. This is used to link free blocks in order +of their size. */ +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. */ +} BlockLink_t; + + +static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) + +/* Create a couple of list links to mark the start and end of the list. */ +static BlockLink_t xStart, xEnd; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE; + +/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */ + +/* + * Insert a block into the list of free blocks - which is ordered by size of + * the block. Small blocks at the start of the list and large blocks at the end + * of the list. + */ +#define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ +{ \ +BlockLink_t *pxIterator; \ +size_t xBlockSize; \ + \ + xBlockSize = pxBlockToInsert->xBlockSize; \ + \ + /* Iterate through the list until a block is found that has a larger size */ \ + /* than the block we are inserting. */ \ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ + { \ + /* There is nothing to do here - just iterate to the correct position. */ \ + } \ + \ + /* Update the list to include the block being inserted in the correct */ \ + /* position. */ \ + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ + pxIterator->pxNextFreeBlock = pxBlockToInsert; \ +} +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; +static BaseType_t xHeapHasBeenInitialised = pdFALSE; +void *pvReturn = NULL; + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if( xHeapHasBeenInitialised == pdFALSE ) + { + prvHeapInit(); + xHeapHasBeenInitialised = pdTRUE; + } + + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += heapSTRUCT_SIZE; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + } + + if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) ) + { + /* Blocks are stored in byte order - traverse the list from the start + (smallest) block until one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If we found the end marker then a block of adequate size was not found. */ + if( pxBlock != &xEnd ) + { + /* Return the memory space - jumping over the BlockLink_t structure + at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE ); + + /* This block is being returned for use so must be taken out of the + list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new block + following the number of bytes requested. The void cast is + used to prevent byte alignment warnings from the compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); + + /* Calculate the sizes of two blocks split from the single + block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + } + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ +uint8_t *puc = ( uint8_t * ) pv; +BlockLink_t *pxLink; + + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= heapSTRUCT_SIZE; + + /* This unexpected casting is to keep some compilers from issuing + byte alignment warnings. */ + pxLink = ( void * ) puc; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE( pv, pxLink->xBlockSize ); + } + ( void ) xTaskResumeAll(); + } +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit( void ) +{ +BlockLink_t *pxFirstFreeBlock; +uint8_t *pucAlignedHeap; + + /* Ensure the heap starts on a correctly aligned boundary. */ + pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); + + /* xStart is used to hold a pointer to the first item in the list of free + blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; + xStart.xBlockSize = ( size_t ) 0; + + /* xEnd is used to mark the end of the list of free blocks. */ + xEnd.xBlockSize = configADJUSTED_HEAP_SIZE; + xEnd.pxNextFreeBlock = NULL; + + /* To start with there is a single free block that is sized to take up the + entire heap space. */ + pxFirstFreeBlock = ( void * ) pucAlignedHeap; + pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE; + pxFirstFreeBlock->pxNextFreeBlock = &xEnd; +} +/*-----------------------------------------------------------*/ diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_3.c b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_3.c new file mode 100644 index 0000000..8b948bf --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_3.c @@ -0,0 +1,131 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +/* + * Implementation of pvPortMalloc() and vPortFree() that relies on the + * compilers own malloc() and free() implementations. + * + * This file can only be used if the linker is configured to to generate + * a heap memory area. + * + * See heap_1.c, heap_2.c and heap_4.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ + +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn; + + vTaskSuspendAll(); + { + pvReturn = malloc( xWantedSize ); + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + if( pv ) + { + vTaskSuspendAll(); + { + free( pv ); + traceFREE( pv, 0 ); + } + ( void ) xTaskResumeAll(); + } +} + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_4.c b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_4.c new file mode 100644 index 0000000..beed365 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_4.c @@ -0,0 +1,489 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * A sample implementation of pvPortMalloc() and vPortFree() that combines + * (coalescences) adjacent memory blocks as they are freed, and in so doing + * limits memory fragmentation. + * + * See heap_1.c, heap_2.c and heap_3.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +//TODO: remove section when combine BD and BF +#include "section_config.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize * 2 ) ) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ( ( size_t ) 8 ) + +/* Allocate the memory for the heap. */ +SRAM_BF_DATA_SECTION +static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; + +/* Define the linked list structure. This is used to link free blocks in order +of their memory address. */ +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. */ +} BlockLink_t; + +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ); + +/* + * Called automatically to setup the required heap structures the first time + * pvPortMalloc() is called. + */ +static void prvHeapInit( void ); + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory +block must by correctly byte aligned. */ +static const size_t xHeapStructSize = ( ( sizeof( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); + +/* Create a couple of list links to mark the start and end of the list. */ +static BlockLink_t xStart, *pxEnd = NULL; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = 0U; +static size_t xMinimumEverFreeBytesRemaining = 0U; + +/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize +member of an BlockLink_t structure is set then the block belongs to the +application. When the bit is free the block is still part of the free heap +space. */ +static size_t xBlockAllocatedBit = 0; + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; +void *pvReturn = NULL; + + vTaskSuspendAll(); + { + /* If this is the first call to malloc then the heap will require + initialisation to setup the list of free blocks. */ + if( pxEnd == NULL ) + { + prvHeapInit(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Check the requested block size is not so large that the top bit is + set. The top bit of the block size member of the BlockLink_t structure + is used to determine who owns the block - the application or the + kernel, so it must be free. */ + if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) + { + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += xHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) + { + /* Traverse the list from the start (lowest address) block until + one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If the end marker was reached then a block of adequate size + was not found. */ + if( pxBlock != pxEnd ) + { + /* Return the memory space pointed to - jumping over the + BlockLink_t structure at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize ); + + /* This block is being returned for use so must be taken out + of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new + block following the number of bytes requested. The void + cast is used to prevent byte alignment warnings from the + compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); + configASSERT( ( ( ( uint32_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 ); + + /* Calculate the sizes of two blocks split from the + single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) + { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The block is being returned - it is allocated and owned + by the application and has no "next" block. */ + pxBlock->xBlockSize |= xBlockAllocatedBit; + pxBlock->pxNextFreeBlock = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + configASSERT( ( ( ( uint32_t ) pvReturn ) & portBYTE_ALIGNMENT_MASK ) == 0 ); + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void __vPortFree( void *pv ) +{ +uint8_t *puc = ( uint8_t * ) pv; +BlockLink_t *pxLink; + + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= xHeapStructSize; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + /* Check the block is actually allocated. */ + configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); + configASSERT( pxLink->pxNextFreeBlock == NULL ); + + if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) + { + if( pxLink->pxNextFreeBlock == NULL ) + { + /* The block is being returned to the heap - it is no longer + allocated. */ + pxLink->xBlockSize &= ~xBlockAllocatedBit; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE( pv, pxLink->xBlockSize ); + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } +} +/*-----------------------------------------------------------*/ +/* Add by Alfa 2015/02/04 -----------------------------------*/ +static void (*ext_free)( void *p ) = NULL; +static uint32_t ext_upper = 0; +static uint32_t ext_lower = 0; +void vPortSetExtFree( void (*free)( void *p ), uint32_t upper, uint32_t lower ) +{ + ext_free = free; + ext_upper = upper; + ext_lower = lower; +} + +void vPortFree( void *pv ) +{ + if( ((uint32_t)pv >= ext_lower) && ((uint32_t)pv < ext_upper) ){ + // use external free function + if( ext_free ) ext_free( pv ); + }else + __vPortFree( pv ); +} + +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetMinimumEverFreeHeapSize( void ) +{ + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* This just exists to keep the linker quiet. */ +} +/*-----------------------------------------------------------*/ + +static void prvHeapInit( void ) +{ +BlockLink_t *pxFirstFreeBlock; +uint8_t *pucAlignedHeap; +uint32_t ulAddress; +size_t xTotalHeapSize = configTOTAL_HEAP_SIZE; + + /* Ensure the heap starts on a correctly aligned boundary. */ + ulAddress = ( uint32_t ) ucHeap; + + if( ( ulAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + ulAddress += ( portBYTE_ALIGNMENT - 1 ); + ulAddress &= ~portBYTE_ALIGNMENT_MASK; + xTotalHeapSize -= ulAddress - ( uint32_t ) ucHeap; + } + + pucAlignedHeap = ( uint8_t * ) ulAddress; + + /* xStart is used to hold a pointer to the first item in the list of free + blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; + xStart.xBlockSize = ( size_t ) 0; + + /* pxEnd is used to mark the end of the list of free blocks and is inserted + at the end of the heap space. */ + ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalHeapSize; + ulAddress -= xHeapStructSize; + ulAddress &= ~portBYTE_ALIGNMENT_MASK; + pxEnd = ( void * ) ulAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block that is sized to take up the + entire heap space, minus the space taken by pxEnd. */ + pxFirstFreeBlock = ( void * ) pucAlignedHeap; + pxFirstFreeBlock->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlock; + pxFirstFreeBlock->pxNextFreeBlock = pxEnd; + + /* Only one block exists - and it covers the entire usable heap space. */ + xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize; + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) +{ +BlockLink_t *pxIterator; +uint8_t *puc; + + /* Iterate through the list until a block is found that has a higher address + than the block being inserted. */ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) + { + /* Nothing to do here, just iterate to the right position. */ + } + + /* Do the block being inserted, and the block it is being inserted after + make a contiguous block of memory? */ + puc = ( uint8_t * ) pxIterator; + if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) + { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Do the block being inserted, and the block it is being inserted before + make a contiguous block of memory? */ + puc = ( uint8_t * ) pxBlockToInsert; + if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) + { + if( pxIterator->pxNextFreeBlock != pxEnd ) + { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gab, so was merged with the block + before and the block after, then it's pxNextFreeBlock pointer will have + already been set, and should not be set here as that would make it point + to itself. */ + if( pxIterator != pxBlockToInsert ) + { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_5.c b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_5.c new file mode 100644 index 0000000..76e56df --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_5.c @@ -0,0 +1,567 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* + * A sample implementation of pvPortMalloc() that allows the heap to be defined + * across multiple non-contigous blocks and combines (coalescences) adjacent + * memory blocks as they are freed. + * + * See heap_1.c, heap_2.c, heap_3.c and heap_4.c for alternative + * implementations, and the memory management pages of http://www.FreeRTOS.org + * for more information. + * + * Usage notes: + * + * vPortDefineHeapRegions() ***must*** be called before pvPortMalloc(). + * pvPortMalloc() will be called if any task objects (tasks, queues, event + * groups, etc.) are created, therefore vPortDefineHeapRegions() ***must*** be + * called before any other objects are defined. + * + * vPortDefineHeapRegions() takes a single parameter. The parameter is an array + * of HeapRegion_t structures. HeapRegion_t is defined in portable.h as + * + * typedef struct HeapRegion + * { + * uint8_t *pucStartAddress; << Start address of a block of memory that will be part of the heap. + * size_t xSizeInBytes; << Size of the block of memory. + * } HeapRegion_t; + * + * The array is terminated using a NULL zero sized region definition, and the + * memory regions defined in the array ***must*** appear in address order from + * low address to high address. So the following is a valid example of how + * to use the function. + * + * HeapRegion_t xHeapRegions[] = + * { + * { ( uint8_t * ) 0x80000000UL, 0x10000 }, << Defines a block of 0x10000 bytes starting at address 0x80000000 + * { ( uint8_t * ) 0x90000000UL, 0xa0000 }, << Defines a block of 0xa0000 bytes starting at address of 0x90000000 + * { NULL, 0 } << Terminates the array. + * }; + * + * vPortDefineHeapRegions( xHeapRegions ); << Pass the array into vPortDefineHeapRegions(). + * + * Note 0x80000000 is the lower address so appears in the array first. + * + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* Block sizes must not get too small. */ +#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( uxHeapStructSize << 1 ) ) + +/* Assumes 8bit bytes! */ +#define heapBITS_PER_BYTE ( ( size_t ) 8 ) + +/* Define the linked list structure. This is used to link free blocks in order +of their memory address. */ +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. */ +} BlockLink_t; + +/*-----------------------------------------------------------*/ + +/* + * Inserts a block of memory that is being freed into the correct position in + * the list of free memory blocks. The block being freed will be merged with + * the block in front it and/or the block behind it if the memory blocks are + * adjacent to each other. + */ +static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ); + +/*-----------------------------------------------------------*/ + +/* The size of the structure placed at the beginning of each allocated memory +block must by correctly byte aligned. */ +static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); + +/* Create a couple of list links to mark the start and end of the list. */ +static BlockLink_t xStart, *pxEnd = NULL; + +/* Keeps track of the number of free bytes remaining, but says nothing about +fragmentation. */ +static size_t xFreeBytesRemaining = 0; +static size_t xMinimumEverFreeBytesRemaining = 0; + +/* Gets set to the top bit of an size_t type. When this bit in the xBlockSize +member of an BlockLink_t structure is set then the block belongs to the +application. When the bit is free the block is still part of the free heap +space. */ +static size_t xBlockAllocatedBit = 0; + +/* Realtek test code start */ +//TODO: remove section when combine BD and BF +#include "section_config.h" +SRAM_BF_DATA_SECTION +static unsigned char ucHeap[ configTOTAL_HEAP_SIZE ]; + +HeapRegion_t xHeapRegions[] = +{ + { NULL, 0 }, // Defines a block from ucHeap + { NULL, 0 }, + { NULL, 0 } // Terminates the array. +}; +/* Realtek test code end */ + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; +void *pvReturn = NULL; + + /* Realtek test code start */ + if(pxEnd == NULL) + { + // lower StartAddress must put in lower index of array, must sort by address + xHeapRegions[0].pucStartAddress = (uint8_t*)0x10002100; // released image1 text + xHeapRegions[0].xSizeInBytes = 0x1F00; + xHeapRegions[1].pucStartAddress = ucHeap; + xHeapRegions[1].xSizeInBytes = sizeof(ucHeap); + vPortDefineHeapRegions( xHeapRegions ); + } + /* Realtek test code end */ + + /* The heap must be initialised before the first call to + prvPortMalloc(). */ + configASSERT( pxEnd ); + + vTaskSuspendAll(); + { + /* Check the requested block size is not so large that the top bit is + set. The top bit of the block size member of the BlockLink_t structure + is used to determine who owns the block - the application or the + kernel, so it must be free. */ + if( ( xWantedSize & xBlockAllocatedBit ) == 0 ) + { + /* The wanted size is increased so it can contain a BlockLink_t + structure in addition to the requested amount of bytes. */ + if( xWantedSize > 0 ) + { + xWantedSize += uxHeapStructSize; + + /* Ensure that blocks are always aligned to the required number + of bytes. */ + if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) ) + { + /* Traverse the list from the start (lowest address) block until + one of adequate size is found. */ + pxPreviousBlock = &xStart; + pxBlock = xStart.pxNextFreeBlock; + while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) + { + pxPreviousBlock = pxBlock; + pxBlock = pxBlock->pxNextFreeBlock; + } + + /* If the end marker was reached then a block of adequate size + was not found. */ + if( pxBlock != pxEnd ) + { + /* Return the memory space pointed to - jumping over the + BlockLink_t structure at its start. */ + pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + uxHeapStructSize ); + + /* This block is being returned for use so must be taken out + of the list of free blocks. */ + pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; + + /* If the block is larger than required it can be split into + two. */ + if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) + { + /* This block is to be split into two. Create a new + block following the number of bytes requested. The void + cast is used to prevent byte alignment warnings from the + compiler. */ + pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); + + /* Calculate the sizes of two blocks split from the + single block. */ + pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; + pxBlock->xBlockSize = xWantedSize; + + /* Insert the new block into the list of free blocks. */ + prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xFreeBytesRemaining -= pxBlock->xBlockSize; + + if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining ) + { + xMinimumEverFreeBytesRemaining = xFreeBytesRemaining; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The block is being returned - it is allocated and owned + by the application and has no "next" block. */ + pxBlock->xBlockSize |= xBlockAllocatedBit; + pxBlock->pxNextFreeBlock = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void __vPortFree( void *pv ) +{ +uint8_t *puc = ( uint8_t * ) pv; +BlockLink_t *pxLink; + + if( pv != NULL ) + { + /* The memory being freed will have an BlockLink_t structure immediately + before it. */ + puc -= uxHeapStructSize; + + /* This casting is to keep the compiler from issuing warnings. */ + pxLink = ( void * ) puc; + + /* Check the block is actually allocated. */ + configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ); + configASSERT( pxLink->pxNextFreeBlock == NULL ); + + if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 ) + { + if( pxLink->pxNextFreeBlock == NULL ) + { + /* The block is being returned to the heap - it is no longer + allocated. */ + pxLink->xBlockSize &= ~xBlockAllocatedBit; + + vTaskSuspendAll(); + { + /* Add this block to the list of free blocks. */ + xFreeBytesRemaining += pxLink->xBlockSize; + traceFREE( pv, pxLink->xBlockSize ); + prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } +} + +/*-----------------------------------------------------------*/ +/* Add by Alfa 2015/02/04 -----------------------------------*/ +static void (*ext_free)( void *p ) = NULL; +static uint32_t ext_upper = 0; +static uint32_t ext_lower = 0; +void vPortSetExtFree( void (*free)( void *p ), uint32_t upper, uint32_t lower ) +{ + ext_free = free; + ext_upper = upper; + ext_lower = lower; +} + +void vPortFree( void *pv ) +{ + if( ((uint32_t)pv >= ext_lower) && ((uint32_t)pv < ext_upper) ){ + // use external free function + if( ext_free ) ext_free( pv ); + }else + __vPortFree( pv ); +} + +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return xFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetMinimumEverFreeHeapSize( void ) +{ + return xMinimumEverFreeBytesRemaining; +} +/*-----------------------------------------------------------*/ + +static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ) +{ +BlockLink_t *pxIterator; +uint8_t *puc; + + /* Iterate through the list until a block is found that has a higher address + than the block being inserted. */ + for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) + { + /* Nothing to do here, just iterate to the right position. */ + } + + /* Do the block being inserted, and the block it is being inserted after + make a contiguous block of memory? */ + puc = ( uint8_t * ) pxIterator; + if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert ) + { + pxIterator->xBlockSize += pxBlockToInsert->xBlockSize; + pxBlockToInsert = pxIterator; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Do the block being inserted, and the block it is being inserted before + make a contiguous block of memory? */ + puc = ( uint8_t * ) pxBlockToInsert; + if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock ) + { + if( pxIterator->pxNextFreeBlock != pxEnd ) + { + /* Form one big block from the two blocks. */ + pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize; + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock; + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxEnd; + } + } + else + { + pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; + } + + /* If the block being inserted plugged a gab, so was merged with the block + before and the block after, then it's pxNextFreeBlock pointer will have + already been set, and should not be set here as that would make it point + to itself. */ + if( pxIterator != pxBlockToInsert ) + { + pxIterator->pxNextFreeBlock = pxBlockToInsert; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) +{ +BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock; +uint8_t *pucAlignedHeap; +size_t xTotalRegionSize, xTotalHeapSize = 0; +BaseType_t xDefinedRegions = 0; +uint32_t ulAddress; +const HeapRegion_t *pxHeapRegion; + + /* Can only call once! */ + configASSERT( pxEnd == NULL ); + + pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); + + while( pxHeapRegion->xSizeInBytes > 0 ) + { + xTotalRegionSize = pxHeapRegion->xSizeInBytes; + + /* Ensure the heap region starts on a correctly aligned boundary. */ + ulAddress = ( uint32_t ) pxHeapRegion->pucStartAddress; + if( ( ulAddress & portBYTE_ALIGNMENT_MASK ) != 0 ) + { + ulAddress += ( portBYTE_ALIGNMENT - 1 ); + ulAddress &= ~portBYTE_ALIGNMENT_MASK; + + /* Adjust the size for the bytes lost to alignment. */ + xTotalRegionSize -= ulAddress - ( uint32_t ) pxHeapRegion->pucStartAddress; + } + + pucAlignedHeap = ( uint8_t * ) ulAddress; + + /* Set xStart if it has not already been set. */ + if( xDefinedRegions == 0 ) + { + /* xStart is used to hold a pointer to the first item in the list of + free blocks. The void cast is used to prevent compiler warnings. */ + xStart.pxNextFreeBlock = ( BlockLink_t * ) pucAlignedHeap; + xStart.xBlockSize = ( size_t ) 0; + } + else + { + /* Should only get here if one region has already been added to the + heap. */ + configASSERT( pxEnd != NULL ); + + /* Check blocks are passed in with increasing start addresses. */ + configASSERT( ulAddress > ( uint32_t ) pxEnd ); + } + + /* Remember the location of the end marker in the previous region, if + any. */ + pxPreviousFreeBlock = pxEnd; + + /* pxEnd is used to mark the end of the list of free blocks and is + inserted at the end of the region space. */ + ulAddress = ( ( uint32_t ) pucAlignedHeap ) + xTotalRegionSize; + ulAddress -= uxHeapStructSize; + ulAddress &= ~portBYTE_ALIGNMENT_MASK; + pxEnd = ( BlockLink_t * ) ulAddress; + pxEnd->xBlockSize = 0; + pxEnd->pxNextFreeBlock = NULL; + + /* To start with there is a single free block in this region that is + sized to take up the entire heap region minus the space taken by the + free block structure. */ + pxFirstFreeBlockInRegion = ( BlockLink_t * ) pucAlignedHeap; + pxFirstFreeBlockInRegion->xBlockSize = ulAddress - ( uint32_t ) pxFirstFreeBlockInRegion; + pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd; + + /* If this is not the first region that makes up the entire heap space + then link the previous region to this region. */ + if( pxPreviousFreeBlock != NULL ) + { + pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion; + } + + xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize; + + /* Move onto the next HeapRegion_t structure. */ + xDefinedRegions++; + pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] ); + } + + xMinimumEverFreeBytesRemaining = xTotalHeapSize; + xFreeBytesRemaining = xTotalHeapSize; + + /* Check something was actually defined before it is accessed. */ + configASSERT( xTotalHeapSize ); + + /* Work out the position of the top bit in a size_t variable. */ + xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 ); +} + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/port.c new file mode 100644 index 0000000..ccd2511 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/port.c @@ -0,0 +1,724 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM3 port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef configKERNEL_INTERRUPT_PRIORITY + #define configKERNEL_INTERRUPT_PRIORITY 255 +#endif + +#if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0 + #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* The __weak attribute does not work as you might expect with the Keil tools +so the configOVERRIDE_DEFAULT_TICK_CONFIGURATION constant must be set to 1 if +the application writer wants to provide their own implementation of +vPortSetupTimerInterrupt(). Ensure configOVERRIDE_DEFAULT_TICK_CONFIGURATION +is defined. */ +#ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION + #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0 +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) + +/* Constants used with memory barrier intrinsics. */ +#define portSY_FULL_READ_WRITE ( 15 ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvStartFirstTask( void ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( uint8_t * ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */ + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ + + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +__asm void vPortSVCHandler( void ) +{ + PRESERVE8 + + ldr r3, =pxCurrentTCB /* Restore the context. */ + ldr r1, [r3] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ + ldmia r0!, {r4-r11} /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + msr psp, r0 /* Restore the task stack pointer. */ + isb + mov r0, #0 + msr basepri, r0 + orr r14, #0xd + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm void prvStartFirstTask( void ) +{ + PRESERVE8 + + /* Use the NVIC offset register to locate the stack. */ + ldr r0, =0xE000ED08 + ldr r0, [r0] + ldr r0, [r0] + /* Set the msp back to the start of the stack. */ + msr msp, r0 + /* Globally enable interrupts. */ + cpsie i + cpsie f + dsb + isb + /* Call SVC to start the first task. */ + svc 0 + nop + nop +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + prvStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __dsb( portSY_FULL_READ_WRITE ); + __isb( portSY_FULL_READ_WRITE ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __dsb( portSY_FULL_READ_WRITE ); + __isb( portSY_FULL_READ_WRITE ); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__asm void xPortPendSVHandler( void ) +{ + extern uxCriticalNesting; + extern pxCurrentTCB; + extern vTaskSwitchContext; + + PRESERVE8 + + mrs r0, psp + isb + + ldr r3, =pxCurrentTCB /* Get the location of the current TCB. */ + ldr r2, [r3] + + stmdb r0!, {r4-r11} /* Save the remaining registers. */ + str r0, [r2] /* Save the new top of stack into the first member of the TCB. */ + + stmdb sp!, {r3, r14} + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + bl vTaskSwitchContext + mov r0, #0 + msr basepri, r0 + ldmia sp!, {r3, r14} + + ldr r1, [r3] + ldr r0, [r1] /* The first item in pxCurrentTCB is the task top of stack. */ + ldmia r0!, {r4-r11} /* Pop the registers and the critical nesting count. */ + msr psp, r0 + isb + bx r14 + nop +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __disable_irq(); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above __disable_irq() call + above. */ + __enable_irq(); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __dsb( portSY_FULL_READ_WRITE ); + __wfi(); + __isb( portSY_FULL_READ_WRITE ); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above __disable_irq() call + above. */ + __enable_irq(); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ + +/*-----------------------------------------------------------*/ + +/* + * Setup the SysTick timer to generate the tick interrupts at the required + * frequency. + */ +#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 + + void vPortSetupTimerInterrupt( void ) + { + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); + } + +#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ +/*-----------------------------------------------------------*/ + +__asm uint32_t ulPortSetInterruptMask( void ) +{ + PRESERVE8 + + mrs r0, basepri + mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r1 + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm void vPortClearInterruptMask( uint32_t ulNewMask ) +{ + PRESERVE8 + + msr basepri, r0 + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm uint32_t vPortGetIPSR( void ) +{ + PRESERVE8 + + mrs r0, ipsr + bx r14 +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + ulCurrentInterrupt = vPortGetIPSR(); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/portmacro.h new file mode 100644 index 0000000..1e97c3e --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM3/portmacro.h @@ -0,0 +1,185 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMask ); +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask( 0 ) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Port specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Check the configuration. */ + #if( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __clz( ( uxReadyPriorities ) ) ) + +#endif /* taskRECORD_READY_PRIORITY */ +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/port.c b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/port.c new file mode 100644 index 0000000..939522b --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/port.c @@ -0,0 +1,803 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the ARM CM4F port. + *----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __TARGET_FPU_VFP + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +#if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0 + #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html +#endif + +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#else + /* The way the SysTick is clocked is not modified in case it is not the same + as the core. */ + #define portNVIC_SYSTICK_CLK_BIT ( 0 ) +#endif + +/* The __weak attribute does not work as you might expect with the Keil tools +so the configOVERRIDE_DEFAULT_TICK_CONFIGURATION constant must be set to 1 if +the application writer wants to provide their own implementation of +vPortSetupTimerInterrupt(). Ensure configOVERRIDE_DEFAULT_TICK_CONFIGURATION +is defined. */ +#ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION + #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0 +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( * ( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( * ( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( * ( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0x1FUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXEC_RETURN ( 0xfffffffd ) + +/* Constants used with memory barrier intrinsics. */ +#define portSY_FULL_READ_WRITE ( 15 ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have +occurred while the SysTick counter is stopped during tickless idle +calculations. */ +#define portMISSED_COUNTS_FACTOR ( 45UL ) + +/* Each task maintains its own interrupt status in the critical nesting +variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvStartFirstTask( void ); + +/* + * Functions defined in portasm.s to enable the VFP. + */ +static void prvEnableVFP( void ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* + * The number of SysTick increments that make up one tick period. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if configUSE_TICKLESS_IDLE == 1 + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( uint8_t * ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pxCode; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) prvTaskExitError; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXEC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + /* A function that implements a task must not exit or attempt to return to + its caller as there is nothing to return to. If a task wants to exit it + should instead call vTaskDelete( NULL ). + + Artificially force an assert() to be triggered if configASSERT() is + defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + for( ;; ); +} +/*-----------------------------------------------------------*/ + +__asm void vPortSVCHandler( void ) +{ + PRESERVE8 + + /* Get the location of the current TCB. */ + ldr r3, =pxCurrentTCB + ldr r1, [r3] + ldr r0, [r1] + /* Pop the core registers. */ + ldmia r0!, {r4-r11, r14} + msr psp, r0 + isb + mov r0, #0 + msr basepri, r0 + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm void prvStartFirstTask( void ) +{ + PRESERVE8 + + /* Use the NVIC offset register to locate the stack. */ + ldr r0, =0xE000ED08 + ldr r0, [r0] + ldr r0, [r0] + /* Set the msp back to the start of the stack. */ + msr msp, r0 + /* Globally enable interrupts. */ + cpsie i + cpsie f + dsb + isb + /* Call SVC to start the first task. */ + svc 0 + nop + nop +} +/*-----------------------------------------------------------*/ + +__asm void prvEnableVFP( void ) +{ + PRESERVE8 + + /* The FPU enable bits are in the CPACR. */ + ldr.w r0, =0xE000ED88 + ldr r1, [r0] + + /* Enable CP10 and CP11 coprocessors, then save back. */ + orr r1, r1, #( 0xf << 20 ) + str r1, [r0] + bx r14 + nop +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + #if( configASSERT_DEFINED == 1 ) + { + volatile uint32_t ulOriginalPriority; + volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + functions can be called. ISR safe functions are those that end in + "FromISR". FreeRTOS maintains separate thread and ISR API functions to + ensure interrupt entry is as fast and simple as possible. + + Save the interrupt priority value that is about to be clobbered. */ + ulOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Calculate the maximum acceptable priority group value for the number + of bits read back. */ + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulMaxPRIGROUPValue--; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + /* Shift the priority group value back to its position within the AIRCR + register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + value. */ + *pucFirstUserPriorityRegister = ulOriginalPriority; + } + #endif /* conifgASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; + portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + prvEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvStartFirstTask(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + /* Set a PendSV to request a context switch. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + + /* Barriers are normally not required but do ensure the code is completely + within the specified behaviour for the architecture. */ + __dsb( portSY_FULL_READ_WRITE ); + __isb( portSY_FULL_READ_WRITE ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + __dsb( portSY_FULL_READ_WRITE ); + __isb( portSY_FULL_READ_WRITE ); + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +__asm void xPortPendSVHandler( void ) +{ + extern uxCriticalNesting; + extern pxCurrentTCB; + extern vTaskSwitchContext; + + PRESERVE8 + + mrs r0, psp + isb + /* Get the location of the current TCB. */ + ldr r3, =pxCurrentTCB + ldr r2, [r3] + + /* Is the task using the FPU context? If so, push high vfp registers. */ + tst r14, #0x10 + it eq + vstmdbeq r0!, {s16-s31} + + /* Save the core registers. */ + stmdb r0!, {r4-r11, r14} + + /* Save the new top of stack into the first member of the TCB. */ + str r0, [r2] + + stmdb sp!, {r3} + mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r0 + bl vTaskSwitchContext + mov r0, #0 + msr basepri, r0 + ldmia sp!, {r3} + + /* The first item in pxCurrentTCB is the task top of stack. */ + ldr r1, [r3] + ldr r0, [r1] + + /* Pop the core registers. */ + ldmia r0!, {r4-r11, r14} + + /* Is the task using the FPU context? If so, pop the high vfp registers + too. */ + tst r14, #0x10 + it eq + vldmiaeq r0!, {s16-s31} + + msr psp, r0 + isb + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */ + #if WORKAROUND_PMU_CM001 == 1 + push { r14 } + pop { pc } + nop + #endif + #endif + + bx r14 + nop +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + executes all interrupts must be unmasked. There is therefore no need to + save and then restore the interrupt mask value as its value is already + known. */ + ( void ) portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( 0 ); +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE == 1 + + __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Stop the SysTick momentarily. The time the SysTick is stopped for + is accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT; + + /* Calculate the reload value required to wait xExpectedIdleTime + tick periods. -1 is used because this code will execute part way + through one of the tick periods. */ + ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + method as that will mask interrupts that should exit sleep mode. */ + __disable_irq(); + + /* If a context switch is pending or a task is waiting for the scheduler + to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Restart from whatever is left in the count register to complete + this tick period. */ + portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Reset the reload register to the value required for normal tick + periods. */ + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + + /* Re-enable interrupts - see comments above __disable_irq() call + above. */ + __enable_irq(); + } + else + { + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + set its parameter to 0 to indicate that its implementation contains + its own wait for interrupt or wait for event instruction, and so wfi + should not be executed again. However, the original expected idle + time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + if( xModifiableIdleTime > 0 ) + { + __dsb( portSY_FULL_READ_WRITE ); + __wfi(); + __isb( portSY_FULL_READ_WRITE ); + } + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Stop SysTick. Again, the time the SysTick is stopped for is + accounted for as best it can be, but using the tickless mode will + inevitably result in some tiny drift of the time maintained by the + kernel with respect to calendar time. */ + ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG; + portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT ); + + /* Re-enable interrupts - see comments above __disable_irq() call + above. */ + __enable_irq(); + + if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt has already executed, and the SysTick + count reloaded with ulReloadValue. Reset the + portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick + period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + underflowed because the post sleep hook did something + that took too long. */ + if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* The tick interrupt handler will already have pended the tick + processing in the kernel. As the pending tick will be + processed as soon as this function exits, the tick value + maintained by the tick is stepped forward by one less than the + time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. + Work out how long the sleep lasted rounded to complete tick + periods (not the ulReload value which accounted for part + ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG; + + /* How many complete tick periods passed while the processor + was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG + again, then set portNVIC_SYSTICK_LOAD_REG back to its standard + value. The critical section is used to ensure the tick interrupt + can only execute once in the case that the reload register is near + zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portENTER_CRITICAL(); + { + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + vTaskStepTick( ulCompleteTickPeriods ); + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + portEXIT_CRITICAL(); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ + +/*-----------------------------------------------------------*/ + +/* + * Setup the SysTick timer to generate the tick interrupts at the required + * frequency. + */ +#if configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 + + void vPortSetupTimerInterrupt( void ) + { + /* Calculate the constants required to configure the tick interrupt. */ + #if configUSE_TICKLESS_IDLE == 1 + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); + } + +#endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */ +/*-----------------------------------------------------------*/ + +__asm uint32_t ulPortSetInterruptMask( void ) +{ + PRESERVE8 + + mrs r0, basepri + mov r1, #configMAX_SYSCALL_INTERRUPT_PRIORITY + msr basepri, r1 + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm void vPortClearInterruptMask( uint32_t ulNewMask ) +{ + PRESERVE8 + + msr basepri, r0 + bx r14 +} +/*-----------------------------------------------------------*/ + +__asm uint32_t vPortGetIPSR( void ) +{ + PRESERVE8 + + mrs r0, ipsr + bx r14 +} +/*-----------------------------------------------------------*/ + +#if( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + ulCurrentInterrupt = vPortGetIPSR(); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + an interrupt that has been assigned a priority above + configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + function. ISR safe FreeRTOS API functions must *only* be called + from interrupts that have been assigned a priority at or below + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Numerically low interrupt priority numbers represent logically high + interrupt priorities, therefore the priority of the interrupt must + be set to a value equal to or numerically *higher* than + configMAX_SYSCALL_INTERRUPT_PRIORITY. + + Interrupts that use the FreeRTOS API must not be left at their + default priority of zero as that is the highest possible priority, + which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + and therefore also guaranteed to be invalid. + + FreeRTOS maintains separate thread and ISR API functions to ensure + interrupt entry is as fast and simple as possible. + + The following links provide detailed information: + http://www.freertos.org/RTOS-Cortex-M3-M4.html + http://www.freertos.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + that define each interrupt's priority to be split between bits that + define the interrupt's pre-emption priority bits and bits that define + the interrupt's sub-priority. For simplicity all bits must be defined + to be pre-emption priority bits. The following assertion will fail if + this is not the case (if some bits represent a sub-priority). + + If the application only uses CMSIS libraries for interrupt + configuration then the correct setting can be achieved on all Cortex-M + devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + scheduler. Note however that some vendor specific peripheral libraries + assume a non-zero priority group setting, in which cases using a value + of zero will result in unpredicable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ + +void vApplicationStackOverflowHook( xTaskHandle pxTask, signed char *pcTaskName ) +{ + /* This function will be called if a task overflows its stack, if + configCHECK_FOR_STACK_OVERFLOW != 0. It might be that the function + parameters have been corrupted, depending on the severity of the stack + overflow. When this is the case pxCurrentTCB can be inspected in the + debugger to find the offending task. */ + printf("\n\r[%s] STACK OVERFLOW - TaskName(%s)\n\r", __FUNCTION__, pcTaskName); + for( ;; ); +} diff --git a/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/portmacro.h b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/portmacro.h new file mode 100644 index 0000000..91dfb7f --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/portable/RVDS/ARM_CM4F/portmacro.h @@ -0,0 +1,186 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE uint32_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff +#else + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); +#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) +#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) +#define portYIELD() vPortYield() +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern uint32_t ulPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( uint32_t ulNewMask ); +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portDISABLE_INTERRUPTS() ulPortSetInterruptMask() +#define portENABLE_INTERRUPTS() vPortClearInterruptMask( 0 ) +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +#define portSET_INTERRUPT_MASK_FROM_ISR() ulPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) + +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ +#ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) +#endif +/*-----------------------------------------------------------*/ + +/* Port specific optimisations. */ +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#endif + +#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + + /* Check the configuration. */ + #if 0//( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + + /* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + + /*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __clz( ( uxReadyPriorities ) ) ) + +#endif /* taskRECORD_READY_PRIORITY */ +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not necessary for to use this port. They are defined so the common demo files +(which build with all the ports) will build. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +#ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() +#endif + +/* portNOP() is not required by this port. */ +#define portNOP() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/component/os/freertos/freertos_v8.1.2/Source/queue.c b/component/os/freertos/freertos_v8.1.2/Source/queue.c new file mode 100644 index 0000000..9bc9b4a --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/queue.c @@ -0,0 +1,2439 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#if ( configUSE_CO_ROUTINES == 1 ) + #include "croutine.h" +#endif + +/* Lint e961 and e750 are suppressed as a MISRA exception justified because the +MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the +header files above, but not in this file, in order to generate the correct +privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ + + +/* Constants used with the xRxLock and xTxLock structure members. */ +#define queueUNLOCKED ( ( BaseType_t ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( BaseType_t ) 0 ) + +/* When the Queue_t structure is used to represent a base queue its pcHead and +pcTail members are used as pointers into the queue storage area. When the +Queue_t structure is used to represent a mutex pcHead and pcTail pointers are +not necessary, and the pcHead pointer is set to NULL to indicate that the +pcTail pointer actually points to the mutex holder (if any). Map alternative +names to the pcHead and pcTail structure members to ensure the readability of +the code is maintained despite this dual use of two structure members. An +alternative implementation would be to use a union, but use of a union is +against the coding standard (although an exception to the standard has been +permitted where the dual use also significantly changes the type of the +structure member). */ +#define pxMutexHolder pcTail +#define uxQueueType pcHead +#define queueQUEUE_IS_MUTEX NULL + +/* Semaphores do not actually store or copy data, so have an item size of +zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + +#if( configUSE_PREEMPTION == 0 ) + /* If the cooperative scheduler is being used then a yield should not be + performed just because a higher priority task has been woken. */ + #define queueYIELD_IF_USING_PREEMPTION() +#else + #define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() +#endif + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. + */ +typedef struct QueueDefinition +{ + int8_t *pcHead; /*< Points to the beginning of the queue storage area. */ + int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */ + + union /* Use of a union is an exception to the coding standard to ensure two mutually exclusive structure members don't appear simultaneously (wasting RAM). */ + { + int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ + UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ + } u; + + List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */ + UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */ + + volatile BaseType_t xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + volatile BaseType_t xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + struct QueueDefinition *pxQueueSetContainer; + #endif + +} xQUEUE; + +/* The old xQUEUE name is maintained above then typedefed to the new Queue_t +name below to enable the use of older kernel aware debuggers. */ +typedef xQUEUE Queue_t; + +/*-----------------------------------------------------------*/ + +/* + * The queue registry is just a means for kernel aware debuggers to locate + * queue structures. It has no other purpose so is an optional component. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + /* The type stored within the queue registry array. This allows a name + to be assigned to each queue making kernel aware debugging a little + more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + const char *pcQueueName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + QueueHandle_t xHandle; + } xQueueRegistryItem; + + /* The old xQueueRegistryItem name is maintained above then typedefed to the + new xQueueRegistryItem name below to enable the use of older kernel aware + debuggers. */ + typedef xQueueRegistryItem QueueRegistryItem_t; + + /* The queue registry is simply an array of QueueRegistryItem_t structures. + The pcQueueName member of a structure being NULL is indicative of the + array position being vacant. */ + QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; + +#endif /* configQUEUE_REGISTRY_SIZE */ + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Copies an item into the queue, either at the front of the queue or the + * back of the queue. + */ +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) PRIVILEGED_FUNCTION; + +/* + * Copies an item out of a queue. + */ +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; + +#if ( configUSE_QUEUE_SETS == 1 ) + /* + * Checks to see if a queue is a member of a queue set, and if so, notifies + * the queue set that the queue contains data. + */ + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +#endif + +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ + taskENTER_CRITICAL(); \ + { \ + if( ( pxQueue )->xRxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->xRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( ( pxQueue )->xTxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->xTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL() +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) +{ +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + pxQueue->pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); + pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; + pxQueue->pcWriteTo = pxQueue->pcHead; + pxQueue->u.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - ( UBaseType_t ) 1U ) * pxQueue->uxItemSize ); + pxQueue->xRxLock = queueUNLOCKED; + pxQueue->xTxLock = queueUNLOCKED; + + if( xNewQueue == pdFALSE ) + { + /* If there are tasks blocked waiting to read from the queue, then + the tasks will remain blocked as after this function exits the queue + will still be empty. If there are tasks blocked waiting to write to + the queue, then one should be unblocked as after this function exits + it will be possible to write to it. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Ensure the event queues start in the correct state. */ + vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); + } + } + taskEXIT_CRITICAL(); + + /* A value is returned for calling semantic consistency with previous + versions. */ + return pdPASS; +} +/*-----------------------------------------------------------*/ + +QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) +{ +Queue_t *pxNewQueue; +size_t xQueueSizeInBytes; +QueueHandle_t xReturn = NULL; + + /* Remove compiler warnings about unused parameters should + configUSE_TRACE_FACILITY not be set to 1. */ + ( void ) ucQueueType; + + /* Allocate the new queue structure. */ + if( uxQueueLength > ( UBaseType_t ) 0 ) + { + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) ); + if( pxNewQueue != NULL ) + { + /* Create the list of pointers to queue items. The queue is one byte + longer than asked for to make wrap checking easier/faster. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + pxNewQueue->pcHead = ( int8_t * ) pvPortMalloc( xQueueSizeInBytes ); + if( pxNewQueue->pcHead != NULL ) + { + /* Initialise the queue members as described above where the + queue type is defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxNewQueue->ucQueueType = ucQueueType; + } + #endif /* configUSE_TRACE_FACILITY */ + + #if( configUSE_QUEUE_SETS == 1 ) + { + pxNewQueue->pxQueueSetContainer = NULL; + } + #endif /* configUSE_QUEUE_SETS */ + + traceQUEUE_CREATE( pxNewQueue ); + xReturn = pxNewQueue; + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + vPortFree( pxNewQueue ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + configASSERT( xReturn ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + + /* Prevent compiler warnings about unused parameters if + configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; + + /* Allocate the new queue structure. */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) ); + if( pxNewQueue != NULL ) + { + /* Information required for priority inheritance. */ + pxNewQueue->pxMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + + /* Queues used as a mutex no data is actually copied into or out + of the queue. */ + pxNewQueue->pcWriteTo = NULL; + pxNewQueue->u.pcReadFrom = NULL; + + /* Each mutex has a length of 1 (like a binary semaphore) and + an item size of 0 as nothing is actually copied into or out + of the mutex. */ + pxNewQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; + pxNewQueue->uxLength = ( UBaseType_t ) 1U; + pxNewQueue->uxItemSize = ( UBaseType_t ) 0U; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxNewQueue->ucQueueType = ucQueueType; + } + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + { + pxNewQueue->pxQueueSetContainer = NULL; + } + #endif + + /* Ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + traceCREATE_MUTEX( pxNewQueue ); + + /* Start with the semaphore in the expected state. */ + ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + + configASSERT( pxNewQueue ); + return pxNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + void* xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + { + void *pxReturn; + + /* This function is called by xSemaphoreGetMutexHolder(), and should not + be called directly. Note: This is a good way of determining if the + calling task is the mutex holder, but not a good way of determining the + identity of the mutex holder, as the holder may change between the + following critical section exiting and the function returning. */ + taskENTER_CRITICAL(); + { + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( void * ) ( ( Queue_t * ) xSemaphore )->pxMutexHolder; + } + else + { + pxReturn = NULL; + } + } + taskEXIT_CRITICAL(); + + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); + + /* If this is the task that holds the mutex then pxMutexHolder will not + change outside of this task. If this task does not hold the mutex then + pxMutexHolder can never coincidentally equal the tasks handle, and as + this is the only condition we are interested in it does not matter if + pxMutexHolder is accessed simultaneously by another task. Therefore no + mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */ + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); + + /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to + the task handle, therefore no underflow check is required. Also, + uxRecursiveCallCount is only modified by the mutex holder, and as + there can only be one, no mutual exclusion is required to modify the + uxRecursiveCallCount member. */ + ( pxMutex->u.uxRecursiveCallCount )--; + + /* Have we unwound the call count? */ + if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + { + /* Return the mutex. This will automatically unblock any other + task that might be waiting to access the mutex. */ + ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + /* The mutex cannot be given because the calling task is not the + holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); + + /* Comments regarding mutual exclusion as per those within + xQueueGiveMutexRecursive(). */ + + traceTAKE_MUTEX_RECURSIVE( pxMutex ); + + if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ + { + ( pxMutex->u.uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE ); + + /* pdPASS will only be returned if the mutex was successfully + obtained. The calling task may have entered the Blocked state + before reaching here. */ + if( xReturn == pdPASS ) + { + ( pxMutex->u.uxRecursiveCallCount )++; + } + else + { + traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_COUNTING_SEMAPHORES == 1 ) + + QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) + { + QueueHandle_t xHandle; + + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); + + xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + configASSERT( xHandle ); + return xHandle; + } + +#endif /* configUSE_COUNTING_SEMAPHORES */ +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) +{ +BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? The running task must be + the highest priority task wanting to access the queue. If + the head item in the queue is to be overwritten then it does + not matter if the queue is full. */ + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + traceQUEUE_SEND( pxQueue ); + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) == pdTRUE ) + { + /* The queue is a member of a queue set, and posting + to the queue set caused a higher priority task to + unblock. A context switch is required. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to + do this from within the critical section - the + kernel takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + executed if the task was holding multiple mutexes + and the mutexes were given back in an order that is + different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to do + this from within the critical section - the kernel + takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + executed if the task was holding multiple mutexes and + the mutexes were given back in an order that is + different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was full and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the + scheduler is suspended the task will go onto the pending + ready last instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + ready list into the ready list - so it is feasible that this + task is already in a ready list before it yields - in which + case the yield will not cause a context switch unless there + is also a higher priority task in the pending ready list. */ + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* Return to the original privilege level before exiting the + function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_ALTERNATIVE_API == 1 ) + + BaseType_t xQueueAltGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, BaseType_t xCopyPosition ) + { + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND( pxQueue ); + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + taskEXIT_CRITICAL(); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } + taskEXIT_CRITICAL(); + } + } + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_ALTERNATIVE_API == 1 ) + + BaseType_t xQueueAltGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, BaseType_t xJustPeeking ) + { + BaseType_t xEntryTimeSet = pdFALSE; + TimeOut_t xTimeOut; + int8_t *pcOriginalReadPosition; + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + for( ;; ) + { + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember our read position in case we are just peeking. */ + pcOriginalReadPosition = pxQueue->u.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Data is actually being removed (not just peeked). */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = ( int8_t * ) xTaskGetCurrentTaskHandle(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* We are not removing the data, so reset our read + pointer. */ + pxQueue->u.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + taskENTER_CRITICAL(); + { + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } + taskEXIT_CRITICAL(); + } + } + + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* Similar to xQueueGenericSend, except without blocking if there is no room + in the queue. Also don't directly wake a task that was blocked on a queue + read, instead return a flag to say whether a context switch is required or + not (i.e. has a task with a higher priority than us been woken by this + post). */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + if( prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ) != pdFALSE ) + { + /* This is a special case that can only be executed if a task + holds multiple mutexes and then gives the mutexes back in an + order that is different to that in which they were taken. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* The event list is not altered if the queue is locked. This will + be done when the queue is unlocked later. */ + if( pxQueue->xTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) == pdTRUE ) + { + /* The queue is a member of a queue set, and posting + to the queue set caused a higher priority task to + unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + ++( pxQueue->xTxLock ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there data in the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position in case the queue is only being + peeked. */ + pcOriginalReadPosition = pxQueue->u.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Actually removing data, not just peeking. */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read + pointer. */ + pxQueue->u.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + taskENTER_CRITICAL(); + { + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Cannot block in an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + --( pxQueue->uxMessagesWaiting ); + + /* If the queue is locked the event list will not be modified. + Instead update the lock count so the task that unlocks the queue + will know that an ISR has removed data while the queue was + locked. */ + if( pxQueue->xRxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + force a context switch. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was removed while it was locked. */ + ++( pxQueue->xRxLock ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Cannot block in an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + traceQUEUE_PEEK_FROM_ISR( pxQueue ); + + /* Remember the read position so it can be reset as nothing is + actually being removed from the queue. */ + pcOriginalReadPosition = pxQueue->u.pcReadFrom; + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->u.pcReadFrom = pcOriginalReadPosition; + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; + + configASSERT( xQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; +Queue_t *pxQueue; + + pxQueue = ( Queue_t * ) xQueue; + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = pxQueue->uxLength - pxQueue->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; + + configASSERT( xQueue ); + + uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +void vQueueDelete( QueueHandle_t xQueue ) +{ +Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + configASSERT( pxQueue ); + + traceQUEUE_DELETE( pxQueue ); + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueUnregisterQueue( pxQueue ); + } + #endif + if( pxQueue->pcHead != NULL ) + { + vPortFree( pxQueue->pcHead ); + } + vPortFree( pxQueue ); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->uxQueueNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) + { + ( ( Queue_t * ) xQueue )->uxQueueNumber = uxQueueNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->ucQueueType; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) +{ +BaseType_t xReturn = pdFALSE; + + if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ) + { + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder ); + pxQueue->pxMutexHolder = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + } + else if( xPosition == queueSEND_TO_BACK ) + { + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. */ + pxQueue->pcWriteTo += pxQueue->uxItemSize; + if( pxQueue->pcWriteTo >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + ( void ) memcpy( ( void * ) pxQueue->u.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + pxQueue->u.pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->u.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->u.pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xPosition == queueOVERWRITE ) + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* An item is not being added but overwritten, so subtract + one from the recorded number of items in the queue so when + one is added again below the number of recorded items remains + correct. */ + --( pxQueue->uxMessagesWaiting ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + ++( pxQueue->uxMessagesWaiting ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) +{ + if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) + { + pxQueue->u.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ + { + pxQueue->u.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. */ + } +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( Queue_t * const pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + removed from the queue while the queue was locked. When a queue is + locked items can be added or removed, but the event lists cannot be + updated. */ + taskENTER_CRITICAL(); + { + /* See if data was added to the queue while it was locked. */ + while( pxQueue->xTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + blocked waiting for data to become available? */ + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue, queueSEND_TO_BACK ) == pdTRUE ) + { + /* The queue is a member of a queue set, and posting to + the queue set caused a higher priority task to unblock. + A context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + #endif /* configUSE_QUEUE_SETS */ + + --( pxQueue->xTxLock ); + } + + pxQueue->xTxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + while( pxQueue->xRxLock > queueLOCKED_UNMODIFIED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --( pxQueue->xRxLock ); + } + else + { + break; + } + } + + pxQueue->xRxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) +{ +BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) +{ +BaseType_t xReturn; + + configASSERT( xQueue ); + if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) +{ +BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) +{ +BaseType_t xReturn; + + configASSERT( xQueue ); + if( ( ( Queue_t * ) xQueue )->uxMessagesWaiting == ( ( Queue_t * ) xQueue )->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + /* If the queue is already full we may have to block. A critical section + is required to prevent an interrupt removing something from the queue + between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + /* If the queue is already empty we may have to block. A critical section + is required to prevent an interrupt adding something to the queue + between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->u.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->u.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ) + { + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + /* Cannot block within an ISR so if there is no space on the queue then + exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + co-routine has not already been woken. */ + if( xCoRoutinePreviouslyWoken == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCoRoutinePreviouslyWoken; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxCoRoutineWoken ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + /* We cannot block from an ISR, so check there is data available. If + not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->u.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->u.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( ( *pxCoRoutineWoken ) == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t ux; + + /* See if there is an empty space in the registry. A NULL name denotes + a free slot. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].pcQueueName == NULL ) + { + /* Store the information on this queue. */ + xQueueRegistry[ ux ].pcQueueName = pcQueueName; + xQueueRegistry[ ux ].xHandle = xQueue; + + traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ); + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueUnregisterQueue( QueueHandle_t xQueue ) + { + UBaseType_t ux; + + /* See if the handle of the queue being unregistered in actually in the + registry. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + } /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TIMERS == 1 ) + + void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait ) + { + Queue_t * const pxQueue = ( Queue_t * ) xQueue; + + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements. + It can result in vListInsert() being called on a list that can only + possibly ever have one item in it, so the list will be fast, but even + so it should be called with the scheduler locked and not from a critical + section. */ + + /* Only do anything if there are no messages in the queue. This function + will not actually cause the task to block, just place it on a blocked + list. It will not block until the scheduler is unlocked - at which + time a yield will be performed. If an item is added to the queue while + the queue is locked, and the calling task blocks on the queue, then the + calling task will be immediately unblocked when the queue is unlocked. */ + prvLockQueue( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) + { + /* There is nothing in the queue, block for the specified period. */ + vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + prvUnlockQueue( pxQueue ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) + { + QueueSetHandle_t pxQueue; + + pxQueue = xQueueGenericCreate( uxEventQueueLength, sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); + + return pxQueue; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL ) + { + /* Cannot add a queue/semaphore to more than one queue set. */ + xReturn = pdFAIL; + } + else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* Cannot add a queue/semaphore to a queue set if there are already + items in the queue/semaphore. */ + xReturn = pdFAIL; + } + else + { + ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet; + xReturn = pdPASS; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore; + + if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet ) + { + /* The queue was not a member of the set. */ + xReturn = pdFAIL; + } + else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* It is dangerous to remove a queue from a set when the queue is + not empty because the queue set will still hold pending events for + the queue. */ + xReturn = pdFAIL; + } + else + { + taskENTER_CRITICAL(); + { + /* The queue is no longer contained in the set. */ + pxQueueOrSemaphore->pxQueueSetContainer = NULL; + } + taskEXIT_CRITICAL(); + xReturn = pdPASS; + } + + return xReturn; + } /*lint !e818 xQueueSet could not be declared as pointing to const as it is a typedef. */ + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait ) + { + QueueSetMemberHandle_t xReturn = NULL; + + ( void ) xQueueGenericReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait, pdFALSE ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) + { + QueueSetMemberHandle_t xReturn = NULL; + + ( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue, const BaseType_t xCopyPosition ) + { + Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer; + BaseType_t xReturn = pdFALSE; + + /* This function must be called form a critical section. */ + + configASSERT( pxQueueSetContainer ); + configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ); + + if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ) + { + traceQUEUE_SEND( pxQueueSetContainer ); + /* The data copied is the handle of the queue that contains data. */ + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, xCopyPosition ); + + if( listLIST_IS_EMPTY( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ + + + + + + + + + + + + diff --git a/component/os/freertos/freertos_v8.1.2/Source/readme.txt b/component/os/freertos/freertos_v8.1.2/Source/readme.txt new file mode 100644 index 0000000..58480c5 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/readme.txt @@ -0,0 +1,17 @@ +Each real time kernel port consists of three files that contain the core kernel +components and are common to every port, and one or more files that are +specific to a particular microcontroller and or compiler. + ++ The FreeRTOS/Source directory contains the three files that are common to +every port - list.c, queue.c and tasks.c. The kernel is contained within these +three files. croutine.c implements the optional co-routine functionality - which +is normally only used on very memory limited systems. + ++ The FreeRTOS/Source/Portable directory contains the files that are specific to +a particular microcontroller and or compiler. + ++ The FreeRTOS/Source/include directory contains the real time kernel header +files. + +See the readme file in the FreeRTOS/Source/Portable directory for more +information. \ No newline at end of file diff --git a/component/os/freertos/freertos_v8.1.2/Source/tasks.c b/component/os/freertos/freertos_v8.1.2/Source/tasks.c new file mode 100644 index 0000000..c8b0ede --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/tasks.c @@ -0,0 +1,3729 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* Standard includes. */ +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "StackMacros.h" + +/* Lint e961 and e750 are suppressed as a MISRA exception justified because the +MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the +header files above, but not in this file, in order to generate the correct +privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ + +#if ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) + /* At the bottom of this file are two optional functions that can be used + to generate human readable text from the raw data generated by the + uxTaskGetSystemState() function. Note the formatting functions are provided + for convenience only, and are NOT considered part of the kernel. */ + #include +#endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */ + +/* Sanity check the configuration. */ +#if configUSE_TICKLESS_IDLE != 0 + #if INCLUDE_vTaskSuspend != 1 + #error INCLUDE_vTaskSuspend must be set to 1 if configUSE_TICKLESS_IDLE is not set to 0 + #endif /* INCLUDE_vTaskSuspend */ +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Defines the size, in words, of the stack allocated to the idle task. + */ +#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE + +#if( configUSE_PREEMPTION == 0 ) + /* If the cooperative scheduler is being used then a yield should not be + performed just because a higher priority task has been woken. */ + #define taskYIELD_IF_USING_PREEMPTION() +#else + #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() +#endif + +/* + * Task control block. A task control block (TCB) is allocated for each task, + * and stores task state information, including a pointer to the task's context + * (the task's run time environment, including register values) + */ +typedef struct tskTaskControlBlock +{ + volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + ListItem_t xGenericListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ + StackType_t *pxStack; /*< Points to the start of the stack. */ + char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + #if ( portSTACK_GROWTH > 0 ) + StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ + uint32_t ulStartRunTimeCounterOfPeroid; /*< Stores the amount of time the task has spent in the Running state during a peroid start. */ + uint32_t ulEndRunTimeCounterOfPeroid; /*< Stores the amount of time the task has spent in the Running state when a peroid end */ + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + /* Allocate a Newlib reent structure that is specific to this task. + Note Newlib support has been included by popular demand, but is not + used by the FreeRTOS maintainers themselves. FreeRTOS is not + responsible for resulting newlib operation. User must be familiar with + newlib and must provide system-wide implementations of the necessary + stubs. Be warned that (at the time of writing) the current newlib design + implements a system-wide malloc() that must be provided with locks. */ + struct _reent xNewLib_reent; + #endif + +} tskTCB; + +/* The old tskTCB name is maintained above then typedefed to the new TCB_t name +below to enable the use of older kernel aware debuggers. */ +typedef tskTCB TCB_t; + +/* + * Some kernel aware debuggers require the data the debugger needs access to to + * be global, rather than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/*lint -e956 A manual analysis and inspection has been used to determine which +static variables must be declared volatile. */ + +PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. --------------------*/ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxTasksDeleted = ( UBaseType_t ) 0U; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ + +#endif + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ + +#endif + +/* Other file private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile UBaseType_t uxPendedTicks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = portMAX_DELAY; + +/* Context switches are held pending while the scheduler is suspended. Also, +interrupts must not manipulate the xStateListItem of a TCB, or any of the +lists the xStateListItem can be referenced from, if the scheduler is suspended. +If an interrupt needs to unblock a task while the scheduler is suspended then it +moves the task's event list item into the xPendingReadyList, ready for the +kernel to move the task from the pending ready list into the real ready list +when the scheduler is unsuspended. The pending ready list itself can only be +accessed from a critical section. */ +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ + PRIVILEGED_DATA static uint32_t ulDeltaTotalRunTime = 0UL; /*< Holds the delta total amount of execution time*/ +#endif + +/*lint +e956 */ + +/* Debugging and trace facilities private variables and macros. ------------*/ + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5U ) + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskBLOCKED_CHAR ( 'B' ) +#define tskREADY_CHAR ( 'R' ) +#define tskDELETED_CHAR ( 'D' ) +#define tskSUSPENDED_CHAR ( 'S' ) + +/*-----------------------------------------------------------*/ + +#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + + /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is + performed in a generic way that is not optimised to any particular + microcontroller architecture. */ + + /* uxTopReadyPriority holds the priority of the highest priority ready + state task. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) \ + { \ + if( ( uxPriority ) > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( uxPriority ); \ + } \ + } /* taskRECORD_READY_PRIORITY */ + + /*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + /* Find the highest priority queue that contains ready tasks. */ \ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) ) \ + { \ + configASSERT( uxTopReadyPriority ); \ + --uxTopReadyPriority; \ + } \ + \ + /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ + the same priority get an equal share of the processor time. */ \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK */ + + /*-----------------------------------------------------------*/ + + /* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as + they are only required when a port optimised method of task selection is + being used. */ + #define taskRESET_READY_PRIORITY( uxPriority ) + #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + +#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + + /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is + performed in a way that is tailored to the particular microcontroller + architecture being used. */ + + /* A port optimised version is provided. Call the port defined macros. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + + /*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + UBaseType_t uxTopPriority; \ + \ + /* Find the highest priority queue that contains ready tasks. */ \ + portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ + configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ + + /*-----------------------------------------------------------*/ + + /* A port optimised version is provided, call it only if the TCB being reset + is being referenced from a ready list. If it is referenced from a delayed + or suspended list then it won't be in a ready list. */ + #define taskRESET_READY_PRIORITY( uxPriority ) \ + { \ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ + { \ + portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ + } \ + } + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick +count overflows. */ +#define taskSWITCH_DELAYED_LISTS() \ +{ \ + List_t *pxTemp; \ + \ + /* The delayed tasks list should be empty when the lists are switched. */ \ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ + \ + pxTemp = pxDelayedTaskList; \ + pxDelayedTaskList = pxOverflowDelayedTaskList; \ + pxOverflowDelayedTaskList = pxTemp; \ + xNumOfOverflows++; \ + prvResetNextTaskUnblockTime(); \ +} + +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready list for + * the task. It is inserted at the end of the list. + */ +#define prvAddTaskToReadyList( pxTCB ) \ + traceMOVED_TASK_TO_READY_STATE( pxTCB ) \ + taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ + vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xGenericListItem ) ) +/*-----------------------------------------------------------*/ + +/* + * Several functions take an TaskHandle_t parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) ) + +/* The item value of the event list item is normally used to hold the priority +of the task to which it belongs (coded to allow it to be held in reverse +priority order). However, it is occasionally borrowed for other purposes. It +is important its value is not updated due to a task priority change while it is +being used for another purpose. The following bit definition is used to inform +the scheduler that the value should not be changed - in which case it is the +responsibility of whichever module is using the value to ensure it gets set back +to its original value when it is released. */ +#if configUSE_16_BIT_TICKS == 1 + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U +#else + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL +#endif + +/* Callback function prototypes. --------------------------*/ +#if configCHECK_FOR_STACK_OVERFLOW > 0 + extern void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ); +#endif + +#if configUSE_TICK_HOOK > 0 + extern void vApplicationTickHook( void ); +#endif + +/* File private functions. --------------------------------*/ + +/* + * Utility to ready a TCB for a given task. Mainly just copies the parameters + * into the TCB structure. + */ +static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + */ +static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t *pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * The currently executing task is entering the Blocked state. Add the task to + * either the current or the overflow delayed task list. + */ +static void prvAddCurrentTaskToDelayedList( const TickType_t xTimeToWake ) PRIVILEGED_FUNCTION; + +/* + * Allocates memory from the heap for a TCB and associated stack. Checks the + * allocation was successful. + */ +static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) PRIVILEGED_FUNCTION; + +/* + * Fills an TaskStatus_t structure with information on each task that is + * referenced from the pxList list (which may be a ready list, a delayed list, + * a suspended list, etc.). + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTaskWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Return the amount of time, in ticks, that will pass before the kernel will + * next move a task from the Blocked state to the Running state. + * + * This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure portSUPPRESS_TICKS_AND_SLEEP() can be called when user + * defined low power mode implementations require configUSE_TICKLESS_IDLE to be + * set to a value other than 1. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Set xNextTaskUnblockTime to the time at which the next Blocked state task + * will exit the Blocked state. + */ +static void prvResetNextTaskUnblockTime( void ); + +/*-----------------------------------------------------------*/ + +BaseType_t xTaskGenericCreate( TaskFunction_t pxTaskCode, const char * const pcName, const uint16_t usStackDepth, void * const pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, StackType_t * const puxStackBuffer, const MemoryRegion_t * const xRegions ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +BaseType_t xReturn; +TCB_t * pxNewTCB; + + configASSERT( pxTaskCode ); + configASSERT( ( ( uxPriority & ( ~portPRIVILEGE_BIT ) ) < configMAX_PRIORITIES ) ); + + /* Allocate the memory required by the TCB and stack for the new task, + checking that the allocation was successful. */ + pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer ); + + if( pxNewTCB != NULL ) + { + StackType_t *pxTopOfStack; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Calculate the top of stack address. This depends on whether the + stack grows from high memory to low (as per the 80x86) or vice versa. + portSTACK_GROWTH is used to make the result positive or negative as + required by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - ( uint16_t ) 1 ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) ); /*lint !e923 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* If we want to use stack checking on architectures that use + a positive stack growth direction then we also need to store the + other extreme of the stack space. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Setup the newly allocated TCB with the initial state of the task. */ + prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth ); + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portUSING_MPU_WRAPPERS */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the TCB out - in an anonymous way. The calling function/ + task can use this as a handle to delete the task later if + required.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Ensure interrupts don't access the task lists while they are being + updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + initialisation required. We will not recover if this call + fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + xReturn = pdPASS; + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(); + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + traceTASK_CREATE_FAILED(); + } + + if( xReturn == pdPASS ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + then it should run now. */ + if( pxCurrentTCB->uxPriority < uxPriority ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( TaskHandle_t xTaskToDelete ) + { + TCB_t *pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the calling task that is + being deleted. */ + pxTCB = prvGetTCBFromHandle( xTaskToDelete ); + + /* Remove task from the ready list and place in the termination list. + This will stop the task from be scheduled. The idle task will check + the termination list and free up any memory allocated by the + scheduler for the TCB and stack. */ + if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* Increment the uxTaskNumberVariable also so kernel aware debuggers + can detect that the task lists need re-generating. */ + uxTaskNumber++; + + traceTASK_DELETE( pxTCB ); + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if it is the currently running task that has just + been deleted. */ + if( xSchedulerRunning != pdFALSE ) + { + if( pxTCB == pxCurrentTCB ) + { + configASSERT( uxSchedulerSuspended == 0 ); + + /* The pre-delete hook is primarily for the Windows simulator, + in which Windows specific clean up operations are performed, + after which it is not possible to yield away from this task - + hence xYieldPending is used to latch that a context switch is + required. */ + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + portYIELD_WITHIN_API(); + } + else + { + /* Reset the next expected unblock time in case it referred to + the task that has just been deleted. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + } + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + + void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + configASSERT( uxSchedulerSuspended == 0 ); + + vTaskSuspendAll(); + { + /* Minor optimisation. The tick count cannot change in this + block. */ + const TickType_t xConstTickCount = xTickCount; + + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xConstTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL(); + + /* Remove the task from the ready list before adding it to the + blocked list as the same list item is used for both lists. */ + if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is + no need to check, and the port reset macro can be called + directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskDelayUntil */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( const TickType_t xTicksToDelay ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded = pdFALSE; + + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( TickType_t ) 0U ) + { + configASSERT( uxSchedulerSuspended == 0 ); + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + scheduler is suspended will not get placed in the ready + list or removed from the blocked list until the scheduler + is resumed. + + This task cannot be in an event list as it is the currently + executing task. */ + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is + no need to check, and the port reset macro can be called + directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + xAlreadyYielded = xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskDelay */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_eTaskGetState == 1 ) + + eTaskState eTaskGetState( TaskHandle_t xTask ) + { + eTaskState eReturn; + List_t *pxStateList; + const TCB_t * const pxTCB = ( TCB_t * ) xTask; + + configASSERT( pxTCB ); + + if( pxTCB == pxCurrentTCB ) + { + /* The task calling this function is querying its own state. */ + eReturn = eRunning; + } + else + { + taskENTER_CRITICAL(); + { + pxStateList = ( List_t * ) listLIST_ITEM_CONTAINER( &( pxTCB->xGenericListItem ) ); + } + taskEXIT_CRITICAL(); + + if( ( pxStateList == pxDelayedTaskList ) || ( pxStateList == pxOverflowDelayedTaskList ) ) + { + /* The task being queried is referenced from one of the Blocked + lists. */ + eReturn = eBlocked; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( pxStateList == &xSuspendedTaskList ) + { + /* The task being queried is referenced from the suspended + list. Is it genuinely suspended or is it block + indefinitely? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) + { + eReturn = eSuspended; + } + else + { + eReturn = eBlocked; + } + } + #endif + + #if ( INCLUDE_vTaskDelete == 1 ) + else if( pxStateList == &xTasksWaitingTermination ) + { + /* The task being queried is referenced from the deleted + tasks list. */ + eReturn = eDeleted; + } + #endif + + else + { + /* If the task is not in any other state, it must be in the + Ready (including pending ready) state. */ + eReturn = eReady; + } + } + + return eReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_eTaskGetState */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + UBaseType_t uxReturn; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) + { + TCB_t *pxTCB; + UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the calling + task that is being changed. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentBasePriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentBasePriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentBasePriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + priority than the calling task. */ + if( uxNewPriority > uxCurrentBasePriority ) + { + if( pxTCB != pxCurrentTCB ) + { + /* The priority of a task other than the currently + running task is being raised. Is the priority being + raised above that of the running task? */ + if( uxNewPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The priority of the running task is being raised, + but the running task must already be the highest + priority task able to run so no yield is required. */ + } + } + else if( pxTCB == pxCurrentTCB ) + { + /* Setting the priority of the running task down means + there may now be another task of higher priority that + is ready to execute. */ + xYieldRequired = pdTRUE; + } + else + { + /* Setting the priority of any other task down does not + require a yield as the running task must be above the + new priority of the task being modified. */ + } + + /* Remember the ready list the task might be referenced from + before its uxPriority member is changed so the + taskRESET_READY_PRIORITY() macro can function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else + { + pxTCB->uxPriority = uxNewPriority; + } + #endif + + /* Only reset the event list item value if the value is not + being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task is in the blocked or suspended list we need do + nothing more than change it's priority variable. However, if + the task is in a ready list it needs to be removed and placed + in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before adding + it to it's new ready list. As we are in a critical section we + can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xYieldRequired == pdTRUE ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Remove compiler warning about unused variables when the port + optimised task selection is not being used. */ + ( void ) uxPriorityUsedOnEntry; + } + } + taskEXIT_CRITICAL(); + } + +#endif /* INCLUDE_vTaskPrioritySet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( TaskHandle_t xTaskToSuspend ) + { + TCB_t *pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the running task that is + being suspended. */ + pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the + suspended list. */ + if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + } + taskEXIT_CRITICAL(); + + if( pxTCB == pxCurrentTCB ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + to by pxCurrentTCB has just been suspended and pxCurrentTCB + must be adjusted to point to a different task. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) + { + /* No other tasks are ready, so set pxCurrentTCB back to + NULL so when the next task is created pxCurrentTCB will + be set to point to it no matter what its relative priority + is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + else + { + if( xSchedulerRunning != pdFALSE ) + { + /* A task other than the currently running task was suspended, + reset the next expected unblock time in case it referred to the + task that is now in the Suspended state. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) + { + BaseType_t xReturn = pdFALSE; + const TCB_t * const pxTCB = ( TCB_t * ) xTask; + + /* Accesses xPendingReadyList so must be called from a critical + section. */ + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task being resumed actually in the suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + { + /* Is it in the suspended list because it is in the Suspended + state, or because is is blocked with no timeout? */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) + { + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( TaskHandle_t xTaskToResume ) + { + TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + + /* It does not make sense to resume the calling task. */ + configASSERT( xTaskToResume ); + + /* The parameter cannot be NULL as it is impossible to resume the + currently executing task. */ + if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) + { + taskENTER_CRITICAL(); + { + if( prvTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME( pxTCB ); + + /* As we are in a critical section we can access the ready + lists even if the scheduler is suspended. */ + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* We may have just resumed a higher priority task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, + but will leave the lists in the correct state for the + next yield. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskSuspend */ + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) + { + BaseType_t xYieldRequired = pdFALSE; + TCB_t * const pxTCB = ( TCB_t * ) xTaskToResume; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToResume ); + + /* RTOS ports that support interrupt nesting have the concept of a + maximum system call (or maximum API call) interrupt priority. + Interrupts that are above the maximum system call priority are keep + permanently enabled, even when the RTOS kernel is in a critical section, + but cannot make any calls to FreeRTOS API functions. If configASSERT() + is defined in FreeRTOSConfig.h then + portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has + been assigned a priority above the configured maximum system call + priority. Only FreeRTOS functions that end in FromISR can be called + from interrupts that have been assigned a priority at or (logically) + below the maximum system call interrupt priority. FreeRTOS maintains a + separate interrupt safe API to ensure interrupt entry is as fast and as + simple as possible. More information (albeit Cortex-M specific) is + provided on the following link: + http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( prvTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + /* Check the ready lists can be accessed. */ + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Ready lists can be accessed so move the task from the + suspended list to the ready list directly. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed or ready lists cannot be accessed so the task + is held in the pending ready list until the scheduler is + unsuspended. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xYieldRequired; + } + +#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */ +/*-----------------------------------------------------------*/ + +void vTaskStartScheduler( void ) +{ +BaseType_t xReturn; + + /* Add the idle task at the lowest priority. */ + #if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + { + /* Create the idle task, storing its handle in xIdleTaskHandle so it can + be returned by the xTaskGetIdleTaskHandle() function. */ + xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + } + #else + { + /* Create the idle task without storing its handle. */ + xReturn = xTaskCreate( prvIdleTask, "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), NULL ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + } + #endif /* INCLUDE_xTaskGetIdleTaskHandle */ + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + + if( xReturn == pdPASS ) + { + /* Interrupts are turned off here, to ensure a tick does not occur + before or during the call to xPortStartScheduler(). The stacks of + the created tasks contain a status word with interrupts switched on + so interrupts will automatically get re-enabled when the first task + starts to run. */ + portDISABLE_INTERRUPTS(); + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + structure specific to the task that will run first. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + + xSchedulerRunning = pdTRUE; + xTickCount = ( TickType_t ) 0U; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + macro must be defined to configure the timer/counter used to generate + the run time counter time base. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + /* Setting up the timer tick is hardware specific and thus in the + portable interface. */ + if( xPortStartScheduler() != pdFALSE ) + { + /* Should not reach here as if the scheduler is running the + function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } + else + { + /* This line will only be reached if the kernel could not be started, + because there was not enough FreeRTOS heap to create the idle task + or the timer task. */ + configASSERT( xReturn ); + } +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + routine so the original ISRs can be restored if necessary. The port + layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + /* A critical section is not required as the variable is of type + BaseType_t. Please read Richard Barry's reply in the following link to a + post in the FreeRTOS support forum before reporting this as a bug! - + http://goo.gl/wu4acr */ + ++uxSchedulerSuspended; +} +/*----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) + { + TickType_t xReturn; + + if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) + { + xReturn = 0; + } + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) + { + /* There are other idle priority tasks in the ready state. If + time slicing is used then the very next tick interrupt must be + processed. */ + xReturn = 0; + } + else + { + xReturn = xNextTaskUnblockTime - xTickCount; + } + + return xReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskResumeAll( void ) +{ +TCB_t *pxTCB; +BaseType_t xAlreadyYielded = pdFALSE; + + /* If uxSchedulerSuspended is zero then this function does not match a + previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended ); + + /* It is possible that an ISR caused a task to be removed from an event + list while the scheduler was suspended. If this was the case then the + removed task will have been added to the xPendingReadyList. Once the + scheduler has been resumed it is safe to move all the pending ready + tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) + { + /* Move any readied tasks from the pending list into the + appropriate ready list. */ + while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + { + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* If we have moved a task that has a priority higher than + the current task then we should yield. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* If any ticks occurred while the scheduler was suspended then + they should be processed now. This ensures the tick count does + not slip, and that any delayed tasks are resumed at the correct + time. */ + if( uxPendedTicks > ( UBaseType_t ) 0U ) + { + while( uxPendedTicks > ( UBaseType_t ) 0U ) + { + if( xTaskIncrementTick() != pdFALSE ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --uxPendedTicks; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xYieldPending == pdTRUE ) + { + #if( configUSE_PREEMPTION != 0 ) + { + xAlreadyYielded = pdTRUE; + } + #endif + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xAlreadyYielded; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCount( void ) +{ +TickType_t xTicks; + + /* Critical section required if running on a 16 bit processor. */ + taskENTER_CRITICAL(); + { + xTicks = xTickCount; + } + taskEXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCountFromISR( void ) +{ +TickType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = xTickCount; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTaskGetNumberOfTasks( void ) +{ + /* A critical section is not required because the variables are of type + BaseType_t. */ + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_pcTaskGetTaskName == 1 ) + + char *pcTaskGetTaskName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + TCB_t *pxTCB; + + /* If null is passed in here then the name of the calling task is being queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB ); + return &( pxTCB->pcTaskName[ 0 ] ); + } + +#endif /* INCLUDE_pcTaskGetTaskName */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) + { + UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; + + vTaskSuspendAll(); + { + /* Is there a space in the array for each task in the system? */ +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + ulDeltaTotalRunTime = 0; +#endif + if( uxArraySize >= uxCurrentNumberOfTasks ) + { + /* Fill in an TaskStatus_t structure with information on each + task in the Ready state. */ + do + { + uxQueue--; + uxTask += prvListTaskWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ); + + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + task in the Blocked state. */ + uxTask += prvListTaskWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ); + uxTask += prvListTaskWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ); + + #if( INCLUDE_vTaskDelete == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + each task that has been deleted but not yet cleaned up. */ + uxTask += prvListTaskWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + each task in the Suspended state. */ + uxTask += prvListTaskWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ); + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1) + { + if( pulTotalRunTime != NULL ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); + #else + *pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + } + } + #else + { + if( pulTotalRunTime != NULL ) + { + *pulTotalRunTime = 0; + } + } + #endif + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + TaskHandle_t xTaskGetIdleTaskHandle( void ) + { + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + started, then xIdleTaskHandle will be NULL. */ + configASSERT( ( xIdleTaskHandle != NULL ) ); + return xIdleTaskHandle; + } + +#endif /* INCLUDE_xTaskGetIdleTaskHandle */ +/*----------------------------------------------------------*/ + +/* This conditional compilation should use inequality to 0, not equality to 1. +This is to ensure vTaskStepTick() is available when user defined low power mode +implementations require configUSE_TICKLESS_IDLE to be set to a value other than +1. */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + void vTaskStepTick( const TickType_t xTicksToJump ) + { + /* Correct the tick count value after a period during which the tick + was suppressed. Note this does *not* call the tick hook function for + each stepped tick. */ + configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); + xTickCount += xTicksToJump; + traceINCREASE_TICK_COUNT( xTicksToJump ); + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) ) + static void prvGenerateRunTimeOfPeroid(xList *pxList, portTickType tickTmp) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + + /* Write the run time stats of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + /* Get next TCB in from the list. */ + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + /* Record start&end run time counter. */ + if (tickTmp%(2*portCONFIGURE_STATS_PEROID_VALUE)) + pxNextTCB->ulStartRunTimeCounterOfPeroid = pxNextTCB->ulRunTimeCounter; + else + pxNextTCB->ulEndRunTimeCounterOfPeroid = pxNextTCB->ulRunTimeCounter; + + } while( pxNextTCB != pxFirstTCB ); + } + + static void prvGetRunTimeStatsOfPeroidForTasksInList(portTickType tickTmp) + { + unsigned portBASE_TYPE uxQueue; + + if (tickTmp%portCONFIGURE_STATS_PEROID_VALUE){ + return;//only portCONFIGURE_STATS_PEROID_VALUE + } + + uxQueue = configMAX_PRIORITIES; + + do + { + uxQueue--; + + if( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) == pdFALSE ) + { + prvGenerateRunTimeOfPeroid(( xList * ) &( pxReadyTasksLists[ uxQueue ] ), tickTmp ); + } + }while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeOfPeroid(( xList * ) pxDelayedTaskList, tickTmp ); + } + + if( listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeOfPeroid(( xList * ) pxOverflowDelayedTaskList, tickTmp ); + } + +#if ( INCLUDE_vTaskDelete == 1 ) + { + if( listLIST_IS_EMPTY( &xTasksWaitingTermination ) == pdFALSE ) + { + prvGenerateRunTimeOfPeroid(&xTasksWaitingTermination, tickTmp ); + } + } +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + { + if( listLIST_IS_EMPTY( &xSuspendedTaskList ) == pdFALSE ) + { + prvGenerateRunTimeOfPeroid(&xSuspendedTaskList, tickTmp ); + } + } +#endif + } +#endif + +BaseType_t xTaskIncrementTick( void ) +{ +TCB_t * pxTCB; +TickType_t xItemValue; +BaseType_t xSwitchRequired = pdFALSE; + + /* Called by the portable layer each time a tick interrupt occurs. + Increments the tick then checks to see if the new tick value will cause any + tasks to be unblocked. */ + traceTASK_INCREMENT_TICK( xTickCount ); + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Increment the RTOS tick, switching the delayed and overflowed + delayed lists if it wraps to 0. */ + ++xTickCount; + + { + /* Minor optimisation. The tick count cannot change in this + block. */ + const TickType_t xConstTickCount = xTickCount; + + if( xConstTickCount == ( TickType_t ) 0U ) + { + taskSWITCH_DELAYED_LISTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* See if this tick has made a timeout expire. Tasks are stored in + the queue in the order of their wake time - meaning once one task + has been found whose block time has not expired there is no need to + look any further down the list. */ + if( xConstTickCount >= xNextTaskUnblockTime ) + { + for( ;; ) + { + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The delayed list is empty. Set xNextTaskUnblockTime + to the maximum possible value so it is extremely + unlikely that the + if( xTickCount >= xNextTaskUnblockTime ) test will pass + next time through. */ + xNextTaskUnblockTime = portMAX_DELAY; + break; + } + else + { + /* The delayed list is not empty, get the value of the + item at the head of the delayed list. This is the time + at which the task at the head of the delayed list must + be removed from the Blocked state. */ + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ); + + if( xConstTickCount < xItemValue ) + { + /* It is not time to unblock this item yet, but the + item value is the time at which the task at the head + of the blocked list must be removed from the Blocked + state - so record the item value in + xNextTaskUnblockTime. */ + xNextTaskUnblockTime = xItemValue; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* It is time to remove the item from the Blocked state. */ + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? If so remove + it from the event list. */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Place the unblocked task into the appropriate ready + list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate + context switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should + only be performed if the unblocked task has a + priority that is equal to or higher than the + currently executing task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + } + } + } + + /* Tasks of equal priority to the currently running task will share + processing time (time slice) if preemption is on, and the application + writer has not explicitly turned time slicing off. */ + #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + count is being unwound (when the scheduler is being unlocked). */ + if( uxPendedTicks == ( UBaseType_t ) 0U ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + } + else + { + ++uxPendedTicks; + + /* The tick hook gets called at regular intervals, even if the + scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + prvGetRunTimeStatsOfPeroidForTasksInList(xTickCount); + #endif + + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + + return xSwitchRequired; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) + { + TCB_t *xTCB; + + /* If xTask is NULL then it is the task hook of the calling task that is + getting set. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = ( TCB_t * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + xTCB->pxTaskTag = pxHookFunction; + taskEXIT_CRITICAL(); + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TCB_t *xTCB; + TaskHookFunction_t xReturn; + + /* If xTask is NULL then we are setting our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = ( TCB_t * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xReturn = xTCB->pxTaskTag; + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) + { + TCB_t *xTCB; + BaseType_t xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = ( TCB_t * ) xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + switch. */ + xYieldPending = pdTRUE; + } + else + { + xYieldPending = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + accumulated time so far. The time the task started running was + stored in ulTaskSwitchedInTime. Note that there is no overflow + protection here so count values are only valid until the timer + overflows. The guard against negative values is to protect + against suspect run time stat counter implementations - which + are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ulTaskSwitchedInTime = ulTotalRunTime; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskFIRST_CHECK_FOR_STACK_OVERFLOW(); + taskSECOND_CHECK_FOR_STACK_OVERFLOW(); + + /* Select a new task to run using either the generic C or port + optimised asm code. */ + taskSELECT_HIGHEST_PRIORITY_TASK(); + traceTASK_SWITCHED_IN(); + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + structure specific to this task. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + } +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) +{ +TickType_t xTimeToWake; + + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ + + /* Place the event list item of the TCB in the appropriate event list. + This is placed in the list in priority order so the highest priority task + is the first to be woken by the event. The queue that contains the event + list is locked, preventing simultaneous access from interrupts. */ + vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* The task must be removed from from the ready list before it is added to + the blocked list as the same list item is used for both lists. Exclusive + access to the ready lists guaranteed because the scheduler is locked. */ + if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( xTicksToWait == portMAX_DELAY ) + { + /* Add the task to the suspended task list instead of a delayed task + list to ensure the task is not woken by a timing event. It will + block indefinitely. */ + vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + does not occur. This may overflow but this doesn't matter, the + scheduler will handle it. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter, the scheduler + will handle it. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + #endif /* INCLUDE_vTaskSuspend */ +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) +{ +TickType_t xTimeToWake; + + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + the event groups implementation. */ + configASSERT( uxSchedulerSuspended != 0 ); + + /* Store the item value in the event list item. It is safe to access the + event list item here as interrupts won't access the event list item of a + task that is not in the Blocked state. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Place the event list item of the TCB at the end of the appropriate event + list. It is safe to access the event list here because it is part of an + event group implementation - and interrupts don't access event groups + directly (instead they access them indirectly by pending function calls to + the task level). */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* The task must be removed from the ready list before it is added to the + blocked list. Exclusive access can be assured to the ready list as the + scheduler is locked. */ + if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( xTicksToWait == portMAX_DELAY ) + { + /* Add the task to the suspended task list instead of a delayed task + list to ensure it is not woken by a timing event. It will block + indefinitely. */ + vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + does not occur. This may overflow but this doesn't matter, the + kernel will manage it correctly. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter, the kernel + will manage it correctly. */ + xTimeToWake = xTickCount + xTicksToWait; + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + #endif /* INCLUDE_vTaskSuspend */ +} +/*-----------------------------------------------------------*/ + +#if configUSE_TIMERS == 1 + + void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, const TickType_t xTicksToWait ) + { + TickType_t xTimeToWake; + + configASSERT( pxEventList ); + + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements - + it should be called from a critical section. */ + + + /* Place the event list item of the TCB in the appropriate event list. + In this case it is assume that this is the only task that is going to + be waiting on this event list, so the faster vListInsertEnd() function + can be used in place of vListInsert. */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* We must remove this task from the ready list before adding it to the + blocked list as the same list item is used for both lists. This + function is called form a critical section. */ + if( uxListRemove( &( pxCurrentTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + + traceTASK_DELAY_UNTIL(); + prvAddCurrentTaskToDelayedList( xTimeToWake ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) +{ +TCB_t *pxUnblockedTCB; +BaseType_t xReturn; + + /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be + called from a critical section within an ISR. */ + + /* The event list is sorted in priority order, so the first in the list can + be removed as it is known to be the highest priority. Remove the TCB from + the delayed list, and add it to the ready list. + + If an event is for a queue that is locked then this function will never + get called - the lock count on the queue will get modified instead. This + means exclusive access to the event list is guaranteed here. + + This function assumes that a check has already been made to ensure that + pxEventList is not empty. */ + pxUnblockedTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxUnblockedTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold this task + pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has a higher + priority than the calling task. This allows the calling task to know if + it should force a context switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) +{ +TCB_t *pxUnblockedTCB; +BaseType_t xReturn; + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + the event flags implementation. */ + configASSERT( uxSchedulerSuspended != pdFALSE ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + event flags. */ + pxUnblockedTCB = ( TCB_t * ) listGET_LIST_ITEM_OWNER( pxEventListItem ); + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( pxEventListItem ); + + /* Remove the task from the delayed list and add it to the ready list. The + scheduler is suspended so interrupts will not be accessing the ready + lists. */ + ( void ) uxListRemove( &( pxUnblockedTCB->xGenericListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + configASSERT( pxTimeOut ); + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) +{ +BaseType_t xReturn; + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + /* Minor optimisation. The tick count cannot change in this block. */ + const TickType_t xConstTickCount = xTickCount; + + #if ( INCLUDE_vTaskSuspend == 1 ) + /* If INCLUDE_vTaskSuspend is set to 1 and the block time specified is + the maximum block time then the task should block indefinitely, and + therefore never time out. */ + if( *pxTicksToWait == portMAX_DELAY ) + { + xReturn = pdFALSE; + } + else /* We are not blocking indefinitely, perform the checks below. */ + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */ + { + /* The tick count is greater than the time at which vTaskSetTimeout() + was called, but has also overflowed since vTaskSetTimeOut() was called. + It must have wrapped all the way around and gone past us again. This + passed since vTaskSetTimeout() was called. */ + xReturn = pdTRUE; + } + else if( ( xConstTickCount - pxTimeOut->xTimeOnEntering ) < *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering ); + vTaskSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xYieldPending = pdTRUE; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t *pxTCB; + + if( xTask != NULL ) + { + pxTCB = ( TCB_t * ) xTask; + uxReturn = pxTCB->uxTaskNumber; + } + else + { + uxReturn = 0U; + } + + return uxReturn; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) + { + TCB_t *pxTCB; + + if( xTask != NULL ) + { + pxTCB = ( TCB_t * ) xTask; + pxTCB->uxTaskNumber = uxHandle; + } + } + +#endif /* configUSE_TRACE_FACILITY */ + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* See if any tasks have been deleted. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + see if any other task has become available. If we are using + preemption we don't need to do this as any task becoming available + will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + timesliced. If a task that is sharing the idle priority is ready + to run then the idle task should yield before the end of the + timeslice. + + A critical region is not required here as we are just reading from + the list, and an occasional incorrect value will not matter. If + the ready list at the idle priority contains more than one task + then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + allows the application designer to add background functionality + without the overhead of a separate task. + NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif /* configUSE_IDLE_HOOK */ + + /* This conditional compilation should use inequality to 0, not equality + to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when + user defined low power mode implementations require + configUSE_TICKLESS_IDLE to be set to a value other than 1. */ + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + TickType_t xExpectedIdleTime; + + /* It is not desirable to suspend then resume the scheduler on + each iteration of the idle task. Therefore, a preliminary + test of the expected idle time is performed without the + scheduler suspended. The result here is not necessarily + valid. */ + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + vTaskSuspendAll(); + { + /* Now the scheduler is suspended, the expected idle + time can be sampled again, and this time its value can + be used. */ + configASSERT( xNextTaskUnblockTime >= xTickCount ); + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + traceLOW_POWER_IDLE_BEGIN(); + portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + traceLOW_POWER_IDLE_END(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICKLESS_IDLE */ + } +} +/*-----------------------------------------------------------*/ + +#if configUSE_TICKLESS_IDLE != 0 + + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) + { + eSleepModeStatus eReturn = eStandardSleep; + + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + { + /* A task was made ready while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xYieldPending != pdFALSE ) + { + /* A yield was pended while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else + { + #if configUSE_TIMERS == 0 + { + /* The idle task exists in addition to the application tasks. */ + const UBaseType_t uxNonApplicationTasks = 1; + + /* If timers are not being used and all the tasks are in the + suspended list (which might mean they have an infinite block + time rather than actually being suspended) then it is safe to + turn all clocks off and just wait for external interrupts. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) + { + eReturn = eNoTasksWaitingTimeout; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + } + + return eReturn; + } +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseTCBVariables( TCB_t * const pxTCB, const char * const pcName, UBaseType_t uxPriority, const MemoryRegion_t * const xRegions, const uint16_t usStackDepth ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +UBaseType_t x; + + /* Store the task name in the TCB. */ + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxTCB->uxBasePriority = uxPriority; + pxTCB->uxMutexesHeld = 0; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxTCB->xEventListItem ) ); + + /* Set the pxTCB as a link back from the ListItem_t. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxTCB->uxCriticalNesting = ( UBaseType_t ) 0U; + } + #endif /* portCRITICAL_NESTING_IN_TCB */ + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxTCB->pxTaskTag = NULL; + } + #endif /* configUSE_APPLICATION_TASK_TAG */ + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTCB->ulRunTimeCounter = 0UL; + pxTCB->ulStartRunTimeCounterOfPeroid = 0UL; + pxTCB->ulEndRunTimeCounterOfPeroid = 0UL; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); + } + #else /* portUSING_MPU_WRAPPERS */ + { + ( void ) xRegions; + ( void ) usStackDepth; + } + #endif /* portUSING_MPU_WRAPPERS */ + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Initialise this task's Newlib reent structure. */ + _REENT_INIT_PTR( ( &( pxTCB->xNewLib_reent ) ) ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, const MemoryRegion_t * const xRegions ) + { + TCB_t *pxTCB; + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseTaskLists( void ) +{ +UBaseType_t uxPriority; + + for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( &xDelayedTaskList1 ); + vListInitialise( &xDelayedTaskList2 ); + vListInitialise( &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( &xTasksWaitingTermination ); + } + #endif /* INCLUDE_vTaskDelete */ + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( &xSuspendedTaskList ); + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + #if ( INCLUDE_vTaskDelete == 1 ) + { + BaseType_t xListIsEmpty; + + /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called + too often in the idle task. */ + while( uxTasksDeleted > ( UBaseType_t ) 0U ) + { + vTaskSuspendAll(); + { + xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + } + ( void ) xTaskResumeAll(); + + if( xListIsEmpty == pdFALSE ) + { + TCB_t *pxTCB; + + taskENTER_CRITICAL(); + { + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); + ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); + --uxCurrentNumberOfTasks; + --uxTasksDeleted; + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #endif /* vTaskDelete */ +} +/*-----------------------------------------------------------*/ + +static void prvAddCurrentTaskToDelayedList( const TickType_t xTimeToWake ) +{ + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xGenericListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + list of blocked tasks then xNextTaskUnblockTime needs to be updated + too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } +} +/*-----------------------------------------------------------*/ + +static TCB_t *prvAllocateTCBAndStack( const uint16_t usStackDepth, StackType_t * const puxStackBuffer ) +{ +TCB_t *pxNewTCB; + + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMallocAligned( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ), puxStackBuffer ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + else + { + /* Avoid dependency on memset() if it is not required. */ + #if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + { + /* Just to help debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) usStackDepth * sizeof( StackType_t ) ); + } + #endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */ + } + } + + return pxNewTCB; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTaskWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) + { + volatile TCB_t *pxNextTCB, *pxFirstTCB; + UBaseType_t uxTask = 0; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + + /* Populate an TaskStatus_t structure within the + pxTaskStatusArray array for each task that is referenced from + pxList. See the definition of TaskStatus_t in task.h for the + meaning of each TaskStatus_t structure member. */ + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + pxTaskStatusArray[ uxTask ].xHandle = ( TaskHandle_t ) pxNextTCB; + pxTaskStatusArray[ uxTask ].pcTaskName = ( const char * ) &( pxNextTCB->pcTaskName [ 0 ] ); + pxTaskStatusArray[ uxTask ].xTaskNumber = pxNextTCB->uxTCBNumber; + pxTaskStatusArray[ uxTask ].eCurrentState = eState; + pxTaskStatusArray[ uxTask ].uxCurrentPriority = pxNextTCB->uxPriority; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a chance + it is actually just blocked indefinitely - so really it should + be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + if( listLIST_ITEM_CONTAINER( &( pxNextTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatusArray[ uxTask ].eCurrentState = eBlocked; + } + } + } + #endif /* INCLUDE_vTaskSuspend */ + + #if ( configUSE_MUTEXES == 1 ) + { + pxTaskStatusArray[ uxTask ].uxBasePriority = pxNextTCB->uxBasePriority; + } + #else + { + pxTaskStatusArray[ uxTask ].uxBasePriority = 0; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTaskStatusArray[ uxTask ].ulRunTimeCounter = pxNextTCB->ulRunTimeCounter; + if (pxNextTCB->ulEndRunTimeCounterOfPeroid > pxNextTCB->ulStartRunTimeCounterOfPeroid) + pxTaskStatusArray[ uxTask ].ulDelataRunTimeCounterOfPeroid = pxNextTCB->ulEndRunTimeCounterOfPeroid - pxNextTCB->ulStartRunTimeCounterOfPeroid; + else + pxTaskStatusArray[ uxTask ].ulDelataRunTimeCounterOfPeroid = pxNextTCB->ulStartRunTimeCounterOfPeroid - pxNextTCB->ulEndRunTimeCounterOfPeroid; + ulDeltaTotalRunTime += pxTaskStatusArray[ uxTask ].ulDelataRunTimeCounterOfPeroid; + } + #else + { + pxTaskStatusArray[ uxTask ].ulRunTimeCounter = 0; + } + #endif + + #if ( portSTACK_GROWTH > 0 ) + { + pxTaskStatusArray[ uxTask ].usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxNextTCB->pxEndOfStack ); + } + #else + { + pxTaskStatusArray[ uxTask ].usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxNextTCB->pxStack ); + } + #endif + + uxTask++; + + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static uint16_t prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + { + uint32_t ulCount = 0U; + + while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + ulCount++; + } + + ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ + + return ( uint16_t ) ulCount; + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + uint8_t *pucEndOfStack; + UBaseType_t uxReturn; + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t *pxTCB ) + { + /* This call is required specifically for the TriCore port. It must be + above the vPortFree() calls. The call is also used by ports/demos that + want to allocate and clean RAM statically. */ + portCLEAN_UP_TCB( pxTCB ); + + /* Free up the memory allocated by the scheduler for the task. It is up + to the task to free any memory allocated at the application level. */ + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + _reclaim_reent( &( pxTCB->xNewLib_reent ) ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +static void prvResetNextTaskUnblockTime( void ) +{ +TCB_t *pxTCB; + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set + xNextTaskUnblockTime to the maximum possible value so it is + extremely unlikely that the + if( xTickCount >= xNextTaskUnblockTime ) test will pass until + there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + the item at the head of the delayed list. This is the time at + which the task at the head of the delayed list should be removed + from the Blocked state. */ + ( pxTCB ) = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); + xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xGenericListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) + + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + + /* A critical section is not required as this is not called from + an interrupt and the current TCB will always be the same for any + individual execution thread. */ + xReturn = pxCurrentTCB; + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + + BaseType_t xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + + /* If the mutex was given back by an interrupt while the queue was + locked then the mutex holder might now be NULL. */ + if( pxMutexHolder != NULL ) + { + if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new + priority. Only reset the event list item value if the value is + not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task being modified is in the ready state it will need to + be moved into a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Inherit the priority before being moved into the new list. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* Just inherit the priority. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + } + + traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + if( pxMutexHolder != NULL ) + { + configASSERT( pxTCB->uxMutexesHeld ); + ( pxTCB->uxMutexesHeld )--; + + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* Only disinherit if no other mutexes are held. */ + if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) + { + /* The holding task must be the running task to be able to give + the mutex back. Remove the holding task from the ready list. */ + if( uxListRemove( &( pxTCB->xGenericListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Disinherit the priority before adding the task into the new + ready list. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + pxTCB->uxPriority = pxTCB->uxBasePriority; + + /* Reset the event list item value. It cannot be in use for + any other purpose if this task is running, and it must be + running to give back the mutex. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + prvAddTaskToReadyList( pxTCB ); + + /* Return true to indicate that a context switch is required. + This is only actually required in the corner case whereby + multiple mutexes were held and the mutexes were given back + in an order different to that in which they were taken. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + + /* This is not the interrupt safe version of the enter critical + function so assert() if it is being called from an interrupt + context. Only API functions that end in "FromISR" can be used in an + interrupt. Only assert if the critical nesting count is 1 to + protect against recursive calls if the assert function also uses a + critical section. */ + if( pxCurrentTCB->uxCriticalNesting == 1 ) + { + portASSERT_IF_IN_ISR(); + } + + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskExitCritical( void ) + { + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) ) + + void vTaskList( char * pcWriteBuffer ) + { + TaskStatus_t *pxTaskStatusArray; + volatile UBaseType_t uxArraySize, x; + char cStatus; + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that + * displays task names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that + * might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, + * and limited functionality implementation of sprintf() is provided in + * many of the FreeRTOS/Demo sub-directories in a file called + * printf-stdarg.c (note printf-stdarg.c does not provide a full + * snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskList(). + */ + + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); + + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + switch( pxTaskStatusArray[ x ].eCurrentState ) + { + case eReady: cStatus = tskREADY_CHAR; + break; + + case eBlocked: cStatus = tskBLOCKED_CHAR; + break; + + case eSuspended: cStatus = tskSUSPENDED_CHAR; + break; + + case eDeleted: cStatus = tskDELETED_CHAR; + break; + + default: /* Should not get here, but it is included + to prevent static checking errors. */ + cStatus = 0x00; + break; + } + + sprintf( pcWriteBuffer, "%s\t\t%c\t%u\t%u\t%u\r\n", pxTaskStatusArray[ x ].pcTaskName, cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); + pcWriteBuffer += strlen( pcWriteBuffer ); + } + + /* Free the array again. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) ) */ +/*----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) ) + + void vTaskGetRunTimeStats( char *pcWriteBuffer ) + { + TaskStatus_t *pxTaskStatusArray; + volatile UBaseType_t uxArraySize, x; + uint32_t ulTotalTime, ulStatsAsPercentage, ulDeltaRunTimeCounter; + + #if( configUSE_TRACE_FACILITY != 1 ) + { + #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats(). + } + #endif + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part + * of the uxTaskGetSystemState() output into a human readable table that + * displays the amount of time each task has spent in the Running state + * in both absolute and percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library + * function that might bloat the code size, use a lot of stack, and + * provide different results on different platforms. An alternative, + * tiny, third party, and limited functionality implementation of + * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in + * a file called printf-stdarg.c (note printf-stdarg.c does not provide + * a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskGetRunTimeStats(). + */ + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); + printf("\n\rCPU total run time is %u", ulTotalTime); + printf("\n\rTaskName\tDeltaRunTime\tpercentage\r\n"); + + /* For percentage calculations. */ + ulTotalTime /= 100UL; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > 0 ) + { + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + /* What percentage of the total run time has the task used? + This will always be rounded down to the nearest integer. + ulTotalRunTimeDiv100 has already been divided by 100. */ +#if 0 + ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; +#else + ulStatsAsPercentage = (100*pxTaskStatusArray[ x ].ulDelataRunTimeCounterOfPeroid) / ulDeltaTotalRunTime; + /* just make run time counter looks like more precise*/ + if (100*(100*pxTaskStatusArray[ x ].ulDelataRunTimeCounterOfPeroid) % ulDeltaTotalRunTime >=50) + ulDeltaRunTimeCounter = portCONFIGURE_STATS_PEROID_VALUE*(ulStatsAsPercentage+1)/100; + else + ulDeltaRunTimeCounter = portCONFIGURE_STATS_PEROID_VALUE*ulStatsAsPercentage/100; +#endif + + if( ulStatsAsPercentage > 0UL ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { +#if 0 + sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); +#else + sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ulDeltaRunTimeCounter, ulStatsAsPercentage ); +#endif + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ +#if 0 + sprintf( pcWriteBuffer, "%s\t\t%u\t\t%u%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); +#else + sprintf( pcWriteBuffer, "%s\t\t%u\t\t%u%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ( unsigned int ) ulDeltaRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); +#endif + } + #endif + } + else + { + /* If the percentage is zero here then the task has + consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { +#if 0 + sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter ); +#else + sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ulDeltaRunTimeCounter ); +#endif + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ +#if 0 + sprintf( pcWriteBuffer, "%s\t\t%u\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); +#else + sprintf( pcWriteBuffer, "%s\t\t%u\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, ( unsigned int ) ulDeltaRunTimeCounter ); +#endif + } + #endif + } + + pcWriteBuffer += strlen( pcWriteBuffer ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Free the array again. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +TickType_t uxTaskResetEventItemValue( void ) +{ +TickType_t uxReturn; + + uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); + + /* Reset the event list item to its normal value - so it can be used with + queues and semaphores. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void *pvTaskIncrementMutexHeldCount( void ) + { + /* If xSemaphoreCreateMutex() is called before any tasks have been created + then pxCurrentTCB will be NULL. */ + if( pxCurrentTCB != NULL ) + { + ( pxCurrentTCB->uxMutexesHeld )++; + } + + return pxCurrentTCB; + } + +#endif /* configUSE_MUTEXES */ + +/*-----------------------------------------------------------*/ +void * vTaskGetCurrentTCB( void ) +{ + return (void*)pxCurrentTCB; +} + +#ifdef FREERTOS_MODULE_TEST + #include "tasks_test_access_functions.h" +#endif + diff --git a/component/os/freertos/freertos_v8.1.2/Source/timers.c b/component/os/freertos/freertos_v8.1.2/Source/timers.c new file mode 100644 index 0000000..7a34343 --- /dev/null +++ b/component/os/freertos/freertos_v8.1.2/Source/timers.c @@ -0,0 +1,885 @@ +/* + FreeRTOS V8.1.2 - Copyright (C) 2014 Real Time Engineers Ltd. + All rights reserved + + VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS provides completely free yet professionally developed, * + * robust, strictly quality controlled, supported, and cross * + * platform software that has become a de facto standard. * + * * + * Help yourself get started quickly and support the FreeRTOS * + * project by purchasing a FreeRTOS tutorial book, reference * + * manual, or both from: http://www.FreeRTOS.org/Documentation * + * * + * Thank you! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. + + >>! NOTE: The modification to the GPL is included to allow you to !<< + >>! distribute a combined work that includes FreeRTOS without being !<< + >>! obliged to provide the source code for proprietary components !<< + >>! outside of the FreeRTOS kernel. !<< + + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. Full license text is available from the following + link: http://www.freertos.org/a00114.html + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + http://www.FreeRTOS.org - Documentation, books, training, latest versions, + license and Real Time Engineers Ltd. contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool, a DOS + compatible FAT file system, and our tiny thread aware UDP/IP stack. + + http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High + Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS + licenses offer ticketed support, indemnification and middleware. + + http://www.SafeRTOS.com - High Integrity Systems also provide a safety + engineered and independently SIL3 certified version for use in safety and + mission critical applications that require provable dependability. + + 1 tab == 4 spaces! +*/ + +/* Standard includes. */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "timers.h" + +#if ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 0 ) + #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. +#endif + +/* Lint e961 and e750 are suppressed as a MISRA exception justified because the +MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined for the +header files above, but not in this file, in order to generate the correct +privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750. */ + + +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. This #if is closed at the very bottom +of this file. If you want to include software timer functionality then ensure +configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#if ( configUSE_TIMERS == 1 ) + +/* Misc definitions. */ +#define tmrNO_DELAY ( TickType_t ) 0U + +/* The definition of the timers themselves. */ +typedef struct tmrTimerControl +{ + const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ + TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ + UBaseType_t uxAutoReload; /*<< Set to pdTRUE if the timer should be automatically restarted once expired. Set to pdFALSE if the timer is, in effect, a one-shot timer. */ + void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ + TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ + #endif +} xTIMER; + +/* The old xTIMER name is maintained above then typedefed to the new Timer_t +name below to enable the use of older kernel aware debuggers. */ +typedef xTIMER Timer_t; + +/* The definition of messages that can be sent and received on the timer queue. +Two types of message can be queued - messages that manipulate a software timer, +and messages that request the execution of a non-timer related callback. The +two message types are defined in two separate structures, xTimerParametersType +and xCallbackParametersType respectively. */ +typedef struct tmrTimerParameters +{ + TickType_t xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ + Timer_t * pxTimer; /*<< The timer to which the command will be applied. */ +} TimerParameter_t; + + +typedef struct tmrCallbackParameters +{ + PendedFunction_t pxCallbackFunction; /* << The callback function to execute. */ + void *pvParameter1; /* << The value that will be used as the callback functions first parameter. */ + uint32_t ulParameter2; /* << The value that will be used as the callback functions second parameter. */ +} CallbackParameters_t; + +/* The structure that contains the two message types, along with an identifier +that is used to determine which message type is valid. */ +typedef struct tmrTimerQueueMessage +{ + BaseType_t xMessageID; /*<< The command being sent to the timer service task. */ + union + { + TimerParameter_t xTimerParameters; + + /* Don't include xCallbackParameters if it is not going to be used as + it makes the structure (and therefore the timer queue) larger. */ + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + CallbackParameters_t xCallbackParameters; + #endif /* INCLUDE_xTimerPendFunctionCall */ + } u; +} DaemonTaskMessage_t; + +/*lint -e956 A manual analysis and inspection has been used to determine which +static variables must be declared volatile. */ + +/* The list in which active timers are stored. Timers are referenced in expire +time order, with the nearest expiry time at the front of the list. Only the +timer service task is allowed to access these lists. */ +PRIVILEGED_DATA static List_t xActiveTimerList1; +PRIVILEGED_DATA static List_t xActiveTimerList2; +PRIVILEGED_DATA static List_t *pxCurrentTimerList; +PRIVILEGED_DATA static List_t *pxOverflowTimerList; + +/* A queue that is used to send commands to the timer service task. */ +PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; + +#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + + PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; + +#endif + +/*lint +e956 */ + +/*-----------------------------------------------------------*/ + +/* + * Initialise the infrastructure used by the timer service task if it has not + * been initialised already. + */ +static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; + +/* + * The timer service task (daemon). Timer functionality is controlled by this + * task. Other tasks communicate with the timer service task using the + * xTimerQueue queue. + */ +static void prvTimerTask( void *pvParameters ) PRIVILEGED_FUNCTION; + +/* + * Called by the timer service task to interpret and process a command it + * received on the timer queue. + */ +static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; + +/* + * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, + * depending on if the expire time causes a timer counter overflow. + */ +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; + +/* + * An active timer has reached its expire time. Reload the timer if it is an + * auto reload timer, then call its callback. + */ +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; + +/* + * The tick count has overflowed. Switch the timer lists after ensuring the + * current timer list does not still reference some timers. + */ +static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; + +/* + * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE + * if a tick count overflow occurred since prvSampleTimeNow() was last called. + */ +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; + +/* + * If the timer list contains any active timers then return the expire time of + * the timer that will expire first and set *pxListWasEmpty to false. If the + * timer list does not contain any timers then return 0 and set *pxListWasEmpty + * to pdTRUE. + */ +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; + +/* + * If a timer has expired, process it. Otherwise, block the timer service task + * until either a timer does expire or a command is received. + */ +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; + +/*-----------------------------------------------------------*/ + +BaseType_t xTimerCreateTimerTask( void ) +{ +BaseType_t xReturn = pdFAIL; + + /* This function is called when the scheduler is started if + configUSE_TIMERS is set to 1. Check that the infrastructure used by the + timer service task has been created/initialised. If timers have already + been created then the initialisation will already have been performed. */ + prvCheckForValidListAndQueue(); + + if( xTimerQueue != NULL ) + { + #if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + { + /* Create the timer task, storing its handle in xTimerTaskHandle so + it can be returned by the xTimerGetTimerDaemonTaskHandle() function. */ + xReturn = xTaskCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, (( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT) + PRIORITIE_OFFSET, &xTimerTaskHandle ); + } + #else + { + /* Create the timer task without storing its handle. */ + xReturn = xTaskCreate( prvTimerTask, "Tmr Svc", ( uint16_t ) configTIMER_TASK_STACK_DEPTH, NULL, (( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT ) + PRIORITIE_OFFSET, NULL); + } + #endif + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + configASSERT( xReturn ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +TimerHandle_t xTimerCreate( const char * const pcTimerName, const TickType_t xTimerPeriodInTicks, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +Timer_t *pxNewTimer; + + /* Allocate the timer structure. */ + if( xTimerPeriodInTicks == ( TickType_t ) 0U ) + { + pxNewTimer = NULL; + } + else + { + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->uxAutoReload = uxAutoReload; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + + traceTIMER_CREATE( pxNewTimer ); + } + else + { + traceTIMER_CREATE_FAILED(); + } + } + + /* 0 is not a valid value for xTimerPeriodInTicks. */ + configASSERT( ( xTimerPeriodInTicks > 0 ) ); + + return ( TimerHandle_t ) pxNewTimer; +} +/*-----------------------------------------------------------*/ + +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) +{ +BaseType_t xReturn = pdFAIL; +DaemonTaskMessage_t xMessage; + + /* Send a message to the timer service task to perform a particular action + on a particular timer definition. */ + if( xTimerQueue != NULL ) + { + /* Send a command to the timer service task to start the xTimer timer. */ + xMessage.xMessageID = xCommandID; + xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; + xMessage.u.xTimerParameters.pxTimer = ( Timer_t * ) xTimer; + + if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) + { + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + } + else + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); + } + } + else + { + xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + } + + traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTimerGetTimerDaemonTaskHandle == 1 ) + + TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) + { + /* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been + started, then xTimerTaskHandle will be NULL. */ + configASSERT( ( xTimerTaskHandle != NULL ) ); + return xTimerTaskHandle; + } + +#endif +/*-----------------------------------------------------------*/ + +const char * pcTimerGetTimerName( TimerHandle_t xTimer ) +{ +Timer_t *pxTimer = ( Timer_t * ) xTimer; + + return pxTimer->pcTimerName; +} +/*-----------------------------------------------------------*/ + +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) +{ +BaseType_t xResult; +Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + + /* Remove the timer from the list of active timers. A check has already + been performed to ensure the list is not empty. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* If the timer is an auto reload timer then calculate the next + expiry time and re-insert the timer in the list of active timers. */ + if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + { + /* The timer is inserted into a list using a time relative to anything + other than the current time. It will therefore be inserted into the + correct list relative to the time this task thinks it is now. */ + if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) == pdTRUE ) + { + /* The timer expired before it was added to the active timer + list. Reload it now. */ + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Call the timer callback. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); +} +/*-----------------------------------------------------------*/ + +static void prvTimerTask( void *pvParameters ) +{ +TickType_t xNextExpireTime; +BaseType_t xListWasEmpty; + + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* Query the timers list to see if it contains any timers, and if so, + obtain the time at which the next timer will expire. */ + xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); + + /* If a timer has expired, process it. Otherwise, block this task + until either a timer does expire, or a command is received. */ + prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); + + /* Empty the command queue. */ + prvProcessReceivedCommands(); + } +} +/*-----------------------------------------------------------*/ + +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, const BaseType_t xListWasEmpty ) +{ +TickType_t xTimeNow; +BaseType_t xTimerListsWereSwitched; + + vTaskSuspendAll(); + { + /* Obtain the time now to make an assessment as to whether the timer + has expired or not. If obtaining the time causes the lists to switch + then don't process this timer as any timers that remained in the list + when the lists were switched will have been processed within the + prvSampleTimeNow() function. */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + if( xTimerListsWereSwitched == pdFALSE ) + { + /* The tick count has not overflowed, has the timer expired? */ + if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) + { + ( void ) xTaskResumeAll(); + prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); + } + else + { + /* The tick count has not overflowed, and the next expire + time has not been reached yet. This task should therefore + block to wait for the next expire time or a command to be + received - whichever comes first. The following line cannot + be reached unless xNextExpireTime > xTimeNow, except in the + case when the current timer list is empty. */ + vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) ); + + if( xTaskResumeAll() == pdFALSE ) + { + /* Yield to wait for either a command to arrive, or the block time + to expire. If a command arrived between the critical section being + exited and this yield then the yield will not cause the task + to block. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + ( void ) xTaskResumeAll(); + } + } +} +/*-----------------------------------------------------------*/ + +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) +{ +TickType_t xNextExpireTime; + + /* Timers are listed in expiry time order, with the head of the list + referencing the task that will expire first. Obtain the time at which + the timer with the nearest expiry time will expire. If there are no + active timers then just set the next expire time to 0. That will cause + this task to unblock when the tick count overflows, at which point the + timer lists will be switched and the next expiry time can be + re-assessed. */ + *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); + if( *pxListWasEmpty == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + } + else + { + /* Ensure the task unblocks when the tick count rolls over. */ + xNextExpireTime = ( TickType_t ) 0U; + } + + return xNextExpireTime; +} +/*-----------------------------------------------------------*/ + +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) +{ +TickType_t xTimeNow; +PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ + + xTimeNow = xTaskGetTickCount(); + + if( xTimeNow < xLastTime ) + { + prvSwitchTimerLists(); + *pxTimerListsWereSwitched = pdTRUE; + } + else + { + *pxTimerListsWereSwitched = pdFALSE; + } + + xLastTime = xTimeNow; + + return xTimeNow; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) +{ +BaseType_t xProcessTimerNow = pdFALSE; + + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + + if( xNextExpiryTime <= xTimeNow ) + { + /* Has the expiry time elapsed between the command to start/reset a + timer was issued, and the time the command was processed? */ + if( ( xTimeNow - xCommandTime ) >= pxTimer->xTimerPeriodInTicks ) + { + /* The time between a command being issued and the command being + processed actually exceeds the timers period. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); + } + } + else + { + if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) + { + /* If, since the command was issued, the tick count has overflowed + but the expiry time has not, then the timer must have already passed + its expiry time and should be processed immediately. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + } + + return xProcessTimerNow; +} +/*-----------------------------------------------------------*/ + +static void prvProcessReceivedCommands( void ) +{ +DaemonTaskMessage_t xMessage; +Timer_t *pxTimer; +BaseType_t xTimerListsWereSwitched, xResult; +TickType_t xTimeNow; + + while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ + { + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + { + /* Negative commands are pended function calls rather than timer + commands. */ + if( xMessage.xMessageID < ( BaseType_t ) 0 ) + { + const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); + + /* The timer uses the xCallbackParameters member to request a + callback be executed. Check the callback is not NULL. */ + configASSERT( pxCallback ); + + /* Call the function. */ + pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* INCLUDE_xTimerPendFunctionCall */ + + /* Commands that are positive are timer commands rather than pended + function calls. */ + if( xMessage.xMessageID >= ( BaseType_t ) 0 ) + { + /* The messages uses the xTimerParameters member to work on a + software timer. */ + pxTimer = xMessage.u.xTimerParameters.pxTimer; + + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) + { + /* The timer is in a list, remove it. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); + + /* In this case the xTimerListsWereSwitched parameter is not used, but + it must be present in the function call. prvSampleTimeNow() must be + called after the message is received from xTimerQueue so there is no + possibility of a higher priority task adding a message to the message + queue with a time that is ahead of the timer daemon task (because it + pre-empted the timer daemon task after the xTimeNow value was set). */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + + switch( xMessage.xMessageID ) + { + case tmrCOMMAND_START : + case tmrCOMMAND_START_FROM_ISR : + case tmrCOMMAND_RESET : + case tmrCOMMAND_RESET_FROM_ISR : + case tmrCOMMAND_START_DONT_TRACE : + /* Start or restart a timer. */ + if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) == pdTRUE ) + { + /* The timer expired before it was added to the active + timer list. Process it now. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + traceTIMER_EXPIRED( pxTimer ); + + if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + break; + + case tmrCOMMAND_STOP : + case tmrCOMMAND_STOP_FROM_ISR : + /* The timer has already been removed from the active list. + There is nothing to do here. */ + break; + + case tmrCOMMAND_CHANGE_PERIOD : + case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : + pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; + configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); + + /* The new period does not really have a reference, and can be + longer or shorter than the old one. The command time is + therefore set to the current time, and as the period cannot be + zero the next expiry time can only be in the future, meaning + (unlike for the xTimerStart() case above) there is no fail case + that needs to be handled here. */ + ( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); + break; + + case tmrCOMMAND_DELETE : + /* The timer has already been removed from the active list, + just free up the memory. */ + vPortFree( pxTimer ); + break; + + default : + /* Don't expect to get here. */ + break; + } + } + } +} +/*-----------------------------------------------------------*/ + +static void prvSwitchTimerLists( void ) +{ +TickType_t xNextExpireTime, xReloadTime; +List_t *pxTemp; +Timer_t *pxTimer; +BaseType_t xResult; + + /* The tick count has overflowed. The timer lists must be switched. + If there are any timers still referenced from the current timer list + then they must have expired and should be processed before the lists + are switched. */ + while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + + /* Remove the timer from the list. */ + pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* Execute its callback, then send a command to restart the timer if + it is an auto-reload timer. It cannot be restarted here as the lists + have not yet been switched. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + + if( pxTimer->uxAutoReload == ( UBaseType_t ) pdTRUE ) + { + /* Calculate the reload value, and if the reload value results in + the timer going into the same timer list then it has already expired + and the timer should be re-inserted into the current list so it is + processed again within this loop. Otherwise a command should be sent + to restart the timer to ensure it is only inserted into a list after + the lists have been swapped. */ + xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); + if( xReloadTime > xNextExpireTime ) + { + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + else + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + pxTemp = pxCurrentTimerList; + pxCurrentTimerList = pxOverflowTimerList; + pxOverflowTimerList = pxTemp; +} +/*-----------------------------------------------------------*/ + +static void prvCheckForValidListAndQueue( void ) +{ + /* Check that the list from which active timers are referenced, and the + queue used to communicate with the timer service, have been + initialised. */ + taskENTER_CRITICAL(); + { + if( xTimerQueue == NULL ) + { + vListInitialise( &xActiveTimerList1 ); + vListInitialise( &xActiveTimerList2 ); + pxCurrentTimerList = &xActiveTimerList1; + pxOverflowTimerList = &xActiveTimerList2; + xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) ); + configASSERT( xTimerQueue ); + + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + if( xTimerQueue != NULL ) + { + vQueueAddToRegistry( xTimerQueue, "TmrQ" ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configQUEUE_REGISTRY_SIZE */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) +{ +BaseType_t xTimerIsInActiveList; +Timer_t *pxTimer = ( Timer_t * ) xTimer; + + /* Is the timer in the list of active timers? */ + taskENTER_CRITICAL(); + { + /* Checking to see if it is in the NULL list in effect checks to see if + it is referenced from either the current or the overflow timer lists in + one go, but the logic has to be reversed, hence the '!'. */ + xTimerIsInActiveList = ( BaseType_t ) !( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) ); + } + taskEXIT_CRITICAL(); + + return xTimerIsInActiveList; +} /*lint !e818 Can't be pointer to const due to the typedef. */ +/*-----------------------------------------------------------*/ + +void *pvTimerGetTimerID( const TimerHandle_t xTimer ) +{ +Timer_t * const pxTimer = ( Timer_t * ) xTimer; + + return pxTimer->pvTimerID; +} +/*-----------------------------------------------------------*/ + +#if( INCLUDE_xTimerPendFunctionCall == 1 ) + + BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; + + /* Complete the message with the function parameters and post it to the + daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + + xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + + tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + + return xReturn; + } + +#endif /* INCLUDE_xTimerPendFunctionCall */ +/*-----------------------------------------------------------*/ + +#if( INCLUDE_xTimerPendFunctionCall == 1 ) + + BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; + + /* Complete the message with the function parameters and post it to the + daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + + tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + + return xReturn; + } + +#endif /* INCLUDE_xTimerPendFunctionCall */ +/*-----------------------------------------------------------*/ + +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. If you want to include software timer +functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#endif /* configUSE_TIMERS == 1 */ + + + diff --git a/component/os/os_dep/include/mailbox.h b/component/os/os_dep/include/mailbox.h new file mode 100644 index 0000000..e3b5f47 --- /dev/null +++ b/component/os/os_dep/include/mailbox.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#ifndef __MAILBOX_H_ +#define __MAILBOX_H_ + +#include "hal_api.h" +#include "osdep_api.h" +#include "hal_util.h" +#ifdef CONFIG_FREERTOS +#include "queue.h" +#endif + +#define MBOX_WAIT_NO_TIMEOUT 0xffffffff // waiting for send/receive message with no timeout +#define MBOX_WAIT_NONE 0 // No wait for send/receive message + +typedef enum _MAILBOX_ID_ { + MBOX_ID_WLAN = 0, + MBOX_ID_UART = 1, + MBOX_ID_I2C = 2, + MBOX_ID_I2S = 3, + MBOX_ID_SPI = 4, + MBOX_ID_SDIO = 5, + MBOX_ID_SDIO_MP = 6, + + MBOX_ID_MAX = 0xff +} MAILBOX_ID; + +#if defined(CONFIG_SDIO_DEVICE_EN) && defined(CONFIG_SDIO_DEVICE_NORMAL) +typedef enum _MSG_TYPE_SDIO { + MSG_SDIO_RX_PKT=1, // request to send a SDIO RX packet to the host side + MSG_SDIO_C2H=2, // request to send a C2H message + MSG_SDIO_RPWM=3, // request to set the RPWM + MSG_SDIO_MP_LOOP_TXPKT=4, // request to loopback this TX packet + + MSG_SDIO_MAX=0xff +} MSG_TYPE_SDIO; +#endif // end of "#ifdef CONFIG_SDIO_DEVICE_EN" + +/* the data structure of a MailBox to deliver message blocks */ +typedef struct _RTL_MAILBOX_ { + void *mbox_hdl; // the mailbox handle which return from OS create queue API + _Sema *pWakeSema; // the semaphore to wakeup the message receiving task + _LIST mbox_list; // the link list to chain all created mailbox + u8 mbox_id; /* the ID of this Mailbox, this ID is + used to locate the MBox for send/get message */ +} RTL_MAILBOX, *PRTL_MAILBOX; + +/* the data structure of a message block */ +typedef struct _RTL_MSG_BLK { + u8 MsgType; // the message type + u8 Reserved; // reserved + u16 DateLen; // the vaild data length of the pBuf + u32 Para; // the optional parameters associated with this message type + u8 *pBuf; // point to a data buffer associated with this message type +} MSG_BLK, *PMSG_BLK; + +/* the data structure for system level message block management */ +typedef struct _RTL_MBOX_ROOT_ { + _LIST mbox_list; // the link list of all created mailbox + _Mutex Mutex; // the Mutex to protect the mailbox create/delete procedure + u8 isInitialed; // is this Mailbox link-list initialed +} RTL_MBOX_ROOT, *PRTL_MBOX_ROOT; + +// Export Funcction API +extern PRTL_MAILBOX RtlMailboxCreate( + IN u8 MboxID, + IN u32 MboxSize, + IN _Sema *pWakeSema +); + +extern VOID RtlMailboxDel( + IN PRTL_MAILBOX MboxHdl +); + +extern u8 RtlMailboxSendToBack( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +extern u8 RtlMailboxSendToFront( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +extern u8 RtlMailboxReceive( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +extern u8 RtlMailboxPeek( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +extern u32 RtlMailboxMsgWaiting( + IN u8 MboxID, + IN u8 IsFromISR +); + + +#endif // #ifndef __MAILBOX_H_ + diff --git a/component/os/os_dep/include/os_support.h b/component/os/os_dep/include/os_support.h new file mode 100644 index 0000000..6020062 --- /dev/null +++ b/component/os/os_dep/include/os_support.h @@ -0,0 +1,344 @@ + /****************************************************************************** + * + * Name: sys-support.h - System type support for Linux + * $Revision: 1.1.1.1 $ + * + *****************************************************************************/ + +#ifndef __OS_SUPPORT_H__ +#define __OS_SUPPORT_H__ + + +#include +#include +#include "os_support.h" +//#include "diag.h" + + + +#if 0 +#define __init +#define __exit +#define __devinit +#define __devexit +#endif +#define RTL_HZ 100 + +#define SemaInit(sem, value) vSemaphoreCreateBinary(sem) +#define SemaPost(sem) xSemaphoreGive(sem) +#define SemaWait(sem, block_time) xSemaphoreTake(sem, block_time) +//#define printk DiagPrintf + +#define SpinLockInit(lock) do { } while (0) +#define SpinLock(x) do { } while (0) +#define SpinUnlock(x) do { } while (0) +#define SpinLockBh(x) do { } while (0) +#define SpinUnlockBh(x) do { } while (0) +#ifdef PLATFORM_FREERTOS +#define RestoreFlags() portEXIT_CRITICAL() +#define SaveAndCli() portENTER_CRITICAL() +#define SpinLockIrqSave(lock, flags) SaveAndCli() +#define SpinUnlockIrqRestore(l, f) RestoreFlags() +#else +#define RestoreFlags(x) portENABLE_INTERRUPTS() +#define SaveAndCli(x) portDISABLE_INTERRUPTS() +#define SpinLockIrqSave(lock, flags) SaveAndCli(flags) +#define SpinUnlockIrqRestore(l, f) RestoreFlags(f) +#endif + + +//#define RtlKmalloc(size, flag) pvPortMallocAligned(size, 0) +#define RtlKmalloc(size, flag) pvPortMalloc(size) +#define RtlKfree(pv) vPortFreeAligned(pv) + + + +#ifdef CONFIG_TIMER_MODULE +extern _LONG_CALL_ u32 HalDelayUs(u32 us); +#define __Delay(t) HalDelayUs(t) +#else +static __inline__ u32 __Delay(u32 us) +{ + DBG_8195A("No Delay: please enable hardware Timer\n"); +} +#endif + + +#define Mdelay(t) __Delay(t*1000) +#define Udelay(t) __Delay(t) + + +#define ASSERT(_bool_) do { } while (0) + +//#define panic_printk DiagPrintf +//#define sprintf DiagPrintf +//#define diag_sprintf DiagPrintf + + +//1TODO: Need check again; the below just for compile ok ; chris + +/* + * ATOMIC_READ - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +//#define AtomicRead(v) ((*v)) + +extern __inline__ u32 +AtomicRead( + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + u32 Temp; + + SaveAndCli(); + Temp = v->counter; + RestoreFlags(); + + return Temp; + +#else + u32 Temp, Flags; + + SaveAndCli(Flags); + Temp = v->counter; + RestoreFlags(Flags); + + return Temp; +#endif +} + +/* + * ATOMIC_SET - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +//#define AtomicSet(v,i) ((v)->counter = (i)) + +extern __inline__ VOID +AtomicSet( + IN u32 i, + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + SaveAndCli(); + v->counter = i; + RestoreFlags(); +#else + u32 Flags; + + SaveAndCli(Flags); + v->counter = i; + RestoreFlags(Flags); +#endif +} + +/* + * The MIPS I implementation is only atomic with respect to + * interrupts. R3000 based multiprocessor machines are rare anyway ... + * + * AtomicAdd - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. Note that the guaranteed useful range + * of an atomic_t is only 24 bits. + */ +extern __inline__ VOID +AtomicAdd( + IN u32 i, + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + SaveAndCli(); + v->counter += i; + RestoreFlags(); +#else + u32 Flags; + + SaveAndCli(Flags); + v->counter += i; + RestoreFlags(Flags); +#endif +} + +/* + * AtomicSub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +extern __inline__ void +AtomicSub( + IN u32 i, + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + SaveAndCli(); + v->counter -= i; + RestoreFlags(); +#else + u32 Flags; + + SaveAndCli(Flags); + v->counter -= i; + RestoreFlags(Flags); +#endif +} + +extern __inline__ u32 +AtomicAddReturn( + IN u32 i, + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + u32 Temp; + + SaveAndCli(); + Temp = v->counter; + Temp += i; + v->counter = Temp; + RestoreFlags(); + + return Temp; + +#else + u32 Temp, Flags; + + SaveAndCli(Flags); + Temp = v->counter; + Temp += i; + v->counter = Temp; + RestoreFlags(Flags); + + return Temp; +#endif +} + +extern __inline__ u32 +AtomicSubReturn( + IN u32 i, + IN atomic_t * v +) +{ +#ifdef PLATFORM_FREERTOS + u32 Temp; + + SaveAndCli(); + Temp = v->counter; + Temp -= i; + v->counter = Temp; + RestoreFlags(); + + return Temp; + +#else + + u32 Temp, Flags; + + SaveAndCli(Flags); + Temp = v->counter; + Temp -= i; + v->counter = Temp; + RestoreFlags(Flags); + + return Temp; +#endif +} + +/* + * ATOMIC_INC - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define AtomicInc(v) AtomicAdd(1,(v)) + +#define AtomicIncReturn(v) AtomicAddReturn(1,(v)) + +/* + * ATOMIC_DEC - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define AtomicDec(v) AtomicSub(1,(v)) + +#define AtomicDecReturn(v) AtomicSubReturn(1,(v)) + +/* + * ATOMIC_DEC_AND_TEST - decrement by 1 and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define AtomicDecAndTest(v) (AtomicSubReturn(1, (v)) == 0) + +/* Not needed on 64bit architectures */ +static __inline__ u32 +__Div64_32( + IN __uint64_t *n, + IN u32 base +) +{ + __uint64_t rem = *n; + __uint64_t b = base; + __uint64_t res, d = 1; + u32 high = rem >> 32; + + /* Reduce the thing a bit first */ + res = 0; + if (high >= base) { + high /= base; + res = (__uint64_t) high << 32; + rem -= (__uint64_t) (high*base) << 32; + } + + while ((__int64_t)b > 0 && b < rem) { + b = b+b; + d = d+d; + } + + do { + if (rem >= b) { + rem -= b; + res += d; + } + b >>= 1; + d >>= 1; + } while (d); + + *n = res; + return rem; +} + +#define DO_DIV(n,base) ({ \ + unsigned int __base = (base); \ + unsigned int __rem; \ + (void)(((typeof((n)) *)0) == ((__uint64_t *)0)); \ + if (((n) >> 32) == 0) { \ + __rem = (unsigned int)(n) % __base; \ + (n) = (unsigned int)(n) / __base; \ + } else \ + __rem = __Div64_32(&(n), __base); \ + __rem; \ + }) + +#endif /* __SYS_SUPPORT_H__ */ diff --git a/component/os/os_dep/include/os_timer.h b/component/os/os_dep/include/os_timer.h new file mode 100644 index 0000000..b2a6529 --- /dev/null +++ b/component/os/os_dep/include/os_timer.h @@ -0,0 +1,215 @@ +/****************************************************************************** + * + * Name: sys-support.h - System type support for Linux + * $Revision: 1.1.1.1 $ + * + *****************************************************************************/ + +#ifndef __OS_TIMER_H__ +#define __OS_TIMER_H__ + +#include "diag.h" +#include "os_support.h" +#include "timers.h" + + +#define JIFFIES xTaskGetTickCount() + +enum { + TIMER_NO_INIT = 0, + TIMER_INIT = 1, + TIMER_START = 2, + TIMER_DISABLE = 3 +}; + +struct TIMER_LIST { + xTimerHandle TimeHdl; + u32 Flag; + unsigned long Data; + VOID (*Function)(void *); + u32 TimerID; +}; + +static inline VOID +InitTimer( + IN struct TIMER_LIST *Timer +) +{ +#ifdef RTK_MODE_TIMER + u32 data = Timer->Data; +#endif +#ifndef PLATFORM_FREERTOS + u32 Flags; +#endif + u32 TimerID = Timer->TimerID; + VOID (*Function)(VOID *) = Timer->Function; +// xTimerHandle timer_handle; + + +#ifdef PLATFORM_FREERTOS + SaveAndCli(); +#else + SaveAndCli(Flags); +#endif + + if (Timer->Flag != TIMER_DISABLE) { + if (Timer->Flag == TIMER_NO_INIT) { + Timer->TimeHdl = xTimerCreate( (const char *)"Timer", // Just a test name, not used by the kernel. + ( 100 ), // The timer period in ticks. + pdFALSE, // The timers will auto-reload themselves when they expire. + ( void * ) TimerID, // Assign each timer a unique id equal to its array index. + Function + #ifdef RTK_MODE_TIMER + ,data // Each timer calls the same callback when it expires. + #endif + ); + if (NULL == Timer->TimeHdl) { + DBG_ERROR_LOG("\rInitial Timer fail !!!!!!!!!\n"); + } + else { + TimerID++; + } + + Timer->Flag = TIMER_INIT; + } + else if (Timer->Flag == TIMER_START) { + xTimerStop(Timer->TimeHdl,0); + Timer->Flag = TIMER_DISABLE; + } + } + +#ifdef PLATFORM_FREERTOS + RestoreFlags(); +#else + RestoreFlags(Flags); +#endif +} + +static inline void +ModTimer( + IN struct TIMER_LIST *Timer, + IN u32 TimeoutTicks +) +{ +#ifndef PLATFORM_FREERTOS + u32 Flags; +#endif + + void (*Function)(void *) = Timer->Function; + +#ifdef PLATFORM_FREERTOS + SaveAndCli(); +#else + SaveAndCli(Flags); +#endif + + if (Timer->Flag == TIMER_NO_INIT) { + if (Timer->Function) { + Timer->TimeHdl = xTimerCreate((const char *)"Timer", // Just a text name, not used by the kernel. + ( 100 ), // The timer period in ticks. + pdFALSE, // The timers will auto-reload themselves when they expire. + ( void * ) Timer->TimerID, // Assign each timer a unique id equal to its array index. + Function + #ifdef RTK_MODE_TIMER + ,Timer->Data // Each timer calls the same callback when it expires. + #endif + ); + if (NULL == Timer->TimeHdl) { + DBG_ERROR_LOG("\rInitial Timer fail !!!!!!!!!\n"); + } + else { + Timer->TimerID++; + } + + Timer->Flag = TIMER_INIT; + } + else { + //printf("###mod_timer() not initilized, timer->flag=%d timer->function=%p timeout_ticks=%llu###\n", timer->flag, timer->function, timeout_ticks); +#ifdef PLATFORM_FREERTOS + RestoreFlags(); +#else + RestoreFlags(Flags); +#endif + return; + } + } + else if (Timer->Flag == TIMER_START) { + xTimerStop(Timer->TimeHdl,0); + Timer->Flag = TIMER_DISABLE; + } + + TimeoutTicks -= xTaskGetTickCount(); + if (TimeoutTicks <= 0) + TimeoutTicks = 2; + + if (xTimerStart(Timer->TimeHdl, TimeoutTicks )) + Timer->Flag = TIMER_START; + else + DBG_ERROR_LOG("\r###mod_timer() - no slots available###\n"); +#ifdef PLATFORM_FREERTOS + RestoreFlags(); +#else + RestoreFlags(Flags); +#endif +} + + +static inline int +TimerPending ( + IN const struct TIMER_LIST *Timer +) +{ + if (Timer->TimeHdl && Timer->Flag != TIMER_NO_INIT) + return 1; + else + return 0; +} + +static inline void +DelTimerSync( + IN struct TIMER_LIST *Timer +) +{ +#ifdef PLATFORM_FREERTOS + SaveAndCli(); +#else + u32 Flags; + SaveAndCli(Flags); +#endif + if (Timer->TimeHdl && Timer->Flag != TIMER_INIT) { + if (Timer->Flag == TIMER_START) + xTimerStop(Timer->TimeHdl, 0); + + xTimerDelete(Timer->TimeHdl, 0); + Timer->Flag = TIMER_NO_INIT; + } + +#ifdef PLATFORM_FREERTOS + RestoreFlags(); +#else + RestoreFlags(Flags); +#endif +} + + /* + * These inlines deal with timer wrapping correctly. You are + * strongly encouraged to use them + * 1. Because people otherwise forget + * 2. Because if the timer wrap changes in future you wont have to + * alter your driver code. + * + * time_after(a,b) returns true if the time a is after time b. + * + * Do this with "<0" and ">=0" to only test the sign of the result. A + * good compiler would generate better code (and a really good compiler + * wouldn't care). Gcc is currently neither. + */ + #define TIME_AFTER(a,b) ((long)(b) - (long)(a) < 0) + #define TIMER_BEFORE(a,b) TIME_AFTER(b,a) + + #define TIME_AFTER_EQ(a,b) ((long)(a) - (long)(b) >= 0) + #define TIMER_BEFORE_EQ(a,b) TIME_AFTER_EQ(b,a) + + + +#endif //__OS_TIMER_H__ \ No newline at end of file diff --git a/component/os/os_dep/include/osdep_api.h b/component/os/os_dep/include/osdep_api.h new file mode 100644 index 0000000..058bccf --- /dev/null +++ b/component/os/os_dep/include/osdep_api.h @@ -0,0 +1,561 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#ifndef __OSDEP_API_H_ +#define __OSDEP_API_H_ + +#include "os_timer.h" +#include "os_support.h" +#include "semphr.h" + +#if 0 +/* Structure used to pass parameters to each task. */ +typedef struct SEMAPHORE_PARAMETERS +{ + xSemaphoreHandle xSemaphore; +// volatile unsigned long *pulSharedVariable; + portTickType xBlockTime; +} xSemaphoreParameters; +#endif + +//#define RTW_STATUS_TIMEDOUT -110 + + +#define MAX_SEMA_COUNT 32 /* the maximum count of a semaphore */ + +typedef xSemaphoreHandle _Sema; +typedef xSemaphoreHandle _Mutex; +typedef u32 _Lock; +typedef struct TIMER_LIST _Timer; + +//typedef unsigned char _buffer; + +typedef unsigned long _IRQL; +//typedef struct net_device * _nic_hdl; +typedef xTaskHandle _THREAD_HDL_; +typedef VOID THREAD_RETURN; +typedef VOID* THREAD_CONTEXT; + + +#ifndef mdelay +#define mdelay(t) ((t/portTICK_RATE_MS)>0)?(vTaskDelay(t/portTICK_RATE_MS)):(vTaskDelay(1)) +#endif + +#ifndef udelay +#define udelay(t) ((t/(portTICK_RATE_MS*1000))>0)?vTaskDelay(t/(portTICK_RATE_MS*1000)):(vTaskDelay(1)) +#endif + +/* to delete/start/stop a timer it will send a message to the timer task through a message queue, + so we define the max wait time for message sending */ +#define RTL_TIMER_API_MAX_BLOCK_TIME 1000 // unit is ms +#define RTL_TIMER_API_MAX_BLOCK_TICKS (RTL_TIMER_API_MAX_BLOCK_TIME/portTICK_RATE_MS) + +typedef VOID +(*RTL_TIMER_CALL_BACK)( + void *pContext +); + +typedef struct _RTL_TIMER{ +#ifdef PLATFORM_FREERTOS + xTimerHandle TimerHandle; // the timer handle of created FreeRTOS soft-timer +#endif + RTL_TIMER_CALL_BACK CallBackFunc; // Callback function of this timer + u32 msPeriod; // The period of this timer + void *Context; // Timer specific context. + u8 isPeriodical; // is a periodical timer + u8 TimerName[35]; // the Name of timer +}RTL_TIMER, *PRTL_TIMER; + +__inline static VOID +RtlEnterCritical(VOID) +{ + portENTER_CRITICAL(); +} + +__inline static VOID +RtlExitCritical(VOID) +{ + portEXIT_CRITICAL(); +} + +__inline static VOID +RtlEnterCriticalBh( + IN _Lock *plock, + IN _IRQL *pirqL +) +{ + SpinLockBh(plock); +} + +__inline static VOID +RtlExitCriticalBh( + IN _Lock *plock, + IN _IRQL *pirqL +) +{ + SpinUnlockBh(plock); +} +__inline static u32 +RtlEnterCriticalMutex( + IN _Mutex *pmutex, + IN _IRQL *pirqL +) +{ + u32 ret = 0; + xSemaphoreTake(*pmutex, portMAX_DELAY); + return ret; +} + + +__inline static VOID +RtlExitCriticalMutex( + IN _Mutex *pmutex, + IN _IRQL *pirqL +) +{ + xSemaphoreGive(*pmutex); +} + +__inline static VOID +RtlInitTimer( + IN _Timer *ptimer, + IN VOID *Data, + IN VOID (*pfunc)(VOID *), + IN VOID* cntx +) +{ + ptimer->Function = pfunc; + ptimer->Data = (unsigned long)cntx; + InitTimer(ptimer); +} + +__inline static VOID +RtlSetTimer( + IN _Timer *ptimer, + IN u32 delay_time +) +{ + ModTimer(ptimer , (JIFFIES+(delay_time*RTL_HZ/1000))); +} + +__inline static VOID +RtlCancelTimer( + IN _Timer *ptimer, + IN u8 *bcancelled +) +{ + DelTimerSync(ptimer); + *bcancelled= _TRUE;//TRUE ==1; FALSE==0 +} + +__inline static u32 +RtlSystime2Ms( + IN u32 systime +) +{ + return systime * 1000 / RTL_HZ; +} + + + +__inline static u32 +RtlMs2Systime( + IN u32 ms +) +{ + return ms * RTL_HZ / 1000; +} + +extern u8* RtlZmalloc(u32 sz); +extern u8* RtlMalloc(u32 sz); +extern VOID RtlMfree(u8 *pbuf, u32 sz); + +extern VOID* RtlMalloc2d(u32 h, u32 w, u32 size); +extern VOID RtlMfree2d(VOID *pbuf, u32 h, u32 w, u32 size); + +extern VOID RtlInitSema(_Sema *sema, u32 init_val); +extern VOID RtlFreeSema(_Sema *sema); +extern VOID RtlUpSema(_Sema *sema); +extern VOID RtlUpSemaFromISR(_Sema *sema); +extern u32 RtlDownSema(_Sema *sema); +extern u32 RtlDownSemaWithTimeout(_Sema *sema, u32 ms); + +extern VOID RtlMutexInit(_Mutex *pmutex); +extern VOID RtlMutexFree(_Mutex *pmutex); + +extern VOID RtlSpinlockInit(_Lock *plock); +extern VOID RtlSpinlockFree(_Lock *plock); +extern VOID RtlSpinlock(_Lock *plock); +extern VOID RtlSpinunlock(_Lock *plock); +extern VOID RtlSpinlockEx(_Lock *plock); +extern VOID RtlSpinunlockEx(_Lock *plock); + +extern VOID RtlSleepSchedulable(u32 ms); + +extern VOID RtlMsleepOS(u32 ms); +extern VOID RtlUsleepOS(u32 us); +extern VOID RtlMdelayOS(u32 ms); +extern VOID RtlUdelayOS(u32 us); + +//extern VOID rtw_mdelay_os(u32 ms); +//extern VOID rtw_udelay_os(u32 us); + +//1TODO: Need Check if we need add this api +extern VOID RtlYieldOS(VOID); + +#define RtlUpMutex(mutex) RtlUpSema(mutex) +#define RtlDownMutex(mutex) RtlDownSema(mutex) + +__inline static u8 +RtlCancelTimerEx( + IN _Timer *ptimer +) +{ + DelTimerSync(ptimer); + return 0; +} + + +static __inline VOID +ThreadEnter( + IN char *name +) +{ + DBG_8195A("\rRTKTHREAD_enter %s\n", name); +} + + + +#define ThreadExit() do{DBG_8195A("\rRTKTHREAD_exit %s\n", __FUNCTION__);}while(0) + +__inline static VOID +FlushSignalsThread(VOID) +{ +#ifdef PLATFORM_LINUX + if (signal_pending (current)) + { + flush_signals(current); + } +#endif +} + + +#define RTL_RND(sz, r) ((((sz)+((r)-1))/(r))*(r)) +#define RTL_RND4(x) (((x >> 2) + (((x & 3) == 0) ? 0: 1)) << 2) + +__inline static u32 +RtlRnd4( + IN u32 sz +) +{ + + u32 val; + + val = ((sz >> 2) + ((sz & 3) ? 1: 0)) << 2; + + return val; + +} + +__inline static u32 +RtlRnd8( + IN u32 sz +) +{ + + u32 val; + + val = ((sz >> 3) + ((sz & 7) ? 1: 0)) << 3; + + return val; + +} + +__inline static u32 +RtlRnd128( + IN u32 sz +) +{ + + u32 val; + + val = ((sz >> 7) + ((sz & 127) ? 1: 0)) << 7; + + return val; + +} + +__inline +static u32 RtlRnd256( + IN u32 sz +) +{ + + u32 val; + + val = ((sz >> 8) + ((sz & 255) ? 1: 0)) << 8; + + return val; + +} + +__inline static u32 +RtlRnd512( + IN u32 sz +) +{ + + u32 val; + + val = ((sz >> 9) + ((sz & 511) ? 1: 0)) << 9; + + return val; + +} + +__inline static u32 +BitShift( + IN u32 BitMask +) +{ + u32 i; + + for (i = 0; i <= 31; i++) + if (((BitMask>>i) & 0x1) == 1) break; + + return i; +} + + +//#ifdef __GNUC__ +#ifdef PLATFORM_LINUX +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define STRUCT_PACKED +#endif + + + + + + +//Atomic integer operations +#define RTL_ATOMIC_T atomic_t + + + +extern inline VOID +RTL_ATOMIC_SET( + IN RTL_ATOMIC_T *v, + IN u32 i +) +{ + AtomicSet(i,v); +} + +extern inline uint32_t +RTL_ATOMIC_READ( + IN RTL_ATOMIC_T *v +) +{ + return AtomicRead(v); +} + +extern inline VOID +RTL_ATOMIC_ADD( + IN RTL_ATOMIC_T *v, + IN u32 i +) +{ + AtomicAdd(i,v); +} +extern inline VOID +RTL_ATOMIC_SUB( + IN RTL_ATOMIC_T *v, + IN u32 i +) +{ + AtomicSub(i,v); +} + +extern inline VOID +RTL_ATOMIC_INC( + IN RTL_ATOMIC_T *v +) +{ + AtomicInc(v); +} + +extern inline VOID +RTL_ATOMIC_DEC( + IN RTL_ATOMIC_T *v +) +{ + AtomicDec(v); +} + +extern inline u32 +RTL_ATOMIC_ADD_RETURN( + IN RTL_ATOMIC_T *v, + IN u32 i +) +{ + return AtomicAddReturn(i,v); +} + +extern inline u32 +RTL_ATOMIC_SUB_RETURN( + IN RTL_ATOMIC_T *v, + IN u32 i +) +{ + return AtomicSubReturn(i,v); +} + +extern inline u32 +RTL_ATOMIC_INC_RETURN( + IN RTL_ATOMIC_T *v +) +{ + return AtomicIncReturn(v); +} + +extern inline u32 +RTL_ATOMIC_DEC_RETURN( + IN RTL_ATOMIC_T *v +) +{ + return AtomicDecReturn(v); +} + + + + +extern u64 RtlModular64(u64 x, u64 y); + + +/* Macros for handling unaligned memory accesses */ +#if 0 +#define RTW_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define RTW_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define RTW_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define RTW_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + +#define RTW_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \ + ((u32) (a)[2])) +#define RTW_PUT_BE24(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[2] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \ + (((u32) (a)[2]) << 8) | ((u32) (a)[3])) +#define RTW_PUT_BE32(a, val) \ + do { \ + (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[3] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \ + (((u32) (a)[1]) << 8) | ((u32) (a)[0])) +#define RTW_PUT_LE32(a, val) \ + do { \ + (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff); \ + (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff); \ + (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff); \ + (a)[0] = (u8) (((u32) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \ + (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \ + (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \ + (((u64) (a)[6]) << 8) | ((u64) (a)[7])) +#define RTW_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (0) + +#define RTW_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \ + (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \ + (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \ + (((u64) (a)[1]) << 8) | ((u64) (a)[0])) +#endif + +extern PRTL_TIMER +RtlTimerCreate( + IN char *pTimerName, + IN u32 TimerPeriodMS, + IN RTL_TIMER_CALL_BACK CallbckFunc, + IN void *pContext, + IN u8 isPeriodical +); + +extern VOID +RtlTimerDelete( + IN PRTL_TIMER pTimerHdl +); + +extern u8 +RtlTimerStart( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +); + +extern u8 +RtlTimerStop( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +); + +extern u8 +RtlTimerReset( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +); + +extern u8 +RtlTimerChangePeriod( + IN PRTL_TIMER pTimerHdl, + IN u32 NewPeriodMS, + IN u8 isFromISR +); + +#endif //#ifndef __OSDEP_API_H_ + + diff --git a/component/os/os_dep/mailbox.c b/component/os/os_dep/mailbox.c new file mode 100644 index 0000000..5d09f48 --- /dev/null +++ b/component/os/os_dep/mailbox.c @@ -0,0 +1,574 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ + + +#define _MAILBOX_C_ + +#include "mailbox.h" + +/****************************************************************************** + * Function Prototype Declaration + ******************************************************************************/ +static PRTL_MAILBOX RtlMBoxIdToHdl( + IN u8 MBoxId +); + +PRTL_MAILBOX RtlMailboxCreate( + IN u8 MboxID, + IN u32 MboxSize, + IN _Sema *pWakeSema +); + +VOID RtlMailboxDel( + IN PRTL_MAILBOX MboxHdl +); + +u8 RtlMailboxSendToBack( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +u8 RtlMailboxSendToFront( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +u8 RtlMailboxReceive( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +u8 RtlMailboxPeek( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +); + +u32 RtlMailboxMsgWaiting( + IN u8 MboxID, + IN u8 IsFromISR +); + +/****************************************************************************** + * Global Variable Declaration + ******************************************************************************/ +static RTL_MBOX_ROOT MBox_Entry={0}; + +/****************************************************************************** + * External Function & Variable Declaration + ******************************************************************************/ + + +/****************************************************************************** + * Function: RtlMBoxIdToHdl + * Desc: Map a mailbox ID to the mailbox pointer. + * Para: + * MBoxId: The Mailbox ID + * Return: The pointer of the mailbox. If didn't found match mailbox, + * return NULL. + * + ******************************************************************************/ +static PRTL_MAILBOX RtlMBoxIdToHdl( + IN u8 MBoxId +) +{ + RTL_MAILBOX *pMbox=NULL; + RTL_MAILBOX *pTmpMbox; + _LIST *pHead; + _LIST *pList; + + // if the Mailbox root entry initialed ? if not, initial it + if (!MBox_Entry.isInitialed) { + RtlMutexInit(&MBox_Entry.Mutex); // Init the Mutex for the mailbox add/delete procedure protection + RtlInitListhead(&MBox_Entry.mbox_list); // Init the link list head to chain all created mailbox + MBox_Entry.isInitialed = 1; + MSG_MBOX_INFO("MBox Entry Initial...\n"); + } + + pHead = &MBox_Entry.mbox_list; + RtlDownMutex(&MBox_Entry.Mutex); + pList = RtlListGetNext(&MBox_Entry.mbox_list); + while (pList != pHead) { + pTmpMbox = CONTAINER_OF(pList, RTL_MAILBOX, mbox_list); + if (MBoxId == pTmpMbox->mbox_id) { + pMbox = pTmpMbox; + break; + } + pList = RtlListGetNext(pList); + } + RtlUpMutex(&MBox_Entry.Mutex); + + return pMbox; +} + +/****************************************************************************** + * Function: RtlMailboxCreate + * Desc: To create a mailbox with a given mailbox ID and size + * Para: + * MboxID: A number to identify this created mailbox. A message block can + * be send to a mailbox by a given MboxID. The MboxID must be unique + * in the whole system. If this MboxID is conflict with a created + * mailbox, the mailbox creation will fail and return NULL. + * MboxSize: The size of this mailbox to be created. It means maximum number + * of message blocks can be stored in this mailbox. + * pWakeSema: The semaphore to wake up the receiving task to receive the new + * message. If the receiving task doesn't need a semaphore to wakeup + * it, then just let this pointer is NULL. + * Return: The created mailbox pointer. If it failed, return NULL. + ******************************************************************************/ +PRTL_MAILBOX RtlMailboxCreate( + IN u8 MboxID, + IN u32 MboxSize, + IN _Sema *pWakeSema +) +{ + PRTL_MAILBOX pMBox=NULL; + + // if the Mailbox root entry initialed ? if not, initial it + if (!MBox_Entry.isInitialed) { + RtlMutexInit(&MBox_Entry.Mutex); // Init the Mutex for the mailbox add/delete procedure protection + RtlInitListhead(&MBox_Entry.mbox_list); // Init the link list head to chain all created mailbox + MBox_Entry.isInitialed = 1; + MSG_MBOX_INFO("MBox Entry Initial...\n"); + } + + // check if this mailbox ID is ocupied ? + pMBox = RtlMBoxIdToHdl(MboxID); + if (NULL != pMBox) { + MSG_MBOX_ERR("RtlMailboxCreate: The Mailbox ID %d is used by someone!!\n", MboxID); + return NULL; + } + + pMBox = (RTL_MAILBOX *)RtlZmalloc(sizeof(RTL_MAILBOX)); + if (NULL==pMBox) { + MSG_MBOX_ERR("RtlMailboxCreate: MAlloc Failed\n"); + return NULL; + } + + RtlInitListhead(&pMBox->mbox_list); // Init the link list to be chained into the created mailbox list + pMBox->mbox_id = MboxID; + pMBox->pWakeSema = pWakeSema; +#ifdef PLATFORM_FREERTOS + pMBox->mbox_hdl = xQueueCreate(MboxSize, sizeof(MSG_BLK)); + if (NULL == pMBox->mbox_hdl) { + MSG_MBOX_ERR("RtlMailboxCreate: xQueueCreate Failed\n"); + RtlMfree((void *)pMBox, sizeof(RTL_MAILBOX)); + return NULL; + } +#endif +#ifdef PLATFORM_ECOS +// TODO: Create mailbox +#endif + + // Add this mailbox to the link list of created mailbox + RtlDownMutex(&MBox_Entry.Mutex); + RtlListInsertTail(&pMBox->mbox_list, &MBox_Entry.mbox_list); + RtlUpMutex(&MBox_Entry.Mutex); + + MSG_MBOX_INFO("A Mailbox Created: Size=%d\n", MboxSize); + + return pMBox; +} + +/****************************************************************************** + * Function: RtlMailboxDel + * Desc: To delete a mailbox by a given mailbox handle. + * Para: + * MboxHdl: The handle of the mailbox to be deleted. + * Return: None. + ******************************************************************************/ +VOID RtlMailboxDel( + IN PRTL_MAILBOX MboxHdl +) +{ + if (NULL == MboxHdl) { + MSG_MBOX_ERR("RtlMailboxDel: Try to delete a NULL mailbox\n"); + return; + } + + // Remove this mailbox from the link list of created mailbox + RtlDownMutex(&MBox_Entry.Mutex); + RtlListDelete(&MboxHdl->mbox_list); + RtlUpMutex(&MBox_Entry.Mutex); + + // delete the Queue/Mailbox +#ifdef PLATFORM_FREERTOS + vQueueDelete((xQueueHandle)(MboxHdl->mbox_hdl)); +#endif +#ifdef PLATFORM_ECOS + // TODO: Delete mailbox +#endif + + RtlMfree((void *)MboxHdl, sizeof(RTL_MAILBOX)); +} + +/****************************************************************************** + * Function: RtlMailboxSendToBack + * Desc: To put a message block to the tail of a given mailbox. + * Para: + * MboxID: The identifier of the target mailbox. + * pMsg: The pointer of the message block to be put into the mailbox. + * MSToWait: If the mailbox is full, this value gives a time to wait to put + * this message. The time unit is millisecond. + * The special values are: + * 0: no waiting; + * 0xffffffff: wait without timeout. + * If the waiting is timeout, the message sending is failed and + * return _FAIL. + * IsFromISR: Is this function is called from an ISR ? + * Return: _SUCCESS or _FAIL. + ******************************************************************************/ +u8 RtlMailboxSendToBack( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +) +{ + RTL_MAILBOX *pMbox=NULL; + u32 wait_ticks; +#ifdef PLATFORM_FREERTOS + portBASE_TYPE ret; +#endif + + pMbox = RtlMBoxIdToHdl(MboxID); + + if (NULL == pMbox) { + MSG_MBOX_ERR("RtlMailboxSendToBack: Didn't find matched MBoxID=%d\n", MboxID); + return _FAIL; + } + +#ifdef PLATFORM_FREERTOS + if (MBOX_WAIT_NO_TIMEOUT == MSToWait) { + wait_ticks = portMAX_DELAY; + } + else if (MBOX_WAIT_NONE == MSToWait) { + wait_ticks = 0; + } + else { + wait_ticks = ((MSToWait/portTICK_RATE_MS)>0)?(MSToWait/portTICK_RATE_MS):(1); + } + + if (IsFromISR) { + ret = xQueueSendToBackFromISR(pMbox->mbox_hdl, (void *)pMsg, NULL);//(portTickType) wait_ticks); + } + else { + ret = xQueueSendToBack(pMbox->mbox_hdl, (void *)pMsg, (portTickType) wait_ticks); + } + + if(ret != pdPASS ) { + // send message to the queue failed + MSG_MBOX_ERR("RtlMailboxSendToBack: Put Msg to Queue Failed, MBoxID=%d\n", MboxID); + ret = _FAIL; + } + else { + // try to give a semaphore to wake up the receiving task + if (pMbox->pWakeSema) { + RtlUpSema(pMbox->pWakeSema); + } + ret = _SUCCESS; + } + + return ret; +#endif + +#ifdef PLATFORM_ECOS + // TODO: Put the message to a mailbox +#endif + +} + + +/****************************************************************************** + * Function: RtlMailboxSendToFront + * Desc: To put a message block to the head of a mailbox. + * Para: + * MboxID: The identifier of the target mailbox. + * pMsg: The pointer of the message block to be put into the mailbox. + * MSToWait: If the mailbox is full, this value gives a time to wait to put + * this message. The time unit is millisecond. + * The special values are: + * 0: no waiting; + * 0xffffffff: wait without timeout. + * If the waiting is timeout, the message sending is failed and + * return _FAIL. + * IsFromISR: Is this function is called from an ISR ? + * Return: _SUCCESS or _FAIL. + ******************************************************************************/ +u8 RtlMailboxSendToFront( + IN u8 MboxID, + IN MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +) +{ + RTL_MAILBOX *pMbox=NULL; + u32 wait_ticks; +#ifdef PLATFORM_FREERTOS + portBASE_TYPE ret; +#endif + + pMbox = RtlMBoxIdToHdl(MboxID); + + if (NULL == pMbox) { + MSG_MBOX_ERR("RtlMailboxSendToBack: Didn't find matched MBoxID=%d\n", MboxID); + return _FAIL; + } + +#ifdef PLATFORM_FREERTOS + if (MBOX_WAIT_NO_TIMEOUT == MSToWait) { + wait_ticks = portMAX_DELAY; + } + else if (MBOX_WAIT_NONE == MSToWait) { + wait_ticks = 0; + } + else { + wait_ticks = ((MSToWait/portTICK_RATE_MS)>0)?(MSToWait/portTICK_RATE_MS):(1); + } + + if (IsFromISR) { + ret = xQueueSendToFrontFromISR(pMbox->mbox_hdl, (void *)pMsg, NULL);//(portTickType) wait_ticks); + } + else { + ret = xQueueSendToFront(pMbox->mbox_hdl, (void *)pMsg, (portTickType) wait_ticks); + } + + if(ret != pdPASS ) { + // send message to the queue failed + MSG_MBOX_ERR("RtlMailboxSendToBack: Put Msg to Queue Failed, MBoxID=%d\n", MboxID); + ret = _FAIL; + } + else { + // try to give a semaphore to wake up the receiving task + if (pMbox->pWakeSema) { + RtlUpSema(pMbox->pWakeSema); + } + ret = _SUCCESS; + } + + return ret; +#endif + +#ifdef PLATFORM_ECOS + // TODO: eCos has no API to put message to the head of a mailbox +#endif + +} + +/****************************************************************************** + * Function: RtlMailboxSendToFront + * Desc: To get a message block from a given mailbox. + * Para: + * MboxID: The identifier of the target mailbox. + * pMsg: The message block to store the gotten message. + * MSToWait: If the mailbox is full, this value gives a time to wait to put + * this message. The time unit is millisecond. + * The special values are: + * 0: no waiting; + * 0xffffffff: wait without timeout. + * If the waiting is timeout, the message sending is failed and + * return _FAIL. + * IsFromISR: Is this function is called from an ISR ? + * Return: _SUCCESS or _FAIL. + ******************************************************************************/ +u8 RtlMailboxReceive( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +) +{ + RTL_MAILBOX *pMbox=NULL; + u32 wait_ticks; +#ifdef PLATFORM_FREERTOS + portBASE_TYPE ret; +#endif + + pMbox = RtlMBoxIdToHdl(MboxID); + + if (NULL == pMbox) { + MSG_MBOX_ERR("RtlMailboxReceive: Didn't find the MBox with ID=%d\n", MboxID); + return _FAIL; + } + +#ifdef PLATFORM_FREERTOS + if (MBOX_WAIT_NONE == MSToWait) { + wait_ticks = 0; + } + else if (MBOX_WAIT_NO_TIMEOUT == MSToWait) { + wait_ticks = portMAX_DELAY; + } + else { + wait_ticks = ((MSToWait/portTICK_RATE_MS)>0)?(MSToWait/portTICK_RATE_MS):(1); + } + + if (IsFromISR) { + ret = xQueueReceiveFromISR(pMbox->mbox_hdl, (void *)pMsg, NULL);//( portTickType ) wait_ticks); + } + else { + ret = xQueueReceive(pMbox->mbox_hdl, (void *)pMsg, ( portTickType ) wait_ticks); + + } + + if(ret != pdTRUE ) { + // receive message failed + if (0 != MSToWait) { + MSG_MBOX_ERR("RtlMailboxReceive: Receive Msg Failed, MBoxID=%d\n", MboxID); + } + ret = _FAIL; + } + else { + ret = _SUCCESS; + } + + return ret; +#endif + +#ifdef PLATFORM_ECOS + // TODO: Get a message from the mailbox +#endif + +} + +/****************************************************************************** + * Function: RtlMailboxPeek + * Desc: To copy the head message from a given mailbox without move this + * message block out from the mailbox. + * Para: + * MboxID: The identifier of the target mailbox. + * pMsg: The message block to store the gotten message. + * MSToWait: If the mailbox is full, this value gives a time to wait to put + * this message. The time unit is millisecond. + * The special values are: + * 0: no waiting; + * 0xffffffff: wait without timeout. + * If the waiting is timeout, the message sending is failed and + * return _FAIL. + * IsFromISR: Is this function is called from an ISR ? + * Return: _SUCCESS or _FAIL. + ******************************************************************************/ +u8 RtlMailboxPeek( + IN u8 MboxID, + OUT MSG_BLK *pMsg, + IN u32 MSToWait, + IN u8 IsFromISR +) +{ + RTL_MAILBOX *pMbox=NULL; + u32 wait_ticks; +#ifdef PLATFORM_FREERTOS + portBASE_TYPE ret; +#endif + + pMbox = RtlMBoxIdToHdl(MboxID); + + if (NULL == pMbox) { + MSG_MBOX_ERR("RtlMailboxPeek: Didn't find the MBox with ID=%d\n", MboxID); + return _FAIL; + } + +#ifdef PLATFORM_FREERTOS + if (MBOX_WAIT_NONE == MSToWait) { + wait_ticks = 0; + } + else if (MBOX_WAIT_NO_TIMEOUT == MSToWait) { + wait_ticks = portMAX_DELAY; + } + else { + wait_ticks = ((MSToWait/portTICK_RATE_MS)>0)?(MSToWait/portTICK_RATE_MS):(1); + } + + if (IsFromISR) { +// ret = xQueuePeekFromISR(pMbox->mbox_hdl, (void *)pMsg, ( portTickType ) wait_ticks); + // TODO: check why we have no "xQueuePeekFromISR" + MSG_MBOX_ERR("RtlMailboxPeek: Current version has no 'xQueuePeekFromISR'\n"); + ret = pdFALSE; + } + else { + ret = xQueuePeek(pMbox->mbox_hdl, (void *)pMsg, ( portTickType ) wait_ticks); + + } + + if(ret != pdTRUE ) { + // receive message failed + MSG_MBOX_ERR("RtlMailboxReceive: Receive Msg Failed, MBoxID=%d\n", MboxID); + ret = _FAIL; + } + else { + ret = _SUCCESS; + } + + return ret; +#endif + +#ifdef PLATFORM_ECOS + // TODO: Get a message from the mailbox +#endif + +} + + +/****************************************************************************** + * Function: RtlMailboxMsgWaiting + * Desc: To get the number of message blocks are storing in a given mailbox. + * Para: + * MboxID: The identifier of the target mailbox. + * IsFromISR: Is this function is called from an ISR ? + * Return: The number of message blocks are storing in this mailbox. + ******************************************************************************/ +u32 RtlMailboxMsgWaiting( + IN u8 MboxID, + IN u8 IsFromISR +) +{ + RTL_MAILBOX *pMbox=NULL; + u32 msg_num=0; + + pMbox = RtlMBoxIdToHdl(MboxID); + + if (NULL == pMbox) { + MSG_MBOX_ERR("RtlMailboxMsgWaiting: Didn't find the MBox with ID=%d\n", MboxID); + return 0; + } + +#ifdef PLATFORM_FREERTOS + if (IsFromISR) { + msg_num = uxQueueMessagesWaitingFromISR(pMbox->mbox_hdl); + } + else { + msg_num = uxQueueMessagesWaiting(pMbox->mbox_hdl); + } +#endif + +#ifdef PLATFORM_ECOS + // TODO: call eCos API to implement this function +#endif + + return msg_num; + +} + diff --git a/component/os/os_dep/osdep_api.c b/component/os/os_dep/osdep_api.c new file mode 100644 index 0000000..334ee51 --- /dev/null +++ b/component/os/os_dep/osdep_api.c @@ -0,0 +1,836 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ + + +#define _OSDEP_API_C_ + +#include + + + + + + +u8* +RtlMalloc( + IN u32 sz +) +{ + u8 *pbuf=NULL; +#ifndef PLATFORM_FREERTOS + u32 v32=0; +#endif + +#ifdef PLATFORM_FREERTOS + SaveAndCli( ); +#else + SaveAndCli(v32); +#endif + + pbuf = RtlKmalloc(sz, GFP_ATOMIC); + +#ifdef PLATFORM_FREERTOS + RestoreFlags( ); +#else + RestoreFlags(v32); +#endif + + return pbuf; + +} + + +u8* +RtlZmalloc( + IN u32 sz +) +{ +#ifdef PLATFORM_FREERTOS + u8 *pbuf; + + pbuf= RtlMalloc(sz); + + if (pbuf != NULL) { + _memset(pbuf, 0, sz); + } + + return pbuf; +#else + u8 *pbuf; + + pbuf= RtlMalloc(sz); + + if (pbuf != NULL) { + _memset(pbuf, 0, sz); + } + + return pbuf; +#endif +} + +VOID +RtlMfree( + IN u8 *pbuf, + IN u32 sz +) +{ + RtlKfree(pbuf); +} + + +VOID* +RtlMalloc2d( + IN u32 h, + IN u32 w, + IN u32 size +) +{ + u32 j; + + VOID **a = (VOID **) RtlZmalloc( h*sizeof(VOID *) + h*w*size ); + if(a == NULL) + { + DBG_ERROR_LOG("%s: alloc memory fail!\n", __FUNCTION__); + return NULL; + } + + for( j=0; jQueue)); + + RtlSpinlockInit(&(pqueue->Lock)); + +} + +u32 +RtlQueueEmpty( + IN _QUEUE *pqueue +) +{ + return (RtlIsListEmpty(&(pqueue->Queue))); +} + + +u32 +RtlendOfQueueSearch( + IN _LIST *head, + IN _LIST *plist) +{ + if (head == plist) + return _TRUE; + else + return _FALSE; +} +#endif + +u32 +RtlGetCurrentTime(VOID) +{ + return JIFFIES; +} + + + +VOID +RtlSleepSchedulable( + IN u32 ms +) +{ + +#ifdef PLATFORM_LINUX + + u32 delta; + + delta = (ms * HZ)/1000;//(ms) + if (delta == 0) { + delta = 1;// 1 ms + } + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(delta) != 0) { + return ; + } + return; + +#endif +#ifdef PLATFORM_FREEBSD + DELAY(ms*1000); + return ; +#endif + +#ifdef PLATFORM_WINDOWS + + NdisMSleep(ms*1000); //(us)*1000=(ms) + +#endif + +} + + + +VOID +RtlMsleepOS( + IN u32 ms +) +{ +#ifdef PLATFORM_FREERTOS + u32 Dealycount = ms/portTICK_RATE_MS; + if (Dealycount > 0) { + vTaskDelay(Dealycount); + } + else { + vTaskDelay(1); + } + +#endif +} + + +VOID +RtlUsleepOS( + IN u32 us +) +{ +#ifdef PLATFORM_FREERTOS + u32 Dealycount = us/portTICK_RATE_MS*1000; + if (Dealycount > 0) { + vTaskDelay(Dealycount); + } + else { + vTaskDelay(1); + } +#endif +} + + +VOID +RtlMdelayOS( + IN u32 ms +) +{ + Mdelay((unsigned long)ms); +} + +VOID +RtlUdelayOS( + IN u32 us +) +{ + Udelay((unsigned long)us); +} + + +VOID +RtlYieldOS(VOID) +{ +} + + +#if defined(__ICCARM__) +u64 +RtlModular64( + IN u64 n, + IN u64 base +) +{ + unsigned int __base = (base); + unsigned int __rem; + //(void)(((typeof((n)) *)0) == ((__uint64_t *)0)); + if (((n) >> 32) == 0) { + __rem = (unsigned int)(n) % __base; + (n) = (unsigned int)(n) / __base; + } else + __rem = __Div64_32(&(n), __base); + return __rem; + +} +#else +u64 +RtlModular64( + IN u64 x, + IN u64 y +) +{ + return DO_DIV(x, y); +} +#endif + +/****************************************************************************** + * Function: RtlTimerCallbckEntry + * Desc: This function is a timer callback wrapper. All OS timer callback + * will call this function and then call the real callback function inside + * this function. + * + * Para: + * pxTimer: The FreeRTOS timer handle which is expired and call this callback. + * + * Return: None + * + ******************************************************************************/ +#ifdef PLATFORM_FREERTOS +void +RtlTimerCallbckEntry ( + IN xTimerHandle pxTimer +) +{ + PRTL_TIMER pTimer; + + if (NULL == pxTimer) { + MSG_TIMER_ERR("RtlTimerCallbckEntry: NULL Timer Handle Err!\n"); + return; + } + + pTimer = (PRTL_TIMER) pvTimerGetTimerID( pxTimer ); + pTimer->CallBackFunc(pTimer->Context); +} +#endif // end of "#ifdef PLATFORM_FREERTOS" + +/****************************************************************************** + * Function: RtlTimerCreate + * Desc: To create a software timer. + * + * Para: + * pTimerName: A string for the timer name. + * TimerPeriodMS: The timer period, the unit is milli-second. + * CallbckFunc: The callback function of this timer. + * pContext: A pointer will be used as the parameter to call the timer + * callback function. + * isPeriodical: Is this timer periodical ? (Auto reload after expired) + * Return: The created timer handle, a pointer. It can be used to delete the + * timer. If timer createion failed, return NULL. + * + ******************************************************************************/ +PRTL_TIMER +RtlTimerCreate( + IN char *pTimerName, + IN u32 TimerPeriodMS, + IN RTL_TIMER_CALL_BACK CallbckFunc, + IN void *pContext, + IN u8 isPeriodical +) +{ + PRTL_TIMER pTimer; + u32 timer_ticks; + int i; + + pTimer = (PRTL_TIMER)RtlZmalloc(sizeof(RTL_TIMER)); + if (NULL == pTimer) { + MSG_TIMER_ERR("RtlTimerCreate: Alloc Mem Err!\n"); + return NULL; + } + + if (portTICK_RATE_MS >= TimerPeriodMS) { + timer_ticks = 1; // at least 1 system tick + } + else { + timer_ticks = TimerPeriodMS/portTICK_RATE_MS; + } + +#ifdef PLATFORM_FREERTOS + pTimer->TimerHandle = xTimerCreate (pTimer->TimerName, timer_ticks, + (portBASE_TYPE)isPeriodical, (void *) pTimer, RtlTimerCallbckEntry); +#endif +#ifdef PLATFORM_ECOS +// TODO: create a timer +#endif + +#ifdef PLATFORM_FREERTOS // if any RTOS is used + if (pTimer->TimerHandle) { + pTimer->msPeriod = TimerPeriodMS; + pTimer->CallBackFunc = CallbckFunc; + pTimer->Context = pContext; + pTimer->isPeriodical = isPeriodical; + // copy the timer name + if (NULL != pTimerName) { + for(i = 0; i < sizeof(pTimer->TimerName); i++) + { + pTimer->TimerName[i] = pTimerName[i]; + if(pTimerName[i] == '\0') + { + break; + } + } + } + else { + _strcpy(pTimer->TimerName, "None"); + } + } + else +#endif + { + RtlMfree((u8 *)pTimer, sizeof(RTL_TIMER)); + pTimer = NULL; + MSG_TIMER_ERR("RtlTimerCreate: OS Create Timer Failed!\n"); + } + + MSG_TIMER_INFO("RtlTimerCreate: SW Timer Created: Name=%s Period=%d isPeriodical=%d\n", \ + pTimer->TimerName, pTimer->msPeriod, pTimer->isPeriodical); + + return (pTimer); +} + +/****************************************************************************** + * Function: RtlTimerDelete + * Desc: To delete a created software timer. + * + * Para: + * pTimerHdl: The timer to be deleted + * + * Return: None + * + ******************************************************************************/ +VOID +RtlTimerDelete( + IN PRTL_TIMER pTimerHdl +) +{ +#ifdef PLATFORM_FREERTOS + portBASE_TYPE ret; +#endif + + MSG_TIMER_INFO("RtlTimerDelete: Name=%s\n", pTimerHdl->TimerName); + + if (NULL == pTimerHdl) { + MSG_TIMER_ERR("RtlTimerDelete: NULL Timer Handle!\n"); + } + +#ifdef PLATFORM_FREERTOS + /* try to delete the soft timer and wait max RTL_TIMER_API_MAX_BLOCK_TICKS + to send the delete command to the timer command queue */ + ret = xTimerDelete(pTimerHdl->TimerHandle, RTL_TIMER_API_MAX_BLOCK_TICKS); + if (pdPASS != ret) { + MSG_TIMER_ERR("RtlTimerDelete: Delete OS Timer Failed!\n"); + } +#endif + +#ifdef PLATFORM_ECOS + // TODO: call OS delete timer +#endif + RtlMfree((u8 *)pTimerHdl, sizeof(RTL_TIMER)); + +} + +/****************************************************************************** + * Function: RtlTimerStart + * Desc: To start a created timer.. + * + * Para: + * pTimerHdl: The timer to be started. + * isFromISR: The flag to indicate that is this function is called from an ISR. + * + * Return: _SUCCESS or _FAIL + * + ******************************************************************************/ +u8 +RtlTimerStart( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +) +{ +#ifdef PLATFORM_FREERTOS + u8 ret=_FAIL; + portBASE_TYPE HigherPriorityTaskWoken=pdFALSE; + + if (isFromISR) { + if (pdPASS == xTimerStartFromISR(pTimerHdl->TimerHandle,&HigherPriorityTaskWoken)) + { + // start OS timer successful + if (pdFALSE != HigherPriorityTaskWoken) { + taskYIELD(); + } + ret = _SUCCESS; + } + else { + MSG_TIMER_ERR("RtlTimerStart: Start Timer(%s) from ISR failed\n", pTimerHdl->TimerName); + } + } + else { + if (pdPASS == xTimerStart(pTimerHdl->TimerHandle, RTL_TIMER_API_MAX_BLOCK_TICKS)) { + ret = _SUCCESS; + } + else { + MSG_TIMER_ERR("RtlTimerStart: Start Timer(%s) failed\n", pTimerHdl->TimerName); + } + } + + MSG_TIMER_INFO("RtlTimerStart: SW Timer %s Started\n", pTimerHdl->TimerName); + + return ret; +#endif +} + +/****************************************************************************** + * Function: RtlTimerStop + * Desc: To stop a running timer.. + * + * Para: + * pTimerHdl: The timer to be stoped. + * isFromISR: The flag to indicate that is this function is called from an ISR. + * + * Return: _SUCCESS or _FAIL + * + ******************************************************************************/ +u8 +RtlTimerStop( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +) +{ +#ifdef PLATFORM_FREERTOS + u8 ret=_FAIL; + portBASE_TYPE HigherPriorityTaskWoken=pdFALSE; + + if (isFromISR) { + if (pdPASS == xTimerStopFromISR(pTimerHdl->TimerHandle,&HigherPriorityTaskWoken)) + { + // start OS timer successful + if (pdFALSE != HigherPriorityTaskWoken) { + taskYIELD(); + } + ret = _SUCCESS; + } + } + else { + if (pdPASS == xTimerStop(pTimerHdl->TimerHandle, RTL_TIMER_API_MAX_BLOCK_TICKS)) { + ret = _SUCCESS; + } + } + + if (_FAIL == ret) { + MSG_TIMER_ERR("RtlTimerStop: Stop Timer(%s) Failed, IsFromISR=%d\n", pTimerHdl->TimerName, isFromISR); + } + + MSG_TIMER_INFO("RtlTimerStop: SW Timer %s Stoped\n", pTimerHdl->TimerName); + + return ret; +#endif +} + +/****************************************************************************** + * Function: RtlTimerReset + * Desc: To reset a timer. A reset will get a re-start and reset + * the timer ticks counting. A running timer expired time is relative + * to the time when Reset function be called. Please ensure the timer + * is in active state (Started). A stopped timer also will be started + * when this function is called. + * + * Para: + * pTimerHdl: The timer to be reset. + * isFromISR: The flag to indicate that is this function is called from an ISR. + * + * Return: _SUCCESS or _FAIL + * + ******************************************************************************/ +u8 +RtlTimerReset( + IN PRTL_TIMER pTimerHdl, + IN u8 isFromISR +) +{ +#ifdef PLATFORM_FREERTOS + u8 ret=_FAIL; + portBASE_TYPE HigherPriorityTaskWoken=pdFALSE; + + if (isFromISR) { + if (pdPASS == xTimerResetFromISR(pTimerHdl->TimerHandle,&HigherPriorityTaskWoken)) + { + // start OS timer successful + if (pdFALSE != HigherPriorityTaskWoken) { + taskYIELD(); + } + ret = _SUCCESS; + } + } + else { + if (pdPASS == xTimerReset(pTimerHdl->TimerHandle, RTL_TIMER_API_MAX_BLOCK_TICKS)) { + ret = _SUCCESS; + } + } + + if (_FAIL == ret) { + MSG_TIMER_ERR("RtlTimerReset: Reset Timer(%s) Failed, IsFromISR=%d\n", pTimerHdl->TimerName, isFromISR); + } + + MSG_TIMER_INFO("RtlTimerReset: SW Timer %s Reset\n", pTimerHdl->TimerName); + + return ret; +#endif +} + +/****************************************************************************** + * Function: RtlTimerChangePeriod + * Desc: To change the period of a timer that was created previously. + * + * Para: + * pTimerHdl: The timer handle to be changed the priod. + * NewPeriodMS: The new timer period, in milli-second. + * isFromISR: The flag to indicate that is this function is called from an ISR. + * + * Return: _SUCCESS or _FAIL + * + ******************************************************************************/ +u8 +RtlTimerChangePeriod( + IN PRTL_TIMER pTimerHdl, + IN u32 NewPeriodMS, + IN u8 isFromISR +) +{ +#ifdef PLATFORM_FREERTOS + u32 timer_ticks; + u8 ret=_FAIL; + portBASE_TYPE HigherPriorityTaskWoken=pdFALSE; + + if (portTICK_RATE_MS >= NewPeriodMS) { + timer_ticks = 1; // at least 1 system tick + } + else { + timer_ticks = NewPeriodMS/portTICK_RATE_MS; + } + + if (isFromISR) { + if (pdPASS == xTimerChangePeriodFromISR(pTimerHdl->TimerHandle, timer_ticks, &HigherPriorityTaskWoken)) + { + // start OS timer successful + if (pdFALSE != HigherPriorityTaskWoken) { + taskYIELD(); + } + ret = _SUCCESS; + } + } + else { + if (pdPASS == xTimerChangePeriod(pTimerHdl->TimerHandle, timer_ticks, RTL_TIMER_API_MAX_BLOCK_TICKS)) { + ret = _SUCCESS; + } + } + + if (_FAIL == ret) { + MSG_TIMER_ERR("RtlTimerChangePeriod: Change Timer(%s) Period Failed, IsFromISR=%d\n", pTimerHdl->TimerName, isFromISR); + } + else { + pTimerHdl->msPeriod = NewPeriodMS; + MSG_TIMER_INFO("RtlTimerChangePeriod: SW Timer %s change period to %d\n", pTimerHdl->TimerName, pTimerHdl->msPeriod); + } + + + return ret; +#endif +} + diff --git a/component/soc/realtek/8195a/cmsis/core_cm3.h b/component/soc/realtek/8195a/cmsis/core_cm3.h new file mode 100644 index 0000000..64db5c7 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/core_cm3.h @@ -0,0 +1,1661 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V3.20 + * @date 25. February 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + 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 ARM 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 COPYRIGHT HOLDERS AND 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 defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** \ingroup Cortex_M3 + @{ + */ + +/* CMSIS CM3 definitions */ +#define __CM3_CMSIS_VERSION_MAIN (0x03) /*!< [31:16] CMSIS HAL main version */ +#define __CM3_CMSIS_VERSION_SUB (0x20) /*!< [15:0] CMSIS HAL sub version */ +#define __CM3_CMSIS_VERSION ((__CM3_CMSIS_VERSION_MAIN << 16) | \ + __CM3_CMSIS_VERSION_SUB ) /*!< CMSIS HAL version number */ + +#define __CORTEX_M (0x03) /*!< Cortex-M Core */ + + + +#if defined ( __CC_ARM ) + #define __ASM __asm /*!< asm keyword for ARM Compiler */ + #define __INLINE __inline /*!< inline keyword for ARM Compiler */ + #define __STATIC_INLINE static __inline + +#elif defined ( __ICCARM__ ) + #define __ASM __asm /*!< asm keyword for IAR Compiler */ + #define __INLINE inline /*!< inline keyword for IAR Compiler. Only available in High optimization mode! */ + #define __STATIC_INLINE static inline + +#elif defined ( __TMS470__ ) + #define __ASM __asm /*!< asm keyword for TI CCS Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __GNUC__ ) + #define __ASM __asm /*!< asm keyword for GNU Compiler */ + #define __INLINE inline /*!< inline keyword for GNU Compiler */ + #define __STATIC_INLINE static inline + +#elif defined ( __TASKING__ ) + #define __ASM __asm /*!< asm keyword for TASKING Compiler */ + #define __INLINE inline /*!< inline keyword for TASKING Compiler */ + #define __STATIC_INLINE static inline + +#endif + +/** __FPU_USED indicates whether an FPU is used or not. This core does not support an FPU at all +*/ +#define __FPU_USED 0 + +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TMS470__ ) + #if defined __TI__VFP_SUPPORT____ + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #endif +#endif + +#include /* standard types definitions */ +//#include /* rlx basic types definitions */ +#include /* Core Instruction Access */ +#include /* Core Function Access */ +//#include "hal_irqn.h" + +#endif /* __CORE_CM3_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM3_H_DEPENDANT +#define __CORE_CM3_H_DEPENDANT + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM3_REV + #define __CM3_REV 0x0200 + #warning "__CM3_REV not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0 + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 4 + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0 + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/*@} end of group Cortex_M3 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + ******************************************************************************/ +/** \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:27; /*!< bit: 0..26 Reserved */ +#else + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ +#endif + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + + +/** \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + + +/** \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ +#if (__CORTEX_M != 0x04) + uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ +#else + uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ +#endif + uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ + uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + + +/** \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/*@} end of group CMSIS_CORE */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24]; + __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RSERVED1[24]; + __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24]; + __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24]; + __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56]; + __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644]; + __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0 /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL << NVIC_STIR_INTID_Pos) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5]; + __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24 /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20 /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16 /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4 /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0 /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL << SCB_CPUID_REVISION_Pos) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31 /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28 /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27 /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26 /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25 /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23 /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22 /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12 /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11 /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0 /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL << SCB_ICSR_VECTACTIVE_Pos) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#if (__CM3_REV < 0x0201) /* core r2p1 */ +#define SCB_VTOR_TBLBASE_Pos 29 /*!< SCB VTOR: TBLBASE Position */ +#define SCB_VTOR_TBLBASE_Msk (1UL << SCB_VTOR_TBLBASE_Pos) /*!< SCB VTOR: TBLBASE Mask */ + +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x3FFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#else +#define SCB_VTOR_TBLOFF_Pos 7 /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ +#endif + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16 /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16 /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15 /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8 /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2 /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1 /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0 /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL << SCB_AIRCR_VECTRESET_Pos) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4 /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2 /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1 /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9 /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8 /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4 /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3 /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1 /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0 /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL << SCB_CCR_NONBASETHRDENA_Pos) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18 /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17 /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16 /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15 /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14 /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13 /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12 /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11 /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10 /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8 /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7 /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3 /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1 /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0 /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL << SCB_SHCSR_MEMFAULTACT_Pos) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Registers Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16 /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8 /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0 /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL << SCB_CFSR_MEMFAULTSR_Pos) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* SCB Hard Fault Status Registers Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31 /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30 /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1 /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4 /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3 /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2 /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1 /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0 /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL << SCB_DFSR_HALTED_Pos) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1]; + __I uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ +#if ((defined __CM3_REV) && (__CM3_REV >= 0x200)) + __IO uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +#else + uint32_t RESERVED1[1]; +#endif +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0 /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL << SCnSCB_ICTR_INTLINESNUM_Pos) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2 /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1 /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0 /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL << SCnSCB_ACTLR_DISMCYCINT_Pos) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __O union + { + __O uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __O uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __O uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864]; + __IO uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15]; + __IO uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15]; + __IO uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[29]; + __O uint32_t IWR; /*!< Offset: 0xEF8 ( /W) ITM Integration Write Register */ + __I uint32_t IRR; /*!< Offset: 0xEFC (R/ ) ITM Integration Read Register */ + __IO uint32_t IMCR; /*!< Offset: 0xF00 (R/W) ITM Integration Mode Control Register */ + uint32_t RESERVED4[43]; + __O uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __I uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6]; + __I uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __I uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __I uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __I uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __I uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __I uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __I uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __I uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __I uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __I uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __I uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __I uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0 /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFUL << ITM_TPR_PRIVMASK_Pos) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23 /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TraceBusID_Pos 16 /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TraceBusID_Msk (0x7FUL << ITM_TCR_TraceBusID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10 /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPrescale_Pos 8 /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPrescale_Msk (3UL << ITM_TCR_TSPrescale_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4 /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3 /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2 /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1 /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0 /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL << ITM_TCR_ITMENA_Pos) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Integration Write Register Definitions */ +#define ITM_IWR_ATVALIDM_Pos 0 /*!< ITM IWR: ATVALIDM Position */ +#define ITM_IWR_ATVALIDM_Msk (1UL << ITM_IWR_ATVALIDM_Pos) /*!< ITM IWR: ATVALIDM Mask */ + +/* ITM Integration Read Register Definitions */ +#define ITM_IRR_ATREADYM_Pos 0 /*!< ITM IRR: ATREADYM Position */ +#define ITM_IRR_ATREADYM_Msk (1UL << ITM_IRR_ATREADYM_Pos) /*!< ITM IRR: ATREADYM Mask */ + +/* ITM Integration Mode Control Register Definitions */ +#define ITM_IMCR_INTEGRATION_Pos 0 /*!< ITM IMCR: INTEGRATION Position */ +#define ITM_IMCR_INTEGRATION_Msk (1UL << ITM_IMCR_INTEGRATION_Pos) /*!< ITM IMCR: INTEGRATION Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos 2 /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_ByteAcc_Msk (1UL << ITM_LSR_ByteAcc_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_Access_Pos 1 /*!< ITM LSR: Access Position */ +#define ITM_LSR_Access_Msk (1UL << ITM_LSR_Access_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_Present_Pos 0 /*!< ITM LSR: Present Position */ +#define ITM_LSR_Present_Msk (1UL << ITM_LSR_Present_Pos) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IO uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IO uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IO uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IO uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IO uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IO uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __I uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IO uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IO uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IO uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1]; + __IO uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IO uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IO uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1]; + __IO uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IO uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IO uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1]; + __IO uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IO uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IO uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28 /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27 /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26 /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25 /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24 /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22 /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21 /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20 /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19 /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18 /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17 /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16 /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12 /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10 /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9 /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5 /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1 /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0 /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL << DWT_CTRL_CYCCNTENA_Pos) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0 /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL << DWT_CPICNT_CPICNT_Pos) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0 /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL << DWT_EXCCNT_EXCCNT_Pos) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0 /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL << DWT_SLEEPCNT_SLEEPCNT_Pos) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0 /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL << DWT_LSUCNT_LSUCNT_Pos) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0 /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL << DWT_FOLDCNT_FOLDCNT_Pos) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0 /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL << DWT_MASK_MASK_Pos) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24 /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16 /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12 /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10 /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9 /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8 /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7 /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5 /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0 /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL << DWT_FUNCTION_FUNCTION_Pos) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IO uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IO uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2]; + __IO uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55]; + __IO uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131]; + __I uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IO uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __I uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759]; + __I uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER */ + __I uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __I uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1]; + __I uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __I uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IO uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39]; + __IO uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IO uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8]; + __I uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __I uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0 /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL << TPI_ACPR_PRESCALER_Pos) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0 /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL << TPI_SPPR_TXMODE_Pos) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3 /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2 /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1 /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0 /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL << TPI_FFSR_FlInProg_Pos) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8 /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1 /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0 /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL << TPI_TRIGGER_TRIGGER_Pos) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29 /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x3UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27 /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26 /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x3UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24 /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16 /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8 /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0 /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL << TPI_FIFO0_ETM0_Pos) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY_Pos 0 /*!< TPI ITATBCTR2: ATREADY Position */ +#define TPI_ITATBCTR2_ATREADY_Msk (0x1UL << TPI_ITATBCTR2_ATREADY_Pos) /*!< TPI ITATBCTR2: ATREADY Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29 /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x3UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27 /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26 /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x3UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24 /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16 /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8 /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0 /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL << TPI_FIFO1_ITM0_Pos) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY_Pos 0 /*!< TPI ITATBCTR0: ATREADY Position */ +#define TPI_ITATBCTR0_ATREADY_Msk (0x1UL << TPI_ITATBCTR0_ATREADY_Pos) /*!< TPI ITATBCTR0: ATREADY Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0 /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x1UL << TPI_ITCTRL_Mode_Pos) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11 /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10 /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9 /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6 /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5 /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0 /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL << TPI_DEVID_NrTraceInput_Pos) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 0 /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL << TPI_DEVTYPE_SubType_Pos) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 4 /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if (__MPU_PRESENT == 1) +/** \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __I uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IO uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IO uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IO uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IO uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IO uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IO uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IO uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IO uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IO uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IO uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +/* MPU Type Register */ +#define MPU_TYPE_IREGION_Pos 16 /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8 /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0 /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL << MPU_TYPE_SEPARATE_Pos) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register */ +#define MPU_CTRL_PRIVDEFENA_Pos 2 /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1 /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0 /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL << MPU_CTRL_ENABLE_Pos) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register */ +#define MPU_RNR_REGION_Pos 0 /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL << MPU_RNR_REGION_Pos) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register */ +#define MPU_RBAR_ADDR_Pos 5 /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4 /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0 /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL << MPU_RBAR_REGION_Pos) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register */ +#define MPU_RASR_ATTRS_Pos 16 /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28 /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24 /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19 /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18 /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17 /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16 /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8 /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1 /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0 /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL << MPU_RASR_ENABLE_Pos) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IO uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __O uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IO uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IO uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16 /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25 /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24 /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19 /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18 /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17 /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16 /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5 /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3 /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2 /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1 /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0 /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL << CoreDebug_DHCSR_C_DEBUGEN_Pos) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register */ +#define CoreDebug_DCRSR_REGWnR_Pos 16 /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0 /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL << CoreDebug_DCRSR_REGSEL_Pos) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register */ +#define CoreDebug_DEMCR_TRCENA_Pos 24 /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19 /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18 /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17 /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16 /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10 /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9 /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8 /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7 /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6 /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5 /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4 /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0 /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL << CoreDebug_DEMCR_VC_CORERESET_Pos) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Cortex-M3 Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if (__MPU_PRESENT == 1) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +/** \brief Set Priority Grouping + + The function sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << 8)); /* Insert write key and priorty group */ + SCB->AIRCR = reg_value; +} + + +/** \brief Get Priority Grouping + + The function reads the priority grouping field from the NVIC Interrupt Controller. + + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t NVIC_GetPriorityGrouping(void) +{ + return ((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos); /* read priority grouping field */ +} + + +/** \brief Enable External Interrupt + + The function enables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn) +{ + NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */ +} + + +/** \brief Disable External Interrupt + + The function disables a device-specific interrupt in the NVIC interrupt controller. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn) +{ + NVIC->ICER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */ +} + + +/** \brief Get Pending Interrupt + + The function reads the pending register in the NVIC and returns the pending bit + for the specified interrupt. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + */ +__STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + return((uint32_t) ((NVIC->ISPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if pending else 0 */ +} + + +/** \brief Set Pending Interrupt + + The function sets the pending bit of an external interrupt. + + \param [in] IRQn Interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ISPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* set interrupt pending */ +} + + +/** \brief Clear Pending Interrupt + + The function clears the pending bit of an external interrupt. + + \param [in] IRQn External interrupt number. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + NVIC->ICPR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */ +} + + +/** \brief Get Active Interrupt + + The function reads the active register in NVIC and returns the active bit. + + \param [in] IRQn Interrupt number. + + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + */ +__STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn) +{ + return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0)); /* Return 1 if active else 0 */ +} + + +/** \brief Set Interrupt Priority + + The function sets the priority of an interrupt. + + \note The priority cannot be set for every core interrupt. + + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + */ +__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if(IRQn < 0) { + SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M System Interrupts */ + else { + NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */ +} + + +/** \brief Get Interrupt Priority + + The function reads the priority of an interrupt. The interrupt + number can be positive to specify an external (device specific) + interrupt, or negative to specify an internal (core) interrupt. + + + \param [in] IRQn Interrupt number. + \return Interrupt Priority. Value is aligned automatically to the implemented + priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn) +{ + + if(IRQn < 0) { + return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for Cortex-M system interrupts */ + else { + return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)] >> (8 - __NVIC_PRIO_BITS))); } /* get priority for device specific interrupts */ +} + + +/** \brief Encode Priority + + The function encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the samllest possible priority group is set. + + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + return ( + ((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) | + ((SubPriority & ((1 << (SubPriorityBits )) - 1))) + ); +} + + +/** \brief Decode Priority + + The function decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the samllest possible priority group is set. + + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* pPreemptPriority, uint32_t* pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp; + SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS; + + *pPreemptPriority = (Priority >> SubPriorityBits) & ((1 << (PreemptPriorityBits)) - 1); + *pSubPriority = (Priority ) & ((1 << (SubPriorityBits )) - 1); +} + + +/** \brief System Reset + + The function initiates a system reset request to reset the MCU. + */ +__STATIC_INLINE void NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + while(1); /* wait until reset */ +} + + +/** \brief Set Vector Table Offset + + + The function sets Vector Table Offset register. + + \param [in] TableAddress Vector table address. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetVectorTable(uint32_t TableAddress) +{ + SCB->VTOR = TableAddress; +} + + +/** \brief Set Vector Table Offset + + + The function sets Vector Table Offset register. + + \param [in] TableAddress Vector table address. Value cannot be negative. + */ +__STATIC_INLINE void NVIC_SetCCR(void) +{ + uint32_t reg_value; + + reg_value = SCB->CCR; + reg_value |= SCB_CCR_DIV_0_TRP_Msk | SCB_CCR_UNALIGN_TRP_Msk; + SCB->CCR = reg_value; +} + + +/*@} end of CMSIS_Core_NVICFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if (__Vendor_SysTickConfig == 0) + +/** \brief System Tick Configuration + + The function initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + + \param [in] ticks Number of ticks between two interrupts. + + \return 0 Function succeeded. + \return 1 Function failed. + + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ + + SysTick->LOAD = ticks - 1; /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY 0x5AA55AA5 /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** \brief ITM Send Character + + The function transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + + \param [in] ch Character to transmit. + + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if ((ITM->TCR & ITM_TCR_ITMENA_Msk) && /* ITM enabled */ + (ITM->TER & (1UL << 0) ) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0].u32 == 0); + ITM->PORT[0].u8 = (uint8_t) ch; + } + return (ch); +} + + +/** \brief ITM Receive Character + + The function inputs a character via the external variable \ref ITM_RxBuffer. + + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) { + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** \brief ITM Check Character + + The function checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) { + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) { + return (0); /* no character available */ + } else { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + +#endif /* __CORE_CM3_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ + +#ifdef __cplusplus +} +#endif diff --git a/component/soc/realtek/8195a/cmsis/core_cmFunc.h b/component/soc/realtek/8195a/cmsis/core_cmFunc.h new file mode 100644 index 0000000..0a18faf --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/core_cmFunc.h @@ -0,0 +1,636 @@ +/**************************************************************************//** + * @file core_cmFunc.h + * @brief CMSIS Cortex-M Core Function Access Header File + * @version V3.20 + * @date 25. February 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + 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 ARM 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 COPYRIGHT HOLDERS AND 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 __CORE_CMFUNC_H +#define __CORE_CMFUNC_H + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + +/* intrinsic void __enable_irq(); */ +/* intrinsic void __disable_irq(); */ + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__STATIC_INLINE uint32_t __get_CONTROL(void) +{ + register uint32_t __regControl __ASM("control"); + return(__regControl); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + register uint32_t __regControl __ASM("control"); + __regControl = control; +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__STATIC_INLINE uint32_t __get_IPSR(void) +{ + register uint32_t __regIPSR __ASM("ipsr"); + return(__regIPSR); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__STATIC_INLINE uint32_t __get_APSR(void) +{ + register uint32_t __regAPSR __ASM("apsr"); + return(__regAPSR); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__STATIC_INLINE uint32_t __get_xPSR(void) +{ + register uint32_t __regXPSR __ASM("xpsr"); + return(__regXPSR); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + return(__regProcessStackPointer); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + __regProcessStackPointer = topOfProcStack; +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + return(__regMainStackPointer); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + __regMainStackPointer = topOfMainStack; +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + register uint32_t __regPriMask __ASM("primask"); + return(__regPriMask); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + register uint32_t __regPriMask __ASM("primask"); + __regPriMask = (priMask); +} + + +#if (__CORTEX_M >= 0x03) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __enable_fault_irq __enable_fiq + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +#define __disable_fault_irq __disable_fiq + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + register uint32_t __regBasePri __ASM("basepri"); + return(__regBasePri); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) +{ + register uint32_t __regBasePri __ASM("basepri"); + __regBasePri = (basePri & 0xff); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + return(__regFaultMask); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + __regFaultMask = (faultMask & (uint32_t)1); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + +#if (__CORTEX_M == 0x04) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + register uint32_t __regfpscr __ASM("fpscr"); + return(__regfpscr); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + register uint32_t __regfpscr __ASM("fpscr"); + __regfpscr = (fpscr); +#endif +} + +#endif /* (__CORTEX_M == 0x04) */ + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ + +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ + +#include + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/** \brief Enable IRQ Interrupts + + This function enables IRQ interrupts by clearing the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** \brief Disable IRQ Interrupts + + This function disables IRQ interrupts by setting the I-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** \brief Get Control Register + + This function returns the content of the Control Register. + + \return Control Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +/** \brief Set Control Register + + This function writes the given value to the Control Register. + + \param [in] control Control Register value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); +} + + +/** \brief Get IPSR Register + + This function returns the content of the IPSR Register. + + \return IPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get APSR Register + + This function returns the content of the APSR Register. + + \return APSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get xPSR Register + + This function returns the content of the xPSR Register. + + \return xPSR Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** \brief Get Process Stack Pointer + + This function returns the current value of the Process Stack Pointer (PSP). + + \return PSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Process Stack Pointer + + This function assigns the given value to the Process Stack Pointer (PSP). + + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); +} + + +/** \brief Get Main Stack Pointer + + This function returns the current value of the Main Stack Pointer (MSP). + + \return MSP Register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t result; + + __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); + return(result); +} + + +/** \brief Set Main Stack Pointer + + This function assigns the given value to the Main Stack Pointer (MSP). + + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); +} + + +/** \brief Get Priority Mask + + This function returns the current state of the priority mask bit from the Priority Mask Register. + + \return Priority Mask value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Priority Mask + + This function assigns the given value to the Priority Mask Register. + + \param [in] priMask Priority Mask + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (__CORTEX_M >= 0x03) + +/** \brief Enable FIQ + + This function enables FIQ interrupts by clearing the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** \brief Disable FIQ + + This function disables FIQ interrupts by setting the F-bit in the CPSR. + Can only be executed in Privileged modes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** \brief Get Base Priority + + This function returns the current value of the Base Priority register. + + \return Base Priority register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_max" : "=r" (result) ); + return(result); +} + + +/** \brief Set Base Priority + + This function assigns the given value to the Base Priority register. + + \param [in] basePri Base Priority value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); +} + + +/** \brief Get Fault Mask + + This function returns the current value of the Fault Mask register. + + \return Fault Mask register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +/** \brief Set Fault Mask + + This function assigns the given value to the Fault Mask register. + + \param [in] faultMask Fault Mask value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + +#if (__CORTEX_M == 0x04) + +/** \brief Get FPSCR + + This function returns the current value of the Floating Point Status/Control register. + + \return Floating Point Status/Control register value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + uint32_t result; + + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + __ASM volatile (""); + return(result); +#else + return(0); +#endif +} + + +/** \brief Set FPSCR + + This function assigns the given value to the Floating Point Status/Control register. + + \param [in] fpscr Floating Point Status/Control value to set + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + /* Empty asm statement works as a scheduling barrier */ + __ASM volatile (""); + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); + __ASM volatile (""); +#endif +} + +#endif /* (__CORTEX_M == 0x04) */ + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ + +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all instrinsics, + * Including the CMSIS ones. + */ + +#endif + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +#endif /* __CORE_CMFUNC_H */ diff --git a/component/soc/realtek/8195a/cmsis/core_cmInstr.h b/component/soc/realtek/8195a/cmsis/core_cmInstr.h new file mode 100644 index 0000000..d213f0e --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/core_cmInstr.h @@ -0,0 +1,688 @@ +/**************************************************************************//** + * @file core_cmInstr.h + * @brief CMSIS Cortex-M Core Instruction Access Header File + * @version V3.20 + * @date 05. March 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 2013 ARM LIMITED + + 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 ARM 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 COPYRIGHT HOLDERS AND 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 __CORE_CMINSTR_H +#define __CORE_CMINSTR_H + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +#if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ +/* ARM armcc specific functions */ + +#if (__ARMCC_VERSION < 400677) + #error "Please use ARM Compiler Toolchain V4.0.677 or later!" +#endif + + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __nop + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +#define __WFI __wfi + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __wfe + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __sev + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +#define __ISB() __isb(0xF) + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __dsb(0xF) + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __dmb(0xF) + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV __rev + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) +{ + rev16 r0, r0 + bx lr +} +#endif + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int32_t __REVSH(int32_t value) +{ + revsh r0, r0 + bx lr +} +#endif + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +#define __ROR __ror + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __breakpoint(value) + + +#if (__CORTEX_M >= 0x03) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __rbit + + +/** \brief LDR Exclusive (8 bit) + + This function performs a exclusive LDR command for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) + + +/** \brief LDR Exclusive (16 bit) + + This function performs a exclusive LDR command for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) + + +/** \brief LDR Exclusive (32 bit) + + This function performs a exclusive LDR command for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) + + +/** \brief STR Exclusive (8 bit) + + This function performs a exclusive STR command for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (16 bit) + + This function performs a exclusive STR command for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH(value, ptr) __strex(value, ptr) + + +/** \brief STR Exclusive (32 bit) + + This function performs a exclusive STR command for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW(value, ptr) __strex(value, ptr) + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +#define __CLREX __clrex + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __ssat + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __usat + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __clz + +#endif /* (__CORTEX_M >= 0x03) */ + + + +#elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ +/* IAR iccarm specific functions */ + +#include + + +#elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ +/* TI CCS specific functions */ + +#include + + +#elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ +/* GNU gcc specific functions */ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constrant "l" + * Otherwise, use general registers, specified by constrant "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** \brief No Operation + + No Operation does nothing. This instruction can be used for code alignment purposes. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __NOP(void) +{ + __ASM volatile ("nop"); +} + + +/** \brief Wait For Interrupt + + Wait For Interrupt is a hint instruction that suspends execution + until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFI(void) +{ + __ASM volatile ("wfi"); +} + + +/** \brief Wait For Event + + Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __WFE(void) +{ + __ASM volatile ("wfe"); +} + + +/** \brief Send Event + + Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __SEV(void) +{ + __ASM volatile ("sev"); +} + + +/** \brief Instruction Synchronization Barrier + + Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or + memory, after the instruction has been completed. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __ISB(void) +{ + __ASM volatile ("isb"); +} + + +/** \brief Data Synchronization Barrier + + This function acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DSB(void) +{ + __ASM volatile ("dsb"); +} + + +/** \brief Data Memory Barrier + + This function ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __DMB(void) +{ + __ASM volatile ("dmb"); +} + + +/** \brief Reverse byte order (32 bit) + + This function reverses the byte order in integer value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM volatile ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Reverse byte order (16 bit) + + This function reverses the byte order in two unsigned short values. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** \brief Reverse byte order in signed short value + + This function reverses the byte order in a signed short value with sign extension to integer. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE int32_t __REVSH(int32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (short)__builtin_bswap16(value); +#else + uint32_t result; + + __ASM volatile ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +#endif +} + + +/** \brief Rotate Right in unsigned value (32 bit) + + This function Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + + \param [in] value Value to rotate + \param [in] value Number of Bits to rotate + \return Rotated value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + return (op1 >> op2) | (op1 << (32 - op2)); +} + + +/** \brief Breakpoint + + This function causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +#if (__CORTEX_M >= 0x03) + +/** \brief Reverse bit order of value + + This function reverses the bit order of the given value. + + \param [in] value Value to reverse + \return Reversed value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rbit %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + + +/** \brief LDR Exclusive (8 bit) + + This function performs a exclusive LDR command for 8 bit value. + + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return(result); +} + + +/** \brief LDR Exclusive (16 bit) + + This function performs a exclusive LDR command for 16 bit values. + + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return(result); +} + + +/** \brief LDR Exclusive (32 bit) + + This function performs a exclusive LDR command for 32 bit values. + + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** \brief STR Exclusive (8 bit) + + This function performs a exclusive STR command for 8 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief STR Exclusive (16 bit) + + This function performs a exclusive STR command for 16 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief STR Exclusive (32 bit) + + This function performs a exclusive STR command for 32 bit values. + + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** \brief Remove the exclusive lock + + This function removes the exclusive lock which is created by LDREX. + + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + + +/** \brief Signed Saturate + + This function saturates a signed value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Unsigned Saturate + + This function saturates an unsigned value. + + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + + +/** \brief Count leading zeros + + This function counts the number of leading zeros of a data value. + + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__attribute__( ( always_inline ) ) __STATIC_INLINE uint8_t __CLZ(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("clz %0, %1" : "=r" (result) : "r" (value) ); + return(result); +} + +#endif /* (__CORTEX_M >= 0x03) */ + + + + +#elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ +/* TASKING carm specific functions */ + +/* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + +#endif + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + +#endif /* __CORE_CMINSTR_H */ diff --git a/component/soc/realtek/8195a/cmsis/device/app_start.c b/component/soc/realtek/8195a/cmsis/device/app_start.c new file mode 100644 index 0000000..64f9bbf --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/app_start.c @@ -0,0 +1,192 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "build_info.h" +#ifdef PLATFORM_FREERTOS +#include "FreeRTOS.h" +#include "task.h" +#endif + +#if defined (CONFIG_USB_EN) && defined(CONFIG_USB_HOST_ONLY) + extern void _usb_init(void); +#endif + +#if defined(CONFIG_SDIO_DEVICE_EN) && defined(CONFIG_SDIO_DEVICE_NORMAL) +extern VOID HalSdioInit(VOID); +#endif + +#if defined(CONFIG_WIFI_NORMAL) && defined(CONFIG_NETWORK) +extern void init_rom_wlan_ram_map(void); +extern VOID wlan_network(VOID); +#endif + +//3 Monitor App Function +extern VOID RtlConsolInitRam(u32 Boot, u32 TBLSz, VOID *pTBL); +#ifndef CONFIG_KERNEL +extern VOID RtlConsolTaskRom(VOID *Data); +#endif + +#ifndef CONFIG_WITHOUT_MONITOR +extern COMMAND_TABLE UartLogRamCmdTable[]; +extern u32 GetRamCmdNum(VOID); +extern VOID UartLogIrqHandleRam(VOID * Data); +#endif + +#ifdef CONFIG_APP_DEMO +#define MAIN_APP_DEFAULT_STACK_SIZE 2048 +#define MAIN_APP_DEFAULT_PRIORITY (tskIDLE_PRIORITY + 1) +#endif + +#ifdef CONFIG_MBED_ENABLED +extern void __libc_fini_array (void); +extern void __libc_init_array (void); +extern void SVC_Handler (void); +extern void PendSV_Handler (void); +extern void SysTick_Handler (void); +#endif + +#ifndef CONFIG_WITHOUT_MONITOR +static +VOID +ReRegisterPlatformLogUart( + VOID +) +{ + IRQ_HANDLE UartIrqHandle; + + //4 Register Log Uart Callback function + UartIrqHandle.Data = NULL;//(u32)&UartAdapter; + UartIrqHandle.IrqNum = UART_LOG_IRQ; + UartIrqHandle.IrqFun = (IRQ_FUN) UartLogIrqHandleRam; + UartIrqHandle.Priority = 0; + + + //4 Register Isr handle + InterruptUnRegister(&UartIrqHandle); + InterruptRegister(&UartIrqHandle); +#if !TASK_SCHEDULER_DISABLED + RtlConsolInitRam((u32)RAM_STAGE,(u32)GetRamCmdNum(),(VOID*)&UartLogRamCmdTable); +#else + RtlConsolInitRam((u32)ROM_STAGE,(u32)GetRamCmdNum(),(VOID*)&UartLogRamCmdTable); +#endif +} +#endif // end of "#ifndef CONFIG_WITHOUT_MONITOR" + + +VOID ShowRamBuildInfo(VOID) +{ + /* + DBG_8195A("=========================================================\n\n"); + //DBG_8195A("Build Time: "UTS_VERSION"\n"); + DBG_8195A("Build Time: "RTL8195AFW_COMPILE_TIME"\n"); + DBG_8195A("Build Author: "RTL8195AFW_COMPILE_BY"\n"); + DBG_8195A("Build Host: "RTL8195AFW_COMPILE_HOST"\n"); + DBG_8195A("Build ToolChain Version: "RTL195AFW_COMPILER"\n\n"); + DBG_8195A("=========================================================\n"); + */ +} + +#ifdef CONFIG_APP_DEMO +#include "device.h" +#include "gpio_api.h" // mbed + +_WEAK void main(void) +{ + gpio_t gpio_led; + +#ifndef CONFIG_WITHOUT_MONITOR + ReRegisterPlatformLogUart(); +#endif + + // Init LED control pin + gpio_init(&gpio_led, PC_5); + gpio_dir(&gpio_led, PIN_OUTPUT); // Direction: Output + gpio_mode(&gpio_led, PullNone); // No pull + while(1){ + gpio_write(&gpio_led, !gpio_read(&gpio_led)); + RtlMsleepOS(1000); + } +} +#else +//default main +_WEAK void main(void) +{ + // Init SDIO +#if defined(CONFIG_SDIO_DEVICE_EN) && defined(CONFIG_SDIO_DEVICE_NORMAL) + HalSdioInit(); +#endif + +#ifndef CONFIG_WITHOUT_MONITOR + ReRegisterPlatformLogUart(); +#endif + +#if defined(CONFIG_WIFI_NORMAL) && defined(CONFIG_NETWORK) + wlan_network(); +#else + +#if defined (CONFIG_USB_EN) && defined(CONFIG_USB_HOST_ONLY) + _usb_init(); +#endif + +#endif // end of else of "#if defined(CONFIG_WIFI_NORMAL) && defined(CONFIG_NETWORK)" + + //3 4)Enable Schedule +#if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED + #ifdef PLATFORM_FREERTOS + vTaskStartScheduler(); + #endif +#else + RtlConsolTaskRom(NULL); +#endif + +} +#endif // end of #if CONFIG_APP_DEMO + +// The Main App entry point +void _AppStart(void) +{ +#ifdef CONFIG_MBED_ENABLED + InterruptForOSInit((VOID*)SVC_Handler, + (VOID*)PendSV_Handler, + (VOID*)SysTick_Handler); + __asm ( + "ldr r0, =SystemInit\n" + "blx r0\n" + "ldr r0, =_start\n" + "bx r0\n" + ); + + for(;;); +#else + // It's Not Mbed BSP +#ifdef CONFIG_KERNEL +#endif + + // Disable debug info log of spiflash + DBG_INFO_MSG_OFF(_DBG_SPI_FLASH_); + +#ifdef CONFIG_APP_DEMO +#ifdef PLATFORM_FREERTOS + xTaskCreate( (TaskFunction_t)main, "MAIN_APP__TASK", (MAIN_APP_DEFAULT_STACK_SIZE/4), (void *)NULL, MAIN_APP_DEFAULT_PRIORITY, NULL); + vTaskStartScheduler(); +#endif +#else + // force SP align to 8 byte not 4 byte (initial SP is 4 byte align) + __asm( + "mov r0, sp\n" + "bic r0, r0, #7\n" + "mov sp, r0\n" + ); + + main(); +#endif // end of #if CONFIG_APP_DEMO + +#endif // end of else of "#ifdef CONFIG_MBED_ENABLED" +} diff --git a/component/soc/realtek/8195a/cmsis/device/cmsis.h b/component/soc/realtek/8195a/cmsis/device/cmsis.h new file mode 100644 index 0000000..420fd53 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/cmsis.h @@ -0,0 +1,38 @@ +/* mbed Microcontroller Library + * A generic CMSIS include header + ******************************************************************************* + * Copyright (c) 2014, STMicroelectronics + * 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 STMicroelectronics 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 HOLDER 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 MBED_CMSIS_H +#define MBED_CMSIS_H + +#include "rtl8195a.h" +#include "cmsis_nvic.h" + +#endif diff --git a/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.c b/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.c new file mode 100644 index 0000000..e89187b --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.c @@ -0,0 +1,55 @@ +/* mbed Microcontroller Library + * CMSIS-style functionality to support dynamic vectors + ******************************************************************************* + * Copyright (c) 2014, STMicroelectronics + * 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 STMicroelectronics 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 HOLDER 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 "cmsis_nvic.h" + +#define NVIC_RAM_VECTOR_ADDRESS (0x10000000) // Vectors positioned at start of RAM +#define NVIC_ROM_VECTOR_ADDRESS (0x00000000) // Initial vector position at start of ROM + +void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) { + uint32_t *vectors = (uint32_t *)SCB->VTOR; + uint32_t i; + + // Copy and switch to dynamic vectors if the first time called + if (SCB->VTOR != NVIC_RAM_VECTOR_ADDRESS) { + uint32_t *old_vectors = vectors; + vectors = (uint32_t*)NVIC_RAM_VECTOR_ADDRESS; + for (i=0; iVTOR = (uint32_t)NVIC_RAM_VECTOR_ADDRESS; + } + vectors[IRQn + NVIC_USER_IRQ_OFFSET] = vector; +} + +uint32_t NVIC_GetVector(IRQn_Type IRQn) { + uint32_t *vectors = (uint32_t*)SCB->VTOR; + return vectors[IRQn + NVIC_USER_IRQ_OFFSET]; +} diff --git a/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.h b/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.h new file mode 100644 index 0000000..ca0b5b9 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/cmsis_nvic.h @@ -0,0 +1,54 @@ +/* mbed Microcontroller Library + * CMSIS-style functionality to support dynamic vectors + ******************************************************************************* + * Copyright (c) 2014, STMicroelectronics + * 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 STMicroelectronics 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 HOLDER 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 MBED_CMSIS_NVIC_H +#define MBED_CMSIS_NVIC_H + +// CORE: 64 vectors = 64 bytes from 0x00 to 0x3F +// MCU Peripherals: 85 vectors = 340 bytes from 0x40 to ... +// Total: 128 vectors = 512 bytes (0x200) to be reserved in RAM +#define NVIC_NUM_VECTORS 128 +#define NVIC_USER_IRQ_OFFSET 64 + +#include "cmsis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void NVIC_SetVector(IRQn_Type IRQn, uint32_t vector); +uint32_t NVIC_GetVector(IRQn_Type IRQn); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/component/soc/realtek/8195a/cmsis/device/diag.h b/component/soc/realtek/8195a/cmsis/device/diag.h new file mode 100644 index 0000000..c3dbf8e --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/diag.h @@ -0,0 +1,808 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _DIAG_H_ +#define _DIAG_H_ + +#include "platform_autoconf.h" +#include "basic_types.h" + +#include /* for size_t */ + +extern u32 ConfigDebugErr; +extern u32 ConfigDebugInfo; +extern u32 ConfigDebugWarn; + +extern u32 CfgSysDebugErr; +extern u32 CfgSysDebugInfo; +extern u32 CfgSysDebugWarn; + +#define DBG_ERR_MSG_ON(x) (ConfigDebugErr |= (x)) +#define DBG_WARN_MSG_ON(x) (ConfigDebugWarn |= (x)) +#define DBG_INFO_MSG_ON(x) (ConfigDebugInfo |= (x)) + +#define DBG_ERR_MSG_OFF(x) (ConfigDebugErr &= ~(x)) +#define DBG_WARN_MSG_OFF(x) (ConfigDebugWarn &= ~(x)) +#define DBG_INFO_MSG_OFF(x) (ConfigDebugInfo &= ~(x)) + +// Define debug group +#define _DBG_BOOT_ 0x00000001 +#define _DBG_GDMA_ 0x00000002 +#define _DBG_GPIO_ 0x00000004 +#define _DBG_TIMER_ 0x00000008 +#define _DBG_I2C_ 0x00000010 +#define _DBG_I2S_ 0x00000020 +#define _DBG_MII_ 0x00000040 +#define _DBG_NFC_ 0x00000080 +#define _DBG_PCM_ 0x00000100 +#define _DBG_PWM_ 0x00000200 +#define _DBG_SDIO_ 0x00000400 +#define _DBG_SSI_ 0x00000800 +#define _DBG_SPI_FLASH_ 0x00001000 +#define _DBG_SDR_ 0x00002000 +#define _DBG_UART_ 0x00004000 +#define _DBG_USB_OTG_ 0x00008000 +#define _DBG_USB_CORE_ 0x00010000 +#define _DBG_CRYPTO_ 0x00020000 +#define _DBG_ADC_ 0x00040000 +#define _DBG_DAC_ 0x00080000 + +#define _DBG_MISC_ 0x40000000 +#define _DBG_FAULT_ 0x80000000 + +typedef enum _SYSTEM_DBG_DEFINE_ { + _SYSDBG_MISC_ = 1<<0, + _SYSDBG_MAILBOX_ = 1<<1, + _SYSDBG_TIMER_ = 1<<2 + +} SYSTEM_DBG; + +extern +_LONG_CALL_ u32 +DiagPrintf( + IN const char *fmt, ... +); + +u32 +DiagSPrintf( + IN u8 *buf, + IN const char *fmt, ... +); + +int +prvDiagPrintf( + IN const char *fmt, ... +); + +int +prvDiagSPrintf( + IN char *buf, + IN const char *fmt, ... +); + + +#define _DbgDump DiagPrintf + +#define DRIVER_PREFIX "RTL8195A[Driver]: " +#define HAL_PREFIX "RTL8195A[HAL]: " +#define DMA_PREFIX "RTL8195A[DMA]: " +#define SDIO_PREFIX "RTL8195A[SDIO]" +#define MBOX_PREFIX "[OS-MBOX]" +#define TIMER_PREFIX "[OS-TMR]" + +#define BOOT_ERR_PREFIX "[BOOT Err]" +#define BOOT_WARN_PREFIX "[BOOT Wrn]" +#define BOOT_INFO_PREFIX "[BOOT Inf]" + +#define GDMA_ERR_PREFIX "[GDMA Err]" +#define GDMA_WARN_PREFIX "[GDMA Wrn]" +#define GDMA_INFO_PREFIX "[GDMA Inf]" + +#define GPIO_ERR_PREFIX "[GPIO Err]" +#define GPIO_WARN_PREFIX "[GPIO Wrn]" +#define GPIO_INFO_PREFIX "[GPIO Inf]" + +#define TIMER_ERR_PREFIX "[TIMR Err]" +#define TIMER_WARN_PREFIX "[TIMR Wrn]" +#define TIMER_INFO_PREFIX "[TIMR Inf]" + +#define I2C_ERR_PREFIX "[I2C Err]" +#define I2C_WARN_PREFIX "[I2C Wrn]" +#define I2C_INFO_PREFIX "[I2C Inf]" + +#define I2S_ERR_PREFIX "[I2S Err]" +#define I2S_WARN_PREFIX "[I2S Wrn]" +#define I2S_INFO_PREFIX "[I2S Inf]" + +#define MII_ERR_PREFIX "[MII Err]" +#define MII_WARN_PREFIX "[MII Wrn]" +#define MII_INFO_PREFIX "[MII Inf]" + +#define NFC_ERR_PREFIX "[NFC Err]" +#define NFC_WARN_PREFIX "[NFC Wrn]" +#define NFC_INFO_PREFIX "[NFC Inf]" + +#define PCM_ERR_PREFIX "[PCM Err]" +#define PCM_WARN_PREFIX "[PCM Wrn]" +#define PCM_INFO_PREFIX "[PCM Inf]" + +#define PWM_ERR_PREFIX "[PWM Err]" +#define PWM_WARN_PREFIX "[PWM Wrn]" +#define PWM_INFO_PREFIX "[PWM Inf]" + +#define SSI_ERR_PREFIX "[SSI Err]" +#define SSI_WARN_PREFIX "[SSI Wrn]" +#define SSI_INFO_PREFIX "[SSI Inf]" + +#define SDIO_ERR_PREFIX "[SDIO Err]" +#define SDIO_WARN_PREFIX "[SDIO Wrn]" +#define SDIO_INFO_PREFIX "[SDIO Inf]" + +#define SPIF_ERR_PREFIX "[SPIF Err]" +#define SPIF_WARN_PREFIX "[SPIF Wrn]" +#define SPIF_INFO_PREFIX "[SPIF Inf]" + +#define SDR_ERR_PREFIX "[SDR Err]" +#define SDR_WARN_PREFIX "[SDR Wrn]" +#define SDR_INFO_PREFIX "[SDR Inf]" + +#define UART_ERR_PREFIX "[UART Err]" +#define UART_WARN_PREFIX "[UART Wrn]" +#define UART_INFO_PREFIX "[UART Inf]" + +#define USB_ERR_PREFIX "[USB Err]" +#define USB_WARN_PREFIX "[USB Wrn]" +#define USB_INFO_PREFIX "[USB Inf]" + +#define IPSEC_ERR_PREFIX "[CRYP Err]" +#define IPSEC_WARN_PREFIX "[CRYP Wrn]" +#define IPSEC_INFO_PREFIX "[CRYP Inf]" + +#define ADC_ERR_PREFIX "[ADC Err]" +#define ADC_WARN_PREFIX "[ADC Wrn]" +#define ADC_INFO_PREFIX "[ADC Inf]" + +#define DAC_ERR_PREFIX "[DAC Err]" +#define DAC_WARN_PREFIX "[DAC Wrn]" +#define DAC_INFO_PREFIX "[DAC Inf]" + +#define MISC_ERR_PREFIX "[MISC Err]" +#define MISC_WARN_PREFIX "[MISC Wrn]" +#define MISC_INFO_PREFIX "[MISC Inf]" + +#define OTG_ERR_PREFIX "[OTG Err]" +#define OTG_WARN_PREFIX "[OTG Wrn]" +#define OTG_INFO_PREFIX "[OTG Inf]" + +#define OTG_PREFIX "RTL8195A[OTG]: " +#define OTG_PREFIX_LVL "RTL8195A[OTG_LVL_%2x]: " + +//#ifdef +#define CONFIG_DEBUG_ERROR 1 +#define CONFIG_DEBUG_WARN 1 +#define CONFIG_DEBUG_INFO 1 + +#ifndef likely +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifdef CONFIG_DEBUG_LOG + +#if CONFIG_DEBUG_ERROR // if Build-In Debug Error Message + +#define DBG_BOOT_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_BOOT_)) \ + _DbgDump("\r"BOOT_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GDMA_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_GDMA_)) \ + _DbgDump("\r"GDMA_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GPIO_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_GPIO_)) \ + _DbgDump("\r"GPIO_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_TIMER_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_TIMER_)) \ + _DbgDump("\r"TIMER_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2C_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_I2C_)) \ + _DbgDump("\r"I2C_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2S_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_I2S_)) \ + _DbgDump("\r"I2S_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_MII_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_MII_)) \ + _DbgDump("\r"MII_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_NFC_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_NFC_)) \ + _DbgDump("\r"NFC_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PCM_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_PCM_)) \ + _DbgDump("\r"PCM_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PWM_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_PWM_)) \ + _DbgDump("\r"PWM_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SSI_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_SSI_)) \ + _DbgDump("\r"SSI_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDIO_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_SDIO_)) \ + _DbgDump("\r"SDIO_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SPIF_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_SPI_FLASH_)) \ + _DbgDump("\r"SPIF_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDR_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_SDR_)) \ + _DbgDump("\r"SDR_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_UART_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_UART_)) \ + _DbgDump("\r"UART_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_USBOTG_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_USB_OTG_)) \ + _DbgDump("\r" __VA_ARGS__);\ +}while(0) + +#define DBG_USBCOR_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_USB_CORE_)) \ + _DbgDump("\r"USB_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_CRYPTO_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_CRYPTO_)) \ + _DbgDump("\r"IPSEC_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_ADC_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_ADC_)) \ + _DbgDump("\r"ADC_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_DAC_ERR(...) do {\ + if (likely(ConfigDebugErr & _DBG_DAC_)) \ + _DbgDump("\r"DAC_ERR_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_MBOX_ERR(...) do {\ + if (likely(CfgSysDebugErr & _SYSDBG_MAILBOX_)) \ + _DbgDump("\r"MBOX_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_TIMER_ERR(...) do {\ + if (likely(CfgSysDebugErr & _SYSDBG_TIMER_)) \ + _DbgDump("\r"TIMER_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_8195A_OTG(...) do{\ + if (unlikely(ConfigDebugInfo & _DBG_USB_OTG_)) \ + _DbgDump("\r"OTG_PREFIX __VA_ARGS__);\ + }while(0) + + +#define DBG_8195A_OTG_LVL(LVL,...) do{\ + if (unlikely(ConfigDebugInfo & _DBG_USB_OTG_)){ \ + _DbgDump("\r"OTG_PREFIX_LVL,LVL);\ + _DbgDump(__VA_ARGS__);\ + }\ + }while(0) + +#else // else of "#if CONFIG_DEBUG_ERROR" + +#define DBG_BOOT_ERR(...) +#define DBG_GDMA_ERR(...) +#define DBG_GPIO_ERR(...) +#define DBG_TIMER_ERR(...) +#define DBG_I2C_ERR(...) +#define DBG_I2S_ERR(...) +#define DBG_MII_ERR(...) +#define DBG_NFC_ERR(...) +#define DBG_PCM_ERR(...) +#define DBG_PWM_ERR(...) +#define DBG_SSI_ERR(...) +#define DBG_SDIO_ERR(...) +#define DBG_SPIF_ERR(...) +#define DBG_SDR_ERR(...) +#define DBG_UART_ERR(...) +#define DBG_USBOTG_ERR(...) +#define DBG_USBCOR_ERR(...) +#define DBG_CRYPTO_ERR(...) +#define DBG_ADC_ERR(...) +#define DBG_DAC_ERR(...) + +#define MSG_MBOX_ERR(...) +#define MSG_TIMER_ERR(...) +#define DBG_8195A_OTG(...) +#define DBG_8195A_OTG_LVL(LVL,...) + +#endif // end of else of "#if CONFIG_DEBUG_ERROR" + +// ============================================================= + +#if CONFIG_DEBUG_WARN // if Build-In Debug Warring Message + +#define DBG_BOOT_WARN(...) do {\ + if (unlikely(ConfigDebugWarn& _DBG_BOOT_)) \ + _DbgDump("\r"BOOT_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GDMA_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_GDMA_)) \ + _DbgDump("\r"GDMA_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GPIO_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_GPIO_)) \ + _DbgDump("\r"GPIO_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_TIMER_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_TIMER_)) \ + _DbgDump("\r"TIMER_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2C_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_I2C_)) \ + _DbgDump("\r"I2C_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2S_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_I2S_)) \ + _DbgDump("\r"I2S_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_MII_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_MII_)) \ + _DbgDump("\r"MII_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_NFC_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_NFC_)) \ + _DbgDump("\r"NFC_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PCM_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_PCM_)) \ + _DbgDump("\r"PCM_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PWM_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_PWM_)) \ + _DbgDump("\r"PWM_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SSI_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_SSI_)) \ + _DbgDump("\r"SSI_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDIO_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_SDIO_)) \ + _DbgDump("\r"SDIO_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SPIF_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_SPI_FLASH_)) \ + _DbgDump("\r"SPIF_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDR_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_SDR_)) \ + _DbgDump("\r"SDR_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_UART_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_UART_)) \ + _DbgDump("\r"UART_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_USBOTG_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_USB_OTG_)) \ + _DbgDump("\r" __VA_ARGS__);\ +}while(0) + +#define DBG_USBCOR_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_USB_CORE_)) \ + _DbgDump("\r"USB_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_CRYPTO_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_CRYPTO_)) \ + _DbgDump("\r"IPSEC_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_ADC_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_ADC_)) \ + _DbgDump("\r"ADC_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_DAC_WARN(...) do {\ + if (unlikely(ConfigDebugWarn & _DBG_DAC_)) \ + _DbgDump("\r"DAC_WARN_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_MBOX_WARN(...) do {\ + if (unlikely(CfgSysDebugWarn& _SYSDBG_MAILBOX_)) \ + _DbgDump("\r"MBOX_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_TIMER_WARN(...) do {\ + if (unlikely(CfgSysDebugWarn & _SYSDBG_TIMER_)) \ + _DbgDump("\r"TIMER_PREFIX __VA_ARGS__);\ +}while(0) + +#else // else of "#if CONFIG_DEBUG_WARN" + +#define DBG_BOOT_WARN(...) +#define DBG_GDMA_WARN(...) +#define DBG_GPIO_WARN(...) +#define DBG_TIMER_WARN(...) +#define DBG_I2C_WARN(...) +#define DBG_I2S_WARN(...) +#define DBG_MII_WARN(...) +#define DBG_NFC_WARN(...) +#define DBG_PCM_WARN(...) +#define DBG_PWM_WARN(...) +#define DBG_SSI_WARN(...) +#define DBG_SDIO_WARN(...) +#define DBG_SPIF_WARN(...) +#define DBG_SDR_WARN(...) +#define DBG_UART_WARN(...) +#define DBG_USBOTG_WARN(...) +#define DBG_USBCOR_WARN(...) +#define DBG_CRYPTO_WARN(...) +#define DBG_ADC_WARN(...) +#define DBG_DAC_WARN(...) + +#define MSG_MBOX_WARN(...) +#define MSG_TIMER_WARN(...) + +#endif // end of else of "#if CONFIG_DEBUG_WARN" + +// ============================================================= + +#if CONFIG_DEBUG_INFO // if Build-In Debug Information Message + +#define DBG_BOOT_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_BOOT_)) \ + _DbgDump("\r"BOOT_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GDMA_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_GDMA_)) \ + _DbgDump("\r"GDMA_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_GPIO_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_GPIO_)) \ + _DbgDump("\r"GPIO_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_TIMER_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_TIMER_)) \ + _DbgDump("\r"TIMER_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2C_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_I2C_)) \ + _DbgDump("\r"I2C_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_I2S_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_I2S_)) \ + _DbgDump("\r"I2S_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_MII_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_MII_)) \ + _DbgDump("\r"MII_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_NFC_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_NFC_)) \ + _DbgDump("\r"NFC_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PCM_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_PCM_)) \ + _DbgDump("\r"PCM_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_PWM_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_PWM_)) \ + _DbgDump("\r"PWM_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SSI_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_SSI_)) \ + _DbgDump("\r"SSI_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDIO_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_SDIO_)) \ + _DbgDump("\r"SDIO_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SPIF_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_SPI_FLASH_)) \ + _DbgDump("\r"SPIF_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_SDR_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_SDR_)) \ + _DbgDump("\r"SDR_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_UART_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_UART_)) \ + _DbgDump("\r"UART_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_USBOTG_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_USB_OTG_)) \ + _DbgDump("\r" __VA_ARGS__);\ +}while(0) + +#define DBG_USBCOR_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_USB_CORE_)) \ + _DbgDump("\r"USB_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_CRYPTO_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_CRYPTO_)) \ + _DbgDump("\r"IPSEC_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_ADC_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_ADC_)) \ + _DbgDump("\r"ADC_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_DAC_INFO(...) do {\ + if (unlikely(ConfigDebugInfo & _DBG_DAC_)) \ + _DbgDump("\r"DAC_INFO_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_MBOX_INFO(...) do {\ + if (unlikely(CfgSysDebugInfo & _SYSDBG_MAILBOX_)) \ + _DbgDump("\r"MBOX_PREFIX __VA_ARGS__);\ +}while(0) + +#define MSG_TIMER_INFO(...) do {\ + if (unlikely(CfgSysDebugInfo & _SYSDBG_TIMER_)) \ + _DbgDump("\r"TIMER_PREFIX __VA_ARGS__);\ +}while(0) + +#else // else of "#if CONFIG_DEBUG_INFO" + +#define DBG_BOOT_INFO(...) +#define DBG_GDMA_INFO(...) +#define DBG_GPIO_INFO(...) +#define DBG_TIMER_INFO(...) +#define DBG_I2C_INFO(...) +#define DBG_I2S_INFO(...) +#define DBG_MII_INFO(...) +#define DBG_NFC_INFO(...) +#define DBG_PCM_INFO(...) +#define DBG_PWM_INFO(...) +#define DBG_SSI_INFO(...) +#define DBG_SDIO_INFO(...) +#define DBG_SPIF_INFO(...) +#define DBG_SDR_INFO(...) +#define DBG_UART_INFO(...) +#define DBG_USBOTG_INFO(...) +#define DBG_USBCOR_INFO(...) +#define DBG_CRYPTO_INFO(...) +#define DBG_ADC_INFO(...) +#define DBG_DAC_INFO(...) + +#define MSG_MBOX_INFO(...) +#define MSG_TIMER_INFO(...) + +#endif // end of else of "#if CONFIG_DEBUG_INFO" + +#define DBG_8195A_DRIVER(...) do {\ + if (unlikely(ConfigDebugErr & (_DBG_I2S_|_DBG_PCM_|_DBG_TIMER_))) \ + _DbgDump("\r"DRIVER_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_8195A_HAL(...) do {\ + if (unlikely(ConfigDebugErr & (_DBG_SDR_|_DBG_MISC_))) \ + _DbgDump("\r"HAL_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_8195A_DMA(...) do {\ + if (unlikely(ConfigDebugErr & _DBG_GDMA_)) \ + _DbgDump("\r"DMA_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_8195A_SDIO(...) do {\ + if (unlikely(ConfigDebugErr & _DBG_SDIO_)) \ + _DbgDump("\r"SDIO_PREFIX __VA_ARGS__);\ +}while(0) + +#define DBG_8195A(...) do {\ + if (unlikely(ConfigDebugErr & _DBG_MISC_)) \ + _DbgDump("\r" __VA_ARGS__);\ +}while(0) + +#define MONITOR_LOG(...) do {\ + if (unlikely(ConfigDebugErr & _DBG_MISC_)) \ + _DbgDump( __VA_ARGS__);\ +}while(0) + +#define DBG_ERROR_LOG(...) do {\ + if (unlikely(ConfigDebugErr & _DBG_FAULT_)) \ + _DbgDump( __VA_ARGS__);\ +}while(0) + +#ifdef __GNUC__ +#define DBG_ASSERT(x) do {\ + if (unlikely(!(x))) \ + _DbgDump("Assertion: %s:%s, %d\n", __FILE__, __func__, __LINE__);\ + }while(0) +#endif + +#ifdef __ICCARM__ +#define DBG_ASSERT(x) do {\ + if (unlikely(!(x))) \ + _DbgDump("Assertion: %s:%s, %d\n", __FILE__, __func__, __LINE__);\ + }while(0) +#endif + +#else // else of "#if CONFIG_DEBUG_LOG" +#define DBG_8195A_DRIVER(...) + +#define DBG_8195A_HAL(...) + +#define DBG_8195A(...) + +#define DBG_8195A_DMA(...) + +#define MONITOR_LOG(...) + +#define DBG_ERROR_LOG(...) + +#define DBG_8195A_SDIO(...) + +#define DBG_BOOT_ERR(...) +#define DBG_GDMA_ERR(...) +#define DBG_GPIO_ERR(...) +#define DBG_TIMER_ERR(...) +#define DBG_I2C_ERR(...) +#define DBG_I2S_ERR(...) +#define DBG_MII_ERR(...) +#define DBG_NFC_ERR(...) +#define DBG_PCM_ERR(...) +#define DBG_PWM_ERR(...) +#define DBG_SSI_ERR(...) +#define DBG_SDIO_ERR(...) +#define DBG_SPIF_ERR(...) +#define DBG_SDR_ERR(...) +#define DBG_UART_ERR(...) +#define DBG_USBOTG_ERR(...) +#define DBG_USBCOR_ERR(...) +#define DBG_CRYPTO_ERR(...) +#define DBG_ADC_ERR(...) +#define DBG_DAC_ERR(...) +#define MSG_MBOX_ERR(...) +#define MSG_TIMER_ERR(...) + +#define DBG_BOOT_WARN(...) +#define DBG_GDMA_WARN(...) +#define DBG_GPIO_WARN(...) +#define DBG_TIMER_WARN(...) +#define DBG_I2C_WARN(...) +#define DBG_I2S_WARN(...) +#define DBG_MII_WARN(...) +#define DBG_NFC_WARN(...) +#define DBG_PCM_WARN(...) +#define DBG_PWM_WARN(...) +#define DBG_SSI_WARN(...) +#define DBG_SDIO_WARN(...) +#define DBG_SPIF_WARN(...) +#define DBG_SDR_WARN(...) +#define DBG_UART_WARN(...) +#define DBG_USBOTG_WARN(...) +#define DBG_USBCOR_WARN(...) +#define DBG_CRYPTO_WARN(...) +#define DBG_ADC_WARN(...) +#define DBG_DAC_WARN(...) +#define MSG_MBOX_WARN(...) +#define MSG_TIMER_WARN(...) + +#define DBG_BOOT_INFO(...) +#define DBG_GDMA_INFO(...) +#define DBG_GPIO_INFO(...) +#define DBG_TIMER_INFO(...) +#define DBG_I2C_INFO(...) +#define DBG_I2S_INFO(...) +#define DBG_MII_INFO(...) +#define DBG_NFC_INFO(...) +#define DBG_PCM_INFO(...) +#define DBG_PWM_INFO(...) +#define DBG_SSI_INFO(...) +#define DBG_SDIO_INFO(...) +#define DBG_SPIF_INFO(...) +#define DBG_SDR_INFO(...) +#define DBG_UART_INFO(...) +#define DBG_USBOTG_INFO(...) +#define DBG_USBCOR_INFO(...) +#define DBG_CRYPTO_INFO(...) +#define DBG_ADC_INFO(...) +#define DBG_DAC_INFO(...) + +#define MSG_MBOX_INFO(...) +#define MSG_TIMER_INFO(...) + +#define DBG_ASSERT(x) + +#endif + +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_RESET "\x1b[0m" + +#define IDENT_ONE_SPACE " " +#define IDENT_TWO_SPACE " " +#define IDENT_FOUR_SPACE " " +#define IDENT_SIX_SPACE " " +#define IDENT_EIGHT_SPACE " " + +#ifdef CONFIG_DEBUG_LOG +typedef enum _DBG_CFG_TYPE_ { + DBG_CFG_ERR=0, + DBG_CFG_WARN=1, + DBG_CFG_INFO=2 +} DBG_CFG_TYPE; + +typedef struct _DBG_CFG_CMD_ { + u8 cmd_name[16]; + u32 cmd_type; +} DBG_CFG_CMD, *PDBG_CFG_CMD; + +#endif + +typedef enum _CONSOLE_OP_STAGE_ { + ROM_STAGE = 0, + RAM_STAGE = 1 +}CONSOLE_OP_STAGE; + +#endif //_DIAG_H_ diff --git a/component/soc/realtek/8195a/cmsis/device/rand.h b/component/soc/realtek/8195a/cmsis/device/rand.h new file mode 100644 index 0000000..47ac51e --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/rand.h @@ -0,0 +1,15 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +u32 +Rand ( + VOID +); + + diff --git a/component/soc/realtek/8195a/cmsis/device/rtl_stdlib.h b/component/soc/realtek/8195a/cmsis/device/rtl_stdlib.h new file mode 100644 index 0000000..36c0d67 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/rtl_stdlib.h @@ -0,0 +1,41 @@ +/* + * Routines for standard lib access + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL_STDLIB_H_ +#define _RTL_STDLIB_H_ + +#include +#include +#include + +// +// string operation +// +#define strlen(str) prvStrLen((const u8*)str) +#define strcmp(str1, str2) prvStrCmp((const u8*)str1, (const u8*)str2) +#define sscanf(src, format...) //TODO +#define strtok(str, delim) prvStrTok(str, delim) +#define strcpy(dst, src) prvStrCpy((u8 *)dst, (const u8*)src) +#define atoi(str) prvAtoi(str) +#define strstr(str1, str2) prvStrStr(str1, str2) + +// +// standard i/o +// +#define snprintf DiagSnPrintf +#define sprintf prvDiagSPrintf +#define printf prvDiagPrintf + +// +// memory management +// +#define malloc pvPortMalloc +#define free vPortFree + +#endif //_RTL_STDLIB_H_ diff --git a/component/soc/realtek/8195a/cmsis/device/rtl_utility.h b/component/soc/realtek/8195a/cmsis/device/rtl_utility.h new file mode 100644 index 0000000..6fa22ff --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/rtl_utility.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * + ******************************************************************************/ +#ifndef __RTL_UTILITY_H_ +#define __RTL_UTILITY_H_ + +VOID RtlMemcpy(VOID* dec, VOID* sour, u32 sz); +u32 RtlMemcmp(VOID *dst, VOID *src, u32 sz); +VOID RtlMemset(VOID *pbuf, u32 c, u32 sz); + +s8 * +RtlStrncpy( + IN s8 *dest, + IN const s8 *src, + IN SIZE_T count +); + +s8 * +RtlStrcpy( + IN s8 *dest, + IN const s8 *src +); + + +SIZE_T +RtlStrlen( + IN const s8 *s +); + + +SIZE_T +RtlStrnlen( + IN const s8 *s, + IN SIZE_T count +); + + +int +RtlStrcmp( + IN const s8 *cs, + IN const s8 *ct + +); + +int +RtlStrncmp( + IN const s8 *cs, + IN const s8 *ct, + IN SIZE_T count +); + +#endif + + diff --git a/component/soc/realtek/8195a/cmsis/device/strproc.h b/component/soc/realtek/8195a/cmsis/device/strproc.h new file mode 100644 index 0000000..127a23d --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/strproc.h @@ -0,0 +1,104 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _STRPROC_H_ +#define _STRPROC_H_ + +#include /* for size_t */ +#include "va_list.h" + +#ifndef isprint +#define in_range(c, lo, up) ((u8)c >= lo && (u8)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' || c == ',') +#endif + + +extern _LONG_CALL_ char *_strncpy(char *dest, const char *src, size_t count); +extern _LONG_CALL_ char *_strcpy(char *dest, const char *src); +extern _LONG_CALL_ size_t _strlen(const char *s); +extern _LONG_CALL_ size_t _strnlen(const char *s, size_t count); +extern _LONG_CALL_ int _strcmp(const char *cs, const char *ct); +extern _LONG_CALL_ int _strncmp(const char *cs, const char *ct, size_t count); +extern _LONG_CALL_ int _sscanf(const char *buf, const char *fmt, ...); +extern _LONG_CALL_ char *_strsep(char **s, const char *ct); +extern _LONG_CALL_ char *skip_spaces(const char *str); +extern _LONG_CALL_ int skip_atoi(const char **s); +extern _LONG_CALL_ int _vsscanf(const char *buf, const char *fmt, va_list args); +extern _LONG_CALL_ unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base); +extern _LONG_CALL_ long simple_strtol(const char *cp, char **endp, unsigned int base); +extern _LONG_CALL_ long long simple_strtoll(const char *cp, char **endp, unsigned int base); +extern _LONG_CALL_ unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base); +extern _LONG_CALL_ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); +extern _LONG_CALL_ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p); +extern _LONG_CALL_ u64 div_u64(u64 dividend, u32 divisor); +extern _LONG_CALL_ s64 div_s64(s64 dividend, s32 divisor); +extern _LONG_CALL_ u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder); +extern _LONG_CALL_ s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); +extern _LONG_CALL_ char *_strpbrk(const char *cs, const char *ct); +extern _LONG_CALL_ char *_strchr(const char *s, int c); + + +extern _LONG_CALL_ VOID +prvStrCpy( + IN u8 *pDES, + IN const u8 *pSRC +); + +extern _LONG_CALL_ u32 +prvStrLen( + IN const u8 *pSRC +); + +extern _LONG_CALL_ u8 +prvStrCmp( + IN const u8 *string1, + IN const u8 *string2 +); + +extern _LONG_CALL_ u8* +StrUpr( + IN u8 *string +); + +extern _LONG_CALL_ int prvAtoi( + IN const char * s +); + +extern _LONG_CALL_ const char * prvStrStr( + IN const char * str1, + IN const char * str2 +); + + +/* + * Fast implementation of tolower() for internal usage. Do not use in your + * code. + */ +static inline char _tolower(const char c) +{ + return c | 0x20; +} + +/* Fast check for octal digit */ +static inline int isodigit(const char c) +{ + return c >= '0' && c <= '7'; +} +#ifndef strtoul +#define strtoul(str, endp, base) simple_strtoul(str, endp, base) +#endif +#ifndef strtol +#define strtol(str, endp, base) simple_strtol(str, endp, base) +#endif + +#endif diff --git a/component/soc/realtek/8195a/cmsis/device/system_8195a.c b/component/soc/realtek/8195a/cmsis/device/system_8195a.c new file mode 100644 index 0000000..a4a8c50 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/system_8195a.c @@ -0,0 +1,139 @@ +/**************************************************************************//** + * @file system_ARMCM3.c + * @brief CMSIS Device System Source File for + * ARMCM3 Device Series + * @version V1.08 + * @date 23. November 2012 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2011 - 2012 ARM LIMITED + + 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 ARM 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 COPYRIGHT HOLDERS AND 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 "basic_types.h" +#include "rtl8195a.h" + +/*---------------------------------------------------------------------------- + Define clocks + *----------------------------------------------------------------------------*/ +#define __HSI ( 8000000UL) +#define __XTAL ( 5000000UL) /* Oscillator frequency */ + +//#define __SYSTEM_CLOCK (5*__XTAL) +#define __SYSTEM_CLOCK (200000000UL/6*5) + +extern unsigned int rand_x; +extern u32 HalGetCpuClk(VOID); + +#if CONFIG_CHIP_A_CUT +const u32 SysCpkClkTbl[]= { + 200000000, + 100000000, + 50000000, + 25000000, + 12500000, + 4000000 +}; +#endif + +u32 Rand2(void) +{ + static unsigned int y = 362436; + static unsigned int z = 521288629; + static unsigned int c = 7654321; + + unsigned long long t, a= 698769069; + + rand_x = 69069 * rand_x + 12345; + y ^= (y << 13); y ^= (y >> 17); y ^= (y << 5); + t = a * z + c; c = (t >> 32); z = t; + + return rand_x + y + z; +} + +/*---------------------------------------------------------------------------- + Clock Variable definitions + *----------------------------------------------------------------------------*/ +uint32_t SystemCoreClock = __SYSTEM_CLOCK;/*!< System Clock Frequency (Core Clock)*/ + + +u32 +SystemGetCpuClk(void) +{ +#if CONFIG_CHIP_A_CUT + + u32 CpuType = 0, CpuClk = 0, FreqDown = 0; + + CpuType = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_CLK_CTRL1) & (0x70)) >> 4); + FreqDown = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1) & BIT17; + + CpuClk = SysCpkClkTbl[CpuType]; + + if ( !FreqDown ) { + if ( CpuClk > 4000000 ){ + CpuClk = (CpuClk*5/6); + } + } + + return CpuClk; +#else + return HalGetCpuClk(); +#endif +} + +/*---------------------------------------------------------------------------- + Clock functions + *----------------------------------------------------------------------------*/ +void SystemCoreClockUpdate (void) /* Get Core Clock Frequency */ +{ +// SystemCoreClock = __SYSTEM_CLOCK; + + SystemCoreClock = SystemGetCpuClk(); +} + +/** + * Initialize the system + * + * @param none + * @return none + * + * @brief Setup the microcontroller system. + * Initialize the System. + */ +void SystemInit (void) +{ + // TODO: Hardware initial +#ifdef UNALIGNED_SUPPORT_DISABLE + SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk; +#endif + + //SystemCoreClock = __SYSTEM_CLOCK; + //SystemCoreClock = HalGetCpuClk(); + SystemCoreClockUpdate(); +} diff --git a/component/soc/realtek/8195a/cmsis/device/system_8195a.h b/component/soc/realtek/8195a/cmsis/device/system_8195a.h new file mode 100644 index 0000000..a730aff --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/system_8195a.h @@ -0,0 +1,77 @@ +/**************************************************************************//** + * @file system_ARMCM3.h + * @brief CMSIS Device System Header File for + * ARMCM3 Device Series + * @version V1.08 + * @date 23. November 2012 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2011 - 2012 ARM LIMITED + + 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 ARM 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 COPYRIGHT HOLDERS AND 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 _SYSTEM_8195A_H +#define _SYSTEM_8195A_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */ + + +/** + * Initialize the system + * + * @param none + * @return none + * + * @brief Setup the microcontroller system. + * Initialize the System and update the SystemCoreClock variable. + */ +extern void SystemInit (void); + +/** + * Update SystemCoreClock variable + * + * @param none + * @return none + * + * @brief Updates the SystemCoreClock with current core Clock + * retrieved from cpu registers. + */ +extern void SystemCoreClockUpdate (void); +extern u32 SystemGetCpuClk(void); +extern u32 Rand2(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSTEM_8195A_H */ diff --git a/component/soc/realtek/8195a/cmsis/device/va_list.h b/component/soc/realtek/8195a/cmsis/device/va_list.h new file mode 100644 index 0000000..d3a1dd1 --- /dev/null +++ b/component/soc/realtek/8195a/cmsis/device/va_list.h @@ -0,0 +1,37 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _VA_LIST_H_ +#define _VA_LIST_H_ + +#include "platform_autoconf.h" +#include "basic_types.h" + +#ifndef va_arg //this part is adapted from linux (Linux/include/acpi/platform/acenv.h) + +typedef s32 acpi_native_int;//this definition is in (Linux/include/acpi/actypes.h) + +#ifndef _VALIST +#define _VALIST + typedef char *va_list; +#endif /* _VALIST */ + +/* Storage alignment properties */ +#define _AUPBND (sizeof (acpi_native_int) - 1) +#define _ADNBND (sizeof (acpi_native_int) - 1) + +/* Variable argument list macro definitions */ +#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) +#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) +#define va_end(ap) (ap = (va_list) NULL) +#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) + +#endif /* va_arg */ + +#endif //_VA_LIST_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_adc.h b/component/soc/realtek/8195a/fwlib/hal_adc.h new file mode 100644 index 0000000..e5c72f5 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_adc.h @@ -0,0 +1,317 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_ADC_H_ +#define _HAL_ADC_H_ + +#include "rtl8195a.h" +#include "rtl8195a_adc.h" +#include "hal_gdma.h" + +//================ ADC Configuration ========================= +#define ADC_INTR_OP_TYPE 1 +#define ADC_DMA_OP_TYPE 1 + +// ADC SAL management macros +#define SAL_ADC_USER_CB_NUM (sizeof(SAL_ADC_USER_CB) / sizeof(PSAL_ADC_USERCB_ADPT)) + +// ADC used module. +// Please set the ADC module flag to 1 to enable the related +#define ADC0_USED 1 +#define ADC1_USED 1 +#define ADC2_USED 1 +#define ADC3_USED 1 + + +//================ Debug MSG Definition ======================= +#define ADC_PREFIX "RTL8195A[adc]: " +#define ADC_PREFIX_LVL " [ADC_DBG]: " + +typedef enum _ADC_DBG_LVL_ { + HAL_ADC_LVL = 0x01, + SAL_ADC_LVL = 0x02, + VERI_ADC_LVL = 0x04, +}ADC_DBG_LVL,*PADC_DBG_LVL; + +#ifdef CONFIG_DEBUG_LOG +#ifdef CONFIG_DEBUG_LOG_ADC_HAL + + #define DBG_8195A_ADC(...) do{ \ + _DbgDump("\r"ADC_PREFIX __VA_ARGS__);\ + }while(0) + + + #define ADCDBGLVL 0xFF + #define DBG_8195A_ADC_LVL(LVL,...) do{\ + if (LVL&ADCDBGLVL){\ + _DbgDump("\r"ADC_PREFIX_LVL __VA_ARGS__);\ + }\ + }while(0) +#else + #define DBG_ADC_LOG_PERD 100 + #define DBG_8195A_ADC(...) + #define DBG_8195A_ADC_LVL(...) +#endif +#endif + + +//================ ADC HAL Related Enumeration ================== +// ADC Module Selection +typedef enum _ADC_MODULE_SEL_ { + ADC0_SEL = 0x0, + ADC1_SEL = 0x1, + ADC2_SEL = 0x2, + ADC3_SEL = 0x3, +}ADC_MODULE_SEL,*PADC_MODULE_SEL; + +// ADC module status +typedef enum _ADC_MODULE_STATUS_ { + ADC_DISABLE = 0x0, + ADC_ENABLE = 0x1, +}ADC_MODULE_STATUS, *PADC_MODULE_STATUS; + +// ADC Data Endian +typedef enum _ADC_DATA_ENDIAN_ { + ADC_DATA_ENDIAN_LITTLE = 0x0, + ADC_DATA_ENDIAN_BIG = 0x1, +}ADC_DATA_ENDIAN,*PADC_DATA_ENDIAN; + +// ADC Debug Select +typedef enum _ADC_DEBUG_SEL_ { + ADC_DBG_SEL_DISABLE = 0x0, + ADC_DBG_SEL_ENABLE = 0x1, +}ADC_DEBUG_SEL,*PADC_DEBUG_SEL; + +typedef enum _ADC_COMPARE_SET_ { + ADC_COMP_SMALLER_THAN = 0x0, + ADC_COMP_GREATER_THAN = 0x1, +}ADC_COMPARE_SET, *PADC_COMPARE_SET; + +// ADC feature status +typedef enum _ADC_FEATURE_STATUS_{ + ADC_FEATURE_DISABLED = 0, + ADC_FEATURE_ENABLED = 1, +}ADC_FEATURE_STATUS,*PADC_FEATURE_STATUS; + +// ADC operation type +typedef enum _ADC_OP_TYPE_ { + ADC_RDREG_TYPE = 0x0, + ADC_DMA_TYPE = 0x1, + ADC_INTR_TYPE = 0x2, +}ADC_OP_TYPE, *PADC_OP_TYPE; + +// ADC device status +typedef enum _ADC_DEVICE_STATUS_ { + ADC_STS_UNINITIAL = 0x00, + ADC_STS_INITIALIZED = 0x01, + ADC_STS_IDLE = 0x02, + + ADC_STS_TX_READY = 0x03, + ADC_STS_TX_ING = 0x04, + + ADC_STS_RX_READY = 0x05, + ADC_STS_RX_ING = 0x06, + + ADC_STS_ERROR = 0x07, + ADC_STS_FULL = 0x08, +}ADC_DEVICE_STATUS, *PADC_DEVICE_STATUS; + +// ADC error type +typedef enum _ADC_ERR_TYPE_ { + ADC_ERR_FIFO_RD_ERROR = 0x40, //ADC FIFO read error +}ADC_ERR_TYPE, *PADC_ERR_TYPE; + +// ADC initial status +typedef enum _ADC_INITAIL_STATUS_ { + ADC0_INITED = 0x1, + ADC1_INITED = 0x2, + ADC2_INITED = 0x4, + ADC3_INITED = 0x8, +}ADC_INITAIL_STATUS, *PADC_INITAIL_STATUS; + + +//================ ADC HAL Data Structure ====================== +// ADC HAL initial data structure +typedef struct _HAL_ADC_INIT_DAT_ { + u8 ADCIdx; //ADC index used + u8 ADCEn; //ADC module enable + u8 ADCEndian; //ADC endian selection, + //but actually it's for 32-bit ADC data swap control + //1'b0: no swap, + //1'b1: swap the upper 16-bit and the lower 16-bit + u8 ADCBurstSz; //ADC DMA operation threshold + + u8 ADCCompOnly; //ADC compare mode only enable (without FIFO enable) + u8 ADCOneShotEn; //ADC one-shot mode enable + u8 ADCOverWREn; //ADC overwrite mode enable + u8 ADCOneShotTD; //ADC one shot mode threshold + + u16 ADCCompCtrl; //ADC compare mode control, + //1'b0:less than the compare threshold + //1'b1:greater than the compare threshod + u16 ADCCompTD; //ADC compare mode threshold + + u8 ADCDataRate; //ADC down sample data rate, + u8 ADCAudioEn; //ADC audio mode enable + u8 ADCEnManul; //ADC enable manually + u8 ADCDbgSel; + + u32 RSVD0; + + u32 *ADCData; //ADC data pointer + u32 ADCPWCtrl; //ADC0 power control + u32 ADCIntrMSK; //ADC Interrupt Mask + u32 ADCAnaParAd3; //ADC analog parameter 3 + u32 ADCInInput; //ADC Input is internal? +}HAL_ADC_INIT_DAT,*PHAL_ADC_INIT_DAT; + +// ADC HAL Operations +typedef struct _HAL_ADC_OP_ { + RTK_STATUS (*HalADCInit) (VOID *Data); //HAL ADC initialization + RTK_STATUS (*HalADCDeInit) (VOID *Data); //HAL ADC de-initialization + RTK_STATUS (*HalADCEnable) (VOID *Data); //HAL ADC de-initialization + u32 (*HalADCReceive) (VOID *Data); //HAL ADC receive + RTK_STATUS (*HalADCIntrCtrl) (VOID *Data); //HAL ADC interrupt control + u32 (*HalADCReadReg) (VOID *Data, u8 ADCReg);//HAL ADC read register +}HAL_ADC_OP, *PHAL_ADC_OP; + +// ADC user callback adapter +typedef struct _SAL_ADC_USERCB_ADPT_ { + VOID (*USERCB) (VOID *Data); + u32 USERData; +}SAL_ADC_USERCB_ADPT, *PSAL_ADC_USERCB_ADPT; + +// ADC user callback structure +typedef struct _SAL_ADC_USER_CB_ { + PSAL_ADC_USERCB_ADPT pTXCB; //ADC Transmit Callback + PSAL_ADC_USERCB_ADPT pTXCCB; //ADC Transmit Complete Callback + PSAL_ADC_USERCB_ADPT pRXCB; //ADC Receive Callback + PSAL_ADC_USERCB_ADPT pRXCCB; //ADC Receive Complete Callback + PSAL_ADC_USERCB_ADPT pRDREQCB; //ADC Read Request Callback + PSAL_ADC_USERCB_ADPT pERRCB; //ADC Error Callback + PSAL_ADC_USERCB_ADPT pDMATXCB; //ADC DMA Transmit Callback + PSAL_ADC_USERCB_ADPT pDMATXCCB; //ADC DMA Transmit Complete Callback + PSAL_ADC_USERCB_ADPT pDMARXCB; //ADC DMA Receive Callback + PSAL_ADC_USERCB_ADPT pDMARXCCB; //ADC DMA Receive Complete Callback +}SAL_ADC_USER_CB, *PSAL_ADC_USER_CB; + +// ADC Transmit Buffer +typedef struct _SAL_ADC_TRANSFER_BUF_ { + u32 DataLen; //ADC Transmfer Length + u32 *pDataBuf; //ADC Transfer Buffer Pointer + u32 RSVD; // +}SAL_ADC_TRANSFER_BUF,*PSAL_ADC_TRANSFER_BUF; + +typedef struct _SAL_ADC_DMA_USER_DEF_ { + + u8 TxDatSrcWdth; + u8 TxDatDstWdth; + u8 TxDatSrcBstSz; + u8 TxDatDstBstSz; + + u8 TxChNo; + u8 LlpCtrl; + u16 RSVD0; + + u32 MaxMultiBlk; + u32 pLlix; + u32 pBlockSizeList; +}SAL_ADC_DMA_USER_DEF, *PSAL_ADC_DMA_USER_DEF; + +// Software API Level ADC Handler +typedef struct _SAL_ADC_HND_ { + u8 DevNum; //ADC device number + u8 PinMux; //ADC pin mux seletion + u8 OpType; //ADC operation type selection + volatile u8 DevSts; //ADC device status + + u32 ADCExd; //ADC extended options: + //bit 0: example + //bit 31~bit 1: Reserved + u32 ErrType; // + u32 TimeOut; //ADC IO Timeout count + + PHAL_ADC_INIT_DAT pInitDat; //Pointer to ADC initial data struct + PSAL_ADC_TRANSFER_BUF pRXBuf; //Pointer to ADC TX buffer + PSAL_ADC_USER_CB pUserCB; //Pointer to ADC User Callback +}SAL_ADC_HND, *PSAL_ADC_HND; + +// ADC SAL handle private +typedef struct _SAL_ADC_HND_PRIV_ { + VOID **ppSalADCHnd; //Pointer to SAL_ADC_HND pointer + SAL_ADC_HND SalADCHndPriv; //Private SAL_ADC_HND +}SAL_ADC_HND_PRIV, *PSAL_ADC_HND_PRIV; + +//ADC SAL management adapter +typedef struct _SAL_ADC_MNGT_ADPT_ { + PSAL_ADC_HND_PRIV pSalHndPriv; //Pointer to SAL_ADC_HND + PHAL_ADC_INIT_DAT pHalInitDat; //Pointer to HAL ADC initial data( HAL_ADC_INIT_DAT ) + PHAL_ADC_OP pHalOp; //Pointer to HAL ADC operation( HAL_ADC_OP ) + VOID (*pHalOpInit)(VOID*);//Pointer to HAL ADC initialize function + + PIRQ_HANDLE pIrqHnd; //Pointer to IRQ handler in SAL layer( IRQ_HANDLE ) + VOID (*pSalIrqFunc)(VOID*); //Used for SAL ADC interrupt function + + PSAL_ADC_DMA_USER_DEF pDMAConf; //Pointer to DAC User Define DMA config + PHAL_GDMA_ADAPTER pHalGdmaAdp; + PHAL_GDMA_OP pHalGdmaOp; + PIRQ_HANDLE pIrqGdmaHnd; + VOID (*pHalGdmaOpInit)(VOID*); //Pointer to HAL DAC initialize function + PSAL_ADC_USER_CB pUserCB; //Pointer to SAL user callbacks (SAL_ADC_USER_CB ) + VOID (*pSalDMAIrqFunc)(VOID*); //Used for SAL DAC interrupt function +}SAL_ADC_MNGT_ADPT, *PSAL_ADC_MNGT_ADPT; + + +//================ ADC HAL Function Prototype =================== +// ADC HAL inline function +// For checking I2C input index valid or not +static inline RTK_STATUS +RtkADCIdxChk( + IN u8 ADCIdx +) +{ +#if !ADC0_USED + if (ADCIdx == ADC0_SEL) + return _EXIT_FAILURE; +#endif + +#if !ADC1_USED + if (ADCIdx == ADC1_SEL) + return _EXIT_FAILURE; +#endif + +#if !ADC2_USED + if (ADCIdx == ADC2_SEL) + return _EXIT_FAILURE; +#endif + +#if !ADC3_USED + if (ADCIdx == ADC3_SEL) + return _EXIT_FAILURE; +#endif + + return _EXIT_SUCCESS; +} + +VOID HalADCOpInit(IN VOID *Data); +PSAL_ADC_HND RtkADCGetSalHnd(IN u8 DACIdx); +RTK_STATUS RtkADCFreeSalHnd(IN PSAL_ADC_HND pSalADCHND); +RTK_STATUS RtkADCLoadDefault(IN VOID *Data); +RTK_STATUS RtkADCInit(IN VOID *Data); +RTK_STATUS RtkADCDeInit(IN VOID *Data); +//RTK_STATUS RtkADCReceive(IN VOID *Data); +u32 RtkADCReceive(IN VOID *Data); +u32 RtkADCReceiveBuf(IN VOID *Data,IN u32 *pBuf); + +PSAL_ADC_MNGT_ADPT RtkADCGetMngtAdpt(IN u8 ADCIdx); +RTK_STATUS RtkADCFreeMngtAdpt(IN PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt); +VOID ADCISRHandle(IN VOID *Data); +VOID ADCGDMAISRHandle(IN VOID *Data); + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_api.h b/component/soc/realtek/8195a/fwlib/hal_api.h new file mode 100644 index 0000000..033a6b9 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_api.h @@ -0,0 +1,123 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#ifndef _HAL_API_H_ +#define _HAL_API_H_ + +#include "basic_types.h" +#include "hal_irqn.h" + +#define HAL_READ32(base, addr) \ + rtk_le32_to_cpu(*((volatile u32*)(base + addr))) + +#define HAL_WRITE32(base, addr, value32) \ + ((*((volatile u32*)(base + addr))) = rtk_cpu_to_le32(value32)) + + +#define HAL_READ16(base, addr) \ + rtk_le16_to_cpu(*((volatile u16*)(base + addr))) + +#define HAL_WRITE16(base, addr, value) \ + ((*((volatile u16*)(base + addr))) = rtk_cpu_to_le16(value)) + + +#define HAL_READ8(base, addr) \ + (*((volatile u8*)(base + addr))) + +#define HAL_WRITE8(base, addr, value) \ + ((*((volatile u8*)(base + addr))) = value) + +#if 0 +// These "extern _LONG_CALL_" function declaration are for RAM code building only +// For ROM code building, thses code should be marked off +extern _LONG_CALL_ u8 +HalPinCtrlRtl8195A( + IN u32 Function, + IN u32 PinLocation, + IN BOOL Operation + ); + +extern _LONG_CALL_ VOID +HalSerialPutcRtl8195a( + IN u8 c + ); + +extern _LONG_CALL_ u8 +HalSerialGetcRtl8195a( + IN BOOL PullMode + ); + +extern _LONG_CALL_ u32 +HalSerialGetIsrEnRegRtl8195a(VOID); + +extern _LONG_CALL_ VOID +HalSerialSetIrqEnRegRtl8195a ( + IN u32 SetValue + ); + +extern _LONG_CALL_ VOID +VectorTableInitForOSRtl8195A( + IN VOID *PortSVC, + IN VOID *PortPendSVH, + IN VOID *PortSysTick + ); + +extern _LONG_CALL_ BOOL +VectorIrqRegisterRtl8195A( + IN PIRQ_HANDLE pIrqHandle + ); + +extern _LONG_CALL_ BOOL +VectorIrqUnRegisterRtl8195A( + IN PIRQ_HANDLE pIrqHandle + ); + +extern _LONG_CALL_ VOID +VectorIrqEnRtl8195A( + IN PIRQ_HANDLE pIrqHandle + ); + +extern _LONG_CALL_ VOID +VectorIrqDisRtl8195A( + IN PIRQ_HANDLE pIrqHandle + ); +#endif + +#define PinCtrl HalPinCtrlRtl8195A + +#define DiagPutChar HalSerialPutcRtl8195a +#define DiagGetChar HalSerialGetcRtl8195a +#define DiagGetIsrEnReg HalSerialGetIsrEnRegRtl8195a +#define DiagSetIsrEnReg HalSerialSetIrqEnRegRtl8195a + +#define InterruptForOSInit VectorTableInitForOSRtl8195A +#define InterruptRegister VectorIrqRegisterRtl8195A +#define InterruptUnRegister VectorIrqUnRegisterRtl8195A + +#define InterruptEn VectorIrqEnRtl8195A +#define InterruptDis VectorIrqDisRtl8195A + +#define SpicFlashInit SpicFlashInitRtl8195A +#define Calibration32k En32KCalibration +#define WDGInit InitWDGIRQ + +typedef enum _HAL_Status +{ + HAL_OK = 0x00, + HAL_BUSY = 0x01, + HAL_TIMEOUT = 0x02, + HAL_ERR_PARA = 0x03, // error with invaild parameters + HAL_ERR_MEM = 0x04, // error with memory allocation failed + HAL_ERR_HW = 0x05, // error with hardware error + + HAL_ERR_UNKNOWN = 0xee // unknown error + +} HAL_Status; + + +#endif //_HAL_API_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_common.h b/component/soc/realtek/8195a/fwlib/hal_common.h new file mode 100644 index 0000000..113c1a1 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_common.h @@ -0,0 +1,17 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_COMMON_H_ +#define _HAL_COMMON_H_ + +//================= Function Prototype START =================== +HAL_Status HalCommonInit(void); +//================= Function Prototype END =================== + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_crypto.h b/component/soc/realtek/8195a/fwlib/hal_crypto.h new file mode 100644 index 0000000..0224448 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_crypto.h @@ -0,0 +1,213 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef __HAL_CRYPTO_H__ +#define __HAL_CRYPTO_H__ + + +#include "hal_api.h" +#include "basic_types.h" + + +#define CRYPTO_MAX_MSG_LENGTH 16000 +#define CRYPTO_MD5_DIGEST_LENGTH 16 +#define CRYPTO_SHA1_DIGEST_LENGTH 20 +#define CRYPTO_SHA2_DIGEST_LENGTH 32 + + +typedef enum _SHA2_TYPE_ { + SHA2_NONE = 0, + SHA2_224 = 224/8, + SHA2_256 = 256/8, + SHA2_384 = 384/8, + SHA2_512 = 512/8 +} SHA2_TYPE; + + +#define _ERRNO_CRYPTO_DESC_NUM_SET_OutRange -2 +#define _ERRNO_CRYPTO_BURST_NUM_SET_OutRange -3 +#define _ERRNO_CRYPTO_NULL_POINTER -4 +#define _ERRNO_CRYPTO_ENGINE_NOT_INIT -5 +#define _ERRNO_CRYPTO_ADDR_NOT_4Byte_Aligned -6 +#define _ERRNO_CRYPTO_KEY_OutRange -7 +#define _ERRNO_CRYPTO_MSG_OutRange -8 +#define _ERRNO_CRYPTO_IV_OutRange -9 +#define _ERRNO_CRYPTO_AUTH_TYPE_NOT_MATCH -10 +#define _ERRNO_CRYPTO_CIPHER_TYPE_NOT_MATCH -11 +#define _ERRNO_CRYPTO_KEY_IV_LEN_DIFF -12 + + + +// +// External API Functions +// + + +// Crypto Engine +extern int rtl_cryptoEngine_init(void); +extern void rtl_cryptoEngine_info(void); + + + +// +// Authentication +// + +// md5 + +extern int rtl_crypto_md5(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + +extern int rtl_crypto_md5_init(void); +extern int rtl_crypto_md5_process(IN const u8* message, const IN u32 msglen, OUT u8* pDigest); + + +// sha1 +extern int rtl_crypto_sha1(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + +extern int rtl_crypto_sha1_init(void); +extern int rtl_crypto_sha1_process(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + +// sha2 + +extern int rtl_crypto_sha2(IN const SHA2_TYPE sha2type, + IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + +extern int rtl_crypto_sha2_init(IN const SHA2_TYPE sha2type); +extern int rtl_crypto_sha2_process(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + + +// HMAC-md5 +extern int rtl_crypto_hmac_md5(IN const u8* message, IN const u32 msglen, + IN const u8* key, IN const u32 keylen, OUT u8* pDigest); + +extern int rtl_crypto_hmac_md5_init(IN const u8* key, IN const u32 keylen); +extern int rtl_crypto_hmac_md5_process(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + + +// HMAC-sha1 +extern int rtl_crypto_hmac_sha1(IN const u8* message, IN const u32 msglen, + IN const u8* key, IN const u32 keylen, OUT u8* pDigest); + +extern int rtl_crypto_hmac_sha1_init(IN const u8* key, IN const u32 keylen); +extern int rtl_crypto_hmac_sha1_process(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + + +// HMAC-sha2 +extern int rtl_crypto_hmac_sha2(IN const SHA2_TYPE sha2type, IN const u8* message, IN const u32 msglen, + IN const u8* key, IN const u32 keylen, OUT u8* pDigest); + +extern int rtl_crypto_hmac_sha2_init(IN const SHA2_TYPE sha2type, IN const u8* key, IN const u32 keylen); +extern int rtl_crypto_hmac_sha2_process(IN const u8* message, IN const u32 msglen, OUT u8* pDigest); + + +// +// Cipher Functions +// + +// AES - CBC + +extern int rtl_crypto_aes_cbc_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_aes_cbc_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_aes_cbc_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// AES - ECB + +extern int rtl_crypto_aes_ecb_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_aes_ecb_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_aes_ecb_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// AES - CTR + +extern int rtl_crypto_aes_ctr_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_aes_ctr_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_aes_ctr_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// 3DES - CBC + +extern int rtl_crypto_3des_cbc_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_3des_cbc_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_3des_cbc_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// 3DES - ECB + +extern int rtl_crypto_3des_ecb_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_3des_ecb_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_3des_ecb_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// DES - CBC + +extern int rtl_crypto_des_cbc_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_des_cbc_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_des_cbc_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// DES - ECB + +extern int rtl_crypto_des_ecb_init(IN const u8* key, IN const u32 keylen); + +extern int rtl_crypto_des_ecb_encrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + +extern int rtl_crypto_des_ecb_decrypt( + IN const u8* message, IN const u32 msglen, + IN const u8* iv, IN const u32 ivlen, OUT u8* pResult); + + +// +// C functions in ROM +// + +extern int rtl_memcmpb(const u8 *dst, const u8 *src, int bytes); +extern int rtl_memcpyb(u8 *dst, const u8 *src, int bytes); + +#endif /* __HAL_CRYPTO_H__ */ + diff --git a/component/soc/realtek/8195a/fwlib/hal_dac.h b/component/soc/realtek/8195a/fwlib/hal_dac.h new file mode 100644 index 0000000..f8b3c8f --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_dac.h @@ -0,0 +1,313 @@ +//====================================================== +// Routines to access hardware +// +// Copyright (c) 2013 Realtek Semiconductor Corp. +// +// This module is a confidential and proprietary property of RealTek and +// possession or use of this module requires written permission of RealTek. +//====================================================== +#ifndef _HAL_DAC_H_ +#define _HAL_DAC_H_ + +#include "rtl8195a.h" +#include "rtl8195a_dac.h" +#include "hal_api.h" +#include "hal_gdma.h" + +//================ DAC Configuration ========================= +#define DAC_INTR_OP_TYPE 1 +#define DAC_DMA_OP_TYPE 1 + +// DAC SAL management macros +#define SAL_DAC_USER_CB_NUM (sizeof(SAL_DAC_USER_CB) / sizeof(PSAL_DAC_USERCB_ADPT)) + +// DAC SAL used module. +// Please set the DAC module flag to 1 to enable the related DAC module functions. +#define DAC0_USED 1 +#define DAC1_USED 1 + + +//================ Debug MSG Definition ======================= +#define DAC_PREFIX "RTL8195A[dac]: " +#define DAC_PREFIX_LVL " [DAC_DBG]: " + +typedef enum _DAC_DBG_LVL_ { + HAL_DAC_LVL = 0x00, + SAL_DAC_LVL = 0x02, + VERI_DAC_LVL = 0x04, +}DAC_DBG_LVL,*PDAC_DBG_LVL; + +#ifdef CONFIG_DEBUG_LOG +#ifdef CONFIG_DEBUG_LOG_DAC_HAL + + #define DBG_8195A_DAC(...) do{ \ + _DbgDump("\r"DAC_PREFIX __VA_ARGS__);\ + }while(0) + + + #define DACDBGLVL 0xFF + #define DBG_8195A_DAC_LVL(LVL,...) do{\ + if (LVL&DACDBGLVL){\ + _DbgDump("\r"DAC_PREFIX_LVL __VA_ARGS__);\ + }\ + }while(0) +#else + #define DBG_DAC_LOG_PERD 100 + #define DBG_8195A_DAC(...) + #define DBG_8195A_DAC_LVL(...) +#endif +#endif + + +//================ DAC HAL Related Enumeration ================== +// DAC Module Selection +typedef enum _DAC_MODULE_SEL_ { + DAC0_SEL = 0x0, + DAC1_SEL = 0x1, +}DAC_MODULE_SEL,*PDAC_MODULE_SEL; + +// DAC module status +typedef enum _DAC_MODULE_STATUS_ { + DAC_DISABLE = 0x0, + DAC_ENABLE = 0x1, +}DAC_MODULE_STATUS, *PDAC_MODULE_STATUS; + +// DAC Data Rate +typedef enum _DAC_DATA_RATE_ { + DAC_DATA_RATE_10K = 0x0, + DAC_DATA_RATE_250K = 0x1, +}DAC_DATA_RATE,*PDAC_DATA_RATE; + +// DAC Data Endian +typedef enum _DAC_DATA_ENDIAN_ { + DAC_DATA_ENDIAN_LITTLE = 0x0, + DAC_DATA_ENDIAN_BIG = 0x1, +}DAC_DATA_ENDIAN,*PDAC_DATA_ENDIAN; + +// DAC Debug Select +typedef enum _DAC_DEBUG_SEL_ { + DAC_DBG_SEL_DISABLE = 0x0, + DAC_DBG_SEL_ENABLE = 0x1, +}DAC_DEBUG_SEL,*PDAC_DEBUG_SEL; + +// DAC Dsc Debug Select +typedef enum _DAC_DSC_DEBUG_SEL_ { + DAC_DSC_DBG_SEL_DISABLE = 0x0, + DAC_DSC_DBG_SEL_ENABLE = 0x1, +}DAC_DSC_DEBUG_SEL,*PDAC_DSC_DEBUG_SEL; + + +// DAC Bypass Dsc Debug Select +typedef enum _DAC_BYPASS_DSC_SEL_ { + DAC_BYPASS_DSC_SEL_DISABLE = 0x0, + DAC_BYPASS_DSC_SEL_ENABLE = 0x1, +}DAC_BYPASS_DSC_SEL,*PDAC_BYPASS_DSC_SEL; + +// DAC feature status +typedef enum _DAC_FEATURE_STATUS_{ + DAC_FEATURE_DISABLED = 0, + DAC_FEATURE_ENABLED = 1, +}DAC_FEATURE_STATUS,*PDAC_FEATURE_STATUS; + +// DAC operation type +typedef enum _DAC_OP_TYPE_ { + DAC_POLL_TYPE = 0x0, + DAC_DMA_TYPE = 0x1, + DAC_INTR_TYPE = 0x2, +}DAC_OP_TYPE, *PDAC_OP_TYPE; + +// DAC device status +typedef enum _DAC_Device_STATUS_ { + DAC_STS_UNINITIAL = 0x00, + DAC_STS_INITIALIZED = 0x01, + DAC_STS_IDLE = 0x02, + + DAC_STS_TX_READY = 0x03, + DAC_STS_TX_ING = 0x04, + + DAC_STS_RX_READY = 0x05, + DAC_STS_RX_ING = 0x06, + + DAC_STS_ERROR = 0x07, +}DAC_Device_STATUS, *PDAC_Device_STATUS; + +//DAC device error type +typedef enum _DAC_ERR_TYPE_ { + DAC_ERR_FIFO_OVER = 0x04, //DAC FIFO overflow. + DAC_ERR_FIFO_STOP = 0x08, //DAC FIFO is completely empty, and it will be stopped automatically. + DAC_ERR_FIFO_WRFAIL = 0x10, //When DAC is NOT enabled, a write operation attempts to access DAC register. + DAC_ERR_FIFO_DSC_OVER0 = 0x20, + DAC_ERR_FIFO_DSC_OVER1 = 0x40, +}DAC_ERR_TYPE, *PDAC_ERR_TYPE; + +// DAC data input method +typedef enum _DAC_INPUT_TYPE_{ + DAC_INPUT_SINGLE_WR = 0x1, //DAC input by using single register write + DAC_INPUT_DMA_ONEBLK = 0x2, //DAC input by using single DMA block + DAC_INPUT_DMA_LLP = 0x3, //DAC input by using DMA linked list mode +}DAC_INPUT_TYPE,*PDAC_INPUT_TYPE; + + + + +//====================================================== +// DAC HAL initial data structure +typedef struct _HAL_DAC_INIT_DAT_ { + u8 DACIdx; //DAC index used + u8 DACEn; //DAC module enable + u8 DACDataRate; //DAC data rate, 1'b0:10KHz, 1'b1:250KHz + u8 DACEndian; //DAC endian selection, + //but actually it's for 32-bit DAC data swap control + //1'b0: no swap, + //1'b1: swap the upper 16-bit and the lower 16-bit + u8 DACFilterSet; //DAC filter settle + u8 DACBurstSz; //DAC burst size + u8 DACDbgSel; //DAC debug sel + u8 DACDscDbgSel; //DAC debug dsc sel + + u8 DACBPDsc; //DAC bypass delta sigma for loopback + u8 DACDeltaSig; //DAC bypass value of delta sigma + u16 RSVD1; + + + + u32 *DACData; //DAC data pointer + u32 DACPWCtrl; //DAC0 and DAC1 power control + u32 DACAnaCtrl0; //DAC anapar_da control 0 + u32 DACAnaCtrl1; //DAC anapar_da control 1 + u32 DACIntrMSK; //DAC Interrupt Mask +}HAL_DAC_INIT_DAT,*PHAL_DAC_INIT_DAT; + +// DAC HAL Operations +typedef struct _HAL_DAC_OP_ { + RTK_STATUS (*HalDACInit) (VOID *Data); //HAL DAC initialization + RTK_STATUS (*HalDACDeInit) (VOID *Data); //HAL DAC de-initialization + RTK_STATUS (*HalDACEnable) (VOID *Data); //HAL DAC de-initialization + u8 (*HalDACSend) (VOID *Data); //HAL DAC receive + RTK_STATUS (*HalDACIntrCtrl) (VOID *Data); //HAL DAC interrupt control + u32 (*HalDACReadReg) (VOID *Data, u8 DACReg);//HAL DAC read register +}HAL_DAC_OP, *PHAL_DAC_OP; + +// DAC user callback adapter +typedef struct _SAL_DAC_USERCB_ADPT_ { + VOID (*USERCB) (VOID *Data); + u32 USERData; +}SAL_DAC_USERCB_ADPT, *PSAL_DAC_USERCB_ADPT; + +// DAC user callback structure +typedef struct _SAL_DAC_USER_CB_ { + PSAL_DAC_USERCB_ADPT pTXCB; //DAC Transmit Callback + PSAL_DAC_USERCB_ADPT pTXCCB; //DAC Transmit Complete Callback + PSAL_DAC_USERCB_ADPT pRXCB; //DAC Receive Callback + PSAL_DAC_USERCB_ADPT pRXCCB; //DAC Receive Complete Callback + PSAL_DAC_USERCB_ADPT pRDREQCB; //DAC Read Request Callback + PSAL_DAC_USERCB_ADPT pERRCB; //DAC Error Callback + PSAL_DAC_USERCB_ADPT pDMATXCB; //DAC DMA Transmit Callback + PSAL_DAC_USERCB_ADPT pDMATXCCB; //DAC DMA Transmit Complete Callback + PSAL_DAC_USERCB_ADPT pDMARXCB; //DAC DMA Receive Callback + PSAL_DAC_USERCB_ADPT pDMARXCCB; //DAC DMA Receive Complete Callback +}SAL_DAC_USER_CB, *PSAL_DAC_USER_CB; + +// DAC Transmit Buffer +typedef struct _SAL_DAC_TRANSFER_BUF_ { + u32 DataLen; //DAC Transmfer Length + u32 *pDataBuf; //DAC Transfer Buffer Pointer + u32 RSVD; // +}SAL_DAC_TRANSFER_BUF,*PSAL_DAC_TRANSFER_BUF; + +typedef struct _SAL_DAC_DMA_USER_DEF_ { + + u8 TxDatSrcWdth; + u8 TxDatDstWdth; + u8 TxDatSrcBstSz; + u8 TxDatDstBstSz; + + u8 TxChNo; + u8 LlpCtrl; + u16 RSVD0; + + u32 MaxMultiBlk; + u32 pLlix; + u32 pBlockSizeList; +}SAL_DAC_DMA_USER_DEF, *PSAL_DAC_DMA_USER_DEF; + +// Software API Level DAC Handler +typedef struct _SAL_DAC_HND_ { + u8 DevNum; //DAC device number + u8 PinMux; //DAC pin mux seletion + u8 OpType; //DAC operation type selection + volatile u8 DevSts; //DAC device status + + u8 DACInType; //DAC input type + u8 RSVD0; + u16 RSVD1; + + u32 DACExd; //DAC extended options: + //bit 0: example + //bit 31~bit 1: Reserved + u32 ErrType; // + u32 TimeOut; //DAC IO Timeout count + + PHAL_DAC_INIT_DAT pInitDat; //Pointer to DAC initial data struct + PSAL_DAC_TRANSFER_BUF pTXBuf; //Pointer to DAC TX buffer + PSAL_DAC_USER_CB pUserCB; //Pointer to DAC User Callback + PSAL_DAC_DMA_USER_DEF pDMAConf; //Pointer to DAC User Define DMA Config +}SAL_DAC_HND, *PSAL_DAC_HND; + +// DAC SAL handle private +typedef struct _SAL_DAC_HND_PRIV_ { + VOID **ppSalDACHnd; //Pointer to SAL_DAC_HND pointer + SAL_DAC_HND SalDACHndPriv; //Private SAL_DAC_HND +}SAL_DAC_HND_PRIV, *PSAL_DAC_HND_PRIV; + +//DAC SAL management adapter +typedef struct _SAL_DAC_MNGT_ADPT_ { + PSAL_DAC_HND_PRIV pSalHndPriv; //Pointer to SAL_DAC_HND + PHAL_DAC_INIT_DAT pHalInitDat; //Pointer to HAL DAC initial data( HAL_I2C_INIT_DAT ) + PHAL_DAC_OP pHalOp; //Pointer to HAL DAC operation( HAL_DAC_OP ) + VOID (*pHalOpInit)(VOID*); //Pointer to HAL DAC initialize function + PIRQ_HANDLE pIrqHnd; //Pointer to IRQ handler in SAL layer( IRQ_HANDLE ) + PSAL_DAC_USER_CB pUserCB; //Pointer to SAL user callbacks (SAL_DAC_USER_CB ) + VOID (*pSalIrqFunc)(VOID*); //Used for SAL DAC interrupt function + + PSAL_DAC_DMA_USER_DEF pDMAConf; //Pointer to DAC User Define DMA config + PHAL_GDMA_ADAPTER pHalGdmaAdp; + PHAL_GDMA_OP pHalGdmaOp; + VOID (*pHalGdmaOpInit)(VOID*); //Pointer to HAL DAC initialize function + PIRQ_HANDLE pIrqGdmaHnd; + VOID (*pSalDMAIrqFunc)(VOID*); //Used for SAL DAC interrupt function +}SAL_DAC_MNGT_ADPT, *PSAL_DAC_MNGT_ADPT; + + +//================ DAC HAL Function Prototype =================== +// DAC HAL inline function +// For checking DAC input index valid or not +static inline RTK_STATUS +RtkDACIdxChk( + IN u8 DACIdx +) +{ +#if !DAC0_USED + if (DACIdx == DAC0_SEL) + return _EXIT_FAILURE; +#endif + +#if !DAC1_USED + if (DACIdx == DAC1_SEL) + return _EXIT_FAILURE; +#endif + + return _EXIT_SUCCESS; +} + +VOID HalDACOpInit(IN VOID *Data); +RTK_STATUS RtkDACLoadDefault(IN VOID *Data); +RTK_STATUS RtkDACInit(IN VOID *Data); +RTK_STATUS RtkDACDeInit(IN VOID *Data); +RTK_STATUS RtkDACSend(IN VOID *Data); +PSAL_DAC_HND RtkDACGetSalHnd(IN u8 DACIdx); +RTK_STATUS RtkDACFreeSalHnd(IN PSAL_DAC_HND pSalDACHND); +PSAL_DAC_MNGT_ADPT RtkDACGetMngtAdpt(IN u8 DACIdx); +RTK_STATUS RtkDACFreeMngtAdpt(IN PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt); + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_diag.h b/component/soc/realtek/8195a/fwlib/hal_diag.h new file mode 100644 index 0000000..61ad49e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_diag.h @@ -0,0 +1,107 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_DIAG_H_ +#define _HAL_DIAG_H_ + + +//Register offset +#define UART_REV_BUF_OFF 0x00 +#define UART_TRAN_HOLD_OFF 0x00 +#define UART_DLH_OFF 0x04 +#define UART_DLL_OFF 0x00 +#define UART_INTERRUPT_EN_REG_OFF 0x04 +#define UART_INTERRUPT_IDEN_REG_OFF 0x08 +#define UART_FIFO_CTL_REG_OFF 0x08 +#define UART_LINE_CTL_REG_OFF 0x0c +#define UART_MODEM_CTL_REG_OFF 0x10 +#define UART_LINE_STATUS_REG_OFF 0x14 +#define UART_MODEM_STATUS_REG_OFF 0x18 +#define UART_FIFO_ACCESS_REG_OFF 0x70 +#define UART_STATUS_REG_OFF 0x7c +#define UART_TFL_OFF 0x80 +#define UART_RFL_OFF 0x84 + + +//Buad rate +#define UART_BAUD_RATE_2400 2400 +#define UART_BAUD_RATE_4800 4800 +#define UART_BAUD_RATE_9600 9600 +#define UART_BAUD_RATE_19200 19200 +#define UART_BAUD_RATE_38400 38400 +#define UART_BAUD_RATE_57600 57600 +#define UART_BAUD_RATE_115200 115200 +#define UART_BAUD_RATE_921600 921600 +#define UART_BAUD_RATE_1152000 1152000 + +#define UART_PARITY_ENABLE 0x08 +#define UART_PARITY_DISABLE 0 + +#define UART_DATA_LEN_5BIT 0x0 +#define UART_DATA_LEN_6BIT 0x1 +#define UART_DATA_LEN_7BIT 0x2 +#define UART_DATA_LEN_8BIT 0x3 + +#define UART_STOP_1BIT 0x0 +#define UART_STOP_2BIT 0x4 + + +#define HAL_UART_READ32(addr) HAL_READ32(LOG_UART_REG_BASE, addr) +#define HAL_UART_WRITE32(addr, value) HAL_WRITE32(LOG_UART_REG_BASE, addr, value) +#define HAL_UART_READ16(addr) HAL_READ16(LOG_UART_REG_BASE, addr) +#define HAL_UART_WRITE16(addr, value) HAL_WRITE16(LOG_UART_REG_BASE, addr, value) +#define HAL_UART_READ8(addr) HAL_READ8(LOG_UART_REG_BASE, addr) +#define HAL_UART_WRITE8(addr, value) HAL_WRITE8(LOG_UART_REG_BASE, addr, value) + +typedef struct _LOG_UART_ADAPTER_ { + u32 BaudRate; + u32 FIFOControl; + u32 IntEnReg; + u8 Parity; + u8 Stop; + u8 DataLength; +}LOG_UART_ADAPTER, *PLOG_UART_ADAPTER; + +typedef struct _COMMAND_TABLE_ { + const u8* cmd; + u16 ArgvCnt; + u32 (*func)(u16 argc, u8* argv[]); + const u8* msg; +}COMMAND_TABLE, *PCOMMAND_TABLE; + +//VOID +//HalLogUartHandle(void); + + +extern _LONG_CALL_ u32 +HalLogUartInit( + IN LOG_UART_ADAPTER UartAdapter + ); + + +extern _LONG_CALL_ VOID +HalSerialPutcRtl8195a( + IN u8 c + ); + +extern _LONG_CALL_ u8 +HalSerialGetcRtl8195a( + IN BOOL PullMode + ); + +extern _LONG_CALL_ u32 +HalSerialGetIsrEnRegRtl8195a(VOID); + +extern _LONG_CALL_ VOID +HalSerialSetIrqEnRegRtl8195a ( + IN u32 SetValue +); + + +#endif//_HAL_DIAG_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_efuse.h b/component/soc/realtek/8195a/fwlib/hal_efuse.h new file mode 100644 index 0000000..047fada --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_efuse.h @@ -0,0 +1,28 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_EFUSE_H_ +#define _HAL_EFUSE_H_ + +_LONG_CALL_ extern VOID HalEFUSEPowerSwitch8195AROM(IN u8 bWrite, IN u8 PwrState, IN u8 L25OutVoltage); +_LONG_CALL_ extern u32 HALEFUSEOneByteReadROM(IN u32 CtrlSetting, IN u16 Addr, OUT u8 *Data, IN u8 L25OutVoltage); +_LONG_CALL_ extern u32 HALEFUSEOneByteWriteROM(IN u32 CtrlSetting, IN u16 Addr, IN u8 Data, IN u8 L25OutVoltage); + +#define EFUSERead8 HALEFUSEOneByteReadROM +#define EFUSEWrite8 HALEFUSEOneByteWriteROM + +#define L25EOUTVOLTAGE 7 + +VOID HalEFUSEOpInit( + IN VOID *Data +); + + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_gdma.h b/component/soc/realtek/8195a/fwlib/hal_gdma.h new file mode 100644 index 0000000..70d254a --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_gdma.h @@ -0,0 +1,127 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_GDMA_H_ +#define _HAL_GDMA_H_ + +#include "rtl8195a_gdma.h" + +typedef struct _GDMA_CH_LLI_ELE_ { + u32 Sarx; + u32 Darx; + u32 Llpx; + u32 CtlxLow; + u32 CtlxUp; + u32 Temp; +}GDMA_CH_LLI_ELE, *PGDMA_CH_LLI_ELE; +#if 1 +#if 0 +typedef struct _GDMA_CH_LLI_ { + PGDMA_CH_LLI_ELE pLliEle; + PGDMA_CH_LLI pNextLli; +}GDMA_CH_LLI, *PGDMA_CH_LLI; + +typedef struct _BLOCK_SIZE_LIST_ { + u32 BlockSize; + PBLOCK_SIZE_LIST pNextBlockSiz; +}BLOCK_SIZE_LIST, *PBLOCK_SIZE_LIST; +#else +struct GDMA_CH_LLI { + PGDMA_CH_LLI_ELE pLliEle; + struct GDMA_CH_LLI *pNextLli; +}; + +struct BLOCK_SIZE_LIST { + u32 BlockSize; + struct BLOCK_SIZE_LIST *pNextBlockSiz; +}; + +#endif + +#endif +typedef struct _HAL_GDMA_ADAPTER_ { + u32 ChSar; + u32 ChDar; + GDMA_CHANNEL_NUM ChEn; + GDMA_CTL_REG GdmaCtl; + GDMA_CFG_REG GdmaCfg; + u32 PacketLen; + u32 BlockLen; + u32 MuliBlockCunt; + u32 MaxMuliBlock; + struct GDMA_CH_LLI *pLlix; + struct BLOCK_SIZE_LIST *pBlockSizeList; + + PGDMA_CH_LLI_ELE pLli; + u32 NextPlli; + u8 TestItem; + u8 ChNum; + u8 GdmaIndex; + u8 IsrCtrl:1; + u8 GdmaOnOff:1; + u8 Llpctrl:1; + u8 Lli0:1; + u8 Rsvd4to7:4; + u8 GdmaIsrType; +}HAL_GDMA_ADAPTER, *PHAL_GDMA_ADAPTER; + +typedef struct _HAL_GDMA_CHNL_ { + u8 GdmaIndx; + u8 GdmaChnl; + u8 IrqNum; + u8 Reserved; +}HAL_GDMA_CHNL, *PHAL_GDMA_CHNL; + + +typedef struct _HAL_GDMA_OP_ { + VOID (*HalGdmaOnOff)(VOID *Data); + BOOL (*HalGdamChInit)(VOID *Data); + BOOL (*HalGdmaChSeting)(VOID *Data); + BOOL (*HalGdmaChBlockSeting)(VOID *Data); + VOID (*HalGdmaChDis)(VOID *Data); + VOID (*HalGdmaChEn)(VOID *Data); + VOID (*HalGdmaChIsrEnAndDis) (VOID *Data); + u8 (*HalGdmaChIsrClean)(VOID *Data); + VOID (*HalGdmaChCleanAutoSrc)(VOID *Data); + VOID (*HalGdmaChCleanAutoDst)(VOID *Data); +}HAL_GDMA_OP, *PHAL_GDMA_OP; + +typedef struct _HAL_GDMA_OBJ_ { + HAL_GDMA_ADAPTER HalGdmaAdapter; + IRQ_HANDLE GdmaIrqHandle; + u8 Busy; // is transfering +} HAL_GDMA_OBJ, *PHAL_GDMA_OBJ; + +VOID HalGdmaOpInit(IN VOID *Data); +VOID HalGdmaOn(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaOff(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +BOOL HalGdmaChInit(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChDis(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChEn(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +BOOL HalGdmaChSeting(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +BOOL HalGdmaChBlockSeting(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChIsrEn(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChIsrDis(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +u8 HalGdmaChIsrClean(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChCleanAutoSrc(PHAL_GDMA_ADAPTER pHalGdmaAdapter); +VOID HalGdmaChCleanAutoDst(PHAL_GDMA_ADAPTER pHalGdmaAdapter); + +extern HAL_Status HalGdmaChnlRegister (u8 GdmaIdx, u8 ChnlNum); +extern VOID HalGdmaChnlUnRegister (u8 GdmaIdx, u8 ChnlNum); +extern PHAL_GDMA_CHNL HalGdmaChnlAlloc (HAL_GDMA_CHNL *pChnlOption); +extern VOID HalGdmaChnlFree (HAL_GDMA_CHNL *pChnl); +extern BOOL HalGdmaMemCpyInit(PHAL_GDMA_OBJ pHalGdmaObj); +extern VOID HalGdmaMemCpyDeInit(PHAL_GDMA_OBJ pHalGdmaObj); +extern VOID* HalGdmaMemCpy(PHAL_GDMA_OBJ pHalGdmaObj, void* pDest, void* pSrc, u32 len); + +extern const HAL_GDMA_OP _HalGdmaOp; +extern const HAL_GDMA_CHNL GDMA_Chnl_Option[]; +extern const u16 HalGdmaChnlEn[6]; + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_gpio.h b/component/soc/realtek/8195a/fwlib/hal_gpio.h new file mode 100644 index 0000000..3046336 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_gpio.h @@ -0,0 +1,236 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_GPIO_H_ +#define _HAL_GPIO_H_ + +#define HAL_GPIO_PIN_INT_MODE 0x80 + +typedef enum { + _PORT_A = 0, + _PORT_B = 1, + _PORT_C = 2, + _PORT_D = 3, + _PORT_E = 4, + _PORT_F = 5, + _PORT_G = 6, + _PORT_H = 7, + _PORT_I = 8, + _PORT_J = 9, + _PORT_K = 10, + + _PORT_MAX +} HAL_GPIO_PORT_NAME; + +typedef enum { + _PA_0 = (_PORT_A<<4|0), + _PA_1 = (_PORT_A<<4|1), + _PA_2 = (_PORT_A<<4|2), + _PA_3 = (_PORT_A<<4|3), + _PA_4 = (_PORT_A<<4|4), + _PA_5 = (_PORT_A<<4|5), + _PA_6 = (_PORT_A<<4|6), + _PA_7 = (_PORT_A<<4|7), + + _PB_0 = (_PORT_B<<4|0), + _PB_1 = (_PORT_B<<4|1), + _PB_2 = (_PORT_B<<4|2), + _PB_3 = (_PORT_B<<4|3), + _PB_4 = (_PORT_B<<4|4), + _PB_5 = (_PORT_B<<4|5), + _PB_6 = (_PORT_B<<4|6), + _PB_7 = (_PORT_B<<4|7), + + _PC_0 = (_PORT_C<<4|0), + _PC_1 = (_PORT_C<<4|1), + _PC_2 = (_PORT_C<<4|2), + _PC_3 = (_PORT_C<<4|3), + _PC_4 = (_PORT_C<<4|4), + _PC_5 = (_PORT_C<<4|5), + _PC_6 = (_PORT_C<<4|6), + _PC_7 = (_PORT_C<<4|7), + _PC_8 = (_PORT_C<<4|8), + _PC_9 = (_PORT_C<<4|9), + + _PD_0 = (_PORT_D<<4|0), + _PD_1 = (_PORT_D<<4|1), + _PD_2 = (_PORT_D<<4|2), + _PD_3 = (_PORT_D<<4|3), + _PD_4 = (_PORT_D<<4|4), + _PD_5 = (_PORT_D<<4|5), + _PD_6 = (_PORT_D<<4|6), + _PD_7 = (_PORT_D<<4|7), + _PD_8 = (_PORT_D<<4|8), + _PD_9 = (_PORT_D<<4|9), + + _PE_0 = (_PORT_E<<4|0), + _PE_1 = (_PORT_E<<4|1), + _PE_2 = (_PORT_E<<4|2), + _PE_3 = (_PORT_E<<4|3), + _PE_4 = (_PORT_E<<4|4), + _PE_5 = (_PORT_E<<4|5), + _PE_6 = (_PORT_E<<4|6), + _PE_7 = (_PORT_E<<4|7), + _PE_8 = (_PORT_E<<4|8), + _PE_9 = (_PORT_E<<4|9), + _PE_A = (_PORT_E<<4|10), + + _PF_0 = (_PORT_F<<4|0), + _PF_1 = (_PORT_F<<4|1), + _PF_2 = (_PORT_F<<4|2), + _PF_3 = (_PORT_F<<4|3), + _PF_4 = (_PORT_F<<4|4), + _PF_5 = (_PORT_F<<4|5), +// _PF_6 = (_PORT_F<<4|6), +// _PF_7 = (_PORT_F<<4|7), + + _PG_0 = (_PORT_G<<4|0), + _PG_1 = (_PORT_G<<4|1), + _PG_2 = (_PORT_G<<4|2), + _PG_3 = (_PORT_G<<4|3), + _PG_4 = (_PORT_G<<4|4), + _PG_5 = (_PORT_G<<4|5), + _PG_6 = (_PORT_G<<4|6), + _PG_7 = (_PORT_G<<4|7), + + _PH_0 = (_PORT_H<<4|0), + _PH_1 = (_PORT_H<<4|1), + _PH_2 = (_PORT_H<<4|2), + _PH_3 = (_PORT_H<<4|3), + _PH_4 = (_PORT_H<<4|4), + _PH_5 = (_PORT_H<<4|5), + _PH_6 = (_PORT_H<<4|6), + _PH_7 = (_PORT_H<<4|7), + + _PI_0 = (_PORT_I<<4|0), + _PI_1 = (_PORT_I<<4|1), + _PI_2 = (_PORT_I<<4|2), + _PI_3 = (_PORT_I<<4|3), + _PI_4 = (_PORT_I<<4|4), + _PI_5 = (_PORT_I<<4|5), + _PI_6 = (_PORT_I<<4|6), + _PI_7 = (_PORT_I<<4|7), + + _PJ_0 = (_PORT_J<<4|0), + _PJ_1 = (_PORT_J<<4|1), + _PJ_2 = (_PORT_J<<4|2), + _PJ_3 = (_PORT_J<<4|3), + _PJ_4 = (_PORT_J<<4|4), + _PJ_5 = (_PORT_J<<4|5), + _PJ_6 = (_PORT_J<<4|6), +// _PJ_7 = (_PORT_J<<4|7), + + _PK_0 = (_PORT_K<<4|0), + _PK_1 = (_PORT_K<<4|1), + _PK_2 = (_PORT_K<<4|2), + _PK_3 = (_PORT_K<<4|3), + _PK_4 = (_PORT_K<<4|4), + _PK_5 = (_PORT_K<<4|5), + _PK_6 = (_PORT_K<<4|6), +// _PK_7 = (_PORT_K<<4|7), + + // Not connected + _PIN_NC = (int)0xFFFFFFFF +} HAL_PIN_NAME; + +typedef enum +{ + GPIO_PIN_LOW = 0, + GPIO_PIN_HIGH = 1, + GPIO_PIN_ERR = 2 // read Pin error +} HAL_GPIO_PIN_STATE; + +typedef enum { + DIN_PULL_NONE = 0, //floating or high impedance ? + DIN_PULL_LOW = 1, + DIN_PULL_HIGH = 2, + + DOUT_PUSH_PULL = 3, + DOUT_OPEN_DRAIN = 4, + + INT_LOW = (5|HAL_GPIO_PIN_INT_MODE), // Interrupt Low level trigger + INT_HIGH = (6|HAL_GPIO_PIN_INT_MODE), // Interrupt High level trigger + INT_FALLING = (7|HAL_GPIO_PIN_INT_MODE), // Interrupt Falling edge trigger + INT_RISING = (8|HAL_GPIO_PIN_INT_MODE) // Interrupt Rising edge trigger +} HAL_GPIO_PIN_MODE; + +enum { + GPIO_PORT_A = 0, + GPIO_PORT_B = 1, + GPIO_PORT_C = 2, + GPIO_PORT_D = 3 +}; + +typedef enum { + hal_PullNone = 0, + hal_PullUp = 1, + hal_PullDown = 2, + hal_OpenDrain = 3, + hal_PullDefault = hal_PullNone +} HAL_PinMode; + +typedef struct _HAL_GPIO_PORT_ { + u32 out_data; // to write the GPIO port + u32 in_data; // to read the GPIO port + u32 dir; // config each pin direction +}HAL_GPIO_PORT, *PHAL_GPIO_PORT; + +#define HAL_GPIO_PIN_NAME(port,pin) (((port)<<5)|(pin)) +#define HAL_GPIO_GET_PORT_BY_NAME(x) ((x>>5) & 0x03) +#define HAL_GPIO_GET_PIN_BY_NAME(x) (x & 0x1f) + +typedef struct _HAL_GPIO_PIN_ { + HAL_GPIO_PIN_MODE pin_mode; + u32 pin_name; // Pin: [7:5]: port number, [4:0]: pin number +}HAL_GPIO_PIN, *PHAL_GPIO_PIN; + +typedef struct _HAL_GPIO_OP_ { +#if defined(__ICCARM__) + void* dummy; +#endif +}HAL_GPIO_OP, *PHAL_GPIO_OP; + +typedef void (*GPIO_IRQ_FUN)(VOID *Data, u32 Id); +typedef void (*GPIO_USER_IRQ_FUN)(u32 Id); + +typedef struct _HAL_GPIO_ADAPTER_ { + IRQ_HANDLE IrqHandle; // GPIO HAL IRQ Handle + GPIO_USER_IRQ_FUN UserIrqHandler; // GPIO IRQ Handler + GPIO_IRQ_FUN PortA_IrqHandler[32]; // The interrupt handler triggered by Port A[x] + VOID *PortA_IrqData[32]; + VOID (*EnterCritical)(void); + VOID (*ExitCritical)(void); + u32 Local_Gpio_Dir[3]; // to record direction setting: 0- IN, 1- Out + u8 Gpio_Func_En; // Is GPIO HW function enabled ? + u8 Locked; +}HAL_GPIO_ADAPTER, *PHAL_GPIO_ADAPTER; + +u32 +HAL_GPIO_GetPinName( + u32 chip_pin +); + +VOID +HAL_GPIO_PullCtrl( + u32 pin, + u32 mode +); + +VOID +HAL_GPIO_Init( + HAL_GPIO_PIN *GPIO_Pin +); + +VOID +HAL_GPIO_Irq_Init( + HAL_GPIO_PIN *GPIO_Pin +); + +#endif // end of "#define _HAL_GPIO_H_" + diff --git a/component/soc/realtek/8195a/fwlib/hal_i2c.h b/component/soc/realtek/8195a/fwlib/hal_i2c.h new file mode 100644 index 0000000..13996c0 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_i2c.h @@ -0,0 +1,565 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_I2C_H_ //#ifndef _HAL_I2C_H_ +#define _HAL_I2C_H_ + +#include "rtl8195a_i2c.h" +#include "hal_gdma.h" + +//================= I2C CONFIGURATION START ================== +// I2C SAL User Configuration Flags + +// I2C SAL operation types +#define I2C_POLL_OP_TYPE 1 +#define I2C_INTR_OP_TYPE 1 +#define I2C_DMA_OP_TYPE 1 + +// I2C supports user register address +#define I2C_USER_REG_ADDR 1 //I2C User specific register address by using + //the first I2C data as the register + //address + +// I2C SAL used module. Please set the I2C module flag to 1 to enable the related +// I2C module functions. +#define I2C0_USED 1 +#define I2C1_USED 1 +#define I2C2_USED 1 +#define I2C3_USED 1 +//================= I2C CONFIGURATION END =================== + + +//================= I2C HAL START ========================== +// I2C debug output +#define I2C_PREFIX "RTL8195A[i2c]: " +#define I2C_PREFIX_LVL " [i2c_DBG]: " + +typedef enum _I2C_DBG_LVL_ { + HAL_I2C_LVL = 0x01, + SAL_I2C_LVL = 0x02, + VERI_I2C_LVL = 0x03, +}I2C_DBG_LVL,*PI2C_DBG_LVL; + +#ifdef CONFIG_DEBUG_LOG +#ifdef CONFIG_DEBUG_LOG_I2C_HAL +#define DBG_I2C_LOG_PERD 100 + + #define I2CDBGLVL 0xFF + #define DBG_8195A_I2C(...) do{ \ + _DbgDump("\r"I2C_PREFIX __VA_ARGS__);\ + }while(0) + + #define DBG_8195A_I2C_LVL(LVL,...) do{\ + if (LVL&I2CDBGLVL){\ + _DbgDump("\r"I2C_PREFIX_LVL __VA_ARGS__);\ + }\ + }while(0) +#else + #define DBG_I2C_LOG_PERD 100 + #define DBG_8195A_I2C(...) + #define DBG_8195A_I2C_LVL(...) +#endif +#else + #define DBG_I2C_LOG_PERD 100 + #define DBG_8195A_I2C(...) + #define DBG_8195A_I2C_LVL(...) +#endif + +//====================================================== +// I2C HAL related enumeration +// I2C Module Selection +typedef enum _I2C_MODULE_SEL_ { + I2C0_SEL = 0x0, + I2C1_SEL = 0x1, + I2C2_SEL = 0x2, + I2C3_SEL = 0x3, +}I2C_MODULE_SEL,*PI2C_MODULE_SEL; + +// I2C HAL initial data structure +typedef struct _HAL_I2C_INIT_DAT_ { + u8 I2CIdx; //I2C index used + u8 I2CEn; //I2C module enable + u8 I2CMaster; //Master or Slave mode + u8 I2CAddrMod; //I2C addressing mode(7-bit, 10-bit) + + u8 I2CSpdMod; //I2C speed mode(Standard, Fast, High) + u8 I2CSetup; //I2C SDA setup time + u8 I2CRXTL; //I2C RX FIFO Threshold + u8 I2CTXTL; //I2C TX FIFO Threshold + + u8 I2CBusLd; //I2C bus load (pf) for high speed mode + u8 I2CReSTR; //I2C restart support + u8 I2CGC; //I2C general support + u8 I2CStartB; //I2C start byte support + + u8 I2CSlvNoAck; //I2C slave no ack support + u8 I2CDMACtrl; //I2C DMA feature support + u8 I2CCmd; //I2C Command + u8 I2CDataLen; //I2C Data Length + + u8 I2CSlvAckGC; //I2C slave acks to General Call + u8 I2CStop; //I2C issues STOP bit or not + u16 RSVD0; + + u8 *I2CRWData; //I2C Read/Write data pointer + + u16 I2CIntrMSK; //I2C Interrupt Mask + u16 I2CIntrClr; //I2C Interrupt register to clear + + u16 I2CAckAddr; //I2C target address in I2C Master mode, + //ack address in I2C Slave mode + u16 I2CSdaHd; //I2C SDA hold time + + u32 I2CClk; //I2C bus clock (in kHz) + + u8 I2CTxDMARqLv; //I2C TX DMA Empty Level + u8 I2CRxDMARqLv; //I2C RX DMA Full Level + u16 RSVD1; //Reserved +}HAL_I2C_INIT_DAT,*PHAL_I2C_INIT_DAT; + +// I2C HAL Operations +typedef struct _HAL_I2C_OP_ { + HAL_Status (*HalI2CInit) (VOID *Data); //HAL I2C initialization + HAL_Status (*HalI2CDeInit) (VOID *Data); //HAL I2C de-initialization + HAL_Status (*HalI2CSend) (VOID *Data); //HAL I2C send + u8 (*HalI2CReceive) (VOID *Data); //HAL I2C receive + HAL_Status (*HalI2CEnable) (VOID *Data); //HAL I2C enable module + HAL_Status (*HalI2CIntrCtrl) (VOID *Data); //HAL I2C interrupt control + u32 (*HalI2CReadReg) (VOID *Data, u8 I2CReg);//HAL I2C read register + HAL_Status (*HalI2CWriteReg) (VOID *Data, u8 I2CReg, u32 RegVal);//HAL I2C write register + HAL_Status (*HalI2CSetCLK) (VOID *Data); //HAL I2C set bus clock + HAL_Status (*HalI2CMassSend) (VOID *Data); //HAL I2C mass send + HAL_Status (*HalI2CClrIntr) (VOID *Data); //HAL I2C clear interrupts + HAL_Status (*HalI2CClrAllIntr) (VOID *Data); //HAL I2C clear all interrupts + HAL_Status (*HalI2CDMACtrl) (VOID *Data); //HAL I2C DMA control +}HAL_I2C_OP, *PHAL_I2C_OP; +//================= I2C HAL END =========================== + + +//================= I2C SAL START ========================== +//I2C SAL Macros + +//====================================================== +// I2C SAL related enumerations +// I2C Extend Features +typedef enum _I2C_EXD_SUPPORT_{ + I2C_EXD_RESTART = 0x1, //BIT_0, RESTART bit + I2C_EXD_GENCALL = 0x2, //BIT_1, Master generates General Call. All "send" operations generate General Call addresss + I2C_EXD_STARTB = 0x4, //BIT_2, Using START BYTE, instead of START Bit + I2C_EXD_SLVNOACK = 0x8, //BIT_3, Slave no ack to master + I2C_EXD_BUS400PF = 0x10, //BIT_4, I2C bus loading is 400pf + I2C_EXD_SLVACKGC = 0x20, //BIT_5, Slave acks to a General Call + I2C_EXD_USER_REG = 0x40, //BIT_6, Using User Register Address + I2C_EXD_USER_TWOB = 0x80, //BIT_7, User Register Address is 2-byte + I2C_EXD_MTR_ADDR_RTY= 0x100, //BIT_8, Master retries to send start condition and Slave address when the slave doesn't ack + // the address. + I2C_EXD_MTR_ADDR_UPD= 0x200, //BIT_9, Master dynamically updates slave address + I2C_EXD_MTR_HOLD_BUS= 0x400, //BIT_10, Master doesn't generate STOP when the FIFO is empty. This would make Master hold + // the bus. +}I2C_EXD_SUPPORT,*PI2C_EXD_SUPPORT; + +// I2C operation type +typedef enum _I2C_OP_TYPE_ { + I2C_POLL_TYPE = 0x0, + I2C_DMA_TYPE = 0x1, + I2C_INTR_TYPE = 0x2, +}I2C_OP_TYPE, *PI2C_OP_TYPE; + +// I2C pinmux selection +typedef enum _I2C_PINMUX_ { + I2C_PIN_S0 = 0x0, + I2C_PIN_S1 = 0x1, + I2C_PIN_S2 = 0x2, + I2C_PIN_S3 = 0x3, //Only valid for I2C0 and I2C3 +}I2C_PINMUX, *PI2C_PINMUX; + +// I2C module status +typedef enum _I2C_MODULE_STATUS_ { + I2C_DISABLE = 0x0, + I2C_ENABLE = 0x1, +}I2C_MODULE_STATUS, *PI2C_MODULE_STATUS; + +// I2C device status +typedef enum _I2C_Device_STATUS_ { + I2C_STS_UNINITIAL = 0x00, + I2C_STS_INITIALIZED = 0x01, + I2C_STS_IDLE = 0x02, + + I2C_STS_TX_READY = 0x03, + I2C_STS_TX_ING = 0x04, + + I2C_STS_RX_READY = 0x05, + I2C_STS_RX_ING = 0x06, + + I2C_STS_ERROR = 0x10, + I2C_STS_TIMEOUT = 0x11, +}I2C_Device_STATUS, *PI2C_Device_STATUS; + +// I2C feature status +typedef enum _I2C_FEATURE_STATUS_{ + I2C_FEATURE_DISABLED = 0, + I2C_FEATURE_ENABLED = 1, +}I2C_FEATURE_STATUS,*PI2C_FEATURE_STATUS; + +// I2C device mode +typedef enum _I2C_DEV_MODE_ { + I2C_SLAVE_MODE = 0x0, + I2C_MASTER_MODE = 0x1, +}I2C_DEV_MODE, *PI2C_DEV_MODE; + +// I2C Bus Transmit/Receive +typedef enum _I2C_DIRECTION_ { + I2C_ONLY_TX = 0x1, + I2C_ONLY_RX = 0x2, + I2C_TXRX = 0x3, +}I2C_DIRECTION, *PI2C_DIRECTION; + +//I2C DMA module number +typedef enum _I2C_DMA_MODULE_SEL_ { + I2C_DMA_MODULE_0 = 0x0, + I2C_DMA_MODULE_1 = 0x1 +}I2C_DMA_MODULE_SEL, *PI2C_DMA_MODULE_SEL; + +// I2C0 DMA peripheral number +typedef enum _I2C0_DMA_PERI_NUM_ { + I2C0_DMA_TX_NUM = 0x8, + I2C0_DMA_RX_NUM = 0x9, +}I2C0_DMA_PERI_NUM,*PI2C0_DMA_PERI_NUM; + +// I2C1 DMA peripheral number +typedef enum _I2C1_DMA_PERI_NUM_ { + I2C1_DMA_TX_NUM = 0xA, + I2C1_DMA_RX_NUM = 0xB, +}I2C1_DMA_PERI_NUM,*PI2C1_DMA_PERI_NUM; + +// I2C0 DMA module used +typedef enum _I2C0_DMA_MODULE_ { + I2C0_DMA0 = 0x0, + I2C0_DMA1 = 0x1, +}I2C0_DMA_MODULE,*PI2C0_DMA_MODULE; + +// I2C0 DMA module used +typedef enum _I2C1_DMA_MODULE_ { + I2C1_DMA0 = 0x0, + I2C1_DMA1 = 0x1, +}I2C1_DMA_MODULE,*PI2C1_DMA_MODULE; + +// I2C command type +typedef enum _I2C_COMMAND_TYPE_ { + I2C_WRITE_CMD = 0x0, + I2C_READ_CMD = 0x1, +}I2C_COMMAND_TYPE,*PI2C_COMMAND_TYPE; + +// I2C STOP BIT +typedef enum _I2C_STOP_TYPE_ { + I2C_STOP_DIS = 0x0, + I2C_STOP_EN = 0x1, +}I2C_STOP_TYPE, *PI2C_STOP_TYPE; + +// I2C error type +typedef enum _I2C_ERR_TYPE_ { + I2C_ERR_RX_UNDER = 0x01, //I2C RX FIFO Underflow + I2C_ERR_RX_OVER = 0x02, //I2C RX FIFO Overflow + I2C_ERR_TX_OVER = 0x04, //I2C TX FIFO Overflow + I2C_ERR_TX_ABRT = 0x08, //I2C TX terminated + I2C_ERR_SLV_TX_NACK = 0x10, //I2C slave transmission terminated by master NACK, + //but there are data in slave TX FIFO + I2C_ERR_USER_REG_TO = 0x20, + + I2C_ERR_RX_CMD_TO = 0x21, + I2C_ERR_RX_FF_TO = 0x22, + I2C_ERR_TX_CMD_TO = 0x23, + I2C_ERR_TX_FF_TO = 0x24, + + I2C_ERR_TX_ADD_TO = 0x25, + I2C_ERR_RX_ADD_TO = 0x26, +}I2C_ERR_TYPE, *PI2C_ERR_TYPE; + +// I2C Time Out type +typedef enum _I2C_TIMEOUT_TYPE_ { + I2C_TIMEOOUT_DISABLE = 0x00, + I2C_TIMEOOUT_ENDLESS = 0xFFFFFFFF, +}I2C_TIMEOUT_TYPE, *PI2C_TIMEOUT_TYPE; + +//====================================================== +// SAL I2C related data structures +// I2C user callback adapter +typedef struct _SAL_I2C_USERCB_ADPT_ { + VOID (*USERCB) (VOID *Data); + u32 USERData; +}SAL_I2C_USERCB_ADPT, *PSAL_I2C_USERCB_ADPT; + +// I2C user callback structure +typedef struct _SAL_I2C_USER_CB_ { + PSAL_I2C_USERCB_ADPT pTXCB; //I2C Transmit Callback + PSAL_I2C_USERCB_ADPT pTXCCB; //I2C Transmit Complete Callback + PSAL_I2C_USERCB_ADPT pRXCB; //I2C Receive Callback + PSAL_I2C_USERCB_ADPT pRXCCB; //I2C Receive Complete Callback + PSAL_I2C_USERCB_ADPT pRDREQCB; //I2C Read Request Callback + PSAL_I2C_USERCB_ADPT pERRCB; //I2C Error Callback + PSAL_I2C_USERCB_ADPT pDMATXCB; //I2C DMA Transmit Callback + PSAL_I2C_USERCB_ADPT pDMATXCCB; //I2C DMA Transmit Complete Callback + PSAL_I2C_USERCB_ADPT pDMARXCB; //I2C DMA Receive Callback + PSAL_I2C_USERCB_ADPT pDMARXCCB; //I2C DMA Receive Complete Callback + PSAL_I2C_USERCB_ADPT pGENCALLCB; //I2C General Call Callback +}SAL_I2C_USER_CB, *PSAL_I2C_USER_CB; + +// I2C Transmit Buffer +typedef struct _SAL_I2C_TRANSFER_BUF_ { + u16 DataLen; //I2C Transmfer Length + u16 TargetAddr; //I2C Target Address. It's only valid in Master Mode. + u32 RegAddr; //I2C Register Address. It's only valid in Master Mode. + u32 RSVD; // + u8 *pDataBuf; //I2C Transfer Buffer Pointer +}SAL_I2C_TRANSFER_BUF,*PSAL_I2C_TRANSFER_BUF; + +typedef struct _SAL_I2C_DMA_USER_DEF_ { + u8 TxDatSrcWdth; + u8 TxDatDstWdth; + u8 TxDatSrcBstSz; + u8 TxDatDstBstSz; + u8 TxChNo; + u8 RSVD0; + u16 RSVD1; + u8 RxDatSrcWdth; + u8 RxDatDstWdth; + u8 RxDatSrcBstSz; + u8 RxDatDstBstSz; + u8 RxChNo; + u8 RSVD2; + u16 RSVD3; +}SAL_I2C_DMA_USER_DEF, *PSAL_I2C_DMA_USER_DEF; + +// RTK I2C OP +typedef struct _RTK_I2C_OP_ { + HAL_Status (*Init) (VOID *Data); + HAL_Status (*DeInit) (VOID *Data); + HAL_Status (*Send) (VOID *Data); + HAL_Status (*Receive) (VOID *Data); + HAL_Status (*IoCtrl) (VOID *Data); + HAL_Status (*PowerCtrl) (VOID *Data); +}RTK_I2C_OP, *PRTK_I2C_OP; + +// Software API Level I2C Handler +typedef struct _SAL_I2C_HND_ { + u8 DevNum; //I2C device number + u8 PinMux; //I2C pin mux seletion + u8 OpType; //I2C operation type selection + volatile u8 DevSts; //I2C device status + + u8 I2CMaster; //I2C Master or Slave mode + u8 I2CAddrMod; //I2C 7-bit or 10-bit mode + u8 I2CSpdMod; //I2C SS/ FS/ HS speed mode + u8 I2CAckAddr; //I2C target address in Master + //mode or ack address in Slave + //mode + + u16 I2CClk; //I2C bus clock + u8 MasterRead; //I2C Master Read Supported, + //An Address will be sent before + //read data back. + + u8 I2CDmaSel; //I2C DMA module select + // 0 for DMA0, + // 1 for DMA1 + u8 I2CTxDMARqLv; //I2C TX DMA Empty Level + u8 I2CRxDMARqLv; //I2C RX DMA Full Level + u16 RSVD0; //Reserved + + u32 RSVD1; //Reserved + + u32 I2CExd; //I2C extended options: + //bit 0: I2C RESTART supported, + // 0 for NOT supported, + // 1 for supported + //bit 1: I2C General Call supported + // 0 for NOT supported, + // 1 for supported + //bit 2: I2C START Byte supported + // 0 for NOT supported, + // 1 for supported + //bit 3: I2C Slave-No-Ack + // supported + // 0 for NOT supported, + // 1 for supported + //bit 4: I2C bus loading, + // 0 for 100pf, + // 1 for 400pf + //bit 5: I2C slave ack to General + // Call + //bit 6: I2C User register address + //bit 7: I2C 2-Byte User register + // address + //bit 8: I2C slave address no ack retry, + // It's only for Master mode, + // when slave doesn't ack the + // address + //bit 31~bit 8: Reserved + u32 ErrType; // + u32 TimeOut; //I2C IO Timeout count, in ms + + PHAL_I2C_INIT_DAT pInitDat; //Pointer to I2C initial data struct + PSAL_I2C_TRANSFER_BUF pTXBuf; //Pointer to I2C TX buffer + PSAL_I2C_TRANSFER_BUF pRXBuf; //Pointer to I2C RX buffer + PSAL_I2C_USER_CB pUserCB; //Pointer to I2C User Callback + PSAL_I2C_DMA_USER_DEF pDMAConf; //Pointer to I2C User Define DMA config +}SAL_I2C_HND, *PSAL_I2C_HND; + + + +//====================================================== +// I2C SAL Function Prototypes + +// For checking I2C input index valid or not +static inline HAL_Status +RtkI2CIdxChk( + IN u8 I2CIdx +) +{ + if (I2CIdx > I2C3_SEL) + return HAL_ERR_UNKNOWN; + + return HAL_OK; +} +#if 0 +//For checking I2C operation type valid or not +static inline HAL_Status +RtkI2COpTypeChk( + IN VOID *Data +) +{ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + + if (pSalI2CHND->OpType == I2C_POLL_TYPE) + return HAL_ERR_UNKNOWN; + + if (pSalI2CHND->OpType == I2C_DMA_TYPE) + return HAL_ERR_UNKNOWN; + + if (pSalI2CHND->OpType == I2C_INTR_TYPE) + return HAL_ERR_UNKNOWN; + + pSalI2CHND = pSalI2CHND; + + return HAL_OK; +} +#endif +//For checking I2C DMA available or not +static inline HAL_Status +RtkI2CDMAChk( + IN VOID *Data +) +{ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + + if (pSalI2CHND->OpType == I2C_DMA_TYPE) { + if (pSalI2CHND->DevNum >= I2C2_SEL) + return HAL_ERR_UNKNOWN; + } + else { + return HAL_ERR_UNKNOWN; + } + + return HAL_OK; +} + +//For checking I2C DMA available or not +static inline HAL_Status +RtkI2CDMAInitChk( + IN VOID *Data +) +{ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + + if (pSalI2CHND->OpType != I2C_DMA_TYPE) { + return HAL_ERR_UNKNOWN; + } + else { + return HAL_OK; + } + +} + +//====================================================== +//SAL I2C management function prototype +_LONG_CALL_ HAL_Status RtkI2CLoadDefault(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CInit(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CDeInit(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CSend(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CReceive(IN VOID *Data); +_LONG_CALL_ VOID RtkSalI2COpInit(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CSendUserAddr(IN VOID *Data,IN u8 MtrWr); +_LONG_CALL_ HAL_Status RtkI2CIoCtrl(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CPowerCtrl(IN VOID *Data); +//================= I2C SAL END =========================== + + +//================= I2C SAL MANAGEMENT START ================= +// I2C SAL management macros +#define SAL_USER_CB_NUM (sizeof(SAL_I2C_USER_CB) / sizeof(PSAL_I2C_USERCB_ADPT)) + +//====================================================== +// I2C SAL management data structures +// I2C SAL handle private +typedef struct _SAL_I2C_HND_PRIV_ { + VOID **ppSalI2CHnd; //Pointer to SAL_I2C_HND pointer + SAL_I2C_HND SalI2CHndPriv; //Private SAL_I2C_HND +}SAL_I2C_HND_PRIV, *PSAL_I2C_HND_PRIV; + +//I2C SAL management adapter +typedef struct _SAL_I2C_MNGT_ADPT_ { + PSAL_I2C_HND_PRIV pSalHndPriv; //Pointer to SAL_I2C_HND + PHAL_I2C_INIT_DAT pHalInitDat; //Pointer to HAL I2C initial data( HAL_I2C_INIT_DAT ) + PHAL_I2C_OP pHalOp; //Pointer to HAL I2C operation( HAL_I2C_OP ) + VOID (*pHalOpInit)(VOID*); //Pointer to HAL I2C initialize function + PIRQ_HANDLE pIrqHnd; //Pointer to IRQ handler in SAL layer( IRQ_HANDLE ) + PSAL_I2C_USER_CB pUserCB; //Pointer to SAL user callbacks (SAL_I2C_USER_CB ) + volatile u32 MstRDCmdCnt; //Used for Master Read command count + volatile u32 InnerTimeOut; //Used for SAL internal timeout count + VOID (*pSalIrqFunc)(VOID*); //Used for SAL I2C interrupt function + + PSAL_I2C_DMA_USER_DEF pDMAConf; //Pointer to I2C User Define DMA config + PHAL_GDMA_ADAPTER pHalTxGdmaAdp; //Pointer to HAL_GDMA_ADAPTER + PHAL_GDMA_ADAPTER pHalRxGdmaAdp; //Pointer to HAL_GDMA_ADAPTER + PHAL_GDMA_OP pHalGdmaOp; //Pointer to HAL_GDMA_OP + VOID (*pHalGdmaOpInit)(VOID*); //Pointer to HAL I2C initialize function + PIRQ_HANDLE pIrqTxGdmaHnd; //Pointer to IRQ handler for Tx GDMA + PIRQ_HANDLE pIrqRxGdmaHnd; //Pointer to IRQ handler for Rx GDMA + VOID (*pSalDMATxIrqFunc)(VOID*); //Used for SAL I2C interrupt function + VOID (*pSalDMARxIrqFunc)(VOID*); //Used for SAL I2C interrupt function + u32 RSVD; //Reserved +}SAL_I2C_MNGT_ADPT, *PSAL_I2C_MNGT_ADPT; + +//====================================================== +//SAL I2C management function prototype +PSAL_I2C_MNGT_ADPT RtkI2CGetMngtAdpt(IN u8 I2CIdx); +HAL_Status RtkI2CFreeMngtAdpt(IN PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt); +PSAL_I2C_HND RtkI2CGetSalHnd(IN u8 I2CIdx); +HAL_Status RtkI2CFreeSalHnd(IN PSAL_I2C_HND pSalI2CHND); +u32 RtkSalI2CSts(IN VOID *Data); + +extern _LONG_CALL_ VOID I2CISRHandle(IN VOID *Data); +extern _LONG_CALL_ VOID I2CTXGDMAISRHandle(IN VOID *Data); +extern _LONG_CALL_ VOID I2CRXGDMAISRHandle(IN VOID *Data); +extern HAL_Status I2CIsTimeout (IN u32 StartCount, IN u32 TimeoutCnt); +extern HAL_TIMER_OP HalTimerOp; +//====================================================== +// Function Prototypes +_LONG_CALL_ VOID HalI2COpInit(IN VOID *Data); +//================= I2C SAL MANAGEMENT END ================== + +//================= Rtl8195a I2C V02 function prototype ============ +_LONG_CALL_ VOID HalI2COpInitV02(IN VOID *Data); +_LONG_CALL_ VOID I2CISRHandleV02(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CSendV02(IN VOID *Data); +_LONG_CALL_ HAL_Status RtkI2CReceiveV02(IN VOID *Data); +_LONG_CALL_ VOID RtkSalI2COpInitV02(IN VOID *Data); +//================= Rtl8195a I2C V02 function prototype END========== + +#endif //#ifndef _HAL_I2C_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_i2s.h b/component/soc/realtek/8195a/fwlib/hal_i2s.h new file mode 100644 index 0000000..2e49d09 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_i2s.h @@ -0,0 +1,333 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_I2S_H_ +#define _HAL_I2S_H_ + +#include "rtl8195a_i2s.h" + +/* User Define Flags */ + +#define I2S_MAX_ID 1 // valid I2S index 0 ~ I2S_MAX_ID + +/**********************************************************************/ +/* I2S HAL initial data structure */ +typedef struct _HAL_I2S_INIT_DAT_ { + u8 I2SIdx; /*I2S index used*/ + u8 I2SEn; /*I2S module enable tx/rx/tx+rx*/ + u8 I2SMaster; /*I2S Master or Slave mode*/ + u8 I2SWordLen; /*I2S Word length 16 or 24bits*/ + + u8 I2SChNum; /*I2S Channel number mono or stereo*/ + u8 I2SPageNum; /*I2S Page Number 2~4*/ + u16 I2SPageSize; /*I2S page Size 1~4096 word*/ + + u8 *I2STxData; /*I2S Tx data pointer*/ + + u8 *I2SRxData; /*I2S Rx data pointer*/ + + u32 I2STxIntrMSK; /*I2S Tx Interrupt Mask*/ + u32 I2STxIntrClr; /*I2S Tx Interrupt register to clear */ + + u32 I2SRxIntrMSK; /*I2S Rx Interrupt Mask*/ + u32 I2SRxIntrClr; /*I2S Rx Interrupt register to clear*/ + + u16 I2STxIdx; /*I2S TX page index */ + u16 I2SRxIdx; /*I2S RX page index */ + + u16 I2SHWTxIdx; /*I2S HW TX page index */ + u16 I2SHWRxIdx; /*I2S HW RX page index */ + + + u16 I2SRate; /*I2S sample rate*/ + u8 I2STRxAct; /*I2S tx rx act*/ +}HAL_I2S_INIT_DAT, *PHAL_I2S_INIT_DAT; + +/**********************************************************************/ +/* I2S Data Structures */ +/* I2S Module Selection */ +typedef enum _I2S_MODULE_SEL_ { + I2S0_SEL = 0x0, + I2S1_SEL = 0x1, +}I2S_MODULE_SEL,*PI2S_MODULE_SEL; +/* +typedef struct _HAL_I2S_ADAPTER_ { + u32 Enable:1; + I2S_CTL_REG I2sCtl; + I2S_SETTING_REG I2sSetting; + u32 abc; + u8 I2sIndex; +}HAL_I2S_ADAPTER, *PHAL_I2S_ADAPTER; +*/ +/* I2S HAL Operations */ +typedef struct _HAL_I2S_OP_ { + RTK_STATUS (*HalI2SInit) (VOID *Data); + RTK_STATUS (*HalI2SDeInit) (VOID *Data); + RTK_STATUS (*HalI2STx) (VOID *Data, u8 *pBuff); + RTK_STATUS (*HalI2SRx) (VOID *Data, u8 *pBuff); + RTK_STATUS (*HalI2SEnable) (VOID *Data); + RTK_STATUS (*HalI2SIntrCtrl) (VOID *Data); + u32 (*HalI2SReadReg) (VOID *Data, u8 I2SReg); + RTK_STATUS (*HalI2SSetRate) (VOID *Data); + RTK_STATUS (*HalI2SSetWordLen) (VOID *Data); + RTK_STATUS (*HalI2SSetChNum) (VOID *Data); + RTK_STATUS (*HalI2SSetPageNum) (VOID *Data); + RTK_STATUS (*HalI2SSetPageSize) (VOID *Data); + + RTK_STATUS (*HalI2SClrIntr) (VOID *Data); + RTK_STATUS (*HalI2SClrAllIntr) (VOID *Data); + RTK_STATUS (*HalI2SDMACtrl) (VOID *Data); +/* + VOID (*HalI2sOnOff)(VOID *Data); + BOOL (*HalI2sInit)(VOID *Data); + BOOL (*HalI2sSetting)(VOID *Data); + BOOL (*HalI2sEn)(VOID *Data); + BOOL (*HalI2sIsrEnAndDis) (VOID *Data); + BOOL (*HalI2sDumpReg)(VOID *Data); + BOOL (*HalI2s)(VOID *Data); +*/ +}HAL_I2S_OP, *PHAL_I2S_OP; + + +/**********************************************************************/ + +/* I2S Pinmux Selection */ +#if 0 +typedef enum _I2S0_PINMUX_ { + I2S0_TO_S0 = 0x0, + I2S0_TO_S1 = 0x1, + I2S0_TO_S2 = 0x2, +}I2S0_PINMUX, *PI2S0_PINMUX; + +typedef enum _I2S1_PINMUX_ { + I2S1_TO_S0 = 0x0, + I2S1_TO_S1 = 0x1, +}I2S1_PINMUX, *PI2S1_PINMUX; +#endif + +typedef enum _I2S_PINMUX_ { + I2S_S0 = 0, + I2S_S1 = 1, + I2S_S2 = 2, + I2S_S3 = 3 +}I2S_PINMUX, *PI2S_PINMUX; + + +/* I2S Module Status */ +typedef enum _I2S_MODULE_STATUS_ { + I2S_DISABLE = 0x0, + I2S_ENABLE = 0x1, +}I2S_MODULE_STATUS, *PI2S_MODULE_STATUS; + + +/* I2S Device Status */ +typedef enum _I2S_Device_STATUS_ { + I2S_STS_UNINITIAL = 0x00, + I2S_STS_INITIALIZED = 0x01, + I2S_STS_IDLE = 0x02, + + I2S_STS_TX_READY = 0x03, + I2S_STS_TX_ING = 0x04, + + I2S_STS_RX_READY = 0x05, + I2S_STS_RX_ING = 0x06, + + I2S_STS_TRX_READY = 0x07, + I2S_STS_TRX_ING = 0x08, + + I2S_STS_ERROR = 0x09, +}I2S_Device_STATUS, *PI2S_Device_STATUS; + + +/* I2S Feature Status */ +typedef enum _I2S_FEATURE_STATUS_{ + I2S_FEATURE_DISABLED = 0, + I2S_FEATURE_ENABLED = 1, +}I2S_FEATURE_STATUS,*PI2S_FEATURE_STATUS; + +/* I2S Device Mode */ +typedef enum _I2S_DEV_MODE_ { + I2S_MASTER_MODE = 0x0, + I2S_SLAVE_MODE = 0x1 +}I2S_DEV_MODE, *PI2S_DEV_MODE; + +/* I2S Word Length */ +typedef enum _I2S_WORD_LEN_ { + I2S_WL_16 = 0x0, + I2S_WL_24 = 0x1, +}I2S_WORD_LEN, *PI2S_WORD_LEN; + +/* I2S Bus Transmit/Receive */ +typedef enum _I2S_DIRECTION_ { + I2S_ONLY_RX = 0x0, + I2S_ONLY_TX = 0x1, + I2S_TXRX = 0x2 +}I2S_DIRECTION, *PI2S_DIRECTION; + +/* I2S Channel number */ +typedef enum _I2S_CH_NUM_ { + I2S_CH_STEREO = 0x0, + I2S_CH_RSVD = 0x1, + I2S_CH_MONO = 0x2 +}I2S_CH_NUM, *PI2S_CH_NUM; + +/* I2S Page number */ +typedef enum _I2S_PAGE_NUM_ { + I2S_1PAGE = 0x0, + I2S_2PAGE = 0x1, + I2S_3PAGE = 0x2, + I2S_4PAGE = 0x3 +}I2S_PAGE_NUM, *PI2S_PAGE_NUM; + +/* I2S Sample rate*/ +typedef enum _I2S_SAMPLE_RATE_ { + I2S_SR_8KHZ = 0x00, // /12 + I2S_SR_16KHZ = 0x01, // /6 + I2S_SR_24KHZ = 0x02, // /4 + I2S_SR_32KHZ = 0x03, // /3 + I2S_SR_48KHZ = 0x05, // /2 + I2S_SR_96KHZ = 0x06, // x1, base 96kHz + I2S_SR_7p35KHZ = 0x10, + I2S_SR_11p02KHZ = 0x11, + I2S_SR_22p05KHZ = 0x12, + I2S_SR_29p4KHZ = 0x13, + I2S_SR_44p1KHZ = 0x15, + I2S_SR_88p2KHZ = 0x16 // x1, base 88200Hz +}I2S_SAMPLE_RATE, *PI2S_SAMPLE_RATE; + +/* I2S TX interrupt mask/status */ +typedef enum _I2S_TX_IMR_ { + I2S_TX_INT_PAGE0_OK = (1<<0), + I2S_TX_INT_PAGE1_OK = (1<<1), + I2S_TX_INT_PAGE2_OK = (1<<2), + I2S_TX_INT_PAGE3_OK = (1<<3), + I2S_TX_INT_FULL = (1<<4), + I2S_TX_INT_EMPTY = (1<<5) +} I2S_TX_IMR, *PI2S_TX_IMR; + +/* I2S RX interrupt mask/status */ +typedef enum _I2S_RX_IMR_ { + I2S_RX_INT_PAGE0_OK = (1<<0), + I2S_RX_INT_PAGE1_OK = (1<<1), + I2S_RX_INT_PAGE2_OK = (1<<2), + I2S_RX_INT_PAGE3_OK = (1<<3), + I2S_RX_INT_EMPTY = (1<<4), + I2S_RX_INT_FULL = (1<<5) +} I2S_RX_IMR, *PI2S_RX_IMR; + +/* I2S User Callbacks */ +typedef struct _SAL_I2S_USER_CB_{ + VOID (*TXCB) (VOID *Data); + VOID (*TXCCB) (VOID *Data); + VOID (*RXCB) (VOID *Data); + VOID (*RXCCB) (VOID *Data); + VOID (*RDREQCB) (VOID *Data); + VOID (*ERRCB) (VOID *Data); + VOID (*GENCALLCB) (VOID *Data); +}SAL_I2S_USER_CB,*PSAL_I2S_USER_CB; + +typedef struct _I2S_USER_CB_{ + VOID (*TxCCB)(uint32_t id, char *pbuf); + u32 TxCBId; + VOID (*RxCCB)(uint32_t id, char *pbuf); + u32 RxCBId; +}I2S_USER_CB,*PI2S_USER_CB; + +/* Software API Level I2S Handler */ +typedef struct _HAL_I2S_ADAPTER_{ + u8 DevNum; //I2S device number + u8 PinMux; //I2S pin mux seletion + u8 RSVD0; //Reserved + volatile u8 DevSts; //I2S device status + + u32 RSVD2; //Reserved + u32 I2SExd; //I2S extended options: + //bit 0: I2C RESTART supported, + // 0 for NOT supported, + // 1 for supported + //bit 1: I2C General Call supported + // 0 for NOT supported, + // 1 for supported + //bit 2: I2C START Byte supported + // 0 for NOT supported, + // 1 for supported + //bit 3: I2C Slave-No-Ack + // supported + // 0 for NOT supported, + // 1 for supported + //bit 4: I2C bus loading, + // 0 for 100pf, + // 1 for 400pf + //bit 5: I2C slave ack to General + // Call + //bit 6: I2C User register address + //bit 7: I2C 2-Byte User register + // address + //bit 31~bit 8: Reserved + u32 ErrType; // + u32 TimeOut; //I2S IO Timeout count + + PHAL_I2S_INIT_DAT pInitDat; //Pointer to I2S initial data struct + I2S_USER_CB UserCB; //Pointer to I2S User Callback + IRQ_HANDLE IrqHandle; // Irq Handler + + u32* TxPageList[4]; // The Tx DAM buffer: pointer of each page + u32* RxPageList[4]; // The Tx DAM buffer: pointer of each page +}HAL_I2S_ADAPTER, *PHAL_I2S_ADAPTER; + +typedef struct _HAL_I2S_DEF_SETTING_{ + u8 I2SMaster; // Master or Slave mode + u8 DevSts; //I2S device status + u8 I2SChNum; //I2S Channel number mono or stereo + u8 I2SPageNum; //I2S Page number 2~4 + u8 I2STRxAct; //I2S tx rx act, tx only or rx only or tx+rx + u8 I2SWordLen; //I2S Word length 16bit or 24bit + u16 I2SPageSize; //I2S Page size 1~4096 word + + u16 I2SRate; //I2S sample rate 8k ~ 96khz + + u32 I2STxIntrMSK; /*I2S Tx Interrupt Mask*/ + u32 I2SRxIntrMSK; /*I2S Rx Interrupt Mask*/ +}HAL_I2S_DEF_SETTING, *PHAL_I2S_DEF_SETTING; + + + +/**********************************************************************/ +HAL_Status +RtkI2SLoadDefault(IN VOID *Adapter, IN VOID *Setting); + +HAL_Status +RtkI2SInit(IN VOID *Data); + +HAL_Status +RtkI2SDeInit(IN VOID *Data); + +HAL_Status +RtkI2SEnable(IN VOID *Data); + +HAL_Status +RtkI2SDisable(IN VOID *Data); + + +/**********************************************************************/ + + +VOID I2S0ISRHandle(VOID *Data); +VOID I2S1ISRHandle(VOID *Data); + + +/**********************************************************************/ + +VOID HalI2SOpInit( + IN VOID *Data +); + + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_irqn.h b/component/soc/realtek/8195a/fwlib/hal_irqn.h new file mode 100644 index 0000000..952a78b --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_irqn.h @@ -0,0 +1,111 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_IRQN_H_ +#define _HAL_IRQN_H_ + +#define PERIPHERAL_IRQ_BASE_NUM 64 + +typedef enum _IRQn_Type_ { +#if 0 +/****** Cortex-M3 Processor Exceptions Numbers ********/ + NON_MASKABLE_INT_IRQ = -14, + HARD_FAULT_IRQ = -13, + MEM_MANAGE_FAULT_IRQ = -12, + BUS_FAULT_IRQ = -11, + USAGE_FAULT_IRQ = -10, + SVCALL_IRQ = -5, + DEBUG_MONITOR_IRQ = -4, + PENDSVC_IRQ = -2, + SYSTICK_IRQ = -1, +#else +/****** Cortex-M3 Processor Exceptions Numbers ********/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 3 Hard Fault, all classes of Fault */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt */ +#endif +/****** RTL8195A Specific Interrupt Numbers ************/ + SYSTEM_ON_IRQ = 0, + WDG_IRQ = 1, + TIMER0_IRQ = 2, + TIMER1_IRQ = 3, + I2C3_IRQ = 4, + TIMER2_7_IRQ = 5, + SPI0_IRQ = 6, + GPIO_IRQ = 7, + UART0_IRQ = 8, + SPI_FLASH_IRQ = 9, + USB_OTG_IRQ = 10, + SDIO_HOST_IRQ = 11, + SDIO_DEVICE_IRQ = 12, + I2S0_PCM0_IRQ = 13, + I2S1_PCM1_IRQ = 14, + WL_DMA_IRQ = 15, + WL_PROTOCOL_IRQ = 16, + CRYPTO_IRQ = 17, + GMAC_IRQ = 18, + PERIPHERAL_IRQ = 19, + GDMA0_CHANNEL0_IRQ = 20, + GDMA0_CHANNEL1_IRQ = 21, + GDMA0_CHANNEL2_IRQ = 22, + GDMA0_CHANNEL3_IRQ = 23, + GDMA0_CHANNEL4_IRQ = 24, + GDMA0_CHANNEL5_IRQ = 25, + GDMA1_CHANNEL0_IRQ = 26, + GDMA1_CHANNEL1_IRQ = 27, + GDMA1_CHANNEL2_IRQ = 28, + GDMA1_CHANNEL3_IRQ = 29, + GDMA1_CHANNEL4_IRQ = 30, + GDMA1_CHANNEL5_IRQ = 31, + +/****** RTL8195A Peripheral Interrupt Numbers ************/ + I2C0_IRQ = 64,// 0 + 64, + I2C1_IRQ = 65,// 1 + 64, + I2C2_IRQ = 66,// 2 + 64, + SPI1_IRQ = 72,// 8 + 64, + SPI2_IRQ = 73,// 9 + 64, + UART1_IRQ = 80,// 16 + 64, + UART2_IRQ = 81,// 17 + 64, + UART_LOG_IRQ = 88,// 24 + 64, + ADC_IRQ = 89,// 25 + 64, + DAC0_IRQ = 91,// 27 + 64, + DAC1_IRQ = 92,// 28 + 64, + //RXI300_IRQ = 93// 29 + 64 + LP_EXTENSION_IRQ = 93,// 29+64 + + RXI300_IRQ = 96,// 0+32 + 64 + NFC_IRQ = 97// 1+32+64 +} IRQn_Type, *PIRQn_Type; + + +typedef VOID (*HAL_VECTOR_FUN) (VOID); + +typedef enum _VECTOR_TABLE_TYPE_{ + DEDECATED_VECTRO_TABLE, + PERIPHERAL_VECTOR_TABLE +}VECTOR_TABLE_TYPE, *PVECTOR_TABLE_TYPE; + + +typedef u32 (*IRQ_FUN)(VOID *Data); + +typedef struct _IRQ_HANDLE_ { + IRQ_FUN IrqFun; + IRQn_Type IrqNum; + u32 Data; + u32 Priority; +}IRQ_HANDLE, *PIRQ_HANDLE; + + +#endif //_HAL_IRQN_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_mii.h b/component/soc/realtek/8195a/fwlib/hal_mii.h new file mode 100644 index 0000000..7a9b970 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_mii.h @@ -0,0 +1,118 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_MII_H_ +#define _HAL_MII_H_ + +#include "rtl8195a_mii.h" + + +/** + * LOG Configurations + */ + +// #define NOLOG + +#define LOG_TAG "NoTag" +#define LOG_INFO_HEADER "I" +#define LOG_DEBUG_HEADER "D" +#define LOG_ERROR_HEADER "E" +#define LOG_TEST_HEADER "T" + +#define IDENT_TWO_SPACE " " +#define IDENT_FOUR_SPACE " " + +#define LOG_INFO(...) do {\ + DiagPrintf("\r"LOG_INFO_HEADER"/"LOG_TAG": " __VA_ARGS__);\ +}while(0) + +#define LOG_DEBUG(...) do {\ + DiagPrintf("\r"LOG_DEBUG_HEADER"/"LOG_TAG": " __VA_ARGS__);\ +}while(0) + +#define LOG_ERROR(...) do {\ + DiagPrintf("\r"LOG_ERROR_HEADER"/"LOG_TAG": " __VA_ARGS__);\ +}while(0) + +#ifdef NOLOG + #define LOGI + #define LOGD + #define LOGE + #define LOGI2 + #define LOGD2 + #define LOGE2 + #define LOGI4 + #define LOGD4 + #define LOGE4 +#else + #define LOGI LOG_INFO + #define LOGD LOG_DEBUG + #define LOGE LOG_ERROR + #define LOGI2(...) LOG_INFO(IDENT_TWO_SPACE __VA_ARGS__) + #define LOGD2(...) LOG_DEBUG(IDENT_TWO_SPACE __VA_ARGS__) + #define LOGE2(...) LOG_ERROR(IDENT_TWO_SPACE __VA_ARGS__) + #define LOGI4(...) LOG_INFO(IDENT_FOUR_SPACE __VA_ARGS__) + #define LOGD4(...) LOG_DEBUG(IDENT_FOUR_SPACE __VA_ARGS__) + #define LOGE4(...) LOG_ERROR(IDENT_FOUR_SPACE __VA_ARGS__) +#endif + +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_RESET "\x1b[0m" + +#define DBG_ENTRANCE LOGI(ANSI_COLOR_GREEN "=> %s() <%s>\n" ANSI_COLOR_RESET, \ + __func__, __FILE__) + + +// GMAC MII Configurations +#ifdef LOG_TAG +#undef LOG_TAG +#define LOG_TAG "MII" +#endif + + +typedef struct _HAL_MII_ADAPTER_ { + u32 InterruptMask; + PPHY_MODE_INFO pPhyModeInfo; +}HAL_MII_ADAPTER, *PHAL_MII_ADAPTER; + +typedef struct _HAL_MII_OP_ { + BOOL (*HalMiiGmacInit)(VOID *Data); + BOOL (*HalMiiInit)(VOID *Data); + BOOL (*HalMiiGmacReset)(VOID *Data); + BOOL (*HalMiiGmacEnablePhyMode)(VOID *Data); + u32 (*HalMiiGmacXmit)(VOID *Data); + VOID (*HalMiiGmacCleanTxRing)(VOID *Data); + VOID (*HalMiiGmacFillTxInfo)(VOID *Data); + VOID (*HalMiiGmacFillRxInfo)(VOID *Data); + VOID (*HalMiiGmacTx)(VOID *Data); + VOID (*HalMiiGmacRx)(VOID *Data); + VOID (*HalMiiGmacSetDefaultEthIoCmd)(VOID *Data); + VOID (*HalMiiGmacInitIrq)(VOID *Data); + u32 (*HalMiiGmacGetInterruptStatus)(VOID); + VOID (*HalMiiGmacClearInterruptStatus)(u32 IsrStatus); +}HAL_MII_OP, *PHAL_MII_OP; + +VOID HalMiiOpInit(IN VOID *Data); + +typedef struct _MII_ADAPTER_ { + PHAL_MII_OP pHalMiiOp; + PHAL_MII_ADAPTER pHalMiiAdapter; + PTX_INFO pTx_Info; + PRX_INFO pRx_Info; + VOID* TxBuffer; + VOID* RxBuffer; +}MII_ADAPTER, *PMII_ADAPTER; + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_misc.h b/component/soc/realtek/8195a/fwlib/hal_misc.h new file mode 100644 index 0000000..1d72114 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_misc.h @@ -0,0 +1,30 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _MISC_H_ +#define _MISC_H_ + +#include + +#ifdef CONFIG_TIMER_MODULE +extern _LONG_CALL_ u32 HalDelayUs(u32 us); +#endif + +extern _LONG_CALL_ u32 HalGetCpuClk(VOID); +extern _LONG_CALL_ u8 HalGetRomInfo(VOID); + +extern _LONG_CALL_ void *_memset( void *s, int c, SIZE_T n ); +extern _LONG_CALL_ void *_memcpy( void *s1, const void *s2, SIZE_T n ); +extern _LONG_CALL_ int _memcmp( const void *av, const void *bv, SIZE_T len ); + +extern _LONG_CALL_ SIZE_T _strlen(const char *s); +extern _LONG_CALL_ int _strcmp(const char *cs, const char *ct); + + +#endif //_MISC_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_nfc.h b/component/soc/realtek/8195a/fwlib/hal_nfc.h new file mode 100644 index 0000000..b73dada --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_nfc.h @@ -0,0 +1,22 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_NFC_H_ +#define _HAL_NFC_H_ + +#include "rtl8195a_nfc.h" + + +VOID HalNFCOpInit( + IN VOID *Data +); + + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_pcm.h b/component/soc/realtek/8195a/fwlib/hal_pcm.h new file mode 100644 index 0000000..fa34432 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_pcm.h @@ -0,0 +1,104 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_PCM_H_ +#define _HAL_PCM_H_ + +#include "rtl8195a_pcm.h" +/* +typedef struct _GDMA_CH_LLI_ELE_ { + u32 Sarx; + u32 Darx; + u32 Llpx; + u32 CtlxLow; + u32 CtlxUp; + u32 Temp; +}GDMA_CH_LLI_ELE, *PGDMA_CH_LLI_ELE; +#if 1 +#if 0 +typedef struct _GDMA_CH_LLI_ { + PGDMA_CH_LLI_ELE pLliEle; + PGDMA_CH_LLI pNextLli; +}GDMA_CH_LLI, *PGDMA_CH_LLI; + +typedef struct _BLOCK_SIZE_LIST_ { + u32 BlockSize; + PBLOCK_SIZE_LIST pNextBlockSiz; +}BLOCK_SIZE_LIST, *PBLOCK_SIZE_LIST; +#else +struct GDMA_CH_LLI { + PGDMA_CH_LLI_ELE pLliEle; + struct GDMA_CH_LLI *pNextLli; +}; + +struct BLOCK_SIZE_LIST { + u32 BlockSize; + struct BLOCK_SIZE_LIST *pNextBlockSiz; +}; + +#endif + +#endif +typedef struct _HAL_GDMA_ADAPTER_ { + u32 ChSar; + u32 ChDar; + GDMA_CHANNEL_NUM ChEn; + GDMA_CTL_REG GdmaCtl; + GDMA_CFG_REG GdmaCfg; + u32 PacketLen; + u32 BlockLen; + u32 MuliBlockCunt; + u32 MaxMuliBlock; + struct GDMA_CH_LLI *pLlix; + struct BLOCK_SIZE_LIST *pBlockSizeList; + + PGDMA_CH_LLI_ELE pLli; + u32 NextPlli; + u8 TestItem; + u8 ChNum; + u8 GdmaIndex; + u8 IsrCtrl:1; + u8 GdmaOnOff:1; + u8 Llpctrl:1; + u8 Lli0:1; + u8 Rsvd4to7:4; + u8 GdmaIsrType; +}HAL_GDMA_ADAPTER, *PHAL_GDMA_ADAPTER; + +*/ + +typedef struct _HAL_PCM_ADAPTER_ { + u32 Enable:1; + PCM_CTL_REG PcmCtl; + PCM_CHCNR03_REG PcmChCNR03; + PCM_TSR03_REG PcmTSR03; + PCM_BSIZE03_REG PcmBSize03; + u32 abc; + u8 PcmIndex; + u8 PcmCh; +}HAL_PCM_ADAPTER, *PHAL_PCM_ADAPTER; + + +typedef struct _HAL_PCM_OP_ { + VOID (*HalPcmOnOff)(VOID *Data); + BOOL (*HalPcmInit)(VOID *Data); + BOOL (*HalPcmSetting)(VOID *Data); + BOOL (*HalPcmEn)(VOID *Data); + BOOL (*HalPcmIsrEnAndDis) (VOID *Data); + BOOL (*HalPcmDumpReg)(VOID *Data); + BOOL (*HalPcm)(VOID *Data); +}HAL_PCM_OP, *PHAL_PCM_OP; + + +VOID HalPcmOpInit( + IN VOID *Data +); + + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_peri_on.h b/component/soc/realtek/8195a/fwlib/hal_peri_on.h new file mode 100644 index 0000000..abcbdd0 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_peri_on.h @@ -0,0 +1,451 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_PERI_ON_H_ +#define _HAL_PERI_ON_H_ + +#define MASK_ALLON 0xFFFFFFFF + +#define HAL_PERI_ON_READ32(addr) HAL_READ32(PERI_ON_BASE, addr) +#define HAL_PERI_ON_WRITE32(addr, value) HAL_WRITE32(PERI_ON_BASE, addr, value) +#define HAL_PERI_ON_READ16(addr) HAL_READ16(PERI_ON_BASE, addr) +#define HAL_PERI_ON_WRITE16(addr, value) HAL_WRITE16(PERI_ON_BASE, addr, value) +#define HAL_PERI_ON_READ8(addr) HAL_READ8(PERI_ON_BASE, addr) +#define HAL_PERI_ON_WRITE8(addr, value) HAL_WRITE8(PERI_ON_BASE, addr, value) +#define HAL_PERL_ON_FUNC_CTRL(addr,value,ctrl) \ + HAL_PERI_ON_WRITE32(addr, ((HAL_PERI_ON_READ32(addr) & (~value))|((MASK_ALLON - ctrl + 1) & value))) +#define HAL_PERL_ON_PIN_SEL(addr,mask,value) \ + HAL_PERI_ON_WRITE32(addr, ((HAL_PERI_ON_READ32(addr) & (~mask)) | value)) + +//40 REG_SYS_REGU_CTRL0 +#define LDO25M_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SYS_REGU_CTRL0, BIT_SYS_REGU_LDO25M_EN, ctrl) + +//A0 SYS_DEBUG_CTRL +#define DEBUG_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SYS_DEBUG_CTRL, BIT_SYS_DBG_PIN_EN, ctrl) + +//A4 SYS_PINMUX_CTRL +#define SIC_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SYS_PINMUX_CTRL, BIT_SIC_PIN_EN, ctrl) +#define EEPROM_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SYS_PINMUX_CTRL, BIT_EEPROM_PIN_EN, ctrl) + + +//210 SOV_FUNC_EN +#define LXBUS_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_LXBUS_EN, ctrl) +#define FLASH_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SPI_FLASH_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_FLASH_EN, ctrl);} + +#define MEM_CTRL_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SDR_SDRAM_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_MEM_CTRL_EN, ctrl);} + +#define LOC_UART_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(LOG_UART_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_LOG_UART_EN, ctrl);} + +#define GDMA0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(GDMA0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_GDMA0_EN, ctrl);} + +#define GDMA1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(GDMA1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_GDMA1_EN, ctrl);} + +#define GTIMER_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(TIMER_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_GTIMER_EN, ctrl);} + +#define SECURITY_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(CRYPTO_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_FUNC_EN, BIT_SOC_SECURITY_ENGINE_EN, ctrl);} + +//214 SOC_HCI_COM_FUNC_EN +#define SDIOD_ON_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SDIO_DEVICE_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_SDIOD_ON_EN, ctrl);} + +#define SDIOD_OFF_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SDIO_DEVICE_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_SDIOD_OFF_EN, ctrl);} + +#define SDIOH_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SDIO_HOST_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_SDIOH_EN, ctrl);} + +#define SDIO_ON_RST_MASK(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_SDIOD_ON_RST_MUX, ctrl) +#define OTG_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(USB_OTG_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_OTG_EN, ctrl);} + +#define OTG_RST_MASK(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_OTG_RST_MUX, ctrl) +#define MII_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(MII_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_MII_EN, ctrl);} + +#define MII_MUX_SEL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_SM_SEL, ctrl) +#define WL_MACON_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(WIFI_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_HCI_COM_FUNC_EN, BIT_SOC_HCI_WL_MACON_EN, ctrl);} + +//218 SOC_PERI_FUNC0_EN +#define UART0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_UART0_EN, ctrl);} + +#define UART1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_UART1_EN, ctrl);} + +#define UART2_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART2_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_UART2_EN, ctrl);} + +#define SPI0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SPI0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_SPI0_EN, ctrl);} + +#define SPI1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SPI1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_SPI1_EN, ctrl);} + +#define SPI2_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(SPI2_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_SPI2_EN, ctrl);} + +#define I2C0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2C0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2C0_EN, ctrl);} + +#define I2C1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2C1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2C1_EN, ctrl);} + +#define I2C2_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2C2_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2C2_EN, ctrl);} + +#define I2C3_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2C3_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2C3_EN, ctrl);} + +#define I2S0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2S0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2S0_EN, ctrl);} + +#define I2S1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(I2S1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_I2S1_EN, ctrl);} + +#define PCM0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(PCM0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_PCM0_EN, ctrl);} + +#define PCM1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(PCM1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC0_EN, BIT_PERI_PCM1_EN, ctrl);} + +//21C SOC_PERI_FUNC1_EN +#define ADC0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(ADC_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC1_EN, BIT_PERI_ADC0_EN, ctrl);} + +#define DAC0_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(DAC_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC1_EN, BIT_PERI_DAC0_EN, ctrl);} + +#define DAC1_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(DAC_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC1_EN, BIT_PERI_DAC1_EN, ctrl);} + +#define GPIO_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(GPIO_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_FUNC1_EN, BIT_PERI_GPIO_EN, ctrl);} + +//220 SOC_PERI_BD_FUNC0_EN +#define UART0_BD_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART0_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_BD_FUNC0_EN, BIT_PERI_UART0_BD_EN, ctrl);} + +#define UART1_BD_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART1_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_BD_FUNC0_EN, BIT_PERI_UART1_BD_EN, ctrl);} + +#define UART2_BD_FCTRL(ctrl) { \ + if (!ctrl) { \ + HAL_READ32(UART2_REG_BASE,0);\ + }\ + HAL_PERL_ON_FUNC_CTRL(REG_SOC_PERI_BD_FUNC0_EN, BIT_PERI_UART2_BD_EN, ctrl);} + +//230 PESOC_CLK_CTRL +#define ACTCK_CPU_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_CKE_PLFM, ctrl) +#define ACTCK_TRACE_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_TRACE_EN, ctrl) +#define SLPCK_TRACE_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_TRACE_EN, ctrl) +#define ACTCK_VENDOR_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_VENDOR_REG_EN, ctrl) +#define SLPCK_VENDOR_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_VENDOR_REG_EN, ctrl) +#define ACTCK_FLASH_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_FLASH_EN, ctrl) +#define SLPCK_FLASH_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_FLASH_EN, ctrl) +#define ACTCK_SDR_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_SDR_EN, ctrl) +#define SLPCK_SDR_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_SDR_EN, ctrl) +#define ACTCK_LOG_UART_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_LOG_UART_EN, ctrl) +#define SLPCK_LOG_UART_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_LOG_UART_EN, ctrl) +#define ACTCK_TIMER_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_TIMER_EN, ctrl) +#define SLPCK_TIMER_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_TIMER_EN, ctrl) +#define ACTCK_GDMA0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_GDMA0_EN, ctrl) +#define SLPCK_GDMA0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_GDMA0_EN, ctrl) +#define ACTCK_GDMA1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_GDMA1_EN, ctrl) +#define SLPCK_GDMA1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_GDMA1_EN, ctrl) +#define ACTCK_GPIO_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_GPIO_EN, ctrl) +#define SLPCK_GPIO_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_GPIO_EN, ctrl) +#define ACTCK_BTCMD_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_ACTCK_BTCMD_EN, ctrl) +#define SLPCK_BTCMD_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_CLK_CTRL, BIT_SOC_SLPCK_BTCMD_EN, ctrl) + +//234 PESOC_PERI_CLK_CTRL0 +#define ACTCK_UART0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_UART0_EN, ctrl) +#define SLPCK_UART0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_UART0_EN, ctrl) +#define ACTCK_UART1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_UART1_EN, ctrl) +#define SLPCK_UART1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_UART1_EN, ctrl) +#define ACTCK_UART2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_UART2_EN, ctrl) +#define SLPCK_UART2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_UART2_EN, ctrl) +#define ACTCK_SPI0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_SPI0_EN, ctrl) +#define SLPCK_SPI0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_SPI0_EN, ctrl) +#define ACTCK_SPI1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_SPI1_EN, ctrl) +#define SLPCK_SPI1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_SPI1_EN, ctrl) +#define ACTCK_SPI2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_ACTCK_SPI2_EN, ctrl) +#define SLPCK_SPI2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL0, BIT_SOC_SLPCK_SPI2_EN, ctrl) + +//238 PESOC_PERI_CLK_CTRL1 +#define ACTCK_I2C0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_I2C0_EN, ctrl) +#define SLPCK_I2C0_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_I2C0_EN, ctrl) +#define ACTCK_I2C1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_I2C1_EN, ctrl) +#define SLPCK_I2C1_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_I2C1_EN, ctrl) +#define ACTCK_I2C2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_I2C2_EN, ctrl) +#define SLPCK_I2C2_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_I2C2_EN, ctrl) +#define ACTCK_I2C3_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_I2C3_EN, ctrl) +#define SLPCK_I2C3_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_I2C3_EN, ctrl) +#define ACTCK_I2S_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_I2S_EN, ctrl) +#define SLPCK_I2S_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_I2S_EN, ctrl) +#define ACTCK_PCM_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_PCM_EN, ctrl) +#define SLPCK_PCM_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_PCM_EN, ctrl) +#define ACTCK_ADC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_ADC_EN, ctrl) +#define SLPCK_ADC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_ADC_EN, ctrl) +#define ACTCK_DAC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_ACTCK_DAC_EN, ctrl) +#define SLPCK_DAC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CLK_CTRL1, BIT_SOC_SLPCK_DAC_EN, ctrl) + +//240 PESOC_HCI_CLK_CTRL0 +#define ACTCK_SDIOD_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_ACTCK_SDIO_DEV_EN, ctrl) +#define SLPCK_SDIOD_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_SLPCK_SDIO_DEV_EN, ctrl) +#define ACTCK_SDIOH_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_ACTCK_SDIO_HST_EN, ctrl) +#define SLPCK_SDIOH_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_SLPCK_SDIO_HST_EN, ctrl) +#define ACTCK_OTG_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_ACTCK_OTG_EN, ctrl) +#define SLPCK_OTG_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_SLPCK_OTG_EN, ctrl) +#define ACTCK_MII_MPHY_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_ACTCK_MII_MPHY_EN, ctrl) +#define SLPCK_MII_MPHY_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_HCI_CLK_CTRL0, BIT_SOC_SLPCK_MII_MPHY_EN, ctrl) + +//244 PESOC_COM_CLK_CTRL1 +#define ACTCK_WL_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_ACTCK_WL_EN, ctrl) +#define SLPCK_WL_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_SLPCK_WL_EN, ctrl) +#define ACTCK_SEC_ENG_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_ACTCK_SECURITY_ENG_EN, ctrl) +#define SLPCK_SEC_ENG_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_SLPCK_SECURITY_ENG_EN, ctrl) +#define ACTCK_NFC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_ACTCK_NFC_EN, ctrl) +#define SLPCK_NFC_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_SLPCK_NFC_EN, ctrl) +#define NFC_CAL_CCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_COM_CLK_CTRL1, BIT_SOC_NFC_CAL_EN, ctrl) + +//250 REG_PERI_CLK_SEL +#define TRACE_CLK_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_CLK_SEL, (BIT_MASK_PESOC_TRACE_CK_SEL << BIT_SHIFT_PESOC_TRACE_CK_SEL), BIT_PESOC_TRACE_CK_SEL(num)) +#define FLASH_CLK_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_CLK_SEL, (BIT_MASK_PESOC_FLASH_CK_SEL << BIT_SHIFT_PESOC_FLASH_CK_SEL), BIT_PESOC_FLASH_CK_SEL(num)) +#define SDR_CLK_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_CLK_SEL, (BIT_MASK_PESOC_SDR_CK_SEL << BIT_SHIFT_PESOC_SDR_CK_SEL), BIT_PESOC_SDR_CK_SEL(num)) +#define I2C_SCLK_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_CLK_SEL, (BIT_MASK_PESOC_PERI_SCLK_SEL << BIT_SHIFT_PESOC_PERI_SCLK_SEL), BIT_PESOC_PERI_SCLK_SEL(num)) + +//270 REG_OSC32K_CTRL +#define OSC32K_CKGEN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_OSC32K_CTRL, BIT_32K_POW_CKGEN_EN, ctrl) + +//280 REG_UART_MUX_CTRL +#define UART0_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_UART_MUX_CTRL, BIT_UART0_PIN_EN, ctrl) +#define UART0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_UART_MUX_CTRL, (BIT_MASK_UART0_PIN_SEL << BIT_SHIFT_UART0_PIN_SEL), BIT_UART0_PIN_SEL(num)) +#define UART1_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_UART_MUX_CTRL, BIT_UART1_PIN_EN, ctrl) +#define UART1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_UART_MUX_CTRL, (BIT_MASK_UART1_PIN_SEL << BIT_SHIFT_UART1_PIN_SEL), BIT_UART1_PIN_SEL(num)) +#define UART2_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_UART_MUX_CTRL, BIT_UART2_PIN_EN, ctrl) +#define UART2_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_UART_MUX_CTRL, (BIT_MASK_UART2_PIN_SEL << BIT_SHIFT_UART2_PIN_SEL), BIT_UART2_PIN_SEL(num)) + +//284 REG_SPI_MUX_CTRL +#define SPI0_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SPI_MUX_CTRL, BIT_SPI0_PIN_EN, ctrl) +#define SPI0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_SPI_MUX_CTRL, (BIT_MASK_SPI0_PIN_SEL << BIT_SHIFT_SPI0_PIN_SEL), BIT_SPI0_PIN_SEL(num)) +#define SPI1_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SPI_MUX_CTRL, BIT_SPI1_PIN_EN, ctrl) +#define SPI1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_SPI_MUX_CTRL, (BIT_MASK_SPI1_PIN_SEL << BIT_SHIFT_SPI1_PIN_SEL), BIT_SPI1_PIN_SEL(num)) +#define SPI2_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SPI_MUX_CTRL, BIT_SPI2_PIN_EN, ctrl) +#define SPI2_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_SPI_MUX_CTRL, (BIT_MASK_SPI2_PIN_SEL << BIT_SHIFT_SPI2_PIN_SEL), BIT_SPI2_PIN_SEL(num)) +#define SPI0_MULTI_CS_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_SPI_MUX_CTRL, BIT_SPI0_MULTI_CS_EN, ctrl) + +//288 REG_I2C_MUX_CTRL +#define I2C0_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2C_MUX_CTRL, BIT_I2C0_PIN_EN, ctrl) +#define I2C0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2C_MUX_CTRL, (BIT_MASK_I2C0_PIN_SEL << BIT_SHIFT_I2C0_PIN_SEL), BIT_I2C0_PIN_SEL(num)) +#define I2C1_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2C_MUX_CTRL, BIT_I2C1_PIN_EN, ctrl) +#define I2C1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2C_MUX_CTRL, (BIT_MASK_I2C1_PIN_SEL << BIT_SHIFT_I2C1_PIN_SEL), BIT_I2C1_PIN_SEL(num)) +#define I2C2_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2C_MUX_CTRL, BIT_I2C2_PIN_EN, ctrl) +#define I2C2_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2C_MUX_CTRL, (BIT_MASK_I2C2_PIN_SEL << BIT_SHIFT_I2C2_PIN_SEL), BIT_I2C2_PIN_SEL(num)) +#define I2C3_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2C_MUX_CTRL, BIT_I2C3_PIN_EN, ctrl) +#define I2C3_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2C_MUX_CTRL, (BIT_MASK_I2C3_PIN_SEL << BIT_SHIFT_I2C3_PIN_SEL), BIT_I2C3_PIN_SEL(num)) + +//28C REG_I2S_MUX_CTRL +#define I2S0_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_I2S0_PIN_EN, ctrl) +#define I2S0_MCK_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_I2S0_MCK_EN, ctrl) +#define I2S0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2S_MUX_CTRL, (BIT_MASK_I2S0_PIN_SEL << BIT_SHIFT_I2S0_PIN_SEL), BIT_I2S0_PIN_SEL(num)) +#define I2S1_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_I2S1_PIN_EN, ctrl) +#define I2S1_MCK_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_I2S1_MCK_EN, ctrl) +#define I2S1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2S_MUX_CTRL, (BIT_MASK_I2S1_PIN_SEL << BIT_SHIFT_I2S1_PIN_SEL), BIT_I2S1_PIN_SEL(num)) +#define PCM0_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_PCM0_PIN_EN, ctrl) +#define PCM0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2S_MUX_CTRL, (BIT_MASK_PCM0_PIN_SEL << BIT_SHIFT_PCM0_PIN_SEL), BIT_PCM0_PIN_SEL(num)) +#define PCM1_PIN_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_I2S_MUX_CTRL, BIT_PCM1_PIN_EN, ctrl) +#define PCM1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_I2S_MUX_CTRL, (BIT_MASK_PCM1_PIN_SEL << BIT_SHIFT_PCM1_PIN_SEL), BIT_PCM1_PIN_SEL(num)) + +//2A0 HCI_PINMUX_CTRL +#define SDIOD_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_HCI_PINMUX_CTRL, BIT_HCI_SDIOD_PIN_EN, ctrl) +#define SDIOH_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_HCI_PINMUX_CTRL, BIT_HCI_SDIOH_PIN_EN, ctrl) +#define MII_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_HCI_PINMUX_CTRL, BIT_HCI_MII_PIN_EN, ctrl) + +//2A4 WL_PINMUX_CTRL +#define LED_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_WL_LED_PIN_EN, ctrl) +#define LED_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_WL_PINMUX_CTRL, (BIT_MASK_WL_LED_PIN_SEL << BIT_SHIFT_WL_LED_PIN_SEL), BIT_WL_LED_PIN_SEL(num)) +#define ANT0_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_WL_ANT0_PIN_EN, ctrl) +#define ANT1_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_WL_ANT1_PIN_EN, ctrl) +#define BTCOEX_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_WL_BTCOEX_PIN_EN, ctrl) +#define BTCMD_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_WL_BTCMD_PIN_EN, ctrl) +#define NFC_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_WL_PINMUX_CTRL, BIT_NFC_PIN_EN, ctrl) + +//2AC PWM_PINMUX_CTRL +#define PWM0_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_PWM0_PIN_EN, ctrl) +#define PWM0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_PWM0_PIN_SEL << BIT_SHIFT_PWM0_PIN_SEL), BIT_PWM0_PIN_SEL(num)) +#define PWM1_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_PWM1_PIN_EN, ctrl) +#define PWM1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_PWM1_PIN_SEL << BIT_SHIFT_PWM1_PIN_SEL), BIT_PWM1_PIN_SEL(num)) +#define PWM2_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_PWM2_PIN_EN, ctrl) +#define PWM2_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_PWM2_PIN_SEL << BIT_SHIFT_PWM2_PIN_SEL), BIT_PWM2_PIN_SEL(num)) +#define PWM3_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_PWM3_PIN_EN, ctrl) +#define PWM3_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_PWM3_PIN_SEL << BIT_SHIFT_PWM3_PIN_SEL), BIT_PWM3_PIN_SEL(num)) +#define ETE0_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_ETE0_PIN_EN, ctrl) +#define ETE0_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_ETE0_PIN_SEL << BIT_SHIFT_ETE0_PIN_SEL), BIT_ETE0_PIN_SEL(num)) +#define ETE1_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_ETE1_PIN_EN, ctrl) +#define ETE1_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_ETE1_PIN_SEL << BIT_SHIFT_ETE1_PIN_SEL), BIT_ETE1_PIN_SEL(num)) +#define ETE2_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_ETE2_PIN_EN, ctrl) +#define ETE2_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_ETE2_PIN_SEL << BIT_SHIFT_ETE2_PIN_SEL), BIT_ETE2_PIN_SEL(num)) +#define ETE3_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PWM_PINMUX_CTRL, BIT_ETE3_PIN_EN, ctrl) +#define ETE3_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PWM_PINMUX_CTRL, (BIT_MASK_ETE3_PIN_SEL << BIT_SHIFT_ETE3_PIN_SEL), BIT_ETE3_PIN_SEL(num)) + +//2C0 CPU_PERIPHERAL_CTRL +#define SPI_FLASH_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_CPU_PERIPHERAL_CTRL, BIT_SPI_FLSH_PIN_EN, ctrl) +#define SPI_FLASH_PIN_SEL(num) HAL_PERL_ON_PIN_SEL(REG_CPU_PERIPHERAL_CTRL, (BIT_MASK_SPI_FLSH_PIN_SEL << BIT_SHIFT_SPI_FLSH_PIN_SEL), BIT_SPI_FLSH_PIN_SEL(num)) +#define SDR_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_CPU_PERIPHERAL_CTRL, BIT_SDR_PIN_EN, ctrl) +#define TRACE_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_CPU_PERIPHERAL_CTRL, BIT_TRACE_PIN_EN, ctrl) +#define LOG_UART_PIN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_CPU_PERIPHERAL_CTRL, BIT_LOG_UART_PIN_EN, ctrl) +#define LOG_UART_IR_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_CPU_PERIPHERAL_CTRL, BIT_LOG_UART_IR_EN, ctrl) + +//300 REG_PESOC_MEM_CTRL +#define SDR_DDL_FCTRL(ctrl) HAL_PERL_ON_PIN_SEL(REG_PESOC_MEM_CTRL, (BIT_MASK_PESOC_SDR_DDL_CTRL << BIT_SHIFT_PESOC_SDR_DDL_CTRL), BIT_PESOC_SDR_DDL_CTRL(ctrl)) +#define FLASH_DDL_FCTRL(ctrl) HAL_PERL_ON_PIN_SEL(REG_PESOC_MEM_CTRL, (BIT_MASK_PESOC_FLASH_DDL_CTRL << BIT_SHIFT_PESOC_FLASH_DDL_CTRL), BIT_PESOC_FLASH_DDL_CTRL(ctrl)) + +//304 REG_PESOC_SOC_CTRL +#define SRAM_MUX_CFG(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_SOC_CTRL, (BIT_MASK_PESOC_SRAM_MUX_CFG << BIT_SHIFT_PESOC_SRAM_MUX_CFG), BIT_PESOC_SRAM_MUX_CFG(num)) +#define LX_WL_SWAP_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_LX_WL_SWAP_SEL, ctrl) +#define LX_MST_SWAP_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_LX_MST_SWAP_SEL, ctrl) +#define LX_SLV_SWAP_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_LX_SLV_SWAP_SEL, ctrl) +#define MII_LX_WRAPPER_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_MII_LX_WRAPPER_EN, ctrl) +#define MII_LX_MST_SWAP_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_MII_LX_MST_SWAP_SEL, ctrl) +#define MII_LX_SLV_SWAP_CTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_SOC_CTRL, BIT_PESOC_MII_LX_SLV_SWAP_SEL, ctrl) +#define GDMA_CFG(num) HAL_PERL_ON_PIN_SEL(REG_PESOC_SOC_CTRL, (BIT_MASK_PESOC_GDMA_CFG << BIT_SHIFT_PESOC_GDMA_CFG), BIT_PESOC_GDMA_CFG(num)) + +//308 PESOC_PERI_CTRL +#define SPI_RN_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PESOC_PERI_CTRL, BIT_SOC_FUNC_SPI_RN, ctrl) + +//320 GPIO_SHTDN_CTRL +#define GPIO_GPA_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPA_SHTDN_N, ctrl) +#define GPIO_GPB_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPB_SHTDN_N, ctrl) +#define GPIO_GPC_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPC_SHTDN_N, ctrl) +#define GPIO_GPD_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPD_SHTDN_N, ctrl) +#define GPIO_GPE_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPE_SHTDN_N, ctrl) +#define GPIO_GPF_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPF_SHTDN_N, ctrl) +#define GPIO_GPG_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPG_SHTDN_N, ctrl) +#define GPIO_GPH_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPH_SHTDN_N, ctrl) +#define GPIO_GPI_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPI_SHTDN_N, ctrl) +#define GPIO_GPJ_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPJ_SHTDN_N, ctrl) +#define GPIO_GPK_SHTDN_N_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_GPIO_SHTDN_CTRL, BIT_GPIO_GPK_SHTDN_N, ctrl) + +//374 +#define EGTIM_FCTRL(ctrl) HAL_PERL_ON_FUNC_CTRL(REG_PERI_EGTIM_CTRL, BIT_PERI_EGTIM_EN, ctrl) +#define EGTIM_RSIG_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PERI_EGTIM_CTRL, (BIT_MASK_PERI_EGTIM_REF_SIG_SEL << BIT_SHIFT_PERI_EGTIM_REF_SIG_SEL), BIT_PERI_EGTIM_REF_SIG_SEL(num)) +#define EGTIME_PIN_G0_OPT_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PERI_EGTIM_CTRL, (BIT_MASK_PERI_EGTIM_PIN_GROUP0_OPT_SEL << BIT_SHIFT_PERI_EGTIM_PIN_GROUP0_OPT_SEL), BIT_PERI_EGTIM_PIN_GROUP0_OPT_SEL(num)) +#define EGTIME_PIN_G1_OPT_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PERI_EGTIM_CTRL, (BIT_MASK_PERI_EGTIM_PIN_GROUP1_OPT_SEL << BIT_SHIFT_PERI_EGTIM_PIN_GROUP1_OPT_SEL), BIT_PERI_EGTIM_PIN_GROUP1_OPT_SEL(num)) +#define EGTIME_PIN_G2_OPT_SEL(num) HAL_PERL_ON_PIN_SEL(REG_PERI_EGTIM_CTRL, (BIT_MASK_PERI_EGTIM_PIN_GROUP2_OPT_SEL << BIT_SHIFT_PERI_EGTIM_PIN_GROUP2_OPT_SEL), BIT_PERI_EGTIM_PIN_GROUP2_OPT_SEL(num)) + + +#endif //_HAL_PERI_ON_H_ + diff --git a/component/soc/realtek/8195a/fwlib/hal_pinmux.h b/component/soc/realtek/8195a/fwlib/hal_pinmux.h new file mode 100644 index 0000000..9a6596e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_pinmux.h @@ -0,0 +1,60 @@ +#ifndef _HAL_PINMUX_ +#define _HAL_PINMUX_ + + +//Function Index +#define UART0 0 +#define UART1 1 +#define UART2 2 +#define SPI0 8 +#define SPI1 9 +#define SPI2 10 +#define SPI0_MCS 15 +#define I2C0 16 +#define I2C1 17 +#define I2C2 18 +#define I2C3 19 +#define I2S0 24 +#define I2S1 25 +#define PCM0 28 +#define PCM1 29 +#define SDIOD 64 +#define SDIOH 65 +#define MII 88 +#define WL_LED 96 +#define WL_ANT0 104 +#define WL_ANT1 105 +#define WL_BTCOEX 108 +#define WL_BTCMD 109 +#define NFC 112 +#define PWM0 160 +#define PWM1 161 +#define PWM2 162 +#define PWM3 163 +#define ETE0 164 +#define ETE1 165 +#define ETE2 166 +#define ETE3 167 +#define EGTIM 168 +#define SPI_FLASH 196 +#define SDR 200 +#define JTAG 216 +#define TRACE 217 +#define LOG_UART 220 +#define LOG_UART_IR 221 +#define SIC 224 +#define EEPROM 225 +#define DEBUG 226 + +//Location Index(Pin Mux Selection) +#define S0 0 +#define S1 1 +#define S2 2 +#define S3 3 + +_LONG_CALL_ u8 +HalPinCtrlRtl8195A( + IN u32 Function, + IN u32 PinLocation, + IN BOOL Operation); +#endif //_HAL_PINMUX_ \ No newline at end of file diff --git a/component/soc/realtek/8195a/fwlib/hal_platform.h b/component/soc/realtek/8195a/fwlib/hal_platform.h new file mode 100644 index 0000000..d0979d7 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_platform.h @@ -0,0 +1,102 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _HAL_PLATFORM_ +#define _HAL_PLATFORM_ + +#define ROMVERSION 0x03 +#define ROMINFORMATION (ROMVERSION) + +#define SYSTEM_CLK PLATFORM_CLOCK + +#define SDR_SDRAM_BASE 0x30000000 +#define SYSTEM_CTRL_BASE 0x40000000 +#define PERI_ON_BASE 0x40000000 +#define VENDOR_REG_BASE 0x40002800 +#define SPI_FLASH_BASE 0x98000000 +#define SDR_CTRL_BASE 0x40005000 + +#define PERIPHERAL_IRQ_STATUS 0x04 +#define PERIPHERAL_IRQ_MODE 0x08 +#define PERIPHERAL_IRQ_EN 0x0C +#define LP_PERI_EXT_IRQ_STATUS 0x24 +#define LP_PERI_EXT_IRQ_MODE 0x28 +#define LP_PERI_EXT_IRQ_EN 0x2C + +#define PERIPHERAL_IRQ_ALL_LEVEL 0 + +#define TIMER_CLK 32*1000 + +//3 Peripheral IP Base Address +#define GPIO_REG_BASE 0x40001000 +#define TIMER_REG_BASE 0x40002000 +#define NFC_INTERFACE_BASE 0x40002400 +#define LOG_UART_REG_BASE 0x40003000 +#define I2C2_REG_BASE 0x40003400 +#define I2C3_REG_BASE 0x40003800 +#define SPI_FLASH_CTRL_BASE 0x40006000 +#define ADC_REG_BASE 0x40010000 +#define DAC_REG_BASE 0x40011000 +#define UART0_REG_BASE 0x40040000 +#define UART1_REG_BASE 0x40040400 +#define UART2_REG_BASE 0x40040800 +#define SPI0_REG_BASE 0x40042000 +#define SPI1_REG_BASE 0x40042400 +#define SPI2_REG_BASE 0x40042800 +#define I2C0_REG_BASE 0x40044000 +#define I2C1_REG_BASE 0x40044400 +#define SDIO_DEVICE_REG_BASE 0x40050000 +#define MII_REG_BASE 0x40050000 +#define SDIO_HOST_REG_BASE 0x40058000 +#define GDMA0_REG_BASE 0x40060000 +#define GDMA1_REG_BASE 0x40061000 +#define I2S0_REG_BASE 0x40062000 +#define I2S1_REG_BASE 0x40063000 +#define PCM0_REG_BASE 0x40064000 +#define PCM1_REG_BASE 0x40065000 +#define CRYPTO_REG_BASE 0x40070000 +#define WIFI_REG_BASE 0x40080000 +#define USB_OTG_REG_BASE 0x400C0000 + +#define GDMA1_REG_OFF 0x1000 +#define I2S1_REG_OFF 0x1000 +#define PCM1_REG_OFF 0x1000 +#define SSI_REG_OFF 0x400 +#define RUART_REG_OFF 0x400 + +#define CPU_CLK_TYPE_NO 6 + +enum _BOOT_TYPE_ { + BOOT_FROM_FLASH = 0, + BOOT_FROM_SDIO = 1, + BOOT_FROM_USB = 2, + BOOT_FROM_RSVD = 3, +}; + +enum _EFUSE_CPU_CLK_ { + #if 1 + CLK_200M = 0, + CLK_100M = 1, + CLK_50M = 2, + CLK_25M = 3, + CLK_12_5M = 4, + CLK_4M = 5, + #else + CLK_25M = 0, + CLK_200M = 1, + CLK_100M = 2, + CLK_50M = 3, + CLK_12_5M = 4, + CLK_4M = 5, + #endif +}; + + +#endif //_HAL_PLATFORM_ diff --git a/component/soc/realtek/8195a/fwlib/hal_pwm.h b/component/soc/realtek/8195a/fwlib/hal_pwm.h new file mode 100644 index 0000000..9863782 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_pwm.h @@ -0,0 +1,57 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_PWM_H_ +#define _HAL_PWM_H_ + +#define MAX_PWM_CTRL_PIN 4 +// the minimum tick time for G-timer is 61 us (clock source = 32768Hz, reload value=1 and reload takes extra 1T) +#define GTIMER_TICK_US 31 // micro-second, 1000000/32768 ~= 30.5 +#define MIN_GTIMER_TIMEOUT 61 // in micro-sec, use this value to set the g-timer to generate tick for PWM. 61=(1000000/32768)*2 +#define PWM_GTIMER_TICK_TIME 61 // in micro-sec, use this value to set the g-timer to generate tick for PWM. 61=(1000000/32768)*2 + +typedef struct _HAL_PWM_ADAPTER_ { + u8 pwm_id; // the PWM ID, 0~3 + u8 sel; // PWM Pin selection, 0~3 + u8 gtimer_id; // using G-Timer ID, there are 7 G-timer, but we prefer to use timer 3~6 + u8 enable; // is enabled +// u32 timer_value; // the G-Timer auto-reload value, source clock is 32768Hz, reload will takes extra 1 tick. To set the time of a tick of PWM + u32 tick_time; // the tick time for the G-timer + u32 period; // the period of a PWM control cycle, in PWM tick + u32 pulsewidth; // the pulse width in a period of a PWM control cycle, in PWM tick. To control the ratio +// float duty_ratio; // the dyty ratio = pulswidth/period +}HAL_PWM_ADAPTER, *PHAL_PWM_ADAPTER; + + +extern HAL_Status +HAL_Pwm_Init( + u32 pwm_id, + u32 sel +); + +extern void +HAL_Pwm_Enable( + u32 pwm_id +); + +extern void +HAL_Pwm_Disable( + u32 pwm_id +); + +extern void +HAL_Pwm_SetDuty( + u32 pwm_id, + u32 period, + u32 pulse_width +); + + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_sdio.h b/component/soc/realtek/8195a/fwlib/hal_sdio.h new file mode 100644 index 0000000..197e142 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_sdio.h @@ -0,0 +1,241 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_SDIO_H_ +#define _HAL_SDIO_H_ + +#include "rtl8195a_sdio.h" + +#if !SDIO_BOOT_DRIVER +#include "mailbox.h" +#endif + +#if SDIO_BOOT_DRIVER +typedef struct _HAL_SDIO_ADAPTER_ { + u8 *pTXBDAddr; /* The TX_BD start address */ + PSDIO_TX_BD pTXBDAddrAligned; /* The TX_BD start address, it must be 4-bytes aligned */ + PSDIO_TX_BD_HANDLE pTXBDHdl; /* point to the allocated memory for TX_BD Handle array */ + u16 TXBDWPtr; /* The SDIO TX(Host->Device) BD local write index, different with HW maintained write Index. */ + u16 TXBDRPtr; /* The SDIO TX(Host->Device) BD read index */ + u16 TXBDRPtrReg; /* The SDIO TX(Host->Device) BD read index has been write to HW register */ + u16 reserve1; + + u8 *pRXBDAddr; /* The RX_BD start address */ + PSDIO_RX_BD pRXBDAddrAligned; /* The RX_BD start address, it must be 8-bytes aligned */ + PSDIO_RX_BD_HANDLE pRXBDHdl; /* point to the allocated memory for RX_BD Handle array */ + u16 RXBDWPtr; /* The SDIO RX(Device->Host) BD write index */ + u16 RXBDRPtr; /* The SDIO RX(Device->Host) BD local read index, different with HW maintained Read Index. */ + u16 IntMask; /* The Interrupt Mask */ + u16 IntStatus; /* The Interrupt Status */ + u32 Events; /* The Event to the SDIO Task */ + + u32 EventSema; /* Semaphore for SDIO events, use to wakeup the SDIO task */ + u8 CCPWM; /* the value write to register CCPWM, which will sync to Host HCPWM */ + u8 reserve2; + u16 CCPWM2; /* the value write to register CCPWM2, which will sync to Host HCPWM2 */ + + s8 (*Tx_Callback)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize); /* to hook the WLan driver TX callback function to handle a Packet TX */ + VOID *pTxCb_Adapter; /* a pointer will be used to call the TX Callback function, + which is from the TX CallBack function register */ + s8 (*pTxCallback_Backup)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize); // Use to back up the registered TX Callback function, for MP/Normal mode switch + VOID *pTxCb_Adapter_Backup; // Backup the pTxCb_Adapter, for MP/Normal mode switch + _LIST FreeTxPktList; /* The list to queue free Tx packets handler */ + _LIST RxPktList; /* The list to queue RX packets */ + _LIST FreeRxPktList; /* The list to queue free Rx packets handler */ + SDIO_TX_PACKET *pTxPktHandler; /* to store allocated TX Packet handler memory address */ + SDIO_RX_PACKET *pRxPktHandler; /* to store allocated RX Packet handler memory address */ + u32 RxInQCnt; /* The packet count for Rx In Queue */ + u32 MemAllocCnt; // Memory allocated count, for debug only + u32 MAllocFailedCnt; // MemAlloc Failed count, for debugging + +// VOID *pHalOp; /* point to HAL operation function table */ +} HAL_SDIO_ADAPTER, *PHAL_SDIO_ADAPTER; + +extern BOOL SDIO_Device_Init_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev +); +extern VOID SDIO_Device_DeInit_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev +); +extern VOID SDIO_Send_C2H_IOMsg_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 *C2HMsg +); +extern u8 SDIO_Send_C2H_PktMsg_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *C2HMsg, + IN u16 MsgLen +); +extern VOID SDIO_Register_Tx_Callback_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN s8 (*Tx_Callback)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize), + IN VOID *pAdapter +); +extern s8 SDIO_Rx_Callback_Rom( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN VOID *pData, + IN u16 Offset, + IN u16 Length, + IN u8 CmdType +); + +#else // else of "#if SDIO_BOOT_DRIVER" +typedef struct _HAL_SDIO_ADAPTER_ { +// u8 *pTxBuff; /* point to the SDIO TX Buffer */ +// u8 *pTxBuffAligned; /* point to the SDIO TX Buffer with 4-bytes aligned */ +// u32 TXFifoRPtr; /* The SDIO TX(Host->Device) FIFO buffer read pointer */ + + u8 *pTXBDAddr; /* The TX_BD start address */ + PSDIO_TX_BD pTXBDAddrAligned; /* The TX_BD start address, it must be 4-bytes aligned */ + PSDIO_TX_BD_HANDLE pTXBDHdl; /* point to the allocated memory for TX_BD Handle array */ + u16 TXBDWPtr; /* The SDIO TX(Host->Device) BD local write index, different with HW maintained write Index. */ + u16 TXBDRPtr; /* The SDIO TX(Host->Device) BD read index */ + u16 TXBDRPtrReg; /* The SDIO TX(Host->Device) BD read index has been write to HW register */ + + u8 *pRXBDAddr; /* The RX_BD start address */ + PSDIO_RX_BD pRXBDAddrAligned; /* The RX_BD start address, it must be 8-bytes aligned */ + PSDIO_RX_BD_HANDLE pRXBDHdl; /* point to the allocated memory for RX_BD Handle array */ + u16 RXBDWPtr; /* The SDIO RX(Device->Host) BD write index */ + u16 RXBDRPtr; /* The SDIO RX(Device->Host) BD local read index, different with HW maintained Read Index. */ + u16 IntMask; /* The Interrupt Mask */ + u16 IntStatus; /* The Interrupt Status */ + u32 Events; /* The Event to the SDIO Task */ + +#if !TASK_SCHEDULER_DISABLED + _Sema TxSema; /* Semaphore for SDIO TX, use to wakeup the SDIO TX task */ + _Sema RxSema; /* Semaphore for SDIO RX, use to wakeup the SDIO RX task */ +#else + u32 EventSema; /* Semaphore for SDIO events, use to wakeup the SDIO task */ +#endif + s8 (*Tx_Callback)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize); /* to hook the WLan driver TX callback function to handle a Packet TX */ + VOID *pTxCb_Adapter; /* a pointer will be used to call the TX Callback function, + which is from the TX CallBack function register */ + s8 (*pTxCallback_Backup)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize); // Use to back up the registered TX Callback function, for MP/Normal mode switch + VOID *pTxCb_Adapter_Backup; // Backup the pTxCb_Adapter, for MP/Normal mode switch + _LIST FreeTxPktList; /* The list to queue free Tx packets handler */ + _LIST RxPktList; /* The list to queue RX packets */ + _LIST FreeRxPktList; /* The list to queue free Rx packets handler */ +// _LIST RecyclePktList; /* The list to queue packets handler to be recycled */ + SDIO_TX_PACKET *pTxPktHandler; /* to store allocated TX Packet handler memory address */ + SDIO_RX_PACKET *pRxPktHandler; /* to store allocated RX Packet handler memory address */ + _Mutex RxMutex; /* The Mutex to protect RxPktList */ + u32 RxInQCnt; /* The packet count for Rx In Queue */ +#if SDIO_DEBUG + _Mutex StatisticMutex; /* The Mutex to protect Statistic data */ + u32 MemAllocCnt; // Memory allocated count, for debug only + u32 MAllocFailedCnt; // MemAlloc Failed count, for debugging +#endif + VOID *pHalOp; /* point to HAL operation function table */ + RTL_MAILBOX *pMBox; /* the Mail box for other driver module can send message to SDIO driver */ + +#ifdef PLATFORM_FREERTOS + xTaskHandle xSDIOTxTaskHandle; /* The handle of the SDIO Task for TX, can be used to delte the task */ + xTaskHandle xSDIORxTaskHandle; /* The handle of the SDIO Task speical for RX, can be used to delte the task */ +#endif + +#if SDIO_MP_MODE +#if !TASK_SCHEDULER_DISABLED + u32 MP_Events; /* The Event to the SDIO Task */ + _Sema MP_EventSema; /* Semaphore for SDIO events, use to wakeup the SDIO task */ + RTL_MAILBOX *pMP_MBox; /* the Mail box for communication with other driver module */ +#ifdef PLATFORM_FREERTOS + xTaskHandle MP_TaskHandle; /* The handle of the MP loopback Task, can be used to delte the task */ +#endif // end of "#ifdef PLATFORM_FREERTOS" +#endif // end of "#if !TASK_SCHEDULER_DISABLED" + // for MP mode + RTL_TIMER *pPeriodTimer; /* a timer to calculate throughput periodically */ + u8 MP_ModeEn; /* is in MP mode */ + u8 MP_LoopBackEn; /* is loop-back enabled */ + u8 MP_ContinueTx; /* is continue TX test enabled */ + u8 MP_ContinueRx; /* is continue RX test enabled */ + u8 MP_ContinueRxMode; /* continue RX test mode: static RX Buf, Dyna-Allocate RX Buf, Pre-Allocate RX Buf */ + u8 MP_CRxInfinite; /* is non-stop SDIO RX, no packet count limit */ + u16 MP_CRxSize; /* SDIO RX test packet size */ + u8 *pMP_CRxBuf; // the buffer for continye RX test + u32 MP_CRxPktCnt; /* SDIO RX test packet count */ + u32 MP_CRxPktPendingCnt; /* SDIO RX test packet pening count */ + u32 MP_TxPktCnt; /* SDIO TX packet count */ + u32 MP_RxPktCnt; /* SDIO RX packet count */ + u32 MP_TxByteCnt; /* SDIO TX Byte count */ + u32 MP_RxByteCnt; /* SDIO RX Byte count */ + u32 MP_TxDropCnt; /* SDIO TX Drop packet count */ + u32 MP_RxDropCnt; /* SDIO RX Drop packet count */ + + u32 MP_TxPktCntInPeriod; /* SDIO TX packet count in a period */ + u32 MP_RxPktCntInPeriod; /* SDIO RX packet count in a period */ + u32 MP_TxByteCntInPeriod; /* SDIO TX Byte count in a period */ + u32 MP_RxByteCntInPeriod; /* SDIO RX Byte count in a period */ + + u32 MP_TxAvgTPWin[SDIO_AVG_TP_WIN_SIZE]; /* a window of SDIO TX byte count history, for average throughput calculation */ + u32 MP_RxAvgTPWin[SDIO_AVG_TP_WIN_SIZE]; /* a window of SDIO RX byte count history, for average throughput calculation */ + u32 MP_TxAvgTPWinSum; /* The sum of all byte-count in the window */ + u32 MP_RxAvgTPWinSum; /* The sum of all byte-count in the window */ + u8 OldestTxAvgWinIdx; /* the index of the oldest TX byte count log */ + u8 TxAvgWinCnt; /* the number of log in the Window */ + u8 OldestRxAvgWinIdx; /* the index of the oldest RX byte count log */ + u8 RxAvgWinCnt; /* the number of log in the Window */ + + _LIST MP_RxPktList; /* The list to queue RX packets, for MP loopback test */ +#endif // end of '#if SDIO_MP_MODE' +} HAL_SDIO_ADAPTER, *PHAL_SDIO_ADAPTER; +#endif // end of "#else of "#if SDIO_BOOT_DRIVER"" + + +typedef struct _HAL_SDIO_OP_ { + BOOL (*HalSdioDevInit)(PHAL_SDIO_ADAPTER pSDIODev); + VOID (*HalSdioDevDeInit)(PHAL_SDIO_ADAPTER pSDIODev); + VOID (*HalSdioSendC2HIOMsg)(PHAL_SDIO_ADAPTER pSDIODev, u32 *C2HMsg); + u8 (*HalSdioSendC2HPktMsg)(PHAL_SDIO_ADAPTER pSDIODev, u8 *C2HMsg, u16 MsgLen); + VOID (*HalSdioRegTxCallback)(PHAL_SDIO_ADAPTER pSDIODev,s8 (*CallbackFun)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize), VOID *pAdapter); + s8 (*HalSdioRxCallback)(PHAL_SDIO_ADAPTER pSDIODev, VOID *pData, u16 Offset, u16 PktSize, u8 CmdType); +#if SDIO_MP_MODE + VOID (*HalSdioDevMPApp)(PHAL_SDIO_ADAPTER pSDIODev, u16 argc, u8 *argv[]); +#endif +}HAL_SDIO_OP, *PHAL_SDIO_OP; + + +extern BOOL SDIO_Device_Init( + IN PHAL_SDIO_ADAPTER pSDIODev +); +extern VOID SDIO_Device_DeInit( + IN PHAL_SDIO_ADAPTER pSDIODev +); +extern VOID SDIO_Send_C2H_IOMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 *C2HMsg +); +extern u8 SDIO_Send_C2H_PktMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *C2HMsg, + IN u16 MsgLen +); +extern VOID SDIO_Register_Tx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN s8 (*Tx_Callback)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize), + IN VOID *pAdapter +); +extern s8 SDIO_Rx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN VOID *pData, + IN u16 Offset, + IN u16 Length, + IN u8 CmdType +); +#if SDIO_MP_MODE +extern VOID SDIO_DeviceMPApp( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u16 argc, + IN u8 *argv[] +); +#endif + +extern PHAL_SDIO_ADAPTER pgSDIODev; +extern VOID HalSdioInit(VOID); +extern VOID HalSdioDeInit(VOID); +#endif // #ifndef _HAL_SDIO_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_sdr_controller.h b/component/soc/realtek/8195a/fwlib/hal_sdr_controller.h new file mode 100644 index 0000000..79487a2 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_sdr_controller.h @@ -0,0 +1,188 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_SDR_CONTROLLER_H_ +#define _HAL_SDR_CONTROLLER_H_ + +typedef enum _DRAM_TYPE_ { + DRAM_DDR_1 = 1, + DRAM_DDR_2 = 2, + DRAM_DDR_3 = 3, + DRAM_DDR_4 = 4, + DRAM_SDR = 8 +}DRAM_TYPE; + +typedef enum _DRAM_COLADDR_WTH_ { + DRAM_COLADDR_8B = 0, + DRAM_COLADDR_9B = 1, + DRAM_COLADDR_10B = 2, + DRAM_COLADDR_11B = 3, + DRAM_COLADDR_12B = 4, + DRAM_COLADDR_13B = 5, + DRAM_COLADDR_14B = 6, + DRAM_COLADDR_15B = 7, + DRAM_COLADDR_16B = 8 +}DRAM_COLADDR_WTH; + +typedef enum _DRAM_BANK_SIZE_ { + DRAM_BANK_2 = 0, + DRAM_BANK_4 = 1, + DRAM_BANK_8 = 2 +}DRAM_BANK_SIZE; + +typedef enum _DRAM_DQ_WIDTH_ { + DRAM_DQ_16 = 0, + DRAM_DQ_32 = 1, + DRAM_HALF_DQ32 = 2 +}DRAM_DQ_WIDTH; + +typedef enum _MODE0_BST_LEN_ { + BST_LEN_4 = 0, + BST_LEN_FLY = 1, + BST_LEN_8 = 2 +}MODE0_BST_LEN; + +typedef enum _MODE0_BST_TYPE_ { + SENQUENTIAL = 0, + INTERLEAVE = 1 +}MODE0_BST_TYPE; + +typedef enum _DFI_RATIO_TYPE_ { + DFI_RATIO_1 = 0, // DFI= 1:1, or SDR + DFI_RATIO_2 = 1, + DFI_RATIO_4 = 2 +}DFI_RATIO_TYPE; + +typedef struct _DRAM_INFO_ { + DRAM_TYPE DeviceType; + DRAM_COLADDR_WTH ColAddrWth; + DRAM_BANK_SIZE Bank; + DRAM_DQ_WIDTH DqWidth; +}DRAM_INFO; + +typedef struct _DRAM_MODE_REG_INFO_ { + MODE0_BST_LEN BstLen; + MODE0_BST_TYPE BstType; + //enum mode0_cas rd_cas; + u32 Mode0Cas; + u32 Mode0Wr; + u32 Mode1DllEnN; + u32 Mode1AllLat; + u32 Mode2Cwl; +}DRAM_MODE_REG_INFO; + +typedef struct _DRAM_TIMING_INFO_ { + u32 TrfcPs; + u32 TrefiPs; + u32 WrMaxTck; + u32 TrcdPs; + u32 TrpPs; + u32 TrasPs; + u32 TrrdTck; + u32 TwrPs; + u32 TwtrTck; + //u32 TrtpPs; + u32 TmrdTck; + u32 TrtpTck; + u32 TccdTck; + u32 TrcPs; +}DRAM_TIMING_INFO; + + +typedef struct _DRAM_DEVICE_INFO_ { + DRAM_INFO *Dev; + DRAM_MODE_REG_INFO *ModeReg; + DRAM_TIMING_INFO *Timing; + u32 DdrPeriodPs; + DFI_RATIO_TYPE *DfiRate; +}DRAM_DEVICE_INFO; + + +//====================================================== +//DRAM Info +#ifdef CONFIG_FPGA + #define DRAM_INFO_TYPE DRAM_SDR + #define DRAM_INFO_COL_ADDR_WTH DRAM_COLADDR_9B + #define DRAM_INFO_BANK_SZ DRAM_BANK_4 + #define DRAM_INFO_DQ_WTH DRAM_DQ_16 +#else + #define DRAM_INFO_TYPE DRAM_SDR + #define DRAM_INFO_COL_ADDR_WTH DRAM_COLADDR_8B + #define DRAM_INFO_BANK_SZ DRAM_BANK_2 + #define DRAM_INFO_DQ_WTH DRAM_DQ_16 +#endif + +//====================================================== +//DRAM Timing +#ifdef CONFIG_SDR_100MHZ +#define DRAM_TIMING_TCK 10000 //ps +#endif +#ifdef CONFIG_SDR_50MHZ +#define DRAM_TIMING_TCK 20000 //ps +#endif +#ifdef CONFIG_SDR_25MHZ +#define DRAM_TIMING_TCK 40000 //ps +#endif +#ifdef CONFIG_SDR_12_5MHZ +#define DRAM_TIMING_TCK 80000 //ps +#endif + +#if 1 +#define DRAM_TIMING_TREF 64000 //us +#define DRAM_ROW_NUM 8192 //depends on row bit number + +#define DRAM_TIMING_TRFC 60000 //ps +#define DRAM_TIMING_TREFI ((u32)((DRAM_TIMING_TREF*1000)/DRAM_ROW_NUM)*1000) //ps +#define DRAM_TIMING_TWRMAXTCK 2 //tck +#define DRAM_TIMING_TRCD 15000 //ps +#define DRAM_TIMING_TRP 15000 //ps +#define DRAM_TIMING_TRAS 42000 //ps +#define DRAM_TIMING_TRRD 2 //tck +#define DRAM_TIMING_TWR ((u32)(DRAM_TIMING_TCK*2)) +#define DRAM_TIMING_TWTR 0 //tck +#define DRAM_TIMING_TMRD 2 //tck +#define DRAM_TIMING_TRTP 0 //tck +#define DRAM_TIMING_TCCD 1 //tck +#define DRAM_TIMING_TRC 60000 //ps +#else + +#define DRAM_TIMING_TREF 66000 //us +#define DRAM_ROW_NUM 8192 //depends on row bit number + +#define DRAM_TIMING_TRFC 66000 //ps +#define DRAM_TIMING_TREFI 63999800 +#define DRAM_TIMING_TWRMAXTCK 2 //tck +#define DRAM_TIMING_TRCD 15000 //ps +#define DRAM_TIMING_TRP 15000 //ps +#define DRAM_TIMING_TRAS 37000 //ps +#define DRAM_TIMING_TRRD 2 //tck +#define DRAM_TIMING_TWR 7000 +#define DRAM_TIMING_TWTR 0 //tck +#define DRAM_TIMING_TMRD 2 //tck +#define DRAM_TIMING_TRTP 0 //tck +#define DRAM_TIMING_TCCD 1 //tck +#define DRAM_TIMING_TRC 60000 //ps +#endif + +#define HAL_SDR_WRITE32(addr, value32) HAL_WRITE32(SDR_CTRL_BASE, addr, value32) +#define HAL_SDR_WRITE16(addr, value16) HAL_WRITE16(SDR_CTRL_BASE, addr, value16) +#define HAL_SDR_WRITE8(addr, value8) HAL_WRITE8(SDR_CTRL_BASE, addr, value8) +#define HAL_SDR_READ32(addr) HAL_READ32(SDR_CTRL_BASE, addr) +#define HAL_SDR_READ16(addr) HAL_READ16(SDR_CTRL_BASE, addr) +#define HAL_SDR_READ8(addr) HAL_READ8(SDR_CTRL_BASE, addr) + +#define HAL_SDRAM_WRITE32(addr, value32) HAL_WRITE32(SDR_SDRAM_BASE, addr, value32) +#define HAL_SDRAM_WRITE16(addr, value16) HAL_WRITE16(SDR_SDRAM_BASE, addr, value16) +#define HAL_SDRAM_WRITE8(addr, value8) HAL_WRITE8(SDR_SDRAM_BASE, addr, value8) +#define HAL_SDRAM_READ32(addr) HAL_READ32(SDR_SDRAM_BASE, addr) +#define HAL_SDRAM_READ16(addr) HAL_READ16(SDR_SDRAM_BASE, addr) +#define HAL_SDRAM_READ8(addr) HAL_READ8(SDR_SDRAM_BASE, addr) + + +#endif // end of "#ifndef _HAL_SDR_CONTROLLER_H_" diff --git a/component/soc/realtek/8195a/fwlib/hal_soc_ps_monitor.h b/component/soc/realtek/8195a/fwlib/hal_soc_ps_monitor.h new file mode 100644 index 0000000..8e33bdc --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_soc_ps_monitor.h @@ -0,0 +1,254 @@ +#ifndef _HAL_SOCPWR_ +#define _HAL_SOCPWR_ + + + +#define MAX_BACKUP_SIZE 129 +#define MAXFUNC 10 +#define FSTREG 0xFF + +#define REG_VDR_ANACK_CAL_CTRL 0xA0 + +//SLP +#define SLP_STIMER BIT0 +#define SLP_GTIMER BIT1 +#define SLP_GPIO BIT2 +#define SLP_WL BIT3 +#define SLP_NFC BIT4 +#define SLP_SDIO BIT5 +#define SLP_USB BIT6 +#define SLP_TIMER33 BIT7 + +//DSTBY +#define DSTBY_STIMER BIT0 +#define DSTBY_NFC BIT1 +#define DSTBY_TIMER33 BIT2 +#define DSTBY_GPIO BIT3 + +//DS wake event +#define DS_TIMER33 BIT0 +#define DS_GPIO BIT1 + +enum power_state_idx{ + ACT = 0, + WFE = 1, + WFI = 2, + SNOOZE = 3, + SLPCG = 4, + SLPPG = 5, + DSTBY = 6, + DSLP = 7, + INACT = 8, + MAXSTATE = 9 +}; + +enum clk_idx{ + ANACK = 0, + A33CK = 1, +}; + + +typedef struct _power_state_{ + u8 FuncIdx; + u8 PowerState; + u32 ReqDuration; + u32 RegCount; + u32 RemainDuration; +}POWER_STATE, *pPOWER_STATE; + +typedef struct _reg_power_state_{ + u8 FuncIdx; + u8 PwrState; + u32 ReqDuration; + //u8 StateIdx; +}REG_POWER_STATE, *pPREG_POWER_STATE; + +typedef struct _power_mgn_{ + u8 ActFuncCount; + POWER_STATE PwrState[MAXFUNC]; + u8 CurrentState; + u32 MSPbackup[MAX_BACKUP_SIZE]; + u32 CPURegbackup[25]; + u32 CPUPSP; + u32 WakeEventFlag; + BOOL SleepFlag; + //u32 CPUReg[13]; + //u32 MSBackUp[128]; +}Power_Mgn, *pPower_Mgn; + +typedef struct _SYS_ADAPTER_ { + u8 function; +}SYS_ADAPTER, *PSYS_ADAPTER; + +extern Power_Mgn PwrAdapter; + +u8 ChangeSoCPwrState( + IN u8 RequestState, + IN u32 ReqCount +); + +VOID PrintCPU(VOID); +void WakeFromSLPPG(void); +VOID SOCPSTestApp(VOID *Data); + + +__inline static VOID +CPURegBackUp( + VOID +) +{ +#if defined (__ICCARM__) + // TODO: IAR has different way using assembly +#elif defined (__GNUC__) + //backup cpu reg + #if 0 + asm volatile + ( + "PUSH {PSR, PC, LR, R12,R3,R2,R1,R0}\n" + ); + #endif + #if 0 + asm volatile + ( + "PUSH {r0,r1,r2,r3,r4}\n" + ); + #endif + + asm volatile + ( + + "MOV %0, r0\n" + :"=r"(PwrAdapter.CPURegbackup[0]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r1\n" + :"=r"(PwrAdapter.CPURegbackup[1]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r2\n" + :"=r"(PwrAdapter.CPURegbackup[2]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r3\n" + :"=r"(PwrAdapter.CPURegbackup[3]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r4\n" + :"=r"(PwrAdapter.CPURegbackup[4]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r5\n" + :"=r"(PwrAdapter.CPURegbackup[5]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r6\n" + :"=r"(PwrAdapter.CPURegbackup[6]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r7\n" + :"=r"(PwrAdapter.CPURegbackup[7]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r8\n" + :"=r"(PwrAdapter.CPURegbackup[8]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r9\n" + :"=r"(PwrAdapter.CPURegbackup[9]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r10\n" + :"=r"(PwrAdapter.CPURegbackup[10]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r11\n" + :"=r"(PwrAdapter.CPURegbackup[11]) + ::"memory" + ); + asm volatile + ( + "MOV %0, r12\n" + :"=r"(PwrAdapter.CPURegbackup[12]) + ::"memory" + ); + + asm volatile + ( + "MOV %0, r13\n" + :"=r"(PwrAdapter.CPURegbackup[13]) + ::"memory" + ); + asm volatile + ( + //"MOV %0, r14\n" + "LDR %0, =SLPPG_WAKEUP_POINT\n" + "ADD %0, #1\n" + :"=r"(PwrAdapter.CPURegbackup[14]) + ::"memory" + ); + asm volatile + ( + "LDR %0, =SLPPG_WAKEUP_POINT\n" + "ADD %0, #1\n" + :"=r"(PwrAdapter.CPURegbackup[15]) + ::"memory" + ); + asm volatile + ( + "MRS %0, PSR\n" + :"=r"(PwrAdapter.CPURegbackup[16]) + ::"memory" + ); + +#if 1 + asm volatile + ( + "mov %0, r13\n" + "MOV %1, PC\n" + "MRS %2, CONTROL\n" + "MRS %3, PSP\n" + "MRS %4, MSP\n" + :"=r"(PwrAdapter.CPURegbackup[24]),"=r"(PwrAdapter.CPURegbackup[23]),"=r"(PwrAdapter.CPURegbackup[22]),"=r"(PwrAdapter.CPURegbackup[21]),"=r"(PwrAdapter.CPURegbackup[20]) + ::"memory" + ); +#endif + #ifdef CONFIG_SOC_PS_VERIFY + PrintCPU(); + #endif //#ifdef CONFIG_SOC_PS_VERIFY +#endif //#elif defined (__GNUC__) +} + +#endif //_HAL_SOCPWR_ diff --git a/component/soc/realtek/8195a/fwlib/hal_spi_flash.h b/component/soc/realtek/8195a/fwlib/hal_spi_flash.h new file mode 100644 index 0000000..04e1306 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_spi_flash.h @@ -0,0 +1,235 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _HAL_SPIFLASH__ +#define _HAL_SPIFLASH__ +//====================================================== +// Header files +#include "rtl8195a_spi_flash.h" + +#define SPIC_CALIBRATION_IN_NVM 1 // if store the SPIC calibration data in the NVM +#ifndef CONFIG_IMAGE_SEPARATE // Store SPIC Calibration only for seprated image +#undef SPIC_CALIBRATION_IN_NVM +#define SPIC_CALIBRATION_IN_NVM 0 +#endif + +//====================================================== +// Definition +#define HAL_SPI_WRITE32(addr, value32) HAL_WRITE32(SPI_FLASH_CTRL_BASE, addr, value32) +#define HAL_SPI_WRITE16(addr, value16) HAL_WRITE16(SPI_FLASH_CTRL_BASE, addr, value16) +#define HAL_SPI_WRITE8(addr, value8) HAL_WRITE8(SPI_FLASH_CTRL_BASE, addr, value8) +#define HAL_SPI_READ32(addr) HAL_READ32(SPI_FLASH_CTRL_BASE, addr) +#define HAL_SPI_READ16(addr) HAL_READ16(SPI_FLASH_CTRL_BASE, addr) +#define HAL_SPI_READ8(addr) HAL_READ8(SPI_FLASH_CTRL_BASE, addr) + +typedef struct _SPIC_INIT_PARA_ { + u8 BaudRate; + u8 RdDummyCyle; + u8 DelayLine; + union { + u8 Rsvd; + u8 Valid; + }; +}SPIC_INIT_PARA, *PSPIC_INIT_PARA; + + +enum _SPIC_BIT_MODE_ { + SpicOneBitMode = 0, + SpicDualBitMode = 1, + SpicQuadBitMode = 2, +}; + +//====================================================== +// Flash type used +#define FLASH_MXIC_MX25L4006E 1 +#define FLASH_MXIC_MX25L8073E 0 + +// The below parts are based on the flash characteristics +//====== Flash Command Definition ====== +#if FLASH_MXIC_MX25L4006E + #define FLASH_CMD_WREN 0x06 //write enable + #define FLASH_CMD_WRDI 0x04 //write disable + #define FLASH_CMD_WRSR 0x01 //write status register + #define FLASH_CMD_RDID 0x9F //read idenfication + #define FLASH_CMD_RDSR 0x05 //read status register + #define FLASH_CMD_READ 0x03 //read data + #define FLASH_CMD_FREAD 0x0B //fast read data + #define FLASH_CMD_RDSFDP 0x5A //Read SFDP + #define FLASH_CMD_RES 0xAB //Read Electronic ID + #define FLASH_CMD_REMS 0x90 //Read Electronic Manufacturer & Device ID + #define FLASH_CMD_DREAD 0x3B //Double Output Mode command + #define FLASH_CMD_SE 0x20 //Sector Erase + #define FLASH_CMD_BE 0xD8 //Block Erase(or 0x52) + #define FLASH_CMD_CE 0x60 //Chip Erase(or 0xC7) + #define FLASH_CMD_PP 0x02 //Page Program + #define FLASH_CMD_DP 0xB9 //Deep Power Down + #define FLASH_CMD_RDP 0xAB //Release from Deep Power-Down +#elif FLASH_MXIC_MX25L8073E + #define FLASH_CMD_WREN 0x06 //write enable + #define FLASH_CMD_WRDI 0x04 //write disable + #define FLASH_CMD_WRSR 0x01 //write status register + #define FLASH_CMD_RDID 0x9F //read idenfication + #define FLASH_CMD_RDSR 0x05 //read status register + #define FLASH_CMD_READ 0x03 //read data + #define FLASH_CMD_FREAD 0x0B //fast read data + #define FLASH_CMD_RDSFDP 0x5A //Read SFDP + #define FLASH_CMD_RES 0xAB //Read Electronic ID + #define FLASH_CMD_REMS 0x90 //Read Electronic Manufacturer & Device ID + #define FLASH_CMD_DREAD 0x3B //Double Output Mode command + #define FLASH_CMD_SE 0x20 //Sector Erase + #define FLASH_CMD_BE 0x52 //Block Erase + #define FLASH_CMD_CE 0x60 //Chip Erase(or 0xC7) + #define FLASH_CMD_PP 0x02 //Page Program + #define FLASH_CMD_DP 0xB9 //Deep Power Down + #define FLASH_CMD_RDP 0xAB //Release from Deep Power-Down + #define FLASH_CMD_2READ 0xBB // 2 x I/O read command + #define FLASH_CMD_4READ 0xEB // 4 x I/O read command + #define FLASH_CMD_QREAD 0x6B // 1I / 4O read command + #define FLASH_CMD_4PP 0x38 //quad page program + #define FLASH_CMD_FF 0xFF //Release Read Enhanced + #define FLASH_CMD_REMS2 0xEF // read ID for 2x I/O mode + #define FLASH_CMD_REMS4 0xDF // read ID for 4x I/O mode + #define FLASH_CMD_ENSO 0xB1 // enter secured OTP + #define FLASH_CMD_EXSO 0xC1 // exit secured OTP + #define FLASH_CMD_RDSCUR 0x2B // read security register + #define FLASH_CMD_WRSCUR 0x2F // write security register +#else + #define FLASH_CMD_WREN 0x06 //write enable + #define FLASH_CMD_WRDI 0x04 //write disable + #define FLASH_CMD_WRSR 0x01 //write status register + #define FLASH_CMD_RDID 0x9F //read idenfication + #define FLASH_CMD_RDSR 0x05 //read status register + #define FLASH_CMD_READ 0x03 //read data + #define FLASH_CMD_FREAD 0x0B //fast read data + #define FLASH_CMD_RDSFDP 0x5A //Read SFDP + #define FLASH_CMD_RES 0xAB //Read Electronic ID + #define FLASH_CMD_REMS 0x90 //Read Electronic Manufacturer & Device ID + #define FLASH_CMD_DREAD 0x3B //Double Output Mode command + #define FLASH_CMD_SE 0x20 //Sector Erase + #define FLASH_CMD_BE 0x52 //Block Erase + #define FLASH_CMD_CE 0x60 //Chip Erase(or 0xC7) + #define FLASH_CMD_PP 0x02 //Page Program + #define FLASH_CMD_DP 0xB9 //Deep Power Down + #define FLASH_CMD_RDP 0xAB //Release from Deep Power-Down + #define FLASH_CMD_2READ 0xBB // 2 x I/O read command + #define FLASH_CMD_4READ 0xEB // 4 x I/O read command + #define FLASH_CMD_QREAD 0x6B // 1I / 4O read command + #define FLASH_CMD_4PP 0x38 //quad page program + #define FLASH_CMD_FF 0xFF //Release Read Enhanced + #define FLASH_CMD_REMS2 0xEF // read ID for 2x I/O mode + #define FLASH_CMD_REMS4 0xDF // read ID for 4x I/O mode + #define FLASH_CMD_ENSO 0xB1 // enter secured OTP + #define FLASH_CMD_EXSO 0xC1 // exit secured OTP + #define FLASH_CMD_RDSCUR 0x2B // read security register + #define FLASH_CMD_WRSCUR 0x2F // write security register +#endif //#if FLASH_MXIC_MX25L4006E +// ============================ + +// ===== Flash Parameter Definition ===== +#if FLASH_MXIC_MX25L4006E + #define FLASH_RD_2IO_EN 0 + #define FLASH_RD_2O_EN 1 + #define FLASH_RD_4IO_EN 0 + #define FLASH_RD_4O_EN 0 + #define FLASH_WR_2IO_EN 0 + #define FLASH_WR_2O_EN 0 + #define FLASH_WR_4IO_EN 0 + #define FLASH_WR_4O_EN 0 + + #define FLASH_DM_CYCLE_2O 0x08 + #define FLASH_VLD_DUAL_CMDS (BIT_WR_BLOCKING | BIT_RD_DUAL_I) + #define FLASH_VLD_QUAD_CMDS (0) + +#elif FLASH_MXIC_MX25L8073E //This flash model is just for prototype, if you want to use it, + //the code MUST be rechecked according to the flash spec. + #define FLASH_RD_2IO_EN 1 + #define FLASH_RD_2O_EN 0 + #define FLASH_RD_4IO_EN 1 + #define FLASH_RD_4O_EN 0 + #define FLASH_WR_2IO_EN 1 + #define FLASH_WR_2O_EN 0 + #define FLASH_WR_4IO_EN 1 + #define FLASH_WR_4O_EN 0 + + #define FLASH_DM_CYCLE_2O 0x08 + #define FLASH_DM_CYCLE_2IO 0x04 + #define FLASH_DM_CYCLE_4O 0x08 + #define FLASH_DM_CYCLE_4IO 0x04 + + #define FLASH_VLD_DUAL_CMDS (BIT_WR_BLOCKING | BIT_RD_DUAL_IO) + #define FLASH_VLD_QUAD_CMDS (BIT_WR_BLOCKING | BIT_WR_QUAD_II | BIT_RD_QUAD_IO) +#else + #define FLASH_RD_2IO_EN 1 + #define FLASH_RD_2O_EN 0 + #define FLASH_RD_4IO_EN 1 + #define FLASH_RD_4O_EN 0 + #define FLASH_WR_2IO_EN 1 + #define FLASH_WR_2O_EN 0 + #define FLASH_WR_4IO_EN 1 + #define FLASH_WR_4O_EN 0 + + #define FLASH_DM_CYCLE_2O 0x08 + #define FLASH_DM_CYCLE_2IO 0x04 + #define FLASH_DM_CYCLE_4O 0x08 + #define FLASH_DM_CYCLE_4IO 0x04 + + #define FLASH_VLD_DUAL_CMDS (BIT_WR_BLOCKING | BIT_RD_DUAL_IO) + #define FLASH_VLD_QUAD_CMDS (BIT_WR_BLOCKING | BIT_WR_QUAD_II | BIT_RD_QUAD_IO) +#endif +#if 0 +//====================================================== +// Function prototype +BOOLEAN SpicFlashInitRtl8195A(u8 SpicBitMode); + +_LONG_CALL_ +extern VOID SpicLoadInitParaFromClockRtl8195A(u8 CpuClkMode, u8 BaudRate, PSPIC_INIT_PARA pSpicInitPara); + +// spi-flash controller initialization +_LONG_CALL_ +extern VOID SpicInitRtl8195A(u8 InitBaudRate, u8 SpicBitMode); + +// wait sr[0] = 0, wait transmission done +_LONG_CALL_ +extern VOID SpicWaitBusyDoneRtl8195A(VOID); + +// wait spi-flash status register[0] = 0 +//_LONG_CALL_ +//extern VOID SpicWaitWipDoneRtl8195A(SPIC_INIT_PARA SpicInitPara); +#endif + +//====================================================== +// ROM Function prototype +_LONG_CALL_ VOID SpiFlashAppV02(IN VOID *Data); +_LONG_CALL_ VOID SpicInitRtl8195AV02(IN u8 InitBaudRate,IN u8 SpicBitMode); + +_LONG_CALL_ VOID SpicEraseFlashRtl8195AV02(); + +_LONG_CALL_ VOID SpicLoadInitParaFromClockRtl8195AV02(IN u8 CpuClkMode,IN u8 BaudRate,IN PSPIC_INIT_PARA pSpicInitPara); + + +VOID SpicBlockEraseFlashRtl8195A(IN u32 Address); +VOID SpicSectorEraseFlashRtl8195A(IN u32 Address); +VOID SpicWriteProtectFlashRtl8195A(IN u32 Protect); +VOID SpicWaitWipDoneRefinedRtl8195A(IN SPIC_INIT_PARA SpicInitPara); +VOID SpicRxCmdRefinedRtl8195A(IN u8 cmd,IN SPIC_INIT_PARA SpicInitPara); +u8 SpicGetFlashStatusRefinedRtl8195A(IN SPIC_INIT_PARA SpicInitPara); +VOID SpicInitRefinedRtl8195A(IN u8 InitBaudRate,IN u8 SpicBitMode); +u32 SpicWaitWipRtl8195A(VOID); +u32 SpicOneBitCalibrationRtl8195A(IN u8 SysCpuClk); +VOID SpicDisableRtl8195A(VOID); +VOID SpicDeepPowerDownFlashRtl8195A(VOID); + +#if SPIC_CALIBRATION_IN_NVM +VOID SpicNVMCalLoad(u8 BitMode, u8 CpuClk); +VOID SpicNVMCalLoadAll(void); +VOID SpicNVMCalStore(u8 BitMode, u8 CpuClk); +#endif // #if SPIC_CALIBRATION_IN_NVM + +#endif //_HAL_SPIFLASH__ diff --git a/component/soc/realtek/8195a/fwlib/hal_ssi.h b/component/soc/realtek/8195a/fwlib/hal_ssi.h new file mode 100644 index 0000000..792bec4 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_ssi.h @@ -0,0 +1,303 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_SSI_H_ +#define _HAL_SSI_H_ + +#include "rtl8195a_ssi.h" + +/** + * LOG Configurations + */ + +extern u32 SSI_DBG_CONFIG; +extern uint8_t SPI0_IS_AS_SLAVE; + + +#define SSI_DBG_ENTRANCE(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_ENTRANCE)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE ANSI_COLOR_GREEN __VA_ARGS__ ANSI_COLOR_RESET); \ +}while(0) + +#define SSI_DBG_INIT(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INIT)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INIT_V(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INIT_V)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INIT_VV(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INIT_VV)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_PINMUX(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_PINMUX)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_ENDIS(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_ENDIS)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INT(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INT)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INT_V(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INT_V)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INT_HNDLR(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INT_HNDLR)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INT_READ(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INT_READ)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_INT_WRITE(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_INT_WRITE)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_STATUS(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_STATUS)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_FIFO(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_FIFO)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_READ(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_READ)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_WRITE(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_WRITE)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +#define SSI_DBG_SLV_CTRL(...) do {\ + if (unlikely(SSI_DBG_CONFIG & DBG_TYPE_SLV_CTRL)) \ + DBG_SSI_INFO(IDENT_FOUR_SPACE __VA_ARGS__); \ +}while(0) + +typedef enum _SSI_DBG_TYPE_LIST_ { + DBG_TYPE_ENTRANCE = 1 << 0, + DBG_TYPE_INIT = 1 << 1, + DBG_TYPE_INIT_V = 1 << 2, + DBG_TYPE_INIT_VV = 1 << 3, + DBG_TYPE_PINMUX = 1 << 4, + DBG_TYPE_ENDIS = 1 << 5, + DBG_TYPE_INT = 1 << 6, + DBG_TYPE_INT_V = 1 << 7, + DBG_TYPE_INT_HNDLR = 1 << 8, + DBG_TYPE_INT_READ = 1 << 9, + DBG_TYPE_INT_WRITE = 1 << 10, + DBG_TYPE_STATUS = 1 << 11, + DBG_TYPE_FIFO = 1 << 12, + DBG_TYPE_READ = 1 << 13, + DBG_TYPE_WRITE = 1 << 14, + DBG_TYPE_SLV_CTRL = 1 << 15 +} SSI_DBG_TYPE_LIST, *PSSI_DBG_TYPE_LIST; + + typedef struct _SSI_DMA_CONFIG_ { + VOID *pHalGdmaOp; + VOID *pTxHalGdmaAdapter; + VOID *pRxHalGdmaAdapter; + u8 RxDmaBurstSize; + u8 TxDmaBurstSize; + u8 RxDmaEnable; + u8 TxDmaEnable; + IRQ_HANDLE RxGdmaIrqHandle; + IRQ_HANDLE TxGdmaIrqHandle; +}SSI_DMA_CONFIG, *PSSI_DMA_CONFIG; + +/** + * DesignWare SSI Configurations + */ +typedef struct _HAL_SSI_ADAPTOR_ { + SSI_DMA_CONFIG DmaConfig; + IRQ_HANDLE IrqHandle; + // + VOID (*RxCompCallback)(VOID *Para); + VOID *RxCompCbPara; + VOID *RxData; + VOID (*TxCompCallback)(VOID *Para); + VOID *TxCompCbPara; + VOID *TxData; + u32 DmaRxDataLevel; + u32 DmaTxDataLevel; + u32 InterruptPriority; + u32 RxLength; + u32 RxLengthRemainder; + u32 RxThresholdLevel; + u32 TxLength; + u32 TxThresholdLevel; + u32 SlaveSelectEnable; + // + u16 ClockDivider; + u16 DataFrameNumber; + // + u8 ControlFrameSize; + u8 DataFrameFormat; + u8 DataFrameSize; + u8 DmaControl; + u8 Index; + u8 InterruptMask; + u8 MicrowireDirection; + u8 MicrowireHandshaking; + u8 MicrowireTransferMode; + u8 PinmuxSelect; + u8 Role; + u8 SclkPhase; + u8 SclkPolarity; + u8 SlaveOutputEnable; + u8 TransferMode; + u8 TransferMechanism; + + // Extend + u32 Reserved1; + u8 DefaultRxThresholdLevel; +}HAL_SSI_ADAPTOR, *PHAL_SSI_ADAPTOR; + +typedef struct _HAL_SSI_OP_{ + HAL_Status (*HalSsiPinmuxEnable)(VOID *Adaptor); + HAL_Status (*HalSsiPinmuxDisable)(VOID *Adaptor); + HAL_Status (*HalSsiEnable)(VOID *Adaptor); + HAL_Status (*HalSsiDisable)(VOID *Adaptor); + HAL_Status (*HalSsiInit)(VOID *Adaptor); + HAL_Status (*HalSsiSetSclkPolarity)(VOID *Adaptor); + HAL_Status (*HalSsiSetSclkPhase)(VOID *Adaptor); + HAL_Status (*HalSsiWrite)(VOID *Adaptor, u32 value); + HAL_Status (*HalSsiLoadSetting)(VOID *Adaptor, VOID *Setting); + HAL_Status (*HalSsiSetInterruptMask)(VOID *Adaptor); + HAL_Status (*HalSsiSetDeviceRole)(VOID *Adaptor, u32 Role); + HAL_Status (*HalSsiInterruptEnable)(VOID *Adaptor); + HAL_Status (*HalSsiInterruptDisable)(VOID *Adaptor); + HAL_Status (*HalSsiReadInterrupt)(VOID *Adaptor, VOID *RxData, u32 Length); + HAL_Status (*HalSsiSetRxFifoThresholdLevel)(VOID *Adaptor); + HAL_Status (*HalSsiSetTxFifoThresholdLevel)(VOID *Adaptor); + HAL_Status (*HalSsiWriteInterrupt)(VOID *Adaptor, u8 *TxData, u32 Length); + HAL_Status (*HalSsiSetSlaveEnableRegister)(VOID *Adaptor, u32 SlaveIndex); + u32 (*HalSsiBusy)(VOID *Adaptor); + u32 (*HalSsiReadable)(VOID *Adaptor); + u32 (*HalSsiWriteable)(VOID *Adaptor); + u32 (*HalSsiGetInterruptMask)(VOID *Adaptor); + u32 (*HalSsiGetRxFifoLevel)(VOID *Adaptor); + u32 (*HalSsiGetTxFifoLevel)(VOID *Adaptor); + u32 (*HalSsiGetStatus)(VOID *Adaptor); + u32 (*HalSsiGetInterruptStatus)(VOID *Adaptor); + u32 (*HalSsiRead)(VOID *Adaptor); + u32 (*HalSsiGetRawInterruptStatus)(VOID *Adaptor); + u32 (*HalSsiGetSlaveEnableRegister)(VOID *Adaptor); +}HAL_SSI_OP, *PHAL_SSI_OP; + +typedef struct _DW_SSI_DEFAULT_SETTING_ { + VOID (*RxCompCallback)(VOID *Para); + VOID *RxCompCbPara; + VOID *RxData; + VOID (*TxCompCallback)(VOID *Para); + VOID *TxCompCbPara; + VOID *TxData; + u32 DmaRxDataLevel; + u32 DmaTxDataLevel; + u32 InterruptPriority; + u32 RxLength; + u32 RxLengthRemainder; + u32 RxThresholdLevel; + u32 TxLength; + u32 TxThresholdLevel; + u32 SlaveSelectEnable; + // + u16 ClockDivider; + u16 DataFrameNumber; + // + u8 ControlFrameSize; + u8 DataFrameFormat; + u8 DataFrameSize; + u8 DmaControl; + //u8 Index; + u8 InterruptMask; + u8 MicrowireDirection; + u8 MicrowireHandshaking; + u8 MicrowireTransferMode; + //u8 PinmuxSelect; + //u8 Role; + u8 SclkPhase; + u8 SclkPolarity; + u8 SlaveOutputEnable; + u8 TransferMode; + u8 TransferMechanism; +} DW_SSI_DEFAULT_SETTING, *PDW_SSI_DEFAULT_SETTING; + + +struct spi_s { + HAL_SSI_ADAPTOR spi_adp; + HAL_SSI_OP spi_op; + u32 irq_handler; + u32 irq_id; + u32 dma_en; + u32 state; + u8 sclk; +#ifdef CONFIG_GDMA_EN + HAL_GDMA_ADAPTER spi_gdma_adp_tx; + HAL_GDMA_ADAPTER spi_gdma_adp_rx; +#endif +}; + +VOID HalSsiOpInit(VOID *Adaptor); +static __inline__ VOID HalSsiSetSclk( + IN PHAL_SSI_ADAPTOR pHalSsiAdapter, + IN u32 ClkRate) +{ + HalSsiSetSclkRtl8195a((VOID*)pHalSsiAdapter, ClkRate); +} + +#ifdef CONFIG_GDMA_EN +HAL_Status HalSsiTxGdmaInit(PHAL_SSI_OP pHalSsiOp, PHAL_SSI_ADAPTOR pHalSsiAdapter); +VOID HalSsiTxGdmaDeInit(PHAL_SSI_ADAPTOR pHalSsiAdapter); +HAL_Status HalSsiRxGdmaInit(PHAL_SSI_OP pHalSsiOp, PHAL_SSI_ADAPTOR pHalSsiAdapter); +VOID HalSsiRxGdmaDeInit(PHAL_SSI_ADAPTOR pHalSsiAdapter); + +static __inline__ VOID +HalSsiDmaInit( + IN PHAL_SSI_ADAPTOR pHalSsiAdapter +) +{ + HalSsiDmaInitRtl8195a((void *)pHalSsiAdapter); +} + +static __inline__ HAL_Status HalSsiDmaSend(VOID *Adapter, u8 *pTxData, u32 Length) +{ + return (HalSsiDmaSendRtl8195a(Adapter, pTxData, Length)); +} + +static __inline__ HAL_Status HalSsiDmaRecv(VOID *Adapter, u8 *pRxData, u32 Length) +{ + return (HalSsiDmaRecvRtl8195a(Adapter, pRxData, Length)); +} + + +#endif // end of "#ifdef CONFIG_GDMA_EN" + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_timer.h b/component/soc/realtek/8195a/fwlib/hal_timer.h new file mode 100644 index 0000000..d0f8bc9 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_timer.h @@ -0,0 +1,57 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_TIMER_H_ +#define _HAL_TIMER_H_ +#include "basic_types.h" +#include "hal_platform.h" +#include "rtl8195a_timer.h" + +#define TIMER_TICK_US_X4 (4*1000000/32000) + +typedef enum _TIMER_MODE_ { + FREE_RUN_MODE = 0, + USER_DEFINED = 1 +}TIMER_MODE, *PTIMER_MODE; + + +typedef struct _TIMER_ADAPTER_ { + + u32 TimerLoadValueUs; + u32 TimerIrqPriority; + TIMER_MODE TimerMode; + IRQ_HANDLE IrqHandle; + u8 TimerId; + u8 IrqDis; + +}TIMER_ADAPTER, *PTIMER_ADAPTER; + + +typedef struct _HAL_TIMER_OP_ { + u32 (*HalGetTimerId)(u32 *TimerId); + BOOL (*HalTimerInit)(VOID *Data); + u32 (*HalTimerReadCount)(u32 TimerId); + VOID (*HalTimerIrqClear)(u32 TimerId); + VOID (*HalTimerDis)(u32 TimerId); + VOID (*HalTimerEn)(u32 TimerId); + VOID (*HalTimerDumpReg)(u32 TimerId); +}HAL_TIMER_OP, *PHAL_TIMER_OP; + +VOID HalTimerOpInit_Patch( + IN VOID *Data +); + + +//====================================================== +// ROM Function prototype +_LONG_CALL_ VOID HalTimerOpInitV02(IN VOID *Data); + +#define HalTimerOpInit HalTimerOpInit_Patch + +#endif diff --git a/component/soc/realtek/8195a/fwlib/hal_uart.h b/component/soc/realtek/8195a/fwlib/hal_uart.h new file mode 100644 index 0000000..c228b90 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_uart.h @@ -0,0 +1,145 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _HAL_UART_H_ +#define _HAL_UART_H_ + +#include "rtl8195a_uart.h" + +/** + * RUART Configurations + */ +#define UART_WAIT_FOREVER 0xffffffff + +typedef struct _UART_DMA_CONFIG_ { + u8 TxDmaEnable; + u8 RxDmaEnable; + u8 TxDmaBurstSize; + u8 RxDmaBurstSize; + VOID *pHalGdmaOp; + VOID *pTxHalGdmaAdapter; + VOID *pRxHalGdmaAdapter; + IRQ_HANDLE TxGdmaIrqHandle; + IRQ_HANDLE RxGdmaIrqHandle; +}UART_DMA_CONFIG, *PUART_DMA_CONFIG; + +typedef struct _HAL_RUART_ADAPTER_ { + u32 BaudRate; + u32 FlowControl; + u32 FifoControl; + u32 Interrupts; + u32 TxCount; // how many byte to TX + u32 RxCount; // how many bytes to RX + u8 *pTxBuf; + u8 *pRxBuf; + HAL_UART_State State; // UART state + u8 Status; // Transfer Status + u8 Locked; // is UART locked for operation + u8 UartIndex; + u8 WordLen; // word length select: 0 -> 7 bits, 1 -> 8 bits + u8 StopBit; // word length select: 0 -> no stop bit, 1 -> 1 stop bit + u8 Parity; // parity check enable + u8 ParityType; // parity check type + u8 StickParity; + u8 ModemStatus; // the modem status + u8 DmaEnable; + u8 TestCaseNumber; + u8 PinmuxSelect; + BOOL PullMode; + IRQ_HANDLE IrqHandle; + PUART_DMA_CONFIG DmaConfig; + VOID (*ModemStatusInd)(VOID *pAdapter); // modem status indication interrupt handler + VOID (*TxTDCallback)(VOID *pAdapter); // User Tx Done callback function + VOID (*RxDRCallback)(VOID *pAdapter); // User Rx Data ready callback function + VOID (*TxCompCallback)(VOID *para); // User Tx complete callback function + VOID (*RxCompCallback)(VOID *para); // User Rx complete callback function + VOID *TxTDCbPara; // the pointer agrument for TxTDCallback + VOID *RxDRCbPara; // the pointer agrument for RxDRCallback + VOID *TxCompCbPara; // the pointer argument for TxCompCbPara + VOID *RxCompCbPara; // the pointer argument for RxCompCallback + VOID (*EnterCritical)(void); + VOID (*ExitCritical)(void); +}HAL_RUART_ADAPTER, *PHAL_RUART_ADAPTER; + +typedef struct _HAL_RUART_OP_ { + VOID (*HalRuartAdapterLoadDef)(VOID *pAdp, u8 UartIdx); // Load UART adapter default setting + VOID (*HalRuartTxGdmaLoadDef)(VOID *pAdp, VOID *pCfg); // Load TX GDMA default setting + VOID (*HalRuartRxGdmaLoadDef)(VOID *pAdp, VOID *pCfg); // Load RX GDMA default setting + HAL_Status (*HalRuartResetRxFifo)(VOID *Data); + HAL_Status (*HalRuartInit)(VOID *Data); + VOID (*HalRuartDeInit)(VOID *Data); + HAL_Status (*HalRuartPutC)(VOID *Data, u8 TxData); + u32 (*HalRuartSend)(VOID *Data, u8 *pTxData, u32 Length, u32 Timeout); + HAL_Status (*HalRuartIntSend)(VOID *Data, u8 *pTxData, u32 Length); + HAL_Status (*HalRuartDmaSend)(VOID *Data, u8 *pTxData, u32 Length); + HAL_Status (*HalRuartStopSend)(VOID *Data); + HAL_Status (*HalRuartGetC)(VOID *Data, u8 *pRxByte); + u32 (*HalRuartRecv)(VOID *Data, u8 *pRxData, u32 Length, u32 Timeout); + HAL_Status (*HalRuartIntRecv)(VOID *Data, u8 *pRxData, u32 Length); + HAL_Status (*HalRuartDmaRecv)(VOID *Data, u8 *pRxData, u32 Length); + HAL_Status (*HalRuartStopRecv)(VOID *Data); + u8 (*HalRuartGetIMR)(VOID *Data); + VOID (*HalRuartSetIMR)(VOID *Data); + u32 (*HalRuartGetDebugValue)(VOID *Data, u32 DbgSel); + VOID (*HalRuartDmaInit)(VOID *Data); + VOID (*HalRuartRTSCtrl)(VOID *Data, BOOLEAN RtsCtrl); + VOID (*HalRuartRegIrq)(VOID *Data); + VOID (*HalRuartIntEnable)(VOID *Data); + VOID (*HalRuartIntDisable)(VOID *Data); +}HAL_RUART_OP, *PHAL_RUART_OP; + +typedef struct _RUART_DATA_ { + PHAL_RUART_ADAPTER pHalRuartAdapter; + BOOL PullMode; + u8 BinaryData; + u8 SendBuffer; + u8 RecvBuffer; +}RUART_DATA, *PRUART_DATA; + +typedef struct _RUART_ADAPTER_ { + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter; + PUART_DMA_CONFIG pHalRuartDmaCfg; +}RUART_ADAPTER, *PRUART_ADAPTER; + +extern VOID +HalRuartOpInit( + IN VOID *Data +); + +extern HAL_Status +HalRuartTxGdmaInit( + PHAL_RUART_OP pHalRuartOp, + PHAL_RUART_ADAPTER pHalRuartAdapter, + PUART_DMA_CONFIG pUartGdmaConfig +); + +extern VOID +HalRuartTxGdmaDeInit( + PUART_DMA_CONFIG pUartGdmaConfig +); + +extern HAL_Status +HalRuartRxGdmaInit( + PHAL_RUART_OP pHalRuartOp, + PHAL_RUART_ADAPTER pHalRuartAdapter, + PUART_DMA_CONFIG pUartGdmaConfig +); + +extern VOID +HalRuartRxGdmaDeInit( + PUART_DMA_CONFIG pUartGdmaConfig +); + +extern const HAL_RUART_OP _HalRuartOp; +extern HAL_Status RuartLock (PHAL_RUART_ADAPTER pHalRuartAdapter); +extern VOID RuartUnLock (PHAL_RUART_ADAPTER pHalRuartAdapter); + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/hal_util.h b/component/soc/realtek/8195a/fwlib/hal_util.h new file mode 100644 index 0000000..d600ed6 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_util.h @@ -0,0 +1,252 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#ifndef _HAL_UTIL_H_ +#define _HAL_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct LIST_HEADER { + struct LIST_HEADER *Next, *Prev; +}; + +typedef struct LIST_HEADER _LIST; + +//#define RTL_LIST_HEAD_INIT(name) { &(name), &(name) } + +#define RTL_INIT_LIST_HEAD(ptr) do { \ + (ptr)->Next = (ptr); (ptr)->Prev = (ptr); \ +} while (0) + + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ + static __inline__ VOID + __List_Add( + IN struct LIST_HEADER * New, + IN struct LIST_HEADER * Prev, + IN struct LIST_HEADER * Next +) +{ + Next->Prev = New; + New->Next = Next; + New->Prev = Prev; + Prev->Next = New; +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ + static __inline__ VOID + __List_Del( + IN struct LIST_HEADER * Prev, + IN struct LIST_HEADER * Next + ) +{ + Next->Prev = Prev; + Prev->Next = Next; +} + +/** + * ListDel - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ VOID +ListDel( + IN struct LIST_HEADER *Entry +) +{ + __List_Del(Entry->Prev, Entry->Next); +} + +/** + * ListDelInit - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ VOID +ListDelInit( + IN struct LIST_HEADER *Entry +) +{ + __List_Del(Entry->Prev, Entry->Next); + RTL_INIT_LIST_HEAD(Entry); + +} + +/** + * ListEmpty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ u32 +ListEmpty( + IN struct LIST_HEADER *Head +) +{ + return Head->Next == Head; +} + +/** + * ListSplice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ VOID +ListSplice( + IN struct LIST_HEADER *List, + IN struct LIST_HEADER *Head +) +{ + struct LIST_HEADER *First = List->Next; + + if (First != List) { + struct LIST_HEADER *Last = List->Prev; + struct LIST_HEADER *At = Head->Next; + + First->Prev = Head; + Head->Next = First; + + Last->Next = At; + At->Prev = Last; + } +} + +static __inline__ VOID +ListAdd( + IN struct LIST_HEADER *New, + IN struct LIST_HEADER *head +) +{ + __List_Add(New, head, head->Next); +} + + +static __inline__ VOID +ListAddTail( + IN struct LIST_HEADER *New, + IN struct LIST_HEADER *head +) +{ + __List_Add(New, head->Prev, head); +} + +static __inline VOID +RtlInitListhead( + IN _LIST *list +) +{ + RTL_INIT_LIST_HEAD(list); +} + + +/* +For the following list_xxx operations, +caller must guarantee the atomic context. +Otherwise, there will be racing condition. +*/ +static __inline u32 +RtlIsListEmpty( + IN _LIST *phead +) +{ + + if (ListEmpty(phead)) + return _TRUE; + else + return _FALSE; + +} + +static __inline VOID +RtlListInsertHead( + IN _LIST *plist, + IN _LIST *phead +) +{ + ListAdd(plist, phead); +} + +static __inline VOID +RtlListInsertTail( + IN _LIST *plist, + IN _LIST *phead +) +{ + ListAddTail(plist, phead); +} + + +static __inline _LIST +*RtlListGetNext( + IN _LIST *plist +) +{ + return plist->Next; +} + +static __inline VOID +RtlListDelete( + IN _LIST *plist +) +{ + ListDelInit(plist); +} + +#define RTL_LIST_CONTAINOR(ptr, type, member) \ + ((type *)((char *)(ptr)-(SIZE_T)(&((type *)0)->member))) + +#ifndef CONTAINER_OF +#define CONTAINER_OF(ptr, type, member) \ + ((type *)((char *)(ptr)-(SIZE_T)(&((type *)0)->member))) +#endif + +#define list_entry(ptr, type, member) \ + CONTAINER_OF(ptr, type, member) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->Next, type, member) + +#define list_next_entry(pos, member, type) \ + list_entry((pos)->member.Next, type, member) + +#define list_for_each_entry(pos, head, member, type) \ + for (pos = list_first_entry(head, type, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member, type)) +#define list_for_each(pos, head) \ + for (pos = (head)->Next; pos != (head); pos = pos->Next) + + +#ifndef BIT + #define BIT(x) ( 1 << (x)) +#endif + +#ifdef __cplusplus +} +#endif + +#endif //_HAL_UTIL_H_ diff --git a/component/soc/realtek/8195a/fwlib/hal_vector_table.h b/component/soc/realtek/8195a/fwlib/hal_vector_table.h new file mode 100644 index 0000000..3a6d701 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/hal_vector_table.h @@ -0,0 +1,53 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _HAL_VECTOR_TABLE_H_ +#define _HAL_VECTOR_TABLE_H_ + + + + +extern _LONG_CALL_ VOID +VectorTableInitRtl8195A( + IN u32 StackP +); + +extern _LONG_CALL_ VOID +VectorTableInitForOSRtl8195A( + IN VOID *PortSVC, + IN VOID *PortPendSVH, + IN VOID *PortSysTick +); + +extern _LONG_CALL_ BOOL +VectorIrqRegisterRtl8195A( + IN PIRQ_HANDLE pIrqHandle +); + +extern _LONG_CALL_ BOOL +VectorIrqUnRegisterRtl8195A( + IN PIRQ_HANDLE pIrqHandle +); + + +extern _LONG_CALL_ VOID +VectorIrqEnRtl8195A( + IN PIRQ_HANDLE pIrqHandle +); + +extern _LONG_CALL_ VOID +VectorIrqDisRtl8195A( + IN PIRQ_HANDLE pIrqHandle +); + + +extern _LONG_CALL_ VOID +HalPeripheralIntrHandle(VOID); +#endif //_HAL_VECTOR_TABLE_H_ diff --git a/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom/rom_wlan_ram_map.h b/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom/rom_wlan_ram_map.h new file mode 100644 index 0000000..2944d18 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom/rom_wlan_ram_map.h @@ -0,0 +1,9 @@ +#ifndef ROM_WLAN_RAM_MAP_H +#define ROM_WLAN_RAM_MAP_H + +struct _rom_wlan_ram_map { + unsigned char * (*rtw_malloc)(unsigned int sz); + void (*rtw_mfree)(unsigned char *pbuf, unsigned int sz); +}; + +#endif /* ROM_WLAN_RAM_MAP_H */ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a.h new file mode 100644 index 0000000..ea0f430 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a.h @@ -0,0 +1,155 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#ifndef _HAL_8195A_H_ +#define _HAL_8195A_H_ + +#include "platform_autoconf.h" +#include "basic_types.h" +#include "section_config.h" +#include "rtl8195a_sys_on.h" +#include "rtl8195a_peri_on.h" +#include "hal_platform.h" +#include "hal_pinmux.h" +#include "hal_api.h" +#include "hal_peri_on.h" +#include "hal_misc.h" +#include "hal_irqn.h" +#include "hal_vector_table.h" +#include "hal_diag.h" +#include "hal_spi_flash.h" +#include "hal_timer.h" +#include "hal_util.h" +#include "hal_efuse.h" +#include "hal_soc_ps_monitor.h" +#include "diag.h" +#include "hal_common.h" +/* ---------------------------------------------------------------------------- + -- Cortex M3 Core Configuration + ---------------------------------------------------------------------------- */ + +/*! + * @addtogroup Cortex_Core_Configuration Cortex M0 Core Configuration + * @{ + */ + +#define __CM3_REV 0x0200 /**< Core revision r0p0 */ +#define __MPU_PRESENT 1 /**< Defines if an MPU is present or not */ +#define __NVIC_PRIO_BITS 4 /**< Number of priority bits implemented in the NVIC */ +#define __Vendor_SysTickConfig 1 /**< Vendor specific implementation of SysTickConfig is defined */ + +#include "core_cm3.h" + +#ifdef CONFIG_TIMER_EN +#include "hal_timer.h" +#endif + +#ifdef CONFIG_GDMA_EN +#include "hal_gdma.h" +#include "rtl8195a_gdma.h" +#endif + +#ifdef CONFIG_GPIO_EN +#include "hal_gpio.h" +#include "rtl8195a_gpio.h" +#endif + +#ifdef CONFIG_SPI_COM_EN +#include "hal_ssi.h" +#include "rtl8195a_ssi.h" +#endif + +#ifdef CONFIG_UART_EN +#include "hal_uart.h" +#include "rtl8195a_uart.h" +#endif + +#ifdef CONFIG_I2C_EN +#include "hal_i2c.h" +#include "rtl8195a_i2c.h" +#endif + +#ifdef CONFIG_PCM_EN +#include "hal_pcm.h" +#include "rtl8195a_pcm.h" +#endif + +#ifdef CONFIG_PWM_EN +#include "hal_pwm.h" +#include "rtl8195a_pwm.h" +#endif + +#ifdef CONFIG_I2S_EN +#include "hal_i2s.h" +#include "rtl8195a_i2s.h" +#endif + +#ifdef CONFIG_DAC_EN +#include "hal_dac.h" +#include "rtl8195a_dac.h" +#endif + +#ifdef CONFIG_ADC_EN +#include "hal_adc.h" +#include "rtl8195a_adc.h" +#endif + +#ifdef CONFIG_SDR_EN +#endif + +#ifdef CONFIG_SPIC_EN +#endif + +#ifdef CONFIG_SDIO_DEVICE_EN +#include "hal_sdio.h" +#endif + +#ifdef CONFIG_NFC_EN +#include "hal_nfc.h" +#include "rtl8195a_nfc.h" +#endif + +#if CONFIG_WDG +#include "rtl8195a_wdt.h" +#endif + +#ifdef CONFIG_USB_EN +#include "hal_usb.h" +#include "rtl8195a_usb.h" +#endif + + +// firmware information, located at the header of Image2 +#define FW_VERSION (0x0100) +#define FW_SUBVERSION (0x0001) +#define FW_CHIP_ID (0x8195) +#define FW_CHIP_VER (0x01) +#define FW_BUS_TYPE (0x01) // the iNIC firmware type: USB/SDIO +#define FW_INFO_RSV1 (0x00) // the firmware information reserved +#define FW_INFO_RSV2 (0x00) // the firmware information reserved +#define FW_INFO_RSV3 (0x00) // the firmware information reserved +#define FW_INFO_RSV4 (0x00) // the firmware information reserved + +#define FLASH_RESERVED_DATA_BASE 0x8000 // reserve 32K for Image1 +#define FLASH_SYSTEM_DATA_ADDR 0x9000 // reserve 32K+4K for Image1 + Reserved data +// Flash Map for Calibration data +#define FLASH_CAL_DATA_BASE 0xA000 +#define FLASH_CAL_DATA_ADDR(_offset) (FLASH_CAL_DATA_BASE + _offset) +#define FLASH_CAL_DATA_SIZE 0x1000 +#define FLASH_SECTOR_SIZE 0x1000 +// SPIC Calibration Data +#define FLASH_SPIC_PARA_OFFSET 0x80 +#define FLASH_SPIC_PARA_BASE (FLASH_SYSTEM_DATA_ADDR+FLASH_SPIC_PARA_OFFSET) +// SDRC Calibration Data +#define FLASH_SDRC_PARA_OFFSET 0x180 +#define FLASH_SDRC_PARA_BASE (FLASH_SYSTEM_DATA_ADDR+FLASH_SDRC_PARA_OFFSET) +// ADC Calibration Data +#define FLASH_ADC_PARA_OFFSET 0x200 +#define FLASH_ADC_PARA_BASE (FLASH_SYSTEM_DATA_ADDR+FLASH_ADC_PARA_OFFSET) + +#endif //_HAL_8195A_H_ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_adc.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_adc.h new file mode 100644 index 0000000..48240b0 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_adc.h @@ -0,0 +1,350 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL8195A_ADC_H_ +#define _RTL8195A_ADC_H_ + + +//================ Register Bit Field ========================== +//2 REG_ADC_FIFO_READ + +#define BIT_SHIFT_ADC_FIFO_RO 0 +#define BIT_MASK_ADC_FIFO_RO 0xffffffffL +#define BIT_ADC_FIFO_RO(x) (((x) & BIT_MASK_ADC_FIFO_RO) << BIT_SHIFT_ADC_FIFO_RO) +#define BIT_CTRL_ADC_FIFO_RO(x) (((x) & BIT_MASK_ADC_FIFO_RO) << BIT_SHIFT_ADC_FIFO_RO) +#define BIT_GET_ADC_FIFO_RO(x) (((x) >> BIT_SHIFT_ADC_FIFO_RO) & BIT_MASK_ADC_FIFO_RO) + + +//2 REG_ADC_CONTROL + +#define BIT_SHIFT_ADC_DBG_SEL 24 +#define BIT_MASK_ADC_DBG_SEL 0x7 +#define BIT_ADC_DBG_SEL(x) (((x) & BIT_MASK_ADC_DBG_SEL) << BIT_SHIFT_ADC_DBG_SEL) +#define BIT_CTRL_ADC_DBG_SEL(x) (((x) & BIT_MASK_ADC_DBG_SEL) << BIT_SHIFT_ADC_DBG_SEL) +#define BIT_GET_ADC_DBG_SEL(x) (((x) >> BIT_SHIFT_ADC_DBG_SEL) & BIT_MASK_ADC_DBG_SEL) + + +#define BIT_SHIFT_ADC_THRESHOLD 16 +#define BIT_MASK_ADC_THRESHOLD 0x3f +#define BIT_ADC_THRESHOLD(x) (((x) & BIT_MASK_ADC_THRESHOLD) << BIT_SHIFT_ADC_THRESHOLD) +#define BIT_CTRL_ADC_THRESHOLD(x) (((x) & BIT_MASK_ADC_THRESHOLD) << BIT_SHIFT_ADC_THRESHOLD) +#define BIT_GET_ADC_THRESHOLD(x) (((x) >> BIT_SHIFT_ADC_THRESHOLD) & BIT_MASK_ADC_THRESHOLD) + + +#define BIT_SHIFT_ADC_BURST_SIZE 8 +#define BIT_MASK_ADC_BURST_SIZE 0x1f +#define BIT_ADC_BURST_SIZE(x) (((x) & BIT_MASK_ADC_BURST_SIZE) << BIT_SHIFT_ADC_BURST_SIZE) +#define BIT_CTRL_ADC_BURST_SIZE(x) (((x) & BIT_MASK_ADC_BURST_SIZE) << BIT_SHIFT_ADC_BURST_SIZE) +#define BIT_GET_ADC_BURST_SIZE(x) (((x) >> BIT_SHIFT_ADC_BURST_SIZE) & BIT_MASK_ADC_BURST_SIZE) + +#define BIT_ADC_ENDIAN BIT(3) +#define BIT_SHIFT_ADC_ENDIAN 3 +#define BIT_MASK_ADC_ENDIAN 0x1 +#define BIT_CTRL_ADC_ENDIAN(x) (((x) & BIT_MASK_ADC_ENDIAN) << BIT_SHIFT_ADC_ENDIAN) + +#define BIT_ADC_OVERWRITE BIT(2) +#define BIT_SHIFT_ADC_OVERWRITE 2 +#define BIT_MASK_ADC_OVERWRITE 0x1 +#define BIT_CTRL_ADC_OVERWRITE(x) (((x) & BIT_MASK_ADC_OVERWRITE) << BIT_SHIFT_ADC_OVERWRITE) + +#define BIT_ADC_ONESHOT BIT(1) +#define BIT_SHIFT_ADC_ONESHOT 1 +#define BIT_MASK_ADC_ONESHOT 0x1 +#define BIT_CTRL_ADC_ONESHOT(x) (((x) & BIT_MASK_ADC_ONESHOT) << BIT_SHIFT_ADC_ONESHOT) + +#define BIT_ADC_COMP_ONLY BIT(0) +#define BIT_SHIFT_ADC_COMP_ONLY 0 +#define BIT_MASK_ADC_COMP_ONLY 0x1 +#define BIT_CTRL_ADC_COMP_ONLY(x) (((x) & BIT_MASK_ADC_COMP_ONLY) << BIT_SHIFT_ADC_COMP_ONLY) + + +//2 REG_ADC_INTR_EN +#define BIT_ADC_AWAKE_CPU_EN BIT(7) +#define BIT_SHIFT_ADC_AWAKE_CPU_EN 7 +#define BIT_MASK_ADC_AWAKE_CPU_EN 0x1 +#define BIT_CTRL_ADC_AWAKE_CPU_EN(x) (((x) & BIT_MASK_ADC_AWAKE_CPU_EN) << BIT_SHIFT_ADC_AWAKE_CPU_EN) + +#define BIT_ADC_FIFO_RD_ERROR_EN BIT(6) +#define BIT_SHIFT_ADC_FIFO_RD_ERROR_EN 6 +#define BIT_MASK_ADC_FIFO_RD_ERROR_EN 0x1 +#define BIT_CTRL_ADC_FIFO_RD_ERROR_EN(x) (((x) & BIT_MASK_ADC_FIFO_RD_ERROR_EN) << BIT_SHIFT_ADC_FIFO_RD_ERROR_EN) + +#define BIT_ADC_FIFO_RD_REQ_EN BIT(5) +#define BIT_SHIFT_ADC_FIFO_RD_REQ_EN 5 +#define BIT_MASK_ADC_FIFO_RD_REQ_EN 0x1 +#define BIT_CTRL_ADC_FIFO_RD_REQ_EN(x) (((x) & BIT_MASK_ADC_FIFO_RD_REQ_EN) << BIT_SHIFT_ADC_FIFO_RD_REQ_EN) + +#define BIT_ADC_FIFO_FULL_EN BIT(4) +#define BIT_SHIFT_ADC_FIFO_FULL_EN 4 +#define BIT_MASK_ADC_FIFO_FULL_EN 0x1 +#define BIT_CTRL_ADC_FIFO_FULL_EN(x) (((x) & BIT_MASK_ADC_FIFO_FULL_EN) << BIT_SHIFT_ADC_FIFO_FULL_EN) + +#define BIT_ADC_COMP_3_EN BIT(3) +#define BIT_SHIFT_ADC_COMP_3_EN 3 +#define BIT_MASK_ADC_COMP_3_EN 0x1 +#define BIT_CTRL_ADC_COMP_3_EN(x) (((x) & BIT_MASK_ADC_COMP_3_EN) << BIT_SHIFT_ADC_COMP_3_EN) + +#define BIT_ADC_COMP_2_EN BIT(2) +#define BIT_SHIFT_ADC_COMP_2_EN 2 +#define BIT_MASK_ADC_COMP_2_EN 0x1 +#define BIT_CTRL_ADC_COMP_2_EN(x) (((x) & BIT_MASK_ADC_COMP_2_EN) << BIT_SHIFT_ADC_COMP_2_EN) + +#define BIT_ADC_COMP_1_EN BIT(1) +#define BIT_SHIFT_ADC_COMP_1_EN 1 +#define BIT_MASK_ADC_COMP_1_EN 0x1 +#define BIT_CTRL_ADC_COMP_1_EN(x) (((x) & BIT_MASK_ADC_COMP_1_EN) << BIT_SHIFT_ADC_COMP_1_EN) + +#define BIT_ADC_COMP_0_EN BIT(0) +#define BIT_SHIFT_ADC_COMP_0_EN 0 +#define BIT_MASK_ADC_COMP_0_EN 0x1 +#define BIT_CTRL_ADC_COMP_0_EN(x) (((x) & BIT_MASK_ADC_COMP_0_EN) << BIT_SHIFT_ADC_COMP_0_EN) + + +//2 REG_ADC_INTR_STS +#define BIT_ADC_FIFO_THRESHOLD BIT(7) +#define BIT_SHIFT_ADC_FIFO_THRESHOLD 7 +#define BIT_MASK_ADC_FIFO_THRESHOLD 0x1 +#define BIT_CTRL_ADC_FIFO_THRESHOLD(x) (((x) & BIT_MASK_ADC_FIFO_THRESHOLD) << BIT_SHIFT_ADC_FIFO_THRESHOLD) + +#define BIT_ADC_FIFO_RD_ERROR_ST BIT(6) +#define BIT_SHIFT_ADC_FIFO_RD_ERROR_ST 6 +#define BIT_MASK_ADC_FIFO_RD_ERROR_ST 0x1 +#define BIT_CTRL_ADC_FIFO_RD_ERROR_ST(x) (((x) & BIT_MASK_ADC_FIFO_RD_ERROR_ST) << BIT_SHIFT_ADC_FIFO_RD_ERROR_ST) + +#define BIT_ADC_FIFO_RD_REQ_ST BIT(5) +#define BIT_SHIFT_ADC_FIFO_RD_REQ_ST 5 +#define BIT_MASK_ADC_FIFO_RD_REQ_ST 0x1 +#define BIT_CTRL_ADC_FIFO_RD_REQ_ST(x) (((x) & BIT_MASK_ADC_FIFO_RD_REQ_ST) << BIT_SHIFT_ADC_FIFO_RD_REQ_ST) + +#define BIT_ADC_FIFO_FULL_ST BIT(4) +#define BIT_SHIFT_ADC_FIFO_FULL_ST 4 +#define BIT_MASK_ADC_FIFO_FULL_ST 0x1 +#define BIT_CTRL_ADC_FIFO_FULL_ST(x) (((x) & BIT_MASK_ADC_FIFO_FULL_ST) << BIT_SHIFT_ADC_FIFO_FULL_ST) + +#define BIT_ADC_COMP_3_ST BIT(3) +#define BIT_SHIFT_ADC_COMP_3_ST 3 +#define BIT_MASK_ADC_COMP_3_ST 0x1 +#define BIT_CTRL_ADC_COMP_3_ST(x) (((x) & BIT_MASK_ADC_COMP_3_ST) << BIT_SHIFT_ADC_COMP_3_ST) + +#define BIT_ADC_COMP_2_ST BIT(2) +#define BIT_SHIFT_ADC_COMP_2_ST 2 +#define BIT_MASK_ADC_COMP_2_ST 0x1 +#define BIT_CTRL_ADC_COMP_2_ST(x) (((x) & BIT_MASK_ADC_COMP_2_ST) << BIT_SHIFT_ADC_COMP_2_ST) + +#define BIT_ADC_COMP_1_ST BIT(1) +#define BIT_SHIFT_ADC_COMP_1_ST 1 +#define BIT_MASK_ADC_COMP_1_ST 0x1 +#define BIT_CTRL_ADC_COMP_1_ST(x) (((x) & BIT_MASK_ADC_COMP_1_ST) << BIT_SHIFT_ADC_COMP_1_ST) + +#define BIT_ADC_COMP_0_ST BIT(0) +#define BIT_SHIFT_ADC_COMP_0_ST 0 +#define BIT_MASK_ADC_COMP_0_ST 0x1 +#define BIT_CTRL_ADC_COMP_0_ST(x) (((x) & BIT_MASK_ADC_COMP_0_ST) << BIT_SHIFT_ADC_COMP_0_ST) + + +//2 REG_ADC_COMP_VALUE_L + +#define BIT_SHIFT_ADC_COMP_TH_1 16 +#define BIT_MASK_ADC_COMP_TH_1 0xffff +#define BIT_ADC_COMP_TH_1(x) (((x) & BIT_MASK_ADC_COMP_TH_1) << BIT_SHIFT_ADC_COMP_TH_1) +#define BIT_CTRL_ADC_COMP_TH_1(x) (((x) & BIT_MASK_ADC_COMP_TH_1) << BIT_SHIFT_ADC_COMP_TH_1) +#define BIT_GET_ADC_COMP_TH_1(x) (((x) >> BIT_SHIFT_ADC_COMP_TH_1) & BIT_MASK_ADC_COMP_TH_1) + + +#define BIT_SHIFT_ADC_COMP_TH_0 0 +#define BIT_MASK_ADC_COMP_TH_0 0xffff +#define BIT_ADC_COMP_TH_0(x) (((x) & BIT_MASK_ADC_COMP_TH_0) << BIT_SHIFT_ADC_COMP_TH_0) +#define BIT_CTRL_ADC_COMP_TH_0(x) (((x) & BIT_MASK_ADC_COMP_TH_0) << BIT_SHIFT_ADC_COMP_TH_0) +#define BIT_GET_ADC_COMP_TH_0(x) (((x) >> BIT_SHIFT_ADC_COMP_TH_0) & BIT_MASK_ADC_COMP_TH_0) + + +//2 REG_ADC_COMP_VALUE_H + +#define BIT_SHIFT_ADC_COMP_TH_3 16 +#define BIT_MASK_ADC_COMP_TH_3 0xffff +#define BIT_ADC_COMP_TH_3(x) (((x) & BIT_MASK_ADC_COMP_TH_3) << BIT_SHIFT_ADC_COMP_TH_3) +#define BIT_CTRL_ADC_COMP_TH_3(x) (((x) & BIT_MASK_ADC_COMP_TH_3) << BIT_SHIFT_ADC_COMP_TH_3) +#define BIT_GET_ADC_COMP_TH_3(x) (((x) >> BIT_SHIFT_ADC_COMP_TH_3) & BIT_MASK_ADC_COMP_TH_3) + + +#define BIT_SHIFT_ADC_COMP_TH_2 0 +#define BIT_MASK_ADC_COMP_TH_2 0xffff +#define BIT_ADC_COMP_TH_2(x) (((x) & BIT_MASK_ADC_COMP_TH_2) << BIT_SHIFT_ADC_COMP_TH_2) +#define BIT_CTRL_ADC_COMP_TH_2(x) (((x) & BIT_MASK_ADC_COMP_TH_2) << BIT_SHIFT_ADC_COMP_TH_2) +#define BIT_GET_ADC_COMP_TH_2(x) (((x) >> BIT_SHIFT_ADC_COMP_TH_2) & BIT_MASK_ADC_COMP_TH_2) + + +//2 REG_ADC_COMP_SET + +#define BIT_SHIFT_ADC_GREATER_THAN 0 +#define BIT_MASK_ADC_GREATER_THAN 0xf +#define BIT_ADC_GREATER_THAN(x) (((x) & BIT_MASK_ADC_GREATER_THAN) << BIT_SHIFT_ADC_GREATER_THAN) +#define BIT_CTRL_ADC_GREATER_THAN(x) (((x) & BIT_MASK_ADC_GREATER_THAN) << BIT_SHIFT_ADC_GREATER_THAN) +#define BIT_GET_ADC_GREATER_THAN(x) (((x) >> BIT_SHIFT_ADC_GREATER_THAN) & BIT_MASK_ADC_GREATER_THAN) + + +//2 REG_ADC_POWER + +#define BIT_SHIFT_ADC_PWR_CUT_CNTR 16 +#define BIT_MASK_ADC_PWR_CUT_CNTR 0xff +#define BIT_ADC_PWR_CUT_CNTR(x) (((x) & BIT_MASK_ADC_PWR_CUT_CNTR) << BIT_SHIFT_ADC_PWR_CUT_CNTR) +#define BIT_CTRL_ADC_PWR_CUT_CNTR(x) (((x) & BIT_MASK_ADC_PWR_CUT_CNTR) << BIT_SHIFT_ADC_PWR_CUT_CNTR) +#define BIT_GET_ADC_PWR_CUT_CNTR(x) (((x) >> BIT_SHIFT_ADC_PWR_CUT_CNTR) & BIT_MASK_ADC_PWR_CUT_CNTR) + +#define BIT_ADC_FIFO_ON_ST BIT(11) +#define BIT_SHIFT_ADC_FIFO_ON_ST 11 +#define BIT_MASK_ADC_FIFO_ON_ST 0x1 +#define BIT_CTRL_ADC_FIFO_ON_ST(x) (((x) & BIT_MASK_ADC_FIFO_ON_ST) << BIT_SHIFT_ADC_FIFO_ON_ST) + +#define BIT_ADC_ISO_ON_ST BIT(10) +#define BIT_SHIFT_ADC_ISO_ON_ST 10 +#define BIT_MASK_ADC_ISO_ON_ST 0x1 +#define BIT_CTRL_ADC_ISO_ON_ST(x) (((x) & BIT_MASK_ADC_ISO_ON_ST) << BIT_SHIFT_ADC_ISO_ON_ST) + +#define BIT_ADC_PWR33_ON_ST BIT(9) +#define BIT_SHIFT_ADC_PWR33_ON_ST 9 +#define BIT_MASK_ADC_PWR33_ON_ST 0x1 +#define BIT_CTRL_ADC_PWR33_ON_ST(x) (((x) & BIT_MASK_ADC_PWR33_ON_ST) << BIT_SHIFT_ADC_PWR33_ON_ST) + +#define BIT_ADC_PWR12_ON_ST BIT(8) +#define BIT_SHIFT_ADC_PWR12_ON_ST 8 +#define BIT_MASK_ADC_PWR12_ON_ST 0x1 +#define BIT_CTRL_ADC_PWR12_ON_ST(x) (((x) & BIT_MASK_ADC_PWR12_ON_ST) << BIT_SHIFT_ADC_PWR12_ON_ST) + +#define BIT_ADC_ISO_MANUAL BIT(3) +#define BIT_SHIFT_ADC_ISO_MANUAL 3 +#define BIT_MASK_ADC_ISO_MANUAL 0x1 +#define BIT_CTRL_ADC_ISO_MANUAL(x) (((x) & BIT_MASK_ADC_ISO_MANUAL) << BIT_SHIFT_ADC_ISO_MANUAL) + +#define BIT_ADC_PWR33_MANUAL BIT(2) +#define BIT_SHIFT_ADC_PWR33_MANUAL 2 +#define BIT_MASK_ADC_PWR33_MANUAL 0x1 +#define BIT_CTRL_ADC_PWR33_MANUAL(x) (((x) & BIT_MASK_ADC_PWR33_MANUAL) << BIT_SHIFT_ADC_PWR33_MANUAL) + +#define BIT_ADC_PWR12_MANUAL BIT(1) +#define BIT_SHIFT_ADC_PWR12_MANUAL 1 +#define BIT_MASK_ADC_PWR12_MANUAL 0x1 +#define BIT_CTRL_ADC_PWR12_MANUAL(x) (((x) & BIT_MASK_ADC_PWR12_MANUAL) << BIT_SHIFT_ADC_PWR12_MANUAL) + +#define BIT_ADC_PWR_AUTO BIT(0) +#define BIT_SHIFT_ADC_PWR_AUTO 0 +#define BIT_MASK_ADC_PWR_AUTO 0x1 +#define BIT_CTRL_ADC_PWR_AUTO(x) (((x) & BIT_MASK_ADC_PWR_AUTO) << BIT_SHIFT_ADC_PWR_AUTO) + + +//2 REG_ADC_ANAPAR_AD0 + +#define BIT_SHIFT_ADC_ANAPAR_AD0 2 +#define BIT_MASK_ADC_ANAPAR_AD0 0x3fffffff +#define BIT_ADC_ANAPAR_AD0(x) (((x) & BIT_MASK_ADC_ANAPAR_AD0) << BIT_SHIFT_ADC_ANAPAR_AD0) +#define BIT_CTRL_ADC_ANAPAR_AD0(x) (((x) & BIT_MASK_ADC_ANAPAR_AD0) << BIT_SHIFT_ADC_ANAPAR_AD0) +#define BIT_GET_ADC_ANAPAR_AD0(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD0) & BIT_MASK_ADC_ANAPAR_AD0) + +#define BIT_ADC_AUDIO_EN BIT(1) +#define BIT_SHIFT_ADC_AUDIO_EN 1 +#define BIT_MASK_ADC_AUDIO_EN 0x1 +#define BIT_CTRL_ADC_AUDIO_EN(x) (((x) & BIT_MASK_ADC_AUDIO_EN) << BIT_SHIFT_ADC_AUDIO_EN) + +#define BIT_ADC_EN_MANUAL BIT(0) +#define BIT_SHIFT_ADC_EN_MANUAL 0 +#define BIT_MASK_ADC_EN_MANUAL 0x1 +#define BIT_CTRL_ADC_EN_MANUAL(x) (((x) & BIT_MASK_ADC_EN_MANUAL) << BIT_SHIFT_ADC_EN_MANUAL) + + +//2 REG_ADC_ANAPAR_AD1 + +#define BIT_SHIFT_ADC_ANAPAR_AD1 0 +#define BIT_MASK_ADC_ANAPAR_AD1 0xffffffffL +#define BIT_ADC_ANAPAR_AD1(x) (((x) & BIT_MASK_ADC_ANAPAR_AD1) << BIT_SHIFT_ADC_ANAPAR_AD1) +#define BIT_CTRL_ADC_ANAPAR_AD1(x) (((x) & BIT_MASK_ADC_ANAPAR_AD1) << BIT_SHIFT_ADC_ANAPAR_AD1) +#define BIT_GET_ADC_ANAPAR_AD1(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD1) & BIT_MASK_ADC_ANAPAR_AD1) + + +//2 REG_ADC_ANAPAR_AD2 + +#define BIT_SHIFT_ADC_ANAPAR_AD2 0 +#define BIT_MASK_ADC_ANAPAR_AD2 0xffffffffL +#define BIT_ADC_ANAPAR_AD2(x) (((x) & BIT_MASK_ADC_ANAPAR_AD2) << BIT_SHIFT_ADC_ANAPAR_AD2) +#define BIT_CTRL_ADC_ANAPAR_AD2(x) (((x) & BIT_MASK_ADC_ANAPAR_AD2) << BIT_SHIFT_ADC_ANAPAR_AD2) +#define BIT_GET_ADC_ANAPAR_AD2(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD2) & BIT_MASK_ADC_ANAPAR_AD2) + + +//2 REG_ADC_ANAPAR_AD3 + +#define BIT_SHIFT_ADC_ANAPAR_AD3 0 +#define BIT_MASK_ADC_ANAPAR_AD3 0xffffffffL +#define BIT_ADC_ANAPAR_AD3(x) (((x) & BIT_MASK_ADC_ANAPAR_AD3) << BIT_SHIFT_ADC_ANAPAR_AD3) +#define BIT_CTRL_ADC_ANAPAR_AD3(x) (((x) & BIT_MASK_ADC_ANAPAR_AD3) << BIT_SHIFT_ADC_ANAPAR_AD3) +#define BIT_GET_ADC_ANAPAR_AD3(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD3) & BIT_MASK_ADC_ANAPAR_AD3) + + +//2 REG_ADC_ANAPAR_AD4 + +#define BIT_SHIFT_ADC_ANAPAR_AD4 0 +#define BIT_MASK_ADC_ANAPAR_AD4 0xffffffffL +#define BIT_ADC_ANAPAR_AD4(x) (((x) & BIT_MASK_ADC_ANAPAR_AD4) << BIT_SHIFT_ADC_ANAPAR_AD4) +#define BIT_CTRL_ADC_ANAPAR_AD4(x) (((x) & BIT_MASK_ADC_ANAPAR_AD4) << BIT_SHIFT_ADC_ANAPAR_AD4) +#define BIT_GET_ADC_ANAPAR_AD4(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD4) & BIT_MASK_ADC_ANAPAR_AD4) + + +//2 REG_ADC_ANAPAR_AD5 + +#define BIT_SHIFT_ADC_ANAPAR_AD5 0 +#define BIT_MASK_ADC_ANAPAR_AD5 0xffffffffL +#define BIT_ADC_ANAPAR_AD5(x) (((x) & BIT_MASK_ADC_ANAPAR_AD5) << BIT_SHIFT_ADC_ANAPAR_AD5) +#define BIT_CTRL_ADC_ANAPAR_AD5(x) (((x) & BIT_MASK_ADC_ANAPAR_AD5) << BIT_SHIFT_ADC_ANAPAR_AD5) +#define BIT_GET_ADC_ANAPAR_AD5(x) (((x) >> BIT_SHIFT_ADC_ANAPAR_AD5) & BIT_MASK_ADC_ANAPAR_AD5) + + +//2 REG_ADC_CALI_DATA + +#define BIT_SHIFT_ADC_CALI_DATA_6 16 +#define BIT_MASK_ADC_CALI_DATA_6 0xffff +#define BIT_ADC_CALI_DATA_6(x) (((x) & BIT_MASK_ADC_CALI_DATA_6) << BIT_SHIFT_ADC_CALI_DATA_6) +#define BIT_CTRL_ADC_CALI_DATA_6(x) (((x) & BIT_MASK_ADC_CALI_DATA_6) << BIT_SHIFT_ADC_CALI_DATA_6) +#define BIT_GET_ADC_CALI_DATA_6(x) (((x) >> BIT_SHIFT_ADC_CALI_DATA_6) & BIT_MASK_ADC_CALI_DATA_6) + + +#define BIT_SHIFT_ADC_CALI_DATA_0 0 +#define BIT_MASK_ADC_CALI_DATA_0 0xffff +#define BIT_ADC_CALI_DATA_0(x) (((x) & BIT_MASK_ADC_CALI_DATA_0) << BIT_SHIFT_ADC_CALI_DATA_0) +#define BIT_CTRL_ADC_CALI_DATA_0(x) (((x) & BIT_MASK_ADC_CALI_DATA_0) << BIT_SHIFT_ADC_CALI_DATA_0) +#define BIT_GET_ADC_CALI_DATA_0(x) (((x) >> BIT_SHIFT_ADC_CALI_DATA_0) & BIT_MASK_ADC_CALI_DATA_0) + +//================ Register Reg Field ========================= +#define REG_ADC_FIFO_READ 0x0000 +#define REG_ADC_CONTROL 0x0004 +#define REG_ADC_INTR_EN 0x0008 +#define REG_ADC_INTR_STS 0x000C +#define REG_ADC_COMP_VALUE_L 0x0010 +#define REG_ADC_COMP_VALUE_H 0x0014 +#define REG_ADC_COMP_SET 0x0018 +#define REG_ADC_POWER 0x001C +#define REG_ADC_ANAPAR_AD0 0x0020 +#define REG_ADC_ANAPAR_AD1 0x0024 +#define REG_ADC_ANAPAR_AD2 0x0028 +#define REG_ADC_ANAPAR_AD3 0x002C +#define REG_ADC_ANAPAR_AD4 0x0030 +#define REG_ADC_ANAPAR_AD5 0x0034 +#define REG_ADC_CALI_DATA 0x0038 + +//================ ADC HAL related enumeration ================== + +//================ ADC Function Prototypes ===================== +#define HAL_ADC_WRITE32(addr, value) HAL_WRITE32(ADC_REG_BASE,addr,value) +#define HAL_ADC_READ32(addr) HAL_READ32(ADC_REG_BASE,addr) + +RTK_STATUS HalADCInit8195a(IN VOID *Data); +RTK_STATUS HalADCDeInit8195a(IN VOID *Data); +RTK_STATUS HalADCEnableRtl8195a(IN VOID *Data); +RTK_STATUS HalADCIntrCtrl8195a(IN VOID *Data); +u32 HalADCReceiveRtl8195a(IN VOID *Data); +u32 HalADCReadRegRtl8195a(IN VOID *Data,IN u8 I2CReg); + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_dac.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_dac.h new file mode 100644 index 0000000..c3a9861 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_dac.h @@ -0,0 +1,294 @@ +#ifndef _RTL8195A_DAC_H_ +#define _RTL8195A_DAC_H_ + +//================ Register Bit Field ========================== +//2 REG_DAC0_FIFO_WR + +#define BIT_SHIFT_DAC0_FIFO_WO 0 +#define BIT_MASK_DAC0_FIFO_WO 0xffffffffL +#define BIT_DAC0_FIFO_WO(x) (((x) & BIT_MASK_DAC0_FIFO_WO) << BIT_SHIFT_DAC0_FIFO_WO) +#define BIT_CTRL_DAC0_FIFO_WO(x) (((x) & BIT_MASK_DAC0_FIFO_WO) << BIT_SHIFT_DAC0_FIFO_WO) +#define BIT_GET_DAC0_FIFO_WO(x) (((x) >> BIT_SHIFT_DAC0_FIFO_WO) & BIT_MASK_DAC0_FIFO_WO) + + +//2 REG_DAC_CTRL + +#define BIT_SHIFT_DAC_DELTA_SIGMA 25 +#define BIT_MASK_DAC_DELTA_SIGMA 0x7 +#define BIT_DAC_DELTA_SIGMA(x) (((x) & BIT_MASK_DAC_DELTA_SIGMA) << BIT_SHIFT_DAC_DELTA_SIGMA) +#define BIT_CTRL_DAC_DELTA_SIGMA(x) (((x) & BIT_MASK_DAC_DELTA_SIGMA) << BIT_SHIFT_DAC_DELTA_SIGMA) +#define BIT_GET_DAC_DELTA_SIGMA(x) (((x) >> BIT_SHIFT_DAC_DELTA_SIGMA) & BIT_MASK_DAC_DELTA_SIGMA) + +#define BIT_DAC_BYPASS_DSC BIT(24) +#define BIT_SHIFT_DAC_BYPASS_DSC 24 +#define BIT_MASK_DAC_BYPASS_DSC 0x1 +#define BIT_CTRL_DAC_BYPASS_DSC(x) (((x) & BIT_MASK_DAC_BYPASS_DSC) << BIT_SHIFT_DAC_BYPASS_DSC) + + +#define BIT_SHIFT_DAC_DSC_DBG_SEL 19 +#define BIT_MASK_DAC_DSC_DBG_SEL 0x3 +#define BIT_DAC_DSC_DBG_SEL(x) (((x) & BIT_MASK_DAC_DSC_DBG_SEL) << BIT_SHIFT_DAC_DSC_DBG_SEL) +#define BIT_CTRL_DAC_DSC_DBG_SEL(x) (((x) & BIT_MASK_DAC_DSC_DBG_SEL) << BIT_SHIFT_DAC_DSC_DBG_SEL) +#define BIT_GET_DAC_DSC_DBG_SEL(x) (((x) >> BIT_SHIFT_DAC_DSC_DBG_SEL) & BIT_MASK_DAC_DSC_DBG_SEL) + + +#define BIT_SHIFT_DAC_DBG_SEL 16 +#define BIT_MASK_DAC_DBG_SEL 0x7 +#define BIT_DAC_DBG_SEL(x) (((x) & BIT_MASK_DAC_DBG_SEL) << BIT_SHIFT_DAC_DBG_SEL) +#define BIT_CTRL_DAC_DBG_SEL(x) (((x) & BIT_MASK_DAC_DBG_SEL) << BIT_SHIFT_DAC_DBG_SEL) +#define BIT_GET_DAC_DBG_SEL(x) (((x) >> BIT_SHIFT_DAC_DBG_SEL) & BIT_MASK_DAC_DBG_SEL) + + +#define BIT_SHIFT_DAC_BURST_SIZE 8 +#define BIT_MASK_DAC_BURST_SIZE 0xf +#define BIT_DAC_BURST_SIZE(x) (((x) & BIT_MASK_DAC_BURST_SIZE) << BIT_SHIFT_DAC_BURST_SIZE) +#define BIT_CTRL_DAC_BURST_SIZE(x) (((x) & BIT_MASK_DAC_BURST_SIZE) << BIT_SHIFT_DAC_BURST_SIZE) +#define BIT_GET_DAC_BURST_SIZE(x) (((x) >> BIT_SHIFT_DAC_BURST_SIZE) & BIT_MASK_DAC_BURST_SIZE) + +#define BIT_DAC_FILTER_SETTLE BIT(4) +#define BIT_SHIFT_DAC_FILTER_SETTLE 4 +#define BIT_MASK_DAC_FILTER_SETTLE 0x1 +#define BIT_CTRL_DAC_FILTER_SETTLE(x) (((x) & BIT_MASK_DAC_FILTER_SETTLE) << BIT_SHIFT_DAC_FILTER_SETTLE) + +#define BIT_DAC_OV_OPTION BIT(3) +#define BIT_SHIFT_DAC_OV_OPTION 3 +#define BIT_MASK_DAC_OV_OPTION 0x1 +#define BIT_CTRL_DAC_OV_OPTION(x) (((x) & BIT_MASK_DAC_OV_OPTION) << BIT_SHIFT_DAC_OV_OPTION) + +#define BIT_DAC_ENDIAN BIT(2) +#define BIT_SHIFT_DAC_ENDIAN 2 +#define BIT_MASK_DAC_ENDIAN 0x1 +#define BIT_CTRL_DAC_ENDIAN(x) (((x) & BIT_MASK_DAC_ENDIAN) << BIT_SHIFT_DAC_ENDIAN) + +#define BIT_DAC_SPEED BIT(1) +#define BIT_SHIFT_DAC_SPEED 1 +#define BIT_MASK_DAC_SPEED 0x1 +#define BIT_CTRL_DAC_SPEED(x) (((x) & BIT_MASK_DAC_SPEED) << BIT_SHIFT_DAC_SPEED) + +#define BIT_DAC_FIFO_EN BIT(0) +#define BIT_SHIFT_DAC_FIFO_EN 0 +#define BIT_MASK_DAC_FIFO_EN 0x1 +#define BIT_CTRL_DAC_FIFO_EN(x) (((x) & BIT_MASK_DAC_FIFO_EN) << BIT_SHIFT_DAC_FIFO_EN) + + +//2 REG_DAC_INTR_CTRL +#define BIT_DAC_DSC_OVERFLOW1_EN BIT(6) +#define BIT_SHIFT_DAC_DSC_OVERFLOW1_EN 6 +#define BIT_MASK_DAC_DSC_OVERFLOW1_EN 0x1 +#define BIT_CTRL_DAC_DSC_OVERFLOW1_EN(x) (((x) & BIT_MASK_DAC_DSC_OVERFLOW1_EN) << BIT_SHIFT_DAC_DSC_OVERFLOW1_EN) + +#define BIT_DAC_DSC_OVERFLOW0_EN BIT(5) +#define BIT_SHIFT_DAC_DSC_OVERFLOW0_EN 5 +#define BIT_MASK_DAC_DSC_OVERFLOW0_EN 0x1 +#define BIT_CTRL_DAC_DSC_OVERFLOW0_EN(x) (((x) & BIT_MASK_DAC_DSC_OVERFLOW0_EN) << BIT_SHIFT_DAC_DSC_OVERFLOW0_EN) + +#define BIT_DAC__WRITE_ERROR_EN BIT(4) +#define BIT_SHIFT_DAC__WRITE_ERROR_EN 4 +#define BIT_MASK_DAC__WRITE_ERROR_EN 0x1 +#define BIT_CTRL_DAC__WRITE_ERROR_EN(x) (((x) & BIT_MASK_DAC__WRITE_ERROR_EN) << BIT_SHIFT_DAC__WRITE_ERROR_EN) + +#define BIT_DAC_FIFO_STOP_EN BIT(3) +#define BIT_SHIFT_DAC_FIFO_STOP_EN 3 +#define BIT_MASK_DAC_FIFO_STOP_EN 0x1 +#define BIT_CTRL_DAC_FIFO_STOP_EN(x) (((x) & BIT_MASK_DAC_FIFO_STOP_EN) << BIT_SHIFT_DAC_FIFO_STOP_EN) + +#define BIT_DAC_FIFO_OVERFLOW_EN BIT(2) +#define BIT_SHIFT_DAC_FIFO_OVERFLOW_EN 2 +#define BIT_MASK_DAC_FIFO_OVERFLOW_EN 0x1 +#define BIT_CTRL_DAC_FIFO_OVERFLOW_EN(x) (((x) & BIT_MASK_DAC_FIFO_OVERFLOW_EN) << BIT_SHIFT_DAC_FIFO_OVERFLOW_EN) + +#define BIT_DAC_FIFO_WR_REQ_EN BIT(1) +#define BIT_SHIFT_DAC_FIFO_WR_REQ_EN 1 +#define BIT_MASK_DAC_FIFO_WR_REQ_EN 0x1 +#define BIT_CTRL_DAC_FIFO_WR_REQ_EN(x) (((x) & BIT_MASK_DAC_FIFO_WR_REQ_EN) << BIT_SHIFT_DAC_FIFO_WR_REQ_EN) + +#define BIT_DAC_FIFO_FULL_EN BIT(0) +#define BIT_SHIFT_DAC_FIFO_FULL_EN 0 +#define BIT_MASK_DAC_FIFO_FULL_EN 0x1 +#define BIT_CTRL_DAC_FIFO_FULL_EN(x) (((x) & BIT_MASK_DAC_FIFO_FULL_EN) << BIT_SHIFT_DAC_FIFO_FULL_EN) + + +//2 REG_DAC_INTR_STS +#define BIT_DAC_DSC_OVERFLOW1_ST BIT(6) +#define BIT_SHIFT_DAC_DSC_OVERFLOW1_ST 6 +#define BIT_MASK_DAC_DSC_OVERFLOW1_ST 0x1 +#define BIT_CTRL_DAC_DSC_OVERFLOW1_ST(x) (((x) & BIT_MASK_DAC_DSC_OVERFLOW1_ST) << BIT_SHIFT_DAC_DSC_OVERFLOW1_ST) + +#define BIT_DAC_DSC_OVERFLOW0_ST BIT(5) +#define BIT_SHIFT_DAC_DSC_OVERFLOW0_ST 5 +#define BIT_MASK_DAC_DSC_OVERFLOW0_ST 0x1 +#define BIT_CTRL_DAC_DSC_OVERFLOW0_ST(x) (((x) & BIT_MASK_DAC_DSC_OVERFLOW0_ST) << BIT_SHIFT_DAC_DSC_OVERFLOW0_ST) + +#define BIT_DAC__WRITE_ERROR_ST BIT(4) +#define BIT_SHIFT_DAC__WRITE_ERROR_ST 4 +#define BIT_MASK_DAC__WRITE_ERROR_ST 0x1 +#define BIT_CTRL_DAC__WRITE_ERROR_ST(x) (((x) & BIT_MASK_DAC__WRITE_ERROR_ST) << BIT_SHIFT_DAC__WRITE_ERROR_ST) + +#define BIT_DAC_FIFO_STOP_ST BIT(3) +#define BIT_SHIFT_DAC_FIFO_STOP_ST 3 +#define BIT_MASK_DAC_FIFO_STOP_ST 0x1 +#define BIT_CTRL_DAC_FIFO_STOP_ST(x) (((x) & BIT_MASK_DAC_FIFO_STOP_ST) << BIT_SHIFT_DAC_FIFO_STOP_ST) + +#define BIT_DAC_FIFO_OVERFLOW_ST BIT(2) +#define BIT_SHIFT_DAC_FIFO_OVERFLOW_ST 2 +#define BIT_MASK_DAC_FIFO_OVERFLOW_ST 0x1 +#define BIT_CTRL_DAC_FIFO_OVERFLOW_ST(x) (((x) & BIT_MASK_DAC_FIFO_OVERFLOW_ST) << BIT_SHIFT_DAC_FIFO_OVERFLOW_ST) + +#define BIT_DAC_FIFO_WR_REQ_ST BIT(1) +#define BIT_SHIFT_DAC_FIFO_WR_REQ_ST 1 +#define BIT_MASK_DAC_FIFO_WR_REQ_ST 0x1 +#define BIT_CTRL_DAC_FIFO_WR_REQ_ST(x) (((x) & BIT_MASK_DAC_FIFO_WR_REQ_ST) << BIT_SHIFT_DAC_FIFO_WR_REQ_ST) + +#define BIT_DAC_FIFO_FULL_ST BIT(0) +#define BIT_SHIFT_DAC_FIFO_FULL_ST 0 +#define BIT_MASK_DAC_FIFO_FULL_ST 0x1 +#define BIT_CTRL_DAC_FIFO_FULL_ST(x) (((x) & BIT_MASK_DAC_FIFO_FULL_ST) << BIT_SHIFT_DAC_FIFO_FULL_ST) + + +//2 REG_DAC_PWR_CTRL + +#define BIT_SHIFT_DAC_PWR_CUT_CNTR 16 +#define BIT_MASK_DAC_PWR_CUT_CNTR 0xff +#define BIT_DAC_PWR_CUT_CNTR(x) (((x) & BIT_MASK_DAC_PWR_CUT_CNTR) << BIT_SHIFT_DAC_PWR_CUT_CNTR) +#define BIT_CTRL_DAC_PWR_CUT_CNTR(x) (((x) & BIT_MASK_DAC_PWR_CUT_CNTR) << BIT_SHIFT_DAC_PWR_CUT_CNTR) +#define BIT_GET_DAC_PWR_CUT_CNTR(x) (((x) >> BIT_SHIFT_DAC_PWR_CUT_CNTR) & BIT_MASK_DAC_PWR_CUT_CNTR) + +#define BIT_ST_DAC_FIFO_ON BIT(11) +#define BIT_SHIFT_ST_DAC_FIFO_ON 11 +#define BIT_MASK_ST_DAC_FIFO_ON 0x1 +#define BIT_CTRL_ST_DAC_FIFO_ON(x) (((x) & BIT_MASK_ST_DAC_FIFO_ON) << BIT_SHIFT_ST_DAC_FIFO_ON) + +#define BIT_ST_DAC_ISO_ON BIT(10) +#define BIT_SHIFT_ST_DAC_ISO_ON 10 +#define BIT_MASK_ST_DAC_ISO_ON 0x1 +#define BIT_CTRL_ST_DAC_ISO_ON(x) (((x) & BIT_MASK_ST_DAC_ISO_ON) << BIT_SHIFT_ST_DAC_ISO_ON) + +#define BIT_ST_DAC_PWR33_ON BIT(9) +#define BIT_SHIFT_ST_DAC_PWR33_ON 9 +#define BIT_MASK_ST_DAC_PWR33_ON 0x1 +#define BIT_CTRL_ST_DAC_PWR33_ON(x) (((x) & BIT_MASK_ST_DAC_PWR33_ON) << BIT_SHIFT_ST_DAC_PWR33_ON) + +#define BIT_ST_DAC_PWR12_ON BIT(8) +#define BIT_SHIFT_ST_DAC_PWR12_ON 8 +#define BIT_MASK_ST_DAC_PWR12_ON 0x1 +#define BIT_CTRL_ST_DAC_PWR12_ON(x) (((x) & BIT_MASK_ST_DAC_PWR12_ON) << BIT_SHIFT_ST_DAC_PWR12_ON) + +#define BIT_DAC_ISO_MANU BIT(3) +#define BIT_SHIFT_DAC_ISO_MANU 3 +#define BIT_MASK_DAC_ISO_MANU 0x1 +#define BIT_CTRL_DAC_ISO_MANU(x) (((x) & BIT_MASK_DAC_ISO_MANU) << BIT_SHIFT_DAC_ISO_MANU) + +#define BIT_DAC_PWR33_MANU BIT(2) +#define BIT_SHIFT_DAC_PWR33_MANU 2 +#define BIT_MASK_DAC_PWR33_MANU 0x1 +#define BIT_CTRL_DAC_PWR33_MANU(x) (((x) & BIT_MASK_DAC_PWR33_MANU) << BIT_SHIFT_DAC_PWR33_MANU) + +#define BIT_DAC_PWR12_MANU BIT(1) +#define BIT_SHIFT_DAC_PWR12_MANU 1 +#define BIT_MASK_DAC_PWR12_MANU 0x1 +#define BIT_CTRL_DAC_PWR12_MANU(x) (((x) & BIT_MASK_DAC_PWR12_MANU) << BIT_SHIFT_DAC_PWR12_MANU) + +#define BIT_DAC_PWR_AUTO BIT(0) +#define BIT_SHIFT_DAC_PWR_AUTO 0 +#define BIT_MASK_DAC_PWR_AUTO 0x1 +#define BIT_CTRL_DAC_PWR_AUTO(x) (((x) & BIT_MASK_DAC_PWR_AUTO) << BIT_SHIFT_DAC_PWR_AUTO) + + +//2 REG_DAC_ANAPAR_DA0 + +#define BIT_SHIFT_PWR_ALL_CNTR 12 +#define BIT_MASK_PWR_ALL_CNTR 0xfffff +#define BIT_PWR_ALL_CNTR(x) (((x) & BIT_MASK_PWR_ALL_CNTR) << BIT_SHIFT_PWR_ALL_CNTR) +#define BIT_CTRL_PWR_ALL_CNTR(x) (((x) & BIT_MASK_PWR_ALL_CNTR) << BIT_SHIFT_PWR_ALL_CNTR) +#define BIT_GET_PWR_ALL_CNTR(x) (((x) >> BIT_SHIFT_PWR_ALL_CNTR) & BIT_MASK_PWR_ALL_CNTR) + + +#define BIT_SHIFT_PWR_FUP_CNTR 0 +#define BIT_MASK_PWR_FUP_CNTR 0xfff +#define BIT_PWR_FUP_CNTR(x) (((x) & BIT_MASK_PWR_FUP_CNTR) << BIT_SHIFT_PWR_FUP_CNTR) +#define BIT_CTRL_PWR_FUP_CNTR(x) (((x) & BIT_MASK_PWR_FUP_CNTR) << BIT_SHIFT_PWR_FUP_CNTR) +#define BIT_GET_PWR_FUP_CNTR(x) (((x) >> BIT_SHIFT_PWR_FUP_CNTR) & BIT_MASK_PWR_FUP_CNTR) + + +//2 REG_DAC_ANAPAR_DA1 +#define BIT_FUP_EN BIT(31) +#define BIT_SHIFT_FUP_EN 31 +#define BIT_MASK_FUP_EN 0x1 +#define BIT_CTRL_FUP_EN(x) (((x) & BIT_MASK_FUP_EN) << BIT_SHIFT_FUP_EN) + + +#define BIT_SHIFT_ANAPAR_DA 8 +#define BIT_MASK_ANAPAR_DA 0x7fffff +#define BIT_ANAPAR_DA(x) (((x) & BIT_MASK_ANAPAR_DA) << BIT_SHIFT_ANAPAR_DA) +#define BIT_CTRL_ANAPAR_DA(x) (((x) & BIT_MASK_ANAPAR_DA) << BIT_SHIFT_ANAPAR_DA) +#define BIT_GET_ANAPAR_DA(x) (((x) >> BIT_SHIFT_ANAPAR_DA) & BIT_MASK_ANAPAR_DA) + +#define BIT_D_POW_DACVREF BIT(7) +#define BIT_SHIFT_D_POW_DACVREF 7 +#define BIT_MASK_D_POW_DACVREF 0x1 +#define BIT_CTRL_D_POW_DACVREF(x) (((x) & BIT_MASK_D_POW_DACVREF) << BIT_SHIFT_D_POW_DACVREF) + +#define BIT_D_POW_VREF2 BIT(6) +#define BIT_SHIFT_D_POW_VREF2 6 +#define BIT_MASK_D_POW_VREF2 0x1 +#define BIT_CTRL_D_POW_VREF2(x) (((x) & BIT_MASK_D_POW_VREF2) << BIT_SHIFT_D_POW_VREF2) + +#define BIT_D_POW_MBIAS BIT(5) +#define BIT_SHIFT_D_POW_MBIAS 5 +#define BIT_MASK_D_POW_MBIAS 0x1 +#define BIT_CTRL_D_POW_MBIAS(x) (((x) & BIT_MASK_D_POW_MBIAS) << BIT_SHIFT_D_POW_MBIAS) + +#define BIT_D_POW_DIV4 BIT(4) +#define BIT_SHIFT_D_POW_DIV4 4 +#define BIT_MASK_D_POW_DIV4 0x1 +#define BIT_CTRL_D_POW_DIV4(x) (((x) & BIT_MASK_D_POW_DIV4) << BIT_SHIFT_D_POW_DIV4) + +#define BIT_D_POW_DF1SE_R BIT(3) +#define BIT_SHIFT_D_POW_DF1SE_R 3 +#define BIT_MASK_D_POW_DF1SE_R 0x1 +#define BIT_CTRL_D_POW_DF1SE_R(x) (((x) & BIT_MASK_D_POW_DF1SE_R) << BIT_SHIFT_D_POW_DF1SE_R) + +#define BIT_D_POW_DF2SE_L BIT(2) +#define BIT_SHIFT_D_POW_DF2SE_L 2 +#define BIT_MASK_D_POW_DF2SE_L 0x1 +#define BIT_CTRL_D_POW_DF2SE_L(x) (((x) & BIT_MASK_D_POW_DF2SE_L) << BIT_SHIFT_D_POW_DF2SE_L) + +#define BIT_D_POW_DAC_R BIT(1) +#define BIT_SHIFT_D_POW_DAC_R 1 +#define BIT_MASK_D_POW_DAC_R 0x1 +#define BIT_CTRL_D_POW_DAC_R(x) (((x) & BIT_MASK_D_POW_DAC_R) << BIT_SHIFT_D_POW_DAC_R) + +#define BIT_D_POW_DAC_L BIT(0) +#define BIT_SHIFT_D_POW_DAC_L 0 +#define BIT_MASK_D_POW_DAC_L 0x1 +#define BIT_CTRL_D_POW_DAC_L(x) (((x) & BIT_MASK_D_POW_DAC_L) << BIT_SHIFT_D_POW_DAC_L) + + +//================ Register Reg Field ========================= +#define REG_DAC0_FIFO_WR 0x0000 +#define REG_DAC_CTRL 0x0004 +#define REG_DAC_INTR_CTRL 0x0008 +#define REG_DAC_INTR_STS 0x000C +#define REG_DAC_PWR_CTRL 0x0010 +#define REG_DAC_ANAPAR_DA0 0x0014 +#define REG_DAC_ANAPAR_DA1 0x0018 + + +//================ DAC HAL related enumeration ================== + + +//================ DAC HAL Macro =========================== +#define HAL_DAC_WRITE32(dacidx, addr, value) HAL_WRITE32(DAC_REG_BASE+dacidx*0x800 \ + ,addr,value) +#define HAL_DAC_READ32(dacidx, addr) HAL_READ32(DAC_REG_BASE+dacidx*0x800,addr) + + +//================ DAC Function Prototypes ===================== +RTK_STATUS HalDACInit8195a(IN VOID *Data); +RTK_STATUS HalDACDeInit8195a(IN VOID *Data); +RTK_STATUS HalDACEnableRtl8195a(IN VOID *Data); +RTK_STATUS HalDACIntrCtrl8195a(IN VOID *Data); +u8 HalDACSendRtl8195a(IN VOID *Data); +u32 HalDACReadRegRtl8195a(IN VOID *Data,IN u8 I2CReg); + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gdma.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gdma.h new file mode 100644 index 0000000..3a20e9b --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gdma.h @@ -0,0 +1,486 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_GDMA_H_ +#define _RTL8195A_GDMA_H_ + +// Define GDMA Handshake interface with peripheral, 0 -> GDMA0, 1-> GDMA1 +// Set this Hnadshake interface map to register REG_PESOC_SOC_CTRL +#define GDMA_HANDSHAKE_UART0_TX 0 +#define GDMA_HANDSHAKE_UART0_RX 1 +#define GDMA_HANDSHAKE_UART1_TX 2 +#define GDMA_HANDSHAKE_UART1_RX 3 +#define GDMA_HANDSHAKE_UART2_TX 14 // Only on GDMA 0, hardware fixed +#define GDMA_HANDSHAKE_UART2_RX 14 // Only on GDMA 1, hardware fixed + +#define GDMA_HANDSHAKE_SSI0_TX 4 +#define GDMA_HANDSHAKE_SSI0_RX 5 +#define GDMA_HANDSHAKE_SSI1_TX 6 +#define GDMA_HANDSHAKE_SSI1_RX 7 +#define GDMA_HANDSHAKE_SSI2_TX 15 // Only on GDMA 0, hardware fixed +#define GDMA_HANDSHAKE_SSI2_RX 15 // Only on GDMA 1, hardware fixed + +#define GDMA_HANDSHAKE_I2C0_TX 8 +#define GDMA_HANDSHAKE_I2C0_RX 9 +#define GDMA_HANDSHAKE_I2C1_TX 10 +#define GDMA_HANDSHAKE_I2C1_RX 11 + +#define GDMA_HANDSHAKE_ADC 12 +#define GDMA_HANDSHAKE_DAC0 13 // Only on GDMA 0, hardware fixed +#define GDMA_HANDSHAKE_DAC1 13 // Only on GDMA 1, hardware fixed + +#define HAL_GDMAX_READ32(GdmaIndex, addr) \ + HAL_READ32(GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF), addr) +#define HAL_GDMAX_WRITE32(GdmaIndex, addr, value) \ + HAL_WRITE32((GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF)), addr, value) +#define HAL_GDMAX_READ16(GdmaIndex, addr) \ + HAL_READ16(GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF), addr) +#define HAL_GDMAX_WRITE16(GdmaIndex, addr, value) \ + HAL_WRITE16(GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF), addr, value) +#define HAL_GDMAX_READ8(GdmaIndex, addr) \ + HAL_READ8(GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF), addr) +#define HAL_GDMAX_WRITE8(GdmaIndex, addr, value) \ + HAL_WRITE8(GDMA0_REG_BASE+ (GdmaIndex*GDMA1_REG_OFF), addr, value) + + +#define GDMA_CH_MAX 0x06 + +#define REG_GDMA_CH_OFF 0x058 +#define REG_GDMA_CH_SAR 0x000 +#define REG_GDMA_CH_DAR 0x008 +#define REG_GDMA_CH_LLP 0x010 +#define REG_GDMA_CH_CTL 0x018 +#define REG_GDMA_CH_SSTAT 0x020 +#define REG_GDMA_CH_DSTAT 0x028 +#define REG_GDMA_CH_SSTATAR 0x030 +#define REG_GDMA_CH_DSTATAR 0x038 +#define REG_GDMA_CH_CFG 0x040 +#define REG_GDMA_CH_SGR 0x048 +#define REG_GDMA_CH_DSR 0x050 + +//3 Interrupt Registers +#define REG_GDMA_RAW_INT_BASE 0x2C0 +#define REG_GDMA_RAW_INT_TFR 0x2C0 +#define REG_GDMA_RAW_INT_BLOCK 0x2c8 +#define REG_GDMA_RAW_INT_SRC_TRAN 0x2D0 +#define REG_GDMA_RAW_INT_DST_TRAN 0x2D8 +#define REG_GDMA_RAW_INT_ERR 0x2E0 + +#define REG_GDMA_STATUS_INT_BASE 0x2E8 +#define REG_GDMA_STATUS_INT_TFR 0x2E8 +#define REG_GDMA_STATUS_INT_BLOCK 0x2F0 +#define REG_GDMA_STATUS_INT_SRC_TRAN 0x2F8 +#define REG_GDMA_STATUS_INT_DST_TRAN 0x300 +#define REG_GDMA_STATUS_INT_ERR 0x308 + +#define REG_GDMA_MASK_INT_BASE 0x310 +#define REG_GDMA_MASK_INT_TFR 0x310 +#define REG_GDMA_MASK_INT_BLOCK 0x318 +#define REG_GDMA_MASK_INT_SRC_TRAN 0x320 +#define REG_GDMA_MASK_INT_DST_TRAN 0x328 +#define REG_GDMA_MASK_INT_INT_ERR 0x330 + +#define REG_GDMA_CLEAR_INT_BASE 0x338 +#define REG_GDMA_CLEAR_INT_TFR 0x338 +#define REG_GDMA_CLEAR_INT_BLOCK 0x340 +#define REG_GDMA_CLEAR_INT_SRC_TRAN 0x348 +#define REG_GDMA_CLEAR_INT_DST_TRAN 0x350 +#define REG_GDMA_CLEAR_INT_ERR 0x358 +#define REG_GDMA_STATUS_INT 0x360 + +//3 Software handshaking Registers +#define REG_GDMA_REQ_SRC 0x368 +#define REG_GDMA_REQ_DST 0x370 +#define REG_GDMA_REQ_SGL_REQ 0x378 +#define REG_GDMA_REQ_DST_REQ 0x380 +#define REG_GDMA_REQ_LST_SRC 0x388 +#define REG_GDMA_REQ_LST_DST 0x390 + +//3 Miscellaneous Registers +#define REG_GDMA_DMAC_CFG 0x398 +#define REG_GDMA_CH_EN 0x3A0 +#define REG_GDMA_DMA_ID 0x3A8 +#define REG_GDMA_DMA_TEST 0x3B0 +#define REG_GDMA_DMA_COM_PARAMS6 0x3C8 +#define REG_GDMA_DMA_COM_PARAMS5 0x3D0 +#define REG_GDMA_DMA_COM_PARAMS4 0x3D8 +#define REG_GDMA_DMA_COM_PARAMS3 0x3E0 +#define REG_GDMA_DMA_COM_PARAMS2 0x3E8 +#define REG_GDMA_DMA_COM_PARAMS1 0x3F0 +#define REG_GDMA_DMA_COM_PARAMS0 0x3F8 + +//3 CTL Register Bit Control +#define BIT_SHIFT_CTLX_LO_INT_EN 0 +#define BIT_MASK_CTLX_LO_INT_EN 0x1 +#define BIT_CTLX_LO_INT_EN(x)(((x) & BIT_MASK_CTLX_LO_INT_EN) << BIT_SHIFT_CTLX_LO_INT_EN) +#define BIT_INVC_CTLX_LO_INT_EN (~(BIT_MASK_CTLX_LO_INT_EN << BIT_SHIFT_CTLX_LO_INT_EN)) + +#define BIT_SHIFT_CTLX_LO_DST_TR_WIDTH 1 +#define BIT_MASK_CTLX_LO_DST_TR_WIDTH 0x7 +#define BIT_CTLX_LO_DST_TR_WIDTH(x) (((x) & BIT_MASK_CTLX_LO_DST_TR_WIDTH) << BIT_SHIFT_CTLX_LO_DST_TR_WIDTH) +#define BIT_INVC_CTLX_LO_DST_TR_WIDTH (~(BIT_MASK_CTLX_LO_DST_TR_WIDTH << BIT_SHIFT_CTLX_LO_DST_TR_WIDTH)) + +#define BIT_SHIFT_CTLX_LO_SRC_TR_WIDTH 4 +#define BIT_MASK_CTLX_LO_SRC_TR_WIDTH 0x7 +#define BIT_CTLX_LO_SRC_TR_WIDTH(x) (((x) & BIT_MASK_CTLX_LO_SRC_TR_WIDTH) << BIT_SHIFT_CTLX_LO_SRC_TR_WIDTH) +#define BIT_INVC_CTLX_LO_SRC_TR_WIDTH (~(BIT_MASK_CTLX_LO_SRC_TR_WIDTH << BIT_SHIFT_CTLX_LO_SRC_TR_WIDTH)) + +#define BIT_SHIFT_CTLX_LO_DINC 7 +#define BIT_MASK_CTLX_LO_DINC 0x3 +#define BIT_CTLX_LO_DINC(x)(((x) & BIT_MASK_CTLX_LO_DINC) << BIT_SHIFT_CTLX_LO_DINC) +#define BIT_INVC_CTLX_LO_DINC (~(BIT_MASK_CTLX_LO_DINC << BIT_SHIFT_CTLX_LO_DINC)) + +#define BIT_SHIFT_CTLX_LO_SINC 9 +#define BIT_MASK_CTLX_LO_SINC 0x3 +#define BIT_CTLX_LO_SINC(x)(((x) & BIT_MASK_CTLX_LO_SINC) << BIT_SHIFT_CTLX_LO_SINC) +#define BIT_INVC_CTLX_LO_SINC (~(BIT_MASK_CTLX_LO_SINC << BIT_SHIFT_CTLX_LO_SINC)) + +#define BIT_SHIFT_CTLX_LO_DEST_MSIZE 11 +#define BIT_MASK_CTLX_LO_DEST_MSIZE 0x7 +#define BIT_CTLX_LO_DEST_MSIZE(x)(((x) & BIT_MASK_CTLX_LO_DEST_MSIZE) << BIT_SHIFT_CTLX_LO_DEST_MSIZE) +#define BIT_INVC_CTLX_LO_DEST_MSIZE (~(BIT_MASK_CTLX_LO_DEST_MSIZE << BIT_SHIFT_CTLX_LO_DEST_MSIZE)) + +#define BIT_SHIFT_CTLX_LO_SRC_MSIZE 14 +#define BIT_MASK_CTLX_LO_SRC_MSIZE 0x7 +#define BIT_CTLX_LO_SRC_MSIZE(x)(((x) & BIT_MASK_CTLX_LO_SRC_MSIZE) << BIT_SHIFT_CTLX_LO_SRC_MSIZE) +#define BIT_INVC_CTLX_LO_SRC_MSIZE (~(BIT_MASK_CTLX_LO_SRC_MSIZE << BIT_SHIFT_CTLX_LO_SRC_MSIZE)) + + +#define BIT_SHIFT_CTLX_LO_SRC_GATHER_EN 17 +#define BIT_MASK_CTLX_LO_SRC_GATHER_EN 0x1 +#define BIT_CTLX_LO_SRC_GATHER_EN(x)(((x) & BIT_MASK_CTLX_LO_SRC_GATHER_EN) << BIT_SHIFT_CTLX_LO_SRC_GATHER_EN) +#define BIT_INVC_CTLX_LO_SRC_GATHER_EN (~(BIT_MASK_CTLX_LO_SRC_GATHER_EN << BIT_SHIFT_CTLX_LO_SRC_GATHER_EN)) + + +#define BIT_SHIFT_CTLX_LO_DST_SCATTER_EN 18 +#define BIT_MASK_CTLX_LO_DST_SCATTER_EN 0x1 +#define BIT_CTLX_LO_DST_SCATTER_EN(x)(((x) & BIT_MASK_CTLX_LO_DST_SCATTER_EN) << BIT_SHIFT_CTLX_LO_DST_SCATTER_EN) +#define BIT_INVC_CTLX_LO_DST_SCATTER_EN (~(BIT_MASK_CTLX_LO_DST_SCATTER_EN << BIT_SHIFT_CTLX_LO_DST_SCATTER_EN)) + + +#define BIT_SHIFT_CTLX_LO_TT_FC 20 +#define BIT_MASK_CTLX_LO_TT_FC 0x7 +#define BIT_CTLX_LO_TT_FC(x)(((x) & BIT_MASK_CTLX_LO_TT_FC) << BIT_SHIFT_CTLX_LO_TT_FC) +#define BIT_INVC_CTLX_LO_TT_FC (~(BIT_MASK_CTLX_LO_TT_FC << BIT_SHIFT_CTLX_LO_TT_FC)) + + +#define BIT_SHIFT_CTLX_LO_DMS 23 +#define BIT_MASK_CTLX_LO_DMS 0x3 +#define BIT_CTLX_LO_DMS(x)(((x) & BIT_MASK_CTLX_LO_DMS) << BIT_MASK_CTLX_LO_DMS) +#define BIT_INVC_CTLX_LO_DMS (~(BIT_MASK_CTLX_LO_DMS << BIT_SHIFT_CTLX_LO_DMS)) + + +#define BIT_SHIFT_CTLX_LO_SMS 25 +#define BIT_MASK_CTLX_LO_SMS 0x3 +#define BIT_CTLX_LO_SMS(x)(((x) & BIT_MASK_CTLX_LO_SMS) << BIT_SHIFT_CTLX_LO_SMS) +#define BIT_INVC_CTLX_LO_SMS (~(BIT_MASK_CTLX_LO_SMS << BIT_SHIFT_CTLX_LO_SMS)) + + +#define BIT_SHIFT_CTLX_LO_LLP_DST_EN 27 +#define BIT_MASK_CTLX_LO_LLP_DST_EN 0x1 +#define BIT_CTLX_LO_LLP_DST_EN(x)(((x) & BIT_MASK_CTLX_LO_LLP_DST_EN) << BIT_SHIFT_CTLX_LO_LLP_DST_EN) +#define BIT_INVC_CTLX_LO_LLP_DST_EN (~(BIT_MASK_CTLX_LO_LLP_DST_EN << BIT_SHIFT_CTLX_LO_LLP_DST_EN)) + +#define BIT_SHIFT_CTLX_LO_LLP_SRC_EN 28 +#define BIT_MASK_CTLX_LO_LLP_SRC_EN 0x1 +#define BIT_CTLX_LO_LLP_SRC_EN(x)(((x) & BIT_MASK_CTLX_LO_LLP_SRC_EN) << BIT_SHIFT_CTLX_LO_LLP_SRC_EN) +#define BIT_INVC_CTLX_LO_LLP_SRC_EN (~(BIT_MASK_CTLX_LO_LLP_SRC_EN << BIT_SHIFT_CTLX_LO_LLP_SRC_EN)) + + +#define BIT_SHIFT_CTLX_UP_BLOCK_BS 0 +#define BIT_MASK_CTLX_UP_BLOCK_BS 0xFFF +#define BIT_CTLX_UP_BLOCK_BS(x)(((x) & BIT_MASK_CTLX_UP_BLOCK_BS) << BIT_SHIFT_CTLX_UP_BLOCK_BS) +#define BIT_INVC_CTLX_UP_BLOCK_BS (~(BIT_MASK_CTLX_UP_BLOCK_BS << BIT_SHIFT_CTLX_UP_BLOCK_BS)) + + +#define BIT_SHIFT_CTLX_UP_DONE 12 +#define BIT_MASK_CTLX_UP_DONE 0x1 +#define BIT_CTLX_UP_DONE(x)(((x) & BIT_MASK_CTLX_UP_DONE) << BIT_SHIFT_CTLX_UP_DONE) +#define BIT_INVC_CTLX_UP_DONE (~(BIT_MASK_CTLX_UP_DONE << BIT_SHIFT_CTLX_UP_DONE)) + + +//3 CFG Register Bit Control +#define BIT_SHIFT_CFGX_LO_CH_PRIOR 5 +#define BIT_MASK_CFGX_LO_CH_PRIOR 0x7 +#define BIT_CFGX_LO_CH_PRIOR(x)(((x) & BIT_MASK_CFGX_LO_CH_PRIOR) << BIT_SHIFT_CFGX_LO_CH_PRIOR) +#define BIT_INVC_CFGX_LO_CH_PRIOR (~(BIT_MASK_CFGX_LO_CH_PRIOR << BIT_SHIFT_CFGX_LO_CH_PRIOR)) + + +#define BIT_SHIFT_CFGX_LO_CH_SUSP 8 +#define BIT_MASK_CFGX_LO_CH_SUSP 0x1 +#define BIT_CFGX_LO_CH_SUSP(x)(((x) & BIT_MASK_CFGX_LO_CH_SUSP) << BIT_SHIFT_CFGX_LO_CH_SUSP) +#define BIT_INVC_CFGX_LO_CH_SUSP (~(BIT_MASK_CFGX_LO_CH_SUSP << BIT_SHIFT_CFGX_LO_CH_SUSP)) + + +#define BIT_SHIFT_CFGX_LO_FIFO_EMPTY 9 +#define BIT_MASK_CFGX_LO_FIFO_EMPTY 0x1 +#define BIT_CFGX_LO_FIFO_EMPTY(x)(((x) & BIT_MASK_CFGX_LO_FIFO_EMPTY) << BIT_SHIFT_CFGX_LO_FIFO_EMPTY) +#define BIT_INVC_CFGX_LO_FIFO_EMPTY (~(BIT_MASK_CFGX_LO_FIFO_EMPTY << BIT_SHIFT_CFGX_LO_FIFO_EMPTY)) + + +#define BIT_SHIFT_CFGX_LO_HS_SEL_DST 10 +#define BIT_MASK_CFGX_LO_HS_SEL_DST 0x1 +#define BIT_CFGX_LO_HS_SEL_DST(x)(((x) & BIT_MASK_CFGX_LO_HS_SEL_DST) << BIT_SHIFT_CFGX_LO_HS_SEL_DST) +#define BIT_INVC_CFGX_LO_HS_SEL_DST (~(BIT_MASK_CFGX_LO_HS_SEL_DST << BIT_SHIFT_CFGX_LO_HS_SEL_DST)) + +#define BIT_SHIFT_CFGX_LO_HS_SEL_SRC 11 +#define BIT_MASK_CFGX_LO_HS_SEL_SRC 0x1 +#define BIT_CFGX_LO_HS_SEL_SRC(x)(((x) & BIT_MASK_CFGX_LO_HS_SEL_SRC) << BIT_SHIFT_CFGX_LO_HS_SEL_SRC) +#define BIT_INVC_CFGX_LO_HS_SEL_SRC (~(BIT_MASK_CFGX_LO_HS_SEL_SRC << BIT_SHIFT_CFGX_LO_HS_SEL_SRC)) + +#define BIT_SHIFT_CFGX_LO_LOCK_CH_L 12 +#define BIT_MASK_CFGX_LO_LOCK_CH_L 0x3 +#define BIT_CFGX_LO_LOCK_CH_L(x)(((x) & BIT_MASK_CFGX_LO_LOCK_CH_L) << BIT_SHIFT_CFGX_LO_LOCK_CH_L) +#define BIT_INVC_CFGX_LO_LOCK_CH_L (~(BIT_MASK_CFGX_LO_LOCK_CH_L << BIT_SHIFT_CFGX_LO_LOCK_CH_L)) + +#define BIT_SHIFT_CFGX_LO_LOCK_B_L 14 +#define BIT_MASK_CFGX_LO_LOCK_B_L 0x3 +#define BIT_CFGX_LO_LOCK_B_L(x)(((x) & BIT_MASK_CFGX_LO_LOCK_B_L) << BIT_SHIFT_CFGX_LO_LOCK_B_L) +#define BIT_INVC_CFGX_LO_LOCK_B_L (~(BIT_MASK_CFGX_LO_LOCK_B_L << BIT_SHIFT_CFGX_LO_LOCK_B_L)) + +#define BIT_SHIFT_CFGX_LO_LOCK_CH 16 +#define BIT_MASK_CFGX_LO_LOCK_CH 0x1 +#define BIT_CFGX_LO_LOCK_CH(x)(((x) & BIT_MASK_CFGX_LO_LOCK_CH) << BIT_SHIFT_CFGX_LO_LOCK_CH) +#define BIT_INVC_CFGX_LO_LOCK_CH (~(BIT_MASK_CFGX_LO_LOCK_CH << BIT_SHIFT_CFGX_LO_LOCK_CH)) + +#define BIT_SHIFT_CFGX_LO_LOCK_B 17 +#define BIT_MASK_CFGX_LO_LOCK_B 0x1 +#define BIT_CFGX_LO_LOCK_B(x)(((x) & BIT_MASK_CFGX_LO_LOCK_B) << BIT_SHIFT_CFGX_LO_LOCK_B) +#define BIT_INVC_CFGX_LO_LOCK_B (~(BIT_MASK_CFGX_LO_LOCK_B << BIT_SHIFT_CFGX_LO_LOCK_B)) + +#define BIT_SHIFT_CFGX_LO_DST_HS_POL 18 +#define BIT_MASK_CFGX_LO_DST_HS_POL 0x1 +#define BIT_CFGX_LO_DST_HS_POL(x)(((x) & BIT_MASK_CFGX_LO_DST_HS_POL) << BIT_SHIFT_CFGX_LO_DST_HS_POL) +#define BIT_INVC_CFGX_LO_DST_HS_POL (~(BIT_MASK_CFGX_LO_DST_HS_POL << BIT_SHIFT_CFGX_LO_DST_HS_POL)) + +#define BIT_SHIFT_CFGX_LO_SRC_HS_POL 19 +#define BIT_MASK_CFGX_LO_SRC_HS_POL 0x1 +#define BIT_CFGX_LO_SRC_HS_POL(x)(((x) & BIT_MASK_CFGX_LO_SRC_HS_POL) << BIT_SHIFT_CFGX_LO_SRC_HS_POL) +#define BIT_INVC_CFGX_LO_SRC_HS_POL (~(BIT_MASK_CFGX_LO_SRC_HS_POL << BIT_SHIFT_CFGX_LO_SRC_HS_POL)) + +#define BIT_SHIFT_CFGX_LO_MAX_ABRST 20 +#define BIT_MASK_CFGX_LO_MAX_ABRST 0x3FF +#define BIT_CFGX_LO_MAX_ABRST(x)(((x) & BIT_MASK_CFGX_LO_MAX_ABRST) << BIT_SHIFT_CFGX_LO_MAX_ABRST) +#define BIT_INVC_CFGX_LO_MAX_ABRST (~(BIT_MASK_CFGX_LO_MAX_ABRST << BIT_SHIFT_CFGX_LO_MAX_ABRST)) + +#define BIT_SHIFT_CFGX_LO_RELOAD_SRC 30 +#define BIT_MASK_CFGX_LO_RELOAD_SRC 0x1 +#define BIT_CFGX_LO_RELOAD_SRC(x)(((x) & BIT_MASK_CFGX_LO_RELOAD_SRC) << BIT_SHIFT_CFGX_LO_RELOAD_SRC) +#define BIT_INVC_CFGX_LO_RELOAD_SRC (~(BIT_MASK_CFGX_LO_RELOAD_SRC << BIT_SHIFT_CFGX_LO_RELOAD_SRC)) + +#define BIT_SHIFT_CFGX_LO_RELOAD_DST 31 +#define BIT_MASK_CFGX_LO_RELOAD_DST 0x1 +#define BIT_CFGX_LO_RELOAD_DST(x)(((x) & BIT_MASK_CFGX_LO_RELOAD_DST) << BIT_SHIFT_CFGX_LO_RELOAD_DST) +#define BIT_INVC_CFGX_LO_RELOAD_DST (~(BIT_MASK_CFGX_LO_RELOAD_DST << BIT_SHIFT_CFGX_LO_RELOAD_DST)) + +#define BIT_SHIFT_CFGX_UP_FCMODE 0 +#define BIT_MASK_CFGX_UP_FCMODE 0x1 +#define BIT_CFGX_UP_FCMODE(x)(((x) & BIT_MASK_CFGX_UP_FCMODE) << BIT_SHIFT_CFGX_UP_FCMODE) +#define BIT_INVC_CFGX_UP_FCMODE (~(BIT_MASK_CFGX_UP_FCMODE << BIT_SHIFT_CFGX_UP_FCMODE)) + +#define BIT_SHIFT_CFGX_UP_FIFO_MODE 1 +#define BIT_MASK_CFGX_UP_FIFO_MODE 0x1 +#define BIT_CFGX_UP_FIFO_MODE(x)(((x) & BIT_MASK_CFGX_UP_FIFO_MODE) << BIT_SHIFT_CFGX_UP_FIFO_MODE) +#define BIT_INVC_CFGX_UP_FIFO_MODE (~(BIT_MASK_CFGX_UP_FIFO_MODE << BIT_SHIFT_CFGX_UP_FIFO_MODE)) + +#define BIT_SHIFT_CFGX_UP_PROTCTL 2 +#define BIT_MASK_CFGX_UP_PROTCTL 0x7 +#define BIT_CFGX_UP_PROTCTL(x)(((x) & BIT_MASK_CFGX_UP_PROTCTL) << BIT_SHIFT_CFGX_UP_PROTCTL) +#define BIT_INVC_CFGX_UP_PROTCTL (~(BIT_MASK_CFGX_UP_PROTCTL << BIT_SHIFT_CFGX_UP_PROTCTL)) + +#define BIT_SHIFT_CFGX_UP_DS_UPD_EN 5 +#define BIT_MASK_CFGX_UP_DS_UPD_EN 0x1 +#define BIT_CFGX_UP_DS_UPD_EN(x)(((x) & BIT_MASK_CFGX_UP_DS_UPD_EN) << BIT_SHIFT_CFGX_UP_DS_UPD_EN) +#define BIT_INVC_CFGX_UP_DS_UPD_EN (~(BIT_MASK_CFGX_UP_DS_UPD_EN << BIT_SHIFT_CFGX_UP_DS_UPD_EN)) + +#define BIT_SHIFT_CFGX_UP_SS_UPD_EN 6 +#define BIT_MASK_CFGX_UP_SS_UPD_EN 0x1 +#define BIT_CFGX_UP_SS_UPD_EN(x)(((x) & BIT_MASK_CFGX_UP_SS_UPD_EN) << BIT_SHIFT_CFGX_UP_SS_UPD_EN) +#define BIT_INVC_CFGX_UP_SS_UPD_EN (~(BIT_MASK_CFGX_UP_SS_UPD_EN << BIT_SHIFT_CFGX_UP_SS_UPD_EN)) + +#define BIT_SHIFT_CFGX_UP_SRC_PER 7 +#define BIT_MASK_CFGX_UP_SRC_PER 0xF +#define BIT_CFGX_UP_SRC_PER(x)(((x) & BIT_MASK_CFGX_UP_SRC_PER) << BIT_SHIFT_CFGX_UP_SRC_PER) +#define BIT_INVC_CFGX_UP_SRC_PER (~(BIT_MASK_CFGX_UP_SRC_PER << BIT_SHIFT_CFGX_UP_SRC_PER)) + +#define BIT_SHIFT_CFGX_UP_DEST_PER 11 +#define BIT_MASK_CFGX_UP_DEST_PER 0xF +#define BIT_CFGX_UP_DEST_PER(x)(((x) & BIT_MASK_CFGX_UP_DEST_PER) << BIT_SHIFT_CFGX_UP_DEST_PER) +#define BIT_INVC_CFGX_UP_DEST_PER (~(BIT_MASK_CFGX_UP_DEST_PER << BIT_SHIFT_CFGX_UP_DEST_PER)) + +typedef enum _GDMA_CHANNEL_NUM_ { + GdmaNoCh = 0x0000, + GdmaCh0 = 0x0101, + GdmaCh1 = 0x0202, + GdmaCh2 = 0x0404, + GdmaCh3 = 0x0808, + GdmaCh4 = 0x1010, + GdmaCh5 = 0x2020, + GdmaCh6 = 0x4040, + GdmaCh7 = 0x8080, + GdmaAllCh = 0xffff +}GDMA_CHANNEL_NUM, *PGDMA_CHANNEL_NUM; + + +//3 CTL register struct + +typedef enum _GDMA_CTL_TT_FC_TYPE_ { + TTFCMemToMem = 0x00, + TTFCMemToPeri = 0x01, + TTFCPeriToMem = 0x02 +}GDMA_CTL_TT_FC_TYPE, *PGDMA_CTL_TT_FC_TYPE; + +//Max type = Bus Width +typedef enum _GDMA_CTL_TR_WIDTH_ { + TrWidthOneByte = 0x00, + TrWidthTwoBytes = 0x01, + TrWidthFourBytes = 0x02 +}GDMA_CTL_TR_WIDTH, *PGDMA_CTL_TR_WIDTH; + +typedef enum _GDMA_CTL_MSIZE_ { + MsizeOne = 0x00, + MsizeFour = 0x01, + MsizeEight = 0x02 +}GDMA_CTL_MSIZE, *PGDMA_CTL_MSIZE; + +typedef enum _GDMA_INC_TYPE_ { + IncType = 0x00, + DecType = 0x01, + NoChange = 0x02 +}GDMA_INC_TYPE, *PGDMA_INC_TYPE; + + +typedef struct _GDMA_CTL_REG_ { + GDMA_CTL_TT_FC_TYPE TtFc; + GDMA_CTL_TR_WIDTH DstTrWidth; + GDMA_CTL_TR_WIDTH SrcTrWidth; + GDMA_INC_TYPE Dinc; + GDMA_INC_TYPE Sinc; + GDMA_CTL_MSIZE DestMsize; + GDMA_CTL_MSIZE SrcMsize; + + u8 IntEn :1; // Bit 0 + u8 SrcGatherEn :1; // Bit 1 + u8 DstScatterEn :1; // Bit 2 + u8 LlpDstEn :1; // Bit 3 + u8 LlpSrcEn :1; // Bit 4 + u8 Done :1; // Bit 5 + u8 Rsvd6To7 :2; //Bit 6 -7 + u16 BlockSize; + +}GDMA_CTL_REG, *PGDMA_CTL_REG; + + +//3 CFG Register Structure + +typedef enum _GDMA_CH_PRIORITY_ { + Prior0 = 0, + Prior1 = 1, + Prior2 = 2, + Prior3 = 3, + Prior4 = 4, + Prior5 = 5, + Prior6 = 6, + Prior7 = 7 +}GDMA_CH_PRIORITY, *PGDMA_CH_PRIORITY; + +typedef enum _GDMA_LOCK_LEVEL_ { + OverComplDmaTransfer = 0x00, + OverComplDmaBlockTransfer = 0x01, + OverComplDmaTransation = 0x02 +}GDMA_LOCK_LEVEL, *PGDMA_LOCK_LEVEL; + + +typedef struct _GDMA_CFG_REG_ { + GDMA_CH_PRIORITY ChPrior; + GDMA_LOCK_LEVEL LockBL; + GDMA_LOCK_LEVEL LockChL; + u16 MaxAbrst; + u8 SrcPer; + u8 DestPer; + u16 ChSusp :1; //Bit 0 + u16 FifoEmpty :1; //Bit 1 + u16 HsSelDst :1; //Bit 2 + u16 HsSelSrc :1; //Bit 3 + u16 LockCh :1; //Bit 4 + u16 LockB :1; //Bit 5 + u16 DstHsPol :1; //Bit 6 + u16 SrcHsPol :1; //Bit 7 + u16 ReloadSrc :1; //Bit 8 + u16 ReloadDst :1; //Bit 9 + u16 FifoMode :1; //Bit 10 + u16 DsUpdEn :1; //Bit 11 + u16 SsUpdEn :1; //Bit 12 + u16 Rsvd13To15 :3; +}GDMA_CFG_REG, *PGDMA_CFG_REG; + +typedef enum _GDMA_ISR_TYPE_ { + TransferType = 0x1, + BlockType = 0x2, + SrcTransferType = 0x4, + DstTransferType = 0x8, + ErrType = 0x10 +}GDMA_ISR_TYPE, *PGDMA_ISR_TYPE; + + +VOID +HalGdmaOnOffRtl8195a ( + IN VOID *Data +); + +BOOL +HalGdamChInitRtl8195a( + IN VOID *Data +); + +BOOL +HalGdmaChSetingRtl8195a( + IN VOID *Data +); + +BOOL +HalGdmaChBlockSetingRtl8195a( + IN VOID *Data +); + + +VOID +HalGdmaChDisRtl8195a ( + IN VOID *Data +); + +VOID +HalGdmaChEnRtl8195a ( + IN VOID *Data +); + +VOID +HalGdmaChIsrEnAndDisRtl8195a ( + IN VOID *Data +); + +u8 +HalGdmaChIsrCleanRtl8195a ( + IN VOID *Data +); + +VOID +HalGdmaChCleanAutoSrcRtl8195a ( + IN VOID *Data +); + +VOID +HalGdmaChCleanAutoDstRtl8195a ( + IN VOID *Data +); + + + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gpio.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gpio.h new file mode 100644 index 0000000..a5ba6cc --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_gpio.h @@ -0,0 +1,352 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_GPIO_H_ +#define _RTL8195A_GPIO_H_ + +#include "hal_api.h" +#include "hal_gpio.h" + +#define GPIO_PORTA_DR 0x00 // data register +#define GPIO_PORTA_DDR 0x04 // data direction +#define GPIO_PORTA_CTRL 0x08 // data source control, we should keep it as default: data source from software + +#define GPIO_PORTB_DR 0x0c // data register +#define GPIO_PORTB_DDR 0x10 // data direction +#define GPIO_PORTB_CTRL 0x14 // data source control, we should keep it as default: data source from software + +#define GPIO_PORTC_DR 0x18 // data register +#define GPIO_PORTC_DDR 0x1c // data direction +#define GPIO_PORTC_CTRL 0x20 // data source control, we should keep it as default: data source from software + +//1 Only the PORTA can be configured to generate interrupts +#define GPIO_INT_EN 0x30 // Interrupt enable register +#define GPIO_INT_MASK 0x34 // Interrupt mask +#define GPIO_INT_TYPE 0x38 // Interrupt type(level/edge) register +#define GPIO_INT_POLARITY 0x3C // Interrupt polarity(Active low/high) register +#define GPIO_INT_STATUS 0x40 // Interrupt status +#define GPIO_INT_RAWSTATUS 0x44 // Interrupt status without mask +#define GPIO_DEBOUNCE 0x48 // Interrupt signal debounce +#define GPIO_PORTA_EOI 0x4c // Clear interrupt + +#define GPIO_EXT_PORTA 0x50 // GPIO IN read or OUT read back +#define GPIO_EXT_PORTB 0x54 // GPIO IN read or OUT read back +#define GPIO_EXT_PORTC 0x58 // GPIO IN read or OUT read back + +#define GPIO_INT_SYNC 0x60 // Is level-sensitive interrupt being sync sith PCLK + +enum { + HAL_GPIO_HIGHZ = 0, + HAL_GPIO_PULL_LOW = 1, + HAL_GPIO_PULL_HIGH = 2 +}; + +//====================================================== +// ROM Function prototype +extern PHAL_GPIO_ADAPTER _pHAL_Gpio_Adapter; + +static __inline HAL_Status +GPIO_Lock ( + VOID +) +{ + HAL_Status Status; + + if (_pHAL_Gpio_Adapter->EnterCritical) { + _pHAL_Gpio_Adapter->EnterCritical(); + } + + if(_pHAL_Gpio_Adapter->Locked) { + Status = HAL_BUSY; + } + else { + _pHAL_Gpio_Adapter->Locked = 1; + Status = HAL_OK; + } + + if (_pHAL_Gpio_Adapter->ExitCritical) { + _pHAL_Gpio_Adapter->ExitCritical(); + } + + return Status; +} + + +static __inline VOID +GPIO_UnLock ( + VOID +) +{ + if (_pHAL_Gpio_Adapter->EnterCritical) { + _pHAL_Gpio_Adapter->EnterCritical(); + } + + _pHAL_Gpio_Adapter->Locked = 0; + + if (_pHAL_Gpio_Adapter->ExitCritical) { + _pHAL_Gpio_Adapter->ExitCritical(); + } +} + + +_LONG_CALL_ extern u32 +HAL_GPIO_IrqHandler_8195a( + IN VOID *pData +); + +_LONG_CALL_ extern u32 +HAL_GPIO_MbedIrqHandler_8195a( + IN VOID *pData +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_IntCtrl_8195a( + HAL_GPIO_PIN *GPIO_Pin, + u32 En +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_Init_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_DeInit_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_GPIO_PIN_STATE +HAL_GPIO_ReadPin_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_WritePin_8195a( + HAL_GPIO_PIN *GPIO_Pin, + HAL_GPIO_PIN_STATE Pin_State +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_RegIrq_8195a( + IN PIRQ_HANDLE pIrqHandle +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_UnRegIrq_8195a( + IN PIRQ_HANDLE pIrqHandle +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_UserRegIrq_8195a( + HAL_GPIO_PIN *GPIO_Pin, + VOID *IrqHandler, + VOID *IrqData +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_UserUnRegIrq_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_MaskIrq_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_UnMaskIrq_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_IntDebounce_8195a( + HAL_GPIO_PIN *GPIO_Pin, + u8 Enable +); + +_LONG_CALL_ u32 +HAL_GPIO_GetIPPinName_8195a( + u32 chip_pin +); + +_LONG_CALL_ HAL_Status +HAL_GPIO_PullCtrl_8195a( + u32 chip_pin, + u8 pull_type +); + +_LONG_CALL_ u32 +GPIO_GetChipPinName_8195a( + u32 port, + u32 pin +); + +_LONG_CALL_ VOID +GPIO_PullCtrl_8195a( + u32 chip_pin, + u8 pull_type +); + +_LONG_CALL_ VOID +GPIO_Int_SetType_8195a( + u8 pin_num, + u8 int_mode +); + + +_LONG_CALL_ HAL_Status HAL_GPIO_IntCtrl_8195aV02(HAL_GPIO_PIN *GPIO_Pin, u32 En); +_LONG_CALL_ u32 GPIO_Int_Clear_8195aV02(u32 irq_clr); + +HAL_Status +HAL_GPIO_ClearISR_8195a( + HAL_GPIO_PIN *GPIO_Pin +); + + +/********** HAL In-Line Functions **********/ + +/** + * @brief De-Initializes a GPIO Pin, reset it as default setting. + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval HAL_Status + */ +static __inline VOID +HAL_GPIO_DeInit( + HAL_GPIO_PIN *GPIO_Pin +) +{ + HAL_GPIO_DeInit_8195a(GPIO_Pin); +} + +/** + * @brief Reads the specified input port pin. + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval The input port pin current status(High or Low). + */ +static __inline s32 +HAL_GPIO_ReadPin( + HAL_GPIO_PIN *GPIO_Pin +) +{ + return (s32)HAL_GPIO_ReadPin_8195a(GPIO_Pin); +} + +/** + * @brief Write the specified output port pin. + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @param Pin_State: The state going to be set to the assigned GPIO pin. + * + * @retval None + */ +static __inline VOID +HAL_GPIO_WritePin( + HAL_GPIO_PIN *GPIO_Pin, + u32 Value +) +{ + HAL_GPIO_WritePin_8195a(GPIO_Pin, (HAL_GPIO_PIN_STATE)Value); +} + +/** + * @brief To register a user interrupt handler for a specified pin + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @param IrqHandler: The IRQ handler to be assigned to the specified pin + * + * @param IrqData: The pointer will be pass the the IRQ handler + * + * @retval None + */ +static __inline VOID +HAL_GPIO_UserRegIrq( + HAL_GPIO_PIN *GPIO_Pin, + VOID *IrqHandler, + VOID *IrqData +) +{ + HAL_GPIO_UserRegIrq_8195a(GPIO_Pin, IrqHandler, IrqData); +} + +/** + * @brief To un-register a user interrupt handler for a specified pin + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval None + */ +static __inline VOID +HAL_GPIO_UserUnRegIrq( + HAL_GPIO_PIN *GPIO_Pin +) +{ + HAL_GPIO_UserUnRegIrq_8195a(GPIO_Pin); +} + + +/** + * @brief Enable/Disable GPIO interrupt + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin initialization. + * + * @param En: Enable (1) or Disable (0) + * + * @retval HAL_Status + */ +static __inline VOID +HAL_GPIO_IntCtrl( + HAL_GPIO_PIN *GPIO_Pin, + u32 En +) +{ + HAL_GPIO_IntCtrl_8195a(GPIO_Pin, En); +} + +/** + * @brief Mask the interrupt of a specified pin + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval None + */ +static __inline VOID +HAL_GPIO_MaskIrq( + HAL_GPIO_PIN *GPIO_Pin +) +{ + HAL_GPIO_MaskIrq_8195a(GPIO_Pin); +} + + +/** + * @brief UnMask the interrupt of a specified pin + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval None + */ +static __inline VOID +HAL_GPIO_UnMaskIrq( + HAL_GPIO_PIN *GPIO_Pin +) +{ + HAL_GPIO_ClearISR_8195a(GPIO_Pin); + HAL_GPIO_UnMaskIrq_8195a(GPIO_Pin); +} + + +#endif // end of "#define _RTL8195A_GPIO_H_" + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2c.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2c.h new file mode 100644 index 0000000..ed7f1a7 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2c.h @@ -0,0 +1,844 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL8195A_I2C_H_ +#define _RTL8195A_I2C_H_ + +#include "hal_api.h" + +//================ Register Bit Field ================== +//2 REG_DW_I2C_IC_CON +#define BIT_IC_CON_IC_SLAVE_DISABLE BIT(6) +#define BIT_SHIFT_IC_CON_IC_SLAVE_DISABLE 6 +#define BIT_MASK_IC_CON_IC_SLAVE_DISABLE 0x1 +#define BIT_CTRL_IC_CON_IC_SLAVE_DISABLE(x) (((x) & BIT_MASK_IC_CON_IC_SLAVE_DISABLE) << BIT_SHIFT_IC_CON_IC_SLAVE_DISABLE) + +#define BIT_IC_CON_IC_RESTART_EN BIT(5) +#define BIT_SHIFT_IC_CON_IC_RESTART_EN 5 +#define BIT_MASK_IC_CON_IC_RESTART_EN 0x1 +#define BIT_CTRL_IC_CON_IC_RESTART_EN(x) (((x) & BIT_MASK_IC_CON_IC_RESTART_EN) << BIT_SHIFT_IC_CON_IC_RESTART_EN) + +#define BIT_IC_CON_IC_10BITADDR_MASTER BIT(4) +#define BIT_SHIFT_IC_CON_IC_10BITADDR_MASTER 4 +#define BIT_MASK_IC_CON_IC_10BITADDR_MASTER 0x1 +#define BIT_CTRL_IC_CON_IC_10BITADDR_MASTER(x) (((x) & BIT_MASK_IC_CON_IC_10BITADDR_MASTER) << BIT_SHIFT_IC_CON_IC_10BITADDR_MASTER) + +#define BIT_IC_CON_IC_10BITADDR_SLAVE BIT(3) +#define BIT_SHIFT_IC_CON_IC_10BITADDR_SLAVE 3 +#define BIT_MASK_IC_CON_IC_10BITADDR_SLAVE 0x1 +#define BIT_CTRL_IC_CON_IC_10BITADDR_SLAVE(x) (((x) & BIT_MASK_IC_CON_IC_10BITADDR_SLAVE) << BIT_SHIFT_IC_CON_IC_10BITADDR_SLAVE) + + +#define BIT_SHIFT_IC_CON_SPEED 1 +#define BIT_MASK_IC_CON_SPEED 0x3 +#define BIT_IC_CON_SPEED(x) (((x) & BIT_MASK_IC_CON_SPEED) << BIT_SHIFT_IC_CON_SPEED) +#define BIT_CTRL_IC_CON_SPEED(x) (((x) & BIT_MASK_IC_CON_SPEED) << BIT_SHIFT_IC_CON_SPEED) +#define BIT_GET_IC_CON_SPEED(x) (((x) >> BIT_SHIFT_IC_CON_SPEED) & BIT_MASK_IC_CON_SPEED) + +#define BIT_IC_CON_MASTER_MODE BIT(0) +#define BIT_SHIFT_IC_CON_MASTER_MODE 0 +#define BIT_MASK_IC_CON_MASTER_MODE 0x1 +#define BIT_CTRL_IC_CON_MASTER_MODE(x) (((x) & BIT_MASK_IC_CON_MASTER_MODE) << BIT_SHIFT_IC_CON_MASTER_MODE) + + +//2 REG_DW_I2C_IC_TAR +#define BIT_IC_TAR_IC_10BITADDR_MASTER BIT(12) +#define BIT_SHIFT_IC_TAR_IC_10BITADDR_MASTER 12 +#define BIT_MASK_IC_TAR_IC_10BITADDR_MASTER 0x1 +#define BIT_CTRL_IC_TAR_IC_10BITADDR_MASTER(x) (((x) & BIT_MASK_IC_TAR_IC_10BITADDR_MASTER) << BIT_SHIFT_IC_TAR_IC_10BITADDR_MASTER) + +#define BIT_IC_TAR_SPECIAL BIT(11) +#define BIT_SHIFT_IC_TAR_SPECIAL 11 +#define BIT_MASK_IC_TAR_SPECIAL 0x1 +#define BIT_CTRL_IC_TAR_SPECIAL(x) (((x) & BIT_MASK_IC_TAR_SPECIAL) << BIT_SHIFT_IC_TAR_SPECIAL) + +#define BIT_IC_TAR_GC_OR_START BIT(10) +#define BIT_SHIFT_IC_TAR_GC_OR_START 10 +#define BIT_MASK_IC_TAR_GC_OR_START 0x1 +#define BIT_CTRL_IC_TAR_GC_OR_START(x) (((x) & BIT_MASK_IC_TAR_GC_OR_START) << BIT_SHIFT_IC_TAR_GC_OR_START) + + +#define BIT_SHIFT_IC_TAR 0 +#define BIT_MASK_IC_TAR 0x3ff +#define BIT_IC_TAR(x) (((x) & BIT_MASK_IC_TAR) << BIT_SHIFT_IC_TAR) +#define BIT_CTRL_IC_TAR(x) (((x) & BIT_MASK_IC_TAR) << BIT_SHIFT_IC_TAR) +#define BIT_GET_IC_TAR(x) (((x) >> BIT_SHIFT_IC_TAR) & BIT_MASK_IC_TAR) + + +//2 REG_DW_I2C_IC_SAR + +#define BIT_SHIFT_IC_SAR 0 +#define BIT_MASK_IC_SAR 0x3ff +#define BIT_IC_SAR(x) (((x) & BIT_MASK_IC_SAR) << BIT_SHIFT_IC_SAR) +#define BIT_CTRL_IC_SAR(x) (((x) & BIT_MASK_IC_SAR) << BIT_SHIFT_IC_SAR) +#define BIT_GET_IC_SAR(x) (((x) >> BIT_SHIFT_IC_SAR) & BIT_MASK_IC_SAR) + + +//2 REG_DW_I2C_IC_HS_MADDR + +#define BIT_SHIFT_IC_HS_MADDR 0 +#define BIT_MASK_IC_HS_MADDR 0x7 +#define BIT_IC_HS_MADDR(x) (((x) & BIT_MASK_IC_HS_MADDR) << BIT_SHIFT_IC_HS_MADDR) +#define BIT_CTRL_IC_HS_MADDR(x) (((x) & BIT_MASK_IC_HS_MADDR) << BIT_SHIFT_IC_HS_MADDR) +#define BIT_GET_IC_HS_MADDR(x) (((x) >> BIT_SHIFT_IC_HS_MADDR) & BIT_MASK_IC_HS_MADDR) + + +//2 REG_DW_I2C_IC_DATA_CMD +#define BIT_IC_DATA_CMD_RESTART BIT(10) +#define BIT_SHIFT_IC_DATA_CMD_RESTART 10 +#define BIT_MASK_IC_DATA_CMD_RESTART 0x1 +#define BIT_CTRL_IC_DATA_CMD_RESTART(x) (((x) & BIT_MASK_IC_DATA_CMD_RESTART) << BIT_SHIFT_IC_DATA_CMD_RESTART) + +#define BIT_IC_DATA_CMD_STOP BIT(9) +#define BIT_SHIFT_IC_DATA_CMD_STOP 9 +#define BIT_MASK_IC_DATA_CMD_STOP 0x1 +#define BIT_CTRL_IC_DATA_CMD_STOP(x) (((x) & BIT_MASK_IC_DATA_CMD_STOP) << BIT_SHIFT_IC_DATA_CMD_STOP) + +#define BIT_IC_DATA_CMD_CMD BIT(8) +#define BIT_SHIFT_IC_DATA_CMD_CMD 8 +#define BIT_MASK_IC_DATA_CMD_CMD 0x1 +#define BIT_CTRL_IC_DATA_CMD_CMD(x) (((x) & BIT_MASK_IC_DATA_CMD_CMD) << BIT_SHIFT_IC_DATA_CMD_CMD) + + +#define BIT_SHIFT_IC_DATA_CMD_DAT 0 +#define BIT_MASK_IC_DATA_CMD_DAT 0xff +#define BIT_IC_DATA_CMD_DAT(x) (((x) & BIT_MASK_IC_DATA_CMD_DAT) << BIT_SHIFT_IC_DATA_CMD_DAT) +#define BIT_CTRL_IC_DATA_CMD_DAT(x) (((x) & BIT_MASK_IC_DATA_CMD_DAT) << BIT_SHIFT_IC_DATA_CMD_DAT) +#define BIT_GET_IC_DATA_CMD_DAT(x) (((x) >> BIT_SHIFT_IC_DATA_CMD_DAT) & BIT_MASK_IC_DATA_CMD_DAT) + + +//2 REG_DW_I2C_IC_SS_SCL_HCNT + +#define BIT_SHIFT_IC_SS_SCL_HCNT 0 +#define BIT_MASK_IC_SS_SCL_HCNT 0xffff +#define BIT_IC_SS_SCL_HCNT(x) (((x) & BIT_MASK_IC_SS_SCL_HCNT) << BIT_SHIFT_IC_SS_SCL_HCNT) +#define BIT_CTRL_IC_SS_SCL_HCNT(x) (((x) & BIT_MASK_IC_SS_SCL_HCNT) << BIT_SHIFT_IC_SS_SCL_HCNT) +#define BIT_GET_IC_SS_SCL_HCNT(x) (((x) >> BIT_SHIFT_IC_SS_SCL_HCNT) & BIT_MASK_IC_SS_SCL_HCNT) + + +//2 REG_DW_I2C_IC_SS_SCL_LCNT + +#define BIT_SHIFT_IC_SS_SCL_LCNT 0 +#define BIT_MASK_IC_SS_SCL_LCNT 0xffff +#define BIT_IC_SS_SCL_LCNT(x) (((x) & BIT_MASK_IC_SS_SCL_LCNT) << BIT_SHIFT_IC_SS_SCL_LCNT) +#define BIT_CTRL_IC_SS_SCL_LCNT(x) (((x) & BIT_MASK_IC_SS_SCL_LCNT) << BIT_SHIFT_IC_SS_SCL_LCNT) +#define BIT_GET_IC_SS_SCL_LCNT(x) (((x) >> BIT_SHIFT_IC_SS_SCL_LCNT) & BIT_MASK_IC_SS_SCL_LCNT) + + +//2 REG_DW_I2C_IC_FS_SCL_HCNT + +#define BIT_SHIFT_IC_FS_SCL_HCNT 0 +#define BIT_MASK_IC_FS_SCL_HCNT 0xffff +#define BIT_IC_FS_SCL_HCNT(x) (((x) & BIT_MASK_IC_FS_SCL_HCNT) << BIT_SHIFT_IC_FS_SCL_HCNT) +#define BIT_CTRL_IC_FS_SCL_HCNT(x) (((x) & BIT_MASK_IC_FS_SCL_HCNT) << BIT_SHIFT_IC_FS_SCL_HCNT) +#define BIT_GET_IC_FS_SCL_HCNT(x) (((x) >> BIT_SHIFT_IC_FS_SCL_HCNT) & BIT_MASK_IC_FS_SCL_HCNT) + + +//2 REG_DW_I2C_IC_FS_SCL_LCNT + +#define BIT_SHIFT_IC_FS_SCL_LCNT 0 +#define BIT_MASK_IC_FS_SCL_LCNT 0xffff +#define BIT_IC_FS_SCL_LCNT(x) (((x) & BIT_MASK_IC_FS_SCL_LCNT) << BIT_SHIFT_IC_FS_SCL_LCNT) +#define BIT_CTRL_IC_FS_SCL_LCNT(x) (((x) & BIT_MASK_IC_FS_SCL_LCNT) << BIT_SHIFT_IC_FS_SCL_LCNT) +#define BIT_GET_IC_FS_SCL_LCNT(x) (((x) >> BIT_SHIFT_IC_FS_SCL_LCNT) & BIT_MASK_IC_FS_SCL_LCNT) + + +//2 REG_DW_I2C_IC_HS_SCL_HCNT + +#define BIT_SHIFT_IC_HS_SCL_HCNT 0 +#define BIT_MASK_IC_HS_SCL_HCNT 0xffff +#define BIT_IC_HS_SCL_HCNT(x) (((x) & BIT_MASK_IC_HS_SCL_HCNT) << BIT_SHIFT_IC_HS_SCL_HCNT) +#define BIT_CTRL_IC_HS_SCL_HCNT(x) (((x) & BIT_MASK_IC_HS_SCL_HCNT) << BIT_SHIFT_IC_HS_SCL_HCNT) +#define BIT_GET_IC_HS_SCL_HCNT(x) (((x) >> BIT_SHIFT_IC_HS_SCL_HCNT) & BIT_MASK_IC_HS_SCL_HCNT) + + +//2 REG_DW_I2C_IC_HS_SCL_LCNT + +#define BIT_SHIFT_IC_HS_SCL_LCNT 0 +#define BIT_MASK_IC_HS_SCL_LCNT 0xffff +#define BIT_IC_HS_SCL_LCNT(x) (((x) & BIT_MASK_IC_HS_SCL_LCNT) << BIT_SHIFT_IC_HS_SCL_LCNT) +#define BIT_CTRL_IC_HS_SCL_LCNT(x) (((x) & BIT_MASK_IC_HS_SCL_LCNT) << BIT_SHIFT_IC_HS_SCL_LCNT) +#define BIT_GET_IC_HS_SCL_LCNT(x) (((x) >> BIT_SHIFT_IC_HS_SCL_LCNT) & BIT_MASK_IC_HS_SCL_LCNT) + + +//2 REG_DW_I2C_IC_INTR_STAT +#define BIT_IC_INTR_STAT_R_GEN_CALL BIT(11) +#define BIT_SHIFT_IC_INTR_STAT_R_GEN_CALL 11 +#define BIT_MASK_IC_INTR_STAT_R_GEN_CALL 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_GEN_CALL(x) (((x) & BIT_MASK_IC_INTR_STAT_R_GEN_CALL) << BIT_SHIFT_IC_INTR_STAT_R_GEN_CALL) + +#define BIT_IC_INTR_STAT_R_START_DET BIT(10) +#define BIT_SHIFT_IC_INTR_STAT_R_START_DET 10 +#define BIT_MASK_IC_INTR_STAT_R_START_DET 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_START_DET(x) (((x) & BIT_MASK_IC_INTR_STAT_R_START_DET) << BIT_SHIFT_IC_INTR_STAT_R_START_DET) + +#define BIT_IC_INTR_STAT_R_STOP_DET BIT(9) +#define BIT_SHIFT_IC_INTR_STAT_R_STOP_DET 9 +#define BIT_MASK_IC_INTR_STAT_R_STOP_DET 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_STOP_DET(x) (((x) & BIT_MASK_IC_INTR_STAT_R_STOP_DET) << BIT_SHIFT_IC_INTR_STAT_R_STOP_DET) + +#define BIT_IC_INTR_STAT_R_ACTIVITY BIT(8) +#define BIT_SHIFT_IC_INTR_STAT_R_ACTIVITY 8 +#define BIT_MASK_IC_INTR_STAT_R_ACTIVITY 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_ACTIVITY(x) (((x) & BIT_MASK_IC_INTR_STAT_R_ACTIVITY) << BIT_SHIFT_IC_INTR_STAT_R_ACTIVITY) + +#define BIT_IC_INTR_STAT_R_RX_DONE BIT(7) +#define BIT_SHIFT_IC_INTR_STAT_R_RX_DONE 7 +#define BIT_MASK_IC_INTR_STAT_R_RX_DONE 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_RX_DONE(x) (((x) & BIT_MASK_IC_INTR_STAT_R_RX_DONE) << BIT_SHIFT_IC_INTR_STAT_R_RX_DONE) + +#define BIT_IC_INTR_STAT_R_TX_ABRT BIT(6) +#define BIT_SHIFT_IC_INTR_STAT_R_TX_ABRT 6 +#define BIT_MASK_IC_INTR_STAT_R_TX_ABRT 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_TX_ABRT(x) (((x) & BIT_MASK_IC_INTR_STAT_R_TX_ABRT) << BIT_SHIFT_IC_INTR_STAT_R_TX_ABRT) + +#define BIT_IC_INTR_STAT_R_RD_REQ BIT(5) +#define BIT_SHIFT_IC_INTR_STAT_R_RD_REQ 5 +#define BIT_MASK_IC_INTR_STAT_R_RD_REQ 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_RD_REQ(x) (((x) & BIT_MASK_IC_INTR_STAT_R_RD_REQ) << BIT_SHIFT_IC_INTR_STAT_R_RD_REQ) + +#define BIT_IC_INTR_STAT_R_TX_EMPTY BIT(4) +#define BIT_SHIFT_IC_INTR_STAT_R_TX_EMPTY 4 +#define BIT_MASK_IC_INTR_STAT_R_TX_EMPTY 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_TX_EMPTY(x) (((x) & BIT_MASK_IC_INTR_STAT_R_TX_EMPTY) << BIT_SHIFT_IC_INTR_STAT_R_TX_EMPTY) + +#define BIT_IC_INTR_STAT_R_TX_OVER BIT(3) +#define BIT_SHIFT_IC_INTR_STAT_R_TX_OVER 3 +#define BIT_MASK_IC_INTR_STAT_R_TX_OVER 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_TX_OVER(x) (((x) & BIT_MASK_IC_INTR_STAT_R_TX_OVER) << BIT_SHIFT_IC_INTR_STAT_R_TX_OVER) + +#define BIT_IC_INTR_STAT_R_RX_FULL BIT(2) +#define BIT_SHIFT_IC_INTR_STAT_R_RX_FULL 2 +#define BIT_MASK_IC_INTR_STAT_R_RX_FULL 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_RX_FULL(x) (((x) & BIT_MASK_IC_INTR_STAT_R_RX_FULL) << BIT_SHIFT_IC_INTR_STAT_R_RX_FULL) + +#define BIT_IC_INTR_STAT_R_RX_OVER BIT(1) +#define BIT_SHIFT_IC_INTR_STAT_R_RX_OVER 1 +#define BIT_MASK_IC_INTR_STAT_R_RX_OVER 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_RX_OVER(x) (((x) & BIT_MASK_IC_INTR_STAT_R_RX_OVER) << BIT_SHIFT_IC_INTR_STAT_R_RX_OVER) + +#define BIT_IC_INTR_STAT_R_RX_UNDER BIT(0) +#define BIT_SHIFT_IC_INTR_STAT_R_RX_UNDER 0 +#define BIT_MASK_IC_INTR_STAT_R_RX_UNDER 0x1 +#define BIT_CTRL_IC_INTR_STAT_R_RX_UNDER(x) (((x) & BIT_MASK_IC_INTR_STAT_R_RX_UNDER) << BIT_SHIFT_IC_INTR_STAT_R_RX_UNDER) + + +//2 REG_DW_I2C_IC_INTR_MASK +#define BIT_IC_INTR_MASK_M_GEN_CALL BIT(11) +#define BIT_SHIFT_IC_INTR_MASK_M_GEN_CALL 11 +#define BIT_MASK_IC_INTR_MASK_M_GEN_CALL 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_GEN_CALL(x) (((x) & BIT_MASK_IC_INTR_MASK_M_GEN_CALL) << BIT_SHIFT_IC_INTR_MASK_M_GEN_CALL) + +#define BIT_IC_INTR_MASK_M_START_DET BIT(10) +#define BIT_SHIFT_IC_INTR_MASK_M_START_DET 10 +#define BIT_MASK_IC_INTR_MASK_M_START_DET 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_START_DET(x) (((x) & BIT_MASK_IC_INTR_MASK_M_START_DET) << BIT_SHIFT_IC_INTR_MASK_M_START_DET) + +#define BIT_IC_INTR_MASK_M_STOP_DET BIT(9) +#define BIT_SHIFT_IC_INTR_MASK_M_STOP_DET 9 +#define BIT_MASK_IC_INTR_MASK_M_STOP_DET 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_STOP_DET(x) (((x) & BIT_MASK_IC_INTR_MASK_M_STOP_DET) << BIT_SHIFT_IC_INTR_MASK_M_STOP_DET) + +#define BIT_IC_INTR_MASK_M_ACTIVITY BIT(8) +#define BIT_SHIFT_IC_INTR_MASK_M_ACTIVITY 8 +#define BIT_MASK_IC_INTR_MASK_M_ACTIVITY 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_ACTIVITY(x) (((x) & BIT_MASK_IC_INTR_MASK_M_ACTIVITY) << BIT_SHIFT_IC_INTR_MASK_M_ACTIVITY) + +#define BIT_IC_INTR_MASK_M_RX_DONE BIT(7) +#define BIT_SHIFT_IC_INTR_MASK_M_RX_DONE 7 +#define BIT_MASK_IC_INTR_MASK_M_RX_DONE 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_RX_DONE(x) (((x) & BIT_MASK_IC_INTR_MASK_M_RX_DONE) << BIT_SHIFT_IC_INTR_MASK_M_RX_DONE) + +#define BIT_IC_INTR_MASK_M_TX_ABRT BIT(6) +#define BIT_SHIFT_IC_INTR_MASK_M_TX_ABRT 6 +#define BIT_MASK_IC_INTR_MASK_M_TX_ABRT 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_TX_ABRT(x) (((x) & BIT_MASK_IC_INTR_MASK_M_TX_ABRT) << BIT_SHIFT_IC_INTR_MASK_M_TX_ABRT) + +#define BIT_IC_INTR_MASK_M_RD_REQ BIT(5) +#define BIT_SHIFT_IC_INTR_MASK_M_RD_REQ 5 +#define BIT_MASK_IC_INTR_MASK_M_RD_REQ 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_RD_REQ(x) (((x) & BIT_MASK_IC_INTR_MASK_M_RD_REQ) << BIT_SHIFT_IC_INTR_MASK_M_RD_REQ) + +#define BIT_IC_INTR_MASK_M_TX_EMPTY BIT(4) +#define BIT_SHIFT_IC_INTR_MASK_M_TX_EMPTY 4 +#define BIT_MASK_IC_INTR_MASK_M_TX_EMPTY 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_TX_EMPTY(x) (((x) & BIT_MASK_IC_INTR_MASK_M_TX_EMPTY) << BIT_SHIFT_IC_INTR_MASK_M_TX_EMPTY) + +#define BIT_IC_INTR_MASK_M_TX_OVER BIT(3) +#define BIT_SHIFT_IC_INTR_MASK_M_TX_OVER 3 +#define BIT_MASK_IC_INTR_MASK_M_TX_OVER 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_TX_OVER(x) (((x) & BIT_MASK_IC_INTR_MASK_M_TX_OVER) << BIT_SHIFT_IC_INTR_MASK_M_TX_OVER) + +#define BIT_IC_INTR_MASK_M_RX_FULL BIT(2) +#define BIT_SHIFT_IC_INTR_MASK_M_RX_FULL 2 +#define BIT_MASK_IC_INTR_MASK_M_RX_FULL 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_RX_FULL(x) (((x) & BIT_MASK_IC_INTR_MASK_M_RX_FULL) << BIT_SHIFT_IC_INTR_MASK_M_RX_FULL) + +#define BIT_IC_INTR_MASK_M_RX_OVER BIT(1) +#define BIT_SHIFT_IC_INTR_MASK_M_RX_OVER 1 +#define BIT_MASK_IC_INTR_MASK_M_RX_OVER 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_RX_OVER(x) (((x) & BIT_MASK_IC_INTR_MASK_M_RX_OVER) << BIT_SHIFT_IC_INTR_MASK_M_RX_OVER) + +#define BIT_IC_INTR_MASK_M_RX_UNDER BIT(0) +#define BIT_SHIFT_IC_INTR_MASK_M_RX_UNDER 0 +#define BIT_MASK_IC_INTR_MASK_M_RX_UNDER 0x1 +#define BIT_CTRL_IC_INTR_MASK_M_RX_UNDER(x) (((x) & BIT_MASK_IC_INTR_MASK_M_RX_UNDER) << BIT_SHIFT_IC_INTR_MASK_M_RX_UNDER) + + +//2 REG_DW_I2C_IC_RAW_INTR_STAT +#define BIT_IC_RAW_INTR_STAT_GEN_CALL BIT(11) +#define BIT_SHIFT_IC_RAW_INTR_STAT_GEN_CALL 11 +#define BIT_MASK_IC_RAW_INTR_STAT_GEN_CALL 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_GEN_CALL(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_GEN_CALL) << BIT_SHIFT_IC_RAW_INTR_STAT_GEN_CALL) + +#define BIT_IC_RAW_INTR_STAT_START_DET BIT(10) +#define BIT_SHIFT_IC_RAW_INTR_STAT_START_DET 10 +#define BIT_MASK_IC_RAW_INTR_STAT_START_DET 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_START_DET(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_START_DET) << BIT_SHIFT_IC_RAW_INTR_STAT_START_DET) + +#define BIT_IC_RAW_INTR_STAT_STOP_DET BIT(9) +#define BIT_SHIFT_IC_RAW_INTR_STAT_STOP_DET 9 +#define BIT_MASK_IC_RAW_INTR_STAT_STOP_DET 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_STOP_DET(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_STOP_DET) << BIT_SHIFT_IC_RAW_INTR_STAT_STOP_DET) + +#define BIT_IC_RAW_INTR_STAT_ACTIVITY BIT(8) +#define BIT_SHIFT_IC_RAW_INTR_STAT_ACTIVITY 8 +#define BIT_MASK_IC_RAW_INTR_STAT_ACTIVITY 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_ACTIVITY(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_ACTIVITY) << BIT_SHIFT_IC_RAW_INTR_STAT_ACTIVITY) + +#define BIT_IC_RAW_INTR_STAT_RX_DONE BIT(7) +#define BIT_SHIFT_IC_RAW_INTR_STAT_RX_DONE 7 +#define BIT_MASK_IC_RAW_INTR_STAT_RX_DONE 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_RX_DONE(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_RX_DONE) << BIT_SHIFT_IC_RAW_INTR_STAT_RX_DONE) + +#define BIT_IC_RAW_INTR_STAT_TX_ABRT BIT(6) +#define BIT_SHIFT_IC_RAW_INTR_STAT_TX_ABRT 6 +#define BIT_MASK_IC_RAW_INTR_STAT_TX_ABRT 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_TX_ABRT(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_TX_ABRT) << BIT_SHIFT_IC_RAW_INTR_STAT_TX_ABRT) + +#define BIT_IC_RAW_INTR_STAT_RD_REQ BIT(5) +#define BIT_SHIFT_IC_RAW_INTR_STAT_RD_REQ 5 +#define BIT_MASK_IC_RAW_INTR_STAT_RD_REQ 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_RD_REQ(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_RD_REQ) << BIT_SHIFT_IC_RAW_INTR_STAT_RD_REQ) + +#define BIT_IC_RAW_INTR_STAT_TX_EMPTY BIT(4) +#define BIT_SHIFT_IC_RAW_INTR_STAT_TX_EMPTY 4 +#define BIT_MASK_IC_RAW_INTR_STAT_TX_EMPTY 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_TX_EMPTY(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_TX_EMPTY) << BIT_SHIFT_IC_RAW_INTR_STAT_TX_EMPTY) + +#define BIT_IC_RAW_INTR_STAT_TX_OVER BIT(3) +#define BIT_SHIFT_IC_RAW_INTR_STAT_TX_OVER 3 +#define BIT_MASK_IC_RAW_INTR_STAT_TX_OVER 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_TX_OVER(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_TX_OVER) << BIT_SHIFT_IC_RAW_INTR_STAT_TX_OVER) + +#define BIT_IC_RAW_INTR_STAT_RX_FULL BIT(2) +#define BIT_SHIFT_IC_RAW_INTR_STAT_RX_FULL 2 +#define BIT_MASK_IC_RAW_INTR_STAT_RX_FULL 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_RX_FULL(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_RX_FULL) << BIT_SHIFT_IC_RAW_INTR_STAT_RX_FULL) + +#define BIT_IC_RAW_INTR_STAT_RX_OVER BIT(1) +#define BIT_SHIFT_IC_RAW_INTR_STAT_RX_OVER 1 +#define BIT_MASK_IC_RAW_INTR_STAT_RX_OVER 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_RX_OVER(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_RX_OVER) << BIT_SHIFT_IC_RAW_INTR_STAT_RX_OVER) + +#define BIT_IC_RAW_INTR_STAT_RX_UNDER BIT(0) +#define BIT_SHIFT_IC_RAW_INTR_STAT_RX_UNDER 0 +#define BIT_MASK_IC_RAW_INTR_STAT_RX_UNDER 0x1 +#define BIT_CTRL_IC_RAW_INTR_STAT_RX_UNDER(x) (((x) & BIT_MASK_IC_RAW_INTR_STAT_RX_UNDER) << BIT_SHIFT_IC_RAW_INTR_STAT_RX_UNDER) + + +//2 REG_DW_I2C_IC_RX_TL + +#define BIT_SHIFT_IC_RX_TL 0 +#define BIT_MASK_IC_RX_TL 0xff +#define BIT_IC_RX_TL(x) (((x) & BIT_MASK_IC_RX_TL) << BIT_SHIFT_IC_RX_TL) +#define BIT_CTRL_IC_RX_TL(x) (((x) & BIT_MASK_IC_RX_TL) << BIT_SHIFT_IC_RX_TL) +#define BIT_GET_IC_RX_TL(x) (((x) >> BIT_SHIFT_IC_RX_TL) & BIT_MASK_IC_RX_TL) + + +//2 REG_DW_I2C_IC_TX_TL + +#define BIT_SHIFT_IC_TX_TL 0 +#define BIT_MASK_IC_TX_TL 0xff +#define BIT_IC_TX_TL(x) (((x) & BIT_MASK_IC_TX_TL) << BIT_SHIFT_IC_TX_TL) +#define BIT_CTRL_IC_TX_TL(x) (((x) & BIT_MASK_IC_TX_TL) << BIT_SHIFT_IC_TX_TL) +#define BIT_GET_IC_TX_TL(x) (((x) >> BIT_SHIFT_IC_TX_TL) & BIT_MASK_IC_TX_TL) + + +//2 REG_DW_I2C_IC_CLR_INTR +#define BIT_IC_CLR_INTR BIT(0) +#define BIT_SHIFT_IC_CLR_INTR 0 +#define BIT_MASK_IC_CLR_INTR 0x1 +#define BIT_CTRL_IC_CLR_INTR(x) (((x) & BIT_MASK_IC_CLR_INTR) << BIT_SHIFT_IC_CLR_INTR) + + +//2 REG_DW_I2C_IC_CLR_RX_UNDER +#define BIT_IC_CLR_RX_UNDER BIT(0) +#define BIT_SHIFT_IC_CLR_RX_UNDER 0 +#define BIT_MASK_IC_CLR_RX_UNDER 0x1 +#define BIT_CTRL_IC_CLR_RX_UNDER(x) (((x) & BIT_MASK_IC_CLR_RX_UNDER) << BIT_SHIFT_IC_CLR_RX_UNDER) + + +//2 REG_DW_I2C_IC_CLR_RX_OVER +#define BIT_IC_CLR_RX_OVER BIT(0) +#define BIT_SHIFT_IC_CLR_RX_OVER 0 +#define BIT_MASK_IC_CLR_RX_OVER 0x1 +#define BIT_CTRL_IC_CLR_RX_OVER(x) (((x) & BIT_MASK_IC_CLR_RX_OVER) << BIT_SHIFT_IC_CLR_RX_OVER) + + +//2 REG_DW_I2C_IC_CLR_TX_OVER +#define BIT_IC_CLR_TX_OVER BIT(0) +#define BIT_SHIFT_IC_CLR_TX_OVER 0 +#define BIT_MASK_IC_CLR_TX_OVER 0x1 +#define BIT_CTRL_IC_CLR_TX_OVER(x) (((x) & BIT_MASK_IC_CLR_TX_OVER) << BIT_SHIFT_IC_CLR_TX_OVER) + + +//2 REG_DW_I2C_IC_CLR_RD_REQ +#define BIT_IC_CLR_RD_REQ BIT(0) +#define BIT_SHIFT_IC_CLR_RD_REQ 0 +#define BIT_MASK_IC_CLR_RD_REQ 0x1 +#define BIT_CTRL_IC_CLR_RD_REQ(x) (((x) & BIT_MASK_IC_CLR_RD_REQ) << BIT_SHIFT_IC_CLR_RD_REQ) + + +//2 REG_DW_I2C_IC_CLR_TX_ABRT +#define BIT_CLR_RD_REQ BIT(0) +#define BIT_SHIFT_CLR_RD_REQ 0 +#define BIT_MASK_CLR_RD_REQ 0x1 +#define BIT_CTRL_CLR_RD_REQ(x) (((x) & BIT_MASK_CLR_RD_REQ) << BIT_SHIFT_CLR_RD_REQ) + + +//2 REG_DW_I2C_IC_CLR_RX_DONE +#define BIT_IC_CLR_RX_DONE BIT(0) +#define BIT_SHIFT_IC_CLR_RX_DONE 0 +#define BIT_MASK_IC_CLR_RX_DONE 0x1 +#define BIT_CTRL_IC_CLR_RX_DONE(x) (((x) & BIT_MASK_IC_CLR_RX_DONE) << BIT_SHIFT_IC_CLR_RX_DONE) + + +//2 REG_DW_I2C_IC_CLR_ACTIVITY +#define BIT_IC_CLR_ACTIVITY BIT(0) +#define BIT_SHIFT_IC_CLR_ACTIVITY 0 +#define BIT_MASK_IC_CLR_ACTIVITY 0x1 +#define BIT_CTRL_IC_CLR_ACTIVITY(x) (((x) & BIT_MASK_IC_CLR_ACTIVITY) << BIT_SHIFT_IC_CLR_ACTIVITY) + + +//2 REG_DW_I2C_IC_CLR_STOP_DET +#define BIT_IC_CLR_STOP_DET BIT(0) +#define BIT_SHIFT_IC_CLR_STOP_DET 0 +#define BIT_MASK_IC_CLR_STOP_DET 0x1 +#define BIT_CTRL_IC_CLR_STOP_DET(x) (((x) & BIT_MASK_IC_CLR_STOP_DET) << BIT_SHIFT_IC_CLR_STOP_DET) + + +//2 REG_DW_I2C_IC_CLR_START_DET +#define BIT_IC_CLR_START_DET BIT(0) +#define BIT_SHIFT_IC_CLR_START_DET 0 +#define BIT_MASK_IC_CLR_START_DET 0x1 +#define BIT_CTRL_IC_CLR_START_DET(x) (((x) & BIT_MASK_IC_CLR_START_DET) << BIT_SHIFT_IC_CLR_START_DET) + + +//2 REG_DW_I2C_IC_CLR_GEN_CALL +#define BIT_IC_CLR_GEN_CALL BIT(0) +#define BIT_SHIFT_IC_CLR_GEN_CALL 0 +#define BIT_MASK_IC_CLR_GEN_CALL 0x1 +#define BIT_CTRL_IC_CLR_GEN_CALL(x) (((x) & BIT_MASK_IC_CLR_GEN_CALL) << BIT_SHIFT_IC_CLR_GEN_CALL) + + +//2 REG_DW_I2C_IC_ENABLE +#define BIT_IC_ENABLE BIT(0) +#define BIT_SHIFT_IC_ENABLE 0 +#define BIT_MASK_IC_ENABLE 0x1 +#define BIT_CTRL_IC_ENABLE(x) (((x) & BIT_MASK_IC_ENABLE) << BIT_SHIFT_IC_ENABLE) + + +//2 REG_DW_I2C_IC_STATUS +#define BIT_IC_STATUS_SLV_ACTIVITY BIT(6) +#define BIT_SHIFT_IC_STATUS_SLV_ACTIVITY 6 +#define BIT_MASK_IC_STATUS_SLV_ACTIVITY 0x1 +#define BIT_CTRL_IC_STATUS_SLV_ACTIVITY(x) (((x) & BIT_MASK_IC_STATUS_SLV_ACTIVITY) << BIT_SHIFT_IC_STATUS_SLV_ACTIVITY) + +#define BIT_IC_STATUS_MST_ACTIVITY BIT(5) +#define BIT_SHIFT_IC_STATUS_MST_ACTIVITY 5 +#define BIT_MASK_IC_STATUS_MST_ACTIVITY 0x1 +#define BIT_CTRL_IC_STATUS_MST_ACTIVITY(x) (((x) & BIT_MASK_IC_STATUS_MST_ACTIVITY) << BIT_SHIFT_IC_STATUS_MST_ACTIVITY) + +#define BIT_IC_STATUS_RFF BIT(4) +#define BIT_SHIFT_IC_STATUS_RFF 4 +#define BIT_MASK_IC_STATUS_RFF 0x1 +#define BIT_CTRL_IC_STATUS_RFF(x) (((x) & BIT_MASK_IC_STATUS_RFF) << BIT_SHIFT_IC_STATUS_RFF) + +#define BIT_IC_STATUS_RFNE BIT(3) +#define BIT_SHIFT_IC_STATUS_RFNE 3 +#define BIT_MASK_IC_STATUS_RFNE 0x1 +#define BIT_CTRL_IC_STATUS_RFNE(x) (((x) & BIT_MASK_IC_STATUS_RFNE) << BIT_SHIFT_IC_STATUS_RFNE) + +#define BIT_IC_STATUS_TFE BIT(2) +#define BIT_SHIFT_IC_STATUS_TFE 2 +#define BIT_MASK_IC_STATUS_TFE 0x1 +#define BIT_CTRL_IC_STATUS_TFE(x) (((x) & BIT_MASK_IC_STATUS_TFE) << BIT_SHIFT_IC_STATUS_TFE) + +#define BIT_IC_STATUS_TFNF BIT(1) +#define BIT_SHIFT_IC_STATUS_TFNF 1 +#define BIT_MASK_IC_STATUS_TFNF 0x1 +#define BIT_CTRL_IC_STATUS_TFNF(x) (((x) & BIT_MASK_IC_STATUS_TFNF) << BIT_SHIFT_IC_STATUS_TFNF) + +#define BIT_IC_STATUS_ACTIVITY BIT(0) +#define BIT_SHIFT_IC_STATUS_ACTIVITY 0 +#define BIT_MASK_IC_STATUS_ACTIVITY 0x1 +#define BIT_CTRL_IC_STATUS_ACTIVITY(x) (((x) & BIT_MASK_IC_STATUS_ACTIVITY) << BIT_SHIFT_IC_STATUS_ACTIVITY) + + +//2 REG_DW_I2C_IC_TXFLR + +#define BIT_SHIFT_IC_TXFLR 0 +#define BIT_MASK_IC_TXFLR 0x3f +#define BIT_IC_TXFLR(x) (((x) & BIT_MASK_IC_TXFLR) << BIT_SHIFT_IC_TXFLR) +#define BIT_CTRL_IC_TXFLR(x) (((x) & BIT_MASK_IC_TXFLR) << BIT_SHIFT_IC_TXFLR) +#define BIT_GET_IC_TXFLR(x) (((x) >> BIT_SHIFT_IC_TXFLR) & BIT_MASK_IC_TXFLR) + + +//2 REG_DW_I2C_IC_RXFLR + +#define BIT_SHIFT_IC_RXFLR 0 +#define BIT_MASK_IC_RXFLR 0x1f +#define BIT_IC_RXFLR(x) (((x) & BIT_MASK_IC_RXFLR) << BIT_SHIFT_IC_RXFLR) +#define BIT_CTRL_IC_RXFLR(x) (((x) & BIT_MASK_IC_RXFLR) << BIT_SHIFT_IC_RXFLR) +#define BIT_GET_IC_RXFLR(x) (((x) >> BIT_SHIFT_IC_RXFLR) & BIT_MASK_IC_RXFLR) + + +//2 REG_DW_I2C_IC_SDA_HOLD + +#define BIT_SHIFT_IC_SDA_HOLD 0 +#define BIT_MASK_IC_SDA_HOLD 0xffff +#define BIT_IC_SDA_HOLD(x) (((x) & BIT_MASK_IC_SDA_HOLD) << BIT_SHIFT_IC_SDA_HOLD) +#define BIT_CTRL_IC_SDA_HOLD(x) (((x) & BIT_MASK_IC_SDA_HOLD) << BIT_SHIFT_IC_SDA_HOLD) +#define BIT_GET_IC_SDA_HOLD(x) (((x) >> BIT_SHIFT_IC_SDA_HOLD) & BIT_MASK_IC_SDA_HOLD) + + +//2 REG_DW_I2C_IC_TX_ABRT_SOURCE +#define BIT_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX BIT(15) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX 15 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLVRD_INTX) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST BIT(14) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST 14 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLV_ARBLOST) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO BIT(13) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO 13 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SLVFLUSH_TXFIFO) + +#define BIT_IC_TX_ABRT_SOURCE_ARB_LOST BIT(12) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ARB_LOST 12 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ARB_LOST 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ARB_LOST(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ARB_LOST) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ARB_LOST) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS BIT(11) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS 11 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_MASTER_DIS) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT BIT(10) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT 10 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10B_RD_NORSTRT) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT BIT(9) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT 9 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_NORSTRT) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT BIT(8) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT 8 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_HS_NORSTRT) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET BIT(7) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET 7 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_SBYTE_ACKDET) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET BIT(6) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET 6 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_HS_ACKDET) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ BIT(5) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ 5 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_GCALL_READ) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK BIT(4) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK 4 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_GCALL_NOACK) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK BIT(3) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK 3 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK BIT(2) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK 2 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10ADDR2_NOACK) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK BIT(1) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK 1 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_10ADDR1_NOACK) + +#define BIT_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK BIT(0) +#define BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK 0 +#define BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK 0x1 +#define BIT_CTRL_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK(x) (((x) & BIT_MASK_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK) << BIT_SHIFT_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK) + + +//2 REG_DW_I2C_IC_SLV_DATA_NACK_ONLY +#define BIT_IC_SLV_DATA_NACK_ONLY BIT(0) +#define BIT_SHIFT_IC_SLV_DATA_NACK_ONLY 0 +#define BIT_MASK_IC_SLV_DATA_NACK_ONLY 0x1 +#define BIT_CTRL_IC_SLV_DATA_NACK_ONLY(x) (((x) & BIT_MASK_IC_SLV_DATA_NACK_ONLY) << BIT_SHIFT_IC_SLV_DATA_NACK_ONLY) + + +//2 REG_DW_I2C_IC_DMA_CR +#define BIT_IC_DMA_CR_TDMAE BIT(1) +#define BIT_SHIFT_IC_DMA_CR_TDMAE 1 +#define BIT_MASK_IC_DMA_CR_TDMAE 0x1 +#define BIT_CTRL_IC_DMA_CR_TDMAE(x) (((x) & BIT_MASK_IC_DMA_CR_TDMAE) << BIT_SHIFT_IC_DMA_CR_TDMAE) + +#define BIT_IC_DMA_CR_RDMAE BIT(0) +#define BIT_SHIFT_IC_DMA_CR_RDMAE 0 +#define BIT_MASK_IC_DMA_CR_RDMAE 0x1 +#define BIT_CTRL_IC_DMA_CR_RDMAE(x) (((x) & BIT_MASK_IC_DMA_CR_RDMAE) << BIT_SHIFT_IC_DMA_CR_RDMAE) + + +//2 REG_DW_I2C_IC_DMA_TDLR + +#define BIT_SHIFT_IC_DMA_TDLR_DMATDL 0 +#define BIT_MASK_IC_DMA_TDLR_DMATDL 0x1f +#define BIT_IC_DMA_TDLR_DMATDL(x) (((x) & BIT_MASK_IC_DMA_TDLR_DMATDL) << BIT_SHIFT_IC_DMA_TDLR_DMATDL) +#define BIT_CTRL_IC_DMA_TDLR_DMATDL(x) (((x) & BIT_MASK_IC_DMA_TDLR_DMATDL) << BIT_SHIFT_IC_DMA_TDLR_DMATDL) +#define BIT_GET_IC_DMA_TDLR_DMATDL(x) (((x) >> BIT_SHIFT_IC_DMA_TDLR_DMATDL) & BIT_MASK_IC_DMA_TDLR_DMATDL) + + +//2 REG_DW_I2C_IC_DMA_RDLR + +#define BIT_SHIFT_IC_DMA_RDLR_DMARDL 0 +#define BIT_MASK_IC_DMA_RDLR_DMARDL 0xf +#define BIT_IC_DMA_RDLR_DMARDL(x) (((x) & BIT_MASK_IC_DMA_RDLR_DMARDL) << BIT_SHIFT_IC_DMA_RDLR_DMARDL) +#define BIT_CTRL_IC_DMA_RDLR_DMARDL(x) (((x) & BIT_MASK_IC_DMA_RDLR_DMARDL) << BIT_SHIFT_IC_DMA_RDLR_DMARDL) +#define BIT_GET_IC_DMA_RDLR_DMARDL(x) (((x) >> BIT_SHIFT_IC_DMA_RDLR_DMARDL) & BIT_MASK_IC_DMA_RDLR_DMARDL) + + +//2 REG_DW_I2C_IC_SDA_SETUP + +#define BIT_SHIFT_IC_SDA_SETUP 0 +#define BIT_MASK_IC_SDA_SETUP 0xff +#define BIT_IC_SDA_SETUP(x) (((x) & BIT_MASK_IC_SDA_SETUP) << BIT_SHIFT_IC_SDA_SETUP) +#define BIT_CTRL_IC_SDA_SETUP(x) (((x) & BIT_MASK_IC_SDA_SETUP) << BIT_SHIFT_IC_SDA_SETUP) +#define BIT_GET_IC_SDA_SETUP(x) (((x) >> BIT_SHIFT_IC_SDA_SETUP) & BIT_MASK_IC_SDA_SETUP) + + +//2 REG_DW_I2C_IC_ACK_GENERAL_CALL +#define BIT_IC_ACK_GENERAL_CALL BIT(0) +#define BIT_SHIFT_IC_ACK_GENERAL_CALL 0 +#define BIT_MASK_IC_ACK_GENERAL_CALL 0x1 +#define BIT_CTRL_IC_ACK_GENERAL_CALL(x) (((x) & BIT_MASK_IC_ACK_GENERAL_CALL) << BIT_SHIFT_IC_ACK_GENERAL_CALL) + + +//2 REG_DW_I2C_IC_ENABLE_STATUS +#define BIT_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2) +#define BIT_SHIFT_IC_ENABLE_STATUS_SLV_RX_DATA_LOST 2 +#define BIT_MASK_IC_ENABLE_STATUS_SLV_RX_DATA_LOST 0x1 +#define BIT_CTRL_IC_ENABLE_STATUS_SLV_RX_DATA_LOST(x) (((x) & BIT_MASK_IC_ENABLE_STATUS_SLV_RX_DATA_LOST) << BIT_SHIFT_IC_ENABLE_STATUS_SLV_RX_DATA_LOST) + +#define BIT_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1) +#define BIT_SHIFT_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY 1 +#define BIT_MASK_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY 0x1 +#define BIT_CTRL_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY(x) (((x) & BIT_MASK_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY) << BIT_SHIFT_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY) + +#define BIT_IC_ENABLE_STATUS_IC_EN BIT(0) +#define BIT_SHIFT_IC_ENABLE_STATUS_IC_EN 0 +#define BIT_MASK_IC_ENABLE_STATUS_IC_EN 0x1 +#define BIT_CTRL_IC_ENABLE_STATUS_IC_EN(x) (((x) & BIT_MASK_IC_ENABLE_STATUS_IC_EN) << BIT_SHIFT_IC_ENABLE_STATUS_IC_EN) + + +//2 REG_DW_I2C_IC_COMP_PARAM_1 + +#define BIT_SHIFT_IC_COMP_PARAM_1_TX_BUFFER_DEPTH 16 +#define BIT_MASK_IC_COMP_PARAM_1_TX_BUFFER_DEPTH 0xff +#define BIT_IC_COMP_PARAM_1_TX_BUFFER_DEPTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) << BIT_SHIFT_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) +#define BIT_CTRL_IC_COMP_PARAM_1_TX_BUFFER_DEPTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) << BIT_SHIFT_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) +#define BIT_GET_IC_COMP_PARAM_1_TX_BUFFER_DEPTH(x) (((x) >> BIT_SHIFT_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) & BIT_MASK_IC_COMP_PARAM_1_TX_BUFFER_DEPTH) + + +#define BIT_SHIFT_IC_COMP_PARAM_1_RX_BUFFER_DEPTH 8 +#define BIT_MASK_IC_COMP_PARAM_1_RX_BUFFER_DEPTH 0xff +#define BIT_IC_COMP_PARAM_1_RX_BUFFER_DEPTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) << BIT_SHIFT_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) +#define BIT_CTRL_IC_COMP_PARAM_1_RX_BUFFER_DEPTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) << BIT_SHIFT_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) +#define BIT_GET_IC_COMP_PARAM_1_RX_BUFFER_DEPTH(x) (((x) >> BIT_SHIFT_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) & BIT_MASK_IC_COMP_PARAM_1_RX_BUFFER_DEPTH) + +#define BIT_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS BIT(7) +#define BIT_SHIFT_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS 7 +#define BIT_MASK_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS 0x1 +#define BIT_CTRL_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS) << BIT_SHIFT_IC_COMP_PARAM_1_ADD_ENCODED_PARAMS) + +#define BIT_IC_COMP_PARAM_1_HAS_DMA BIT(6) +#define BIT_SHIFT_IC_COMP_PARAM_1_HAS_DMA 6 +#define BIT_MASK_IC_COMP_PARAM_1_HAS_DMA 0x1 +#define BIT_CTRL_IC_COMP_PARAM_1_HAS_DMA(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_HAS_DMA) << BIT_SHIFT_IC_COMP_PARAM_1_HAS_DMA) + +#define BIT_IC_COMP_PARAM_1_INTR_IO BIT(5) +#define BIT_SHIFT_IC_COMP_PARAM_1_INTR_IO 5 +#define BIT_MASK_IC_COMP_PARAM_1_INTR_IO 0x1 +#define BIT_CTRL_IC_COMP_PARAM_1_INTR_IO(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_INTR_IO) << BIT_SHIFT_IC_COMP_PARAM_1_INTR_IO) + +#define BIT_IC_COMP_PARAM_1_HC_COUNT_VALUES BIT(4) +#define BIT_SHIFT_IC_COMP_PARAM_1_HC_COUNT_VALUES 4 +#define BIT_MASK_IC_COMP_PARAM_1_HC_COUNT_VALUES 0x1 +#define BIT_CTRL_IC_COMP_PARAM_1_HC_COUNT_VALUES(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_HC_COUNT_VALUES) << BIT_SHIFT_IC_COMP_PARAM_1_HC_COUNT_VALUES) + + +#define BIT_SHIFT_IC_COMP_PARAM_1_MAX_SPEED_MODE 2 +#define BIT_MASK_IC_COMP_PARAM_1_MAX_SPEED_MODE 0x3 +#define BIT_IC_COMP_PARAM_1_MAX_SPEED_MODE(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_MAX_SPEED_MODE) << BIT_SHIFT_IC_COMP_PARAM_1_MAX_SPEED_MODE) +#define BIT_CTRL_IC_COMP_PARAM_1_MAX_SPEED_MODE(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_MAX_SPEED_MODE) << BIT_SHIFT_IC_COMP_PARAM_1_MAX_SPEED_MODE) +#define BIT_GET_IC_COMP_PARAM_1_MAX_SPEED_MODE(x) (((x) >> BIT_SHIFT_IC_COMP_PARAM_1_MAX_SPEED_MODE) & BIT_MASK_IC_COMP_PARAM_1_MAX_SPEED_MODE) + + +#define BIT_SHIFT_IC_COMP_PARAM_1_APB_DATA_WIDTH 0 +#define BIT_MASK_IC_COMP_PARAM_1_APB_DATA_WIDTH 0x3 +#define BIT_IC_COMP_PARAM_1_APB_DATA_WIDTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_APB_DATA_WIDTH) << BIT_SHIFT_IC_COMP_PARAM_1_APB_DATA_WIDTH) +#define BIT_CTRL_IC_COMP_PARAM_1_APB_DATA_WIDTH(x) (((x) & BIT_MASK_IC_COMP_PARAM_1_APB_DATA_WIDTH) << BIT_SHIFT_IC_COMP_PARAM_1_APB_DATA_WIDTH) +#define BIT_GET_IC_COMP_PARAM_1_APB_DATA_WIDTH(x) (((x) >> BIT_SHIFT_IC_COMP_PARAM_1_APB_DATA_WIDTH) & BIT_MASK_IC_COMP_PARAM_1_APB_DATA_WIDTH) + + +//2 REG_DW_I2C_IC_COMP_VERSION + +#define BIT_SHIFT_IC_COMP_VERSION 0 +#define BIT_MASK_IC_COMP_VERSION 0xffffffffL +#define BIT_IC_COMP_VERSION(x) (((x) & BIT_MASK_IC_COMP_VERSION) << BIT_SHIFT_IC_COMP_VERSION) +#define BIT_CTRL_IC_COMP_VERSION(x) (((x) & BIT_MASK_IC_COMP_VERSION) << BIT_SHIFT_IC_COMP_VERSION) +#define BIT_GET_IC_COMP_VERSION(x) (((x) >> BIT_SHIFT_IC_COMP_VERSION) & BIT_MASK_IC_COMP_VERSION) + + +//2 REG_DW_I2C_IC_COMP_TYPE + +#define BIT_SHIFT_IC_COMP_TYPE 0 +#define BIT_MASK_IC_COMP_TYPE 0xffffffffL +#define BIT_IC_COMP_TYPE(x) (((x) & BIT_MASK_IC_COMP_TYPE) << BIT_SHIFT_IC_COMP_TYPE) +#define BIT_CTRL_IC_COMP_TYPE(x) (((x) & BIT_MASK_IC_COMP_TYPE) << BIT_SHIFT_IC_COMP_TYPE) +#define BIT_GET_IC_COMP_TYPE(x) (((x) >> BIT_SHIFT_IC_COMP_TYPE) & BIT_MASK_IC_COMP_TYPE) + +//======================== Register Address Definition ======================== +#define REG_DW_I2C_IC_CON 0x0000 +#define REG_DW_I2C_IC_TAR 0x0004 +#define REG_DW_I2C_IC_SAR 0x0008 +#define REG_DW_I2C_IC_HS_MADDR 0x000C +#define REG_DW_I2C_IC_DATA_CMD 0x0010 +#define REG_DW_I2C_IC_SS_SCL_HCNT 0x0014 +#define REG_DW_I2C_IC_SS_SCL_LCNT 0x0018 +#define REG_DW_I2C_IC_FS_SCL_HCNT 0x001C +#define REG_DW_I2C_IC_FS_SCL_LCNT 0x0020 +#define REG_DW_I2C_IC_HS_SCL_HCNT 0x0024 +#define REG_DW_I2C_IC_HS_SCL_LCNT 0x0028 +#define REG_DW_I2C_IC_INTR_STAT 0x002C +#define REG_DW_I2C_IC_INTR_MASK 0x0030 +#define REG_DW_I2C_IC_RAW_INTR_STAT 0x0034 +#define REG_DW_I2C_IC_RX_TL 0x0038 +#define REG_DW_I2C_IC_TX_TL 0x003C +#define REG_DW_I2C_IC_CLR_INTR 0x0040 +#define REG_DW_I2C_IC_CLR_RX_UNDER 0x0044 +#define REG_DW_I2C_IC_CLR_RX_OVER 0x0048 +#define REG_DW_I2C_IC_CLR_TX_OVER 0x004C +#define REG_DW_I2C_IC_CLR_RD_REQ 0x0050 +#define REG_DW_I2C_IC_CLR_TX_ABRT 0x0054 +#define REG_DW_I2C_IC_CLR_RX_DONE 0x0058 +#define REG_DW_I2C_IC_CLR_ACTIVITY 0x005C +#define REG_DW_I2C_IC_CLR_STOP_DET 0x0060 +#define REG_DW_I2C_IC_CLR_START_DET 0x0064 +#define REG_DW_I2C_IC_CLR_GEN_CALL 0x0068 +#define REG_DW_I2C_IC_ENABLE 0x006C +#define REG_DW_I2C_IC_STATUS 0x0070 +#define REG_DW_I2C_IC_TXFLR 0x0074 +#define REG_DW_I2C_IC_RXFLR 0x0078 +#define REG_DW_I2C_IC_SDA_HOLD 0x007C +#define REG_DW_I2C_IC_TX_ABRT_SOURCE 0x0080 +#define REG_DW_I2C_IC_SLV_DATA_NACK_ONLY 0x0084 +#define REG_DW_I2C_IC_DMA_CR 0x0088 +#define REG_DW_I2C_IC_DMA_TDLR 0x008C +#define REG_DW_I2C_IC_DMA_RDLR 0x0090 +#define REG_DW_I2C_IC_SDA_SETUP 0x0094 +#define REG_DW_I2C_IC_ACK_GENERAL_CALL 0x0098 +#define REG_DW_I2C_IC_ENABLE_STATUS 0x009C +#define REG_DW_I2C_IC_COMP_PARAM_1 0x00F4 +#define REG_DW_I2C_IC_COMP_VERSION 0x00F8 +#define REG_DW_I2C_IC_COMP_TYPE 0x00FC + +//====================================================== +// I2C related enumeration +// I2C Address Mode +typedef enum _I2C_ADDR_MODE_ { + I2C_ADDR_7BIT = 0, + I2C_ADDR_10BIT = 1, +}I2C_ADDR_MODE,*PI2C_ADDR_MODE; + +// I2C Speed Mode +typedef enum _I2C_SPD_MODE_ { + I2C_SS_MODE = 1, + I2C_FS_MODE = 2, + I2C_HS_MODE = 3, +}I2C_SPD_MODE,*PI2C_SPD_MODE; + +//I2C Timing Parameters +#define I2C_SS_MIN_SCL_HTIME 4000 //the unit is ns. +#define I2C_SS_MIN_SCL_LTIME 4700 //the unit is ns. + +#define I2C_FS_MIN_SCL_HTIME 600 //the unit is ns. +#define I2C_FS_MIN_SCL_LTIME 1300 //the unit is ns. + +#define I2C_HS_MIN_SCL_HTIME_100 60 //the unit is ns, with bus loading = 100pf +#define I2C_HS_MIN_SCL_LTIME_100 120 //the unit is ns., with bus loading = 100pf + +#define I2C_HS_MIN_SCL_HTIME_400 160 //the unit is ns, with bus loading = 400pf +#define I2C_HS_MIN_SCL_LTIME_400 320 //the unit is ns., with bus loading = 400pf + + +//====================================================== +//I2C Essential functions and macros +_LONG_CALL_ VOID HalI2CWrite32(IN u8 I2CIdx, IN u8 I2CReg, IN u32 I2CVal); +_LONG_CALL_ u32 HalI2CRead32(IN u8 I2CIdx, IN u8 I2CReg); + +#define HAL_I2C_WRITE32(I2CIdx, addr, value) HalI2CWrite32(I2CIdx,addr,value) +#define HAL_I2C_READ32(I2CIdx, addr) HalI2CRead32(I2CIdx,addr) + +// Rtl8195a I2C function prototypes +_LONG_CALL_ HAL_Status HalI2CEnableRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CInit8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CDeInit8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CSetCLKRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CMassSendRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CSendRtl8195a(IN VOID *Data); +_LONG_CALL_ u8 HalI2CReceiveRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CIntrCtrl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CClrIntrRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CClrAllIntrRtl8195a(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CDMACtrl8195a(IN VOID *Data); +_LONG_CALL_ u32 HalI2CReadRegRtl8195a(IN VOID *Data, IN u8 I2CReg); +_LONG_CALL_ HAL_Status HalI2CWriteRegRtl8195a(IN VOID *Data, IN u8 I2CReg, IN u32 RegVal); +_LONG_CALL_ HAL_Status HalI2CDMACtrl8195a(IN VOID *Data); + +//Rtl8195a I2C V02 function prototype +_LONG_CALL_ HAL_Status HalI2CSendRtl8195aV02(IN VOID *Data); +_LONG_CALL_ HAL_Status HalI2CSetCLKRtl8195aV02(IN VOID *Data); +//Rtl8195a I2C V02 function prototype END + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2s.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2s.h new file mode 100644 index 0000000..33a0bf8 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_i2s.h @@ -0,0 +1,617 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_I2S_H_ +#define _RTL8195A_I2S_H_ + + +//=============== Register Bit Field Definition ==================== +// REG_I2S_CONTROL +#define BIT_CTLX_I2S_EN BIT(0) +#define BIT_SHIFT_CTLX_I2S_EN 0 +#define BIT_MASK_CTLX_I2S_EN 0x1 +#define BIT_CTRL_CTLX_I2S_EN(x) (((x) & BIT_MASK_CTLX_I2S_EN) << BIT_SHIFT_CTLX_I2S_EN) + +#define BIT_SHIFT_CTLX_I2S_TRX_ACT 1 +#define BIT_MASK_CTLX_I2S_TRX_ACT 0x3 +#define BIT_CTRL_CTLX_I2S_TRX_ACT(x) (((x) & BIT_MASK_CTLX_I2S_TRX_ACT) << BIT_SHIFT_CTLX_I2S_TRX_ACT) +#define BIT_GET_CTLX_I2S_TRX_ACT(x) (((x) >> BIT_SHIFT_CTLX_I2S_TRX_ACT) & BIT_MASK_CTLX_I2S_TRX_ACT) + +#define BIT_SHIFT_CTLX_I2S_CH_NUM 3 +#define BIT_MASK_CTLX_I2S_CH_NUM 0x3 +#define BIT_CTRL_CTLX_I2S_CH_NUM(x) (((x) & BIT_MASK_CTLX_I2S_CH_NUM) << BIT_SHIFT_CTLX_I2S_CH_NUM) +#define BIT_GET_CTLX_I2S_CH_NUM(x) (((x) >> BIT_SHIFT_CTLX_I2S_CH_NUM) & BIT_MASK_CTLX_I2S_CH_NUM) + +#define BIT_CTLX_I2S_WL BIT(6) +#define BIT_SHIFT_CTLX_I2S_WL 6 +#define BIT_MASK_CTLX_I2S_WL 0x1 +#define BIT_CTRL_CTLX_I2S_WL(x) (((x) & BIT_MASK_CTLX_I2S_WL) << BIT_SHIFT_CTLX_I2S_WL) + +#define BIT_CTLX_I2S_LRSWAP BIT(10) +#define BIT_SHIFT_CTLX_I2S_LRSWAP 10 +#define BIT_MASK_CTLX_I2S_LRSWAP 0x1 +#define BIT_CTRL_CTLX_I2S_LRSWAP(x) (((x) & BIT_MASK_CTLX_I2S_LRSWAP) << BIT_SHIFT_CTLX_I2S_LRSWAP) + +#define BIT_CTLX_I2S_SCK_INV BIT(11) +#define BIT_SHIFT_CTLX_I2S_SCK_INV 11 +#define BIT_MASK_CTLX_I2S_SCK_INV 0x1 +#define BIT_CTRL_CTLX_I2S_SCK_INV(x) (((x) & BIT_MASK_CTLX_I2S_SCK_INV) << BIT_SHIFT_CTLX_I2S_SCK_INV) + +#define BIT_CTLX_I2S_ENDIAN_SWAP BIT(12) +#define BIT_SHIFT_CTLX_I2S_ENDIAN_SWAP 12 +#define BIT_MASK_CTLX_I2S_ENDIAN_SWAP 0x1 +#define BIT_CTRL_CTLX_I2S_ENDIAN_SWAP(x) (((x) & BIT_MASK_CTLX_I2S_ENDIAN_SWAP) << BIT_SHIFT_CTLX_I2S_ENDIAN_SWAP) + +#define BIT_CTLX_I2S_SLAVE_MODE BIT(29) +#define BIT_SHIFT_CTLX_I2S_SLAVE_MODE 29 +#define BIT_MASK_CTLX_I2S_SLAVE_MODE 0x1 +#define BIT_CTRL_CTLX_I2S_SLAVE_MODE(x) (((x) & BIT_MASK_CTLX_I2S_SLAVE_MODE) << BIT_SHIFT_CTLX_I2S_SLAVE_MODE) + +#define BIT_CTLX_I2S_CLK_SRC BIT(30) +#define BIT_SHIFT_CTLX_I2S_CLK_SRC 30 +#define BIT_MASK_CTLX_I2S_CLK_SRC 0x1 +#define BIT_CTRL_CTLX_I2S_CLK_SRC(x) (((x) & BIT_MASK_CTLX_I2S_CLK_SRC) << BIT_SHIFT_CTLX_I2S_CLK_SRC) + +#define BIT_CTLX_I2S_SW_RSTN BIT(31) +#define BIT_SHIFT_CTLX_I2S_SW_RSTN 31 +#define BIT_MASK_CTLX_I2S_SW_RSTN 0x1 +#define BIT_CTRL_CTLX_I2S_SW_RSTN(x) (((x) & BIT_MASK_CTLX_I2S_SW_RSTN) << BIT_SHIFT_CTLX_I2S_SW_RSTN) + +// REG_I2S_SETTING +#define BIT_SHIFT_SETTING_I2S_PAGE_SZ 0 +#define BIT_MASK_SETTING_I2S_PAGE_SZ 0xFFF +#define BIT_CTRL_SETTING_I2S_PAGE_SZ(x) (((x) & BIT_MASK_SETTING_I2S_PAGE_SZ) << BIT_SHIFT_SETTING_I2S_PAGE_SZ) +#define BIT_GET_SETTING_I2S_PAGE_SZ(x) (((x) >> BIT_SHIFT_SETTING_I2S_PAGE_SZ) & BIT_MASK_SETTING_I2S_PAGE_SZ) + +#define BIT_SHIFT_SETTING_I2S_PAGE_NUM 12 +#define BIT_MASK_SETTING_I2S_PAGE_NUM 0x3 +#define BIT_CTRL_SETTING_I2S_PAGE_NUM(x) (((x) & BIT_MASK_SETTING_I2S_PAGE_NUM) << BIT_SHIFT_SETTING_I2S_PAGE_NUM) +#define BIT_GET_SETTING_I2S_PAGE_NUM(x) (((x) >> BIT_SHIFT_SETTING_I2S_PAGE_NUM) & BIT_MASK_SETTING_I2S_PAGE_NUM) + +#define BIT_SHIFT_SETTING_I2S_SAMPLE_RATE 14 +#define BIT_MASK_SETTING_I2S_SAMPLE_RATE 0x7 +#define BIT_CTRL_SETTING_I2S_SAMPLE_RATE(x) (((x) & BIT_MASK_SETTING_I2S_SAMPLE_RATE) << BIT_SHIFT_SETTING_I2S_SAMPLE_RATE) +#define BIT_GET_SETTING_I2S_SAMPLE_RATE(x) (((x) >> BIT_SHIFT_SETTING_I2S_SAMPLE_RATE) & BIT_MASK_SETTING_I2S_SAMPLE_RATE) + +// i2s trx page own bit +#define BIT_PAGE_I2S_OWN_BIT BIT(31) +#define BIT_SHIFT_PAGE_I2S_OWN_BIT 31 +#define BIT_MASK_PAGE_I2S_OWN_BIT 0x1 +#define BIT_CTRL_PAGE_I2S_OWN_BIT(x) (((x) & BIT_MASK_PAGE_I2S_OWN_BIT) << BIT_SHIFT_PAGE_I2S_OWN_BIT) + +//=============== Register Address Definition ==================== +#define REG_I2S_PAGE_OWN_OFF 0x004 + +#define REG_I2S_CTL 0x000 +#define REG_I2S_TX_PAGE_PTR 0x004 +#define REG_I2S_RX_PAGE_PTR 0x008 +#define REG_I2S_SETTING 0x00C + +#define REG_I2S_TX_MASK_INT 0x010 +#define REG_I2S_TX_STATUS_INT 0x014 +#define REG_I2S_RX_MASK_INT 0x018 +#define REG_I2S_RX_STATUS_INT 0x01c + + +#define REG_I2S_TX_PAGE0_OWN 0x020 +#define REG_I2S_TX_PAGE1_OWN 0x024 +#define REG_I2S_TX_PAGE2_OWN 0x028 +#define REG_I2S_TX_PAGE3_OWN 0x02C +#define REG_I2S_RX_PAGE0_OWN 0x030 +#define REG_I2S_RX_PAGE1_OWN 0x034 +#define REG_I2S_RX_PAGE2_OWN 0x038 +#define REG_I2S_RX_PAGE3_OWN 0x03C + +/*I2S Essential Functions and Macros*/ +VOID +HalI2SWrite32( + IN u8 I2SIdx, + IN u8 I2SReg, + IN u32 I2SVal +); + +u32 +HalI2SRead32( + IN u8 I2SIdx, + IN u8 I2SReg +); + +/* +#define HAL_I2SX_READ32(I2sIndex, addr) \ + HAL_READ32(I2S0_REG_BASE+ (I2sIndex*I2S1_REG_OFF), addr) +#define HAL_I2SX_WRITE32(I2sIndex, addr, value) \ + HAL_WRITE32((I2S0_REG_BASE+ (I2sIndex*I2S1_REG_OFF)), addr, value) +*/ + +#define HAL_I2S_WRITE32(I2SIdx, addr, value) HalI2SWrite32(I2SIdx,addr,value) +#define HAL_I2S_READ32(I2SIdx, addr) HalI2SRead32(I2SIdx,addr) + +/* I2S debug output*/ +#define I2S_PREFIX "RTL8195A[i2s]: " +#define I2S_PREFIX_LVL " [i2s_DBG]: " + +typedef enum _I2S_DBG_LVL_ { + HAL_I2S_LVL = 0x01, + SAL_I2S_LVL = 0x02, + VERI_I2S_LVL = 0x03, +}I2S_DBG_LVL,*PI2S_DBG_LVL; + +#ifdef CONFIG_DEBUG_LOG +#ifdef CONFIG_DEBUG_LOG_I2S_HAL + + #define DBG_8195A_I2S(...) do{ \ + _DbgDump("\r"I2S_PREFIX __VA_ARGS__);\ + }while(0) + + + #define I2SDBGLVL 0xFF + #define DBG_8195A_I2S_LVL(LVL,...) do{\ + if (LVL&I2SDBGLVL){\ + _DbgDump("\r"I2S_PREFIX_LVL __VA_ARGS__);\ + }\ + }while(0) +#else + #define DBG_I2S_LOG_PERD 100 + #define DBG_8195A_I2S(...) + #define DBG_8195A_I2S_LVL(...) +#endif +#else + #define DBG_I2S_LOG_PERD 100 + #define DBG_8195A_I2S(...) + #define DBG_8195A_I2S_LVL(...) +#endif + +/* +#define REG_I2S_PAGE_OWN_OFF 0x004 +#define REG_I2S_CTL 0x000 +#define REG_I2S_TX_PAGE_PTR 0x004 +#define REG_I2S_RX_PAGE_PTR 0x008 +#define REG_I2S_SETTING 0x00C + +#define REG_I2S_TX_MASK_INT 0x010 +#define REG_I2S_TX_STATUS_INT 0x014 +#define REG_I2S_RX_MASK_INT 0x018 +#define REG_I2S_RX_STATUS_INT 0x01c + + + +#define REG_I2S_TX_PAGE0_OWN 0x020 +#define REG_I2S_TX_PAGE1_OWN 0x024 +#define REG_I2S_TX_PAGE2_OWN 0x028 +#define REG_I2S_TX_PAGE3_OWN 0x02C +#define REG_I2S_RX_PAGE0_OWN 0x030 +#define REG_I2S_RX_PAGE1_OWN 0x034 +#define REG_I2S_RX_PAGE2_OWN 0x038 +#define REG_I2S_RX_PAGE3_OWN 0x03C +*/ +/* template +#define BIT_SHIFT_CTLX_ 7 +#define BIT_MASK_CTLX_ 0x1 +#define BIT_CTLX_(x) (((x) & BIT_MASK_CTLX_) << BIT_SHIFT_CTLX_) +#define BIT_INV_CTLX_ (~(BIT_MASK_CTLX_ << BIT_SHIFT_CTLX_)) +*//* +#define BIT_SHIFT_CTLX_IIS_EN 0 +#define BIT_MASK_CTLX_IIS_EN 0x1 +#define BIT_CTLX_IIS_EN(x) (((x) & BIT_MASK_CTLX_IIS_EN) << BIT_SHIFT_CTLX_IIS_EN) +#define BIT_INV_CTLX_IIS_EN (~(BIT_MASK_CTLX_IIS_EN << BIT_SHIFT_CTLX_IIS_EN)) + +#define BIT_SHIFT_CTLX_TRX 1 +#define BIT_MASK_CTLX_TRX 0x3 +#define BIT_CTLX_TRX(x) (((x) & BIT_MASK_CTLX_TRX) << BIT_SHIFT_CTLX_TRX) +#define BIT_INV_CTLX_TRX (~(BIT_MASK_CTLX_TRX << BIT_SHIFT_CTLX_TRX)) + +#define BIT_SHIFT_CTLX_CH_NUM 3 +#define BIT_MASK_CTLX_CH_NUM 0x3 +#define BIT_CTLX_CH_NUM(x) (((x) & BIT_MASK_CTLX_CH_NUM) << BIT_SHIFT_CTLX_CH_NUM) +#define BIT_INV_CTLX_CH_NUM (~(BIT_MASK_CTLX_CH_NUM << BIT_SHIFT_CTLX_CH_NUM)) + +#define BIT_SHIFT_CTLX_EDGE_SW 5 +#define BIT_MASK_CTLX_EDGE_SW 0x1 +#define BIT_CTLX_EDGE_SW(x) (((x) & BIT_MASK_CTLX_EDGE_SW) << BIT_SHIFT_CTLX_EDGE_SW) +#define BIT_INV_CTLX_EDGE_SW (~(BIT_MASK_CTLX_EDGE_SW << BIT_SHIFT_CTLX_EDGE_SW)) + +#define BIT_SHIFT_CTLX_WL 6 +#define BIT_MASK_CTLX_WL 0x1 +#define BIT_CTLX_WL(x) (((x) & BIT_MASK_CTLX_WL) << BIT_SHIFT_CTLX_WL) +#define BIT_INV_CTLX_WL (~(BIT_MASK_CTLX_WL << BIT_SHIFT_CTLX_WL)) + +#define BIT_SHIFT_CTLX_LOOP_BACK 7 +#define BIT_MASK_CTLX_LOOP_BACK 0x1 +#define BIT_CTLX_LOOP_BACK(x) (((x) & BIT_MASK_CTLX_LOOP_BACK) << BIT_SHIFT_CTLX_LOOP_BACK) +#define BIT_INV_CTLX_LOOP_BACK (~(BIT_MASK_CTLX_LOOP_BACK << BIT_SHIFT_CTLX_LOOP_BACK)) + + +#define BIT_SHIFT_CTLX_FORMAT 8 +#define BIT_MASK_CTLX_FORMAT 0x3 +#define BIT_CTLX_FORMAT(x) (((x) & BIT_MASK_CTLX_FORMAT) << BIT_SHIFT_CTLX_FORMAT) +#define BIT_INV_CTLX_FORMAT (~(BIT_MASK_CTLX_FORMAT << BIT_SHIFT_CTLX_FORMAT)) + +#define BIT_SHIFT_CTLX_LRSWAP 10 +#define BIT_MASK_CTLX_LRSWAP 0x1 +#define BIT_CTLX_LRSWAP(x) (((x) & BIT_MASK_CTLX_LRSWAP) << BIT_SHIFT_CTLX_LRSWAP) +#define BIT_INV_CTLX_LRSWAP (~(BIT_MASK_CTLX_LRSWAP << BIT_SHIFT_CTLX_LRSWAP)) + +#define BIT_SHIFT_CTLX_SCK_INV 11 +#define BIT_MASK_CTLX_SCK_INV 0x1 +#define BIT_CTLX_SCK_INV(x) (((x) & BIT_MASK_CTLX_SCK_INV) << BIT_SHIFT_CTLX_SCK_INV) +#define BIT_INV_CTLX_SCK_INV (~(BIT_MASK_CTLX_SCK_INV << BIT_SHIFT_CTLX_SCK_INV)) + +#define BIT_SHIFT_CTLX_ENDIAN_SWAP 12 +#define BIT_MASK_CTLX_ENDIAN_SWAP 0x1 +#define BIT_CTLX_ENDIAN_SWAP(x) (((x) & BIT_MASK_CTLX_ENDIAN_SWAP) << BIT_SHIFT_CTLX_ENDIAN_SWAP) +#define BIT_INV_CTLX_ENDIAN_SWAP (~(BIT_MASK_CTLX_ENDIAN_SWAP << BIT_SHIFT_CTLX_ENDIAN_SWAP)) + + +#define BIT_SHIFT_CTLX_DEBUG_SWITCH 15 +#define BIT_MASK_CTLX_DEBUG_SWITCH 0x3 +#define BIT_CTLX_DEBUG_SWITCH(x) (((x) & BIT_MASK_CTLX_DEBUG_SWITCH) << BIT_SHIFT_CTLX_DEBUG_SWITCH) +#define BIT_INV_CTLX_DEBUG_SWITCH (~(BIT_MASK_CTLX_DEBUG_SWITCH << BIT_SHIFT_CTLX_DEBUG_SWITCH)) + +#define BIT_SHIFT_CTLX_SLAVE_SEL 29 +#define BIT_MASK_CTLX_SLAVE_SEL 0x1 +#define BIT_CTLX_SLAVE_SEL(x) (((x) & BIT_MASK_CTLX_SLAVE_SEL) << BIT_SHIFT_CTLX_SLAVE_SEL) +#define BIT_INV_CTLX_SLAVE_SEL (~(BIT_MASK_CTLX_SLAVE_SEL << BIT_SHIFT_CTLX_SLAVE_SEL)) + + +#define BIT_SHIFT_CTLX_CLK_SRC 30 +#define BIT_MASK_CTLX_CLK_SRC 0x1 +#define BIT_CTLX_CLK_SRC(x) (((x) & BIT_MASK_CTLX_CLK_SRC) << BIT_SHIFT_CTLX_CLK_SRC) +#define BIT_INV_CTLX_CLK_SRC (~(BIT_MASK_CTLX_CLK_SRC << BIT_SHIFT_CTLX_CLK_SRC)) + + + +#define BIT_SHIFT_CTLX_SW_RSTN 31 +#define BIT_MASK_CTLX_SW_RSTN 0x1 +#define BIT_CTLX_SW_RSTN(x) (((x) & BIT_MASK_CTLX_SW_RSTN) << BIT_SHIFT_CTLX_SW_RSTN) +#define BIT_INV_CTLX_SW_RSTN (~(BIT_MASK_CTLX_SW_RSTN << BIT_SHIFT_CTLX_SW_RSTN)) + + +#define BIT_SHIFT_SETTING_PAGE_SZ 0 +#define BIT_MASK_SETTING_PAGE_SZ 0xFFF +#define BIT_SETTING_PAGE_SZ(x) (((x) & BIT_MASK_SETTING_PAGE_SZ) << BIT_SHIFT_SETTING_PAGE_SZ) +#define BIT_INV_SETTING_PAGE_SZ (~(BIT_MASK_SETTING_PAGE_SZ << BIT_SHIFT_SETTING_PAGE_SZ)) + +#define BIT_SHIFT_SETTING_PAGE_NUM 12 +#define BIT_MASK_SETTING_PAGE_NUM 0x3 +#define BIT_SETTING_PAGE_NUM(x) (((x) & BIT_MASK_SETTING_PAGE_NUM) << BIT_SHIFT_SETTING_PAGE_NUM) +#define BIT_INV_SETTING_PAGE_NUM (~(BIT_MASK_SETTING_PAGE_NUM << BIT_SHIFT_SETTING_PAGE_NUM)) + +#define BIT_SHIFT_SETTING_SAMPLE_RATE 14 +#define BIT_MASK_SETTING_SAMPLE_RATE 0x7 +#define BIT_SETTING_SAMPLE_RATE(x) (((x) & BIT_MASK_SETTING_SAMPLE_RATE) << BIT_SHIFT_SETTING_SAMPLE_RATE) +#define BIT_INV_SETTING_SAMPLE_RATE (~(BIT_MASK_SETTING_SAMPLE_RATE << BIT_SHIFT_SETTING_SAMPLE_RATE)) +*/ + +typedef enum _I2S_CTL_FORMAT { + FormatI2s = 0x00, + FormatLeftJustified = 0x01, + FormatRightJustified = 0x02 +}I2S_CTL_FORMAT, *PI2S_CTL_FORMAT; + +typedef enum _I2S_CTL_CHNUM { + ChannelStereo = 0x00, + Channel5p1 = 0x01, + ChannelMono = 0x02 +}I2S_CTL_CHNUM, *PI2S_CTL_CHNUM; + +typedef enum _I2S_CTL_TRX_ACT { + RxOnly = 0x00, + TxOnly = 0x01, + TXRX = 0x02 +}I2S_CTL_TRX_ACT, *PI2S_CTL_TRX_ACT; +/* +typedef struct _I2S_CTL_REG_ { + I2S_CTL_FORMAT Format; + I2S_CTL_CHNUM ChNum; + I2S_CTL_TRX_ACT TrxAct; + + u32 I2s_En :1; // Bit 0 + u32 Rsvd1to4 :4; // Bit 1-4 is TrxAct, ChNum + u32 EdgeSw :1; // Bit 5 Edge switch + u32 WordLength :1; // Bit 6 + u32 LoopBack :1; // Bit 7 + u32 Rsvd8to9 :2; // Bit 8-9 is Format + u32 DacLrSwap :1; // Bit 10 + u32 SckInv :1; // Bit 11 + u32 EndianSwap :1; // Bit 12 + u32 Rsvd13to14 :2; // Bit 11-14 + u32 DebugSwitch :2; // Bit 15-16 + u32 Rsvd17to28 :12; // Bit 17-28 + u32 SlaveMode :1; // Bit 29 + u32 SR44p1KHz :1; // Bit 30 + u32 SwRstn :1; // Bit 31 +} I2S_CTL_REG, *PI2S_CTL_REG; +*/ +typedef enum _I2S_SETTING_PAGE_NUM { + I2s1Page = 0x00, + I2s2Page = 0x01, + I2s3Page = 0x02, + I2s4Page = 0x03 +}I2S_SETTING_PAGE_NUM, *PI2S_SETTING_PAGE_NUM; + +//sampling rate +typedef enum _I2S_SETTING_SR { + I2sSR8K = 0x00, + I2sSR16K = 0x01, + I2sSR24K = 0x02, + I2sSR32K = 0x03, + I2sSR48K = 0x05, + I2sSR44p1K = 0x15, + I2sSR96K = 0x06, + I2sSR88p2K = 0x16 +}I2S_SETTING_SR, *PI2S_SETTING_SR; +/* +typedef struct _I2S_SETTING_REG_ { + I2S_SETTING_PAGE_NUM PageNum; + I2S_SETTING_SR SampleRate; + + u32 PageSize:12; // Bit 0-11 +}I2S_SETTING_REG, *PI2S_SETTING_REG; + +typedef enum _I2S_TX_ISR { + I2sTxP0OK = 0x01, + I2sTxP1OK = 0x02, + I2sTxP2OK = 0x04, + I2sTxP3OK = 0x08, + I2sTxPageUn = 0x10, + I2sTxFifoEmpty = 0x20 +}I2S_TX_ISR, *PI2S_TX_ISR; + +typedef enum _I2S_RX_ISR { + I2sRxP0OK = 0x01, + I2sRxP1OK = 0x02, + I2sRxP2OK = 0x04, + I2sRxP3OK = 0x08, + I2sRxPageUn = 0x10, + I2sRxFifoFull = 0x20 +}I2S_RX_ISR, *PI2S_RX_ISR; +*/ + +/* Hal I2S function prototype*/ +RTK_STATUS +HalI2SInitRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SInitRtl8195a_Patch( + IN VOID *Data +); + +RTK_STATUS +HalI2SDeInitRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2STxRtl8195a( + IN VOID *Data, + IN u8 *pBuff +); + +RTK_STATUS +HalI2SRxRtl8195a( + IN VOID *Data, + OUT u8 *pBuff +); + +RTK_STATUS +HalI2SEnableRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SIntrCtrlRtl8195a( + IN VOID *Data +); + +u32 +HalI2SReadRegRtl8195a( + IN VOID *Data, + IN u8 I2SReg +); + +RTK_STATUS +HalI2SSetRateRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetWordLenRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetChNumRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetPageNumRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetPageSizeRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetDirectionRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SSetDMABufRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SClrIntrRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SClrAllIntrRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SDMACtrlRtl8195a( + IN VOID *Data +); + +u8 +HalI2SGetTxPageRtl8195a( + IN VOID *Data +); + +u8 +HalI2SGetRxPageRtl8195a( + IN VOID *Data +); + +RTK_STATUS +HalI2SPageSendRtl8195a( + IN VOID *Data, + IN u8 PageIdx +); + +#if 0 +RTK_STATUS +HalI2SPageRecvRtl8195a( + IN VOID *Data, + IN u8 PageIdx +); +#else +RTK_STATUS +HalI2SPageRecvRtl8195a( + IN VOID *Data +); +#endif + +RTK_STATUS +HalI2SClearAllOwnBitRtl8195a( + IN VOID *Data +); + + +// HAL functions Wrapper +static __inline VOID +HalI2SSetRate( + IN VOID *Data +) +{ + HalI2SSetRateRtl8195a(Data); +} + +static __inline VOID +HalI2SSetWordLen( + IN VOID *Data +) +{ + HalI2SSetWordLenRtl8195a(Data); +} + +static __inline VOID +HalI2SSetChNum( + IN VOID *Data +) +{ + HalI2SSetChNumRtl8195a(Data); +} + +static __inline VOID +HalI2SSetPageNum( + IN VOID *Data +) +{ + HalI2SSetPageNumRtl8195a(Data); +} + +static __inline VOID +HalI2SSetPageSize( + IN VOID *Data +) +{ + HalI2SSetPageSizeRtl8195a(Data); +} + +static __inline VOID +HalI2SSetDirection( + IN VOID *Data +) +{ + HalI2SSetDirectionRtl8195a(Data); +} + +static __inline VOID +HalI2SSetDMABuf( + IN VOID *Data +) +{ + HalI2SSetDMABufRtl8195a(Data); +} + +static __inline u8 +HalI2SGetTxPage( + IN VOID *Data +) +{ + return HalI2SGetTxPageRtl8195a(Data); +} + +static __inline u8 +HalI2SGetRxPage( + IN VOID *Data +) +{ + return HalI2SGetRxPageRtl8195a(Data); +} + +static __inline VOID +HalI2SPageSend( + IN VOID *Data, + IN u8 PageIdx +) +{ + HalI2SPageSendRtl8195a(Data, PageIdx); +} + +#if 0 +static __inline VOID +HalI2SPageRecv( + IN VOID *Data, + IN u8 PageIdx +) +{ + HalI2SPageRecvRtl8195a(Data, PageIdx); +} +#else +static __inline VOID +HalI2SPageRecv( + IN VOID *Data +) +{ + HalI2SPageRecvRtl8195a(Data); +} +#endif + +static __inline VOID +HalI2SClearAllOwnBit( + IN VOID *Data +) +{ + HalI2SClearAllOwnBitRtl8195a(Data); +} + +#endif /* _RTL8195A_I2S_H_ */ + + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_mii.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_mii.h new file mode 100644 index 0000000..56ebbdc --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_mii.h @@ -0,0 +1,674 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_MII_H_ +#define _RTL8195A_MII_H_ + +#include "basic_types.h" +#include "hal_api.h" + + +#define ETHERNET_REG_BASE 0x40050000 +#define ETHERNET_MODULE_BASE ETHERNET_REG_BASE + 0x0000 +#define CPU_INTERFACE_BASE ETHERNET_REG_BASE + 0x1300 + +/* Ethernet Module registers */ +#define REG_RTL_MII_IDR0 0x0000 // Table 2 IDR0 (Offset 0000h-0003h, R/W) +#define REG_RTL_MII_IDR4 0x0004 // Table 3 IDR4 (Offset 0004h-0007h, R/W) +#define REG_RTL_MII_MAR0 0x0008 // Table 4 MAR0 (Offset 0008h-000bh, R/W) +#define REG_RTL_MII_MAR4 0x000C // Table 5 MAR4 (Offset 000ch-000fh, R/W) +#define REG_RTL_MII_CR 0x0038 // Table 21 Command Register (COM_REG, Offset 0038-003Bh, R/W) +#define REG_RTL_MII_IMRISR 0x003C // Table 22 + Table 23 +#define REG_RTL_MII_TCR 0x0040 // Table 24 Transmit Configuration Register (TC_REG, Offset 0040h-0043h, R/W) +#define REG_RTL_MII_RCR 0x0044 // Table 25 Receive Configuration Register (RC_REG, Offset 0044h-0047h, R/W) +#define REG_RTL_MII_CTCR 0x0048 // Table 26 CPU Tag Control Register (CPUTAG_REG, Offset 0048h-004bh, R/W) +#define REG_RTL_MII_CONFIG 0x004C // Table 27 Configuration Register (CONFIG_REG, Offset 004ch-004fh, R/W) +#define REG_RTL_MII_CTCR1 0x0050 // Table 28 CPUTAG1 Register (CPUTAG1_REG, Offset 0050h-0053h, R/W) +#define REG_RTL_MII_MSR 0x0058 // Table 29 Media Status Register (MS_reg: Offset 0058h – 005bh, R/W) +#define REG_RTL_MII_MIIAR 0x005C // Table 30 MII Access Register (MIIA_REG, Offset 005c-005fh, R/W) +#define REG_RTL_MII_VR 0x0064 // Table 32 VLAN Register (VLAN_REG, Offset 0064-0067h, R/W) +#define REG_RTL_MII_IMR0 0x00D0 // Table 50 IMR0_REG (IMR0_REG, Offset D0h-D3h) +#define REG_RTL_MII_IMR1 0x00D4 // Table 51 IMR1_REG (IMR1_REG, Offset d4h-d7h) +#define REG_RTL_MII_ISR1 0x00D8 // Table 52 ISR1 Register (ISR1_REG, Offset D8h-DBh) +#define REG_RTL_MII_INTR 0x00DC // Table 53 Interrupt routing register (INTR_REG, Offset DCh-DFh) +#define REG_RTL_MII_CCR 0x00E4 // Table xx Clock Control Register (CLKCTL_REG, Offset E4h-E7h) + +/* CPU Interface registers */ +#define REG_RTL_MII_TXFDP1 0x1300 // Table 55 TxFDP1 register (TXFDP1_REG, offset 1300h-1303h) +#define REG_RTL_MII_TXCDO1 0x1304 // Table 56 TxCDO1 register (TXCDO1_REG, offset 1304h-1305h) +#define REG_RTL_MII_TXFDP2 0x1310 // Table 57 TxFDP2 register (TXFDP2_REG, offset 1310h-1313h) +#define REG_RTL_MII_TXCDO2 0x1314 // Table 58 TxCDO2 register (TXCDO2_REG, offset 1314h-1315h) +#define REG_RTL_MII_TXFDP3 0x1320 // Table 59 TxFDP3 register (TXFDP3_REG, offset 1320h-1323h) +#define REG_RTL_MII_TXCDO3 0x1324 // Table 60 TxCDO3 register (TXCDO3_REG, offset 1324h-1325h) +#define REG_RTL_MII_TXFDP4 0x1330 // Table 61 TxFDP4 register (TXFDP4_REG, offset 1330h-1333h) +#define REG_RTL_MII_TXCDO4 0x1334 // Table 62 TxCDO4 register (TXCDO4_REG, offset 1334h-1335h) +#define REG_RTL_MII_TXFDP5 0x1340 // Table 63 TxFDP5 register (TXFDP5_REG, offset 1340h-1343h) +#define REG_RTL_MII_TXCDO5 0x1344 // Table 64 TxCDO5 register (TXCDO5_REG, offset 1344h-1345h) +#define REG_RTL_MII_RXFDP2 0x1390 // Table 66 RxFDP2 register (RXFDP#_REG, offset 1390h-1393h) +#define REG_RTL_MII_RXFDP1 0x13F0 // Table 71 RxFDP1 register (RXFDP1_REG, offset 13F0h-13F3h) +#define REG_RTL_MII_RXRS1 0x13F6 // Table 73 Rx Ring Size1 register (RX_RS1_REG, offset 13F6h-13F7h) + +#define REG_RTL_MII_RX_PSE1 0x142C // Table 77 Rx_Pse_Des_Thres_1_h (RX_PSE1_REG, Offset 142ch) +#define REG_RTL_MII_ETNRXCPU1 0x1430 // Table 79 EhtrntRxCPU_Des_Num1 (ETNRXCPU1_REG, Offset 1430h-1433h) +#define REG_RTL_MII_IOCMD 0x1434 // Table 80 Ethernet_IO_CMD (ETN_IO_CMD_REG, Offset 1434h-1437h) +#define REG_RTL_MII_IOCMD1 0x1438 // Table 81 Ethernet_IO_CMD1 (IO_CMD1_REG: Offset 1438h-143bh) + + +#define HAL_MII_READ32(addr) \ + HAL_READ32(ETHERNET_REG_BASE, addr) +#define HAL_MII_WRITE32(addr, value) \ + HAL_WRITE32(ETHERNET_REG_BASE, addr, value) +#define HAL_MII_READ16(addr) \ + HAL_READ16(ETHERNET_REG_BASE, addr) +#define HAL_MII_WRITE16(addr, value) \ + HAL_WRITE16(ETHERNET_REG_BASE, addr, value) +#define HAL_MII_READ8(addr) \ + HAL_READ8(ETHERNET_REG_BASE, addr) +#define HAL_MII_WRITE8(addr, value) \ + HAL_WRITE8(ETHERNET_REG_BASE, addr, value) + +#define CMD_CONFIG 0x00081000 + +//2014-04-29 yclin (disable [27] r_en_precise_dma) +// #define CMD1_CONFIG 0x39000000 +#define CMD1_CONFIG 0x31000000 + +// #define MAX_RX_DESC_SIZE 6 +#define MAX_RX_DESC_SIZE 1 +#define MAX_TX_DESC_SIZE 5 + +// 0058h +#define BIT_SHIFT_MSR_FORCE_SPEED_SELECT 16 +#define BIT_MASK_MSR_FORCE_SPEED_SELECT 0x3 +#define BIT_MSR_FORCE_SPEED_SELECT(x)(((x) & BIT_MASK_MSR_FORCE_SPEED_SELECT) << BIT_SHIFT_MSR_FORCE_SPEED_SELECT) +#define BIT_INVC_MSR_FORCE_SPEED_SELECT (~(BIT_MASK_MSR_FORCE_SPEED_SELECT << BIT_SHIFT_MSR_FORCE_SPEED_SELECT)) + +#define BIT_SHIFT_MSR_FORCE_SPEED_MODE_ENABLE 10 +#define BIT_MASK_MSR_FORCE_SPEED_MODE_ENABLE 0x1 +#define BIT_MSR_FORCE_SPEED_MODE_ENABLE(x)(((x) & BIT_MASK_MSR_FORCE_SPEED_MODE_ENABLE) << BIT_SHIFT_MSR_FORCE_SPEED_MODE_ENABLE) +#define BIT_INVC_MSR_FORCE_SPEED_MODE_ENABLE (~(BIT_MASK_MSR_FORCE_SPEED_MODE_ENABLE << BIT_SHIFT_MSR_FORCE_SPEED_MODE_ENABLE)) + +// 1434h +#define BIT_SHIFT_IOCMD_RXENABLE 5 +#define BIT_MASK_IOCMD_RXENABLE 0x1 +#define BIT_IOCMD_RXENABLE(x)(((x) & BIT_MASK_IOCMD_RXENABLE) << BIT_SHIFT_IOCMD_RXENABLE) +#define BIT_INVC_IOCMD_RXENABLE (~(BIT_MASK_IOCMD_RXENABLE << BIT_SHIFT_IOCMD_RXENABLE)) + +#define BIT_SHIFT_IOCMD_TXENABLE 4 +#define BIT_MASK_IOCMD_TXENABLE 0x1 +#define BIT_IOCMD_TXENABLE(x)(((x) & BIT_MASK_IOCMD_TXENABLE) << BIT_SHIFT_IOCMD_TXENABLE) +#define BIT_INVC_IOCMD_TXENABLE (~(BIT_MASK_IOCMD_TXENABLE << BIT_SHIFT_IOCMD_TXENABLE)) + +#define BIT_SHIFT_IOCMD_FIRST_DMATX_ENABLE 0 +#define BIT_MASK_IOCMD_FIRST_DMATX_ENABLE 0x1 +#define BIT_IOCMD_FIRST_DMATX_ENABLE(x)(((x) & BIT_MASK_IOCMD_FIRST_DMATX_ENABLE) << BIT_SHIFT_IOCMD_FIRST_DMATX_ENABLE) +#define BIT_INVC_IOCMD_FIRST_DMATX_ENABLE (~(BIT_MASK_IOCMD_FIRST_DMATX_ENABLE << BIT_SHIFT_IOCMD_FIRST_DMATX_ENABLE)) + +// 1438h +#define BIT_SHIFT_IOCMD1_FIRST_DMARX_ENABLE 16 +#define BIT_MASK_IOCMD1_FIRST_DMARX_ENABLE 0x1 +#define BIT_IOCMD1_FIRST_DMARX_ENABLE(x)(((x) & BIT_MASK_IOCMD1_FIRST_DMARX_ENABLE) << BIT_SHIFT_IOCMD1_FIRST_DMARX_ENABLE) +#define BIT_INVC_IOCMD1_FIRST_DMARX_ENABLE (~(BIT_MASK_IOCMD1_FIRST_DMARX_ENABLE << BIT_SHIFT_IOCMD1_FIRST_DMARX_ENABLE)) + + +/** + * 1.4.1.7 Tx command descriptor used in RL6266 + * 5 dobule words + */ +typedef struct _TX_INFO_ { + union { + struct { + u32 own:1; //31 + u32 eor:1; //30 + u32 fs:1; //29 + u32 ls:1; //28 + u32 ipcs:1; //27 + u32 l4cs:1; //26 + u32 keep:1; //25 + u32 blu:1; //24 + u32 crc:1; //23 + u32 vsel:1; //22 + u32 dislrn:1; //21 + u32 cputag_ipcs:1; //20 + u32 cputag_l4cs:1; //19 + u32 cputag_psel:1; //18 + u32 rsvd:1; //17 + u32 data_length:17; //0~16 + } bit; + u32 dw; //double word + } opts1; + + u32 addr; + + union { + struct { + u32 cputag:1; //31 + u32 aspri:1; //30 + u32 cputag_pri:3; //27~29 + u32 tx_vlan_action:2; //25~26 + u32 tx_pppoe_action:2; //23~24 + u32 tx_pppoe_idx:3; //20~22 + u32 efid:1; //19 + u32 enhance_fid:3; //16~18 + u32 vidl:8; // 8~15 + u32 prio:3; // 5~7 + u32 cfi:1; // 4 + u32 vidh:4; // 0~3 + } bit; + u32 dw; //double word + } opts2; + + union { + struct { + u32 extspa:3; //29~31 + u32 tx_portmask:6; //23~28 + u32 tx_dst_stream_id:7; //16~22 + u32 rsvd:14; // 2~15 + u32 l34keep:1; // 1 + u32 ptp:1; // 0 + } bit; + u32 dw; //double word + } opts3; + + union { + struct { + u32 lgsen:1; //31 + u32 lgmss:11; //20~30 + u32 rsvd:20; // 0~19 + } bit; + u32 dw; //double word + } opts4; + +} TX_INFO, *PTX_INFO; + +typedef struct _RX_INFO_ { + union{ + struct{ + u32 own:1; //31 + u32 eor:1; //30 + u32 fs:1; //29 + u32 ls:1; //28 + u32 crcerr:1; //27 + u32 ipv4csf:1; //26 + u32 l4csf:1; //25 + u32 rcdf:1; //24 + u32 ipfrag:1; //23 + u32 pppoetag:1; //22 + u32 rwt:1; //21 + u32 pkttype:4; //20-17 + u32 l3routing:1; //16 + u32 origformat:1; //15 + u32 pctrl:1; //14 +#ifdef CONFIG_RG_JUMBO_FRAME + u32 data_length:14; //13~0 +#else + u32 rsvd:2; //13~12 + u32 data_length:12; //11~0 +#endif + }bit; + u32 dw; //double word + }opts1; + + u32 addr; + + union{ + struct{ + u32 cputag:1; //31 + u32 ptp_in_cpu_tag_exist:1; //30 + u32 svlan_tag_exist:1; //29 + u32 rsvd_2:2; //27~28 + u32 pon_stream_id:7; //20~26 + u32 rsvd_1:3; //17~19 + u32 ctagva:1; //16 + u32 cvlan_tag:16; //15~0 + }bit; + u32 dw; //double word + }opts2; + + union{ + struct{ + u32 src_port_num:5; //27~31 + u32 dst_port_mask:6; //21~26 + u32 reason:8; //13~20 + u32 internal_priority:3; //10~12 + u32 ext_port_ttl_1:5; //5~9 + u32 rsvd:5; //4~0 + }bit; + u32 dw; //double word + }opts3; +} RX_INFO, *PRX_INFO; + +/** + * GMAC_STATUS_REGS + */ +// TX/RX Descriptor Common +#define BIT_SHIFT_GMAC_DESCOWN 31 +#define BIT_MASK_GMAC_DESCOWN 0x1 +#define BIT_GMAC_DESCOWN(x)(((x) & BIT_MASK_GMAC_DESCOWN) << BIT_SHIFT_GMAC_DESCOWN) +#define BIT_INVC_GMAC_DESCOWN (~(BIT_MASK_GMAC_DESCOWN << BIT_SHIFT_GMAC_DESCOWN)) + +#define BIT_SHIFT_GMAC_RINGEND 30 +#define BIT_MASK_GMAC_RINGEND 0x1 +#define BIT_GMAC_RINGEND(x)(((x) & BIT_MASK_GMAC_RINGEND) << BIT_SHIFT_GMAC_RINGEND) +#define BIT_INVC_GMAC_RINGEND (~(BIT_MASK_GMAC_RINGEND << BIT_SHIFT_GMAC_RINGEND)) + +#define BIT_SHIFT_GMAC_FIRSTFRAG 29 +#define BIT_MASK_GMAC_FIRSTFRAG 0x1 +#define BIT_GMAC_FIRSTFRAG(x)(((x) & BIT_MASK_GMAC_FIRSTFRAG) << BIT_SHIFT_GMAC_FIRSTFRAG) +#define BIT_INVC_GMAC_FIRSTFRAG (~(BIT_MASK_GMAC_FIRSTFRAG << BIT_SHIFT_GMAC_FIRSTFRAG)) + +#define BIT_SHIFT_GMAC_LASTFRAG 28 +#define BIT_MASK_GMAC_LASTFRAG 0x1 +#define BIT_GMAC_LASTFRAG(x)(((x) & BIT_MASK_GMAC_LASTFRAG) << BIT_SHIFT_GMAC_LASTFRAG) +#define BIT_INVC_GMAC_LASTFRAG (~(BIT_MASK_GMAC_LASTFRAG << BIT_SHIFT_GMAC_LASTFRAG)) + +// TX Descriptor opts1 +#define BIT_SHIFT_GMAC_IPCS 27 +#define BIT_MASK_GMAC_IPCS 0x1 +#define BIT_GMAC_IPCS(x)(((x) & BIT_MASK_GMAC_IPCS) << BIT_SHIFT_GMAC_IPCS) +#define BIT_INVC_GMAC_IPCS (~(BIT_MASK_GMAC_IPCS << BIT_SHIFT_GMAC_IPCS)) + +#define BIT_SHIFT_GMAC_L4CS 26 +#define BIT_MASK_GMAC_L4CS 0x1 +#define BIT_GMAC_L4CS(x)(((x) & BIT_MASK_GMAC_L4CS) << BIT_SHIFT_GMAC_L4CS) +#define BIT_INVC_GMAC_L4CS (~(BIT_MASK_GMAC_L4CS << BIT_SHIFT_GMAC_L4CS)) + +#define BIT_SHIFT_GMAC_KEEP 25 +#define BIT_MASK_GMAC_KEEP 0x1 +#define BIT_GMAC_KEEP(x)(((x) & BIT_MASK_GMAC_KEEP) << BIT_SHIFT_GMAC_KEEP) +#define BIT_INVC_GMAC_KEEP (~(BIT_MASK_GMAC_KEEP << BIT_SHIFT_GMAC_KEEP)) + +#define BIT_SHIFT_GMAC_BLU 24 +#define BIT_MASK_GMAC_BLU 0x1 +#define BIT_GMAC_BLU(x)(((x) & BIT_MASK_GMAC_BLU) << BIT_SHIFT_GMAC_BLU) +#define BIT_INVC_GMAC_BLU (~(BIT_MASK_GMAC_BLU << BIT_SHIFT_GMAC_BLU)) + +#define BIT_SHIFT_GMAC_TXCRC 23 +#define BIT_MASK_GMAC_TXCRC 0x1 +#define BIT_GMAC_TXCRC(x)(((x) & BIT_MASK_GMAC_TXCRC) << BIT_SHIFT_GMAC_TXCRC) +#define BIT_INVC_GMAC_TXCRC (~(BIT_MASK_GMAC_TXCRC << BIT_SHIFT_GMAC_TXCRC)) + +#define BIT_SHIFT_GMAC_VSEL 22 +#define BIT_MASK_GMAC_VSEL 0x1 +#define BIT_GMAC_VSEL(x)(((x) & BIT_MASK_GMAC_VSEL) << BIT_SHIFT_GMAC_VSEL) +#define BIT_INVC_GMAC_VSEL (~(BIT_MASK_GMAC_VSEL << BIT_SHIFT_GMAC_VSEL)) + +#define BIT_SHIFT_GMAC_DISLRN 21 +#define BIT_MASK_GMAC_DISLRN 0x1 +#define BIT_GMAC_DISLRN(x)(((x) & BIT_MASK_GMAC_DISLRN) << BIT_SHIFT_GMAC_DISLRN) +#define BIT_INVC_GMAC_DISLRN (~(BIT_MASK_GMAC_DISLRN << BIT_SHIFT_GMAC_DISLRN)) + +#define BIT_SHIFT_GMAC_CPUTAG_IPCS 20 +#define BIT_MASK_GMAC_CPUTAG_IPCS 0x1 +#define BIT_GMAC_CPUTAG_IPCS(x)(((x) & BIT_MASK_GMAC_CPUTAG_IPCS) << BIT_SHIFT_GMAC_CPUTAG_IPCS) +#define BIT_INVC_GMAC_CPUTAG_IPCS (~(BIT_MASK_GMAC_CPUTAG_IPCS << BIT_SHIFT_GMAC_CPUTAG_IPCS)) + +#define BIT_SHIFT_GMAC_CPUTAG_L4CS 19 +#define BIT_MASK_GMAC_CPUTAG_L4CS 0x1 +#define BIT_GMAC_CPUTAG_L4CS(x)(((x) & BIT_MASK_GMAC_CPUTAG_L4CS) << BIT_SHIFT_GMAC_CPUTAG_L4CS) +#define BIT_INVC_GMAC_CPUTAG_L4CS (~(BIT_MASK_GMAC_CPUTAG_L4CS << BIT_SHIFT_GMAC_CPUTAG_L4CS)) + +#define BIT_SHIFT_GMAC_CPUTAG_PSEL 18 +#define BIT_MASK_GMAC_CPUTAG_PSEL 0x1 +#define BIT_GMAC_CPUTAG_PSEL(x)(((x) & BIT_MASK_GMAC_CPUTAG_PSEL) << BIT_SHIFT_GMAC_CPUTAG_PSEL) +#define BIT_INVC_GMAC_CPUTAG_PSEL (~(BIT_MASK_GMAC_CPUTAG_PSEL << BIT_SHIFT_GMAC_CPUTAG_PSEL)) + + +#if 0 +enum RE8670_STATUS_REGS +{ + /*TX/RX share */ + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + + /*Tx descriptor opt1*/ + IPCS = (1 << 27), + L4CS = (1 << 26), + KEEP = (1 << 25), + BLU = (1 << 24), + TxCRC = (1 << 23), + VSEL = (1 << 22), + DisLrn = (1 << 21), + CPUTag_ipcs = (1 << 20), + CPUTag_l4cs = (1 << 19), + + /*Tx descriptor opt2*/ + CPUTag = (1 << 31), + aspri = (1 << 30), + CPRI = (1 << 27), + TxVLAN_int = (0 << 25), //intact + TxVLAN_ins = (1 << 25), //insert + TxVLAN_rm = (2 << 25), //remove + TxVLAN_re = (3 << 25), //remark + //TxPPPoEAct = (1 << 23), + TxPPPoEAct = 23, + //TxPPPoEIdx = (1 << 20), + TxPPPoEIdx = 20, + Efid = (1 << 19), + //Enhan_Fid = (1 << 16), + Enhan_Fid = 16, + /*Tx descriptor opt3*/ + SrcExtPort = 29, + TxDesPortM = 23, + TxDesStrID = 16, + TxDesVCM = 0, + /*Tx descriptor opt4*/ + /*Rx descriptor opt1*/ + CRCErr = (1 << 27), + IPV4CSF = (1 << 26), + L4CSF = (1 << 25), + RCDF = (1 << 24), + IP_FRAG = (1 << 23), + PPPoE_tag = (1 << 22), + RWT = (1 << 21), + PktType = (1 << 17), + RxProtoIP = 1, + RxProtoPPTP = 2, + RxProtoICMP = 3, + RxProtoIGMP = 4, + RxProtoTCP = 5, + RxProtoUDP = 6, + RxProtoIPv6 = 7, + RxProtoICMPv6 = 8, + RxProtoTCPv6 = 9, + RxProtoUDPv6 = 10, + L3route = (1 << 16), + OrigFormat = (1 << 15), + PCTRL = (1 << 14), + /*Rx descriptor opt2*/ + PTPinCPU = (1 << 30), + SVlanTag = (1 << 29), + /*Rx descriptor opt3*/ + SrcPort = (1 << 27), + DesPortM = (1 << 21), + Reason = (1 << 13), + IntPriority = (1 << 10), + ExtPortTTL = (1 << 5), +}; + +enum _DescStatusBit { + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + + /* Tx private */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0xfff, /* MSS value + LargeSend bit: 12 bits */ + IPCS = (1 << 18), /* Calculate IP checksum */ + UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 2/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + RxVlanTag = (1 << 16), /* VLAN tag available */ +}; +#endif + +typedef struct _PHY_MODE_INFO_ { + u8 PhyAddress; + u8 PhyMode; + u8 PhyInterface; +} PHY_MODE_INFO, *PPHY_MODE_INFO; + +typedef enum _PHY_MODE_SWITCH_ { + PHY_MODE_DISABLE = 0, + PHY_MODE_ENABLE = 1 +} PHY_MODE_SWITCH, *PPHY_MODE_SWITCH; + +typedef enum _PHY_INTERFACE_SELECT_ { + PHY_INTERFACE_ONE_WORKS = 0, + PHY_INTERFACE_ZERO_WORKS = 1 +} PHY_INTERFACE_SELECT, *PPHY_INTERFACE_SELECT; + +typedef enum _GMAC_MSR_FORCE_SPEED_ { + FORCE_SPD_100M = 0, + FORCE_SPD_10M = 1, + FORCE_SPD_GIGA = 2, + NO_FORCE_SPD = 3 +}GMAC_MSR_FORCE_SPEED, *PGMAC_MSR_FORCE_SPEED; + +// typedef enum _GMAC_INTERRUPT_MASK_ { +// GMAC_IMR_ROK = BIT0, +// GMAC_IMR_CNT_WRAP = BIT1, +// GMAC_IMR_RER_RUNT = BIT2, +// // BIT3 Reserved +// GMAC_IMR_RER_OVF = BIT4, +// GMAC_IMR_RDU = BIT5, +// GMAC_IMR_TOK_TI = BIT6, +// GMAC_IMR_TER = BIT7, +// GMAC_IMR_LINKCHG = BIT8, +// GMAC_IMR_TDU = BIT9, +// GMAC_IMR_SWINT = BIT10, +// GMAC_IMR_RDU2 = BIT11, +// GMAC_IMR_RDU3 = BIT12, +// GMAC_IMR_RDU4 = BIT13, +// GMAC_IMR_RDU5 = BIT14, +// GMAC_IMR_RDU6 = BIT15, +// } GMAC_INTERRUPT_MASK, *PGMAC_INTERRUPT_MASK; + +typedef enum _GMAC_INTERRUPT_MASK_ { + GMAC_IMR_ROK = BIT16, + GMAC_IMR_CNT_WRAP = BIT17, + GMAC_IMR_RER_RUNT = BIT18, + // BIT19 Reserved + GMAC_IMR_RER_OVF = BIT20, + GMAC_IMR_RDU = BIT21, + GMAC_IMR_TOK_TI = BIT22, + GMAC_IMR_TER = BIT23, + GMAC_IMR_LINKCHG = BIT24, + GMAC_IMR_TDU = BIT25, + GMAC_IMR_SWINT = BIT26, + GMAC_IMR_RDU2 = BIT27, + GMAC_IMR_RDU3 = BIT28, + GMAC_IMR_RDU4 = BIT29, + GMAC_IMR_RDU5 = BIT30, + GMAC_IMR_RDU6 = BIT31, +} GMAC_INTERRUPT_MASK, *PGMAC_INTERRUPT_MASK; + +typedef enum _GMAC_INTERRUPT_STATUS_ { + GMAC_ISR_ROK = BIT0, + GMAC_ISR_CNT_WRAP = BIT1, + GMAC_ISR_RER_RUNT = BIT2, + // BIT3 Reserved + GMAC_ISR_RER_OVF = BIT4, + GMAC_ISR_RDU = BIT5, + GMAC_ISR_TOK_TI = BIT6, + GMAC_ISR_TER = BIT7, + GMAC_ISR_LINKCHG = BIT8, + GMAC_ISR_TDU = BIT9, + GMAC_ISR_SWINT = BIT10, + GMAC_ISR_RDU2 = BIT11, + GMAC_ISR_RDU3 = BIT12, + GMAC_ISR_RDU4 = BIT13, + GMAC_ISR_RDU5 = BIT14, + GMAC_ISR_RDU6 = BIT15, +} GMAC_INTERRUPT_STATUS, *PGMAC_INTERRUPT_STATUS; + +typedef enum _GMAC_TX_VLAN_ACTION_ { + INTACT = 0, + INSERT_VLAN_HDR = 1, + REMOVE_VLAN_HDR = 2, + REMARKING_VID = 3 +}GMAC_TX_VLAN_ACTION, *PGMAC_TX_VLAN_ACTION; + +typedef enum _GMAC_RX_PACKET_TYPE_ { + TYPE_ETHERNET = 0, + TYPE_IPV4 = 1, + TYPE_IPV4_PPTP = 2, + TYPE_IPV4_ICMP = 3, + TYPE_IPV4_IGMP = 4, + TYPE_IPV4_TCP = 5, + TYPE_IPV4_UDP = 6, + TYPE_IPV6 = 7, + TYPE_ICMPV6 = 8, + TYPE_IPV6_TCP = 9, + TYPE_IPV6_UDP = 10 +}GMAC_RX_PACKET_TYPE, *PGMAC_RX_PACKET_TYPE; + + +/* + +// Memory Map of DW_apb_ssi +#define REG_DW_SSI_CTRLR0 0x00 // 16 bits +#define REG_DW_SSI_CTRLR1 0x04 // 16 bits +#define REG_DW_SSI_SSIENR 0x08 // 1 bit +#define REG_DW_SSI_RX_SAMPLE_DLY 0xF0 // 8 bits +#define REG_DW_SSI_RSVD_0 0xF4 // 32 bits +#define REG_DW_SSI_RSVD_1 0xF8 // 32 bits +#define REG_DW_SSI_RSVD_2 0xFC // 32 bits + +// CTRLR0 0x00 // 16 bits, 6.2.1 +// DFS Reset Value: 0x7 +#define BIT_SHIFT_CTRLR0_DFS 0 +#define BIT_MASK_CTRLR0_DFS 0xF +#define BIT_CTRLR0_DFS(x)(((x) & BIT_MASK_CTRLR0_DFS) << BIT_SHIFT_CTRLR0_DFS) +#define BIT_INVC_CTRLR0_DFS (~(BIT_MASK_CTRLR0_DFS << BIT_SHIFT_CTRLR0_DFS)) + +#define BIT_SHIFT_CTRLR0_FRF 4 +#define BIT_MASK_CTRLR0_FRF 0x3 +#define BIT_CTRLR0_FRF(x)(((x) & BIT_MASK_CTRLR0_FRF) << BIT_SHIFT_CTRLR0_FRF) +#define BIT_INVC_CTRLR0_FRF (~(BIT_MASK_CTRLR0_FRF << BIT_SHIFT_CTRLR0_FRF)) + +#define BIT_SHIFT_CTRLR0_SCPH 6 +#define BIT_MASK_CTRLR0_SCPH 0x1 +#define BIT_CTRLR0_SCPH(x)(((x) & BIT_MASK_CTRLR0_SCPH) << BIT_SHIFT_CTRLR0_SCPH) +#define BIT_INVC_CTRLR0_SCPH (~(BIT_MASK_CTRLR0_SCPH << BIT_SHIFT_CTRLR0_SCPH)) + +// CTRLR1 0x04 // 16 bits +#define BIT_SHIFT_CTRLR1_NDF 0 +#define BIT_MASK_CTRLR1_NDF 0xFFFF +#define BIT_CTRLR1_NDF(x)(((x) & BIT_MASK_CTRLR1_NDF) << BIT_SHIFT_CTRLR1_NDF) +#define BIT_INVC_CTRLR1_NDF (~(BIT_MASK_CTRLR1_NDF << BIT_SHIFT_CTRLR1_NDF)) + +// TXFLTR 0x18 // Variable Length +#define BIT_SHIFT_TXFTLR_TFT 0 +#define BIT_MASK_TXFTLR_TFT 0x3F // (TX_ABW-1):0 +#define BIT_TXFTLR_TFT(x)(((x) & BIT_MASK_TXFTLR_TFT) << BIT_SHIFT_TXFTLR_TFT) +#define BIT_INVC_TXFTLR_TFT (~(BIT_MASK_TXFTLR_TFT << BIT_SHIFT_TXFTLR_TFT)) + +// TXFLR 0x20 // see [READ ONLY] +#define BIT_MASK_TXFLR_TXTFL 0x7F // (TX_ABW):0 + +// RXFLR 0x24 // see [READ ONLY] +#define BIT_MASK_RXFLR_RXTFL 0x7F // (RX_ABW):0 + +// SR 0x28 // 7 bits [READ ONLY] +#define BIT_SR_BUSY BIT0 +#define BIT_SR_TFNF BIT1 +#define BIT_SR_TFE BIT2 +#define BIT_SR_RFNE BIT3 +#define BIT_SR_RFF BIT4 +#define BIT_SR_TXE BIT5 +#define BIT_SR_DCOL BIT6 + +#define BIT_IMR_TXEIM BIT0 +#define BIT_IMR_TXOIM BIT1 +#define BIT_IMR_RXUIM BIT2 +#define BIT_IMR_RXOIM BIT3 +#define BIT_IMR_RXFIM BIT4 +#define BIT_IMR_MSTIM BIT5 + +// ISR 0x30 // 6 bits [READ ONLY] +#define BIT_ISR_TXEIS BIT0 +#define BIT_ISR_TXOIS BIT1 +#define BIT_ISR_RXUIS BIT2 +#define BIT_ISR_RXOIS BIT3 +#define BIT_ISR_RXFIS BIT4 +#define BIT_ISR_MSTIS BIT5 + +*/ + +BOOL +HalMiiGmacInitRtl8195a( + IN VOID *Data + ); + +BOOL +HalMiiInitRtl8195a( + IN VOID *Data + ); + +BOOL +HalMiiGmacResetRtl8195a( + IN VOID *Data + ); + +BOOL +HalMiiGmacEnablePhyModeRtl8195a( + IN VOID *Data + ); + +u32 +HalMiiGmacXmitRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacCleanTxRingRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacFillTxInfoRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacFillRxInfoRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacTxRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacRxRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacSetDefaultEthIoCmdRtl8195a( + IN VOID *Data + ); + +VOID +HalMiiGmacInitIrqRtl8195a( + IN VOID *Data + ); + +u32 +HalMiiGmacGetInterruptStatusRtl8195a( + VOID + ); + +VOID +HalMiiGmacClearInterruptStatusRtl8195a( + u32 IsrStatus + ); + +#endif + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_nfc.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_nfc.h new file mode 100644 index 0000000..257454e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_nfc.h @@ -0,0 +1,153 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_NFC_H_ +#define _RTL8195A_NFC_H_ + +#include "hal_api.h" +#include "osdep_api.h" +#ifdef CONFIG_NFC_VERIFY +#include "../test/nfc/rtl8195a_nfc_test.h" +#endif + +#if CONFIG_NFC_NORMAL +//===================== Register Bit Field Definition ===================== +// TODO: +//===================== Register Address Definition ===================== +//TODO: +#include "osdep_api.h" +#define N2A_Q_LENGTH 10 +#define N2ARLENGTH 4 +//#define NFCTAGLENGTH 36 // maximum 36*4=144 bytes +#define NFCTAG_BASE 0x7F000 +#define NFCTAG_PAGESIZE 256 +#define NFCTAG_MAXPAGEIDX 16//(4*(1024/NFCTAG_PAGESIZE)) +#define A2NWCLENGTH 4 + +#define FLASHAPPLENGTH 31 +#define FLASHAPP_BASE 0x7E000 +#define FLASH_PAGESIZE 128 +#define FLASH_MAXPAGEIDX 32//(4*(1024/FLASH_PAGESIZE)) + +typedef struct _A2N_CATCH_W_ { + //u8 Vaild; + u8 A2NCatchRPage; + u32 A2NCatchWData[A2NWCLENGTH]; +}A2N_CATCH_W_QUEUE, *PA2N_CATCH_W_QUEUE; + +typedef struct _A2N_MAILBOX_Q_ { + u8 Length; + u8 Response; + u32 Content[A2NWCLENGTH+1]; +}A2N_MAILBOX_Q,*PA2N_MAILBOX_Q; + +typedef struct _N2A_CATCH_R_ { + u8 Vaild; + u8 N2ACatchRPage; + u32 N2ACatchRData[N2ARLENGTH]; +}N2A_CATCH_R_QUEUE, *PN2A_CATCH_R_QUEUE; + + +typedef struct _N2A_R_ { + u8 Vaild; + u8 N2ARPage; +}N2A_R_QUEUE, *PN2A_R_QUEUE; + +typedef struct _N2A_W_ { + u8 Vaild; + u8 N2AWPage; + u32 N2AWData; +}N2A_W_QUEUE, *PN2A_W_QUEUE; + +typedef struct _NFC_ADAPTER_ { + u8 Function; + u32 NFCIsr; + u8 N2ABoxOpen; + u8 A2NSeq; + //u8 NFCTagFlashWIdx; + //u8 NFCTagFlashRIdx; +// u32 NFCTag[NFCTAGLENGTH]; +#if !TASK_SCHEDULER_DISABLED + _Sema VeriSema; +#else + u32 VeriSema; +#endif +#ifdef PLATFORM_FREERTOS + xTaskHandle NFCTask; +#else + u32 NFCTask; +#endif +#ifdef CONFIG_NFC_VERIFY + //N2A Write Tag + u8 N2AWQRIdx; + u8 N2AWQWIdx; + N2A_W_QUEUE N2AWQ[N2A_Q_LENGTH]; + //N2A Read Tag + u8 N2ARQRIdx; + u8 N2ARQWIdx; + N2A_R_QUEUE N2ARQ[N2A_Q_LENGTH]; + //N2A Read Catch + u8 N2ARCRIdx; + u8 N2ARCWIdx; + N2A_CATCH_R_QUEUE N2ACatchR[N2A_Q_LENGTH]; +#endif + //A2N Write Catch + //u8 A2NWCRIdx; + //u8 A2NWCWIdx; + //A2N_CATCH_W_QUEUE A2NCatchW[N2A_Q_LENGTH]; + + //A2N Write mailbox queue + u8 A2NWMailBox; + u8 A2NWQRIdx; + u8 A2NWQWIdx; + A2N_MAILBOX_Q A2NMAILQ[N2A_Q_LENGTH]; + + u8 TaskStop; + void *nfc_obj; +}NFC_ADAPTER, *PNFC_ADAPTER; + +typedef enum _N2A_CMD_ { + TAG_READ = 0, + TAG_WRITE = 1, + CATCH_READ_DATA = 2, + NFC_R_PRESENT = 4, + N2A_MAILBOX_STATE = 5, + EXT_CLK_REQ = 6, + MAX_N2ACMD +}; + +typedef enum _A2N_CMD_ { + TAG_READ_DATA = 0, + CATCH_READ = 2, + CATCH_WRITE = 3, + A2N_MAILBOX_STATE = 4, + CONFIRM_N2A_BOX_STATE = 5, + EXT_CLK_RSP = 6, + MAX_A2NCMD +}; + +// Callback event defination +typedef enum _NFC_HAL_EVENT_ { + NFC_HAL_READER_PRESENT = (1<<0), + NFC_HAL_READ = (1<<1), + NFC_HAL_WRITE = (1<<2), + NFC_HAL_ERR = (1<<3), + NFC_HAL_CACHE_RD = (1<<4) +}NFC_CB_EVENT, *PNFC_CB_EVENT; + +VOID A2NWriteCatch(IN VOID *pNFCAdapte, IN u8 N2AWPage, + IN u8 Length, IN u32 *WData); +VOID A2NReadCatch(IN VOID *pNFCAdapte, IN u8 A2NRPage); +VOID HalNFCDmemInit(IN u32 *pTagData, IN u32 TagLen); +VOID HalNFCInit(PNFC_ADAPTER pNFCAdp); +VOID HalNFCDeinit(PNFC_ADAPTER pNFCAdp); +VOID HalNFCFwDownload(VOID); +#endif //CONFIG_NFC_NORMAL +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pcm.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pcm.h new file mode 100644 index 0000000..c2bd793 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pcm.h @@ -0,0 +1,449 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_PCM_H_ +#define _RTL8195A_PCM_H_ + +#include "basic_types.h" +#include "hal_api.h" + +#define HAL_PCMX_READ32(PcmIndex, addr) \ + HAL_READ32(PCM0_REG_BASE+ (PcmIndex*PCM1_REG_OFF), addr) +#define HAL_PCMX_WRITE32(PcmIndex, addr, value) \ + HAL_WRITE32((PCM0_REG_BASE+ (PcmIndex*PCM1_REG_OFF)), addr, value) + +#define REG_PCM_TRXBSA_OFF 0x004 +#define REG_PCM_CTL 0x000 +#define REG_PCM_CHCNR03 0x004 +#define REG_PCM_TSR03 0x008 +#define REG_PCM_BSIZE03 0x00C + +#define REG_PCM_CH0TXBSA 0x010 +#define REG_PCM_CH1TXBSA 0x014 +#define REG_PCM_CH2TXBSA 0x018 +#define REG_PCM_CH3TXBSA 0x01c +#define REG_PCM_CH0RXBSA 0x020 +#define REG_PCM_CH1RXBSA 0x024 +#define REG_PCM_CH2RXBSA 0x028 +#define REG_PCM_CH3RXBSA 0x02c + +#define REG_PCM_IMR03 0x030 +#define REG_PCM_ISR03 0x034 + +#define REG_PCM_CHCNR47 0x038 +#define REG_PCM_TSR47 0x03c +#define REG_PCM_BSIZE47 0x040 +#define REG_PCM_CH4TXBSA 0x044 +#define REG_PCM_CH5TXBSA 0x048 +#define REG_PCM_CH6TXBSA 0x04c +#define REG_PCM_CH7TXBSA 0x050 +#define REG_PCM_CH4RXBSA 0x054 +#define REG_PCM_CH5RXBSA 0x058 +#define REG_PCM_CH6RXBSA 0x05c +#define REG_PCM_CH7RXBSA 0x060 + +#define REG_PCM_IMR47 0x064 +#define REG_PCM_ISR47 0x068 + +#define REG_PCM_CHCNR811 0x06c +#define REG_PCM_TSR811 0x070 +#define REG_PCM_BSIZE811 0x074 +#define REG_PCM_CH8TXBSA 0x078 +#define REG_PCM_CH9TXBSA 0x07c +#define REG_PCM_CH10TXBSA 0x080 +#define REG_PCM_CH11TXBSA 0x084 +#define REG_PCM_CH8RXBSA 0x088 +#define REG_PCM_CH9RXBSA 0x08c +#define REG_PCM_CH10RXBSA 0x090 +#define REG_PCM_CH11RXBSA 0x094 + +#define REG_PCM_IMR811 0x098 +#define REG_PCM_ISR811 0x09c + +#define REG_PCM_CHCNR1215 0x0a0 +#define REG_PCM_TSR1215 0x0a4 +#define REG_PCM_BSIZE1215 0x0a8 +#define REG_PCM_CH12TXBSA 0x0ac +#define REG_PCM_CH13TXBSA 0x0b0 +#define REG_PCM_CH14TXBSA 0x0b4 +#define REG_PCM_CH15TXBSA 0x0b8 +#define REG_PCM_CH12RXBSA 0x0bc +#define REG_PCM_CH13RXBSA 0x0c0 +#define REG_PCM_CH14RXBSA 0x0c4 +#define REG_PCM_CH15RXBSA 0x0c8 + +#define REG_PCM_IMR1215 0x0cc +#define REG_PCM_ISR1215 0x0d0 + +#define REG_PCM_INTMAP 0x0d4 +#define REG_PCM_WTSR03 0x0d8 +#define REG_PCM_WTSR47 0x0dc + +#define REG_PCM_RX_BUFOW 0x0e0 + +/* template +#define BIT_SHIFT_CTLX_ 7 +#define BIT_MASK_CTLX_ 0x1 +#define BIT_CTLX_(x) (((x) & BIT_MASK_CTLX_) << BIT_SHIFT_CTLX_) +#define BIT_INV_CTLX_ (~(BIT_MASK_CTLX_ << BIT_SHIFT_CTLX_)) +*/ +#define BIT_SHIFT_CTLX_SLAVE_SEL 8 +#define BIT_MASK_CTLX_SLAVE_SEL 0x1 +#define BIT_CTLX_SLAVE_SEL(x) (((x) & BIT_MASK_CTLX_SLAVE_SEL) << BIT_SHIFT_CTLX_SLAVE_SEL) +#define BIT_INV_CTLX_SLAVE_SEL (~(BIT_MASK_CTLX_SLAVE_SEL << BIT_SHIFT_CTLX_SLAVE_SEL)) + +#define BIT_SHIFT_CTLX_FSINV 9 +#define BIT_MASK_CTLX_FSINV 0x1 +#define BIT_CTLX_FSINV(x) (((x) & BIT_MASK_CTLX_FSINV) << BIT_SHIFT_CTLX_FSINV) +#define BIT_INV_CTLX_FSINV (~(BIT_MASK_CTLX_FSINV << BIT_SHIFT_CTLX_FSINV)) + +#define BIT_SHIFT_CTLX_PCM_EN 12 +#define BIT_MASK_CTLX_PCM_EN 0x1 +#define BIT_CTLX_PCM_EN(x) (((x) & BIT_MASK_CTLX_PCM_EN) << BIT_SHIFT_CTLX_PCM_EN) +#define BIT_INV_CTLX_PCM_EN (~(BIT_MASK_CTLX_PCM_EN << BIT_SHIFT_CTLX_PCM_EN)) + +#define BIT_SHIFT_CTLX_LINEARMODE 13 +#define BIT_MASK_CTLX_LINEARMODE 0x1 +#define BIT_CTLX_LINEARMODE(x) (((x) & BIT_MASK_CTLX_LINEARMODE) << BIT_SHIFT_CTLX_LINEARMODE) +#define BIT_INV_CTLX_LINEARMODE (~(BIT_MASK_CTLX_LINEARMODE << BIT_SHIFT_CTLX_LINEARMODE)) + +#define BIT_SHIFT_CTLX_LOOP_BACK 14 +#define BIT_MASK_CTLX_LOOP_BACK 0x1 +#define BIT_CTLX_LOOP_BACK(x) (((x) & BIT_MASK_CTLX_LOOP_BACK) << BIT_SHIFT_CTLX_LOOP_BACK) +#define BIT_INV_CTLX_LOOP_BACK (~(BIT_MASK_CTLX_LOOP_BACK << BIT_SHIFT_CTLX_LOOP_BACK)) + +#define BIT_SHIFT_CTLX_ENDIAN_SWAP 17 +#define BIT_MASK_CTLX_ENDIAN_SWAP 0x1 +#define BIT_CTLX_ENDIAN_SWAP(x) (((x) & BIT_MASK_CTLX_ENDIAN_SWAP) << BIT_SHIFT_CTLX_ENDIAN_SWAP) +#define BIT_INV_CTLX_ENDIAN_SWAP (~(BIT_MASK_CTLX_ENDIAN_SWAP << BIT_SHIFT_CTLX_ENDIAN_SWAP)) + +#define BIT_SHIFT_CHCNR03_CH0RE 24 +#define BIT_MASK_CHCNR03_CH0RE 0x1 +#define BIT_CHCNR03_CH0RE(x) (((x) & BIT_MASK_CHCNR03_CH0RE) << BIT_SHIFT_CHCNR03_CH0RE) +#define BIT_INV_CHCNR03_CH0RE (~(BIT_MASK_CHCNR03_CH0RE << BIT_SHIFT_CHCNR03_CH0RE)) + +#define BIT_SHIFT_CHCNR03_CH0TE 25 +#define BIT_MASK_CHCNR03_CH0TE 0x1 +#define BIT_CHCNR03_CH0TE(x) (((x) & BIT_MASK_CHCNR03_CH0TE) << BIT_SHIFT_CHCNR03_CH0TE) +#define BIT_INV_CHCNR03_CH0TE (~(BIT_MASK_CHCNR03_CH0TE << BIT_SHIFT_CHCNR03_CH0TE)) + +#define BIT_SHIFT_CHCNR03_CH1RE 16 +#define BIT_MASK_CHCNR03_CH1RE 0x1 +#define BIT_CHCNR03_CH1RE(x) (((x) & BIT_MASK_CHCNR03_CH1RE) << BIT_SHIFT_CHCNR03_CH1RE) +#define BIT_INV_CHCNR03_CH1RE (~(BIT_MASK_CHCNR03_CH1RE << BIT_SHIFT_CHCNR03_CH1RE)) + +#define BIT_SHIFT_CHCNR03_CH1TE 17 +#define BIT_MASK_CHCNR03_CH1TE 0x1 +#define BIT_CHCNR03_CH1TE(x) (((x) & BIT_MASK_CHCNR03_CH1TE) << BIT_SHIFT_CHCNR03_CH1TE) +#define BIT_INV_CHCNR03_CH1TE (~(BIT_MASK_CHCNR03_CH1TE << BIT_SHIFT_CHCNR03_CH1TE)) + +#define BIT_SHIFT_CHCNR03_CH2RE 8 +#define BIT_MASK_CHCNR03_CH2RE 0x1 +#define BIT_CHCNR03_CH2RE(x) (((x) & BIT_MASK_CHCNR03_CH2RE) << BIT_SHIFT_CHCNR03_CH2RE) +#define BIT_INV_CHCNR03_CH2RE (~(BIT_MASK_CHCNR03_CH2RE << BIT_SHIFT_CHCNR03_CH2RE)) + +#define BIT_SHIFT_CHCNR03_CH2TE 9 +#define BIT_MASK_CHCNR03_CH2TE 0x1 +#define BIT_CHCNR03_CH2TE(x) (((x) & BIT_MASK_CHCNR03_CH2TE) << BIT_SHIFT_CHCNR03_CH2TE) +#define BIT_INV_CHCNR03_CH2TE (~(BIT_MASK_CHCNR03_CH2TE << BIT_SHIFT_CHCNR03_CH2TE)) + +#define BIT_SHIFT_CHCNR03_CH3RE 0 +#define BIT_MASK_CHCNR03_CH3RE 0x1 +#define BIT_CHCNR03_CH3RE(x) (((x) & BIT_MASK_CHCNR03_CH3RE) << BIT_SHIFT_CHCNR03_CH3RE) +#define BIT_INV_CHCNR03_CH3RE (~(BIT_MASK_CHCNR03_CH3RE << BIT_SHIFT_CHCNR03_CH3RE)) + +#define BIT_SHIFT_CHCNR03_CH3TE 1 +#define BIT_MASK_CHCNR03_CH3TE 0x1 +#define BIT_CHCNR03_CH3TE(x) (((x) & BIT_MASK_CHCNR03_CH3TE) << BIT_SHIFT_CHCNR03_CH3TE) +#define BIT_INV_CHCNR03_CH3TE (~(BIT_MASK_CHCNR03_CH3TE << BIT_SHIFT_CHCNR03_CH3TE)) + +#define BIT_SHIFT_CHCNR03_CH0MUA 26 +#define BIT_MASK_CHCNR03_CH0MUA 0x1 +#define BIT_CHCNR03_CH0MUA(x) (((x) & BIT_MASK_CHCNR03_CH0MUA) << BIT_SHIFT_CHCNR03_CH0MUA) +#define BIT_INV_CHCNR03_CH0MUA (~(BIT_MASK_CHCNR03_CH0MUA << BIT_SHIFT_CHCNR03_CH0MUA)) + +#define BIT_SHIFT_CHCNR03_CH0BAND 27 +#define BIT_MASK_CHCNR03_CH0BAND 0x1 +#define BIT_CHCNR03_CH0BAND(x) (((x) & BIT_MASK_CHCNR03_CH0BAND) << BIT_SHIFT_CHCNR03_CH0BAND) +#define BIT_INV_CHCNR03_CH0BAND (~(BIT_MASK_CHCNR03_CH0BAND << BIT_SHIFT_CHCNR03_CH0BAND)) + +#define BIT_SHIFT_TSR03_CH0TSA 24 +#define BIT_MASK_TSR03_CH0TSA 0x1F +#define BIT_TSR03_CH0TSA(x) (((x) & BIT_MASK_TSR03_CH0TSA) << BIT_SHIFT_TSR03_CH0TSA) +#define BIT_INV_TSR03_CH0TSA (~(BIT_MASK_TSR03_CH0TSA << BIT_SHIFT_TSR03_CH0TSA)) + +#define BIT_SHIFT_BSIZE03_CH0BSIZE 24 +#define BIT_MASK_BSIZE03_CH0BSIZE 0xFF +#define BIT_BSIZE03_CH0BSIZE(x) (((x) & BIT_MASK_BSIZE03_CH0BSIZE) << BIT_SHIFT_BSIZE03_CH0BSIZE) +#define BIT_INV_BSIZE03_CH0BSIZE (~(BIT_MASK_BSIZE03_CH0BSIZE << BIT_SHIFT_BSIZE03_CH0BSIZE)) + +typedef struct _PCM_CTL_REG_ { + u32 FCNT :8; // Bit 0-7 + u32 SlaveMode :1; // Bit 8 + u32 FsInv :1; // Bit 9 + u32 Rsvd10to11 :1; // Bit 10-11 + u32 Pcm_En :1; // Bit 12 + u32 LinearMode :1; // Bit 13 + u32 LoopBack :1; // Bit 14 + u32 Rsvd15to16 :2; // Bit 15-16 + u32 EndianSwap :1; // Bit 17 + u32 Rsvd18to31 :14; // Bit 18-31 +} PCM_CTL_REG, *PPCM_CTL_REG; + + + +typedef struct _PCM_CHCNR03_REG_ { + u32 CH3RE :1; // Bit 0 + u32 CH3TE :1; // Bit 1 + u32 CH3MuA :1; // Bit 2 + u32 CH3Band :1; // Bit 3 + u32 CH3SlicSel:4; // Bit 4-7 + u32 CH2RE :1; // Bit 8 + u32 CH2TE :1; // Bit 9 + u32 CH2MuA :1; // Bit 10 + u32 CH2Band :1; // Bit 11 + u32 CH2SlicSel:4; // Bit 12-15 + u32 CH1RE :1; // Bit 16 + u32 CH1TE :1; // Bit 17 + u32 CH1MuA :1; // Bit 18 + u32 CH1Band :1; // Bit 19 + u32 CH1SlicSel:4; // Bit 20-23 + u32 CH0RE :1; // Bit 24 + u32 CH0TE :1; // Bit 25 + u32 CH0MuA :1; // Bit 26 + u32 CH0Band :1; // Bit 27 + u32 CH0SlicSel:4; // Bit 28-31 +}PCM_CHCNR03_REG, *PPCM_CHCNR03_REG; + +typedef struct _PCM_TSR03_REG_ { + u32 CH3TSA :5; // Bit 0-4 + u32 Rsvd5to7 :3; // Bit 5-7 + u32 CH2TSA :5; // Bit 8-12 + u32 Rsvd13to15:3; // Bit 13-15 + u32 CH1TSA :5; // Bit 16-20 + u32 Rsvd21to23:3; // Bit 21-23 + u32 CH0TSA :5; // Bit 24-28 + u32 Rsvd29to31:3; // Bit 29-31 +}PCM_TSR03_REG, *PPCM_TSR03_REG; + +typedef struct _PCM_BSIZE03_REG_ { + u32 CH3BSize :8; // Bit 0-7 + u32 CH2BSize :8; // Bit 8-15 + u32 CH1BSize :8; // Bit 16-23 + u32 CH0BSize :8; // Bit 24-31 +}PCM_BSIZE03_REG, *PPCM_BSIZE03_REG; + +typedef struct _PCM_ISR03_REG_ { + u32 CH3RXP1UA :1; // Bit 0 + u32 CH3RXP0UA :1; // Bit 1 + u32 CH3TXP1UA :1; // Bit 2 + u32 CH3TXP0UA :1; // Bit 3 + u32 CH3RXP1IP :1; // Bit 4 + u32 CH3RXP0IP :1; // Bit 5 + u32 CH3TXP1IP :1; // Bit 6 + u32 CH3TXP0IP :1; // Bit 7 + u32 CH2RXP1UA :1; // Bit 8 + u32 CH2RXP0UA :1; // Bit 9 + u32 CH2TXP1UA :1; // Bit 10 + u32 CH2TXP0UA :1; // Bit 11 + u32 CH2RXP1IP :1; // Bit 12 + u32 CH2RXP0IP :1; // Bit 13 + u32 CH2TXP1IP :1; // Bit 14 + u32 CH2TXP0IP :1; // Bit 15 + u32 CH1RXP1UA :1; // Bit 16 + u32 CH1RXP0UA :1; // Bit 17 + u32 CH1TXP1UA :1; // Bit 18 + u32 CH1TXP0UA :1; // Bit 19 + u32 CH1RXP1IP :1; // Bit 20 + u32 CH1RXP0IP :1; // Bit 21 + u32 CH1TXP1IP :1; // Bit 22 + u32 CH1TXP0IP :1; // Bit 23 + u32 CH0RXP1UA :1; // Bit 24 + u32 CH0RXP0UA :1; // Bit 25 + u32 CH0TXP1UA :1; // Bit 26 + u32 CH0TXP0UA :1; // Bit 27 + u32 CH0RXP1IP :1; // Bit 28 + u32 CH0RXP0IP :1; // Bit 29 + u32 CH0TXP1IP :1; // Bit 30 + u32 CH0TXP0IP :1; // Bit 31 +}PCM_ISR03_REG, *PPCM_ISR03_REG; + +typedef enum _PCM_ISR015 { + PcmCh3P1RBU = 0x00000001, //ch0-3 + PcmCh3P0RBU = 0x00000002, + PcmCh3P1TBU = 0x00000004, + PcmCh3P0TBU = 0x00000008, + PcmCh3P1ROK = 0x00000010, + PcmCh3P0ROK = 0x00000020, + PcmCh3P1TOK = 0x00000040, + PcmCh3P0TOK = 0x00000080, + PcmCh2P1RBU = 0x00000100, + PcmCh2P0RBU = 0x00000200, + PcmCh2P1TBU = 0x00000400, + PcmCh2P0TBU = 0x00000800, + PcmCh2P1ROK = 0x00001000, + PcmCh2P0ROK = 0x00002000, + PcmCh2P1TOK = 0x00004000, + PcmCh2P0TOK = 0x00008000, + PcmCh1P1RBU = 0x00010000, + PcmCh1P0RBU = 0x00020000, + PcmCh1P1TBU = 0x00040000, + PcmCh1P0TBU = 0x00080000, + PcmCh1P1ROK = 0x00100000, + PcmCh1P0ROK = 0x00200000, + PcmCh1P1TOK = 0x00400000, + PcmCh1P0TOK = 0x00800000, + PcmCh0P1RBU = 0x01000000, + PcmCh0P0RBU = 0x02000000, + PcmCh0P1TBU = 0x04000000, + PcmCh0P0TBU = 0x08000000, + PcmCh0P1ROK = 0x10000000, + PcmCh0P0ROK = 0x20000000, + PcmCh0P1TOK = 0x40000000, + PcmCh0P0TOK = 0x80000000, + + PcmCh7P1RBU = 0x00000001, //ch4-7 + PcmCh7P0RBU = 0x00000002, + PcmCh7P1TBU = 0x00000004, + PcmCh7P0TBU = 0x00000008, + PcmCh7P1ROK = 0x00000010, + PcmCh7P0ROK = 0x00000020, + PcmCh7P1TOK = 0x00000040, + PcmCh7P0TOK = 0x00000080, + PcmCh6P1RBU = 0x00000100, + PcmCh6P0RBU = 0x00000200, + PcmCh6P1TBU = 0x00000400, + PcmCh6P0TBU = 0x00000800, + PcmCh6P1ROK = 0x00001000, + PcmCh6P0ROK = 0x00002000, + PcmCh6P1TOK = 0x00004000, + PcmCh6P0TOK = 0x00008000, + PcmCh5P1RBU = 0x00010000, + PcmCh5P0RBU = 0x00020000, + PcmCh5P1TBU = 0x00040000, + PcmCh5P0TBU = 0x00080000, + PcmCh5P1ROK = 0x00100000, + PcmCh5P0ROK = 0x00200000, + PcmCh5P1TOK = 0x00400000, + PcmCh5P0TOK = 0x00800000, + PcmCh4P1RBU = 0x01000000, + PcmCh4P0RBU = 0x02000000, + PcmCh4P1TBU = 0x04000000, + PcmCh4P0TBU = 0x08000000, + PcmCh4P1ROK = 0x10000000, + PcmCh4P0ROK = 0x20000000, + PcmCh4P1TOK = 0x40000000, + PcmCh4P0TOK = 0x80000000, + + PcmCh11P1RBU = 0x00000001, //ch8-11 + PcmCh11P0RBU = 0x00000002, + PcmCh11P1TBU = 0x00000004, + PcmCh11P0TBU = 0x00000008, + PcmCh11P1ROK = 0x00000010, + PcmCh11P0ROK = 0x00000020, + PcmCh11P1TOK = 0x00000040, + PcmCh11P0TOK = 0x00000080, + PcmCh10P1RBU = 0x00000100, + PcmCh10P0RBU = 0x00000200, + PcmCh10P1TBU = 0x00000400, + PcmCh10P0TBU = 0x00000800, + PcmCh10P1ROK = 0x00001000, + PcmCh10P0ROK = 0x00002000, + PcmCh10P1TOK = 0x00004000, + PcmCh10P0TOK = 0x00008000, + PcmCh9P1RBU = 0x00010000, + PcmCh9P0RBU = 0x00020000, + PcmCh9P1TBU = 0x00040000, + PcmCh9P0TBU = 0x00080000, + PcmCh9P1ROK = 0x00100000, + PcmCh9P0ROK = 0x00200000, + PcmCh9P1TOK = 0x00400000, + PcmCh9P0TOK = 0x00800000, + PcmCh8P1RBU = 0x01000000, + PcmCh8P0RBU = 0x02000000, + PcmCh8P1TBU = 0x04000000, + PcmCh8P0TBU = 0x08000000, + PcmCh8P1ROK = 0x10000000, + PcmCh8P0ROK = 0x20000000, + PcmCh8P1TOK = 0x40000000, + PcmCh8P0TOK = 0x80000000, + + PcmCh15P1RBU = 0x00000001, //ch12-15 + PcmCh15P0RBU = 0x00000002, + PcmCh15P1TBU = 0x00000004, + PcmCh15P0TBU = 0x00000008, + PcmCh15P1ROK = 0x00000010, + PcmCh15P0ROK = 0x00000020, + PcmCh15P1TOK = 0x00000040, + PcmCh15P0TOK = 0x00000080, + PcmCh14P1RBU = 0x00000100, + PcmCh14P0RBU = 0x00000200, + PcmCh14P1TBU = 0x00000400, + PcmCh14P0TBU = 0x00000800, + PcmCh14P1ROK = 0x00001000, + PcmCh14P0ROK = 0x00002000, + PcmCh14P1TOK = 0x00004000, + PcmCh14P0TOK = 0x00008000, + PcmCh13P1RBU = 0x00010000, + PcmCh13P0RBU = 0x00020000, + PcmCh13P1TBU = 0x00040000, + PcmCh13P0TBU = 0x00080000, + PcmCh13P1ROK = 0x00100000, + PcmCh13P0ROK = 0x00200000, + PcmCh13P1TOK = 0x00400000, + PcmCh13P0TOK = 0x00800000, + PcmCh12P1RBU = 0x01000000, + PcmCh12P0RBU = 0x02000000, + PcmCh12P1TBU = 0x04000000, + PcmCh12P0TBU = 0x08000000, + PcmCh12P1ROK = 0x10000000, + PcmCh12P0ROK = 0x20000000, + PcmCh12P1TOK = 0x40000000, + PcmCh12P0TOK = 0x80000000 +}PCM_ISR015, *PPCM_ISR015; + +VOID +HalPcmOnOffRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmInitRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmSettingRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmEnRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmIsrEnAndDisRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmDumpRegRtl8195a( + IN VOID *Data +); + +BOOL +HalPcmRtl8195a( + IN VOID *Data +); + +#endif /* _RTL8195A_PCM_H_ */ + + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_peri_on.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_peri_on.h new file mode 100644 index 0000000..da99b98 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_peri_on.h @@ -0,0 +1,1251 @@ +#ifndef __INC_RTL8195A_PERI_ON_H +#define __INC_RTL8195A_PERI_ON_H + +#define CPU_OPT_WIDTH 0x1F + +//2 REG_NOT_VALID + +//2 REG_PEON_PWR_CTRL +#define BIT_SOC_UAHV_EN BIT(2) +#define BIT_SOC_UALV_EN BIT(1) +#define BIT_SOC_USBD_EN BIT(0) + +//2 REG_PON_ISO_CTRL + +//2 REG_NOT_VALID +#define BIT_ISO_OSC32K_EN BIT(4) +//#define BIT_ISO_USBA_EN BIT(1) +//#define BIT_ISO_USBD_EN BIT(0) + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SOC_FUNC_EN +#define BIT_SOC_SECURITY_ENGINE_EN BIT(20) +#define BIT_SOC_GTIMER_EN BIT(16) +#define BIT_SOC_GDMA1_EN BIT(14) +#define BIT_SOC_GDMA0_EN BIT(13) +#define BIT_SOC_LOG_UART_EN BIT(12) +#define BIT_SOC_CPU_EN BIT(8) +#define BIT_SOC_MEM_CTRL_EN BIT(6) +#define BIT_SOC_FLASH_EN BIT(4) +#define BIT_SOC_LXBUS_EN BIT(2) +#define BIT_SOC_OCP_EN BIT(1) +#define BiT_SOC_FUN_EN BIT(0) + +//2 REG_SOC_HCI_COM_FUNC_EN +#define BIT_SOC_HCI_WL_MACON_EN BIT(16) +#define BIT_SOC_HCI_SM_SEL BIT(13) +#define BIT_SOC_HCI_MII_EN BIT(12) +#define BIT_SOC_HCI_OTG_RST_MUX BIT(5) +#define BIT_SOC_HCI_OTG_EN BIT(4) +#define BIT_SOC_HCI_SDIOD_ON_RST_MUX BIT(3) +#define BIT_SOC_HCI_SDIOH_EN BIT(2) +#define BIT_SOC_HCI_SDIOD_OFF_EN BIT(1) +#define BIT_SOC_HCI_SDIOD_ON_EN BIT(0) + +//2 REG_SOC_PERI_FUNC0_EN +#define BIT_PERI_PCM1_EN BIT(29) +#define BIT_PERI_PCM0_EN BIT(28) +#define BIT_PERI_I2S1_EN BIT(25) +#define BIT_PERI_I2S0_EN BIT(24) +#define BIT_PERI_I2C3_EN BIT(19) +#define BIT_PERI_I2C2_EN BIT(18) +#define BIT_PERI_I2C1_EN BIT(17) +#define BIT_PERI_I2C0_EN BIT(16) +#define BIT_PERI_SPI2_EN BIT(10) +#define BIT_PERI_SPI1_EN BIT(9) +#define BIT_PERI_SPI0_EN BIT(8) +#define BIT_PERI_UART2_EN BIT(2) +#define BIT_PERI_UART1_EN BIT(1) +#define BIT_PERI_UART0_EN BIT(0) + +//2 REG_SOC_PERI_FUNC1_EN +#define BIT_PERI_GPIO_EN BIT(8) +#define BIT_PERI_DAC1_EN BIT(5) +#define BIT_PERI_DAC0_EN BIT(4) +#define BIT_PERI_ADC0_EN BIT(0) + +//2 REG_SOC_PERI_BD_FUNC0_EN +#define BIT_PERI_UART2_BD_EN BIT(2) +#define BIT_PERI_UART1_BD_EN BIT(1) +#define BIT_PERI_UART0_BD_EN BIT(0) + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_PESOC_CLK_CTRL +#define BIT_SOC_SLPCK_BTCMD_EN BIT(29) +#define BIT_SOC_ACTCK_BTCMD_EN BIT(28) +#define BIT_SOC_SLPCK_GPIO_EN BIT(25) +#define BIT_SOC_ACTCK_GPIO_EN BIT(24) +#define BIT_SOC_SLPCK_GDMA1_EN BIT(19) +#define BIT_SOC_ACTCK_GDMA1_EN BIT(18) +#define BIT_SOC_SLPCK_GDMA0_EN BIT(17) +#define BIT_SOC_ACTCK_GDMA0_EN BIT(16) +#define BIT_SOC_SLPCK_TIMER_EN BIT(15) +#define BIT_SOC_ACTCK_TIMER_EN BIT(14) +#define BIT_SOC_SLPCK_LOG_UART_EN BIT(13) +#define BIT_SOC_ACTCK_LOG_UART_EN BIT(12) +#define BIT_SOC_SLPCK_SDR_EN BIT(11) +#define BIT_SOC_ACTCK_SDR_EN BIT(10) +#define BIT_SOC_SLPCK_FLASH_EN BIT(9) +#define BIT_SOC_ACTCK_FLASH_EN BIT(8) +#define BIT_SOC_SLPCK_VENDOR_REG_EN BIT(7) +#define BIT_SOC_ACTCK_VENDOR_REG_EN BIT(6) +#define BIT_SOC_SLPCK_TRACE_EN BIT(5) +#define BIT_SOC_ACTCK_TRACE_EN BIT(4) +#define BIT_SOC_CKE_PLFM BIT(2) +#define BIT_SOC_CKE_OCP BIT(0) + +//2 REG_PESOC_PERI_CLK_CTRL0 +#define BIT_SOC_SLPCK_SPI2_EN BIT(21) +#define BIT_SOC_ACTCK_SPI2_EN BIT(20) +#define BIT_SOC_SLPCK_SPI1_EN BIT(19) +#define BIT_SOC_ACTCK_SPI1_EN BIT(18) +#define BIT_SOC_SLPCK_SPI0_EN BIT(17) +#define BIT_SOC_ACTCK_SPI0_EN BIT(16) +#define BIT_SOC_SLPCK_UART2_EN BIT(5) +#define BIT_SOC_ACTCK_UART2_EN BIT(4) +#define BIT_SOC_SLPCK_UART1_EN BIT(3) +#define BIT_SOC_ACTCK_UART1_EN BIT(2) +#define BIT_SOC_SLPCK_UART0_EN BIT(1) +#define BIT_SOC_ACTCK_UART0_EN BIT(0) + +//2 REG_PESOC_PERI_CLK_CTRL1 +#define BIT_SOC_SLPCK_DAC_EN BIT(29) +#define BIT_SOC_ACTCK_DAC_EN BIT(28) +#define BIT_SOC_SLPCK_ADC_EN BIT(25) +#define BIT_SOC_ACTCK_ADC_EN BIT(24) +#define BIT_SOC_SLPCK_PCM_EN BIT(21) +#define BIT_SOC_ACTCK_PCM_EN BIT(20) +#define BIT_SOC_SLPCK_I2S_EN BIT(17) +#define BIT_SOC_ACTCK_I2S_EN BIT(16) +#define BIT_SOC_SLPCK_I2C3_EN BIT(7) +#define BIT_SOC_ACTCK_I2C3_EN BIT(6) +#define BIT_SOC_SLPCK_I2C2_EN BIT(5) +#define BIT_SOC_ACTCK_I2C2_EN BIT(4) +#define BIT_SOC_SLPCK_I2C1_EN BIT(3) +#define BIT_SOC_ACTCK_I2C1_EN BIT(2) +#define BIT_SOC_SLPCK_I2C0_EN BIT(1) +#define BIT_SOC_ACTCK_I2C0_EN BIT(0) + +//2 REG_PESOC_CLK_CTRL3 + +//2 REG_PESOC_HCI_CLK_CTRL0 +#define BIT_SOC_SLPCK_MII_MPHY_EN BIT(25) +#define BIT_SOC_ACTCK_MII_MPHY_EN BIT(24) +#define BIT_SOC_SLPCK_OTG_EN BIT(5) +#define BIT_SOC_ACTCK_OTG_EN BIT(4) +#define BIT_SOC_SLPCK_SDIO_HST_EN BIT(3) +#define BIT_SOC_ACTCK_SDIO_HST_EN BIT(2) +#define BIT_SOC_SLPCK_SDIO_DEV_EN BIT(1) +#define BIT_SOC_ACTCK_SDIO_DEV_EN BIT(0) + +//2 REG_PESOC_COM_CLK_CTRL1 +#define BIT_SOC_NFC_CAL_EN BIT(18) +#define BIT_SOC_SLPCK_NFC_EN BIT(17) +#define BIT_SOC_ACTCK_NFC_EN BIT(16) +#define BIT_SOC_SLPCK_SECURITY_ENG_EN BIT(5) +#define BIT_SOC_ACTCK_SECURITY_ENG_EN BIT(4) +#define BIT_SOC_SLPCK_WL_EN BIT(1) +#define BIT_SOC_ACTCK_WL_EN BIT(0) + +//2 REG_PESOC_HW_ENG_CLK_CTRL + +//2 REG_RSVD + +//2 REG_PESOC_CLK_SEL +#define BIT_PESOC_SPI1_SCLK_SEL BIT(18) + +#define BIT_SHIFT_PESOC_PERI_SCLK_SEL 16 +#define BIT_MASK_PESOC_PERI_SCLK_SEL 0x3 +#define BIT_PESOC_PERI_SCLK_SEL(x) (((x) & BIT_MASK_PESOC_PERI_SCLK_SEL) << BIT_SHIFT_PESOC_PERI_SCLK_SEL) + + +#define BIT_SHIFT_PESOC_SDR_CK_SEL 10 +#define BIT_MASK_PESOC_SDR_CK_SEL 0x3 +#define BIT_PESOC_SDR_CK_SEL(x) (((x) & BIT_MASK_PESOC_SDR_CK_SEL) << BIT_SHIFT_PESOC_SDR_CK_SEL) + + +#define BIT_SHIFT_PESOC_FLASH_CK_SEL 8 +#define BIT_MASK_PESOC_FLASH_CK_SEL 0x3 +#define BIT_PESOC_FLASH_CK_SEL(x) (((x) & BIT_MASK_PESOC_FLASH_CK_SEL) << BIT_SHIFT_PESOC_FLASH_CK_SEL) + + +#define BIT_SHIFT_PESOC_TRACE_CK_SEL 4 +#define BIT_MASK_PESOC_TRACE_CK_SEL 0x3 +#define BIT_PESOC_TRACE_CK_SEL(x) (((x) & BIT_MASK_PESOC_TRACE_CK_SEL) << BIT_SHIFT_PESOC_TRACE_CK_SEL) + + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_SYS_ANACK_CAL_CTRL +#define BIT_SYS_ANACK_CAL_CMD BIT(15) +#define BIT_SYS_ANACK_CAL_SEL BIT(14) + +#define BIT_SHIFT_SYS_ANACK_CAL_RPT 0 +#define BIT_MASK_SYS_ANACK_CAL_RPT 0x3fff +#define BIT_SYS_ANACK_CAL_RPT(x) (((x) & BIT_MASK_SYS_ANACK_CAL_RPT) << BIT_SHIFT_SYS_ANACK_CAL_RPT) + + +//2 REG_OSC32K_CTRL + +#define BIT_SHIFT_32K_BIAS_CURRENT 16 +#define BIT_MASK_32K_BIAS_CURRENT 0xffff +#define BIT_32K_BIAS_CURRENT(x) (((x) & BIT_MASK_32K_BIAS_CURRENT) << BIT_SHIFT_32K_BIAS_CURRENT) + + +#define BIT_SHIFT_32K_RESISTOR_COM 2 +#define BIT_MASK_32K_RESISTOR_COM 0x3 +#define BIT_32K_RESISTOR_COM(x) (((x) & BIT_MASK_32K_RESISTOR_COM) << BIT_SHIFT_32K_RESISTOR_COM) + +#define BIT_32K_DBG_SEL BIT(1) +#define BIT_32K_POW_CKGEN_EN BIT(0) + +//2 REG_OSC32K_REG_CTRL0 +#define BIT_32K_REG_INDIRT_CMD BIT(23) + +#define BIT_SHIFT_32K_REG_INDIRT_ADDR 16 +#define BIT_MASK_32K_REG_INDIRT_ADDR 0x3f +#define BIT_32K_REG_INDIRT_ADDR(x) (((x) & BIT_MASK_32K_REG_INDIRT_ADDR) << BIT_SHIFT_32K_REG_INDIRT_ADDR) + + +#define BIT_SHIFT_32K_REG_INDIRT_WDATA 0 +#define BIT_MASK_32K_REG_INDIRT_WDATA 0xffff +#define BIT_32K_REG_INDIRT_WDATA(x) (((x) & BIT_MASK_32K_REG_INDIRT_WDATA) << BIT_SHIFT_32K_REG_INDIRT_WDATA) + + +//2 REG_OSC32K_REG_CTRL1 + +#define BIT_SHIFT_32K_REG_INDIRT_RDATA 0 +#define BIT_MASK_32K_REG_INDIRT_RDATA 0xffff +#define BIT_32K_REG_INDIRT_RDATA(x) (((x) & BIT_MASK_32K_REG_INDIRT_RDATA) << BIT_SHIFT_32K_REG_INDIRT_RDATA) + + +//2 REG_THERMAL_METER_CTRL + +#define BIT_SHIFT_TEMP_VALUE 24 +#define BIT_MASK_TEMP_VALUE 0x3f +#define BIT_TEMP_VALUE(x) (((x) & BIT_MASK_TEMP_VALUE) << BIT_SHIFT_TEMP_VALUE) + + +#define BIT_SHIFT_TEMP_DELTA 16 +#define BIT_MASK_TEMP_DELTA 0x3f +#define BIT_TEMP_DELTA(x) (((x) & BIT_MASK_TEMP_DELTA) << BIT_SHIFT_TEMP_DELTA) + +#define BIT_THERMAL_METER_EN BIT(15) +#define BIT_THERMAL_METER_VALID BIT(14) + +#define BIT_SHIFT_THERMAL_METER_TIMER 0 +#define BIT_MASK_THERMAL_METER_TIMER 0xfff +#define BIT_THERMAL_METER_TIMER(x) (((x) & BIT_MASK_THERMAL_METER_TIMER) << BIT_SHIFT_THERMAL_METER_TIMER) + + +//2 REG_UART_MUX_CTRL + +#define BIT_SHIFT_UART2_PIN_SEL 9 +#define BIT_MASK_UART2_PIN_SEL 0x7 +#define BIT_UART2_PIN_SEL(x) (((x) & BIT_MASK_UART2_PIN_SEL) << BIT_SHIFT_UART2_PIN_SEL) + +#define BIT_UART2_PIN_EN BIT(8) + +#define BIT_SHIFT_UART1_PIN_SEL 5 +#define BIT_MASK_UART1_PIN_SEL 0x7 +#define BIT_UART1_PIN_SEL(x) (((x) & BIT_MASK_UART1_PIN_SEL) << BIT_SHIFT_UART1_PIN_SEL) + +#define BIT_UART1_PIN_EN BIT(4) + +#define BIT_SHIFT_UART0_PIN_SEL 1 +#define BIT_MASK_UART0_PIN_SEL 0x7 +#define BIT_UART0_PIN_SEL(x) (((x) & BIT_MASK_UART0_PIN_SEL) << BIT_SHIFT_UART0_PIN_SEL) + +#define BIT_UART0_PIN_EN BIT(0) + +//2 REG_SPI_MUX_CTRL +#define BIT_SPI0_MULTI_CS_EN BIT(28) + +#define BIT_SHIFT_SPI2_PIN_SEL 9 +#define BIT_MASK_SPI2_PIN_SEL 0x7 +#define BIT_SPI2_PIN_SEL(x) (((x) & BIT_MASK_SPI2_PIN_SEL) << BIT_SHIFT_SPI2_PIN_SEL) + +#define BIT_SPI2_PIN_EN BIT(8) + +#define BIT_SHIFT_SPI1_PIN_SEL 5 +#define BIT_MASK_SPI1_PIN_SEL 0x7 +#define BIT_SPI1_PIN_SEL(x) (((x) & BIT_MASK_SPI1_PIN_SEL) << BIT_SHIFT_SPI1_PIN_SEL) + +#define BIT_SPI1_PIN_EN BIT(4) + +#define BIT_SHIFT_SPI0_PIN_SEL 1 +#define BIT_MASK_SPI0_PIN_SEL 0x7 +#define BIT_SPI0_PIN_SEL(x) (((x) & BIT_MASK_SPI0_PIN_SEL) << BIT_SHIFT_SPI0_PIN_SEL) + +#define BIT_SPI0_PIN_EN BIT(0) + +//2 REG_I2C_MUX_CTRL + +#define BIT_SHIFT_I2C3_PIN_SEL 13 +#define BIT_MASK_I2C3_PIN_SEL 0x7 +#define BIT_I2C3_PIN_SEL(x) (((x) & BIT_MASK_I2C3_PIN_SEL) << BIT_SHIFT_I2C3_PIN_SEL) + +#define BIT_I2C3_PIN_EN BIT(12) + +#define BIT_SHIFT_I2C2_PIN_SEL 9 +#define BIT_MASK_I2C2_PIN_SEL 0x7 +#define BIT_I2C2_PIN_SEL(x) (((x) & BIT_MASK_I2C2_PIN_SEL) << BIT_SHIFT_I2C2_PIN_SEL) + +#define BIT_I2C2_PIN_EN BIT(8) + +#define BIT_SHIFT_I2C1_PIN_SEL 5 +#define BIT_MASK_I2C1_PIN_SEL 0x7 +#define BIT_I2C1_PIN_SEL(x) (((x) & BIT_MASK_I2C1_PIN_SEL) << BIT_SHIFT_I2C1_PIN_SEL) + +#define BIT_I2C1_PIN_EN BIT(4) + +#define BIT_SHIFT_I2C0_PIN_SEL 1 +#define BIT_MASK_I2C0_PIN_SEL 0x7 +#define BIT_I2C0_PIN_SEL(x) (((x) & BIT_MASK_I2C0_PIN_SEL) << BIT_SHIFT_I2C0_PIN_SEL) + +#define BIT_I2C0_PIN_EN BIT(0) + +//2 REG_I2S_MUX_CTRL/ REG_PCM_MUX_CTRL + +//2 REG_NOT_VALID + +#define BIT_SHIFT_PCM1_PIN_SEL 21 +#define BIT_MASK_PCM1_PIN_SEL 0x7 +#define BIT_PCM1_PIN_SEL(x) (((x) & BIT_MASK_PCM1_PIN_SEL) << BIT_SHIFT_PCM1_PIN_SEL) + +#define BIT_PCM1_PIN_EN BIT(20) + +#define BIT_SHIFT_PCM0_PIN_SEL 17 +#define BIT_MASK_PCM0_PIN_SEL 0x7 +#define BIT_PCM0_PIN_SEL(x) (((x) & BIT_MASK_PCM0_PIN_SEL) << BIT_SHIFT_PCM0_PIN_SEL) + +#define BIT_PCM0_PIN_EN BIT(16) + +//2 REG_NOT_VALID + +#define BIT_SHIFT_I2S1_PIN_SEL 6 +#define BIT_MASK_I2S1_PIN_SEL 0x3 +#define BIT_I2S1_PIN_SEL(x) (((x) & BIT_MASK_I2S1_PIN_SEL) << BIT_SHIFT_I2S1_PIN_SEL) + +#define BIT_I2S1_MCK_EN BIT(5) +#define BIT_I2S1_PIN_EN BIT(4) + +#define BIT_SHIFT_I2S0_PIN_SEL 2 +#define BIT_MASK_I2S0_PIN_SEL 0x3 +#define BIT_I2S0_PIN_SEL(x) (((x) & BIT_MASK_I2S0_PIN_SEL) << BIT_SHIFT_I2S0_PIN_SEL) + +#define BIT_I2S0_MCK_EN BIT(1) +#define BIT_I2S0_PIN_EN BIT(0) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_HCI_PINMUX_CTRL +#define BIT_HCI_MII_PIN_EN BIT(24) +#define BIT_HCI_SDIOH_PIN_EN BIT(1) +#define BIT_HCI_SDIOD_PIN_EN BIT(0) + +//2 REG_WL_PINMUX_CTRL +#define BIT_NFC_PIN_EN BIT(16) +#define BIT_WL_BTCMD_PIN_EN BIT(13) +#define BIT_WL_BTCOEX_PIN_EN BIT(12) +#define BIT_WL_ANT1_PIN_EN BIT(9) +#define BIT_WL_ANT0_PIN_EN BIT(8) + +#define BIT_SHIFT_WL_LED_PIN_SEL 1 +#define BIT_MASK_WL_LED_PIN_SEL 0x3 +#define BIT_WL_LED_PIN_SEL(x) (((x) & BIT_MASK_WL_LED_PIN_SEL) << BIT_SHIFT_WL_LED_PIN_SEL) + +#define BIT_WL_LED_PIN_EN BIT(0) + +//2 REG_BT_PINMUX_CTRL + +//2 REG_PWM_PINMUX_CTRL + +#define BIT_SHIFT_ETE3_PIN_SEL 29 +#define BIT_MASK_ETE3_PIN_SEL 0x3 +#define BIT_ETE3_PIN_SEL(x) (((x) & BIT_MASK_ETE3_PIN_SEL) << BIT_SHIFT_ETE3_PIN_SEL) + +#define BIT_ETE3_PIN_EN BIT(28) + +#define BIT_SHIFT_ETE2_PIN_SEL 25 +#define BIT_MASK_ETE2_PIN_SEL 0x3 +#define BIT_ETE2_PIN_SEL(x) (((x) & BIT_MASK_ETE2_PIN_SEL) << BIT_SHIFT_ETE2_PIN_SEL) + +#define BIT_ETE2_PIN_EN BIT(24) + +#define BIT_SHIFT_ETE1_PIN_SEL 21 +#define BIT_MASK_ETE1_PIN_SEL 0x3 +#define BIT_ETE1_PIN_SEL(x) (((x) & BIT_MASK_ETE1_PIN_SEL) << BIT_SHIFT_ETE1_PIN_SEL) + +#define BIT_ETE1_PIN_EN BIT(20) + +#define BIT_SHIFT_ETE0_PIN_SEL 17 +#define BIT_MASK_ETE0_PIN_SEL 0x3 +#define BIT_ETE0_PIN_SEL(x) (((x) & BIT_MASK_ETE0_PIN_SEL) << BIT_SHIFT_ETE0_PIN_SEL) + +#define BIT_ETE0_PIN_EN BIT(16) + +#define BIT_SHIFT_PWM3_PIN_SEL 13 +#define BIT_MASK_PWM3_PIN_SEL 0x3 +#define BIT_PWM3_PIN_SEL(x) (((x) & BIT_MASK_PWM3_PIN_SEL) << BIT_SHIFT_PWM3_PIN_SEL) + +#define BIT_PWM3_PIN_EN BIT(12) + +#define BIT_SHIFT_PWM2_PIN_SEL 9 +#define BIT_MASK_PWM2_PIN_SEL 0x3 +#define BIT_PWM2_PIN_SEL(x) (((x) & BIT_MASK_PWM2_PIN_SEL) << BIT_SHIFT_PWM2_PIN_SEL) + +#define BIT_PWM2_PIN_EN BIT(8) + +#define BIT_SHIFT_PWM1_PIN_SEL 5 +#define BIT_MASK_PWM1_PIN_SEL 0x3 +#define BIT_PWM1_PIN_SEL(x) (((x) & BIT_MASK_PWM1_PIN_SEL) << BIT_SHIFT_PWM1_PIN_SEL) + +#define BIT_PWM1_PIN_EN BIT(4) + +#define BIT_SHIFT_PWM0_PIN_SEL 1 +#define BIT_MASK_PWM0_PIN_SEL 0x3 +#define BIT_PWM0_PIN_SEL(x) (((x) & BIT_MASK_PWM0_PIN_SEL) << BIT_SHIFT_PWM0_PIN_SEL) + +#define BIT_PWM0_PIN_EN BIT(0) + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_CPU_PERIPHERAL_CTRL + +#define BIT_SHIFT_LOG_UART_PIN_SEL 22 +#define BIT_MASK_LOG_UART_PIN_SEL 0x3 +#define BIT_LOG_UART_PIN_SEL(x) (((x) & BIT_MASK_LOG_UART_PIN_SEL) << BIT_SHIFT_LOG_UART_PIN_SEL) + +#define BIT_LOG_UART_IR_EN BIT(21) +#define BIT_LOG_UART_PIN_EN BIT(20) +#define BIT_TRACE_PIN_EN BIT(17) +#define BIT_SDR_PIN_EN BIT(4) + +#define BIT_SHIFT_SPI_FLSH_PIN_SEL 1 +#define BIT_MASK_SPI_FLSH_PIN_SEL 0x3 +#define BIT_SPI_FLSH_PIN_SEL(x) (((x) & BIT_MASK_SPI_FLSH_PIN_SEL) << BIT_SHIFT_SPI_FLSH_PIN_SEL) + +#define BIT_SPI_FLSH_PIN_EN BIT(0) + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_HCI_CTRL_STATUS_0 + +//2 REG_HCI_CTRL_STATUS_1 + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_PESOC_MEM_CTRL + +#define BIT_SHIFT_PESOC_SDR_DDL_CTRL 16 +#define BIT_MASK_PESOC_SDR_DDL_CTRL 0xff +#define BIT_PESOC_SDR_DDL_CTRL(x) (((x) & BIT_MASK_PESOC_SDR_DDL_CTRL) << BIT_SHIFT_PESOC_SDR_DDL_CTRL) + + +#define BIT_SHIFT_PESOC_FLASH_DDL_CTRL 0 +#define BIT_MASK_PESOC_FLASH_DDL_CTRL 0xff +#define BIT_PESOC_FLASH_DDL_CTRL(x) (((x) & BIT_MASK_PESOC_FLASH_DDL_CTRL) << BIT_SHIFT_PESOC_FLASH_DDL_CTRL) + + +//2 REG_PESOC_SOC_CTRL + +#define BIT_SHIFT_PESOC_GDMA_CFG 16 +#define BIT_MASK_PESOC_GDMA_CFG 0x1fff +#define BIT_PESOC_GDMA_CFG(x) (((x) & BIT_MASK_PESOC_GDMA_CFG) << BIT_SHIFT_PESOC_GDMA_CFG) + +#define BIT_PESOC_MII_LX_SLV_SWAP_SEL BIT(13) +#define BIT_PESOC_MII_LX_MST_SWAP_SEL BIT(12) +#define BIT_PESOC_MII_LX_WRAPPER_EN BIT(11) +#define BIT_PESOC_LX_SLV_SWAP_SEL BIT(10) +#define BIT_PESOC_LX_MST_SWAP_SEL BIT(9) +#define BIT_PESOC_LX_WL_SWAP_SEL BIT(8) + +#define BIT_SHIFT_PESOC_SRAM_MUX_CFG 0 +#define BIT_MASK_PESOC_SRAM_MUX_CFG 0x7 +#define BIT_PESOC_SRAM_MUX_CFG(x) (((x) & BIT_MASK_PESOC_SRAM_MUX_CFG) << BIT_SHIFT_PESOC_SRAM_MUX_CFG) + + +//2 REG_PESOC_PERI_CTRL +#define BIT_SOC_FUNC_SPI_RN BIT(8) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_FW_CTRL_INT0 BIT(24) + +//2 REG_NOT_VALID + +//2 REG_GPIO_SHTDN_CTRL +#define BIT_GPIO_GPK_SHTDN_N BIT(10) +#define BIT_GPIO_GPJ_SHTDN_N BIT(9) +#define BIT_GPIO_GPI_SHTDN_N BIT(8) +#define BIT_GPIO_GPH_SHTDN_N BIT(7) +#define BIT_GPIO_GPG_SHTDN_N BIT(6) +#define BIT_GPIO_GPF_SHTDN_N BIT(5) +#define BIT_GPIO_GPE_SHTDN_N BIT(4) +#define BIT_GPIO_GPD_SHTDN_N BIT(3) +#define BIT_GPIO_GPC_SHTDN_N BIT(2) +#define BIT_GPIO_GPB_SHTDN_N BIT(1) +#define BIT_GPIO_GPA_SHTDN_N BIT(0) + +//2 REG_GPIO_DRIVING_CTRL +#define BIT_GPIO_GPK_DRV_SEL BIT(20) +#define BIT_GPIO_GPJ_DRV_SEL BIT(18) +#define BIT_GPIO_GPI_DRV_SEL BIT(16) +#define BIT_GPIO_GPH_DRV_SEL BIT(14) +#define BIT_GPIO_GPG_DRV_SEL BIT(12) +#define BIT_GPIO_GPF_DRV_SEL BIT(10) +#define BIT_GPIO_GPE_DRV_SEL BIT(8) +#define BIT_GPIO_GPD_DRV_SEL BIT(6) +#define BIT_GPIO_GPC_DRV_SEL BIT(4) +#define BIT_GPIO_GPB_DRV_SEL BIT(2) +#define BIT_GPIO_GPA_DRV_SEL BIT(0) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_GPIO_PULL_CTRL0 + +#define BIT_SHIFT_GPIO_GPB7_PULL_CTRL 30 +#define BIT_MASK_GPIO_GPB7_PULL_CTRL 0x3 +#define BIT_GPIO_GPB7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB7_PULL_CTRL) << BIT_SHIFT_GPIO_GPB7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB6_PULL_CTRL 28 +#define BIT_MASK_GPIO_GPB6_PULL_CTRL 0x3 +#define BIT_GPIO_GPB6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB6_PULL_CTRL) << BIT_SHIFT_GPIO_GPB6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB5_PULL_CTRL 26 +#define BIT_MASK_GPIO_GPB5_PULL_CTRL 0x3 +#define BIT_GPIO_GPB5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB5_PULL_CTRL) << BIT_SHIFT_GPIO_GPB5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB4_PULL_CTRL 24 +#define BIT_MASK_GPIO_GPB4_PULL_CTRL 0x3 +#define BIT_GPIO_GPB4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB4_PULL_CTRL) << BIT_SHIFT_GPIO_GPB4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB3_PULL_CTRL 22 +#define BIT_MASK_GPIO_GPB3_PULL_CTRL 0x3 +#define BIT_GPIO_GPB3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB3_PULL_CTRL) << BIT_SHIFT_GPIO_GPB3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB2_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPB2_PULL_CTRL 0x3 +#define BIT_GPIO_GPB2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB2_PULL_CTRL) << BIT_SHIFT_GPIO_GPB2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB1_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPB1_PULL_CTRL 0x3 +#define BIT_GPIO_GPB1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB1_PULL_CTRL) << BIT_SHIFT_GPIO_GPB1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPB0_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPB0_PULL_CTRL 0x3 +#define BIT_GPIO_GPB0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPB0_PULL_CTRL) << BIT_SHIFT_GPIO_GPB0_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA7_PULL_CTRL 14 +#define BIT_MASK_GPIO_GPA7_PULL_CTRL 0x3 +#define BIT_GPIO_GPA7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA7_PULL_CTRL) << BIT_SHIFT_GPIO_GPA7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA6_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPA6_PULL_CTRL 0x3 +#define BIT_GPIO_GPA6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA6_PULL_CTRL) << BIT_SHIFT_GPIO_GPA6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPA5_PULL_CTRL 0x3 +#define BIT_GPIO_GPA5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA5_PULL_CTRL) << BIT_SHIFT_GPIO_GPA5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPA4_PULL_CTRL 0x3 +#define BIT_GPIO_GPA4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA4_PULL_CTRL) << BIT_SHIFT_GPIO_GPA4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPA3_PULL_CTRL 0x3 +#define BIT_GPIO_GPA3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA3_PULL_CTRL) << BIT_SHIFT_GPIO_GPA3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPA2_PULL_CTRL 0x3 +#define BIT_GPIO_GPA2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA2_PULL_CTRL) << BIT_SHIFT_GPIO_GPA2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPA1_PULL_CTRL 0x3 +#define BIT_GPIO_GPA1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA1_PULL_CTRL) << BIT_SHIFT_GPIO_GPA1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPA0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPA0_PULL_CTRL 0x3 +#define BIT_GPIO_GPA0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPA0_PULL_CTRL) << BIT_SHIFT_GPIO_GPA0_PULL_CTRL) + + +//2 REG_GPIO_PULL_CTRL1 + +#define BIT_SHIFT_GPIO_GPD7_PULL_CTRL 29 +#define BIT_MASK_GPIO_GPD7_PULL_CTRL 0x7 +#define BIT_GPIO_GPD7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD7_PULL_CTRL) << BIT_SHIFT_GPIO_GPD7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD6_PULL_CTRL 28 +#define BIT_MASK_GPIO_GPD6_PULL_CTRL 0x3 +#define BIT_GPIO_GPD6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD6_PULL_CTRL) << BIT_SHIFT_GPIO_GPD6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD5_PULL_CTRL 26 +#define BIT_MASK_GPIO_GPD5_PULL_CTRL 0x3 +#define BIT_GPIO_GPD5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD5_PULL_CTRL) << BIT_SHIFT_GPIO_GPD5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD4_PULL_CTRL 24 +#define BIT_MASK_GPIO_GPD4_PULL_CTRL 0x3 +#define BIT_GPIO_GPD4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD4_PULL_CTRL) << BIT_SHIFT_GPIO_GPD4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD3_PULL_CTRL 22 +#define BIT_MASK_GPIO_GPD3_PULL_CTRL 0x3 +#define BIT_GPIO_GPD3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD3_PULL_CTRL) << BIT_SHIFT_GPIO_GPD3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD2_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPD2_PULL_CTRL 0x3 +#define BIT_GPIO_GPD2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD2_PULL_CTRL) << BIT_SHIFT_GPIO_GPD2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD1_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPD1_PULL_CTRL 0x3 +#define BIT_GPIO_GPD1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD1_PULL_CTRL) << BIT_SHIFT_GPIO_GPD1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD0_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPD0_PULL_CTRL 0x3 +#define BIT_GPIO_GPD0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD0_PULL_CTRL) << BIT_SHIFT_GPIO_GPD0_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC7_PULL_CTRL 14 +#define BIT_MASK_GPIO_GPC7_PULL_CTRL 0x3 +#define BIT_GPIO_GPC7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC7_PULL_CTRL) << BIT_SHIFT_GPIO_GPC7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC6_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPC6_PULL_CTRL 0x3 +#define BIT_GPIO_GPC6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC6_PULL_CTRL) << BIT_SHIFT_GPIO_GPC6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPC5_PULL_CTRL 0x3 +#define BIT_GPIO_GPC5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC5_PULL_CTRL) << BIT_SHIFT_GPIO_GPC5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPC4_PULL_CTRL 0x3 +#define BIT_GPIO_GPC4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC4_PULL_CTRL) << BIT_SHIFT_GPIO_GPC4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPC3_PULL_CTRL 0x3 +#define BIT_GPIO_GPC3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC3_PULL_CTRL) << BIT_SHIFT_GPIO_GPC3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPC2_PULL_CTRL 0x3 +#define BIT_GPIO_GPC2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC2_PULL_CTRL) << BIT_SHIFT_GPIO_GPC2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPC1_PULL_CTRL 0x3 +#define BIT_GPIO_GPC1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC1_PULL_CTRL) << BIT_SHIFT_GPIO_GPC1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPC0_PULL_CTRL 0x3 +#define BIT_GPIO_GPC0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC0_PULL_CTRL) << BIT_SHIFT_GPIO_GPC0_PULL_CTRL) + + +//2 REG_GPIO_PULL_CTRL2 + +#define BIT_SHIFT_GPIO_GPF5_PULL_CTRL 26 +#define BIT_MASK_GPIO_GPF5_PULL_CTRL 0x3 +#define BIT_GPIO_GPF5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF5_PULL_CTRL) << BIT_SHIFT_GPIO_GPF5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPF4_PULL_CTRL 24 +#define BIT_MASK_GPIO_GPF4_PULL_CTRL 0x3 +#define BIT_GPIO_GPF4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF4_PULL_CTRL) << BIT_SHIFT_GPIO_GPF4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPF3_PULL_CTRL 22 +#define BIT_MASK_GPIO_GPF3_PULL_CTRL 0x3 +#define BIT_GPIO_GPF3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF3_PULL_CTRL) << BIT_SHIFT_GPIO_GPF3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPF2_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPF2_PULL_CTRL 0x3 +#define BIT_GPIO_GPF2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF2_PULL_CTRL) << BIT_SHIFT_GPIO_GPF2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPF1_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPF1_PULL_CTRL 0x3 +#define BIT_GPIO_GPF1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF1_PULL_CTRL) << BIT_SHIFT_GPIO_GPF1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPF0_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPF0_PULL_CTRL 0x3 +#define BIT_GPIO_GPF0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPF0_PULL_CTRL) << BIT_SHIFT_GPIO_GPF0_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE7_PULL_CTRL 14 +#define BIT_MASK_GPIO_GPE7_PULL_CTRL 0x3 +#define BIT_GPIO_GPE7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE7_PULL_CTRL) << BIT_SHIFT_GPIO_GPE7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE6_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPE6_PULL_CTRL 0x3 +#define BIT_GPIO_GPE6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE6_PULL_CTRL) << BIT_SHIFT_GPIO_GPE6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPE5_PULL_CTRL 0x3 +#define BIT_GPIO_GPE5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE5_PULL_CTRL) << BIT_SHIFT_GPIO_GPE5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPE4_PULL_CTRL 0x3 +#define BIT_GPIO_GPE4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE4_PULL_CTRL) << BIT_SHIFT_GPIO_GPE4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPE3_PULL_CTRL 0x3 +#define BIT_GPIO_GPE3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE3_PULL_CTRL) << BIT_SHIFT_GPIO_GPE3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPE2_PULL_CTRL 0x3 +#define BIT_GPIO_GPE2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE2_PULL_CTRL) << BIT_SHIFT_GPIO_GPE2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPE1_PULL_CTRL 0x3 +#define BIT_GPIO_GPE1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE1_PULL_CTRL) << BIT_SHIFT_GPIO_GPE1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPE0_PULL_CTRL 0x3 +#define BIT_GPIO_GPE0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE0_PULL_CTRL) << BIT_SHIFT_GPIO_GPE0_PULL_CTRL) + + +//2 REG_NOT_VALID + +#define BIT_SHIFT_GPIO_GPH7_PULL_CTRL 30 +#define BIT_MASK_GPIO_GPH7_PULL_CTRL 0x3 +#define BIT_GPIO_GPH7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH7_PULL_CTRL) << BIT_SHIFT_GPIO_GPH7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH6_PULL_CTRL 28 +#define BIT_MASK_GPIO_GPH6_PULL_CTRL 0x3 +#define BIT_GPIO_GPH6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH6_PULL_CTRL) << BIT_SHIFT_GPIO_GPH6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH5_PULL_CTRL 26 +#define BIT_MASK_GPIO_GPH5_PULL_CTRL 0x3 +#define BIT_GPIO_GPH5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH5_PULL_CTRL) << BIT_SHIFT_GPIO_GPH5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH4_PULL_CTRL 24 +#define BIT_MASK_GPIO_GPH4_PULL_CTRL 0x3 +#define BIT_GPIO_GPH4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH4_PULL_CTRL) << BIT_SHIFT_GPIO_GPH4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH3_PULL_CTRL 22 +#define BIT_MASK_GPIO_GPH3_PULL_CTRL 0x3 +#define BIT_GPIO_GPH3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH3_PULL_CTRL) << BIT_SHIFT_GPIO_GPH3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH2_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPH2_PULL_CTRL 0x3 +#define BIT_GPIO_GPH2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH2_PULL_CTRL) << BIT_SHIFT_GPIO_GPH2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH1_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPH1_PULL_CTRL 0x3 +#define BIT_GPIO_GPH1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH1_PULL_CTRL) << BIT_SHIFT_GPIO_GPH1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPH0_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPH0_PULL_CTRL 0x3 +#define BIT_GPIO_GPH0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPH0_PULL_CTRL) << BIT_SHIFT_GPIO_GPH0_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG7_PULL_CTRL 14 +#define BIT_MASK_GPIO_GPG7_PULL_CTRL 0x3 +#define BIT_GPIO_GPG7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG7_PULL_CTRL) << BIT_SHIFT_GPIO_GPG7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG6_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPG6_PULL_CTRL 0x3 +#define BIT_GPIO_GPG6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG6_PULL_CTRL) << BIT_SHIFT_GPIO_GPG6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPG5_PULL_CTRL 0x3 +#define BIT_GPIO_GPG5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG5_PULL_CTRL) << BIT_SHIFT_GPIO_GPG5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPG4_PULL_CTRL 0x3 +#define BIT_GPIO_GPG4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG4_PULL_CTRL) << BIT_SHIFT_GPIO_GPG4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPG3_PULL_CTRL 0x3 +#define BIT_GPIO_GPG3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG3_PULL_CTRL) << BIT_SHIFT_GPIO_GPG3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPG2_PULL_CTRL 0x3 +#define BIT_GPIO_GPG2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG2_PULL_CTRL) << BIT_SHIFT_GPIO_GPG2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPG1_PULL_CTRL 0x3 +#define BIT_GPIO_GPG1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG1_PULL_CTRL) << BIT_SHIFT_GPIO_GPG1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPG0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPG0_PULL_CTRL 0x3 +#define BIT_GPIO_GPG0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPG0_PULL_CTRL) << BIT_SHIFT_GPIO_GPG0_PULL_CTRL) + + +//2 REG_GPIO_PULL_CTRL4 + +#define BIT_SHIFT_GPIO_GPJ6_PULL_CTRL 28 +#define BIT_MASK_GPIO_GPJ6_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ6_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ5_PULL_CTRL 26 +#define BIT_MASK_GPIO_GPJ5_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ5_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ4_PULL_CTRL 24 +#define BIT_MASK_GPIO_GPJ4_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ4_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ3_PULL_CTRL 22 +#define BIT_MASK_GPIO_GPJ3_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ3_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ2_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPJ2_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ2_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ1_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPJ1_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ1_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPJ0_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPJ0_PULL_CTRL 0x3 +#define BIT_GPIO_GPJ0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPJ0_PULL_CTRL) << BIT_SHIFT_GPIO_GPJ0_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI7_PULL_CTRL 14 +#define BIT_MASK_GPIO_GPI7_PULL_CTRL 0x3 +#define BIT_GPIO_GPI7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI7_PULL_CTRL) << BIT_SHIFT_GPIO_GPI7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI6_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPI6_PULL_CTRL 0x3 +#define BIT_GPIO_GPI6_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI6_PULL_CTRL) << BIT_SHIFT_GPIO_GPI6_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPI5_PULL_CTRL 0x3 +#define BIT_GPIO_GPI5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI5_PULL_CTRL) << BIT_SHIFT_GPIO_GPI5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPI4_PULL_CTRL 0x3 +#define BIT_GPIO_GPI4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI4_PULL_CTRL) << BIT_SHIFT_GPIO_GPI4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPI3_PULL_CTRL 0x3 +#define BIT_GPIO_GPI3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI3_PULL_CTRL) << BIT_SHIFT_GPIO_GPI3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPI2_PULL_CTRL 0x3 +#define BIT_GPIO_GPI2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI2_PULL_CTRL) << BIT_SHIFT_GPIO_GPI2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPI1_PULL_CTRL 0x3 +#define BIT_GPIO_GPI1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI1_PULL_CTRL) << BIT_SHIFT_GPIO_GPI1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPI0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPI0_PULL_CTRL 0x3 +#define BIT_GPIO_GPI0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPI0_PULL_CTRL) << BIT_SHIFT_GPIO_GPI0_PULL_CTRL) + + +//2 REG_GPIO_PULL_CTRL5 + +#define BIT_SHIFT_GPIO_GPEA_PULL_CTRL 20 +#define BIT_MASK_GPIO_GPEA_PULL_CTRL 0x3 +#define BIT_GPIO_GPEA_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPEA_PULL_CTRL) << BIT_SHIFT_GPIO_GPEA_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE9_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPE9_PULL_CTRL 0x3 +#define BIT_GPIO_GPE9_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE9_PULL_CTRL) << BIT_SHIFT_GPIO_GPE9_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPE8_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPE8_PULL_CTRL 0x3 +#define BIT_GPIO_GPE8_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPE8_PULL_CTRL) << BIT_SHIFT_GPIO_GPE8_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK7_PULL_CTRL 12 +#define BIT_MASK_GPIO_GPK7_PULL_CTRL 0x3 +#define BIT_GPIO_GPK7_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK7_PULL_CTRL) << BIT_SHIFT_GPIO_GPK7_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK5_PULL_CTRL 10 +#define BIT_MASK_GPIO_GPK5_PULL_CTRL 0x3 +#define BIT_GPIO_GPK5_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK5_PULL_CTRL) << BIT_SHIFT_GPIO_GPK5_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK4_PULL_CTRL 8 +#define BIT_MASK_GPIO_GPK4_PULL_CTRL 0x3 +#define BIT_GPIO_GPK4_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK4_PULL_CTRL) << BIT_SHIFT_GPIO_GPK4_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK3_PULL_CTRL 6 +#define BIT_MASK_GPIO_GPK3_PULL_CTRL 0x3 +#define BIT_GPIO_GPK3_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK3_PULL_CTRL) << BIT_SHIFT_GPIO_GPK3_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK2_PULL_CTRL 4 +#define BIT_MASK_GPIO_GPK2_PULL_CTRL 0x3 +#define BIT_GPIO_GPK2_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK2_PULL_CTRL) << BIT_SHIFT_GPIO_GPK2_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK1_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPK1_PULL_CTRL 0x3 +#define BIT_GPIO_GPK1_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK1_PULL_CTRL) << BIT_SHIFT_GPIO_GPK1_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPK0_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPK0_PULL_CTRL 0x3 +#define BIT_GPIO_GPK0_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPK0_PULL_CTRL) << BIT_SHIFT_GPIO_GPK0_PULL_CTRL) + + +//2 REG_GPIO_PULL_CTRL6 + +#define BIT_SHIFT_GPIO_GPD9_PULL_CTRL 18 +#define BIT_MASK_GPIO_GPD9_PULL_CTRL 0x3 +#define BIT_GPIO_GPD9_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD9_PULL_CTRL) << BIT_SHIFT_GPIO_GPD9_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPD8_PULL_CTRL 16 +#define BIT_MASK_GPIO_GPD8_PULL_CTRL 0x3 +#define BIT_GPIO_GPD8_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPD8_PULL_CTRL) << BIT_SHIFT_GPIO_GPD8_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC9_PULL_CTRL 2 +#define BIT_MASK_GPIO_GPC9_PULL_CTRL 0x3 +#define BIT_GPIO_GPC9_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC9_PULL_CTRL) << BIT_SHIFT_GPIO_GPC9_PULL_CTRL) + + +#define BIT_SHIFT_GPIO_GPC8_PULL_CTRL 0 +#define BIT_MASK_GPIO_GPC8_PULL_CTRL 0x3 +#define BIT_GPIO_GPC8_PULL_CTRL(x) (((x) & BIT_MASK_GPIO_GPC8_PULL_CTRL) << BIT_SHIFT_GPIO_GPC8_PULL_CTRL) + + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_PERI_PWM0_CTRL +#define BIT_PERI_PWM0_EN BIT(31) + +#define BIT_SHIFT_PERI_PWM0_GT_SEL 24 +#define BIT_MASK_PERI_PWM0_GT_SEL 0xf +#define BIT_PERI_PWM0_GT_SEL(x) (((x) & BIT_MASK_PERI_PWM0_GT_SEL) << BIT_SHIFT_PERI_PWM0_GT_SEL) + + +#define BIT_SHIFT_PERI_PWM0_DUTY 12 +#define BIT_MASK_PERI_PWM0_DUTY 0x3ff +#define BIT_PERI_PWM0_DUTY(x) (((x) & BIT_MASK_PERI_PWM0_DUTY) << BIT_SHIFT_PERI_PWM0_DUTY) + + +#define BIT_SHIFT_PERI_PWM0_PERIOD 0 +#define BIT_MASK_PERI_PWM0_PERIOD 0x3ff +#define BIT_PERI_PWM0_PERIOD(x) (((x) & BIT_MASK_PERI_PWM0_PERIOD) << BIT_SHIFT_PERI_PWM0_PERIOD) + + +//2 REG_PERI_PWM1_CTRL +#define BIT_PERI_PWM1_EN BIT(31) + +#define BIT_SHIFT_PERI_PWM1_GT_SEL 24 +#define BIT_MASK_PERI_PWM1_GT_SEL 0xf +#define BIT_PERI_PWM1_GT_SEL(x) (((x) & BIT_MASK_PERI_PWM1_GT_SEL) << BIT_SHIFT_PERI_PWM1_GT_SEL) + + +#define BIT_SHIFT_PERI_PWM1_DUTY 12 +#define BIT_MASK_PERI_PWM1_DUTY 0x3ff +#define BIT_PERI_PWM1_DUTY(x) (((x) & BIT_MASK_PERI_PWM1_DUTY) << BIT_SHIFT_PERI_PWM1_DUTY) + + +#define BIT_SHIFT_PERI_PWM1_PERIOD 0 +#define BIT_MASK_PERI_PWM1_PERIOD 0x3ff +#define BIT_PERI_PWM1_PERIOD(x) (((x) & BIT_MASK_PERI_PWM1_PERIOD) << BIT_SHIFT_PERI_PWM1_PERIOD) + + +//2 REG_PERI_PWM2_CTRL +#define BIT_PERI_PWM2_EN BIT(31) + +#define BIT_SHIFT_PERI_PWM2_GT_SEL 24 +#define BIT_MASK_PERI_PWM2_GT_SEL 0xf +#define BIT_PERI_PWM2_GT_SEL(x) (((x) & BIT_MASK_PERI_PWM2_GT_SEL) << BIT_SHIFT_PERI_PWM2_GT_SEL) + + +#define BIT_SHIFT_PERI_PWM2_DUTY 12 +#define BIT_MASK_PERI_PWM2_DUTY 0x3ff +#define BIT_PERI_PWM2_DUTY(x) (((x) & BIT_MASK_PERI_PWM2_DUTY) << BIT_SHIFT_PERI_PWM2_DUTY) + + +#define BIT_SHIFT_PERI_PWM2_PERIOD 0 +#define BIT_MASK_PERI_PWM2_PERIOD 0x3ff +#define BIT_PERI_PWM2_PERIOD(x) (((x) & BIT_MASK_PERI_PWM2_PERIOD) << BIT_SHIFT_PERI_PWM2_PERIOD) + + +//2 REG_PERI_PWM3_CTRL +#define BIT_PERI_PWM3_EN BIT(31) + +#define BIT_SHIFT_PERI_PWM3_GT_SEL 24 +#define BIT_MASK_PERI_PWM3_GT_SEL 0xf +#define BIT_PERI_PWM3_GT_SEL(x) (((x) & BIT_MASK_PERI_PWM3_GT_SEL) << BIT_SHIFT_PERI_PWM3_GT_SEL) + + +#define BIT_SHIFT_PERI_PWM3_DUTY 12 +#define BIT_MASK_PERI_PWM3_DUTY 0x3ff +#define BIT_PERI_PWM3_DUTY(x) (((x) & BIT_MASK_PERI_PWM3_DUTY) << BIT_SHIFT_PERI_PWM3_DUTY) + + +#define BIT_SHIFT_PERI_PWM3_PERIOD 0 +#define BIT_MASK_PERI_PWM3_PERIOD 0x3ff +#define BIT_PERI_PWM3_PERIOD(x) (((x) & BIT_MASK_PERI_PWM3_PERIOD) << BIT_SHIFT_PERI_PWM3_PERIOD) + + +//2 REG_PERI_TIM_EVT_CTRL +#define BIT_PERI_GT_EVT3_EN BIT(31) + +#define BIT_SHIFT_PERI_GT_EVT3_SRC_SEL 28 +#define BIT_MASK_PERI_GT_EVT3_SRC_SEL 0x7 +#define BIT_PERI_GT_EVT3_SRC_SEL(x) (((x) & BIT_MASK_PERI_GT_EVT3_SRC_SEL) << BIT_SHIFT_PERI_GT_EVT3_SRC_SEL) + + +#define BIT_SHIFT_PERI_GT_EVT3_PULSE_DUR 24 +#define BIT_MASK_PERI_GT_EVT3_PULSE_DUR 0xf +#define BIT_PERI_GT_EVT3_PULSE_DUR(x) (((x) & BIT_MASK_PERI_GT_EVT3_PULSE_DUR) << BIT_SHIFT_PERI_GT_EVT3_PULSE_DUR) + +#define BIT_PERI_GT_EVT2_EN BIT(23) + +#define BIT_SHIFT_PERI_GT_EVT2_SRC_SEL 20 +#define BIT_MASK_PERI_GT_EVT2_SRC_SEL 0x7 +#define BIT_PERI_GT_EVT2_SRC_SEL(x) (((x) & BIT_MASK_PERI_GT_EVT2_SRC_SEL) << BIT_SHIFT_PERI_GT_EVT2_SRC_SEL) + + +#define BIT_SHIFT_PERI_GT_EVT2_PULSE_DUR 16 +#define BIT_MASK_PERI_GT_EVT2_PULSE_DUR 0xf +#define BIT_PERI_GT_EVT2_PULSE_DUR(x) (((x) & BIT_MASK_PERI_GT_EVT2_PULSE_DUR) << BIT_SHIFT_PERI_GT_EVT2_PULSE_DUR) + +#define BIT_PERI_GT_EVT1_EN BIT(15) + +#define BIT_SHIFT_PERI_GT_EVT1_SRC_SEL 12 +#define BIT_MASK_PERI_GT_EVT1_SRC_SEL 0x7 +#define BIT_PERI_GT_EVT1_SRC_SEL(x) (((x) & BIT_MASK_PERI_GT_EVT1_SRC_SEL) << BIT_SHIFT_PERI_GT_EVT1_SRC_SEL) + + +#define BIT_SHIFT_PERI_GT_EVT1_PULSE_DUR 8 +#define BIT_MASK_PERI_GT_EVT1_PULSE_DUR 0xf +#define BIT_PERI_GT_EVT1_PULSE_DUR(x) (((x) & BIT_MASK_PERI_GT_EVT1_PULSE_DUR) << BIT_SHIFT_PERI_GT_EVT1_PULSE_DUR) + +#define BIT_PERI_GT_EVT0_EN BIT(7) + +#define BIT_SHIFT_PERI_GT_EVT0_SRC_SEL 4 +#define BIT_MASK_PERI_GT_EVT0_SRC_SEL 0x7 +#define BIT_PERI_GT_EVT0_SRC_SEL(x) (((x) & BIT_MASK_PERI_GT_EVT0_SRC_SEL) << BIT_SHIFT_PERI_GT_EVT0_SRC_SEL) + + +#define BIT_SHIFT_PERI_GT_EVT0_PULSE_DUR 0 +#define BIT_MASK_PERI_GT_EVT0_PULSE_DUR 0xf +#define BIT_PERI_GT_EVT0_PULSE_DUR(x) (((x) & BIT_MASK_PERI_GT_EVT0_PULSE_DUR) << BIT_SHIFT_PERI_GT_EVT0_PULSE_DUR) + + +//2 REG_PERI_EGTIM_CTRL + +#define BIT_SHIFT_PERI_EGTIM_PIN_GROUP2_OPT_SEL 12 +#define BIT_MASK_PERI_EGTIM_PIN_GROUP2_OPT_SEL 0x3 +#define BIT_PERI_EGTIM_PIN_GROUP2_OPT_SEL(x) (((x) & BIT_MASK_PERI_EGTIM_PIN_GROUP2_OPT_SEL) << BIT_SHIFT_PERI_EGTIM_PIN_GROUP2_OPT_SEL) + + +#define BIT_SHIFT_PERI_EGTIM_PIN_GROUP1_OPT_SEL 10 +#define BIT_MASK_PERI_EGTIM_PIN_GROUP1_OPT_SEL 0x3 +#define BIT_PERI_EGTIM_PIN_GROUP1_OPT_SEL(x) (((x) & BIT_MASK_PERI_EGTIM_PIN_GROUP1_OPT_SEL) << BIT_SHIFT_PERI_EGTIM_PIN_GROUP1_OPT_SEL) + + +#define BIT_SHIFT_PERI_EGTIM_PIN_GROUP0_OPT_SEL 8 +#define BIT_MASK_PERI_EGTIM_PIN_GROUP0_OPT_SEL 0x3 +#define BIT_PERI_EGTIM_PIN_GROUP0_OPT_SEL(x) (((x) & BIT_MASK_PERI_EGTIM_PIN_GROUP0_OPT_SEL) << BIT_SHIFT_PERI_EGTIM_PIN_GROUP0_OPT_SEL) + + +//2 REG_NOT_VALID + +#define BIT_SHIFT_PERI_EGTIM_REF_SIG_SEL 4 +#define BIT_MASK_PERI_EGTIM_REF_SIG_SEL 0x3 +#define BIT_PERI_EGTIM_REF_SIG_SEL(x) (((x) & BIT_MASK_PERI_EGTIM_REF_SIG_SEL) << BIT_SHIFT_PERI_EGTIM_REF_SIG_SEL) + +#define BIT_PERI_EGTIM_EN BIT(0) + +//2 REG_NOT_VALID + +//2 REG_PEON_CFG + +//2 REG_PEON_STATUS +#define BIT_PEON_SDIO_ALDN BIT(0) + + +//========== Register Address Definition ==================// +#define REG_PEON_PWR_CTRL 0x0200 +#define REG_PON_ISO_CTRL 0x0204 +#define REG_SOC_FUNC_EN 0x0210 +#define REG_SOC_HCI_COM_FUNC_EN 0x0214 +#define REG_SOC_PERI_FUNC0_EN 0x0218 +#define REG_SOC_PERI_FUNC1_EN 0x021C +#define REG_SOC_PERI_BD_FUNC0_EN 0x0220 +#define REG_PESOC_CLK_CTRL 0x0230 +#define REG_PESOC_PERI_CLK_CTRL0 0x0234 +#define REG_PESOC_PERI_CLK_CTRL1 0x0238 +#define REG_PESOC_CLK_CTRL3 0x023C +#define REG_PESOC_HCI_CLK_CTRL0 0x0240 +#define REG_PESOC_COM_CLK_CTRL1 0x0244 +#define REG_PESOC_HW_ENG_CLK_CTRL 0x0248 +#define REG_PESOC_CLK_SEL 0x0250 +#define REG_SYS_ANACK_CAL_CTRL 0x026C +#define REG_OSC32K_CTRL 0x0270 +#define REG_OSC32K_REG_CTRL0 0x0274 +#define REG_OSC32K_REG_CTRL1 0x0278 +#define REG_THERMAL_METER_CTRL 0x027C +#define REG_UART_MUX_CTRL 0x0280 +#define REG_SPI_MUX_CTRL 0x0284 +#define REG_I2C_MUX_CTRL 0x0288 +#define REG_I2S_MUX_CTRL 0x028C +#define REG_HCI_PINMUX_CTRL 0x02A0 +#define REG_WL_PINMUX_CTRL 0x02A4 +#define REG_BT_PINMUX_CTRL 0x02A8 +#define REG_PWM_PINMUX_CTRL 0x02AC +#define REG_CPU_PERIPHERAL_CTRL 0x02C0 +#define REG_HCI_CTRL_STATUS_0 0x02E0 +#define REG_HCI_CTRL_STATUS_1 0x02E4 +#define REG_PESOC_MEM_CTRL 0x0300 +#define REG_PESOC_SOC_CTRL 0x0304 +#define REG_PESOC_PERI_CTRL 0x0308 +#define REG_GPIO_SHTDN_CTRL 0x0320 +#define REG_GPIO_DRIVING_CTRL 0x0324 +#define REG_GPIO_PULL_CTRL0 0x0330 +#define REG_GPIO_PULL_CTRL1 0x0334 +#define REG_GPIO_PULL_CTRL2 0x0338 +#define REG_GPIO_PULL_CTRL3 0x033C +#define REG_GPIO_PULL_CTRL4 0x0340 +#define REG_GPIO_PULL_CTRL5 0x0344 +#define REG_GPIO_PULL_CTRL6 0x0348 +#define REG_PERI_PWM0_CTRL 0x0360 +#define REG_PERI_PWM1_CTRL 0x0364 +#define REG_PERI_PWM2_CTRL 0x0368 +#define REG_PERI_PWM3_CTRL 0x036C +#define REG_PERI_TIM_EVT_CTRL 0x0370 +#define REG_PERI_EGTIM_CTRL 0x0374 +#define REG_PEON_CFG 0x03F0 +#define REG_PEON_STATUS 0x03F4 + + +#endif // end of "#ifndef __INC_RTL8195A_PERI_ON_H" diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pwm.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pwm.h new file mode 100644 index 0000000..ddf166b --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_pwm.h @@ -0,0 +1,37 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_PWM_H_ +#define _RTL8195A_PWM_H_ + +extern void +HAL_Pwm_SetDuty_8195a( + HAL_PWM_ADAPTER *pPwmAdapt, + u32 period, + u32 pulse_width +); + +extern HAL_Status +HAL_Pwm_Init_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +); + +extern void +HAL_Pwm_Enable_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +); + +extern void +HAL_Pwm_Disable_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +); + + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio.h new file mode 100644 index 0000000..f60ae95 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio.h @@ -0,0 +1,946 @@ + /* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_SDIO_H_ +#define _RTL8195A_SDIO_H_ + +#include "hal_api.h" +#include "hal_util.h" +#if defined(CONFIG_SDIO_BOOT_SIM) || defined(CONFIG_SDIO_BOOT_ROM) +#define SDIO_BOOT_DRIVER 1 // is this SDIO driver works for booting +#else +#include "osdep_api.h" +#define SDIO_BOOT_DRIVER 0 // is this SDIO driver works for booting +#endif +#define SDIO_DEBUG 1 +#define SDIO_MP_MODE 1 // if includes MP mode function +#define SDIO_MAX_WAIT_RX_DMA 100 // Wait RX DMA done +#define SDIO_RX_PKT_SIZE_OVER_16K 0 /* is support SDIO RX packet size > 16K. if true, + a big packet will be transmited via multiple RX_BD */ +#define SDIO_MAILBOX_SIZE 10 // the maximum number of message block can be stored in this mailbox +#define SDIO_PERIODICAL_TIMER_INTERVAL 2000 // in ms, the interval of SDIO periodical timer +#define SDIO_AVG_TP_WIN_SIZE 20 // the number of entry to log the byte count for every periodical timer statistic, to calculate throughput + +#define HAL_SDIO_READ32(addr) HAL_READ32(SDIO_DEVICE_REG_BASE, addr) +#define HAL_SDIO_WRITE32(addr, value) HAL_WRITE32(SDIO_DEVICE_REG_BASE, addr, value) +#define HAL_SDIO_READ16(addr) HAL_READ16(SDIO_DEVICE_REG_BASE, addr) +#define HAL_SDIO_WRITE16(addr, value) HAL_WRITE16(SDIO_DEVICE_REG_BASE, addr, value) +#define HAL_SDIO_READ8(addr) HAL_READ8(SDIO_DEVICE_REG_BASE, addr) +#define HAL_SDIO_WRITE8(addr, value) HAL_WRITE8(SDIO_DEVICE_REG_BASE, addr, value) + +/***** Register Address *****/ +#define REG_SPDIO_TXBD_ADDR 0xA0 // 4 Bytes +#define REG_SPDIO_TXBD_SIZE 0xA4 // 4 Bytes +#define REG_SPDIO_TXBD_WPTR 0xA8 // 2 Bytes +#define REG_SPDIO_TXBD_RPTR 0xAC // 2 Bytes +#define REG_SPDIO_RXBD_ADDR 0xB0 // 4 Bytes +#define REG_SPDIO_RXBD_SIZE 0xB4 // 2 Bytes +#define REG_SPDIO_RXBD_C2H_WPTR 0xB6 // 2 Bytes +#define REG_SPDIO_RXBD_C2H_RPTR 0xB8 // 2 Bytes +#define REG_SPDIO_HCI_RX_REQ 0xBA // 1 Byte +#define REG_SPDIO_CPU_RST_DMA 0xBB // 1 Byte +#define REG_SPDIO_RX_REQ_ADDR 0xBC // 2 Bytes +#define REG_SPDIO_CPU_INT_MASK 0xC0 // 2 Bytes +#define REG_SPDIO_CPU_INT_STAS 0xC2 // 2 Bytes +#define REG_SPDIO_CCPWM 0xC4 // 1 Byts +#define REG_SPDIO_CPU_IND 0xC5 // 1 Byte +#define REG_SPDIO_CCPWM2 0xC6 // 2 Bytes +#define REG_SPDIO_CPU_H2C_MSG 0xC8 // 4 Bytes +#define REG_SPDIO_CPU_C2H_MSG 0xCC // 4 Bytes +#define REG_SPDIO_CRPWM 0xD0 // 1 Bytes +#define REG_SPDIO_CRPWM2 0xD2 // 2 Bytes +#define REG_SPDIO_AHB_DMA_CTRL 0xD4 // 4 Bytes +#define REG_SPDIO_RXBD_CNT 0xD8 // 4 Bytes +#define REG_SPDIO_TX_BUF_UNIT_SZ 0xD9 // 1 Bytes +#define REG_SPDIO_RX_BD_FREE_CNT 0xDA // 2 Bytes +#define REG_SPDIO_CPU_H2C_MSG_EXT 0xDC // 4 Bytes +#define REG_SPDIO_CPU_C2H_MSG_EXT 0xE0 // 4 Bytes + +// Register REG_SPDIO_CPU_RST_DMA +#define BIT_CPU_RST_SDIO_DMA BIT(7) + +// Register REG_SPDIO_CPU_INT_MASK, REG_SPDIO_CPU_INT_STAS +#define BIT_TXFIFO_H2C_OVF BIT(0) +#define BIT_H2C_BUS_RES_FAIL BIT(1) +#define BIT_H2C_DMA_OK BIT(2) +#define BIT_C2H_DMA_OK BIT(3) +#define BIT_H2C_MSG_INT BIT(4) +#define BIT_RPWM1_INT BIT(5) +#define BIT_RPWM2_INT BIT(6) +#define BIT_SDIO_RST_CMD_INT BIT(7) +#define BIT_RXBD_FLAG_ERR_INT BIT(8) +#define BIT_RX_BD_AVAI_INT BIT(9) +#define BIT_HOST_WAKE_CPU_INT BIT(10) + +// Register REG_SPDIO_CPU_IND +#define BIT_SYSTEM_TRX_RDY_IND BIT(0) + +// Register REG_SPDIO_HCI_RX_REQ +#define BIT_HCI_RX_REQ BIT(0) + +/* Register for SOC_HCI_COM_FUN_EN */ +#define BIT_SOC_HCI_SDIOD_OFF_EN BIT(1) // SDIO Function Block on Power_Off domain +#define BIT_SOC_HCI_SDIOD_ON_EN BIT(0) // SDIO Function Block on Power_On domain + +/* Register REG_PESOC_HCI_CLK_CTRL0 */ +#define BIT_SOC_SLPCK_SDIO_HST_EN BIT(3) // SDIO_HST clock enable when CPU sleep command +#define BIT_SOC_ACTCK_SDIO_HST_EN BIT(2) // SDIO_HST clock enable in CPU run mode +#define BIT_SOC_SLPCK_SDIO_DEV_EN BIT(1) // SDIO_DEV clock enable when CPU sleep command +#define BIT_SOC_ACTCK_SDIO_DEV_EN BIT(0) // SDIO_DEV clock enable in CPU run mode + +/***** Structer for each Register *****/ +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) +// Little Endian +// Register REG_SPDIO_HCI_RX_REQ @ 0xBA +typedef struct _SPDIO_HCI_RX_REQ { + u8 HCI_RX_REQ:1; /* bit[0], CPU trigger this bit to enable SDIO IP RX transfer by fetch BD info */ + u8 Reserved:7; /* bit[7:1], Reserved */ +} SPDIO_HCI_RX_REQ, *PSPDIO_HCI_RX_REQ; + +// Register REG_SPDIO_CPU_RST_DMA @ 0xBB +typedef struct _SPDIO_CPU_RST_DMA { + u8 Reserved:7; /* bit[6:0], Reserved */ + u8 CPU_RST_SDIO:1; /* bit[7], CPU set this bit to reset SDIO DMA */ +} SPDIO_CPU_RST_DMA, *PSPDIO_CPU_RST_DMA; + +// Register REG_SPDIO_CPU_INT_MASK @ 0xC0 +typedef struct _SPDIO_CPU_INT_MASK { + u16 TXFIFO_H2C_OVF:1; /* bit[0], set 0 to mask TXFIFO_H2C_OVF_INT */ + u16 H2C_BUS_RES_FAIL:1; /* bit[1], set 0 to mask H2C_BUS_RES_FAIL_INT */ + u16 H2C_DMA_OK:1; /* bit[2], set 0 to mask H2C_DMA_OK_INT */ + u16 C2H_DMA_OK:1; /* bit[3], set 0 to mask C2H_DMA_OK_INT */ + u16 H2C_MSG_INT:1; /* bit[4], set 0 to mask H2C_MSG_INT_INT */ + u16 RPWM_INT:1; /* bit[5], set 0 to mask RPWM_INT */ + u16 RPWM2_INT:1; /* bit[6], set 0 to mask RPWM2_INT */ + u16 SDIO_RST_CMD_INT:1; /* bit[7], set 0 to mask SDIO_RST_CMD_INT */ + u16 BD_FLAG_ERR_INT:1; /* bit[8], set 0 to mask BD_FLAG_ERR_INT */ + u16 Reserved:7; /* bit[15:9], Reserved */ +} SPDIO_CPU_INT_MASK, *PSPDIO_CPU_INT_MASK; + +// Register REG_SPDIO_CPU_INT_STATUS @ 0xC2 +typedef struct _SPDIO_CPU_INT_STAS { + u16 TXFIFO_H2C_OVF:1; /* bit[0], set 0 to mask TXFIFO_H2C_OVF_INT */ + u16 H2C_BUS_RES_FAIL:1; /* bit[1], set 0 to mask H2C_BUS_RES_FAIL_INT */ + u16 H2C_DMA_OK:1; /* bit[2], set 0 to mask H2C_DMA_OK_INT */ + u16 C2H_DMA_OK:1; /* bit[3], set 0 to mask C2H_DMA_OK_INT */ + u16 H2C_MSG_INT:1; /* bit[4], set 0 to mask H2C_MSG_INT_INT */ + u16 RPWM_INT:1; /* bit[5], set 0 to mask RPWM_INT */ + u16 RPWM2_INT:1; /* bit[6], set 0 to mask RPWM2_INT */ + u16 SDIO_RST_CMD_INT:1; /* bit[7], set 0 to mask SDIO_RST_CMD_INT */ + u16 BD_FLAG_ERR_INT:1; /* bit[8], set 0 to mask BD_FLAG_ERR_INT */ + u16 Reserved:7; /* bit[15:9], Reserved */ +} SPDIO_CPU_INT_STAS, *PSPDIO_CPU_INT_STAS; + +// Register REG_SPDIO_CCPWM @ 0xC4 +typedef struct _SPDIO_CCPWM { + u8 :1; /* bit[0] */ + u8 WLAN_TRX:1; /* bit[1], 0: WLAN Off; 1: WLAN On */ + u8 RPS_ST:1; /* bit[2], 0/1: AP Register Sleep/Active state */ + u8 WWLAN:1; /* bit[3], 0/1: "Wake on WLAN"/"Normal" state */ + u8 Reserved:3; /* bit[6:4], Reserved */ + u8 TOGGLING:1; /* bit[7], issue interrupt when 0->1 or 1->0 */ +} SPDIO_CCPWM, *PSPDIO_CCPWM; + +// Register REG_SPDIO_CPU_IND @ 0xC5 +typedef struct _SPDIO_CPU_IND { + u8 SYS_TRX_RDY:1; /* bit[0], To indicate the Host system that CPU is ready for TRX + , to be sync to 0x87[0] */ + u8 Reserved:7; /* bit[7:1], Reserved */ +} SPDIO_CPU_IND, *PSPDIO_CPU_IND; + +// Register REG_SPDIO_CPU_H2C_MSG @ 0xC8 +typedef struct _SPDIO_CPU_H2C_MSG { + u32 CPU_H2C_MSG:30; /* bit[30:0], Host CPU to FW message, sync from REG_SDIO_H2C_MSG */ + u32 Reserved:1; /* bit[31], Reserved */ +} SPDIO_CPU_H2C_MSG, *PSPDIO_CPU_H2C_MSG; + +// Register REG_SPDIO_CPU_C2H_MSG @ 0xCC +typedef struct _SPDIO_CPU_C2H_MSG { + u32 CPU_C2H_MSG:30; /* bit[30:0], FW to Host CPU message, sync to REG_SDIO_C2H_MSG */ + u32 Reserved:1; /* bit[31], Reserved */ +} SPDIO_CPU_C2H_MSG, *PSPDIO_CPU_C2H_MSG; + +// Register REG_SPDIO_CRPWM @ 0xD0 +typedef struct _SPDIO_CRPWM { + u8 :1; /* bit[0] */ + u8 WLAN_TRX:1; /* bit[1], 0: WLAN Off; 1: WLAN On */ + u8 RPS_ST:1; /* bit[2], 0/1: AP Register Sleep/Active state */ + u8 WWLAN:1; /* bit[3], 0/1: "Wake on WLAN"/"Normal" state */ + u8 Reserved:3; /* bit[6:4], Reserved */ + u8 TOGGLING:1; /* bit[7], issue interrupt when 0->1 or 1->0 */ +} SPDIO_CRPWM, *PSPDIO_CRPWM; + +// Register REG_SPDIO_AHB_DMA_CTRL @ 0xD4 +typedef struct _SPDIO_AHB_DMA_CTRL { + u32 TXFF_WLEVEL:7; /* bit[6:0], SPDIO TX FIFO water level */ + u32 :1; /* bit[7] */ + u32 RXFF_WLEVEL:7; /* bit[14:8], SPDIO RX FIFO water level */ + u32 :1; /* bit[15] */ + u32 AHB_DMA_CS:4; /* bit[19:16], AHB DMA state */ + u32 :1; /* bit[20] */ + u32 AHB_MASTER_RDY:1; /* bit[21], AHB Master Hready signal */ + u32 AHB_DMA_TRANS:2; /* bit[23:22], AHB DMA Trans value, for debugging */ + u32 AHB_BUSY_WAIT_CNT:4; /* bit[27:24], timeout for AHB controller to wait busy */ + u32 AHB_BURST_TYPE:3; /* bit[30:28], AHB burst type */ + u32 DISPATCH_TXAGG:1; /* bit[31], Enable to dispatch aggregated TX packet */ +} SPDIO_AHB_DMA_CTRL, *PSPDIO_AHB_DMA_CTRL; + +#else /* else of '#if LITTLE_ENDIAN' */ +// Big Endian +typedef struct _SPDIO_HCI_RX_REQ { + u8 Reserved:7; /* bit[7:1], Reserved */ + u8 HCI_RX_REQ:1; /* bit[0], CPU trigger this bit to enable SDIO IP RX transfer by fetch BD info */ +} SPDIO_HCI_RX_REQ, *PSPDIO_HCI_RX_REQ; + +// Register REG_SPDIO_CPU_RST_DMA @ 0xBB +typedef struct _SPDIO_CPU_RST_DMA { + u8 CPU_RST_SDIO:1; /* bit[7], CPU set this bit to reset SDIO DMA */ + u8 Reserved:7; /* bit[6:0], Reserved */ +} SPDIO_CPU_RST_DMA, *PSPDIO_CPU_RST_DMA; + +// Register REG_SPDIO_CPU_INT_MASK @ 0xC0 +typedef struct _SPDIO_CPU_INT_MASK { + u16 Reserved:7; /* bit[15:9], Reserved */ + u16 BD_FLAG_ERR_INT:1; /* bit[8], set 0 to mask BD_FLAG_ERR_INT */ + u16 SDIO_RST_CMD_INT:1; /* bit[7], set 0 to mask SDIO_RST_CMD_INT */ + u16 RPWM2_INT:1; /* bit[6], set 0 to mask RPWM2_INT */ + u16 RPWM_INT:1; /* bit[5], set 0 to mask RPWM_INT */ + u16 H2C_MSG_INT:1; /* bit[4], set 0 to mask H2C_MSG_INT_INT */ + u16 C2H_DMA_OK:1; /* bit[3], set 0 to mask C2H_DMA_OK_INT */ + u16 H2C_DMA_OK:1; /* bit[2], set 0 to mask H2C_DMA_OK_INT */ + u16 H2C_BUS_RES_FAIL:1; /* bit[1], set 0 to mask H2C_BUS_RES_FAIL_INT */ + u16 TXFIFO_H2C_OVF:1; /* bit[0], set 0 to mask TXFIFO_H2C_OVF_INT */ +} SPDIO_CPU_INT_MASK, *PSPDIO_CPU_INT_MASK; + +// Register REG_SPDIO_CPU_INT_STAS @ 0xC2 +typedef struct _SPDIO_CPU_INT_STAS { + u16 Reserved:7; /* bit[15:9], Reserved */ + u16 BD_FLAG_ERR_INT:1; /* bit[8], set 0 to mask BD_FLAG_ERR_INT */ + u16 SDIO_RST_CMD_INT:1; /* bit[7], set 0 to mask SDIO_RST_CMD_INT */ + u16 RPWM2_INT:1; /* bit[6], set 0 to mask RPWM2_INT */ + u16 RPWM_INT:1; /* bit[5], set 0 to mask RPWM_INT */ + u16 H2C_MSG_INT:1; /* bit[4], set 0 to mask H2C_MSG_INT_INT */ + u16 C2H_DMA_OK:1; /* bit[3], set 0 to mask C2H_DMA_OK_INT */ + u16 H2C_DMA_OK:1; /* bit[2], set 0 to mask H2C_DMA_OK_INT */ + u16 H2C_BUS_RES_FAIL:1; /* bit[1], set 0 to mask H2C_BUS_RES_FAIL_INT */ + u16 TXFIFO_H2C_OVF:1; /* bit[0], set 0 to mask TXFIFO_H2C_OVF_INT */ +} SPDIO_CPU_INT_STAS, *PSPDIO_CPU_INT_STAS; + +// Register REG_SPDIO_CCPWM @ 0xC4 +typedef struct _SPDIO_CCPWM { + u8 TOGGLING:1; /* bit[7], issue interrupt when 0->1 or 1->0 */ + u8 Reserved:3; /* bit[6:4], Reserved */ + u8 WWLAN:1; /* bit[3], 0/1: "Wake on WLAN"/"Normal" state */ + u8 RPS_ST:1; /* bit[2], 0/1: AP Register Sleep/Active state */ + u8 WLAN_TRX:1; /* bit[1], 0: WLAN Off; 1: WLAN On */ + u8 :1; /* bit[0] */ +} SPDIO_CCPWM, *PSPDIO_CCPWM; + +// Register REG_SPDIO_CPU_IND @ 0xC5 +typedef struct _SPDIO_CPU_IND { + u8 Reserved:7; /* bit[7:1], Reserved */ + u8 SYS_TRX_RDY:1; /* bit[0], To indicate the Host system that CPU is ready for TRX + , to be sync to 0x87[0] */ +} SPDIO_CPU_IND, *PSPDIO_CPU_IND; + +// Register REG_SPDIO_CPU_H2C_MSG @ 0xC8 +typedef struct _SPDIO_CPU_H2C_MSG { + u32 Reserved:1; /* bit[31], Reserved */ + u32 CPU_H2C_MSG:30; /* bit[30:0], Host CPU to FW message */ +} SPDIO_CPU_H2C_MSG, *PSPDIO_CPU_H2C_MSG; + +// Register REG_SPDIO_CPU_C2H_MSG @ 0xCC +typedef struct _SPDIO_CPU_C2H_MSG { + u32 Reserved:1; /* bit[31], Reserved */ + u32 CPU_C2H_MSG:30; /* bit[30:0], FW to Host CPU message, sync to REG_SDIO_C2H_MSG */ +} SPDIO_CPU_C2H_MSG, *PSPDIO_CPU_C2H_MSG; + +// Register REG_SPDIO_CRPWM @ 0xD0 +typedef struct _SPDIO_CRPWM { + u8 TOGGLING:1; /* bit[7], issue interrupt when 0->1 or 1->0 */ + u8 Reserved:3; /* bit[6:4], Reserved */ + u8 WWLAN:1; /* bit[3], 0/1: "Wake on WLAN"/"Normal" state */ + u8 RPS_ST:1; /* bit[2], 0/1: AP Register Sleep/Active state */ + u8 WLAN_TRX:1; /* bit[1], 0: WLAN Off; 1: WLAN On */ + u8 :1; /* bit[0] */ +} SPDIO_CRPWM, *PSPDIO_CRPWM; + +// Register REG_SPDIO_AHB_DMA_CTRL @ 0xD4 +typedef struct _SPDIO_AHB_DMA_CTRL { + u32 DISPATCH_TXAGG:1; /* bit[31], Enable to dispatch aggregated TX packet */ + u32 AHB_BURST_TYPE:3; /* bit[30:28], AHB burst type */ + u32 AHB_BUSY_WAIT_CNT:4; /* bit[27:24], timeout for AHB controller to wait busy */ + u32 AHB_DMA_TRANS:2; /* bit[23:22], AHB DMA Trans value, for debugging */ + u32 AHB_MASTER_RDY:1; /* bit[21], AHB Master Hready signal */ + u32 :1; /* bit[20] */ + u32 AHB_DMA_CS:4; /* bit[19:16], AHB DMA state */ + u32 :1; /* bit[15] */ + u32 RXFF_WLEVEL:7; /* bit[14:8], SPDIO RX FIFO water level */ + u32 :1; /* bit[7] */ + u32 TXFF_WLEVEL:7; /* bit[6:0], SPDIO TX FIFO water level */ +} SPDIO_AHB_DMA_CTRL, *PSPDIO_AHB_DMA_CTRL; + +#endif /* end of '#if LITTLE_ENDIAN' */ + + +//#define TX_FIFO_ADDR 0x0000 +//#define TX_FIFO_SIZE 0x8000 + +//TX BD setting +#if SDIO_BOOT_DRIVER +// for build ROM library +#define SDIO_TX_BD_NUM 2 // Number of TX BD +#define SDIO_TX_BD_BUF_SIZE (2048+32) // the size of a TX BD pointed buffer, WLan header = 26 bytes +#define SDIO_TX_PKT_NUM 10 // Number of TX packet handler + +//RX BD setting +#define RX_BD_FREE_TH 4 // trigger the interrupt when free RX BD over this threshold + +#define MAX_RX_BD_BUF_SIZE 16380 // the Maximum size for a RX_BD point to, make it 4-bytes aligned + +#define SDIO_RX_PKT_NUM 3 // Number of RX packet handler +//#define SDIO_RX_BD_NUM 10 // Number of RX BD, to make 32K of bus aggregation, it needs 22 RX_BD at least +#define SDIO_RX_BD_NUM (SDIO_RX_PKT_NUM*2) // Number of RX BD, to make 32K of bus aggregation, it needs 22 RX_BD at least +#define SDIO_RX_BD_BUF_SIZE (2048+24) // the size of a RX BD pointed buffer, sizeof(RX Desc) = 26 bytes +#define MIN_RX_BD_SEND_PKT 2 /* the minum needed RX_BD to send a Packet to Host, we need 2: + one for RX_Desc, the other for payload */ + +// CCPWM2 bit map definition for Firmware download +#define SDIO_INIT_DONE (BIT0) +#define SDIO_MEM_WR_DONE (BIT1) +#define SDIO_MEM_RD_DONE (BIT2) +#define SDIO_MEM_ST_DONE (BIT3) + +#define SDIO_CPWM2_TOGGLE (BIT15) + +#else +#define SDIO_TX_BD_NUM 24 // Number of TX BD +#define SDIO_TX_BD_BUF_SIZE (2048+32) // the size of a TX BD pointed buffer, WLan header = 26 bytes +#define SDIO_TX_PKT_NUM 128 // Number of TX packet handler + +//RX BD setting +#define RX_BD_FREE_TH 5 // trigger the interrupt when free RX BD over this threshold + +#define SDIO_RX_BD_BUF_SIZE 2048 +#define MAX_RX_BD_BUF_SIZE 16380 // the Maximum size for a RX_BD point to, make it 4-bytes aligned + +//#define SDIO_TX_FIFO_SIZE (1024*64) // 64K +#define SDIO_RX_BD_NUM 24 // Number of RX BD, to make 32K of bus aggregation, it needs 22 RX_BD at least +#define SDIO_RX_PKT_NUM 128 // Number of RX packet handler +#define MIN_RX_BD_SEND_PKT 2 /* the minum needed RX_BD to send a Packet to Host, we need 2: + one for RX_Desc, the other for payload */ +#endif + +#define SDIO_IRQ_PRIORITY 10 + +/* SDIO Events */ +#define SDIO_EVENT_IRQ BIT(0) // Interrupt triggered +#define SDIO_EVENT_RX_PKT_RDY BIT(1) // A new SDIO packet ready +#define SDIO_EVENT_C2H_DMA_DONE BIT(2) // Interrupt of C2H DMA done triggered +#define SDIO_EVENT_DUMP BIT(3) // SDIO status dump periodically Enable +#define SDIO_EVENT_TXBD_REFILL BIT(4) // To refill TX BD buffer +#define SDIO_EVENT_EXIT BIT(28) // Request to exit the SDIO task +#define SDIO_EVENT_MP_STOPPED BIT(29) // The SDIO task is stopped +#define SDIO_EVENT_TX_STOPPED BIT(30) // The SDIO task is stopped +#define SDIO_EVENT_RX_STOPPED BIT(31) // The SDIO task is stopped + +#define SDIO_TASK_PRIORITY 1 // it can be 0(lowest) ~ configMAX_PRIORITIES-1(highest) +#define SDIO_MP_TASK_PRIORITY 2 // it can be 0(lowest) ~ configMAX_PRIORITIES-1(highest) +//#if SDIO_TASK_PRIORITY > (configMAX_PRIORITIES - 1) +#if SDIO_TASK_PRIORITY > (4 - 1) +#error "SDIO Task Priority Should be 0~(configMAX_PRIORITIES-1)" +#endif + +//#define TX_RX_PACKET_SIZE 0x144 + +typedef struct _SDIO_TX_BD_ { + u32 Address; /* The TX buffer physical address, it must be 4-bytes aligned */ +}SDIO_TX_BD, *PSDIO_TX_BD; + +#define TX_BD_STRUCTURE_SIZE (sizeof(SDIO_TX_BD)) + + +/* The RX Buffer Descriptor format */ + +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) +typedef struct _SDIO_RX_BD_ { + u32 BuffSize:14; /* bit[13:0], RX Buffer Size, Maximum 16384-1 */ + u32 LS:1; /* bit[14], is the Last Segment ? */ + u32 FS:1; /* bit[15], is the First Segment ? */ + u32 Seq:16; /* bit[31:16], The sequence number, it's no use for now */ + u32 PhyAddr; /* The RX buffer physical address, it must be 4-bytes aligned */ +} SDIO_RX_BD, *PSDIO_RX_BD; +#else +typedef struct _SDIO_RX_BD_ { + u32 Seq:16; /* bit[31:16], The sequence number, be used for ?? */ + u32 FS:1; /* bit[15], is the First Segment ? */ + u32 LS:1; /* bit[14], is the Last Segment ? */ + u32 BuffSize:14; /* bit[13:0], RX Buffer Size, Maximum 16384 */ + u32 PhyAddr; /* The RX buffer physical address, it must be 4-bytes aligned */ +} SDIO_RX_BD, *PSDIO_RX_BD; +#endif +#define RX_BD_STRUCTURE_SIZE (sizeof(SDIO_RX_BD)) + +// TODO: This data structer just for test, we should modify it for the normal driver +typedef struct _SDIO_TX_DESC{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 txpktsize:16; // bit[15:0] + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number +#else + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 txpktsize:16; // bit[15:0] +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the packet type + u32 rsvd0:24; +#else + u32 rsvd0:24; + u32 type:8; // bit[7:0], the packet type +#endif + + // u4Byte 2 + u32 rsvd1; + + // u4Byte 3 + u32 rsvd2; + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_TX_DESC, *PSDIO_TX_DESC; + +// TX Desc for Memory Write command +typedef struct _SDIO_TX_DESC_MW{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 txpktsize:16; // bit[15:0] + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number +#else + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 txpktsize:16; // bit[15:0] +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the packet type + u32 reply:1; // bit[8], request to send a reply message + u32 rsvd0:23; +#else + u32 rsvd0:23; + u32 reply:1; // bit[8], request to send a reply message + u32 type:8; // bit[7:0], the packet type +#endif + + // u4Byte 2 + u32 start_addr; // memory write start address + + // u4Byte 3 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 write_len:16; // bit[15:0], the length to write + u32 rsvd2:16; // bit[31:16] +#else + u32 rsvd2:16; // bit[31:16] + u32 write_len:16; // bit[15:0], the length to write +#endif + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_TX_DESC_MW, *PSDIO_TX_DESC_MW; + +// TX Desc for Memory Read command +typedef struct _SDIO_TX_DESC_MR{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 txpktsize:16; // bit[15:0] + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number +#else + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 txpktsize:16; // bit[15:0] +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the packet type + u32 rsvd0:24; +#else + u32 rsvd0:24; + u32 type:8; // bit[7:0], the packet type +#endif + + // u4Byte 2 + u32 start_addr; // memory write start address + + // u4Byte 3 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 read_len:16; // bit[15:0], the length to read + u32 rsvd2:16; // bit[31:16] +#else + u32 rsvd2:16; // bit[31:16] + u32 read_len:16; // bit[15:0], the length to read +#endif + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_TX_DESC_MR, *PSDIO_TX_DESC_MR; + +// TX Desc for Memory Set command +typedef struct _SDIO_TX_DESC_MS{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 txpktsize:16; // bit[15:0] + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number +#else + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 txpktsize:16; // bit[15:0] +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the packet type + u32 data:8; // bit[8:15], the value to be written to the memory + u32 reply:1; // bit[16], request to send a reply message + u32 rsvd0:15; +#else + u32 rsvd0:15; + u32 reply:1; // bit[16], request to send a reply message + u32 data:8; // bit[8:15], the value to be written to the memory + u32 type:8; // bit[7:0], the packet type +#endif + + // u4Byte 2 + u32 start_addr; // memory write start address + + // u4Byte 3 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 write_len:16; // bit[15:0], the length to write + u32 rsvd2:16; // bit[31:16] +#else + u32 rsvd2:16; // bit[31:16] + u32 write_len:16; // bit[15:0], the length to write +#endif + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_TX_DESC_MS, *PSDIO_TX_DESC_MS; + +// TX Desc for Jump to Start command +typedef struct _SDIO_TX_DESC_JS{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 txpktsize:16; // bit[15:0] + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number +#else + u32 bus_agg_num:8; // bit[31:24], the bus aggregation number + u32 offset:8; // bit[23:16], store the sizeof(SDIO_TX_DESC) + u32 txpktsize:16; // bit[15:0] +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the packet type + u32 rsvd0:24; +#else + u32 rsvd0:24; + u32 type:8; // bit[7:0], the packet type +#endif + + // u4Byte 2 + u32 start_fun; // the pointer of the startup function + + // u4Byte 3 + u32 rsvd2; + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_TX_DESC_JS, *PSDIO_TX_DESC_JS; + + +#define SIZE_TX_DESC (sizeof(SDIO_TX_DESC)) +// define the TX BD buffer size with unite of 64 byets +/* Be carefull!! the setting of hardware's TX BD buffer size may exceed the real size of + the TX BD buffer size, and then it may cause the hardware DMA write the buffer overflow */ +#define SDIO_TX_BUF_SZ_UNIT 64 +#define SDIO_TX_BD_BUF_USIZE ((((SDIO_TX_BD_BUF_SIZE+sizeof(SDIO_TX_DESC)-1)/SDIO_TX_BUF_SZ_UNIT)+1)&0xff) + +typedef struct _SDIO_TX_BD_BUFFER_ { + SDIO_TX_DESC TX_Desc; + u8 TX_Buffer[SDIO_TX_BD_BUF_SIZE]; +}SDIO_TX_BD_BUFFER, *PSDIO_TX_BD_BUFFER; + + +// TODO: This data structer just for test, we should modify it for the normal driver +typedef struct _SDIO_RX_DESC{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 pkt_len:16; // bit[15:0], the packet size + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 rsvd0:6; // bit[29:24] + u32 icv:1; // bit[30], ICV error + u32 crc:1; // bit[31], CRC error +#else + u32 crc:1; // bit[31], CRC error + u32 icv:1; // bit[30], ICV error + u32 rsvd0:6; // bit[29:24] + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 pkt_len:16; // bit[15:0], the packet size +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the type of this packet + u32 rsvd1:24; // bit[31:8] +#else + u32 rsvd1:24; // bit[31:8] + u32 type:8; // bit[7:0], the type of this packet +#endif + + // u4Byte 2 + u32 rsvd2; + + // u4Byte 3 + u32 rsvd3; + + // u4Byte 4 + u32 rsvd4; + + // u4Byte 5 + u32 rsvd5; +} SDIO_RX_DESC, *PSDIO_RX_DESC; + +// For memory read command +typedef struct _SDIO_RX_DESC_MR{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 pkt_len:16; // bit[15:0], the packet size + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 rsvd0:8; // bit[31:24] +#else + u32 rsvd0:8; // bit[31:24] + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 pkt_len:16; // bit[15:0], the packet size +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the type of this packet + u32 rsvd1:24; // bit[31:8] +#else + u32 rsvd1:24; // bit[31:8] + u32 type:8; // bit[7:0], the type of this packet +#endif + + // u4Byte 2 + u32 start_addr; + + // u4Byte 3 + u32 rsvd2; + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_RX_DESC_MR, *PSDIO_RX_DESC_MR; + +// For memory write reply command +typedef struct _SDIO_RX_DESC_MW{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 pkt_len:16; // bit[15:0], the packet size + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 rsvd0:8; // bit[31:24] +#else + u32 rsvd0:8; // bit[31:24] + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 pkt_len:16; // bit[15:0], the packet size +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the type of this packet + u32 rsvd1:24; // bit[31:8] +#else + u32 rsvd1:24; // bit[31:8] + u32 type:8; // bit[7:0], the type of this packet +#endif + + // u4Byte 2 + u32 start_addr; + + // u4Byte 3 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 write_len:16; // bit[15:0], the type of this packet + u32 result:8; // bit[23:16], the result of memory write command + u32 rsvd2:8; // bit[31:24] +#else + u32 rsvd2:8; // bit[31:24] + u32 result:8; // bit[23:16], the result of memory write command + u32 write_len:16; // bit[15:0], the type of this packet +#endif + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_RX_DESC_MW, *PSDIO_RX_DESC_MW; + +// For memory set reply command +typedef struct _SDIO_RX_DESC_MS{ + // u4Byte 0 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 pkt_len:16; // bit[15:0], the packet size + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 rsvd0:8; // bit[31:24] +#else + u32 rsvd0:8; // bit[31:24] + u32 offset:8; // bit[23:16], the offset from the packet start to the buf start, also means the size of RX Desc + u32 pkt_len:16; // bit[15:0], the packet size +#endif + + // u4Byte 1 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 type:8; // bit[7:0], the type of this packet + u32 rsvd1:24; // bit[31:8] +#else + u32 rsvd1:24; // bit[31:8] + u32 type:8; // bit[7:0], the type of this packet +#endif + + // u4Byte 2 + u32 start_addr; + + // u4Byte 3 +#if (SYSTEM_ENDIAN==PLATFORM_LITTLE_ENDIAN) + u32 write_len:16; // bit[15:0], the type of this packet + u32 result:8; // bit[23:16], the result of memory write command + u32 rsvd2:8; // bit[31:24] +#else + u32 rsvd2:8; // bit[31:24] + u32 result:8; // bit[23:16], the result of memory write command + u32 write_len:16; // bit[15:0], the type of this packet +#endif + + // u4Byte 4 + u32 rsvd3; + + // u4Byte 5 + u32 rsvd4; +} SDIO_RX_DESC_MS, *PSDIO_RX_DESC_MS; + +#define SIZE_RX_DESC (sizeof(SDIO_RX_DESC)) + +typedef struct _SDIO_RX_BD_BUFFER_ { + SDIO_RX_DESC RX_Desc; + u8 RX_Buffer[SDIO_RX_BD_BUF_SIZE]; +}SDIO_RX_BD_BUFFER, *PSDIO_RX_BD_BUFFER; + + +/* The data structer for a packet fordwarding to the WLan driver to transmit it */ +// TODO: This data structer just for test, we may need modify it for the normal driver +typedef struct _SDIO_TX_PACKET_ { + u8 *pHeader; // Point to the 1st byte of the packets + u16 PktSize; // the size (bytes) of this packet + _LIST list; // the link list to chain packets + u8 isDyna; // is Dynamic allocated +} SDIO_TX_PACKET, *PSDIO_TX_PACKET; + +/* the data structer to bind a TX_BD with a TX Packet */ +typedef struct _SDIO_TX_BD_HANDLE_ { + SDIO_TX_BD *pTXBD; // Point to the TX_BD buffer + SDIO_TX_PACKET *pPkt; // point to the Tx Packet + u8 isPktEnd; // For a packet over 1 BD , this flag to indicate is this BD contains a packet end + u8 isFree; // is this TX BD free +} SDIO_TX_BD_HANDLE, *PSDIO_TX_BD_HANDLE; + +/* The data structer for a packet which from the WLan driver to send to the Host */ +// TODO: This data structer just for test, we may need modify it for the normal driver + +#if SDIO_BOOT_DRIVER +typedef struct _SDIO_RX_PACKET_ { +// SDIO_RX_DESC RxDesc; // The RX Descriptor for this packet, to be send to Host ahead this packet + u8 *pData; // point to the head of payload of this packet + u16 Offset; // the offset from the pData to the payload buffer + _LIST list; // the link list to chain packets + u8 PktBuf[SDIO_RX_BD_BUF_SIZE]; // the Rx_Desc + payload data buffer, the first 24 bytes is reserved for RX_DESC +} SDIO_RX_PACKET, *PSDIO_RX_PACKET; +#else +typedef struct _SDIO_RX_PACKET_ { + SDIO_RX_DESC RxDesc; // The RX Descriptor for this packet, to be send to Host ahead this packet + u8 *pData; // point to the head of payload of this packet + u16 Offset; // the offset from the pData to the payload buffer + _LIST list; // the link list to chain packets + u8 isDyna; // is Dynamic allocated +} SDIO_RX_PACKET, *PSDIO_RX_PACKET; +#endif + +/* the data structer to bind a RX_BD with a RX Packet */ +typedef struct _SDIO_RX_BD_HANDLE_ { + SDIO_RX_BD *pRXBD; // Point to the RX_BD buffer + SDIO_RX_PACKET *pPkt; // point to the Rx Packet + u8 isPktEnd; // For a packet over 1 BD , this flag to indicate is this BD contains a packet end + u8 isFree; // is this RX BD free (DMA done and its RX packet has been freed) +} SDIO_RX_BD_HANDLE, *PSDIO_RX_BD_HANDLE; + +#if SDIO_MP_MODE +typedef struct _SDIO_MP_CMD_ { + u8 cmd_name[16]; + u32 cmd_type; +} SDIO_MP_CMD, *PSDIO_MP_CMD; + +typedef enum _SDIO_MP_CMD_TYPE_{ + SDIO_MP_START=1, + SDIO_MP_STOP=2, + SDIO_MP_LOOPBACK=3, + SDIO_MP_STATUS=4, + SDIO_MP_READ_REG8=5, + SDIO_MP_READ_REG16=6, + SDIO_MP_READ_REG32=7, + SDIO_MP_WRITE_REG8=8, + SDIO_MP_WRITE_REG16=9, + SDIO_MP_WRITE_REG32=10, + SDIO_MP_WAKEUP=11, // wakeup the SDIO task manually, for debugging + SDIO_MP_DUMP=12, // start/stop to dump the SDIO status periodically + SDIO_MP_CTX=13, // setup continue TX test + SDIO_MP_CRX=14, // setup continue RX test + SDIO_MP_CRX_DA=15, // setup continue RX with dynamic allocate RX Buf test + SDIO_MP_CRX_STOP=16, // setup continue RX test + SDIO_MP_DBG_MSG=17, // Debug message On/Off + +}SDIO_MP_CMD_TYPE; + +typedef enum _SDIO_CRX_MODE_{ + SDIO_CRX_STATIC_BUF = 1, + SDIO_CRX_DYNA_BUF = 2, +} SDIO_CRX_MODE; + +typedef struct _SDIO_MP_RX_PACKET_ { + _LIST list; // this member MUST be the 1st one, the link list to chain packets + u8 *pData; // point to the head of payload of this packet + u16 Offset; // the offset from the pData to the payload + u16 DataLen; // the data length of this packet +} SDIO_MP_RX_PACKET, *PSDIO_MP_RX_PACKET; + +#endif // end of '#if SDIO_MP_MODE' + +#define SDIO_CMD_TX_ETH 0x83 // request to TX a 802.3 packet +#define SDIO_CMD_TX_WLN 0x81 // request to TX a 802.11 packet +#define SDIO_CMD_H2C 0x11 // H2C(host to device) command packet +#define SDIO_CMD_MEMRD 0x51 // request to read a block of memory data +#define SDIO_CMD_MEMWR 0x53 // request to write a block of memory +#define SDIO_CMD_MEMST 0x55 // request to set a block of memory with a value +#define SDIO_CMD_STARTUP 0x61 // request to jump to the start up function + +#define SDIO_CMD_RX_ETH 0x82 // indicate a RX 802.3 packet +#define SDIO_CMD_RX_WLN 0x80 // indicate a RX 802.11 packet +#define SDIO_CMD_C2H 0x10 // C2H(device to host) command packet +#define SDIO_CMD_MEMRD_RSP 0x50 // response to memory block read command +#define SDIO_CMD_MEMWR_RSP 0x52 // response to memory write command +#define SDIO_CMD_MEMST_RSP 0x54 // response to memory set command +#define SDIO_CMD_STARTED 0x60 // indicate the program has jumped to the given function + +#ifdef CONFIG_SDIO_DEVICE_VERIFY + +#define TX_BD_STRUCTURE_NUM 10 +#define RX_BD_STRUCTURE_NUM 10 +#define TX_BD_BUFFER_SIZE 0x1000//0x2000//0x800 +#define RX_BD_BUFFER_SIZE 0x400//0x800 + +#define SDIO_RAM_ADDR_BASE 0x20080000 +#define SDIO_BUFFER_HEAD(addr) SDIO_RAM_ADDR_BASE + addr +#define HAL_SDIO_BUFFER_READ8(addr) HAL_READ8(SDIO_RAM_ADDR_BASE, addr) +#define HAL_SDIO_BUFFER_READ32(addr) HAL_READ32(SDIO_RAM_ADDR_BASE, addr) +#define HAL_SDIO_BUFFER_WRITE32(addr, value) HAL_WRITE32(SDIO_RAM_ADDR_BASE, addr, value) + +//#define RX_BD_ADDR 0x8000 +//#define RX_BUFFER_ADDR 0x8050 + +typedef enum _SDIO_TEST_FUNC_ { + SDIO_TEST_INIT, // 0 + SDIO_TEST_INT_ON, // 1 + SDIO_TEST_INT_OFF, // 2 + SDIO_HCI_RX_REQ, // 3 + SDIO_RESET_TXFIFIO, // 4 + SDIO_CPU_RST_DMA, // 5 + SDIO_CPU_CLR_INT_REG, // 6 + SDIO_TIMER_TEST, // 7 + SDIO_TEST_DEBUG, // 8 + SDIO_TEST, // 9 + SDIO_HELP = 0xff +}SDIO_TEST_FUNC, *PSDIO_TEST_FUNC; + +typedef struct _SDIO_TEST_ADAPTER_ { + u32 TXWritePtr; + u32 TXReadPtr; + u16 RXWritePtr; + u16 RXReadPtr; + u16 IntMask; + u16 IntStatus; +} SDIO_TEST_ADAPTER, *PSDIO_TEST_ADAPTER; + + +VOID +MovePKTToRX( + IN u32 Source, IN u32 Destination, IN u32 PKTSize +); + +BOOL +PacketProcess( + IN SDIO_TEST_ADAPTER *pDevStatus +); + +VOID +SdioDeviceIrqHandleFunc( + IN VOID *DATA +); + +VOID +SdioDeviceTestApp( + IN u32 Data +); + +VOID +InitRXBD(VOID); + +VOID +InitTXFIFO(VOID); + +VOID +IrqRegister(VOID); + +#endif // end of "#ifdef CONFIG_SDIO_DEVICE_VERIFY" + +#endif /* #ifndef _RTL8195A_SDIO_H_ */ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio_host.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio_host.h new file mode 100644 index 0000000..ee1f892 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdio_host.h @@ -0,0 +1,295 @@ + /* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_SDIO_HOST_H_ +#define _RTL8195A_SDIO_HOST_H_ + +#include "hal_api.h" +#include "osdep_api.h" + + + + +#ifdef CONFIG_SDIO_HOST_VERIFY + +#define HAL_MMC_HOST_READ32(addr) HAL_READ32(SDIO_HOST_REG_BASE, addr) +#define HAL_MMC_HOST_WRITE32(addr, value) HAL_WRITE32(SDIO_HOST_REG_BASE, addr, value) +#define HAL_MMC_HOST_READ16(addr) HAL_READ16(SDIO_HOST_REG_BASE, addr) +#define HAL_MMC_HOST_WRITE16(addr, value) HAL_WRITE16(SDIO_HOST_REG_BASE, addr, value) +#define HAL_MMC_HOST_READ8(addr) HAL_READ8(SDIO_HOST_REG_BASE, addr) +#define HAL_MMC_HOST_WRITE8(addr, value) HAL_WRITE8(SDIO_HOST_REG_BASE, addr, value) + +/* RTL8195A Register */ +// REG_SOC_HCI_COM_FUNC_EN (0x214) +#define SD_DEVICE_IP_ON_BLK BIT0 +#define SD_DEVICE_IP_OFF_BLK BIT1 +#define SD_HOST_IP_BLK BIT2 + +// REG_PESOC_HCI_CLK_CTRL0 (0x240) +#define SD_HOST_CLKEN_IN_CPU_RUN_MODE BIT2 + +// REG_HCI_PINMUX_CTRL (0x2A0) +#define SD_DEVICE_MODE_PINMUX_EN BIT0 +#define SD_HOST_MODE_PINMUX_EN BIT1 + +// 0x40059000 +#define SD_HOST_CARD_DETECT_CIRCUIT BIT10 + + + +/* SD Host Register */ +#define REG_SDMA_SYS_ADDR_ARG 0x00 // 4byte +#define REG_BLOCK_SIZE 0x04 // 2byte +#define REG_BLOCK_COUNT 0x06 // 2byte +#define REG_ARGUMENT1 0x08 // 4byte +#define REG_TRANSFER_MODE 0x0C // 2byte +#define REG_COMMAND 0x0E // 2byte +#define REG_RESPONSE0 0x10 // 4byte +#define REG_RESPONSE2 0x14 // 4byte +#define REG_RESPONSE4 0x18 // 4byte +#define REG_RESPONSE6 0x1C // 4byte +#define REG_BUFFER_DATA_PORT 0x20 // 4byte +#define REG_PRESENT_STATE 0x24 // 4byte +#define REG_HOST_CONTROL1 0x28 // 1byte +#define REG_POWER_CONTROL 0x29 // 1byte +#define REG_BLOCK_GAP_CONTROL 0x2A // 1byte +#define REG_WAKEUP_CONTROL 0x2B // 1byte +#define REG_CLOCK_CONTROL 0x2C // 2byte +#define REG_TIMEOUT_CONTROL 0x2E // 1byte +#define REG_SW_RESET 0x2F // 1byte +#define REG_NORMAL_INT_STATUS 0x30 // 2byte +#define REG_ERROR_INT_STATUS 0x32 // 2byte +#define REG_NORMAL_INT_STATUS_ENABLE 0x34 // 2byte +#define REG_ERROR_INT_STATUS_ENABLE 0x36 // 2byte +#define REG_NORMAL_INT_SIGNAL_ENABLE 0x38 // 2byte +#define REG_ERROR_INT_SIGNAL_ENABLE 0x3A // 2byte +#define REG_CAPABILITIES 0x40 // 8byte +#define REG_ADMA_ADDRESS 0x58 // 8byte + +// Transfer Mode (0x0C) +#define BIT_DMA_EN BIT0 +#define BIT_BLK_CNT_EN BIT1 +#define BIT_AUTO_CMD12_EN BIT2 +#define BIT_AUTO_CMD23_EN BIT3 +#define BIT_READ_TRANS BIT4 +#define BIT_MULTI_BLK BIT5 + +// Present State (0x24) +#define BIT_CMD_INHIBIT_CMD BIT0 +#define BIT_CMD_INHIBIT_DAT BIT1 +#define BIT_CARD_INSERTED BIT16 +#define BIT_WRITE_PROTECT_SWITCH_PIN BIT19 + +// Power Control (0x29) +#define BIT_POWER_33 0xE +#define BIT_POWER_30 0xC +#define BIT_POWER_18 0xA + +// Clock Control (0x2C) +#define BIT_INTERNAL_CLK_EN BIT0 +#define BIT_INTERNAL_CLK_STABLE BIT1 +#define BIT_SD_CLK_EN BIT2 + +// Software Reset (0x2F) +#define BIT_SW_RESET_ALL BIT0 +#define BIT_SW_RESET_CMD_LINE BIT1 +#define BIT_SW_RESET_DAT_LINE BIT2 + +// Norma Interrupt Status (0x30) +#define BIT_COMMAND_COMPLETE BIT0 +#define BIT_TRANSFER_COMPLETE BIT1 +#define BIT_BLOCK_GAP_EVENT BIT2 +#define BIT_DMA_INT BIT3 +#define BIT_BUFFER_WRITE_RDY BIT4 +#define BIT_BUFFER_READ_RDY BIT5 +#define BIT_CARD_INSERTION BIT6 +#define BIT_CARD_REMOVAL BIT7 +#define BIT_CARD_INT BIT8 +#define BIT_ERROR_INT BIT15 + +// Error Interrupt Status (0x32) +#define BIT_DATA_TIME_OUT_ERROR BIT4 +#define BIT_DATA_CRC_ERROR BIT5 +#define BIT_ADMA_ERROR BIT9 + +// Capabilities (0x40) +#define BIT_VDD_33 BIT24 +#define BIT_VDD_30 BIT25 +#define BIT_VDD_18 BIT26 + + +#define ENABLE 1 +#define DISABLE 0 + +#define ADMA_DESC_NUM 50 + +#define BUFFER_UNIT_SIZE 512 + +typedef enum _MMC_HOST_TEST_FUNC_ { + MMC_HOST_TEST_HW_INIT, // 0 + MMC_HOST_TEST_CARD_INIT, // 1 + MMC_HOST_TEST_SEND_CMD, // 2 + MMC_HOST_TEST_DEBUG, // 3 + MMC_HOST_TEST_SW_RESET, // 4 + MMC_HOST_TEST_READ_SINGLE, // 5 + MMC_HOST_TEST_WRITE_SINGLE, // 6 + MMC_HOST_TEST_READ_MULTI, // 7 + MMC_HOST_TEST_WRITE_MULTI, // 8 + MMC_HOST_TEST_SINGLE_LONGRUN, // 9 + MMC_HOST_TEST_MULTI_LONGRUN, // 10 + MMC_HOST_TEST_CARD_DETECTION, // 11 + MMC_HOST_TEST_WRITE_PROTECT, // 12 + MMC_HOST_TEST_REGISTER_RW // 13 +}MMC_HOST_TEST_FUNC; + +typedef enum _RESPONSE_TYPE_ { + No_Response, // 00b + Response_136, // 01b + Response_48, // 10b + Response_48_Busy // 11b +}RESPONSE_TYPE; + +typedef enum _COMMAND_TYPE_ { + Normal, // 00b + Suspend, // 01b + Resume, // 10b + Abort // 11b +}COMMAND_TYPE; + +typedef enum _DATA_PRESENT_ { + No_Data_Present, // 00b + Data_Present, // 01b +}DATA_PRESENT; + +typedef enum _SUPPLY_VOLTAGE_ { + MMC_VDD_27_28 = BIT15, + MMC_VDD_28_29 = BIT16, + MMC_VDD_29_30 = BIT17, + MMC_VDD_30_31 = BIT18, + MMC_VDD_31_32 = BIT19, + MMC_VDD_32_33 = BIT20, + MMC_VDD_33_34 = BIT21, + MMC_VDD_34_35 = BIT22, + MMC_VDD_35_36 = BIT23, +}SUPPLY_VOLTAGE; + +typedef enum _COMMAND_INDEX_ { + GO_IDLE_STATE = 0, + ALL_SEND_CID = 2, + SEND_RELATIVE_ADDR = 3, + SET_BUS_WIDTH = 6, + SELECT_CARD = 7, + SEND_IF_COND = 8, + SEND_CSD = 9, + STOP_TRANSMISSION = 12, + SEND_STATUS = 13, + READ_SINGLE_BLOCK = 17, + READ_MULTIPLE_BLOCK = 18, + WRITE_BLOCK = 24, + WRITE_MULTIPLE_BLOCK = 25, + SD_SEND_OP_COND = 41, + APP_CMD = 55, +}COMMAND_INDEX; + +typedef enum _TRANSFER_CONFIG_ { + Read_Data = 0, + Write_Data = 1, + Single_Block = 0, + Multiple_Block = 1, +}TRANSFER_CONFIG; + +typedef enum _ERROR_STATUS_ { + General_Error, // 0 + CRC_Error, // 1 + TIME_OUT_ERROR, // 2 + CRC_Error_NeedCMD12, // 3 + Transfer_OK // 4 +}ERROR_STATUS; + +typedef enum _CARD_CURRENT_STATE_ { + IDLE_STATE, + READY_STATE, + IDENT_STATE, + STBY_STATE, + TRAN_STATE, + DATA_STATE, + RCV_STATE, + PRG_STATE, + DIS_STATE, + UNKNOWN_STATE +}CARD_CURRENT_STATE; + +typedef struct _COMMAND_FORMAT_ +{ + u16 Resp_Type:2; + u16 Rsvd0:1; + u16 CMD_CRC_Chk:1; + u16 CMD_Idx_Chk:1; + u16 Data_Present:1; + u16 CMD_Type:2; + u16 CMD_Idx:6; + u16 Rsvd1:2; +}COMMAND_FORMAT, *PCOMMAND_FPRMAT; + +typedef struct _MMC_COMMAND +{ + COMMAND_FORMAT Cmd_Format; + u32 Arg; +}MMC_COMMAND; + +typedef struct _MMC_HOST_ +{ + u32 OCR_Avail; + u32 Resp[4]; + u32 CID[4]; + u32 RCA; +}MMC_HOST, *PMMC_HOST; + +typedef struct _ADMA_ATTR_ +{ + u16 Valid:1; + u16 End:1; + u16 Int:1; + u16 Rsvd1:1; + u16 Act1:1; + u16 Act2:1; + u16 Rsvd2:10; +}ADMA_ATTR, *PADMA_ATTR; +// 24 bytes +typedef struct _ADMA_DESC_TABLE_ +{ + // 1st buffer desc + ADMA_ATTR Attribute1; + u16 Length1; + u32 Address1; + // 2nd buffer desc + ADMA_ATTR Attribute2; + u16 Length2; + u32 Address2; + // 3rd buffer desc + ADMA_ATTR Attribute3; + u16 Length3; + u32 Address3; +}ADMA_DESC_TABLE, *PADMA_DESC_TABLE; +// 1024 bytes +typedef struct _ADMA_BUFFER_ +{ + u8 Data1[512]; /* 1st buffer */ + u8 Data2[512]; /* 2nd buffer */ +}ADMA_BUFFER, *PADMA_BUFFER; + + +VOID +SdHostTestApp( + IN u8 *argv[] +); +#endif // end of "#ifdef CONFIG_SDIO_HOST_VERIFY" + +#endif /* #ifndef _RTL8195A_SDIO_HOST_H_ */ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdr.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdr.h new file mode 100644 index 0000000..05a13fa --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sdr.h @@ -0,0 +1,379 @@ +#ifndef _RTL8195A_SDR_H +#define _RTL8195A_SDR_H + +#define MS_0_CTRL_BASE BSP_MS_I_DRAMC_0_BASE +#define MS_0_CTRL_PHY_BASE (BSP_MS_I_DRAMC_0_BASE) +#define MS_0_WRAP_BASE (MS_0_CTRL_BASE + 0x200) + +#define MS_1_CTRL_BASE BSP_MS_I_DRAMC_1_BASE +#define MS_1_CTRL_PHY_BASE (BSP_MS_I_DRAMC_1_BASE) +#define MS_1_WRAP_BASE (MS_1_CTRL_BASE + 0x200) + +#define MS_PCTL_CCR_OFFSET 0x000 +#define MS_PCTL_DCR_OFFSET 0x004 +#define MS_PCTL_IOCR_OFFSET 0x008 +#define MS_PCTL_CSR_OFFSET 0x00c +#define MS_PCTL_DRR_OFFSET 0x010 +#define MS_PCTL_TPR0_OFFSET 0x014 +#define MS_PCTL_TPR1_OFFSET 0x018 +#define MS_PCTL_TPR2_OFFSET 0x01c +#define MS_PCTL_MR_OFFSET 0x020 +#define MS_PCTL_EMR1_OFFSET 0x024 +#define MS_PCTL_EMR2_OFFSET 0x028 +#define MS_PCTL_EMR3_OFFSET 0x02c +#define MS_PCTL_CSR2_OFFSET 0x030 +#define MS_PCTL_SRST_OFFSET 0x034 +#define MS_PCTL_DTR2_OFFSET 0x038 +#define MS_PCTL_DTR3_OFFSET 0x03c +#define MS_PCTL_GDLLCR_OFFSET 0x040 +#define MS_PCTL_DLLCR0_OFFSET 0x044 +#define MS_PCTL_DLLCR1_OFFSET 0x048 +#define MS_PCTL_DLLCR2_OFFSET 0x04c +#define MS_PCTL_DLLCR3_OFFSET 0x050 +#define MS_PCTL_DLLCR4_OFFSET 0x054 +#define MS_PCTL_DLLCR5_OFFSET 0x058 +#define MS_PCTL_DLLCR6_OFFSET 0x05c +#define MS_PCTL_DLLCR7_OFFSET 0x060 +#define MS_PCTL_DLLCR8_OFFSET 0x064 +#define MS_PCTL_DQTR0_OFFSET 0x068 +#define MS_PCTL_DQTR1_OFFSET 0x06c +#define MS_PCTL_DQTR2_OFFSET 0x070 +#define MS_PCTL_DQTR3_OFFSET 0x074 +#define MS_PCTL_DQTR4_OFFSET 0x078 +#define MS_PCTL_DQTR5_OFFSET 0x07c +#define MS_PCTL_DQTR6_OFFSET 0x080 +#define MS_PCTL_DQTR7_OFFSET 0x084 +#define MS_PCTL_DQSTR_OFFSET 0x088 +#define MS_PCTL_DQSBTR_OFFSET 0x08c +#define MS_PCTL_ODTCR_OFFSET 0x090 +#define MS_PCTL_DTR0_OFFSET 0x094 +#define MS_PCTL_DTR1_OFFSET 0x098 +#define MS_PCTL_DTAR_OFFSET 0x09c +#define MS_PCTL_ZQCR0_OFFSET 0x0a0 +#define MS_PCTL_ZQCR1_OFFSET 0x0a4 +#define MS_PCTL_ZQSR_OFFSET 0x0a8 +#define MS_PCTL_RSLR0_OFFSET 0x0ac +#define MS_PCTL_RSLR1_OFFSET 0x0b0 +#define MS_PCTL_RSLR2_OFFSET 0x0b4 +#define MS_PCTL_RSLR3_OFFSET 0x0b8 +#define MS_PCTL_RDGR0_OFFSET 0x0bc +#define MS_PCTL_RDGR1_OFFSET 0x0c0 +#define MS_PCTL_RDGR2_OFFSET 0x0c4 +#define MS_PCTL_RDGR3_OFFSET 0x0c8 +#define MS_PCTL_MXSL_OFFSET 0x0cc + +#define MS_PCTL_BCR_OFFSET 0x0d0 +#define MS_PCTL_BALR0_OFFSET 0x0d4 +#define MS_PCTL_BALR1_OFFSET 0x0d8 +#define MS_PCTL_BDR0_OFFSET 0x0dc +#define MS_PCTL_BDR1_OFFSET 0x0e0 +#define MS_PCTL_BBR_OFFSET 0x0e4 +#define MS_PCTL_BSR_OFFSET 0x0e8 +#define MS_PCTL_BYR_OFFSET 0x0ec +#define MS_PCTL_BFA_OFFSET 0x0f0 +#define MS_PCTL_IDR_OFFSET 0x0f8 +#define MS_PCTL_ERR_OFFSET 0x0fc + +#define MS_WRAP_SCR_OFFSET 0x224 +#define MS_WRAP_QCR_OFFSET 0x230 +#define MS_WRAP_PCR_OFFSET 0x234 +#define MS_WRAP_QTR0_OFFSET 0x240 +#define MS_WRAP_QTR1_OFFSET 0x244 +#define MS_WRAP_QTR2_OFFSET 0x248 +#define MS_WRAP_QTR3_OFFSET 0x24c +#define MS_WRAP_QTR4_OFFSET 0x250 +#define MS_WRAP_QTR5_OFFSET 0x254 +#define MS_WRAP_QTR6_OFFSET 0x258 +#define MS_WRAP_QTR7_OFFSET 0x25c +#define MS_WRAP_QTR8_OFFSET 0x260 +#define MS_WRAP_QTR9_OFFSET 0x264 +#define MS_WRAP_QTR10_OFFSET 0x268 +#define MS_WRAP_QTR11_OFFSET 0x26c +#define MS_WRAP_QTR12_OFFSET 0x270 +#define MS_WRAP_QTR13_OFFSET 0x274 +#define MS_WRAP_QTR14_OFFSET 0x278 +#define MS_WRAP_QTR15_OFFSET 0x27c + +#define MS_PHY_DLY0 0x100 +#define MS_PHY_DLY1_RST 0x104 +#define MS_PHY_DLY_CLK 0x108 +#define MS_PHY_DLY_ST 0x10c +#define MS_PHY_DLY_NUM 0x100 + +#define PCTL_CCR_INIT_BFO 0 +#define PCTL_CCR_INIT_BFW 1 +#define PCTL_CCR_DTT_BFO 1 +#define PCTL_CCR_DTT_BFW 1 +#define PCTL_CCR_BTT_BFO 2 +#define PCTL_CCR_BTT_BFW 1 +#define PCTL_CCR_DPIT_BFO 3 +#define PCTL_CCR_DPIT_BFW 1 +#define PCTL_CCR_FLUSH_FIFO_BFO 8 +#define PCTL_CCR_FLUSH_FIFO_BFW 1 + +#define PCTL_DCR_DDR3_BFO 0 +#define PCTL_DCR_DDR3_BFW 1 +#define PCTL_DCR_SDR_BFO 1 +#define PCTL_DCR_SDR_BFW 1 +#define PCTL_DCR_DQ32_BFO 4 +#define PCTL_DCR_DQ32_BFW 1 +#define PCTL_DCR_DFI_RATE_BFO 8 +#define PCTL_DCR_DFI_RATE_BFW 3 + +#define PCTL_IOCR_RD_PIPE_BFO 8 +#define PCTL_IOCR_RD_PIPE_BFW 4 +#define PCTL_IOCR_TPHY_WD_BFO 12 +#define PCTL_IOCR_TPHY_WD_BFW 5 +#define PCTL_IOCR_TPHY_WL_BFO 17 +#define PCTL_IOCR_TPHY_WL_BFW 3 +#define PCTL_IOCR_TPHY_RD_EN_BFO 20 +#define PCTL_IOCR_TPHY_RD_EN_BFW 5 + +#define PCTL_CSR_MEM_IDLE_BFO 8 +#define PCTL_CSR_MEM_IDLE_BFW 1 +#define PCTL_CSR_DT_IDLE_BFO 9 +#define PCTL_CSR_DT_IDLE_BFW 1 +#define PCTL_CSR_BIST_IDLE_BFO 10 +#define PCTL_CSR_BIST_IDLE_BFW 1 +#define PCTL_CSR_DT_FAIL_BFO 11 +#define PCTL_CSR_DT_FAIL_BFW 1 +#define PCTL_CSR_BT_FAIL_BFO 12 +#define PCTL_CSR_BT_FAIL_BFW 1 + +#define PCTL_DRR_TRFC_BFO 0 +#define PCTL_DRR_TRFC_BFW 7 +#define PCTL_DRR_TREF_BFO 8 +#define PCTL_DRR_TREF_BFW 24 +#define PCTL_DRR_REF_NUM_BFO 24 +#define PCTL_DRR_REF_NUM_BFW 4 +#define PCTL_DRR_REF_DIS_BFO 28 +#define PCTL_DRR_REF_DIS_BFW 1 + +#define PCTL_TPR0_TRP_BFO 0 +#define PCTL_TPR0_TRP_BFW 4 +#define PCTL_TPR0_TRAS_BFO 4 +#define PCTL_TPR0_TRAS_BFW 5 +#define PCTL_TPR0_TWR_BFO 9 +#define PCTL_TPR0_TWR_BFW 4 +#define PCTL_TPR0_TRTP_BFO 13 +#define PCTL_TPR0_TRTP_BFW 3 + +#define PCTL_TPR1_TRRD_BFO 0 +#define PCTL_TPR1_TRRD_BFW 4 +#define PCTL_TPR1_TRC_BFO 4 +#define PCTL_TPR1_TRC_BFW 6 +#define PCTL_TPR1_TRCD_BFO 10 +#define PCTL_TPR1_TRCD_BFW 4 +#define PCTL_TPR1_TCCD_BFO 14 +#define PCTL_TPR1_TCCD_BFW 3 +#define PCTL_TPR1_TWTR_BFO 17 +#define PCTL_TPR1_TWTR_BFW 3 +#define PCTL_TPR1_TRTW_BFO 20 +#define PCTL_TPR1_TRTW_BFW 4 + +#define PCTL_TPR2_INIT_REF_NUM_BFO 0 +#define PCTL_TPR2_INIT_REF_NUM_BFW 4 +#define PCTL_TPR2_INIT_NS_EN_BFO 4 +#define PCTL_TPR2_INIT_NS_EN_BFW 1 +#define PCTL_TPR2_TMRD_BFO 5 +#define PCTL_TPR2_TMRD_BFW 2 + +#define PCTL_MR_BL_BFO 0 +#define PCTL_MR_BL_BFW 3 +#define PCTL_MR_BT_BFO 3 +#define PCTL_MR_BT_BFW 1 +#define PCTL_MR_CAS_BFO 4 +#define PCTL_MR_CAS_BFW 3 +#define PCTL_MR_OP_BFO 8 +#define PCTL_MR_OP_BFW 12 + +#define PCTL_EMR1_ADDLAT_BFO 3 +#define PCTL_EMR1_ADDLAT_BFW 3 + +#define PCTL_CMD_DPIN_RSTN_BFO 0 +#define PCTL_CMD_DPIN_RSTN_BFW 1 +#define PCTL_CMD_DPIN_CKE_BFO 1 +#define PCTL_CMD_DPIN_CKE_BFW 1 +#define PCTL_CMD_DPIN_ODT_BFO 2 +#define PCTL_CMD_DPIN_ODT_BFW 1 + +#define PCTL_BCR_STOP_BFO 0 +#define PCTL_BCR_STOP_BFW 1 +#define PCTL_BCR_CMP_BFO 1 +#define PCTL_BCR_CMP_BFW 1 +#define PCTL_BCR_LOOP_BFO 2 +#define PCTL_BCR_LOOP_BFW 1 +#define PCTL_BCR_DIS_MASK_BFO 3 +#define PCTL_BCR_DIS_MASK_BFW 1 +#define PCTL_BCR_AT_STOP_BFO 4 +#define PCTL_BCR_AT_STOP_BFW 1 +#define PCTL_BCR_FLUSH_CMD_BFO 8 +#define PCTL_BCR_FLUSH_CMD_BFW 1 +#define PCTL_BCR_FLUSH_WD_BFO 9 +#define PCTL_BCR_FLUSH_WD_BFW 1 +#define PCTL_BCR_FLUSH_RGD_BFO 10 +#define PCTL_BCR_FLUSH_RGD_BFW 1 +#define PCTL_BCR_FLUSH_RD_BFO 11 +#define PCTL_BCR_FLUSH_RD_BFW 1 +#define PCTL_BCR_FLUSH_RD_EXPC_BFO 16 +#define PCTL_BCR_FLUSH_RD_EXPC_BFW 14 + +#define PCTL_BST_ERR_FST_TH_BFO 0 +#define PCTL_BST_ERR_FST_TH_BFW 12 +#define PCTL_BST_ERR_CNT_BFO 16 +#define PCTL_BST_ERR_CNT_BFW 14 + +#define PCTL_BSRAM0_CMD_LEVEL_BFO 0 +#define PCTL_BSRAM0_CMD_LEVEL_BFW 12 +#define PCTL_BSRAM0_WD_LEVEL_BFO 16 +#define PCTL_BSRAM0_WD_LEVEL_BFW 14 + +#define PCTL_BSRAM1_RG_LEVEL_BFO 0 +#define PCTL_BSRAM1_RG_LEVEL_BFW 14 +#define PCTL_BSRAM1_RD_LEVEL_BFO 16 +#define PCTL_BSRAM1_RD_LEVEL_BFW 14 + +#define WRAP_MISC_PAGE_SIZE_BFO 0 +#define WRAP_MISC_PAGE_SIZE_BFW 4 +#define WRAP_MISC_BANK_SIZE_BFO 4 +#define WRAP_MISC_BANK_SIZE_BFW 2 +#define WRAP_MISC_BST_SIZE_BFO 6 +#define WRAP_MISC_BST_SIZE_BFW 2 +#define WRAP_MISC_DDR_PARAL_BFO 8 +#define WRAP_MISC_DDR_PARAL_BFW 1 + +struct ms_rxi310_portmap { + volatile unsigned int ccr; /* 0x000 */ + volatile unsigned int dcr; /* 0x004 */ + volatile unsigned int iocr; /* 0x008 */ + volatile unsigned int csr; /* 0x00c */ + volatile unsigned int drr; /* 0x010 */ + volatile unsigned int tpr0; /* 0x014 */ + volatile unsigned int tpr1; /* 0x018 */ + volatile unsigned int tpr2; /* 0x01c */ + volatile unsigned int mr; /* 0x020 */ + volatile unsigned int emr1; /* 0x024 */ + volatile unsigned int emr2; /* 0x028 */ + volatile unsigned int emr3; /* 0x02c */ + volatile unsigned int cdpin; /* 0x030 */ + volatile unsigned int tdpin; /* 0x034 */ + volatile unsigned int dtr2; /* 0x038 */ + volatile unsigned int dtr3; /* 0x03c */ + volatile unsigned int gdllcr; /* 0x040 */ + volatile unsigned int dllcr0; /* 0x044 */ + volatile unsigned int dllcr1; /* 0x048 */ + volatile unsigned int dllcr2; /* 0x04c */ + volatile unsigned int dllcr3; /* 0x050 */ + volatile unsigned int dllcr4; /* 0x054 */ + volatile unsigned int dllcr5; /* 0x058 */ + volatile unsigned int dllcr6; /* 0x05c */ + volatile unsigned int dllcr7; /* 0x060 */ + volatile unsigned int dllcr8; /* 0x064 */ + volatile unsigned int dqtr0; /* 0x068 */ + volatile unsigned int dqtr1; /* 0x06c */ + volatile unsigned int dqtr2; /* 0x070 */ + volatile unsigned int dqtr3; /* 0x074 */ + volatile unsigned int dqtr4; /* 0x078 */ + volatile unsigned int dqtr5; /* 0x07c */ + volatile unsigned int dqtr6; /* 0x080 */ + volatile unsigned int dqtr7; /* 0x084 */ + volatile unsigned int dqstr; /* 0x088 */ + volatile unsigned int dqsbtr; /* 0x08c */ + volatile unsigned int odtcr; /* 0x090 */ + volatile unsigned int dtr0; /* 0x094 */ + volatile unsigned int dtr1; /* 0x098 */ + volatile unsigned int dtar; /* 0x09c */ + volatile unsigned int zqcr0; /* 0x0a0 */ + volatile unsigned int zqcr1; /* 0x0a4 */ + volatile unsigned int zqsr; /* 0x0a8 */ + volatile unsigned int rslr0; /* 0x0ac */ + volatile unsigned int rslr1; /* 0x0b0 */ + volatile unsigned int rslr2; /* 0x0b4 */ + volatile unsigned int rslr3; /* 0x0b8 */ + volatile unsigned int rdgr0; /* 0x0bc */ + volatile unsigned int rdgr1; /* 0x0c0 */ + volatile unsigned int rdgr2; /* 0x0c4 */ + volatile unsigned int rdgr3; /* 0x0c8 */ + volatile unsigned int mxsl; /* 0x0cc */ + volatile unsigned int bcr; /* 0x0d0 */ + volatile unsigned int bst; /* 0x0d4 */ + volatile unsigned int bsram0; /* 0x0d8 */ + volatile unsigned int bsram1; /* 0x0dc */ + volatile unsigned int bdr1; /* 0x0e0 */ + volatile unsigned int bbr; /* 0x0e4 */ + volatile unsigned int bsr; /* 0x0e8 */ + volatile unsigned int byr; /* 0x0ec */ + volatile unsigned int bfa; /* 0x0f0 */ + volatile unsigned int pctl_svn; /* 0x0f4 */ + volatile unsigned int pctl_idr; /* 0x0f8 */ + volatile unsigned int err; /* 0x0fc */ + + // SDR_PHY CONTROL REGISTER + volatile unsigned int phy_dly0; /* 0x100 */ + volatile unsigned int phy_dly1_rst; /* 0x104 */ + volatile unsigned int phy_dly_clk; /* 0x108 */ + volatile unsigned int phy_dly_st; /* 0x10c */ + volatile unsigned int phy_dly_num; /* 0x110 */ + volatile unsigned int reserved0[68]; + + // WRAP CONTROL REGISTER + volatile unsigned int misc; /* 0x224 */ + volatile unsigned int cq_ver; /* 0x228 */ + volatile unsigned int cq_mon; /* 0x22c */ + volatile unsigned int wq_ver; /* 0x230 */ + volatile unsigned int wq_mon; /* 0x234 */ + volatile unsigned int rq_ver; /* 0x240 */ + volatile unsigned int rq_mon; /* 0x244 */ + volatile unsigned int reserved1[22]; + volatile unsigned int wwrap_idr; /* 0x2a0 */ + volatile unsigned int wrap_svn; /* 0x2a4 */ + +}; //ms_rxi310_portmap + +#define QFIFO_CMD_BANK_BFO (35 - QFIFO_CMD_WRRD_BFO) // [38:35] +#define QFIFO_CMD_BANK_BFW 4 +#define QFIFO_CMD_PAGE_BFO (20 - QFIFO_CMD_WRRD_BFO) // [34:20] +#define QFIFO_CMD_PAGE_BFW 15 +#define QFIFO_CMD_COLU_BFO (7 - QFIFO_CMD_WRRD_BFO) // [19: 7] +#define QFIFO_CMD_COLU_BFW 13 // [19: 7] +#define QFIFO_BST_LEN_BFO (3 - QFIFO_CMD_WRRD_BFO) // [6:3] +#define QFIFO_BST_LEN_BFW 4 // [6:3] +#define QFIFO_CMD_WRRD_BFO 2 // [2], remove bit[1:0] +#define QFIFO_CMD_WRRD_BFW 1 // [2], remove bit[1:0] + +//====================================================// + +#define REG_SDR_CCR 0x00 +#define REG_SDR_DCR 0x04 +#define REG_SDR_IOCR 0x08 +#define REG_SDR_CSR 0x0C +#define REG_SDR_DRR 0x10 +#define REG_SDR_TPR0 0x14 +#define REG_SDR_TPR1 0x18 +#define REG_SDR_TPR2 0x1C +#define REG_SDR_MR 0x20 +#define REG_SDR_EMR1 0x24 +#define REG_SDR_EMR2 0x28 +#define REG_SDR_EMR3 0x2C +#define REG_SDR_CMD_DPIN 0x30 +#define REG_SDR_TIE_DPIN 0x34 +#define REG_SDR_BCR 0xD0 +#define REG_SDR_BST 0xD4 +#define REG_SDR_BSRAM0 0xD8 +#define REG_SDR_BSRAM1 0xDC +#define REG_SDR_PCTL_SVN_ID 0xF4 +#define REG_SDR_PCTL_IDR 0xF8 +#define REG_SDR_DLY0 0x100 + +#define REG_SDR_DLY1 0x104 +#define REG_SDR_DCM_RST 0x104 + +#define REG_SDR_DLY_CLK_PHA 0x108 +#define REG_SDR_DLY_ST 0x10C + +#define REG_SDR_MISC 0x224 +#define REG_SDR_OCP_WRAP_IDR 0x2A0 +#define REG_SDR_OCP_WRAP_VERSION 0x2A4 + + +#endif // end of "#ifndef _RTL8195A_SDR_H" diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_spi_flash.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_spi_flash.h new file mode 100644 index 0000000..1ac2c16 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_spi_flash.h @@ -0,0 +1,990 @@ +#ifndef _RTL8195A_SPI_FLASH_H +#define _RTL8195A_SPI_FLASH_H + +#define CPU_OPT_WIDTH 0x1F + +//2 REG_NOT_VALID + +//2 REG_SPIC_CTRLR0 + +#define BIT_SHIFT_CK_MTIMES 23 +#define BIT_MASK_CK_MTIMES 0x1f +#define BIT_CK_MTIMES(x) (((x) & BIT_MASK_CK_MTIMES) << BIT_SHIFT_CK_MTIMES) +#define BIT_CTRL_CK_MTIMES(x) (((x) & BIT_MASK_CK_MTIMES) << BIT_SHIFT_CK_MTIMES) +#define BIT_GET_CK_MTIMES(x) (((x) >> BIT_SHIFT_CK_MTIMES) & BIT_MASK_CK_MTIMES) + +#define BIT_FAST_RD BIT(22) +#define BIT_SHIFT_FAST_RD 22 +#define BIT_MASK_FAST_RD 0x1 +#define BIT_CTRL_FAST_RD(x) (((x) & BIT_MASK_FAST_RD) << BIT_SHIFT_FAST_RD) + + +#define BIT_SHIFT_CMD_CH 20 +#define BIT_MASK_CMD_CH 0x3 +#define BIT_CMD_CH(x) (((x) & BIT_MASK_CMD_CH) << BIT_SHIFT_CMD_CH) +#define BIT_CTRL_CMD_CH(x) (((x) & BIT_MASK_CMD_CH) << BIT_SHIFT_CMD_CH) +#define BIT_GET_CMD_CH(x) (((x) >> BIT_SHIFT_CMD_CH) & BIT_MASK_CMD_CH) + + +#define BIT_SHIFT_DATA_CH 18 +#define BIT_MASK_DATA_CH 0x3 +#define BIT_DATA_CH(x) (((x) & BIT_MASK_DATA_CH) << BIT_SHIFT_DATA_CH) +#define BIT_CTRL_DATA_CH(x) (((x) & BIT_MASK_DATA_CH) << BIT_SHIFT_DATA_CH) +#define BIT_GET_DATA_CH(x) (((x) >> BIT_SHIFT_DATA_CH) & BIT_MASK_DATA_CH) + + +#define BIT_SHIFT_ADDR_CH 16 +#define BIT_MASK_ADDR_CH 0x3 +#define BIT_ADDR_CH(x) (((x) & BIT_MASK_ADDR_CH) << BIT_SHIFT_ADDR_CH) +#define BIT_CTRL_ADDR_CH(x) (((x) & BIT_MASK_ADDR_CH) << BIT_SHIFT_ADDR_CH) +#define BIT_GET_ADDR_CH(x) (((x) >> BIT_SHIFT_ADDR_CH) & BIT_MASK_ADDR_CH) + + +#define BIT_SHIFT_TMOD 8 +#define BIT_MASK_TMOD 0x3 +#define BIT_TMOD(x) (((x) & BIT_MASK_TMOD) << BIT_SHIFT_TMOD) +#define BIT_CTRL_TMOD(x) (((x) & BIT_MASK_TMOD) << BIT_SHIFT_TMOD) +#define BIT_GET_TMOD(x) (((x) >> BIT_SHIFT_TMOD) & BIT_MASK_TMOD) + +#define BIT_SCPOL BIT(7) +#define BIT_SHIFT_SCPOL 7 +#define BIT_MASK_SCPOL 0x1 +#define BIT_CTRL_SCPOL(x) (((x) & BIT_MASK_SCPOL) << BIT_SHIFT_SCPOL) + +#define BIT_SCPH BIT(6) +#define BIT_SHIFT_SCPH 6 +#define BIT_MASK_SCPH 0x1 +#define BIT_CTRL_SCPH(x) (((x) & BIT_MASK_SCPH) << BIT_SHIFT_SCPH) + +//2 REG_SPIC_CTRLR1 + +#define BIT_SHIFT_NDF 0 +#define BIT_MASK_NDF 0xfff +#define BIT_NDF(x) (((x) & BIT_MASK_NDF) << BIT_SHIFT_NDF) +#define BIT_CTRL_NDF(x) (((x) & BIT_MASK_NDF) << BIT_SHIFT_NDF) +#define BIT_GET_NDF(x) (((x) >> BIT_SHIFT_NDF) & BIT_MASK_NDF) + + +//2 REG_SPIC_SSIENR +#define BIT_ATCK_CMD BIT(1) +#define BIT_SHIFT_ATCK_CMD 1 +#define BIT_MASK_ATCK_CMD 0x1 +#define BIT_CTRL_ATCK_CMD(x) (((x) & BIT_MASK_ATCK_CMD) << BIT_SHIFT_ATCK_CMD) + +#define BIT_SPIC_EN BIT(0) +#define BIT_SHIFT_SPIC_EN 0 +#define BIT_MASK_SPIC_EN 0x1 +#define BIT_CTRL_SPIC_EN(x) (((x) & BIT_MASK_SPIC_EN) << BIT_SHIFT_SPIC_EN) + +//2 REG_SPIC_MWCR + +//2 REG_SPIC_SER +#define BIT_SER BIT(0) +#define BIT_SHIFT_SER 0 +#define BIT_MASK_SER 0x1 +#define BIT_CTRL_SER(x) (((x) & BIT_MASK_SER) << BIT_SHIFT_SER) + +//2 REG_SPIC_BAUDR + +#define BIT_SHIFT_SCKDV 0 +#define BIT_MASK_SCKDV 0xffff +#define BIT_SCKDV(x) (((x) & BIT_MASK_SCKDV) << BIT_SHIFT_SCKDV) +#define BIT_CTRL_SCKDV(x) (((x) & BIT_MASK_SCKDV) << BIT_SHIFT_SCKDV) +#define BIT_GET_SCKDV(x) (((x) >> BIT_SHIFT_SCKDV) & BIT_MASK_SCKDV) + + +//2 REG_SPIC_TXFTLR + +#define BIT_SHIFT_TFT 0 +#define BIT_MASK_TFT 0x1f +#define BIT_TFT(x) (((x) & BIT_MASK_TFT) << BIT_SHIFT_TFT) +#define BIT_CTRL_TFT(x) (((x) & BIT_MASK_TFT) << BIT_SHIFT_TFT) +#define BIT_GET_TFT(x) (((x) >> BIT_SHIFT_TFT) & BIT_MASK_TFT) + + +//2 REG_SPIC_RXFTLR + +#define BIT_SHIFT_RFT 0 +#define BIT_MASK_RFT 0x1f +#define BIT_RFT(x) (((x) & BIT_MASK_RFT) << BIT_SHIFT_RFT) +#define BIT_CTRL_RFT(x) (((x) & BIT_MASK_RFT) << BIT_SHIFT_RFT) +#define BIT_GET_RFT(x) (((x) >> BIT_SHIFT_RFT) & BIT_MASK_RFT) + + +//2 REG_SPIC_TXFLR + +#define BIT_SHIFT_TXFL 0 +#define BIT_MASK_TXFL 0x3f +#define BIT_TXFL(x) (((x) & BIT_MASK_TXFL) << BIT_SHIFT_TXFL) +#define BIT_CTRL_TXFL(x) (((x) & BIT_MASK_TXFL) << BIT_SHIFT_TXFL) +#define BIT_GET_TXFL(x) (((x) >> BIT_SHIFT_TXFL) & BIT_MASK_TXFL) + + +//2 REG_SPIC_RXFLR + +#define BIT_SHIFT_RXFL 0 +#define BIT_MASK_RXFL 0x3f +#define BIT_RXFL(x) (((x) & BIT_MASK_RXFL) << BIT_SHIFT_RXFL) +#define BIT_CTRL_RXFL(x) (((x) & BIT_MASK_RXFL) << BIT_SHIFT_RXFL) +#define BIT_GET_RXFL(x) (((x) >> BIT_SHIFT_RXFL) & BIT_MASK_RXFL) + + +//2 REG_SPIC_SR +#define BIT_TXE BIT(5) +#define BIT_SHIFT_TXE 5 +#define BIT_MASK_TXE 0x1 +#define BIT_CTRL_TXE(x) (((x) & BIT_MASK_TXE) << BIT_SHIFT_TXE) + +#define BIT_RFF BIT(4) +#define BIT_SHIFT_RFF 4 +#define BIT_MASK_RFF 0x1 +#define BIT_CTRL_RFF(x) (((x) & BIT_MASK_RFF) << BIT_SHIFT_RFF) + +#define BIT_RFNE BIT(3) +#define BIT_SHIFT_RFNE 3 +#define BIT_MASK_RFNE 0x1 +#define BIT_CTRL_RFNE(x) (((x) & BIT_MASK_RFNE) << BIT_SHIFT_RFNE) + +#define BIT_TFE BIT(2) +#define BIT_SHIFT_TFE 2 +#define BIT_MASK_TFE 0x1 +#define BIT_CTRL_TFE(x) (((x) & BIT_MASK_TFE) << BIT_SHIFT_TFE) + +#define BIT_TFNF BIT(1) +#define BIT_SHIFT_TFNF 1 +#define BIT_MASK_TFNF 0x1 +#define BIT_CTRL_TFNF(x) (((x) & BIT_MASK_TFNF) << BIT_SHIFT_TFNF) + +#define BIT_BUSY BIT(0) +#define BIT_SHIFT_BUSY 0 +#define BIT_MASK_BUSY 0x1 +#define BIT_CTRL_BUSY(x) (((x) & BIT_MASK_BUSY) << BIT_SHIFT_BUSY) + +//2 REG_SPIC_IMR +#define BIT_TXSIM BIT(9) +#define BIT_SHIFT_TXSIM 9 +#define BIT_MASK_TXSIM 0x1 +#define BIT_CTRL_TXSIM(x) (((x) & BIT_MASK_TXSIM) << BIT_SHIFT_TXSIM) + +#define BIT_ACEIM BIT(8) +#define BIT_SHIFT_ACEIM 8 +#define BIT_MASK_ACEIM 0x1 +#define BIT_CTRL_ACEIM(x) (((x) & BIT_MASK_ACEIM) << BIT_SHIFT_ACEIM) + +#define BIT_BYEIM BIT(7) +#define BIT_SHIFT_BYEIM 7 +#define BIT_MASK_BYEIM 0x1 +#define BIT_CTRL_BYEIM(x) (((x) & BIT_MASK_BYEIM) << BIT_SHIFT_BYEIM) + +#define BIT_WBEIM BIT(6) +#define BIT_SHIFT_WBEIM 6 +#define BIT_MASK_WBEIM 0x1 +#define BIT_CTRL_WBEIM(x) (((x) & BIT_MASK_WBEIM) << BIT_SHIFT_WBEIM) + +#define BIT_FSEIM BIT(5) +#define BIT_SHIFT_FSEIM 5 +#define BIT_MASK_FSEIM 0x1 +#define BIT_CTRL_FSEIM(x) (((x) & BIT_MASK_FSEIM) << BIT_SHIFT_FSEIM) + +#define BIT_RXFIM BIT(4) +#define BIT_SHIFT_RXFIM 4 +#define BIT_MASK_RXFIM 0x1 +#define BIT_CTRL_RXFIM(x) (((x) & BIT_MASK_RXFIM) << BIT_SHIFT_RXFIM) + +#define BIT_RXOIM BIT(3) +#define BIT_SHIFT_RXOIM 3 +#define BIT_MASK_RXOIM 0x1 +#define BIT_CTRL_RXOIM(x) (((x) & BIT_MASK_RXOIM) << BIT_SHIFT_RXOIM) + +#define BIT_RXUIM BIT(2) +#define BIT_SHIFT_RXUIM 2 +#define BIT_MASK_RXUIM 0x1 +#define BIT_CTRL_RXUIM(x) (((x) & BIT_MASK_RXUIM) << BIT_SHIFT_RXUIM) + +#define BIT_TXOIM BIT(1) +#define BIT_SHIFT_TXOIM 1 +#define BIT_MASK_TXOIM 0x1 +#define BIT_CTRL_TXOIM(x) (((x) & BIT_MASK_TXOIM) << BIT_SHIFT_TXOIM) + +#define BIT_TXEIM BIT(0) +#define BIT_SHIFT_TXEIM 0 +#define BIT_MASK_TXEIM 0x1 +#define BIT_CTRL_TXEIM(x) (((x) & BIT_MASK_TXEIM) << BIT_SHIFT_TXEIM) + +//2 REG_SPIC_ISR +#define BIT_TXSIS BIT(9) +#define BIT_SHIFT_TXSIS 9 +#define BIT_MASK_TXSIS 0x1 +#define BIT_CTRL_TXSIS(x) (((x) & BIT_MASK_TXSIS) << BIT_SHIFT_TXSIS) + +#define BIT_ACEIS BIT(8) +#define BIT_SHIFT_ACEIS 8 +#define BIT_MASK_ACEIS 0x1 +#define BIT_CTRL_ACEIS(x) (((x) & BIT_MASK_ACEIS) << BIT_SHIFT_ACEIS) + +#define BIT_BYEIS BIT(7) +#define BIT_SHIFT_BYEIS 7 +#define BIT_MASK_BYEIS 0x1 +#define BIT_CTRL_BYEIS(x) (((x) & BIT_MASK_BYEIS) << BIT_SHIFT_BYEIS) + +#define BIT_WBEIS BIT(6) +#define BIT_SHIFT_WBEIS 6 +#define BIT_MASK_WBEIS 0x1 +#define BIT_CTRL_WBEIS(x) (((x) & BIT_MASK_WBEIS) << BIT_SHIFT_WBEIS) + +#define BIT_FSEIS BIT(5) +#define BIT_SHIFT_FSEIS 5 +#define BIT_MASK_FSEIS 0x1 +#define BIT_CTRL_FSEIS(x) (((x) & BIT_MASK_FSEIS) << BIT_SHIFT_FSEIS) + +#define BIT_RXFIS BIT(4) +#define BIT_SHIFT_RXFIS 4 +#define BIT_MASK_RXFIS 0x1 +#define BIT_CTRL_RXFIS(x) (((x) & BIT_MASK_RXFIS) << BIT_SHIFT_RXFIS) + +#define BIT_RXOIS BIT(3) +#define BIT_SHIFT_RXOIS 3 +#define BIT_MASK_RXOIS 0x1 +#define BIT_CTRL_RXOIS(x) (((x) & BIT_MASK_RXOIS) << BIT_SHIFT_RXOIS) + +#define BIT_RXUIS BIT(2) +#define BIT_SHIFT_RXUIS 2 +#define BIT_MASK_RXUIS 0x1 +#define BIT_CTRL_RXUIS(x) (((x) & BIT_MASK_RXUIS) << BIT_SHIFT_RXUIS) + +#define BIT_TXOIS BIT(1) +#define BIT_SHIFT_TXOIS 1 +#define BIT_MASK_TXOIS 0x1 +#define BIT_CTRL_TXOIS(x) (((x) & BIT_MASK_TXOIS) << BIT_SHIFT_TXOIS) + +#define BIT_TXEIS BIT(0) +#define BIT_SHIFT_TXEIS 0 +#define BIT_MASK_TXEIS 0x1 +#define BIT_CTRL_TXEIS(x) (((x) & BIT_MASK_TXEIS) << BIT_SHIFT_TXEIS) + +//2 REG_SPIC_RISR +#define BIT_ACEIR BIT(8) +#define BIT_SHIFT_ACEIR 8 +#define BIT_MASK_ACEIR 0x1 +#define BIT_CTRL_ACEIR(x) (((x) & BIT_MASK_ACEIR) << BIT_SHIFT_ACEIR) + +#define BIT_BYEIR BIT(7) +#define BIT_SHIFT_BYEIR 7 +#define BIT_MASK_BYEIR 0x1 +#define BIT_CTRL_BYEIR(x) (((x) & BIT_MASK_BYEIR) << BIT_SHIFT_BYEIR) + +#define BIT_WBEIR BIT(6) +#define BIT_SHIFT_WBEIR 6 +#define BIT_MASK_WBEIR 0x1 +#define BIT_CTRL_WBEIR(x) (((x) & BIT_MASK_WBEIR) << BIT_SHIFT_WBEIR) + +#define BIT_FSEIR BIT(5) +#define BIT_SHIFT_FSEIR 5 +#define BIT_MASK_FSEIR 0x1 +#define BIT_CTRL_FSEIR(x) (((x) & BIT_MASK_FSEIR) << BIT_SHIFT_FSEIR) + +#define BIT_RXFIR BIT(4) +#define BIT_SHIFT_RXFIR 4 +#define BIT_MASK_RXFIR 0x1 +#define BIT_CTRL_RXFIR(x) (((x) & BIT_MASK_RXFIR) << BIT_SHIFT_RXFIR) + +#define BIT_RXOIR BIT(3) +#define BIT_SHIFT_RXOIR 3 +#define BIT_MASK_RXOIR 0x1 +#define BIT_CTRL_RXOIR(x) (((x) & BIT_MASK_RXOIR) << BIT_SHIFT_RXOIR) + +#define BIT_RXUIR BIT(2) +#define BIT_SHIFT_RXUIR 2 +#define BIT_MASK_RXUIR 0x1 +#define BIT_CTRL_RXUIR(x) (((x) & BIT_MASK_RXUIR) << BIT_SHIFT_RXUIR) + +#define BIT_TXOIR BIT(1) +#define BIT_SHIFT_TXOIR 1 +#define BIT_MASK_TXOIR 0x1 +#define BIT_CTRL_TXOIR(x) (((x) & BIT_MASK_TXOIR) << BIT_SHIFT_TXOIR) + +#define BIT_TXEIR BIT(0) +#define BIT_SHIFT_TXEIR 0 +#define BIT_MASK_TXEIR 0x1 +#define BIT_CTRL_TXEIR(x) (((x) & BIT_MASK_TXEIR) << BIT_SHIFT_TXEIR) + +//2 REG_SPIC_TXOICR +#define BIT_TXOICR BIT(0) +#define BIT_SHIFT_TXOICR 0 +#define BIT_MASK_TXOICR 0x1 +#define BIT_CTRL_TXOICR(x) (((x) & BIT_MASK_TXOICR) << BIT_SHIFT_TXOICR) + +//2 REG_SPIC_RXOICR +#define BIT_RXOCIR BIT(0) +#define BIT_SHIFT_RXOCIR 0 +#define BIT_MASK_RXOCIR 0x1 +#define BIT_CTRL_RXOCIR(x) (((x) & BIT_MASK_RXOCIR) << BIT_SHIFT_RXOCIR) + +//2 REG_SPC_RXUICR +#define BIT_RXUICR BIT(0) +#define BIT_SHIFT_RXUICR 0 +#define BIT_MASK_RXUICR 0x1 +#define BIT_CTRL_RXUICR(x) (((x) & BIT_MASK_RXUICR) << BIT_SHIFT_RXUICR) + +//2 REG_SPIC_MSTICR +#define BIT_MSTICR BIT(0) +#define BIT_SHIFT_MSTICR 0 +#define BIT_MASK_MSTICR 0x1 +#define BIT_CTRL_MSTICR(x) (((x) & BIT_MASK_MSTICR) << BIT_SHIFT_MSTICR) + +//2 REG_SPIC_ICR + +#define BIT_SHIFT_ICR 0 +#define BIT_MASK_ICR 0xff +#define BIT_ICR(x) (((x) & BIT_MASK_ICR) << BIT_SHIFT_ICR) +#define BIT_CTRL_ICR(x) (((x) & BIT_MASK_ICR) << BIT_SHIFT_ICR) +#define BIT_GET_ICR(x) (((x) >> BIT_SHIFT_ICR) & BIT_MASK_ICR) + + +//2 REG_SPIC_DMACR + +//2 REG_SPIC_DMATDLR0 + +//2 REG_SPIC_DMATDLR1 + +//2 REG_SPIC_IDR + +#define BIT_SHIFT_IDCODE 0 +#define BIT_MASK_IDCODE 0xffffffffL +#define BIT_IDCODE(x) (((x) & BIT_MASK_IDCODE) << BIT_SHIFT_IDCODE) +#define BIT_CTRL_IDCODE(x) (((x) & BIT_MASK_IDCODE) << BIT_SHIFT_IDCODE) +#define BIT_GET_IDCODE(x) (((x) >> BIT_SHIFT_IDCODE) & BIT_MASK_IDCODE) + + +//2 REG_SPIC_VERSION + +#define BIT_SHIFT_SPIC_VERSION 0 +#define BIT_MASK_SPIC_VERSION 0xffffffffL +#define BIT_SPIC_VERSION(x) (((x) & BIT_MASK_SPIC_VERSION) << BIT_SHIFT_SPIC_VERSION) +#define BIT_CTRL_SPIC_VERSION(x) (((x) & BIT_MASK_SPIC_VERSION) << BIT_SHIFT_SPIC_VERSION) +#define BIT_GET_SPIC_VERSION(x) (((x) >> BIT_SHIFT_SPIC_VERSION) & BIT_MASK_SPIC_VERSION) + + +//2 REG_SPIC_DR0 + +#define BIT_SHIFT_DR0 0 +#define BIT_MASK_DR0 0xffffffffL +#define BIT_DR0(x) (((x) & BIT_MASK_DR0) << BIT_SHIFT_DR0) +#define BIT_CTRL_DR0(x) (((x) & BIT_MASK_DR0) << BIT_SHIFT_DR0) +#define BIT_GET_DR0(x) (((x) >> BIT_SHIFT_DR0) & BIT_MASK_DR0) + + +//2 REG_SPIC_DR1 + +#define BIT_SHIFT_DR1 0 +#define BIT_MASK_DR1 0xffffffffL +#define BIT_DR1(x) (((x) & BIT_MASK_DR1) << BIT_SHIFT_DR1) +#define BIT_CTRL_DR1(x) (((x) & BIT_MASK_DR1) << BIT_SHIFT_DR1) +#define BIT_GET_DR1(x) (((x) >> BIT_SHIFT_DR1) & BIT_MASK_DR1) + + +//2 REG_SPIC_DR2 + +#define BIT_SHIFT_DR2 0 +#define BIT_MASK_DR2 0xffffffffL +#define BIT_DR2(x) (((x) & BIT_MASK_DR2) << BIT_SHIFT_DR2) +#define BIT_CTRL_DR2(x) (((x) & BIT_MASK_DR2) << BIT_SHIFT_DR2) +#define BIT_GET_DR2(x) (((x) >> BIT_SHIFT_DR2) & BIT_MASK_DR2) + + +//2 REG_SPIC_DR3 + +#define BIT_SHIFT_DR3 0 +#define BIT_MASK_DR3 0xffffffffL +#define BIT_DR3(x) (((x) & BIT_MASK_DR3) << BIT_SHIFT_DR3) +#define BIT_CTRL_DR3(x) (((x) & BIT_MASK_DR3) << BIT_SHIFT_DR3) +#define BIT_GET_DR3(x) (((x) >> BIT_SHIFT_DR3) & BIT_MASK_DR3) + + +//2 REG_SPIC_DR4 + +#define BIT_SHIFT_DR4 0 +#define BIT_MASK_DR4 0xffffffffL +#define BIT_DR4(x) (((x) & BIT_MASK_DR4) << BIT_SHIFT_DR4) +#define BIT_CTRL_DR4(x) (((x) & BIT_MASK_DR4) << BIT_SHIFT_DR4) +#define BIT_GET_DR4(x) (((x) >> BIT_SHIFT_DR4) & BIT_MASK_DR4) + + +//2 REG_SPIC_DR5 + +#define BIT_SHIFT_DR5 0 +#define BIT_MASK_DR5 0xffffffffL +#define BIT_DR5(x) (((x) & BIT_MASK_DR5) << BIT_SHIFT_DR5) +#define BIT_CTRL_DR5(x) (((x) & BIT_MASK_DR5) << BIT_SHIFT_DR5) +#define BIT_GET_DR5(x) (((x) >> BIT_SHIFT_DR5) & BIT_MASK_DR5) + + +//2 REG_SPIC_DR6 + +#define BIT_SHIFT_DR6 0 +#define BIT_MASK_DR6 0xffffffffL +#define BIT_DR6(x) (((x) & BIT_MASK_DR6) << BIT_SHIFT_DR6) +#define BIT_CTRL_DR6(x) (((x) & BIT_MASK_DR6) << BIT_SHIFT_DR6) +#define BIT_GET_DR6(x) (((x) >> BIT_SHIFT_DR6) & BIT_MASK_DR6) + + +//2 REG_SPIC_DR7 + +#define BIT_SHIFT_DR7 0 +#define BIT_MASK_DR7 0xffffffffL +#define BIT_DR7(x) (((x) & BIT_MASK_DR7) << BIT_SHIFT_DR7) +#define BIT_CTRL_DR7(x) (((x) & BIT_MASK_DR7) << BIT_SHIFT_DR7) +#define BIT_GET_DR7(x) (((x) >> BIT_SHIFT_DR7) & BIT_MASK_DR7) + + +//2 REG_SPIC_DR8 + +#define BIT_SHIFT_DR8 0 +#define BIT_MASK_DR8 0xffffffffL +#define BIT_DR8(x) (((x) & BIT_MASK_DR8) << BIT_SHIFT_DR8) +#define BIT_CTRL_DR8(x) (((x) & BIT_MASK_DR8) << BIT_SHIFT_DR8) +#define BIT_GET_DR8(x) (((x) >> BIT_SHIFT_DR8) & BIT_MASK_DR8) + + +//2 REG_SPIC_DR9 + +#define BIT_SHIFT_DR9 0 +#define BIT_MASK_DR9 0xffffffffL +#define BIT_DR9(x) (((x) & BIT_MASK_DR9) << BIT_SHIFT_DR9) +#define BIT_CTRL_DR9(x) (((x) & BIT_MASK_DR9) << BIT_SHIFT_DR9) +#define BIT_GET_DR9(x) (((x) >> BIT_SHIFT_DR9) & BIT_MASK_DR9) + + +//2 REG_SPIC_DR10 + +#define BIT_SHIFT_DR10 0 +#define BIT_MASK_DR10 0xffffffffL +#define BIT_DR10(x) (((x) & BIT_MASK_DR10) << BIT_SHIFT_DR10) +#define BIT_CTRL_DR10(x) (((x) & BIT_MASK_DR10) << BIT_SHIFT_DR10) +#define BIT_GET_DR10(x) (((x) >> BIT_SHIFT_DR10) & BIT_MASK_DR10) + + +//2 REG_SPIC_DR11 + +#define BIT_SHIFT_DR11 0 +#define BIT_MASK_DR11 0xffffffffL +#define BIT_DR11(x) (((x) & BIT_MASK_DR11) << BIT_SHIFT_DR11) +#define BIT_CTRL_DR11(x) (((x) & BIT_MASK_DR11) << BIT_SHIFT_DR11) +#define BIT_GET_DR11(x) (((x) >> BIT_SHIFT_DR11) & BIT_MASK_DR11) + + +//2 REG_SPIC_DR12 + +#define BIT_SHIFT_DR12 0 +#define BIT_MASK_DR12 0xffffffffL +#define BIT_DR12(x) (((x) & BIT_MASK_DR12) << BIT_SHIFT_DR12) +#define BIT_CTRL_DR12(x) (((x) & BIT_MASK_DR12) << BIT_SHIFT_DR12) +#define BIT_GET_DR12(x) (((x) >> BIT_SHIFT_DR12) & BIT_MASK_DR12) + + +//2 REG_SPIC_DR13 + +#define BIT_SHIFT_DR13 0 +#define BIT_MASK_DR13 0xffffffffL +#define BIT_DR13(x) (((x) & BIT_MASK_DR13) << BIT_SHIFT_DR13) +#define BIT_CTRL_DR13(x) (((x) & BIT_MASK_DR13) << BIT_SHIFT_DR13) +#define BIT_GET_DR13(x) (((x) >> BIT_SHIFT_DR13) & BIT_MASK_DR13) + + +//2 REG_SPIC_DR14 + +#define BIT_SHIFT_DR14 0 +#define BIT_MASK_DR14 0xffffffffL +#define BIT_DR14(x) (((x) & BIT_MASK_DR14) << BIT_SHIFT_DR14) +#define BIT_CTRL_DR14(x) (((x) & BIT_MASK_DR14) << BIT_SHIFT_DR14) +#define BIT_GET_DR14(x) (((x) >> BIT_SHIFT_DR14) & BIT_MASK_DR14) + + +//2 REG_SPIC_DR15 + +#define BIT_SHIFT_DR15 0 +#define BIT_MASK_DR15 0xffffffffL +#define BIT_DR15(x) (((x) & BIT_MASK_DR15) << BIT_SHIFT_DR15) +#define BIT_CTRL_DR15(x) (((x) & BIT_MASK_DR15) << BIT_SHIFT_DR15) +#define BIT_GET_DR15(x) (((x) >> BIT_SHIFT_DR15) & BIT_MASK_DR15) + + +//2 REG_SPIC_DR16 + +#define BIT_SHIFT_DR16 0 +#define BIT_MASK_DR16 0xffffffffL +#define BIT_DR16(x) (((x) & BIT_MASK_DR16) << BIT_SHIFT_DR16) +#define BIT_CTRL_DR16(x) (((x) & BIT_MASK_DR16) << BIT_SHIFT_DR16) +#define BIT_GET_DR16(x) (((x) >> BIT_SHIFT_DR16) & BIT_MASK_DR16) + + +//2 REG_SPIC_DR17 + +#define BIT_SHIFT_DR17 0 +#define BIT_MASK_DR17 0xffffffffL +#define BIT_DR17(x) (((x) & BIT_MASK_DR17) << BIT_SHIFT_DR17) +#define BIT_CTRL_DR17(x) (((x) & BIT_MASK_DR17) << BIT_SHIFT_DR17) +#define BIT_GET_DR17(x) (((x) >> BIT_SHIFT_DR17) & BIT_MASK_DR17) + + +//2 REG_SPIC_DR18 + +#define BIT_SHIFT_DR18 0 +#define BIT_MASK_DR18 0xffffffffL +#define BIT_DR18(x) (((x) & BIT_MASK_DR18) << BIT_SHIFT_DR18) +#define BIT_CTRL_DR18(x) (((x) & BIT_MASK_DR18) << BIT_SHIFT_DR18) +#define BIT_GET_DR18(x) (((x) >> BIT_SHIFT_DR18) & BIT_MASK_DR18) + + +//2 REG_SPIC_DR19 + +#define BIT_SHIFT_DR19 0 +#define BIT_MASK_DR19 0xffffffffL +#define BIT_DR19(x) (((x) & BIT_MASK_DR19) << BIT_SHIFT_DR19) +#define BIT_CTRL_DR19(x) (((x) & BIT_MASK_DR19) << BIT_SHIFT_DR19) +#define BIT_GET_DR19(x) (((x) >> BIT_SHIFT_DR19) & BIT_MASK_DR19) + + +//2 REG_SPIC_DR20 + +#define BIT_SHIFT_DR20 0 +#define BIT_MASK_DR20 0xffffffffL +#define BIT_DR20(x) (((x) & BIT_MASK_DR20) << BIT_SHIFT_DR20) +#define BIT_CTRL_DR20(x) (((x) & BIT_MASK_DR20) << BIT_SHIFT_DR20) +#define BIT_GET_DR20(x) (((x) >> BIT_SHIFT_DR20) & BIT_MASK_DR20) + + +//2 REG_SPIC_DR21 + +#define BIT_SHIFT_DR21 0 +#define BIT_MASK_DR21 0xffffffffL +#define BIT_DR21(x) (((x) & BIT_MASK_DR21) << BIT_SHIFT_DR21) +#define BIT_CTRL_DR21(x) (((x) & BIT_MASK_DR21) << BIT_SHIFT_DR21) +#define BIT_GET_DR21(x) (((x) >> BIT_SHIFT_DR21) & BIT_MASK_DR21) + + +//2 REG_SPIC_DR22 + +#define BIT_SHIFT_DR22 0 +#define BIT_MASK_DR22 0xffffffffL +#define BIT_DR22(x) (((x) & BIT_MASK_DR22) << BIT_SHIFT_DR22) +#define BIT_CTRL_DR22(x) (((x) & BIT_MASK_DR22) << BIT_SHIFT_DR22) +#define BIT_GET_DR22(x) (((x) >> BIT_SHIFT_DR22) & BIT_MASK_DR22) + + +//2 REG_SPIC_DR23 + +#define BIT_SHIFT_DR23 0 +#define BIT_MASK_DR23 0xffffffffL +#define BIT_DR23(x) (((x) & BIT_MASK_DR23) << BIT_SHIFT_DR23) +#define BIT_CTRL_DR23(x) (((x) & BIT_MASK_DR23) << BIT_SHIFT_DR23) +#define BIT_GET_DR23(x) (((x) >> BIT_SHIFT_DR23) & BIT_MASK_DR23) + + +//2 REG_SPIC_DR24 + +#define BIT_SHIFT_DR24 0 +#define BIT_MASK_DR24 0xffffffffL +#define BIT_DR24(x) (((x) & BIT_MASK_DR24) << BIT_SHIFT_DR24) +#define BIT_CTRL_DR24(x) (((x) & BIT_MASK_DR24) << BIT_SHIFT_DR24) +#define BIT_GET_DR24(x) (((x) >> BIT_SHIFT_DR24) & BIT_MASK_DR24) + + +//2 REG_SPIC_DR25 + +#define BIT_SHIFT_DR25 0 +#define BIT_MASK_DR25 0xffffffffL +#define BIT_DR25(x) (((x) & BIT_MASK_DR25) << BIT_SHIFT_DR25) +#define BIT_CTRL_DR25(x) (((x) & BIT_MASK_DR25) << BIT_SHIFT_DR25) +#define BIT_GET_DR25(x) (((x) >> BIT_SHIFT_DR25) & BIT_MASK_DR25) + + +//2 REG_SPIC_DR26 + +#define BIT_SHIFT_DR26 0 +#define BIT_MASK_DR26 0xffffffffL +#define BIT_DR26(x) (((x) & BIT_MASK_DR26) << BIT_SHIFT_DR26) +#define BIT_CTRL_DR26(x) (((x) & BIT_MASK_DR26) << BIT_SHIFT_DR26) +#define BIT_GET_DR26(x) (((x) >> BIT_SHIFT_DR26) & BIT_MASK_DR26) + + +//2 REG_SPIC_DR27 + +#define BIT_SHIFT_DR27 0 +#define BIT_MASK_DR27 0xffffffffL +#define BIT_DR27(x) (((x) & BIT_MASK_DR27) << BIT_SHIFT_DR27) +#define BIT_CTRL_DR27(x) (((x) & BIT_MASK_DR27) << BIT_SHIFT_DR27) +#define BIT_GET_DR27(x) (((x) >> BIT_SHIFT_DR27) & BIT_MASK_DR27) + + +//2 REG_SPIC_DR28 + +#define BIT_SHIFT_DR28 0 +#define BIT_MASK_DR28 0xffffffffL +#define BIT_DR28(x) (((x) & BIT_MASK_DR28) << BIT_SHIFT_DR28) +#define BIT_CTRL_DR28(x) (((x) & BIT_MASK_DR28) << BIT_SHIFT_DR28) +#define BIT_GET_DR28(x) (((x) >> BIT_SHIFT_DR28) & BIT_MASK_DR28) + + +//2 REG_SPIC_DR29 + +#define BIT_SHIFT_DR29 0 +#define BIT_MASK_DR29 0xffffffffL +#define BIT_DR29(x) (((x) & BIT_MASK_DR29) << BIT_SHIFT_DR29) +#define BIT_CTRL_DR29(x) (((x) & BIT_MASK_DR29) << BIT_SHIFT_DR29) +#define BIT_GET_DR29(x) (((x) >> BIT_SHIFT_DR29) & BIT_MASK_DR29) + + +//2 REG_SPIC_DR30 + +#define BIT_SHIFT_DR30 0 +#define BIT_MASK_DR30 0xffffffffL +#define BIT_DR30(x) (((x) & BIT_MASK_DR30) << BIT_SHIFT_DR30) +#define BIT_CTRL_DR30(x) (((x) & BIT_MASK_DR30) << BIT_SHIFT_DR30) +#define BIT_GET_DR30(x) (((x) >> BIT_SHIFT_DR30) & BIT_MASK_DR30) + + +//2 REG_SPIC_DR31 + +#define BIT_SHIFT_DR31 0 +#define BIT_MASK_DR31 0xffffffffL +#define BIT_DR31(x) (((x) & BIT_MASK_DR31) << BIT_SHIFT_DR31) +#define BIT_CTRL_DR31(x) (((x) & BIT_MASK_DR31) << BIT_SHIFT_DR31) +#define BIT_GET_DR31(x) (((x) >> BIT_SHIFT_DR31) & BIT_MASK_DR31) + + +//2 REG_SPIC_READ_FAST_SINGLE + +#define BIT_SHIFT_FRD_CMD 0 +#define BIT_MASK_FRD_CMD 0xff +#define BIT_FRD_CMD(x) (((x) & BIT_MASK_FRD_CMD) << BIT_SHIFT_FRD_CMD) +#define BIT_CTRL_FRD_CMD(x) (((x) & BIT_MASK_FRD_CMD) << BIT_SHIFT_FRD_CMD) +#define BIT_GET_FRD_CMD(x) (((x) >> BIT_SHIFT_FRD_CMD) & BIT_MASK_FRD_CMD) + + +//2 REG_SPIC_READ_DUAL_DATA + +#define BIT_SHIFT_RD_DUAL_O_CMD 0 +#define BIT_MASK_RD_DUAL_O_CMD 0xff +#define BIT_RD_DUAL_O_CMD(x) (((x) & BIT_MASK_RD_DUAL_O_CMD) << BIT_SHIFT_RD_DUAL_O_CMD) +#define BIT_CTRL_RD_DUAL_O_CMD(x) (((x) & BIT_MASK_RD_DUAL_O_CMD) << BIT_SHIFT_RD_DUAL_O_CMD) +#define BIT_GET_RD_DUAL_O_CMD(x) (((x) >> BIT_SHIFT_RD_DUAL_O_CMD) & BIT_MASK_RD_DUAL_O_CMD) + + +//2 REG_SPIC_READ_DUAL_ADDR_DATA + +#define BIT_SHIFT_RD_DUAL_IO_CMD 0 +#define BIT_MASK_RD_DUAL_IO_CMD 0xff +#define BIT_RD_DUAL_IO_CMD(x) (((x) & BIT_MASK_RD_DUAL_IO_CMD) << BIT_SHIFT_RD_DUAL_IO_CMD) +#define BIT_CTRL_RD_DUAL_IO_CMD(x) (((x) & BIT_MASK_RD_DUAL_IO_CMD) << BIT_SHIFT_RD_DUAL_IO_CMD) +#define BIT_GET_RD_DUAL_IO_CMD(x) (((x) >> BIT_SHIFT_RD_DUAL_IO_CMD) & BIT_MASK_RD_DUAL_IO_CMD) + + +//2 REG_SPIC_READ_QUAD_DATA + +#define BIT_SHIFT_RD_QUAD_O_CMD 0 +#define BIT_MASK_RD_QUAD_O_CMD 0xff +#define BIT_RD_QUAD_O_CMD(x) (((x) & BIT_MASK_RD_QUAD_O_CMD) << BIT_SHIFT_RD_QUAD_O_CMD) +#define BIT_CTRL_RD_QUAD_O_CMD(x) (((x) & BIT_MASK_RD_QUAD_O_CMD) << BIT_SHIFT_RD_QUAD_O_CMD) +#define BIT_GET_RD_QUAD_O_CMD(x) (((x) >> BIT_SHIFT_RD_QUAD_O_CMD) & BIT_MASK_RD_QUAD_O_CMD) + + +//2 REG_SPIC_READ_QUAD_ADDR_DATA + +#define BIT_SHIFT_RD_QUAD_IO_CMD 0 +#define BIT_MASK_RD_QUAD_IO_CMD 0xff +#define BIT_RD_QUAD_IO_CMD(x) (((x) & BIT_MASK_RD_QUAD_IO_CMD) << BIT_SHIFT_RD_QUAD_IO_CMD) +#define BIT_CTRL_RD_QUAD_IO_CMD(x) (((x) & BIT_MASK_RD_QUAD_IO_CMD) << BIT_SHIFT_RD_QUAD_IO_CMD) +#define BIT_GET_RD_QUAD_IO_CMD(x) (((x) >> BIT_SHIFT_RD_QUAD_IO_CMD) & BIT_MASK_RD_QUAD_IO_CMD) + + +//2 REG_SPIC_WRITE_SIGNLE + +#define BIT_SHIFT_WR_CMD 0 +#define BIT_MASK_WR_CMD 0xff +#define BIT_WR_CMD(x) (((x) & BIT_MASK_WR_CMD) << BIT_SHIFT_WR_CMD) +#define BIT_CTRL_WR_CMD(x) (((x) & BIT_MASK_WR_CMD) << BIT_SHIFT_WR_CMD) +#define BIT_GET_WR_CMD(x) (((x) >> BIT_SHIFT_WR_CMD) & BIT_MASK_WR_CMD) + + +//2 REG_SPIC_WRITE_DUAL_DATA + +#define BIT_SHIFT_WR_DUAL_I_CMD 0 +#define BIT_MASK_WR_DUAL_I_CMD 0xff +#define BIT_WR_DUAL_I_CMD(x) (((x) & BIT_MASK_WR_DUAL_I_CMD) << BIT_SHIFT_WR_DUAL_I_CMD) +#define BIT_CTRL_WR_DUAL_I_CMD(x) (((x) & BIT_MASK_WR_DUAL_I_CMD) << BIT_SHIFT_WR_DUAL_I_CMD) +#define BIT_GET_WR_DUAL_I_CMD(x) (((x) >> BIT_SHIFT_WR_DUAL_I_CMD) & BIT_MASK_WR_DUAL_I_CMD) + + +//2 REG_SPIC_WRITE_DUAL_ADDR_DATA + +#define BIT_SHIFT_WR_DUAL_II_CMD 0 +#define BIT_MASK_WR_DUAL_II_CMD 0xff +#define BIT_WR_DUAL_II_CMD(x) (((x) & BIT_MASK_WR_DUAL_II_CMD) << BIT_SHIFT_WR_DUAL_II_CMD) +#define BIT_CTRL_WR_DUAL_II_CMD(x) (((x) & BIT_MASK_WR_DUAL_II_CMD) << BIT_SHIFT_WR_DUAL_II_CMD) +#define BIT_GET_WR_DUAL_II_CMD(x) (((x) >> BIT_SHIFT_WR_DUAL_II_CMD) & BIT_MASK_WR_DUAL_II_CMD) + + +//2 REG_SPIC_WRITE_QUAD_DATA + +#define BIT_SHIFT_WR_QUAD_I_CMD 0 +#define BIT_MASK_WR_QUAD_I_CMD 0xff +#define BIT_WR_QUAD_I_CMD(x) (((x) & BIT_MASK_WR_QUAD_I_CMD) << BIT_SHIFT_WR_QUAD_I_CMD) +#define BIT_CTRL_WR_QUAD_I_CMD(x) (((x) & BIT_MASK_WR_QUAD_I_CMD) << BIT_SHIFT_WR_QUAD_I_CMD) +#define BIT_GET_WR_QUAD_I_CMD(x) (((x) >> BIT_SHIFT_WR_QUAD_I_CMD) & BIT_MASK_WR_QUAD_I_CMD) + + +//2 REG_SPIC_WRITE_QUAD_ADDR_DATA + +#define BIT_SHIFT_WR_QUAD_II_CMD 0 +#define BIT_MASK_WR_QUAD_II_CMD 0xff +#define BIT_WR_QUAD_II_CMD(x) (((x) & BIT_MASK_WR_QUAD_II_CMD) << BIT_SHIFT_WR_QUAD_II_CMD) +#define BIT_CTRL_WR_QUAD_II_CMD(x) (((x) & BIT_MASK_WR_QUAD_II_CMD) << BIT_SHIFT_WR_QUAD_II_CMD) +#define BIT_GET_WR_QUAD_II_CMD(x) (((x) >> BIT_SHIFT_WR_QUAD_II_CMD) & BIT_MASK_WR_QUAD_II_CMD) + + +//2 REG_SPIC_WRITE_ENABLE + +#define BIT_SHIFT_WR_EN_CMD 0 +#define BIT_MASK_WR_EN_CMD 0xff +#define BIT_WR_EN_CMD(x) (((x) & BIT_MASK_WR_EN_CMD) << BIT_SHIFT_WR_EN_CMD) +#define BIT_CTRL_WR_EN_CMD(x) (((x) & BIT_MASK_WR_EN_CMD) << BIT_SHIFT_WR_EN_CMD) +#define BIT_GET_WR_EN_CMD(x) (((x) >> BIT_SHIFT_WR_EN_CMD) & BIT_MASK_WR_EN_CMD) + + +//2 REG_SPIC_READ_STATUS + +#define BIT_SHIFT_RD_ST_CMD 0 +#define BIT_MASK_RD_ST_CMD 0xff +#define BIT_RD_ST_CMD(x) (((x) & BIT_MASK_RD_ST_CMD) << BIT_SHIFT_RD_ST_CMD) +#define BIT_CTRL_RD_ST_CMD(x) (((x) & BIT_MASK_RD_ST_CMD) << BIT_SHIFT_RD_ST_CMD) +#define BIT_GET_RD_ST_CMD(x) (((x) >> BIT_SHIFT_RD_ST_CMD) & BIT_MASK_RD_ST_CMD) + + +//2 REG_SPIC_CTRLR2 + +#define BIT_SHIFT_FIFO_ENTRY 4 +#define BIT_MASK_FIFO_ENTRY 0xf +#define BIT_FIFO_ENTRY(x) (((x) & BIT_MASK_FIFO_ENTRY) << BIT_SHIFT_FIFO_ENTRY) +#define BIT_CTRL_FIFO_ENTRY(x) (((x) & BIT_MASK_FIFO_ENTRY) << BIT_SHIFT_FIFO_ENTRY) +#define BIT_GET_FIFO_ENTRY(x) (((x) >> BIT_SHIFT_FIFO_ENTRY) & BIT_MASK_FIFO_ENTRY) + +#define BIT_WR_SEQ BIT(3) +#define BIT_SHIFT_WR_SEQ 3 +#define BIT_MASK_WR_SEQ 0x1 +#define BIT_CTRL_WR_SEQ(x) (((x) & BIT_MASK_WR_SEQ) << BIT_SHIFT_WR_SEQ) + +#define BIT_WPN_DNUM BIT(2) +#define BIT_SHIFT_WPN_DNUM 2 +#define BIT_MASK_WPN_DNUM 0x1 +#define BIT_CTRL_WPN_DNUM(x) (((x) & BIT_MASK_WPN_DNUM) << BIT_SHIFT_WPN_DNUM) + +#define BIT_WPN_SET BIT(1) +#define BIT_SHIFT_WPN_SET 1 +#define BIT_MASK_WPN_SET 0x1 +#define BIT_CTRL_WPN_SET(x) (((x) & BIT_MASK_WPN_SET) << BIT_SHIFT_WPN_SET) + +#define BIT_SO_DUM BIT(0) +#define BIT_SHIFT_SO_DUM 0 +#define BIT_MASK_SO_DUM 0x1 +#define BIT_CTRL_SO_DUM(x) (((x) & BIT_MASK_SO_DUM) << BIT_SHIFT_SO_DUM) + +//2 REG_SPIC_FBAUDR + +#define BIT_SHIFT_FSCKDV 0 +#define BIT_MASK_FSCKDV 0xfff +#define BIT_FSCKDV(x) (((x) & BIT_MASK_FSCKDV) << BIT_SHIFT_FSCKDV) +#define BIT_CTRL_FSCKDV(x) (((x) & BIT_MASK_FSCKDV) << BIT_SHIFT_FSCKDV) +#define BIT_GET_FSCKDV(x) (((x) >> BIT_SHIFT_FSCKDV) & BIT_MASK_FSCKDV) + + +//2 REG_SPIC_ADDR_LENGTH + +#define BIT_SHIFT_ADDR_PHASE_LENGTH 0 +#define BIT_MASK_ADDR_PHASE_LENGTH 0x3 +#define BIT_ADDR_PHASE_LENGTH(x) (((x) & BIT_MASK_ADDR_PHASE_LENGTH) << BIT_SHIFT_ADDR_PHASE_LENGTH) +#define BIT_CTRL_ADDR_PHASE_LENGTH(x) (((x) & BIT_MASK_ADDR_PHASE_LENGTH) << BIT_SHIFT_ADDR_PHASE_LENGTH) +#define BIT_GET_ADDR_PHASE_LENGTH(x) (((x) >> BIT_SHIFT_ADDR_PHASE_LENGTH) & BIT_MASK_ADDR_PHASE_LENGTH) + + +//2 REG_SPIC_AUTO_LENGTH + +#define BIT_SHIFT_CS_H_WR_DUM_LEN 28 +#define BIT_MASK_CS_H_WR_DUM_LEN 0xf +#define BIT_CS_H_WR_DUM_LEN(x) (((x) & BIT_MASK_CS_H_WR_DUM_LEN) << BIT_SHIFT_CS_H_WR_DUM_LEN) +#define BIT_CTRL_CS_H_WR_DUM_LEN(x) (((x) & BIT_MASK_CS_H_WR_DUM_LEN) << BIT_SHIFT_CS_H_WR_DUM_LEN) +#define BIT_GET_CS_H_WR_DUM_LEN(x) (((x) >> BIT_SHIFT_CS_H_WR_DUM_LEN) & BIT_MASK_CS_H_WR_DUM_LEN) + + +#define BIT_SHIFT_CS_H_RD_DUM_LEN 26 +#define BIT_MASK_CS_H_RD_DUM_LEN 0x3 +#define BIT_CS_H_RD_DUM_LEN(x) (((x) & BIT_MASK_CS_H_RD_DUM_LEN) << BIT_SHIFT_CS_H_RD_DUM_LEN) +#define BIT_CTRL_CS_H_RD_DUM_LEN(x) (((x) & BIT_MASK_CS_H_RD_DUM_LEN) << BIT_SHIFT_CS_H_RD_DUM_LEN) +#define BIT_GET_CS_H_RD_DUM_LEN(x) (((x) >> BIT_SHIFT_CS_H_RD_DUM_LEN) & BIT_MASK_CS_H_RD_DUM_LEN) + + +#define BIT_SHIFT_AUTO_DUM_LEN 18 +#define BIT_MASK_AUTO_DUM_LEN 0xff +#define BIT_AUTO_DUM_LEN(x) (((x) & BIT_MASK_AUTO_DUM_LEN) << BIT_SHIFT_AUTO_DUM_LEN) +#define BIT_CTRL_AUTO_DUM_LEN(x) (((x) & BIT_MASK_AUTO_DUM_LEN) << BIT_SHIFT_AUTO_DUM_LEN) +#define BIT_GET_AUTO_DUM_LEN(x) (((x) >> BIT_SHIFT_AUTO_DUM_LEN) & BIT_MASK_AUTO_DUM_LEN) + + +#define BIT_SHIFT_AUTO_ADDR__LENGTH 16 +#define BIT_MASK_AUTO_ADDR__LENGTH 0x3 +#define BIT_AUTO_ADDR__LENGTH(x) (((x) & BIT_MASK_AUTO_ADDR__LENGTH) << BIT_SHIFT_AUTO_ADDR__LENGTH) +#define BIT_CTRL_AUTO_ADDR__LENGTH(x) (((x) & BIT_MASK_AUTO_ADDR__LENGTH) << BIT_SHIFT_AUTO_ADDR__LENGTH) +#define BIT_GET_AUTO_ADDR__LENGTH(x) (((x) >> BIT_SHIFT_AUTO_ADDR__LENGTH) & BIT_MASK_AUTO_ADDR__LENGTH) + + +#define BIT_SHIFT_RD_DUMMY_LENGTH 0 +#define BIT_MASK_RD_DUMMY_LENGTH 0xffff +#define BIT_RD_DUMMY_LENGTH(x) (((x) & BIT_MASK_RD_DUMMY_LENGTH) << BIT_SHIFT_RD_DUMMY_LENGTH) +#define BIT_CTRL_RD_DUMMY_LENGTH(x) (((x) & BIT_MASK_RD_DUMMY_LENGTH) << BIT_SHIFT_RD_DUMMY_LENGTH) +#define BIT_GET_RD_DUMMY_LENGTH(x) (((x) >> BIT_SHIFT_RD_DUMMY_LENGTH) & BIT_MASK_RD_DUMMY_LENGTH) + + +//2 REG_SPIC_VALID_CMD +#define BIT_WR_BLOCKING BIT(9) +#define BIT_SHIFT_WR_BLOCKING 9 +#define BIT_MASK_WR_BLOCKING 0x1 +#define BIT_CTRL_WR_BLOCKING(x) (((x) & BIT_MASK_WR_BLOCKING) << BIT_SHIFT_WR_BLOCKING) + +#define BIT_WR_QUAD_II BIT(8) +#define BIT_SHIFT_WR_QUAD_II 8 +#define BIT_MASK_WR_QUAD_II 0x1 +#define BIT_CTRL_WR_QUAD_II(x) (((x) & BIT_MASK_WR_QUAD_II) << BIT_SHIFT_WR_QUAD_II) + +#define BIT_WR_QUAD_I BIT(7) +#define BIT_SHIFT_WR_QUAD_I 7 +#define BIT_MASK_WR_QUAD_I 0x1 +#define BIT_CTRL_WR_QUAD_I(x) (((x) & BIT_MASK_WR_QUAD_I) << BIT_SHIFT_WR_QUAD_I) + +#define BIT_WR_DUAL_II BIT(6) +#define BIT_SHIFT_WR_DUAL_II 6 +#define BIT_MASK_WR_DUAL_II 0x1 +#define BIT_CTRL_WR_DUAL_II(x) (((x) & BIT_MASK_WR_DUAL_II) << BIT_SHIFT_WR_DUAL_II) + +#define BIT_WR_DUAL_I BIT(5) +#define BIT_SHIFT_WR_DUAL_I 5 +#define BIT_MASK_WR_DUAL_I 0x1 +#define BIT_CTRL_WR_DUAL_I(x) (((x) & BIT_MASK_WR_DUAL_I) << BIT_SHIFT_WR_DUAL_I) + +#define BIT_RD_QUAD_IO BIT(4) +#define BIT_SHIFT_RD_QUAD_IO 4 +#define BIT_MASK_RD_QUAD_IO 0x1 +#define BIT_CTRL_RD_QUAD_IO(x) (((x) & BIT_MASK_RD_QUAD_IO) << BIT_SHIFT_RD_QUAD_IO) + +#define BIT_RD_QUAD_O BIT(3) +#define BIT_SHIFT_RD_QUAD_O 3 +#define BIT_MASK_RD_QUAD_O 0x1 +#define BIT_CTRL_RD_QUAD_O(x) (((x) & BIT_MASK_RD_QUAD_O) << BIT_SHIFT_RD_QUAD_O) + +#define BIT_RD_DUAL_IO BIT(2) +#define BIT_SHIFT_RD_DUAL_IO 2 +#define BIT_MASK_RD_DUAL_IO 0x1 +#define BIT_CTRL_RD_DUAL_IO(x) (((x) & BIT_MASK_RD_DUAL_IO) << BIT_SHIFT_RD_DUAL_IO) + +#define BIT_RD_DUAL_I BIT(1) +#define BIT_SHIFT_RD_DUAL_I 1 +#define BIT_MASK_RD_DUAL_I 0x1 +#define BIT_CTRL_RD_DUAL_I(x) (((x) & BIT_MASK_RD_DUAL_I) << BIT_SHIFT_RD_DUAL_I) + +#define BIT_FRD_SINGEL BIT(0) +#define BIT_SHIFT_FRD_SINGEL 0 +#define BIT_MASK_FRD_SINGEL 0x1 +#define BIT_CTRL_FRD_SINGEL(x) (((x) & BIT_MASK_FRD_SINGEL) << BIT_SHIFT_FRD_SINGEL) + +//2 REG_SPIC_FLASE_SIZE + +#define BIT_SHIFT_FLASE_SIZE 0 +#define BIT_MASK_FLASE_SIZE 0xf +#define BIT_FLASE_SIZE(x) (((x) & BIT_MASK_FLASE_SIZE) << BIT_SHIFT_FLASE_SIZE) +#define BIT_CTRL_FLASE_SIZE(x) (((x) & BIT_MASK_FLASE_SIZE) << BIT_SHIFT_FLASE_SIZE) +#define BIT_GET_FLASE_SIZE(x) (((x) >> BIT_SHIFT_FLASE_SIZE) & BIT_MASK_FLASE_SIZE) + + +//2 REG_SPIC_FLUSH_FIFO +#define BIT_FLUSH_FIFO BIT(0) +#define BIT_SHIFT_FLUSH_FIFO 0 +#define BIT_MASK_FLUSH_FIFO 0x1 +#define BIT_CTRL_FLUSH_FIFO(x) (((x) & BIT_MASK_FLUSH_FIFO) << BIT_SHIFT_FLUSH_FIFO) + +//=================== Register Address Definition ============================// +#define REG_SPIC_CTRLR0 0x0000//O +#define REG_SPIC_CTRLR1 0x0004//O +#define REG_SPIC_SSIENR 0x0008//O +#define REG_SPIC_MWCR 0x000C +#define REG_SPIC_SER 0x0010//O +#define REG_SPIC_BAUDR 0x0014//O +#define REG_SPIC_TXFTLR 0x0018 +#define REG_SPIC_RXFTLR 0x001C//O +#define REG_SPIC_TXFLR 0x0020//O +#define REG_SPIC_RXFLR 0x0024 +#define REG_SPIC_SR 0x0028 +#define REG_SPIC_IMR 0x002C//O +#define REG_SPIC_ISR 0x0030 +#define REG_SPIC_RISR 0x0034 +#define REG_SPIC_TXOICR 0x0038 +#define REG_SPIC_RXOICR 0x003C +#define REG_SPC_RXUICR 0x0040 +#define REG_SPIC_MSTICR 0x0044 +#define REG_SPIC_ICR 0x0048 +#define REG_SPIC_DMACR 0x004C +#define REG_SPIC_DMATDLR0 0x0050 +#define REG_SPIC_DMATDLR1 0x0054 +#define REG_SPIC_IDR 0x0058 +#define REG_SPIC_VERSION 0x005C +#define REG_SPIC_DR0 0x0060 +#define REG_SPIC_DR1 0x0064 +#define REG_SPIC_DR2 0x0068 +#define REG_SPIC_DR3 0x006C +#define REG_SPIC_DR4 0x0070 +#define REG_SPIC_DR5 0x0074 +#define REG_SPIC_DR6 0x0078 +#define REG_SPIC_DR7 0x007C +#define REG_SPIC_DR8 0x0080 +#define REG_SPIC_DR9 0x0084 +#define REG_SPIC_DR10 0x0088 +#define REG_SPIC_DR11 0x008C +#define REG_SPIC_DR12 0x0090 +#define REG_SPIC_DR13 0x0094 +#define REG_SPIC_DR14 0x0098 +#define REG_SPIC_DR15 0x009C +#define REG_SPIC_DR16 0x00A0 +#define REG_SPIC_DR17 0x00A4 +#define REG_SPIC_DR18 0x00A8 +#define REG_SPIC_DR19 0x00AC +#define REG_SPIC_DR20 0x00B0 +#define REG_SPIC_DR21 0x00B4 +#define REG_SPIC_DR22 0x00B8 +#define REG_SPIC_DR23 0x00BC +#define REG_SPIC_DR24 0x00C0 +#define REG_SPIC_DR25 0x00C4 +#define REG_SPIC_DR26 0x00C8 +#define REG_SPIC_DR27 0x00CC +#define REG_SPIC_DR28 0x00D0 +#define REG_SPIC_DR29 0x00D4 +#define REG_SPIC_DR30 0x00D8 +#define REG_SPIC_DR31 0x00DC +#define REG_SPIC_READ_FAST_SINGLE 0x00E0//O +#define REG_SPIC_READ_DUAL_DATA 0x00E4//O +#define REG_SPIC_READ_DUAL_ADDR_DATA 0x00E8//O +#define REG_SPIC_READ_QUAD_DATA 0x00EC//O +#define REG_SPIC_READ_QUAD_ADDR_DATA 0x00F0//O +#define REG_SPIC_WRITE_SIGNLE 0x00F4//O +#define REG_SPIC_WRITE_DUAL_DATA 0x00F8//O +#define REG_SPIC_WRITE_DUAL_ADDR_DATA 0x00FC//O +#define REG_SPIC_WRITE_QUAD_DATA 0x0100//O +#define REG_SPIC_WRITE_QUAD_ADDR_DATA 0x0104//O +#define REG_SPIC_WRITE_ENABLE 0x0108//O +#define REG_SPIC_READ_STATUS 0x010C//O +#define REG_SPIC_CTRLR2 0x0110//O +#define REG_SPIC_FBAUDR 0x0114//O +#define REG_SPIC_ADDR_LENGTH 0x0118//O +#define REG_SPIC_AUTO_LENGTH 0x011C//O +#define REG_SPIC_VALID_CMD 0x0120//O +#define REG_SPIC_FLASE_SIZE 0x0124//O +#define REG_SPIC_FLUSH_FIFO 0x0128//O + +#endif // end of "#ifndef _RTL8195A_SPI_FLASH_H" diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_ssi.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_ssi.h new file mode 100644 index 0000000..1611401 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_ssi.h @@ -0,0 +1,495 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL8195A_SSI_H_ +#define _RTL8195A_SSI_H_ + +#define SSI_DUMMY_DATA 0x00 // for master mode, we need to push a Dummy data to TX FIFO for read + +#define SSI_CLK_SPI1 (PLATFORM_CLOCK/2) +#define SSI_CLK_SPI0_2 (PLATFORM_CLOCK/4) + +/* Parameters of DW_apb_ssi for RTL8195A */ +#define SSI_TX_FIFO_DEPTH 64 +#define TX_ABW 6 // 1-8, log2(SSI_TX_FIFO_DEPTH) +#define SSI_RX_FIFO_DEPTH 64 +#define RX_ABW 6 // 1-8, log2(SSI_RX_FIFO_DEPTH) + +#define SSI0_REG_BASE 0x40042000 +#define SSI1_REG_BASE 0x40042400 +#define SSI2_REG_BASE 0x40042800 + +/* Memory Map of DW_apb_ssi */ +#define REG_DW_SSI_CTRLR0 0x00 // 16 bits +#define REG_DW_SSI_CTRLR1 0x04 // 16 bits +#define REG_DW_SSI_SSIENR 0x08 // 1 bit +#define REG_DW_SSI_MWCR 0x0C // 3 bits +#define REG_DW_SSI_SER 0x10 // +#define REG_DW_SSI_BAUDR 0x14 // 16 bits +#define REG_DW_SSI_TXFTLR 0x18 // TX_ABW +#define REG_DW_SSI_RXFTLR 0x1C // RX_ABW +#define REG_DW_SSI_TXFLR 0x20 // +#define REG_DW_SSI_RXFLR 0x24 // +#define REG_DW_SSI_SR 0x28 // 7 bits +#define REG_DW_SSI_IMR 0x2C // +#define REG_DW_SSI_ISR 0x30 // 6 bits +#define REG_DW_SSI_RISR 0x34 // 6 bits +#define REG_DW_SSI_TXOICR 0x38 // 1 bits +#define REG_DW_SSI_RXOICR 0x3C // 1 bits +#define REG_DW_SSI_RXUICR 0x40 // 1 bits +#define REG_DW_SSI_MSTICR 0x44 // 1 bits +#define REG_DW_SSI_ICR 0x48 // 1 bits +#define REG_DW_SSI_DMACR 0x4C // 2 bits +#define REG_DW_SSI_DMATDLR 0x50 // TX_ABW +#define REG_DW_SSI_DMARDLR 0x54 // RX_ABW +#define REG_DW_SSI_IDR 0x58 // 32 bits +#define REG_DW_SSI_COMP_VERSION 0x5C // 32 bits +#define REG_DW_SSI_DR 0x60 // 16 bits 0x60-0xEC +#define REG_DW_SSI_RX_SAMPLE_DLY 0xF0 // 8 bits +#define REG_DW_SSI_RSVD_0 0xF4 // 32 bits +#define REG_DW_SSI_RSVD_1 0xF8 // 32 bits +#define REG_DW_SSI_RSVD_2 0xFC // 32 bits + +// CTRLR0 0x00 // 16 bits, 6.2.1 +// DFS Reset Value: 0x7 +#define BIT_SHIFT_CTRLR0_DFS 0 +#define BIT_MASK_CTRLR0_DFS 0xF +#define BIT_CTRLR0_DFS(x)(((x) & BIT_MASK_CTRLR0_DFS) << BIT_SHIFT_CTRLR0_DFS) +#define BIT_INVC_CTRLR0_DFS (~(BIT_MASK_CTRLR0_DFS << BIT_SHIFT_CTRLR0_DFS)) + +#define BIT_SHIFT_CTRLR0_FRF 4 +#define BIT_MASK_CTRLR0_FRF 0x3 +#define BIT_CTRLR0_FRF(x)(((x) & BIT_MASK_CTRLR0_FRF) << BIT_SHIFT_CTRLR0_FRF) +#define BIT_INVC_CTRLR0_FRF (~(BIT_MASK_CTRLR0_FRF << BIT_SHIFT_CTRLR0_FRF)) + +#define BIT_SHIFT_CTRLR0_SCPH 6 +#define BIT_MASK_CTRLR0_SCPH 0x1 +#define BIT_CTRLR0_SCPH(x)(((x) & BIT_MASK_CTRLR0_SCPH) << BIT_SHIFT_CTRLR0_SCPH) +#define BIT_INVC_CTRLR0_SCPH (~(BIT_MASK_CTRLR0_SCPH << BIT_SHIFT_CTRLR0_SCPH)) + +#define BIT_SHIFT_CTRLR0_SCPOL 7 +#define BIT_MASK_CTRLR0_SCPOL 0x1 +#define BIT_CTRLR0_SCPOL(x)(((x) & BIT_MASK_CTRLR0_SCPOL) << BIT_SHIFT_CTRLR0_SCPOL) +#define BIT_INVC_CTRLR0_SCPOL (~(BIT_MASK_CTRLR0_SCPOL << BIT_SHIFT_CTRLR0_SCPOL)) + +#define BIT_SHIFT_CTRLR0_TMOD 8 +#define BIT_MASK_CTRLR0_TMOD 0x3 +#define BIT_CTRLR0_TMOD(x)(((x) & BIT_MASK_CTRLR0_TMOD) << BIT_SHIFT_CTRLR0_TMOD) +#define BIT_INVC_CTRLR0_TMOD (~(BIT_MASK_CTRLR0_TMOD << BIT_SHIFT_CTRLR0_TMOD)) + +#define BIT_SHIFT_CTRLR0_SLV_OE 10 +#define BIT_MASK_CTRLR0_SLV_OE 0x1 +#define BIT_CTRLR0_SLV_OE(x)(((x) & BIT_MASK_CTRLR0_SLV_OE) << BIT_SHIFT_CTRLR0_SLV_OE) +#define BIT_INVC_CTRLR0_SLV_OE (~(BIT_MASK_CTRLR0_SLV_OE << BIT_SHIFT_CTRLR0_SLV_OE)) + +#define BIT_SHIFT_CTRLR0_SRL 11 +#define BIT_MASK_CTRLR0_SRL 0x1 +#define BIT_CTRLR0_SRL(x)(((x) & BIT_MASK_CTRLR0_SRL) << BIT_SHIFT_CTRLR0_SRL) +#define BIT_INVC_CTRLR0_SRL (~(BIT_MASK_CTRLR0_SRL << BIT_SHIFT_CTRLR0_SRL)) + +#define BIT_SHIFT_CTRLR0_CFS 12 +#define BIT_MASK_CTRLR0_CFS 0xF +#define BIT_CTRLR0_CFS(x)(((x) & BIT_MASK_CTRLR0_CFS) << BIT_SHIFT_CTRLR0_CFS) +#define BIT_INVC_CTRLR0_CFS (~(BIT_MASK_CTRLR0_CFS << BIT_SHIFT_CTRLR0_CFS)) + +// CTRLR1 0x04 // 16 bits +#define BIT_SHIFT_CTRLR1_NDF 0 +#define BIT_MASK_CTRLR1_NDF 0xFFFF +#define BIT_CTRLR1_NDF(x)(((x) & BIT_MASK_CTRLR1_NDF) << BIT_SHIFT_CTRLR1_NDF) +#define BIT_INVC_CTRLR1_NDF (~(BIT_MASK_CTRLR1_NDF << BIT_SHIFT_CTRLR1_NDF)) + +// SSIENR 0x08 // 1 bit +#define BIT_SHIFT_SSIENR_SSI_EN 0 +#define BIT_MASK_SSIENR_SSI_EN 0x1 +#define BIT_SSIENR_SSI_EN(x)(((x) & BIT_MASK_SSIENR_SSI_EN) << BIT_SHIFT_SSIENR_SSI_EN) +#define BIT_INVC_SSIENR_SSI_EN (~(BIT_MASK_SSIENR_SSI_EN << BIT_SHIFT_SSIENR_SSI_EN)) + +// MWCR 0x0c // 3 bits +#define BIT_SHIFT_MWCR_MWMOD 0 +#define BIT_MASK_MWCR_MWMOD 0x1 +#define BIT_MWCR_MWMOD(x)(((x) & BIT_MASK_MWCR_MWMOD) << BIT_SHIFT_MWCR_MWMOD) +#define BIT_INVC_MWCR_MWMOD (~(BIT_MASK_MWCR_MWMOD << BIT_SHIFT_MWCR_MWMOD)) + +#define BIT_SHIFT_MWCR_MDD 1 +#define BIT_MASK_MWCR_MDD 0x1 +#define BIT_MWCR_MDD(x)(((x) & BIT_MASK_MWCR_MDD) << BIT_SHIFT_MWCR_MDD) +#define BIT_INVC_MWCR_MDD (~(BIT_MASK_MWCR_MDD << BIT_SHIFT_MWCR_MDD)) + +#define BIT_SHIFT_MWCR_MHS 2 +#define BIT_MASK_MWCR_MHS 0x1 +#define BIT_MWCR_MHS(x)(((x) & BIT_MASK_MWCR_MHS) << BIT_SHIFT_MWCR_MHS) +#define BIT_INVC_MWCR_MHS (~(BIT_MASK_MWCR_MHS << BIT_SHIFT_MWCR_MHS)) + +// SER 0x10 // Variable Length +#define BIT_SHIFT_SER_SER 0 +#define BIT_MASK_SER_SER 0xFF +#define BIT_SER_SER(x)(((x) & BIT_MASK_SER_SER) << BIT_SHIFT_SER_SER) +#define BIT_INVC_SER_SER (~(BIT_MASK_SER_SER << BIT_SHIFT_SER_SER)) + +// BAUDR 0x14 // 16 bits +#define BIT_SHIFT_BAUDR_SCKDV 0 +#define BIT_MASK_BAUDR_SCKDV 0xFFFF +#define BIT_BAUDR_SCKDV(x)(((x) & BIT_MASK_BAUDR_SCKDV) << BIT_SHIFT_BAUDR_SCKDV) +#define BIT_INVC_BAUDR_SCKDV (~(BIT_MASK_BAUDR_SCKDV << BIT_SHIFT_BAUDR_SCKDV)) + +// TXFLTR 0x18 // Variable Length +#define BIT_SHIFT_TXFTLR_TFT 0 +#define BIT_MASK_TXFTLR_TFT 0x3F // (TX_ABW-1):0 +#define BIT_TXFTLR_TFT(x)(((x) & BIT_MASK_TXFTLR_TFT) << BIT_SHIFT_TXFTLR_TFT) +#define BIT_INVC_TXFTLR_TFT (~(BIT_MASK_TXFTLR_TFT << BIT_SHIFT_TXFTLR_TFT)) + +// RXFLTR 0x1c // Variable Length +#define BIT_SHIFT_RXFTLR_RFT 0 +#define BIT_MASK_RXFTLR_RFT 0x3F // (RX_ABW-1):0 +#define BIT_RXFTLR_RFT(x)(((x) & BIT_MASK_RXFTLR_RFT) << BIT_SHIFT_RXFTLR_RFT) +#define BIT_INVC_RXFTLR_RFT (~(BIT_MASK_RXFTLR_RFT << BIT_SHIFT_RXFTLR_RFT)) + +// TXFLR 0x20 // see [READ ONLY] +#define BIT_MASK_TXFLR_TXTFL 0x7F // (TX_ABW):0 + +// RXFLR 0x24 // see [READ ONLY] +#define BIT_MASK_RXFLR_RXTFL 0x7F // (RX_ABW):0 + +// SR 0x28 // 7 bits [READ ONLY] +#define BIT_SR_BUSY BIT0 +#define BIT_SR_TFNF BIT1 +#define BIT_SR_TFE BIT2 +#define BIT_SR_RFNE BIT3 +#define BIT_SR_RFF BIT4 +#define BIT_SR_TXE BIT5 +#define BIT_SR_DCOL BIT6 + +// IMR 0x2c // see +#define BIT_SHIFT_IMR_TXEIM 0 +#define BIT_MASK_IMR_TXEIM 0x1 +// #define BIT_IMR_TXEIM(x)(((x) & BIT_MASK_IMR_TXEIM) << BIT_SHIFT_IMR_TXEIM) +#define BIT_INVC_IMR_TXEIM (~(BIT_MASK_IMR_TXEIM << BIT_SHIFT_IMR_TXEIM)) + +#define BIT_SHIFT_IMR_TXOIM 1 +#define BIT_MASK_IMR_TXOIM 0x1 +// #define BIT_IMR_TXOIM(x)(((x) & BIT_MASK_IMR_TXOIM) << BIT_SHIFT_IMR_TXOIM) +#define BIT_INVC_IMR_TXOIM (~(BIT_MASK_IMR_TXOIM << BIT_SHIFT_IMR_TXOIM)) + +#define BIT_SHIFT_IMR_RXUIM 2 +#define BIT_MASK_IMR_RXUIM 0x1 +// #define BIT_IMR_RXUIM(x)(((x) & BIT_MASK_IMR_RXUIM) << BIT_SHIFT_IMR_RXUIM) +#define BIT_INVC_IMR_RXUIM (~(BIT_MASK_IMR_RXUIM << BIT_SHIFT_IMR_RXUIM)) + +#define BIT_SHIFT_IMR_RXOIM 3 +#define BIT_MASK_IMR_RXOIM 0x1 +// #define BIT_IMR_RXOIM(x)(((x) & BIT_MASK_IMR_RXOIM) << BIT_SHIFT_IMR_RXOIM) +#define BIT_INVC_IMR_RXOIM (~(BIT_MASK_IMR_RXOIM << BIT_SHIFT_IMR_RXOIM)) + +#define BIT_SHIFT_IMR_RXFIM 4 +#define BIT_MASK_IMR_RXFIM 0x1 +// #define BIT_IMR_RXFIM(x)(((x) & BIT_MASK_IMR_RXFIM) << BIT_SHIFT_IMR_RXFIM) +#define BIT_INVC_IMR_RXFIM (~(BIT_MASK_IMR_RXFIM << BIT_SHIFT_IMR_RXFIM)) + +#define BIT_SHIFT_IMR_MSTIM 5 +#define BIT_MASK_IMR_MSTIM 0x1 +// #define BIT_IMR_MSTIM(x)(((x) & BIT_MASK_IMR_MSTIM) << BIT_SHIFT_IMR_MSTIM) +#define BIT_INVC_IMR_MSTIM (~(BIT_MASK_IMR_MSTIM << BIT_SHIFT_IMR_MSTIM)) + +#define BIT_IMR_TXEIM BIT0 +#define BIT_IMR_TXOIM BIT1 +#define BIT_IMR_RXUIM BIT2 +#define BIT_IMR_RXOIM BIT3 +#define BIT_IMR_RXFIM BIT4 +#define BIT_IMR_MSTIM BIT5 + +// ISR 0x30 // 6 bits [READ ONLY] +#define BIT_ISR_TXEIS BIT0 +#define BIT_ISR_TXOIS BIT1 +#define BIT_ISR_RXUIS BIT2 +#define BIT_ISR_RXOIS BIT3 +#define BIT_ISR_RXFIS BIT4 +#define BIT_ISR_MSTIS BIT5 + +// RISR 0x34 // 6 bits [READ ONLY] +#define BIT_RISR_TXEIR BIT0 +#define BIT_RISR_TXOIR BIT1 +#define BIT_RISR_RXUIR BIT2 +#define BIT_RISR_RXOIR BIT3 +#define BIT_RISR_RXFIR BIT4 +#define BIT_RISR_MSTIR BIT5 + +// TXOICR 0x38 // 1 bits [READ ONLY] +// RXOICR 0x3c // 1 bits [READ ONLY] +// RXUICR 0x40 // 1 bits [READ ONLY] +// MSTICR 0x44 // 1 bits [READ ONLY] +// ICR 0x48 // 1 bits [READ ONLY] + +// DMACR 0x4c // 2 bits +#define BIT_SHIFT_DMACR_RDMAE 0 +#define BIT_MASK_DMACR_RDMAE 0x1 +#define BIT_DMACR_RDMAE(x)(((x) & BIT_MASK_DMACR_RDMAE) << BIT_SHIFT_DMACR_RDMAE) +#define BIT_INVC_DMACR_RDMAE (~(BIT_MASK_DMACR_RDMAE << BIT_SHIFT_DMACR_RDMAE)) + +#define BIT_SHIFT_DMACR_TDMAE 1 +#define BIT_MASK_DMACR_TDMAE 0x1 +#define BIT_DMACR_TDMAE(x)(((x) & BIT_MASK_DMACR_TDMAE) << BIT_SHIFT_DMACR_TDMAE) +#define BIT_INVC_DMACR_TDMAE (~(BIT_MASK_DMACR_TDMAE << BIT_SHIFT_DMACR_TDMAE)) + +// DMATDLR 0x50 +#define BIT_SHIFT_DMATDLR_DMATDL 0 +#define BIT_MASK_DMATDLR_DMATDL 0x3F // (TX_ABW-1):0 +#define BIT_DMATDLR_DMATDL(x)(((x) & BIT_MASK_DMATDLR_DMATDL) << BIT_SHIFT_DMATDLR_DMATDL) +#define BIT_INVC_DMATDLR_DMATDL (~(BIT_MASK_DMATDLR_DMATDL << BIT_SHIFT_DMATDLR_DMATDL)) + +// DMARDLR 0x54 +#define BIT_SHIFT_DMARDLR_DMARDL 0 +#define BIT_MASK_DMARDLR_DMARDL 0x3F // (RX_ABW-1):0 +#define BIT_DMARDLR_DMARDL(x)(((x) & BIT_MASK_DMARDLR_DMARDL) << BIT_SHIFT_DMARDLR_DMARDL) +#define BIT_INVC_DMARDLR_DMARDL (~(BIT_MASK_DMARDLR_DMARDL << BIT_SHIFT_DMARDLR_DMARDL)) + +// IDR 0x58 // 32 bits [READ ONLY] +// COMP_VERSION 0x5c // 32 bits [READ ONLY] + +// DR 0x60 // 16 bits 0x60-0xEC +#define BIT_SHIFT_DR_DR 0 +#define BIT_MASK_DR_DR 0xFFFF +#define BIT_DR_DR(x)(((x) & BIT_MASK_DR_DR) << BIT_SHIFT_DR_DR) +#define BIT_INVC_DR_DR (~(BIT_MASK_DR_DR << BIT_SHIFT_DR_DR)) + +// RX_SAMPLE_DLY 0xF0 // 8 bits +#define BIT_SHIFT_RX_SAMPLE_DLY_RSD 0 +#define BIT_MASK_RX_SAMPLE_DLY_RSD 0xFFFF +#define BIT_RX_SAMPLE_DLY_RSD(x)(((x) & BIT_MASK_RX_SAMPLE_DLY_RSD) << BIT_SHIFT_RX_SAMPLE_DLY_RSD) +#define BIT_INVC_RX_SAMPLE_DLY_RSD (~(BIT_MASK_RX_SAMPLE_DLY_RSD << BIT_SHIFT_RX_SAMPLE_DLY_RSD)) + +// RSVD_0 0xF4 // 32 bits +// RSVD_1 0xF8 // 32 bits +// RSVD_2 0xFC // 32 bits + +// SSI0 Pinmux +#define BIT_SHIFT_SSI0_PIN_EN 0 +#define BIT_MASK_SSI0_PIN_EN 0x1 +#define BIT_SSI0_PIN_EN(x)(((x) & BIT_MASK_SSI0_PIN_EN) << BIT_SHIFT_SSI0_PIN_EN) +#define BIT_INVC_SSI0_PIN_EN (~(BIT_MASK_SSI0_PIN_EN << BIT_SHIFT_SSI0_PIN_EN)) + +#define BIT_SHIFT_SSI0_PIN_SEL 1 +#define BIT_MASK_SSI0_PIN_SEL 0x7 +#define BIT_SSI0_PIN_SEL(x)(((x) & BIT_MASK_SSI0_PIN_SEL) << BIT_SHIFT_SSI0_PIN_SEL) +#define BIT_INVC_SSI0_PIN_SEL (~(BIT_MASK_SSI0_PIN_SEL << BIT_SHIFT_SSI0_PIN_SEL)) + +// SSI1 Pinmux +#define BIT_SHIFT_SSI1_PIN_EN 4 +#define BIT_MASK_SSI1_PIN_EN 0x1 +#define BIT_SSI1_PIN_EN(x)(((x) & BIT_MASK_SSI1_PIN_EN) << BIT_SHIFT_SSI1_PIN_EN) +#define BIT_INVC_SSI1_PIN_EN (~(BIT_MASK_SSI1_PIN_EN << BIT_SHIFT_SSI1_PIN_EN)) + +#define BIT_SHIFT_SSI1_PIN_SEL 5 +#define BIT_MASK_SSI1_PIN_SEL 0x7 +#define BIT_SSI1_PIN_SEL(x)(((x) & BIT_MASK_SSI1_PIN_SEL) << BIT_SHIFT_SSI1_PIN_SEL) +#define BIT_INVC_SSI1_PIN_SEL (~(BIT_MASK_SSI1_PIN_SEL << BIT_SHIFT_SSI1_PIN_SEL)) + +// SSI2 Pinmux +#define BIT_SHIFT_SSI2_PIN_EN 8 +#define BIT_MASK_SSI2_PIN_EN 0x1 +#define BIT_SSI2_PIN_EN(x)(((x) & BIT_MASK_SSI2_PIN_EN) << BIT_SHIFT_SSI2_PIN_EN) +#define BIT_INVC_SSI2_PIN_EN (~(BIT_MASK_SSI2_PIN_EN << BIT_SHIFT_SSI2_PIN_EN)) + +#define BIT_SHIFT_SSI2_PIN_SEL 9 +#define BIT_MASK_SSI2_PIN_SEL 0x7 +#define BIT_SSI2_PIN_SEL(x)(((x) & BIT_MASK_SSI2_PIN_SEL) << BIT_SHIFT_SSI2_PIN_SEL) +#define BIT_INVC_SSI2_PIN_SEL (~(BIT_MASK_SSI2_PIN_SEL << BIT_SHIFT_SSI2_PIN_SEL)) + +// SSI0 Multiple Chip Selection (Pinmux Select is controlled by BIT_SSI0_PIN_SEL) +#define BIT_SHIFT_SSI0_MULTI_CS_EN 28 +#define BIT_MASK_SSI0_MULTI_CS_EN 0x1 +#define BIT_SSI0_MULTI_CS_EN(x)(((x) & BIT_MASK_SSI0_MULTI_CS_EN) << BIT_SHIFT_SSI0_MULTI_CS_EN) +#define BIT_INVC_SSI0_MULTI_CS_EN (~(BIT_MASK_SSI0_MULTI_CS_EN << BIT_SHIFT_SSI0_MULTI_CS_EN)) + + +#define HAL_SSI_READ32(SsiIndex, addr) \ + HAL_READ32(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr) +#define HAL_SSI_WRITE32(SsiIndex, addr, value) \ + HAL_WRITE32(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr, value) +#define HAL_SSI_READ16(SsiIndex, addr) \ + HAL_READ16(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr) +#define HAL_SSI_WRITE16(SsiIndex, addr, value) \ + HAL_WRITE16(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr, value) +#define HAL_SSI_READ8(SsiIndex, addr) \ + HAL_READ8(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr) +#define HAL_SSI_WRITE8(SsiIndex, addr, value) \ + HAL_WRITE8(SPI0_REG_BASE+ (SsiIndex*SSI_REG_OFF), addr, value) + + +// SSI Pinmux Select +typedef enum _SSI0_PINMUX_SELECT_ { + SSI0_MUX_TO_GPIOE = S0, + SSI0_MUX_TO_GPIOC = S1 +}SSI0_PINMUX_SELECT, *PSSI0_PINMUX_SELECT; + +typedef enum _SSI1_PINMUX_SELECT_ { + SSI1_MUX_TO_GPIOA = S0, + SSI1_MUX_TO_GPIOB = S1, + SSI1_MUX_TO_GPIOD = S2 +}SSI1_PINMUX_SELECT, *PSSI1_PINMUX_SELECT; + +typedef enum _SSI2_PINMUX_SELECT_ { + SSI2_MUX_TO_GPIOG = S0, + SSI2_MUX_TO_GPIOE = S1, + SSI2_MUX_TO_GPIOD = S2 +}SSI2_PINMUX_SELECT, *PSSI2_PINMUX_SELECT; + +typedef enum _SSI0_MULTI_CS_PINMUX_SELECT_ { + SSI0_CS_MUX_TO_GPIOE = S0, + SSI0_CS_MUX_TO_GPIOC = S1 +}SSI0_MULTI_CS_PINMUX_SELECT, *PSSI0_MULTI_CS_PINMUX_SELECT; + +typedef enum _SSI_CTRLR0_TMOD_ { + TMOD_TR = 0, + TMOD_TO = 1, + TMOD_RO = 2, + TMOD_EEPROM_R = 3 +}SSI_CTRLR0_TMOD, *PSSI_CTRLR0_TMOD; + +typedef enum _SSI_CTRLR0_SCPOL_ { + SCPOL_INACTIVE_IS_LOW = 0, + SCPOL_INACTIVE_IS_HIGH = 1 +}SSI_CTRLR0_SCPOL, *PSSI_CTRLR0_SCPOL; + +typedef enum _SSI_CTRLR0_SCPH_ { + SCPH_TOGGLES_IN_MIDDLE = 0, + SCPH_TOGGLES_AT_START = 1 +}SSI_CTRLR0_SCPH, *PSSI_CTRLR0_SCPH; + +typedef enum _SSI_CTRLR0_DFS_ { + DFS_4_BITS = 3, + DFS_5_BITS = 4, + DFS_6_BITS = 5, + DFS_7_BITS = 6, + DFS_8_BITS = 7, + DFS_9_BITS = 8, + DFS_10_BITS = 9, + DFS_11_BITS = 10, + DFS_12_BITS = 11, + DFS_13_BITS = 12, + DFS_14_BITS = 13, + DFS_15_BITS = 14, + DFS_16_BITS = 15, +}SSI_CTRLR0_DFS, *PSSI_CTRLR0_DFS; + +typedef enum _SSI_CTRLR0_CFS_ { + CFS_1_BIT = 0, + CFS_2_BITS = 1, + CFS_3_BITS = 2, + CFS_4_BITS = 3, + CFS_5_BITS = 4, + CFS_6_BITS = 5, + CFS_7_BITS = 6, + CFS_8_BITS = 7, + CFS_9_BITS = 8, + CFS_10_BITS = 9, + CFS_11_BITS = 10, + CFS_12_BITS = 11, + CFS_13_BITS = 12, + CFS_14_BITS = 13, + CFS_15_BITS = 14, + CFS_16_BITS = 15 +}SSI_CTRLR0_CFS, *PSSI_CTRLR0_CFS; + +typedef enum _SSI_CTRLR0_SLV_OE_ { + SLV_TXD_ENABLE = 0, + SLV_TXD_DISABLE = 1 +}SSI_CTRLR0_SLV_OE, *PSSI_CTRLR0_SLV_OE; + +typedef enum _SSI_ROLE_SELECT_ { + SSI_SLAVE = 0, + SSI_MASTER = 1 +}SSI_ROLE_SELECT, *PSSI_ROLE_SELECT; + +typedef enum _SSI_FRAME_FORMAT_ { + FRF_MOTOROLA_SPI = 0, + FRF_TI_SSP = 1, + FRF_NS_MICROWIRE = 2, + FRF_RSVD = 3 +}SSI_FRAME_FORMAT, *PSSI_FRAME_FORMAT; + +typedef enum _SSI_DMACR_ENABLE_ { + SSI_NODMA = 0, + SSI_RXDMA_ENABLE = 1, + SSI_TXDMA_ENABLE = 2, + SSI_TRDMA_ENABLE = 3 +}SSI_DMACR_ENABLE, *PSSI_DMACR_ENABLE; + +typedef enum _SSI_MWCR_HANDSHAKE_ { + MW_HANDSHAKE_DISABLE = 0, + MW_HANDSHAKE_ENABLE = 1 +}SSI_MWCR_HANDSHAKE, *PSSI_MWCR_HANDSHAKE; + +typedef enum _SSI_MWCR_DIRECTION_ { + MW_DIRECTION_SLAVE_TO_MASTER = 0, + MW_DIRECTION_MASTER_TO_SLAVE = 1 +}SSI_MWCR_DIRECTION, *PSSI_MWCR_DIRECTION; + +typedef enum _SSI_MWCR_TMOD_ { + MW_TMOD_NONSEQUENTIAL = 0, + MW_TMOD_SEQUENTIAL = 1 +}SSI_MWCR_TMOD, *PSSI_MWCR_TMOD; + +typedef enum _SSI_DATA_TRANSFER_MECHANISM_ { + SSI_DTM_BASIC, + SSI_DTM_INTERRUPT, + SSI_DTM_DMA +}SSI_DATA_TRANSFER_MECHANISM, *PSSI_DATA_TRANSFER_MECHANISM; + + +_LONG_CALL_ HAL_Status HalSsiPinmuxEnableRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiEnableRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiDisableRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiInitRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiSetSclkPolarityRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiSetSclkPhaseRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiWriteRtl8195a(VOID *Adaptor, u32 value); +_LONG_CALL_ HAL_Status HalSsiLoadSettingRtl8195a(VOID *Adaptor, VOID *Setting); +_LONG_CALL_ HAL_Status HalSsiSetInterruptMaskRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiSetDeviceRoleRtl8195a(VOID *Adaptor, u32 Role); +_LONG_CALL_ HAL_Status HalSsiInterruptEnableRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiInterruptDisableRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiReadInterruptRtl8195a(VOID *Adaptor, VOID *RxData, u32 Length); +_LONG_CALL_ HAL_Status HalSsiSetRxFifoThresholdLevelRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiSetTxFifoThresholdLevelRtl8195a(VOID *Adaptor); +_LONG_CALL_ HAL_Status HalSsiWriteInterruptRtl8195a(VOID *Adaptor, VOID *TxData, u32 Length); +_LONG_CALL_ HAL_Status HalSsiSetSlaveEnableRegisterRtl8195a(VOID *Adaptor, u32 SlaveIndex); +_LONG_CALL_ u32 HalSsiBusyRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiWriteableRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiReadableRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetInterruptMaskRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetRxFifoLevelRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetTxFifoLevelRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetStatusRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetInterruptStatusRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiReadRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetRawInterruptStatusRtl8195a(VOID *Adaptor); +_LONG_CALL_ u32 HalSsiGetSlaveEnableRegisterRtl8195a(VOID *Adaptor); + +_LONG_CALL_ VOID _SsiReadInterrupt(VOID *Adaptor); +_LONG_CALL_ VOID _SsiWriteInterrupt(VOID *Adaptor); +_LONG_CALL_ u32 _SsiIrqHandle(VOID *Adaptor); + +// ROM code patch +VOID _SsiReadInterruptRtl8195a(VOID *Adapter); +VOID _SsiWriteInterruptRtl8195a(VOID *Adapter); +HAL_Status HalSsiInitRtl8195a_Patch(VOID *Adaptor); +HAL_Status HalSsiPinmuxEnableRtl8195a_Patch(VOID *Adaptor); +HAL_Status HalSsiPinmuxDisableRtl8195a(VOID *Adaptor); +VOID HalSsiSetSclkRtl8195a(VOID *Adapter, u32 ClkRate); +HAL_Status HalSsiIntReadRtl8195a(VOID *Adapter, VOID *RxData, u32 Length); +HAL_Status HalSsiIntWriteRtl8195a(VOID *Adapter, u8 *pTxData, u32 Length); +#ifdef CONFIG_GDMA_EN +VOID HalSsiTxGdmaLoadDefRtl8195a(VOID *Adapter); +VOID HalSsiRxGdmaLoadDefRtl8195a(VOID *Adapter); +VOID HalSsiDmaInitRtl8195a(VOID *Adapter); +HAL_Status HalSsiDmaSendRtl8195a(VOID *Adapter, u8 *pTxData, u32 Length); +HAL_Status HalSsiDmaRecvRtl8195a(VOID *Adapter, u8 *pRxData, u32 Length); +#endif // end of "#ifdef CONFIG_GDMA_EN" + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sys_on.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sys_on.h new file mode 100644 index 0000000..2c5ef62 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_sys_on.h @@ -0,0 +1,1093 @@ +#ifndef __INC_RTL8195A_SYS_ON_BIT_H +#define __INC_RTL8195A_SYS_ON_BIT_H + +#define CPU_OPT_WIDTH 0x1F + +//2 REG_NOT_VALID + +//2 REG_SYS_PWR_CTRL + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SYS_PWR_SOC_EN BIT(2) +#define BIT_SYS_PWR_RET_MEM_EN BIT(1) +#define BIT_SYS_PWR_PEON_EN BIT(0) + +//2 REG_SYS_ISO_CTRL + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SYS_ISO_SYSPLL BIT(7) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SYS_ISO_SOC BIT(2) +#define BIT_SYS_ISO_RET_MEM BIT(1) +#define BIT_SYS_ISO_PEON BIT(0) + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SYS_FUNC_EN +#define BIT_SYS_AMACRO_EN BIT(31) +#define BIT_SYS_PWRON_TRAP_SHTDN_N BIT(30) +#define BIT_SYS_FEN_SIC_MST BIT(25) +#define BIT_SYS_FEN_SIC BIT(24) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SOC_SYSPEON_EN BIT(4) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SYS_FEN_EELDR BIT(0) + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SYS_CLK_CTRL0 + +//2 REG_NOT_VALID +#define BIT_SOC_OCP_IOBUS_CK_EN BIT(2) +#define BIT_SYSON_CK_EELDR_EN BIT(1) +#define BIT_SYSON_CK_SYSREG_EN BIT(0) + +//2 REG_SYS_CLK_CTRL1 + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +#define BIT_SHIFT_PESOC_OCP_CPU_CK_SEL 4 +#define BIT_MASK_PESOC_OCP_CPU_CK_SEL 0x7 +#define BIT_PESOC_OCP_CPU_CK_SEL(x) (((x) & BIT_MASK_PESOC_OCP_CPU_CK_SEL) << BIT_SHIFT_PESOC_OCP_CPU_CK_SEL) + + +//2 REG_NOT_VALID +#define BIT_PESOC_EELDR_CK_SEL BIT(0) + +//2 REG_SYS_SWR_CTRL3 + +//2 REG_RSV_CTRL + +//2 REG_RF_CTRL + +//2 REG_SYS_EFUSE_SYSCFG0 + +#define BIT_SHIFT_SYS_EEROM_SWR_PAR_05_00 24 +#define BIT_MASK_SYS_EEROM_SWR_PAR_05_00 0x3f +#define BIT_SYS_EEROM_SWR_PAR_05_00(x) (((x) & BIT_MASK_SYS_EEROM_SWR_PAR_05_00) << BIT_SHIFT_SYS_EEROM_SWR_PAR_05_00) + + +#define BIT_SHIFT_SYS_EEROM_LDO_PAR_07_04 20 +#define BIT_MASK_SYS_EEROM_LDO_PAR_07_04 0xf +#define BIT_SYS_EEROM_LDO_PAR_07_04(x) (((x) & BIT_MASK_SYS_EEROM_LDO_PAR_07_04) << BIT_SHIFT_SYS_EEROM_LDO_PAR_07_04) + +#define BIT_SYS_CHIPPDN_EN BIT(17) +#define BIT_SYS_EEROM_B12V_EN BIT(16) + +#define BIT_SHIFT_SYS_EEROM_VID1 8 +#define BIT_MASK_SYS_EEROM_VID1 0xff +#define BIT_SYS_EEROM_VID1(x) (((x) & BIT_MASK_SYS_EEROM_VID1) << BIT_SHIFT_SYS_EEROM_VID1) + + +#define BIT_SHIFT_SYS_EEROM_VID0 0 +#define BIT_MASK_SYS_EEROM_VID0 0xff +#define BIT_SYS_EEROM_VID0(x) (((x) & BIT_MASK_SYS_EEROM_VID0) << BIT_SHIFT_SYS_EEROM_VID0) + + +//2 REG_SYS_EFUSE_SYSCFG1 + +#define BIT_SHIFT_SYS_PDSPL_STL 24 +#define BIT_MASK_SYS_PDSPL_STL 0x3 +#define BIT_SYS_PDSPL_STL(x) (((x) & BIT_MASK_SYS_PDSPL_STL) << BIT_SHIFT_SYS_PDSPL_STL) + + +#define BIT_SHIFT_SYS_PDSOC_STL 22 +#define BIT_MASK_SYS_PDSOC_STL 0x3 +#define BIT_SYS_PDSOC_STL(x) (((x) & BIT_MASK_SYS_PDSOC_STL) << BIT_SHIFT_SYS_PDSOC_STL) + + +#define BIT_SHIFT_SYS_PDPON_STL 20 +#define BIT_MASK_SYS_PDPON_STL 0x3 +#define BIT_SYS_PDPON_STL(x) (((x) & BIT_MASK_SYS_PDPON_STL) << BIT_SHIFT_SYS_PDPON_STL) + + +#define BIT_SHIFT_SYS_SWREG_XRT 18 +#define BIT_MASK_SYS_SWREG_XRT 0x3 +#define BIT_SYS_SWREG_XRT(x) (((x) & BIT_MASK_SYS_SWREG_XRT) << BIT_SHIFT_SYS_SWREG_XRT) + + +#define BIT_SHIFT_SYS_SWSLC_STL 16 +#define BIT_MASK_SYS_SWSLC_STL 0x3 +#define BIT_SYS_SWSLC_STL(x) (((x) & BIT_MASK_SYS_SWSLC_STL) << BIT_SHIFT_SYS_SWSLC_STL) + + +#define BIT_SHIFT_SYS_EEROM_SWR_PAR_46_45 14 +#define BIT_MASK_SYS_EEROM_SWR_PAR_46_45 0x3 +#define BIT_SYS_EEROM_SWR_PAR_46_45(x) (((x) & BIT_MASK_SYS_EEROM_SWR_PAR_46_45) << BIT_SHIFT_SYS_EEROM_SWR_PAR_46_45) + + +#define BIT_SHIFT_SYS_EEROM_SWR_PAR_40_39 12 +#define BIT_MASK_SYS_EEROM_SWR_PAR_40_39 0x3 +#define BIT_SYS_EEROM_SWR_PAR_40_39(x) (((x) & BIT_MASK_SYS_EEROM_SWR_PAR_40_39) << BIT_SHIFT_SYS_EEROM_SWR_PAR_40_39) + + +#define BIT_SHIFT_SYS_EEROM_SWR_PAR_33_26 4 +#define BIT_MASK_SYS_EEROM_SWR_PAR_33_26 0xff +#define BIT_SYS_EEROM_SWR_PAR_33_26(x) (((x) & BIT_MASK_SYS_EEROM_SWR_PAR_33_26) << BIT_SHIFT_SYS_EEROM_SWR_PAR_33_26) + + +#define BIT_SHIFT_SYS_EEROM_SWSLD_VOL 0 +#define BIT_MASK_SYS_EEROM_SWSLD_VOL 0x7 +#define BIT_SYS_EEROM_SWSLD_VOL(x) (((x) & BIT_MASK_SYS_EEROM_SWSLD_VOL) << BIT_SHIFT_SYS_EEROM_SWSLD_VOL) + + +//2 REG_SYS_EFUSE_SYSCFG2 + +#define BIT_SHIFT_SYS_EERROM_ANAPAR_SPLL_24_15 21 +#define BIT_MASK_SYS_EERROM_ANAPAR_SPLL_24_15 0x3ff +#define BIT_SYS_EERROM_ANAPAR_SPLL_24_15(x) (((x) & BIT_MASK_SYS_EERROM_ANAPAR_SPLL_24_15) << BIT_SHIFT_SYS_EERROM_ANAPAR_SPLL_24_15) + + +#define BIT_SHIFT_SYS_EEROM_ANAPAR_SPLL_05_02 16 +#define BIT_MASK_SYS_EEROM_ANAPAR_SPLL_05_02 0xf +#define BIT_SYS_EEROM_ANAPAR_SPLL_05_02(x) (((x) & BIT_MASK_SYS_EEROM_ANAPAR_SPLL_05_02) << BIT_SHIFT_SYS_EEROM_ANAPAR_SPLL_05_02) + + +#define BIT_SHIFT_SYS_EEROM_XTAL_STEL_SEL 12 +#define BIT_MASK_SYS_EEROM_XTAL_STEL_SEL 0x3 +#define BIT_SYS_EEROM_XTAL_STEL_SEL(x) (((x) & BIT_MASK_SYS_EEROM_XTAL_STEL_SEL) << BIT_SHIFT_SYS_EEROM_XTAL_STEL_SEL) + + +#define BIT_SHIFT_SYS_EEROM_XTAL_FREQ_SEL 8 +#define BIT_MASK_SYS_EEROM_XTAL_FREQ_SEL 0xf +#define BIT_SYS_EEROM_XTAL_FREQ_SEL(x) (((x) & BIT_MASK_SYS_EEROM_XTAL_FREQ_SEL) << BIT_SHIFT_SYS_EEROM_XTAL_FREQ_SEL) + + +//2 REG_SYS_EFUSE_SYSCFG3 + +#define BIT_SHIFT_SYS_DBG_PINGP_EN 28 +#define BIT_MASK_SYS_DBG_PINGP_EN 0xf +#define BIT_SYS_DBG_PINGP_EN(x) (((x) & BIT_MASK_SYS_DBG_PINGP_EN) << BIT_SHIFT_SYS_DBG_PINGP_EN) + + +#define BIT_SHIFT_SYS_DBG_SEL 16 +#define BIT_MASK_SYS_DBG_SEL 0xfff +#define BIT_SYS_DBG_SEL(x) (((x) & BIT_MASK_SYS_DBG_SEL) << BIT_SHIFT_SYS_DBG_SEL) + + +#define BIT_SHIFT_SYS_DBGBY3_LOC_SEL 14 +#define BIT_MASK_SYS_DBGBY3_LOC_SEL 0x3 +#define BIT_SYS_DBGBY3_LOC_SEL(x) (((x) & BIT_MASK_SYS_DBGBY3_LOC_SEL) << BIT_SHIFT_SYS_DBGBY3_LOC_SEL) + + +#define BIT_SHIFT_SYS_DBGBY2_LOC_SEL 12 +#define BIT_MASK_SYS_DBGBY2_LOC_SEL 0x3 +#define BIT_SYS_DBGBY2_LOC_SEL(x) (((x) & BIT_MASK_SYS_DBGBY2_LOC_SEL) << BIT_SHIFT_SYS_DBGBY2_LOC_SEL) + + +#define BIT_SHIFT_SYS_DBGBY1_LOC_SEL 10 +#define BIT_MASK_SYS_DBGBY1_LOC_SEL 0x3 +#define BIT_SYS_DBGBY1_LOC_SEL(x) (((x) & BIT_MASK_SYS_DBGBY1_LOC_SEL) << BIT_SHIFT_SYS_DBGBY1_LOC_SEL) + + +#define BIT_SHIFT_SYS_DBGBY0_LOC_SEL 8 +#define BIT_MASK_SYS_DBGBY0_LOC_SEL 0x3 +#define BIT_SYS_DBGBY0_LOC_SEL(x) (((x) & BIT_MASK_SYS_DBGBY0_LOC_SEL) << BIT_SHIFT_SYS_DBGBY0_LOC_SEL) + +#define BIT_SYS_EEROM_ANAPAR_SPLL_49 BIT(3) + +#define BIT_SHIFT_SYS_EEROM_ANAPAR_SPLL_27_25 0 +#define BIT_MASK_SYS_EEROM_ANAPAR_SPLL_27_25 0x7 +#define BIT_SYS_EEROM_ANAPAR_SPLL_27_25(x) (((x) & BIT_MASK_SYS_EEROM_ANAPAR_SPLL_27_25) << BIT_SHIFT_SYS_EEROM_ANAPAR_SPLL_27_25) + + +//2 REG_SYS_EFUSE_SYSCFG4 + +#define BIT_SHIFT_SYS_GPIOA_E2 1 +#define BIT_MASK_SYS_GPIOA_E2 0x7 +#define BIT_SYS_GPIOA_E2(x) (((x) & BIT_MASK_SYS_GPIOA_E2) << BIT_SHIFT_SYS_GPIOA_E2) + +#define BIT_SYS_GPIOA_H3L1 BIT(0) + +//2 REG_SYS_EFUSE_SYSCFG5 + +//2 REG_NOT_VALID + +//2 REG_SYS_EFUSE_SYSCFG6 + +#define BIT_SHIFT_SYS_SPIC_INIT_BAUD_RATE_SEL 26 +#define BIT_MASK_SYS_SPIC_INIT_BAUD_RATE_SEL 0x3 +#define BIT_SYS_SPIC_INIT_BAUD_RATE_SEL(x) (((x) & BIT_MASK_SYS_SPIC_INIT_BAUD_RATE_SEL) << BIT_SHIFT_SYS_SPIC_INIT_BAUD_RATE_SEL) + + +#define BIT_SHIFT_SYS_CPU_CLK_SEL 24 +#define BIT_MASK_SYS_CPU_CLK_SEL 0x3 +#define BIT_SYS_CPU_CLK_SEL(x) (((x) & BIT_MASK_SYS_CPU_CLK_SEL) << BIT_SHIFT_SYS_CPU_CLK_SEL) + + +//2 REG_SYS_EFUSE_SYSCFG7 +#define BIT_SYS_MEM_RMV_SIGN BIT(31) +#define BIT_SYS_MEM_RMV_1PRF1 BIT(29) +#define BIT_SYS_MEM_RMV_1PRF0 BIT(28) +#define BIT_SYS_MEM_RMV_1PSR BIT(27) +#define BIT_SYS_MEM_RMV_1PHSR BIT(26) +#define BIT_SYS_MEM_RMV_ROM BIT(25) + +#define BIT_SHIFT_SYS_MEM_RME_CPU 22 +#define BIT_MASK_SYS_MEM_RME_CPU 0x7 +#define BIT_SYS_MEM_RME_CPU(x) (((x) & BIT_MASK_SYS_MEM_RME_CPU) << BIT_SHIFT_SYS_MEM_RME_CPU) + + +#define BIT_SHIFT_SYS_MEM_RME_WLAN 19 +#define BIT_MASK_SYS_MEM_RME_WLAN 0x7 +#define BIT_SYS_MEM_RME_WLAN(x) (((x) & BIT_MASK_SYS_MEM_RME_WLAN) << BIT_SHIFT_SYS_MEM_RME_WLAN) + +#define BIT_SYS_MEM_RME_USB BIT(18) +#define BIT_SYS_MEM_RME_SDIO BIT(17) + +//2 REG_SYS_REGU_CTRL0 + +#define BIT_SHIFT_SYS_REGU_LDO25M_ADJ 20 +#define BIT_MASK_SYS_REGU_LDO25M_ADJ 0xf +#define BIT_SYS_REGU_LDO25M_ADJ(x) (((x) & BIT_MASK_SYS_REGU_LDO25M_ADJ) << BIT_SHIFT_SYS_REGU_LDO25M_ADJ) + +#define BIT_SYS_REGU_ANACK_4M_EN BIT(19) +#define BIT_SYS_REGU_ANACK_4M_SEL BIT(18) +#define BIT_SYS_REGU_PC_EF_EN BIT(17) +#define BIT_SYS_REGU_LDOH12_SLP_EN BIT(16) + +#define BIT_SHIFT_SYS_REGU_LDOH12_ADJ 12 +#define BIT_MASK_SYS_REGU_LDOH12_ADJ 0xf +#define BIT_SYS_REGU_LDOH12_ADJ(x) (((x) & BIT_MASK_SYS_REGU_LDOH12_ADJ) << BIT_SHIFT_SYS_REGU_LDOH12_ADJ) + + +#define BIT_SHIFT_SYS_REGU_LDO25E_ADJ 8 +#define BIT_MASK_SYS_REGU_LDO25E_ADJ 0xf +#define BIT_SYS_REGU_LDO25E_ADJ(x) (((x) & BIT_MASK_SYS_REGU_LDO25E_ADJ) << BIT_SHIFT_SYS_REGU_LDO25E_ADJ) + +#define BIT_SYS_REGU_DSLEPM_EN BIT(7) +#define BIT_SYS_REGU_PC_33V_EN BIT(3) +#define BIT_SYS_REGU_PC_EF25_EN BIT(2) +#define BIT_SYS_REGU_LDO25M_EN BIT(1) +#define BIT_SYS_REGU_LDO25E_EN BIT(0) + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SYS_SWR_CTRL0 + +#define BIT_SHIFT_SYS_SWR12_COMP_R2 30 +#define BIT_MASK_SYS_SWR12_COMP_R2 0x3 +#define BIT_SYS_SWR12_COMP_R2(x) (((x) & BIT_MASK_SYS_SWR12_COMP_R2) << BIT_SHIFT_SYS_SWR12_COMP_R2) + + +#define BIT_SHIFT_SYS_SWR12_COMP_R1 28 +#define BIT_MASK_SYS_SWR12_COMP_R1 0x3 +#define BIT_SYS_SWR12_COMP_R1(x) (((x) & BIT_MASK_SYS_SWR12_COMP_R1) << BIT_SHIFT_SYS_SWR12_COMP_R1) + + +#define BIT_SHIFT_SYS_SWR12_COMP_C3 26 +#define BIT_MASK_SYS_SWR12_COMP_C3 0x3 +#define BIT_SYS_SWR12_COMP_C3(x) (((x) & BIT_MASK_SYS_SWR12_COMP_C3) << BIT_SHIFT_SYS_SWR12_COMP_C3) + + +#define BIT_SHIFT_SYS_SWR12_COMP_C2 24 +#define BIT_MASK_SYS_SWR12_COMP_C2 0x3 +#define BIT_SYS_SWR12_COMP_C2(x) (((x) & BIT_MASK_SYS_SWR12_COMP_C2) << BIT_SHIFT_SYS_SWR12_COMP_C2) + + +#define BIT_SHIFT_SYS_SWR12_COMP_C1 22 +#define BIT_MASK_SYS_SWR12_COMP_C1 0x3 +#define BIT_SYS_SWR12_COMP_C1(x) (((x) & BIT_MASK_SYS_SWR12_COMP_C1) << BIT_SHIFT_SYS_SWR12_COMP_C1) + +#define BIT_SYS_SWR12_COMP_TYPE_L BIT(21) +#define BIT_SYS_SWR12_FPWM_MD BIT(20) + +#define BIT_SHIFT_SYS_SPSLDO_VOL 17 +#define BIT_MASK_SYS_SPSLDO_VOL 0x7 +#define BIT_SYS_SPSLDO_VOL(x) (((x) & BIT_MASK_SYS_SPSLDO_VOL) << BIT_SHIFT_SYS_SPSLDO_VOL) + + +#define BIT_SHIFT_SYS_SWR12_IN 14 +#define BIT_MASK_SYS_SWR12_IN 0x7 +#define BIT_SYS_SWR12_IN(x) (((x) & BIT_MASK_SYS_SWR12_IN) << BIT_SHIFT_SYS_SWR12_IN) + + +#define BIT_SHIFT_SYS_SWR12_STD 12 +#define BIT_MASK_SYS_SWR12_STD 0x3 +#define BIT_SYS_SWR12_STD(x) (((x) & BIT_MASK_SYS_SWR12_STD) << BIT_SHIFT_SYS_SWR12_STD) + + +#define BIT_SHIFT_SYS_SWR12_VOL 8 +#define BIT_MASK_SYS_SWR12_VOL 0xf +#define BIT_SYS_SWR12_VOL(x) (((x) & BIT_MASK_SYS_SWR12_VOL) << BIT_SHIFT_SYS_SWR12_VOL) + + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID +#define BIT_SYS_SWR_EN BIT(1) +#define BIT_SYS_SWR_LDO_EN BIT(0) + +//2 REG_SYS_SWR_CTRL1 +#define BIT_SYS_SW12_PFM_SEL BIT(25) +#define BIT_SYS_SW12_AUTO_ZCD_L BIT(24) +#define BIT_SYS_SW12_AUTO_MODE BIT(23) +#define BIT_SYS_SW12_LDOF_L BIT(22) +#define BIT_SYS_SW12_OCPS_L BIT(21) + +#define BIT_SHIFT_SYS_SW12_TBOX 17 +#define BIT_MASK_SYS_SW12_TBOX 0x3 +#define BIT_SYS_SW12_TBOX(x) (((x) & BIT_MASK_SYS_SW12_TBOX) << BIT_SHIFT_SYS_SW12_TBOX) + + +#define BIT_SHIFT_SYS_SW12_NONOVRLAP_DLY 15 +#define BIT_MASK_SYS_SW12_NONOVRLAP_DLY 0x3 +#define BIT_SYS_SW12_NONOVRLAP_DLY(x) (((x) & BIT_MASK_SYS_SW12_NONOVRLAP_DLY) << BIT_SHIFT_SYS_SW12_NONOVRLAP_DLY) + +#define BIT_SYS_SW12_CLAMP_DUTY BIT(14) +#define BIT_SYS_SWR12_BYPASS_SSR BIT(13) +#define BIT_SYS_SWR12_ZCDOUT_EN BIT(12) +#define BIT_SYS_SWR12_POW_ZCD BIT(11) +#define BIT_SYS_SW12_AREN BIT(10) + +#define BIT_SHIFT_SYS_SWR12_OCP_CUR 7 +#define BIT_MASK_SYS_SWR12_OCP_CUR 0x7 +#define BIT_SYS_SWR12_OCP_CUR(x) (((x) & BIT_MASK_SYS_SWR12_OCP_CUR) << BIT_SHIFT_SYS_SWR12_OCP_CUR) + +#define BIT_SYS_SWR12_OCP_EN BIT(6) + +#define BIT_SHIFT_SYS_SWR12_SAWTOOTH_CF_L 4 +#define BIT_MASK_SYS_SWR12_SAWTOOTH_CF_L 0x3 +#define BIT_SYS_SWR12_SAWTOOTH_CF_L(x) (((x) & BIT_MASK_SYS_SWR12_SAWTOOTH_CF_L) << BIT_SHIFT_SYS_SWR12_SAWTOOTH_CF_L) + + +#define BIT_SHIFT_SYS_SWR12_SAWTOOTH_CFC_L 2 +#define BIT_MASK_SYS_SWR12_SAWTOOTH_CFC_L 0x3 +#define BIT_SYS_SWR12_SAWTOOTH_CFC_L(x) (((x) & BIT_MASK_SYS_SWR12_SAWTOOTH_CFC_L) << BIT_SHIFT_SYS_SWR12_SAWTOOTH_CFC_L) + + +#define BIT_SHIFT_SYS_SWR12_COMP_R3 0 +#define BIT_MASK_SYS_SWR12_COMP_R3 0x3 +#define BIT_SYS_SWR12_COMP_R3(x) (((x) & BIT_MASK_SYS_SWR12_COMP_R3) << BIT_SHIFT_SYS_SWR12_COMP_R3) + + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_SYS_XTAL_CTRL0 +#define BIT_SYS_XTAL_XQSEL BIT(31) +#define BIT_SYS_XTAL_XQSEL_RF BIT(30) + +#define BIT_SHIFT_SYS_XTAL_SC_XO 24 +#define BIT_MASK_SYS_XTAL_SC_XO 0x3f +#define BIT_SYS_XTAL_SC_XO(x) (((x) & BIT_MASK_SYS_XTAL_SC_XO) << BIT_SHIFT_SYS_XTAL_SC_XO) + + +#define BIT_SHIFT_SYS_XTAL_SC_XI 18 +#define BIT_MASK_SYS_XTAL_SC_XI 0x3f +#define BIT_SYS_XTAL_SC_XI(x) (((x) & BIT_MASK_SYS_XTAL_SC_XI) << BIT_SHIFT_SYS_XTAL_SC_XI) + + +#define BIT_SHIFT_SYS_XTAL_GMN 13 +#define BIT_MASK_SYS_XTAL_GMN 0x1f +#define BIT_SYS_XTAL_GMN(x) (((x) & BIT_MASK_SYS_XTAL_GMN) << BIT_SHIFT_SYS_XTAL_GMN) + + +#define BIT_SHIFT_SYS_XTAL_GMP 8 +#define BIT_MASK_SYS_XTAL_GMP 0x1f +#define BIT_SYS_XTAL_GMP(x) (((x) & BIT_MASK_SYS_XTAL_GMP) << BIT_SHIFT_SYS_XTAL_GMP) + +#define BIT_SYS_XTAL_EN BIT(1) +#define BIT_SYS_XTAL_BGMB_EN BIT(0) + +//2 REG_SYS_XTAL_CTRL1 + +#define BIT_SHIFT_SYS_XTAL_COUNTER_MUX 25 +#define BIT_MASK_SYS_XTAL_COUNTER_MUX 0x3 +#define BIT_SYS_XTAL_COUNTER_MUX(x) (((x) & BIT_MASK_SYS_XTAL_COUNTER_MUX) << BIT_SHIFT_SYS_XTAL_COUNTER_MUX) + +#define BIT_SYS_XTAL_DELAY_SYSPLL BIT(24) +#define BIT_SYS_XTAL_DELAY_USB BIT(23) +#define BIT_SYS_XTAL_DELAY_WLAFE BIT(22) +#define BIT_SYS_XTAL_AGPIO_SEL BIT(21) + +#define BIT_SHIFT_SYS_XTAL_DRV_AGPIO 19 +#define BIT_MASK_SYS_XTAL_DRV_AGPIO 0x3 +#define BIT_SYS_XTAL_DRV_AGPIO(x) (((x) & BIT_MASK_SYS_XTAL_DRV_AGPIO) << BIT_SHIFT_SYS_XTAL_DRV_AGPIO) + + +#define BIT_SHIFT_SYS_XTAL_AGPIO 16 +#define BIT_MASK_SYS_XTAL_AGPIO 0x7 +#define BIT_SYS_XTAL_AGPIO(x) (((x) & BIT_MASK_SYS_XTAL_AGPIO) << BIT_SHIFT_SYS_XTAL_AGPIO) + + +#define BIT_SHIFT_SYS_XTAL_DRV_SYSPLL 14 +#define BIT_MASK_SYS_XTAL_DRV_SYSPLL 0x3 +#define BIT_SYS_XTAL_DRV_SYSPLL(x) (((x) & BIT_MASK_SYS_XTAL_DRV_SYSPLL) << BIT_SHIFT_SYS_XTAL_DRV_SYSPLL) + +#define BIT_SYS_XTAL_GATE_SYSPLL BIT(13) + +#define BIT_SHIFT_SYS_XTAL_DRV_USB 11 +#define BIT_MASK_SYS_XTAL_DRV_USB 0x3 +#define BIT_SYS_XTAL_DRV_USB(x) (((x) & BIT_MASK_SYS_XTAL_DRV_USB) << BIT_SHIFT_SYS_XTAL_DRV_USB) + +#define BIT_SYS_XTAL_GATE_USB BIT(10) + +#define BIT_SHIFT_SYS_XTAL_DRV_WLAFE 8 +#define BIT_MASK_SYS_XTAL_DRV_WLAFE 0x3 +#define BIT_SYS_XTAL_DRV_WLAFE(x) (((x) & BIT_MASK_SYS_XTAL_DRV_WLAFE) << BIT_SHIFT_SYS_XTAL_DRV_WLAFE) + +#define BIT_SYS_XTAL_GATE_WLAFE BIT(7) + +#define BIT_SHIFT_SYS_XTAL_DRV_RF2 5 +#define BIT_MASK_SYS_XTAL_DRV_RF2 0x3 +#define BIT_SYS_XTAL_DRV_RF2(x) (((x) & BIT_MASK_SYS_XTAL_DRV_RF2) << BIT_SHIFT_SYS_XTAL_DRV_RF2) + +#define BIT_SYS_XTAL_GATE_RF2 BIT(4) + +#define BIT_SHIFT_SYS_XTAL_DRV_RF1 3 +#define BIT_MASK_SYS_XTAL_DRV_RF1 0x3 +#define BIT_SYS_XTAL_DRV_RF1(x) (((x) & BIT_MASK_SYS_XTAL_DRV_RF1) << BIT_SHIFT_SYS_XTAL_DRV_RF1) + +#define BIT_SYS_XTAL_GATE_RF1 BIT(1) + +#define BIT_SHIFT_SYS_XTAL_LDO 0 +#define BIT_MASK_SYS_XTAL_LDO 0x3 +#define BIT_SYS_XTAL_LDO(x) (((x) & BIT_MASK_SYS_XTAL_LDO) << BIT_SHIFT_SYS_XTAL_LDO) + + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_SYS_SYSPLL_CTRL0 + +#define BIT_SHIFT_SYS_SYSPLL_LPF_R3 29 +#define BIT_MASK_SYS_SYSPLL_LPF_R3 0x7 +#define BIT_SYS_SYSPLL_LPF_R3(x) (((x) & BIT_MASK_SYS_SYSPLL_LPF_R3) << BIT_SHIFT_SYS_SYSPLL_LPF_R3) + + +#define BIT_SHIFT_SYS_SYSPLL_LPF_CS 27 +#define BIT_MASK_SYS_SYSPLL_LPF_CS 0x3 +#define BIT_SYS_SYSPLL_LPF_CS(x) (((x) & BIT_MASK_SYS_SYSPLL_LPF_CS) << BIT_SHIFT_SYS_SYSPLL_LPF_CS) + + +#define BIT_SHIFT_SYS_SYSPLL_LPF_CP 25 +#define BIT_MASK_SYS_SYSPLL_LPF_CP 0x3 +#define BIT_SYS_SYSPLL_LPF_CP(x) (((x) & BIT_MASK_SYS_SYSPLL_LPF_CP) << BIT_SHIFT_SYS_SYSPLL_LPF_CP) + + +#define BIT_SHIFT_SYS_SYSPLL_LPF_C3 23 +#define BIT_MASK_SYS_SYSPLL_LPF_C3 0x3 +#define BIT_SYS_SYSPLL_LPF_C3(x) (((x) & BIT_MASK_SYS_SYSPLL_LPF_C3) << BIT_SHIFT_SYS_SYSPLL_LPF_C3) + +#define BIT_SYS_SYSPLL_WDOG_ENB BIT(22) +#define BIT_SYS_SYSPLL_CKTST_EN BIT(21) + +#define BIT_SHIFT_SYS_SYSPLL_MONCK_SEL 18 +#define BIT_MASK_SYS_SYSPLL_MONCK_SEL 0x7 +#define BIT_SYS_SYSPLL_MONCK_SEL(x) (((x) & BIT_MASK_SYS_SYSPLL_MONCK_SEL) << BIT_SHIFT_SYS_SYSPLL_MONCK_SEL) + + +#define BIT_SHIFT_SYS_SYSPLL_CP_IOFFSET 13 +#define BIT_MASK_SYS_SYSPLL_CP_IOFFSET 0x1f +#define BIT_SYS_SYSPLL_CP_IOFFSET(x) (((x) & BIT_MASK_SYS_SYSPLL_CP_IOFFSET) << BIT_SHIFT_SYS_SYSPLL_CP_IOFFSET) + +#define BIT_SYS_SYSPLL_CP_IDOUBLE BIT(12) + +#define BIT_SHIFT_SYS_SYSPLL_CP_BIAS 9 +#define BIT_MASK_SYS_SYSPLL_CP_BIAS 0x7 +#define BIT_SYS_SYSPLL_CP_BIAS(x) (((x) & BIT_MASK_SYS_SYSPLL_CP_BIAS) << BIT_SHIFT_SYS_SYSPLL_CP_BIAS) + +#define BIT_SYS_SYSPLL_FREF_EDGE BIT(8) +#define BIT_SYS_SYSPLL_EN BIT(1) +#define BIT_SYS_SYSPLL_LVPC_EN BIT(0) + +//2 REG_SYS_SYSPLL_CTRL1 +#define BIT_SYS_SYSPLL_CK500K_SEL BIT(15) +#define BIT_SYS_SYSPLL_CK200M_EN BIT(14) +#define BIT_SYS_SYSPLL_CKSDR_EN BIT(13) + +#define BIT_SHIFT_SYS_SYSPLL_CKSDR_DIV 11 +#define BIT_MASK_SYS_SYSPLL_CKSDR_DIV 0x3 +#define BIT_SYS_SYSPLL_CKSDR_DIV(x) (((x) & BIT_MASK_SYS_SYSPLL_CKSDR_DIV) << BIT_SHIFT_SYS_SYSPLL_CKSDR_DIV) + +#define BIT_SYS_SYSPLL_CK24P576_EN BIT(9) +#define BIT_SYS_SYSPLL_CK22P5792_EN BIT(8) +#define BIT_SYS_SYSPLL_CK_PS_EN BIT(6) + +#define BIT_SHIFT_SYS_SYSPLL_CK_PS_SEL 3 +#define BIT_MASK_SYS_SYSPLL_CK_PS_SEL 0x7 +#define BIT_SYS_SYSPLL_CK_PS_SEL(x) (((x) & BIT_MASK_SYS_SYSPLL_CK_PS_SEL) << BIT_SHIFT_SYS_SYSPLL_CK_PS_SEL) + + +#define BIT_SHIFT_SYS_SYSPLL_LPF_RS 0 +#define BIT_MASK_SYS_SYSPLL_LPF_RS 0x7 +#define BIT_SYS_SYSPLL_LPF_RS(x) (((x) & BIT_MASK_SYS_SYSPLL_LPF_RS) << BIT_SHIFT_SYS_SYSPLL_LPF_RS) + + +//2 REG_SYS_SYSPLL_CTRL2 + +#define BIT_SHIFT_XTAL_DRV_RF_LATCH 0 +#define BIT_MASK_XTAL_DRV_RF_LATCH 0xffffffffL +#define BIT_XTAL_DRV_RF_LATCH(x) (((x) & BIT_MASK_XTAL_DRV_RF_LATCH) << BIT_SHIFT_XTAL_DRV_RF_LATCH) + + +//2 REG_RSVD + +//2 REG_RSVD + +#define BIT_SHIFT_PESOC_CPU_OCP_CK_SEL 0 +#define BIT_MASK_PESOC_CPU_OCP_CK_SEL 0x7 +#define BIT_PESOC_CPU_OCP_CK_SEL(x) (((x) & BIT_MASK_PESOC_CPU_OCP_CK_SEL) << BIT_SHIFT_PESOC_CPU_OCP_CK_SEL) + + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_ + +//2 REG_SYS_ANA_TIM_CTRL + +#define BIT_SHIFT_SYS_ANACK_TU_TIME 16 +#define BIT_MASK_SYS_ANACK_TU_TIME 0x3f +#define BIT_SYS_ANACK_TU_TIME(x) (((x) & BIT_MASK_SYS_ANACK_TU_TIME) << BIT_SHIFT_SYS_ANACK_TU_TIME) + +#define BIT_SYS_DSBYCNT_EN BIT(15) + +#define BIT_SHIFT_SYS_DSTDY_TIM_SCAL 8 +#define BIT_MASK_SYS_DSTDY_TIM_SCAL 0xf +#define BIT_SYS_DSTDY_TIM_SCAL(x) (((x) & BIT_MASK_SYS_DSTDY_TIM_SCAL) << BIT_SHIFT_SYS_DSTDY_TIM_SCAL) + + +#define BIT_SHIFT_SYS_DSTBY_TIM_PERIOD 0 +#define BIT_MASK_SYS_DSTBY_TIM_PERIOD 0xff +#define BIT_SYS_DSTBY_TIM_PERIOD(x) (((x) & BIT_MASK_SYS_DSTBY_TIM_PERIOD) << BIT_SHIFT_SYS_DSTBY_TIM_PERIOD) + + +//2 REG_SYS_DSLP_TIM_CTRL + +#define BIT_SHIFT_SYS_REGU_ASIF_EN 24 +#define BIT_MASK_SYS_REGU_ASIF_EN 0xff +#define BIT_SYS_REGU_ASIF_EN(x) (((x) & BIT_MASK_SYS_REGU_ASIF_EN) << BIT_SHIFT_SYS_REGU_ASIF_EN) + + +#define BIT_SHIFT_SYS_REGU_ASIF_THP_DA 20 +#define BIT_MASK_SYS_REGU_ASIF_THP_DA 0x3 +#define BIT_SYS_REGU_ASIF_THP_DA(x) (((x) & BIT_MASK_SYS_REGU_ASIF_THP_DA) << BIT_SHIFT_SYS_REGU_ASIF_THP_DA) + + +#define BIT_SHIFT_SYS_REGU_ASIF_TPD_CK 18 +#define BIT_MASK_SYS_REGU_ASIF_TPD_CK 0x3 +#define BIT_SYS_REGU_ASIF_TPD_CK(x) (((x) & BIT_MASK_SYS_REGU_ASIF_TPD_CK) << BIT_SHIFT_SYS_REGU_ASIF_TPD_CK) + + +#define BIT_SHIFT_SYS_REGU_ASIF_TSP_DA 16 +#define BIT_MASK_SYS_REGU_ASIF_TSP_DA 0x3 +#define BIT_SYS_REGU_ASIF_TSP_DA(x) (((x) & BIT_MASK_SYS_REGU_ASIF_TSP_DA) << BIT_SHIFT_SYS_REGU_ASIF_TSP_DA) + +#define BIT_SYS_REGU_ASIF_POLL BIT(15) +#define BIT_SYS_REGU_ASIF_MODE BIT(14) +#define BIT_SYS_REGU_ASIF_WE BIT(12) + +#define BIT_SHIFT_SYS_REGU_ASIF_AD 8 +#define BIT_MASK_SYS_REGU_ASIF_AD 0xf +#define BIT_SYS_REGU_ASIF_AD(x) (((x) & BIT_MASK_SYS_REGU_ASIF_AD) << BIT_SHIFT_SYS_REGU_ASIF_AD) + + +#define BIT_SHIFT_SYS_REGU_ASIF_WD 0 +#define BIT_MASK_SYS_REGU_ASIF_WD 0xff +#define BIT_SYS_REGU_ASIF_WD(x) (((x) & BIT_MASK_SYS_REGU_ASIF_WD) << BIT_SHIFT_SYS_REGU_ASIF_WD) + + +//2 REG_SYS_DSLP_TIM_CAL_CTRL +#define BIT_SYS_DSLP_TIM_EN BIT(24) + +#define BIT_SHIFT_SYS_DSLP_TIM_PERIOD 0 +#define BIT_MASK_SYS_DSLP_TIM_PERIOD 0x7fffff +#define BIT_SYS_DSLP_TIM_PERIOD(x) (((x) & BIT_MASK_SYS_DSLP_TIM_PERIOD) << BIT_SHIFT_SYS_DSLP_TIM_PERIOD) + + +//2 REG_RSVD + +//2 REG_SYS_DEBUG_CTRL +#define BIT_SYS_DBG_PIN_EN BIT(0) + +//2 REG_SYS_PINMUX_CTRL +#define BIT_EEPROM_PIN_EN BIT(4) +#define BIT_SIC_PIN_EN BIT(0) + +//2 REG_SYS_GPIO_DSTBY_WAKE_CTRL0 +#define BIT_SYS_GPIOE3_WEVENT_STS BIT(27) +#define BIT_SYS_GPIOD5_WEVENT_STS BIT(26) +#define BIT_SYS_GPIOC7_WEVENT_STS BIT(25) +#define BIT_SYS_GPIOA5_WEVENT_STS BIT(24) +#define BIT_SYS_GPIO_GPE3_PULL_CTRL_EN BIT(19) +#define BIT_SYS_GPIO_GPD5_PULL_CTRL_EN BIT(18) +#define BIT_SYS_GPIO_GPC7_PULL_CTRL_EN BIT(17) +#define BIT_SYS_GPIO_GPA5_PULL_CTRL_EN BIT(16) +#define BIT_SYS_GPIOE3_WINT_MODE BIT(11) +#define BIT_SYS_GPIOD5_WINT_MODE BIT(10) +#define BIT_SYS_GPIOC7_WINT_MODE BIT(9) +#define BIT_SYS_GPIOA5_WINT_MODE BIT(8) +#define BIT_SYS_GPIOE3_PIN_EN BIT(3) +#define BIT_SYS_GPIOD5_PIN_EN BIT(2) +#define BIT_SYS_GPIOC7_PIN_EN BIT(1) +#define BIT_SYS_GPIOA5_PIN_EN BIT(0) + +//2 REG_SYS_GPIO_DSTBY_WAKE_CTRL1 +#define BIT_SYS_GPIOE3_SHTDN_N BIT(19) +#define BIT_SYS_GPIOD5_SHTDN_N BIT(18) +#define BIT_SYS_GPIOC7_SHTDN_N BIT(17) +#define BIT_SYS_GPIOA5_SHTDN_N BIT(16) + +#define BIT_SHIFT_SYS_WINT_DEBOUNCE_TIM_SCAL 8 +#define BIT_MASK_SYS_WINT_DEBOUNCE_TIM_SCAL 0x3 +#define BIT_SYS_WINT_DEBOUNCE_TIM_SCAL(x) (((x) & BIT_MASK_SYS_WINT_DEBOUNCE_TIM_SCAL) << BIT_SHIFT_SYS_WINT_DEBOUNCE_TIM_SCAL) + +#define BIT_SYS_GPIOE3_WINT_DEBOUNCE_EN BIT(3) +#define BIT_SYS_GPIOD5_WINT_DEBOUNCE_EN BIT(2) +#define BIT_SYS_GPIOC7_WINT_DEBOUNCE_EN BIT(1) +#define BIT_SYS_GPIOA5_WINT_DEBOUNCE_EN BIT(0) + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SYS_DEBUG_REG + +#define BIT_SHIFT_SYS_DBG_VALUE 0 +#define BIT_MASK_SYS_DBG_VALUE 0xffffffffL +#define BIT_SYS_DBG_VALUE(x) (((x) & BIT_MASK_SYS_DBG_VALUE) << BIT_SHIFT_SYS_DBG_VALUE) + + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_SYS_EEPROM_CTRL0 + +#define BIT_SHIFT_EFUSE_UNLOCK 24 +#define BIT_MASK_EFUSE_UNLOCK 0xff +#define BIT_EFUSE_UNLOCK(x) (((x) & BIT_MASK_EFUSE_UNLOCK) << BIT_SHIFT_EFUSE_UNLOCK) + + +//2 REG_NOT_VALID +#define BIT_SYS_EFUSE_LDALL BIT(16) + +#define BIT_SHIFT_SYS_EEPROM_VPDIDX 8 +#define BIT_MASK_SYS_EEPROM_VPDIDX 0xff +#define BIT_SYS_EEPROM_VPDIDX(x) (((x) & BIT_MASK_SYS_EEPROM_VPDIDX) << BIT_SHIFT_SYS_EEPROM_VPDIDX) + + +#define BIT_SHIFT_SYS_EEPROM_MD 6 +#define BIT_MASK_SYS_EEPROM_MD 0x3 +#define BIT_SYS_EEPROM_MD(x) (((x) & BIT_MASK_SYS_EEPROM_MD) << BIT_SHIFT_SYS_EEPROM_MD) + +#define BIT_SYS_AUTOLOAD_SUS BIT(5) +#define BIT_SYS_EEPROM_SEL BIT(4) +#define BIT_SYS_EEPROM_EECS BIT(3) +#define BIT_SYS_EEPROM_EESK BIT(2) +#define BIT_SYS_EEPROM_EEDI BIT(1) +#define BIT_SYS_EEPROM_EEDO BIT(0) + +//2 REG_SYS_EEPROM_CTRL1 + +#define BIT_SHIFT_SYS_EEPROM_VPD 0 +#define BIT_MASK_SYS_EEPROM_VPD 0xffffffffL +#define BIT_SYS_EEPROM_VPD(x) (((x) & BIT_MASK_SYS_EEPROM_VPD) << BIT_SHIFT_SYS_EEPROM_VPD) + + +//2 REG_SYS_EFUSE_CTRL +#define BIT_SYS_EF_RWFLAG BIT(31) + +#define BIT_SHIFT_SYS_EF_PGPD 28 +#define BIT_MASK_SYS_EF_PGPD 0x7 +#define BIT_SYS_EF_PGPD(x) (((x) & BIT_MASK_SYS_EF_PGPD) << BIT_SHIFT_SYS_EF_PGPD) + + +#define BIT_SHIFT_SYS_EF_RDT 24 +#define BIT_MASK_SYS_EF_RDT 0xf +#define BIT_SYS_EF_RDT(x) (((x) & BIT_MASK_SYS_EF_RDT) << BIT_SHIFT_SYS_EF_RDT) + + +#define BIT_SHIFT_SYS_EF_PGTS 20 +#define BIT_MASK_SYS_EF_PGTS 0xf +#define BIT_SYS_EF_PGTS(x) (((x) & BIT_MASK_SYS_EF_PGTS) << BIT_SHIFT_SYS_EF_PGTS) + +#define BIT_SYS_EF_PDWN BIT(19) +#define BIT_SYS_EF_ALDEN BIT(18) + +#define BIT_SHIFT_SYS_EF_ADDR 8 +#define BIT_MASK_SYS_EF_ADDR 0x3ff +#define BIT_SYS_EF_ADDR(x) (((x) & BIT_MASK_SYS_EF_ADDR) << BIT_SHIFT_SYS_EF_ADDR) + + +#define BIT_SHIFT_SYS_EF_DATA 0 +#define BIT_MASK_SYS_EF_DATA 0xff +#define BIT_SYS_EF_DATA(x) (((x) & BIT_MASK_SYS_EF_DATA) << BIT_SHIFT_SYS_EF_DATA) + + +//2 REG_SYS_EFUSE_TEST +#define BIT_SYS_EF_CRES_SEL BIT(26) + +#define BIT_SHIFT_SYS_EF_SCAN_START 16 +#define BIT_MASK_SYS_EF_SCAN_START 0x1ff +#define BIT_SYS_EF_SCAN_START(x) (((x) & BIT_MASK_SYS_EF_SCAN_START) << BIT_SHIFT_SYS_EF_SCAN_START) + + +#define BIT_SHIFT_SYS_EF_SCAN_END 12 +#define BIT_MASK_SYS_EF_SCAN_END 0xf +#define BIT_SYS_EF_SCAN_END(x) (((x) & BIT_MASK_SYS_EF_SCAN_END) << BIT_SHIFT_SYS_EF_SCAN_END) + +#define BIT_SYS_EF_FORCE_PGMEN BIT(11) + +#define BIT_SHIFT_SYS_EF_CELL_SEL 8 +#define BIT_MASK_SYS_EF_CELL_SEL 0x3 +#define BIT_SYS_EF_CELL_SEL(x) (((x) & BIT_MASK_SYS_EF_CELL_SEL) << BIT_SHIFT_SYS_EF_CELL_SEL) + +#define BIT_SYS_EF_TRPT BIT(7) + +#define BIT_SHIFT_SYS_EF_SCAN_TTHD 0 +#define BIT_MASK_SYS_EF_SCAN_TTHD 0x7f +#define BIT_SYS_EF_SCAN_TTHD(x) (((x) & BIT_MASK_SYS_EF_SCAN_TTHD) << BIT_SHIFT_SYS_EF_SCAN_TTHD) + + +//2 REG_SYS_DSTBY_INFO0 + +//2 REG_NOT_VALID + +//2 REG_SYS_DSTBY_INFO1 + +//2 REG_SYS_DSTBY_INFO2 + +//2 REG_NOT_VALID + +//2 REG_SYS_DSTBY_INFO3 + +//2 REG_SYS_SLP_WAKE_EVENT_MSK0 +#define BIT_SYSON_WEVT_GPIO_DSTBY_MSK BIT(29) +#define BIT_SYSON_WEVT_A33_MSK BIT(28) +#define BIT_SYSON_WEVT_ADC_MSK BIT(26) +#define BIT_SYSON_WEVT_I2C_MSK BIT(24) +#define BIT_SYSON_WEVT_SPI_MSK BIT(22) +#define BIT_SYSON_WEVT_UART_MSK BIT(20) +#define BIT_SYSON_WEVT_USB_MSK BIT(16) +#define BIT_SYSON_WEVT_SDIO_MSK BIT(14) +#define BIT_SYSON_WEVT_NFC_MSK BIT(9) +#define BIT_SYSON_WEVT_WLAN_MSK BIT(8) +#define BIT_SYSON_WEVT_GPIO_MSK BIT(4) +#define BIT_SYSON_WEVT_CHIP_EN_MSK BIT(3) +#define BIT_SYSON_WEVT_OVER_CURRENT_MSK BIT(2) +#define BIT_SYSON_WEVT_GTIM_MSK BIT(1) +#define BIT_SYSON_WEVT_SYSTIM_MSK BIT(0) + +//2 REG_SYS_SLP_WAKE_EVENT_MSK1 + +//2 REG_SYS_SLP_WAKE_EVENT_STATUS0 +#define BIT_SYSON_WEVT_GPIO_DSTBY_STS BIT(29) +#define BIT_SYSON_WEVT_A33_STS BIT(28) +#define BIT_SYSON_WEVT_ADC_STS BIT(26) +#define BIT_SYSON_WEVT_I2C_STS BIT(24) +#define BIT_SYSON_WEVT_SPI_STS BIT(22) +#define BIT_SYSON_WEVT_UART_STS BIT(20) +#define BIT_SYSON_WEVT_USB_STS BIT(16) +#define BIT_SYSON_WEVT_SDIO_STS BIT(14) +#define BIT_SYSON_WEVT_NFC_STS BIT(9) +#define BIT_SYSON_WEVT_WLAN_STS BIT(8) +#define BIT_SYSON_WEVT_GPIO_STS BIT(4) +#define BIT_SYSON_WEVT_CHIP_EN_STS BIT(3) +#define BIT_SYSON_WEVT_OVER_CURRENT_STS BIT(2) +#define BIT_SYSON_WEVT_GTIM_STS BIT(1) +#define BIT_SYSON_WEVT_SYSTIM_STS BIT(0) + +//2 REG_SYS_SLP_WAKE_EVENT_STATUS1 + +//2 REG_SYS_SNF_WAKE_EVENT_MSK0 + +#define BIT_SHIFT_SYS_WKPERI_IMR0 1 +#define BIT_MASK_SYS_WKPERI_IMR0 0x7fffffffL +#define BIT_SYS_WKPERI_IMR0(x) (((x) & BIT_MASK_SYS_WKPERI_IMR0) << BIT_SHIFT_SYS_WKPERI_IMR0) + +#define BIT_SYSON_SNFEVT_ADC_MSK BIT(0) + +//2 REG_SYS_SNF_WAKE_EVENT_STATUS + +#define BIT_SHIFT_SYS_WKPERI_ISR0 1 +#define BIT_MASK_SYS_WKPERI_ISR0 0x7fffffffL +#define BIT_SYS_WKPERI_ISR0(x) (((x) & BIT_MASK_SYS_WKPERI_ISR0) << BIT_SHIFT_SYS_WKPERI_ISR0) + +#define BIT_SYSON_SNFEVT_ADC_STS BIT(0) + +//2 REG_SYS_PWRMGT_CTRL +#define BIT_SYSON_REGU_DSLP BIT(7) + +//2 REG_NOT_VALID +#define BIT_SYSON_PM_CMD_SLP BIT(2) +#define BIT_SYSON_PM_CMD_DSTBY BIT(1) +#define BIT_SYSON_PM_CMD_DSLP BIT(0) + +//2 REG_RSVD + +//2 REG_SYS_PWRMGT_OPTION +#define BIT_SYSON_PMOPT_NORM_SYSCLK_SEL BIT(30) +#define BIT_SYSON_PMOPT_NORM_SYSPLL_EN BIT(29) +#define BIT_SYSON_PMOPT_NORM_XTAL_EN BIT(28) +#define BIT_SYSON_PMOPT_NORM_EN_SOC BIT(27) +#define BIT_SYSON_PMOPT_NORM_EN_PWM BIT(26) +#define BIT_SYSON_PMOPT_NORM_EN_SWR BIT(25) +#define BIT_SYSON_PMOPT_NORM_LPLDO_SEL BIT(24) +#define BIT_SYSON_PMOPT_SNZ_SYSCLK_SEL BIT(22) +#define BIT_SYSON_PMOPT_SNZ_SYSPLL_EN BIT(21) +#define BIT_SYSON_PMOPT_SNZ_XTAL_EN BIT(20) +#define BIT_SYSON_PMOPT_SNZ_EN_SOC BIT(19) +#define BIT_SYSON_PMOPT_SNZ_EN_PWM BIT(18) +#define BIT_SYSON_PMOPT_SNZ_EN_SWR BIT(17) +#define BIT_SYSON_PMOPT_SNZ_LPLDO_SEL BIT(16) +#define BIT_SYSON_PMOPT_SLP_SYSCLK_SEL BIT(14) +#define BIT_SYSON_PMOPT_SLP_SYSPLL_EN BIT(13) +#define BIT_SYSON_PMOPT_SLP_XTAL_EN BIT(12) +#define BIT_SYSON_PMOPT_SLP_EN_SOC BIT(11) +#define BIT_SYSON_PMOPT_SLP_EN_PWM BIT(10) +#define BIT_SYSON_PMOPT_SLP_EN_SWR BIT(9) +#define BIT_SYSON_PMOPT_SLP_LPLDO_SEL BIT(8) +#define BIT_SYSON_PMOPT_DSTBY_SYSCLK_SEL BIT(6) +#define BIT_SYSON_PMOPT_DSTBY_SYSPLL_EN BIT(5) +#define BIT_SYSON_PMOPT_DSTBY_XTAL_EN BIT(4) +#define BIT_SYSON_PMOPT_DSTBY_EN_SOC BIT(3) +#define BIT_SYSON_PMOPT_DSTBY_EN_PWM BIT(2) +#define BIT_SYSON_PMOPT_DSTBY_EN_SWR BIT(1) +#define BIT_SYSON_PMOPT_DSTBY_LPLDO_SEL BIT(0) + +//2 REG_SYS_PWRMGT_OPTION_EXT +#define BIT_SYSON_PMOPT_SLP_ANACK_SEL BIT(2) +#define BIT_SYSON_PMOPT_SLP_ANACK_EN BIT(1) +#define BIT_SYSON_PMOPT_SLP_SWR_ADJ BIT(0) + +//2 REG_SYS_DSLP_WEVENT +#define BIT_SYSON_DSLP_GPIO BIT(2) +#define BIT_SYSON_DSLP_NFC BIT(1) +#define BIT_SYSON_DSLP_WTIMER33 BIT(0) + +//2 REG_SYS_PERI_MONITOR +#define BIT_SYSON_ISO33_NFC BIT(0) + +//2 REG_SYS_SYSTEM_CFG0 +#define BIT_SYSCFG_BD_PKG_SEL BIT(31) + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +#define BIT_SHIFT_VENDOR_ID 8 +#define BIT_MASK_VENDOR_ID 0xf +#define BIT_VENDOR_ID(x) (((x) & BIT_MASK_VENDOR_ID) << BIT_SHIFT_VENDOR_ID) + + +#define BIT_SHIFT_CHIP_VER 4 +#define BIT_MASK_CHIP_VER 0xf +#define BIT_CHIP_VER(x) (((x) & BIT_MASK_CHIP_VER) << BIT_SHIFT_CHIP_VER) + + +#define BIT_SHIFT_RF_RL_ID 0 +#define BIT_MASK_RF_RL_ID 0xf +#define BIT_RF_RL_ID(x) (((x) & BIT_MASK_RF_RL_ID) << BIT_SHIFT_RF_RL_ID) + + +//2 REG_SYS_SYSTEM_CFG1 + +#define BIT_SHIFT_SYSCFG_TRP_ICFG 28 +#define BIT_MASK_SYSCFG_TRP_ICFG 0xf +#define BIT_SYSCFG_TRP_ICFG(x) (((x) & BIT_MASK_SYSCFG_TRP_ICFG) << BIT_SHIFT_SYSCFG_TRP_ICFG) + +#define BIT_SYSCFG_TRP_BOOT_SEL_ BIT(27) +#define BIT_SysCFG_TRP_SPSLDO_SEL BIT(26) +#define BIT_V15_VLD BIT(16) +#define BIT_SYS_SYSPLL_CLK_RDY BIT(9) +#define BIT_SYS_XCLK_VLD BIT(8) +#define BIT_SYSCFG_ALDN_STS BIT(0) + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + +//2 REG_RSVD + +//2 REG_NOT_VALID + +//2 REG_NOT_VALID + + +//================= Register Address Definition =====================// +#define REG_SYS_PWR_CTRL 0x0000 +#define REG_SYS_ISO_CTRL 0x0002 +#define REG_SYS_FUNC_EN 0x0008 +#define REG_SYS_CLK_CTRL0 0x0010 +#define REG_SYS_CLK_CTRL1 0x0014 +#define REG_SYS_EFUSE_SYSCFG0 0x0020 +#define REG_SYS_EFUSE_SYSCFG1 0x0024 +#define REG_SYS_EFUSE_SYSCFG2 0x0028 +#define REG_SYS_EFUSE_SYSCFG3 0x002C +#define REG_SYS_EFUSE_SYSCFG4 0x0030 +#define REG_SYS_EFUSE_SYSCFG5 0x0034 +#define REG_SYS_EFUSE_SYSCFG6 0x0038 +#define REG_SYS_EFUSE_SYSCFG7 0x003C +#define REG_SYS_REGU_CTRL0 0x0040 +#define REG_SYS_SWR_CTRL0 0x0048 +#define REG_SYS_SWR_CTRL1 0x004C +#define REG_SYS_XTAL_CTRL0 0x0060 +#define REG_SYS_XTAL_CTRL1 0x0064 +#define REG_SYS_SYSPLL_CTRL0 0x0070 +#define REG_SYS_SYSPLL_CTRL1 0x0074 +#define REG_SYS_SYSPLL_CTRL2 0x0078 +#define REG_SYS_ANA_TIM_CTRL 0x0090 +#define REG_SYS_DSLP_TIM_CTRL 0x0094 +#define REG_SYS_DSLP_TIM_CAL_CTRL 0x0098 +#define REG_SYS_DEBUG_CTRL 0x00A0 +#define REG_SYS_PINMUX_CTRL 0x00A4 +#define REG_SYS_GPIO_DSTBY_WAKE_CTRL0 0x00A8 +#define REG_SYS_GPIO_DSTBY_WAKE_CTRL1 0x00AC +#define REG_SYS_DEBUG_REG 0x00BC +#define REG_SYS_EEPROM_CTRL0 0x00E0 +#define REG_SYS_EEPROM_CTRL1 0x00E4 +#define REG_SYS_EFUSE_CTRL 0x00E8 +#define REG_SYS_EFUSE_TEST 0x00EC +#define REG_SYS_DSTBY_INFO0 0x00F0 +#define REG_SYS_DSTBY_INFO1 0x00F4 +#define REG_SYS_DSTBY_INFO2 0x00F8 +#define REG_SYS_DSTBY_INFO3 0x00FC +#define REG_SYS_SLP_WAKE_EVENT_MSK0 0x0100 +#define REG_SYS_SLP_WAKE_EVENT_MSK1 0x0104 +#define REG_SYS_SLP_WAKE_EVENT_STATUS0 0x0108 +#define REG_SYS_SLP_WAKE_EVENT_STATUS1 0x010C +#define REG_SYS_SNF_WAKE_EVENT_MSK0 0x0110 +#define REG_SYS_SNF_WAKE_EVENT_STATUS 0x0114 +#define REG_SYS_PWRMGT_CTRL 0x0118 +#define REG_SYS_PWRMGT_OPTION 0x0120 +#define REG_SYS_PWRMGT_OPTION_EXT 0x0124 +#define REG_SYS_DSLP_WEVENT 0x0130 +#define REG_SYS_PERI_MONITOR 0x0134 +#define REG_SYS_SYSTEM_CFG0 0x01F0 +#define REG_SYS_SYSTEM_CFG1 0x01F4 +#define REG_SYS_SYSTEM_CFG2 0x01F8 + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_timer.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_timer.h new file mode 100644 index 0000000..da4a540 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_timer.h @@ -0,0 +1,222 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL8195A_TIMER_H_ +#define _RTL8195A_TIMER_H_ + + +#define TIMER_TICK_US 31 + +#define TIMER_LOAD_COUNT_OFF 0x00 +#define TIMER_CURRENT_VAL_OFF 0x04 +#define TIMER_CTL_REG_OFF 0x08 +#define TIMER_EOI_OFF 0x0c +#define TIMER_INT_STATUS_OFF 0x10 +#define TIMER_INTERVAL 0x14 +#define TIMERS_INT_STATUS_OFF 0xa0 +#define TIMERS_EOI_OFF 0xa4 +#define TIMERS_RAW_INT_STATUS_OFF 0xa8 +#define TIMERS_COMP_VER_OFF 0xac + +#define MAX_TIMER_VECTOR_TABLE_NUM 6 + +#define HAL_TIMER_READ32(addr) (*((volatile u32*)(TIMER_REG_BASE + addr)))//HAL_READ32(TIMER_REG_BASE, addr) +#define HAL_TIMER_WRITE32(addr, value) ((*((volatile u32*)(TIMER_REG_BASE + addr))) = value)//HAL_WRITE32(TIMER_REG_BASE, addr, value) +#define HAL_TIMER_READ16(addr) (*((volatile u16*)(TIMER_REG_BASE + addr)))//HAL_READ16(TIMER_REG_BASE, addr) +#define HAL_TIMER_WRITE16(addr, value) ((*((volatile u16*)(TIMER_REG_BASE + addr))) = value)//HAL_WRITE16(TIMER_REG_BASE, addr, value) +#define HAL_TIMER_READ8(addr) (*((volatile u8*)(TIMER_REG_BASE + addr)))//HAL_READ8(TIMER_REG_BASE, addr) +#define HAL_TIMER_WRITE8(addr, value) ((*((volatile u8*)(TIMER_REG_BASE + addr))) = value)//HAL_WRITE8(TIMER_REG_BASE, addr, value) + +_LONG_CALL_ u32 +HalGetTimerIdRtl8195a( + IN u32 *TimerID +); + +_LONG_CALL_ BOOL +HalTimerInitRtl8195a( + IN VOID *Data +); + +_LONG_CALL_ u32 +HalTimerReadCountRtl8195a( + IN u32 TimerId +); + +_LONG_CALL_ VOID +HalTimerIrqClearRtl8195a( + IN u32 TimerId +); + +_LONG_CALL_ VOID +HalTimerDisRtl8195a( + IN u32 TimerId +); + +_LONG_CALL_ VOID +HalTimerEnRtl8195a( + IN u32 TimerId +); + +_LONG_CALL_ VOID +HalTimerDumpRegRtl8195a( + IN u32 TimerId +); + +// ROM Code patch +HAL_Status +HalTimerInitRtl8195a_Patch( + IN VOID *Data +); + +u32 +HalTimerReadCountRtl8195a_Patch( + IN u32 TimerId +); + +VOID +HalTimerReLoadRtl8195a_Patch( + IN u32 TimerId, + IN u32 LoadUs +); + +u32 +HalTimerReadCountRtl8195a_Patch( + IN u32 TimerId +); + +VOID +HalTimerIrqEnRtl8195a( + IN u32 TimerId +); + +VOID +HalTimerIrqDisRtl8195a( + IN u32 TimerId +); + +VOID +HalTimerEnRtl8195a_Patch( + IN u32 TimerId +); + +VOID +HalTimerDisRtl8195a_Patch( + IN u32 TimerId +); + +VOID +HalTimerDeInitRtl8195a_Patch( + IN VOID *Data +); + +#ifdef CONFIG_CHIP_C_CUT + +__weak _LONG_CALL_ +VOID +HalTimerIrq2To7HandleV02( + IN VOID *Data +); + +__weak _LONG_CALL_ +HAL_Status +HalTimerIrqRegisterRtl8195aV02( + IN VOID *Data +); + +__weak _LONG_CALL_ +HAL_Status +HalTimerInitRtl8195aV02( + IN VOID *Data +); + +__weak _LONG_CALL_ +u32 +HalTimerReadCountRtl8195aV02( + IN u32 TimerId +); + +__weak _LONG_CALL_ +VOID +HalTimerReLoadRtl8195aV02( + IN u32 TimerId, + IN u32 LoadUs +); + +__weak _LONG_CALL_ +HAL_Status +HalTimerIrqUnRegisterRtl8195aV02( + IN VOID *Data +); + +__weak _LONG_CALL_ +VOID +HalTimerDeInitRtl8195aV02( + IN VOID *Data +); + +#endif // end of "#ifdef CONFIG_CHIP_C_CUT" + +// HAL functions wrapper +static __inline HAL_Status +HalTimerInit( + IN VOID *Data +) +{ + return (HalTimerInitRtl8195a_Patch(Data)); +} + +static __inline VOID +HalTimerEnable( + IN u32 TimerId +) +{ + HalTimerIrqEnRtl8195a(TimerId); + HalTimerEnRtl8195a_Patch(TimerId); +} + +static __inline VOID +HalTimerDisable( + IN u32 TimerId +) +{ + HalTimerDisRtl8195a_Patch(TimerId); +} + +static __inline VOID +HalTimerReLoad( + IN u32 TimerId, + IN u32 LoadUs +) +{ + HalTimerReLoadRtl8195a_Patch(TimerId, LoadUs); +} + +#ifndef CONFIG_CHIP_C_CUT + +static __inline VOID +HalTimerDeInit( + IN VOID *Data +) +{ + HalTimerDeInitRtl8195a_Patch(Data); +} + +#else + +static __inline VOID +HalTimerDeInit( + IN VOID *Data +) +{ + HalTimerDeInitRtl8195aV02(Data); +} + +#endif // end of "#ifndef CONFIG_CHIP_C_CUT" + +#endif //_RTL8195A_TIMER_H_ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_uart.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_uart.h new file mode 100644 index 0000000..a2051b4 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_uart.h @@ -0,0 +1,403 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#ifndef _RTL8195A_UART_H_ +#define _RTL8195A_UART_H_ + +#define MAX_UART_INDEX 2 + +#define RUART_DLL_OFF 0x00 +#define RUART_DLM_OFF 0x04 //RW, DLAB = 1 +#define RUART_INTERRUPT_EN_REG_OFF 0x04 +#define RUART_IER_ERBI 0x01 //BIT0, Enable Received Data Available Interrupt (rx trigger) +#define RUART_IER_ETBEI (1<<1) //BIT1, Enable Transmitter FIFO Empty Interrupt (tx fifo empty) +#define RUART_IER_ELSI (1<<2) //BIT2, Enable Receiver Line Status Interrupt (receiver line status) +#define RUART_IER_EDSSI (1<<3) //BIT3, Enable Modem Status Interrupt (modem status transition) + +#define RUART_INT_ID_REG_OFF 0x08 //[R] +#define RUART_IIR_INT_PEND 0x01 +#define RUART_IIR_INT_ID (0x07<<1) //011(3), 010(2), 110(6), 001(1), 000(0) +#define RUART_FIFO_CTL_REG_OFF 0x08 //[W] +#define RUART_FIFO_CTL_REG_CLEAR_RXFIFO (1<<1) //BIT1, 0x02, Write 1 clear +#define RUART_FIFO_CTL_REG_CLEAR_TXFIFO (1<<2) //BIT2, 0x04, Write 1 clear +#define RUART_FIFO_CTL_REG_DMA_ENABLE 0x08 //BIT3 + +#define FIFO_CTL_DEFAULT_WITH_FIFO_DMA 0xC9 +#define FIFO_CTL_DEFAULT_WITH_FIFO 0xC1 + +#define RUART_MODEM_CTL_REG_OFF 0x10 +#define RUART_MCR_RTS BIT1 +#define RUART_MCL_AUTOFLOW_ENABLE (1<<5) //BIT5, 0x20 + +#define RUART_LINE_CTL_REG_OFF 0x0C +#define RUART_LINE_CTL_REG_DLAB_ENABLE (1<<7) //BIT7, 0x80 + +#define RUART_LINE_STATUS_REG_OFF 0x14 +#define RUART_LINE_STATUS_REG_DR 0x01 //BIT0, Data Ready indicator +#define RUART_LINE_STATUS_ERR_OVERRUN (1<<1) //BIT1, Over Run +#define RUART_LINE_STATUS_ERR_PARITY (1<<2) //BIT2, Parity error +#define RUART_LINE_STATUS_ERR_FRAMING (1<<3) //BIT3, Framing error +#define RUART_LINE_STATUS_ERR_BREAK (1<<4) //BIT4, Break interrupt error +#define RUART_LINE_STATUS_REG_THRE (1<<5) //BIT5, 0x20, Transmit Holding Register Empty Interrupt enable +#define RUART_LINE_STATUS_REG_TEMT (1<<6) //BIT6, 0x40, Transmitter Empty indicator(bit) +#define RUART_LINE_STATUS_ERR_RXFIFO (1<<7) //BIT7, RX FIFO error +#define RUART_LINE_STATUS_ERR (RUART_LINE_STATUS_ERR_OVERRUN|RUART_LINE_STATUS_ERR_PARITY| \ + RUART_LINE_STATUS_ERR_FRAMING|RUART_LINE_STATUS_ERR_BREAK| \ + RUART_LINE_STATUS_ERR_RXFIFO) //Line status error + +#define RUART_MODEM_STATUS_REG_OFF 0x18 //Modem Status Register +#define RUART_SCRATCH_PAD_REG_OFF 0x1C //Scratch Pad Register +#define RUART_SP_REG_RXBREAK_INT_STATUS (1<<7) //BIT7, 0x80, Write 1 clear +#define RUART_SP_REG_DBG_SEL 0x0F<<8 //[11:8], Debug port selection +#define RUART_SP_REG_XFACTOR_ADJ 0x7FF<<16 //[26:16] + +#define RUART_STS_REG_OFF 0x20 +#define RUART_STS_REG_RESET_RCV (1<<3) //BIT3, 0x08, Reset Uart Receiver +#define RUART_STS_REG_XFACTOR 0xF<<4 + +#define RUART_REV_BUF_REG_OFF 0x24 //Receiver Buffer Register +#define RUART_TRAN_HOLD_REG_OFF 0x24 //Transmitter Holding Register + +#define RUART_MISC_CTL_REG_OFF 0x28 +#define RUART_TXDMA_BURSTSIZE_MASK 0xF8 //7:3 +#define RUART_RXDMA_BURSTSIZE_MASK 0x1F00 //12:8 + +#define RUART_DEBUG_REG_OFF 0x3C + +// RUART_LINE_CTL_REG_OFF (0x0C) +#define BIT_SHIFT_LCR_WLS 0 // word length select: 0: 7 bits, 1: 8bits +#define BIT_MASK_LCR_WLS_8BITS 0x1 +#define BIT_LCR_WLS(x)(((x) & BIT_MASK_LCR_WLS_8BITS) << BIT_SHIFT_LCR_WLS) +#define BIT_CLR_LCR_WLS (~(BIT_MASK_LCR_WLS_8BITS << BIT_SHIFT_LCR_WLS)) + +#define BIT_SHIFT_LCR_STB 2 // Stop bit select: 0: no stop bit, 1: 1 stop bit +#define BIT_MASK_LCR_STB_EN 0x1 +#define BIT_LCR_STB_EN(x)(((x) & BIT_MASK_LCR_STB_EN) << BIT_SHIFT_LCR_STB) +#define BIT_INVC_LCR_STB_EN (~(BIT_MASK_LCR_STB_EN << BIT_SHIFT_LCR_STB)) + +#define BIT_SHIFT_LCR_PARITY_EN 3 +#define BIT_MASK_LCR_PARITY_EN 0x1 +#define BIT_LCR_PARITY_EN(x)(((x) & BIT_MASK_LCR_PARITY_EN) << BIT_SHIFT_LCR_PARITY_EN) +#define BIT_INVC_LCR_PARITY_EN (~(BIT_MASK_LCR_PARITY_EN << BIT_SHIFT_LCR_PARITY_EN)) + +#define BIT_SHIFT_LCR_PARITY_TYPE 4 +#define BIT_MASK_LCR_PARITY_TYPE 0x1 +#define BIT_LCR_PARITY_TYPE(x)(((x) & BIT_MASK_LCR_PARITY_TYPE) << BIT_SHIFT_LCR_PARITY_TYPE) +#define BIT_INVC_LCR_PARITY_TYPE (~(BIT_MASK_LCR_PARITY_TYPE << BIT_SHIFT_LCR_PARITY_TYPE)) + +#define BIT_SHIFT_LCR_STICK_PARITY_EN 5 +#define BIT_MASK_LCR_STICK_PARITY_EN 0x1 +#define BIT_LCR_STICK_PARITY_EN(x)(((x) & BIT_MASK_LCR_STICK_PARITY_EN) << BIT_SHIFT_LCR_STICK_PARITY_EN) +#define BIT_INVC_LCR_STICK_PARITY_EN (~(BIT_MASK_LCR_STICK_PARITY_EN << BIT_SHIFT_LCR_STICK_PARITY_EN)) + +#define BIT_SHIFT_LCR_BREAK_CTRL 6 +#define BIT_MASK_LCR_BREAK_CTRL 0x1 +#define BIT_UART_LCR_BREAK_CTRL ((BIT_MASK_LCR_BREAK_CTRL) << BIT_SHIFT_LCR_BREAK_CTRL) + +#define RUART_BAUD_RATE_2400 2400 +#define RUART_BAUD_RATE_4800 4800 +#define RUART_BAUD_RATE_9600 9600 +#define RUART_BAUD_RATE_19200 19200 +#define RUART_BAUD_RATE_38400 38400 +#define RUART_BAUD_RATE_57600 57600 +#define RUART_BAUD_RATE_115200 115200 +#define RUART_BAUD_RATE_921600 921600 +#define RUART_BAUD_RATE_1152000 1152000 + +#define HAL_RUART_READ32(UartIndex, addr) \ + HAL_READ32(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr) +#define HAL_RUART_WRITE32(UartIndex, addr, value) \ + HAL_WRITE32(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr, value) +#define HAL_RUART_READ16(UartIndex, addr) \ + HAL_READ16(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr) +#define HAL_RUART_WRITE16(UartIndex, addr, value) \ + HAL_WRITE16(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr, value) +#define HAL_RUART_READ8(UartIndex, addr) \ + HAL_READ8(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr) +#define HAL_RUART_WRITE8(UartIndex, addr, value) \ + HAL_WRITE8(UART0_REG_BASE+ (UartIndex*RUART_REG_OFF), addr, value) + +typedef struct _RUART_SPEED_SETTING_ { + u32 BaudRate; + u32 Ovsr; + u32 Div; + u32 Ovsr_adj; +}RUART_SPEED_SETTING, *PRUART_SPEED_SETTING; + +typedef enum _UART_RXFIFO_TRIGGER_LEVEL_ { + OneByte = 0x00, + FourBytes = 0x01, + EightBytes = 0x10, + FourteenBytes = 0x11 +}UART_RXFIFO_TRIGGER_LEVEL, *PUART_RXFIFO_TRIGGER_LEVEL; + +typedef enum _RUART0_PINMUX_SELECT_ { + RUART0_MUX_TO_GPIOC = S0, + RUART0_MUX_TO_GPIOE = S1, + RUART0_MUX_TO_GPIOA = S2 +}RUART0_PINMUX_SELECT, *PRUART0_PINMUX_SELECT; + +typedef enum _RUART1_PINMUX_SELECT_ { + RUART1_MUX_TO_GPIOD = S0, + RUART1_MUX_TO_GPIOE = S1, + RUART1_MUX_TO_GPIOB = S2 +}RUART1_PINMUX_SELECT, *PRUART1_PINMUX_SELECT; + +typedef enum _RUART2_PINMUX_SELECT_ { + RUART2_MUX_TO_GPIOA = S0, + RUART2_MUX_TO_GPIOC = S1, + RUART2_MUX_TO_GPIOD = S2 +}RUART2_PINMUX_SELECT, *PRUART2_PINMUX_SELECT; + +typedef enum _RUART_FLOW_CONTROL_ { + AUTOFLOW_DISABLE = 0, + AUTOFLOW_ENABLE = 1 +}RUART_FLOW_CONTROL, *PRUART_FLOW_CONTROL; + +typedef enum _RUART_WORD_LEN_SEL_ { + RUART_WLS_7BITS = 0, + RUART_WLS_8BITS = 1 +}RUART_WORD_LEN_SEL, *PRUART_WORD_LEN_SEL; + +typedef enum _RUART_STOP_BITS_ { + RUART_NO_STOP_BIT = 0, + RUART_1_STOP_BIT = 1 +}RUART_STOP_BITS, *PRUART_STOP_BITS; + +typedef enum _RUART_PARITY_CONTROL_ { + RUART_PARITY_DISABLE = 0, + RUART_PARITY_ENABLE = 1 +}RUART_PARITY_CONTROL, *PRUART_PARITY_CONTROL; + +typedef enum _RUART_PARITY_TYPE_ { + RUART_ODD_PARITY = 0, + RUART_EVEN_PARITY = 1 +}RUART_PARITY_TYPE, *PRUART_PARITY_TYPE; + +typedef enum _RUART_STICK_PARITY_CONTROL_ { + RUART_STICK_PARITY_DISABLE = 0, + RUART_STICK_PARITY_ENABLE = 1 +}RUART_STICK_PARITY_CONTROL, *PRUART_STICK_PARITY_CONTROL; + +typedef enum _UART_INT_ID_ { + ModemStatus = 0, + TxFifoEmpty = 1, + ReceiverDataAvailable = 2, + ReceivLineStatus = 3, + TimeoutIndication = 6 +}UART_INT_ID, *PUART_INT_ID; + +typedef enum _HAL_UART_State_ +{ + HAL_UART_STATE_NULL = 0x00, // UART hardware not been initial yet + HAL_UART_STATE_READY = 0x10, // UART is initialed, ready to use + HAL_UART_STATE_BUSY = 0x20, // UART hardware is busy on configuration + HAL_UART_STATE_BUSY_TX = 0x21, // UART is buzy on TX + HAL_UART_STATE_BUSY_RX = 0x22, // UART is busy on RX + HAL_UART_STATE_BUSY_TX_RX = 0x23, // UART is busy on TX an RX + HAL_UART_STATE_TIMEOUT = 0x30, // Transfer timeout + HAL_UART_STATE_ERROR = 0x40 // UART Error +}HAL_UART_State, *PHAL_UART_State; + +typedef enum _HAL_UART_Status_ +{ + HAL_UART_STATUS_OK = 0x00, // Transfer OK + HAL_UART_STATUS_TIMEOUT = 0x01, // Transfer Timeout + HAL_UART_STATUS_ERR_OVERRUN = 0x02, // RX Over run + HAL_UART_STATUS_ERR_PARITY = 0x04, // Parity error + HAL_UART_STATUS_ERR_FRAM = 0x08, // Framing Error + HAL_UART_STATUS_ERR_BREAK = 0x10, // Break Interrupt + HAL_UART_STATUS_ERR_PARA = 0x20, // Parameter error + HAL_UART_STATUS_ERR_RXFIFO = 0x80, // RX FIFO error +}HAL_UART_Status, *PHAL_UART_Status; + +u32 +HalRuartGetDebugValueRtl8195a( + IN VOID* Data, + IN u32 DbgSel + ); + +#if 0 +u32 +FindElementIndex( + u32 Element, + u32* Array + ); +#endif + +VOID +RuartResetRxFifoRtl8195a( + IN u8 UartIndex + ); +#if 0 +VOID +RuartBusDomainEnableRtl8195a( + IN u8 UartIndex + ); +#endif + +HAL_Status +HalRuartResetRxFifoRtl8195a( + IN VOID *Data + ); + +HAL_Status +HalRuartInitRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartDeInitRtl8195a( + IN VOID *Data ///< RUART Adapter + ); + +HAL_Status +HalRuartPutCRtl8195a( + IN VOID *Data, + IN u8 TxData + ); + +u32 +HalRuartSendRtl8195a( + IN VOID *Data, + IN u8 *pTxData, + IN u32 Length, + IN u32 Timeout + ); + +HAL_Status +HalRuartIntSendRtl8195a( + IN VOID *Data, // PHAL_RUART_ADAPTER + IN u8 *pTxData, // the Buffer to be send + IN u32 Length // the length of data to be send + ); + +HAL_Status +HalRuartDmaSendRtl8195a( + IN VOID *Data, // PHAL_RUART_ADAPTER + IN u8 *pTxData, // the Buffer to be send + IN u32 Length // the length of data to be send +); + +HAL_Status +HalRuartStopSendRtl8195a( + IN VOID *Data // PHAL_RUART_ADAPTER +); + +HAL_Status +HalRuartGetCRtl8195a( + IN VOID *Data, + OUT u8 *pRxByte + ); + +u32 +HalRuartRecvRtl8195a( + IN VOID *Data, + IN u8 *pRxData, + IN u32 Length, + IN u32 Timeout + ); + +HAL_Status +HalRuartIntRecvRtl8195a( + IN VOID *Data, ///< RUART Adapter + IN u8 *pRxData, ///< Rx buffer + IN u32 Length // buffer length + ); + +HAL_Status +HalRuartDmaRecvRtl8195a( + IN VOID *Data, ///< RUART Adapter + IN u8 *pRxData, ///< Rx buffer + IN u32 Length // buffer length + ); + +HAL_Status +HalRuartStopRecvRtl8195a( + IN VOID *Data // PHAL_RUART_ADAPTER +); + +u8 +HalRuartGetIMRRtl8195a( + IN VOID *Data + ); + +_LONG_CALL_ VOID +HalRuartSetIMRRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartDmaInitRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartRTSCtrlRtl8195a( + IN VOID *Data, + IN BOOLEAN RtsCtrl + ); + +VOID +HalRuartRegIrqRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartIntEnableRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartIntDisableRtl8195a( + IN VOID *Data + ); + +VOID +HalRuartAdapterLoadDefRtl8195a( + IN VOID *pAdp, + IN u8 UartIdx +); + +VOID +HalRuartTxGdmaLoadDefRtl8195a( + IN VOID *pAdp, + IN VOID *pCfg +); + +VOID +HalRuartRxGdmaLoadDefRtl8195a( + IN VOID *pAdp, + IN VOID *pCfg +); + +_LONG_CALL_ HAL_Status HalRuartIntSendRtl8195aV02( + IN VOID *Data, // PHAL_RUART_ADAPTER + IN u8 *pTxData, // the Buffer to be send + IN u32 Length // the length of data to be send +); + +_LONG_CALL_ HAL_Status +HalRuartIntRecvRtl8195aV02( + IN VOID *Data, ///< RUART Adapter + IN u8 *pRxData, ///< Rx buffer + IN u32 Length // buffer length +); + +_LONG_CALL_ s32 +FindElementIndex_v02( + u32 Element, ///< RUART Baudrate + u32* Array, ///< Pre-defined Baudrate Array + u32 ElementNo +); + +_LONG_CALL_ HAL_Status HalRuartInitRtl8195a_v02(IN VOID *Data); + +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_wdt.h b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_wdt.h new file mode 100644 index 0000000..edbedd2 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/rtl8195a_wdt.h @@ -0,0 +1,86 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2014 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTL8195A_WDT_H_ +#define _RTL8195A_WDT_H_ + +#define WDGTIMERELY (10*1024) //us + +typedef struct _WDG_REG_ { + u16 WdgScalar; + u8 WdgEnByte; + u8 WdgClear:1; + u8 WdgCunLimit:4; + u8 Rsvd:1; + u8 WdgMode:1; + u8 WdgToISR:1; +}WDG_REG, *PWDG_REG; + +typedef struct _WDG_ADAPTER_ { + + WDG_REG Ctrl; + IRQ_HANDLE IrqHandle; + TIMER_ADAPTER WdgGTimer; + VOID (*UserCallback)(u32 callback_id); // User callback function + u32 callback_id; +}WDG_ADAPTER, *PWDG_ADAPTER; + +typedef enum _WDG_CNTLMT_ { + CNT1H = 0, + CNT3H = 1, + CNT7H = 2, + CNTFH = 3, + CNT1FH = 4, + CNT3FH = 5, + CNT7FH = 6, + CNTFFH = 7, + CNT1FFH = 8, + CNT3FFH = 9, + CNT7FFH = 10, + CNTFFFH = 11 +}WDG_CNTLMT, *PWDG_CNTLMT; + + +typedef enum _WDG_MODE_ { + INT_MODE = 0, + RESET_MODE = 1 +}WDG_MODE, *PWDG_MODE; + +extern VOID +WDGInitial( + IN u32 Period +); + +extern VOID +WDGIrqInitial( + VOID +); + +extern VOID +WDGIrqInitial( + VOID +); + +extern VOID +WDGStop( + VOID +); + +extern VOID +WDGRefresh( + VOID +); + +extern VOID +WDGIrqCallBackReg( + IN VOID *CallBack, + IN u32 Id +); + +#endif //_RTL8195A_WDT_H_ diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_adc.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_adc.c new file mode 100644 index 0000000..133b1fa --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_adc.c @@ -0,0 +1,380 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "platform_autoconf.h" +#include "diag.h" +#include "rtl8195a_adc.h" +#include "hal_adc.h" + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CInit8195a +// +// Description: +// To initialize I2C module by using the given data. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the DeInit process. +// _EXIT_SUCCESS if the initialization succeeded. +// _EXIT_FAILURE if the initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalADCInit8195a( + IN VOID *Data +) +{ + PHAL_ADC_INIT_DAT pHalAdcInitData = (PHAL_ADC_INIT_DAT)Data; + u32 AdcTempDat; + u8 AdcTempIdx = pHalAdcInitData->ADCIdx; + + /* Enable ADC power cut */ +/* + AdcTempDat = HAL_ADC_READ32(REG_ADC_POWER); + AdcTempDat |= BIT_ADC_PWR_AUTO; + HAL_ADC_WRITE32(REG_ADC_POWER, AdcTempDat); +*/ + + /* ADC Control register set-up*/ + AdcTempDat = 0; + AdcTempDat |= (BIT_CTRL_ADC_COMP_ONLY(pHalAdcInitData->ADCCompOnly) | + BIT_CTRL_ADC_ONESHOT(pHalAdcInitData->ADCOneShotEn) | + BIT_CTRL_ADC_OVERWRITE(pHalAdcInitData->ADCOverWREn) | + BIT_CTRL_ADC_ENDIAN(pHalAdcInitData->ADCEndian) | + BIT_CTRL_ADC_BURST_SIZE(pHalAdcInitData->ADCBurstSz) | + BIT_CTRL_ADC_THRESHOLD(pHalAdcInitData->ADCOneShotTD) | + BIT_CTRL_ADC_DBG_SEL(pHalAdcInitData->ADCDbgSel)); + HAL_ADC_WRITE32(REG_ADC_CONTROL,AdcTempDat); + + DBG_8195A_ADC_LVL(HAL_ADC_LVL,"REG_ADC_CONTROL:%x\n", HAL_ADC_READ32(REG_ADC_CONTROL)); + + /* ADC compare value and compare method setting*/ + switch (AdcTempIdx) { + case ADC0_SEL: + AdcTempDat = HAL_ADC_READ32(REG_ADC_COMP_VALUE_L); + AdcTempDat &= ~(BIT_ADC_COMP_TH_0(0xFFFF)); + AdcTempDat |= BIT_CTRL_ADC_COMP_TH_0(pHalAdcInitData->ADCCompTD); + HAL_ADC_WRITE32(REG_ADC_COMP_VALUE_L, AdcTempDat); + break; + + case ADC1_SEL: + AdcTempDat = HAL_ADC_READ32(REG_ADC_COMP_VALUE_L); + AdcTempDat &= ~(BIT_ADC_COMP_TH_1(0xFFFF)); + AdcTempDat |= BIT_CTRL_ADC_COMP_TH_1(pHalAdcInitData->ADCCompTD); + HAL_ADC_WRITE32(REG_ADC_COMP_VALUE_L, AdcTempDat); + break; + + case ADC2_SEL: + AdcTempDat = HAL_ADC_READ32(REG_ADC_COMP_VALUE_H); + AdcTempDat &= ~(BIT_ADC_COMP_TH_2(0xFFFF)); + AdcTempDat |= BIT_CTRL_ADC_COMP_TH_2(pHalAdcInitData->ADCCompTD); + HAL_ADC_WRITE32(REG_ADC_COMP_VALUE_H, AdcTempDat); + break; + + case ADC3_SEL: + AdcTempDat = HAL_ADC_READ32(REG_ADC_COMP_VALUE_H); + AdcTempDat &= ~(BIT_ADC_COMP_TH_3(0xFFFF)); + AdcTempDat |= BIT_CTRL_ADC_COMP_TH_3(pHalAdcInitData->ADCCompTD); + HAL_ADC_WRITE32(REG_ADC_COMP_VALUE_H, AdcTempDat); + break; + default: + return _EXIT_FAILURE; + } + + /* ADC compare mode setting */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_COMP_SET); + AdcTempDat &= (~(0x01 << pHalAdcInitData->ADCIdx)); + AdcTempDat |= (BIT_CTRL_ADC_COMP_0_EN(pHalAdcInitData->ADCCompCtrl) << + pHalAdcInitData->ADCIdx); + HAL_ADC_WRITE32(REG_ADC_COMP_SET, AdcTempDat); + + /* ADC audio mode set-up */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD0); + AdcTempDat &= ~(BIT_ADC_AUDIO_EN); + AdcTempDat |= BIT_CTRL_ADC_AUDIO_EN(pHalAdcInitData->ADCAudioEn); + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD0, AdcTempDat); + + /* ADC enable manually setting */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD0); + AdcTempDat &= ~(BIT_ADC_EN_MANUAL); + AdcTempDat |= BIT_CTRL_ADC_EN_MANUAL(pHalAdcInitData->ADCEnManul); + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD0, AdcTempDat); + + + /* ADC analog parameter 0 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD0); + DBG_ADC_INFO("AD0:%x\n", AdcTempDat); + AdcTempDat |= (BIT0); + if (pHalAdcInitData->ADCInInput == 1){ + AdcTempDat &= (~BIT14); + } + else { + AdcTempDat |= (BIT14); + } + AdcTempDat &= (~(BIT3|BIT2)); + + /* Adjust VCM for C-Cut*/ +#ifdef CONFIG_CHIP_C_CUT + AdcTempDat |= (BIT22); +#endif + + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD0, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD0); + DBG_ADC_INFO("AD0:%x\n", AdcTempDat); + + /* ADC analog parameter 1 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD1); + AdcTempDat &= (~BIT1); + AdcTempDat |= (BIT2|BIT0); + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD1, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD1); + DBG_ADC_INFO("AD1:%x\n", AdcTempDat); + + /* ADC analog parameter 2 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD2); + DBG_ADC_INFO("AD2:%x\n", AdcTempDat); + AdcTempDat = 0x67884400; + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD2, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD2); + DBG_ADC_INFO("AD2:%x\n", AdcTempDat); + + /* ADC analog parameter 3 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD3); + DBG_ADC_INFO("AD3:%x\n", AdcTempDat); + AdcTempDat = 0x77780039; + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD3, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD3); + DBG_ADC_INFO("AD3:%x\n", AdcTempDat); + + /* ADC analog parameter 4 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD4); + DBG_ADC_INFO("AD4:%x\n", AdcTempDat); + AdcTempDat = 0x0004d501; + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD4, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD4); + DBG_ADC_INFO("AD4:%x\n", AdcTempDat); + + /* ADC analog parameter 5 */ + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD5); + DBG_ADC_INFO("AD5:%x\n", AdcTempDat); + AdcTempDat = 0x1E010800; + HAL_ADC_WRITE32(REG_ADC_ANAPAR_AD5, AdcTempDat); + AdcTempDat = HAL_ADC_READ32(REG_ADC_ANAPAR_AD5); + DBG_ADC_INFO("AD5:%x\n", AdcTempDat); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CInit8195a +// +// Description: +// To initialize I2C module by using the given data. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the DeInit process. +// _EXIT_SUCCESS if the initialization succeeded. +// _EXIT_FAILURE if the initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalADCDeInit8195a( + IN VOID *Data +) +{ + u32 AdcTempDat; + + AdcTempDat = HAL_ADC_READ32(REG_ADC_POWER); + AdcTempDat &= ~(BIT_ADC_PWR_AUTO); + HAL_ADC_WRITE32(REG_ADC_POWER, AdcTempDat); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CIntrCtrl8195a +// +// Description: +// Modify the I2C interrupt mask according to the given value +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the de-initialization succeeded. +// _EXIT_FAILURE if the de-initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalADCEnableRtl8195a( + IN VOID *Data +){ + PHAL_ADC_INIT_DAT pHalAdcInitData = (PHAL_ADC_INIT_DAT)Data; + u32 AdcTempDat; + + + AdcTempDat = HAL_ADC_READ32(REG_ADC_POWER); + AdcTempDat &= (~BIT_ADC_PWR_AUTO); + AdcTempDat |= BIT_CTRL_ADC_PWR_AUTO(pHalAdcInitData->ADCEn); + HAL_ADC_WRITE32(REG_ADC_POWER, AdcTempDat); + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CIntrCtrl8195a +// +// Description: +// Modify the I2C interrupt mask according to the given value +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the de-initialization succeeded. +// _EXIT_FAILURE if the de-initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalADCIntrCtrl8195a( + IN VOID *Data +){ + PHAL_ADC_INIT_DAT pHalAdcInitData = (PHAL_ADC_INIT_DAT)Data; + + HAL_ADC_WRITE32(REG_ADC_INTR_EN, pHalAdcInitData->ADCIntrMSK); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CReceiveRtl8195a +// +// Description: +// Directly read one data byte a I2C data fifo. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The first data fifo content. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +u32 +HalADCReceiveRtl8195a( + IN VOID *Data +){ + u32 AdcTempDat; + + AdcTempDat = HAL_ADC_READ32(REG_ADC_FIFO_READ); + + return (AdcTempDat); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CReadRegRtl8195a +// +// Description: +// Directly read a I2C register according to the register offset. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// [in] I2CReg - +// The I2C register offset. +// +// Return: +// The register content in u32 format. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +u32 +HalADCReadRegRtl8195a( + IN VOID *Data, + IN u8 I2CReg +){ + u32 AdcTempDat; + + AdcTempDat = HAL_ADC_READ32(I2CReg); + return (AdcTempDat); +} + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_dac.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_dac.c new file mode 100644 index 0000000..a9bc7a7 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_dac.c @@ -0,0 +1,269 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "rtl8195a_dac.h" +#include "hal_dac.h" + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalDACInit8195a +// +// Description: +// To initialize DAC module by using the given data. +// +// Arguments: +// [in] VOID *Data - +// The DAC parameter data struct. +// +// Return: +// The status of the DeInit process. +// _EXIT_SUCCESS if the initialization succeeded. +// _EXIT_FAILURE if the initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-15. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalDACInit8195a( + IN VOID *Data +){ + PHAL_DAC_INIT_DAT pHalDacInitData = (PHAL_DAC_INIT_DAT)Data; + u32 DacTempDat; + u8 DacTempIdx = pHalDacInitData->DACIdx; + + /* Enable DAC power cut */ + DacTempDat = HAL_DAC_READ32(0, REG_DAC_PWR_CTRL); + DacTempDat |= BIT_DAC_PWR_AUTO; + + HAL_DAC_WRITE32(0, REG_DAC_PWR_CTRL, DacTempDat); + + /* Disable DAC module first */ + HAL_DAC_WRITE32(DacTempIdx, REG_DAC_CTRL, 0); + + /* Setup DAC module */ + DacTempDat = 0; + DacTempDat |= (BIT_CTRL_DAC_SPEED(pHalDacInitData->DACDataRate) | + BIT_CTRL_DAC_ENDIAN(pHalDacInitData->DACEndian) | + BIT_CTRL_DAC_FILTER_SETTLE(pHalDacInitData->DACFilterSet) | + BIT_CTRL_DAC_BURST_SIZE(pHalDacInitData->DACBurstSz) | + BIT_CTRL_DAC_DBG_SEL(pHalDacInitData->DACDbgSel) | + BIT_CTRL_DAC_DSC_DBG_SEL(pHalDacInitData->DACDscDbgSel) | + BIT_CTRL_DAC_BYPASS_DSC(pHalDacInitData->DACBPDsc) | + BIT_CTRL_DAC_DELTA_SIGMA(pHalDacInitData->DACDeltaSig)); + + HAL_DAC_WRITE32(DacTempIdx, REG_DAC_CTRL, DacTempDat); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CInit8195a +// +// Description: +// To initialize I2C module by using the given data. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the DeInit process. +// _EXIT_SUCCESS if the initialization succeeded. +// _EXIT_FAILURE if the initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalDACDeInit8195a( + IN VOID *Data +){ + PHAL_DAC_INIT_DAT pHalDacInitData = (PHAL_DAC_INIT_DAT)Data; + u32 DacTempDat; + + DacTempDat = HAL_DAC_READ32(pHalDacInitData->DACIdx, REG_DAC_CTRL); + DacTempDat &= (~BIT_DAC_FIFO_EN); + HAL_DAC_WRITE32(pHalDacInitData->DACIdx, REG_DAC_CTRL ,DacTempDat); + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CIntrCtrl8195a +// +// Description: +// Modify the I2C interrupt mask according to the given value +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the de-initialization succeeded. +// _EXIT_FAILURE if the de-initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalDACEnableRtl8195a( + IN VOID *Data +){ + PHAL_DAC_INIT_DAT pHalDacInitData = (PHAL_DAC_INIT_DAT)Data; + u32 DacTempDat; + u8 DacTempIdx = pHalDacInitData->DACIdx; + + DacTempDat = HAL_DAC_READ32(DacTempIdx, REG_DAC_CTRL); + DacTempDat &= (~BIT_DAC_FIFO_EN); + + DacTempDat |= BIT_CTRL_DAC_FIFO_EN(pHalDacInitData->DACEn); + HAL_DAC_WRITE32(DacTempIdx, REG_DAC_CTRL, DacTempDat); + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CIntrCtrl8195a +// +// Description: +// Modify the I2C interrupt mask according to the given value +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the de-initialization succeeded. +// _EXIT_FAILURE if the de-initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +HalDACIntrCtrl8195a( + IN VOID *Data +){ + PHAL_DAC_INIT_DAT pHalDacInitData = (PHAL_DAC_INIT_DAT)Data; + + HAL_DAC_WRITE32(pHalDacInitData->DACIdx, REG_DAC_INTR_CTRL, pHalDacInitData->DACIntrMSK); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CReceiveRtl8195a +// +// Description: +// Directly read one data byte a I2C data fifo. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The first data fifo content. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +u8 +HalDACSendRtl8195a( + IN VOID *Data +){ + + + return (0); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalDACReadRegRtl8195a +// +// Description: +// +// +// Arguments: +// [in] VOID *Data - +// The DAC parameter data struct. +// [in] I2CReg - +// The DAC register offset. +// +// Return: +// The DAC register content in u32 format. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-15. +// +//--------------------------------------------------------------------------------------------------- +u32 +HalDACReadRegRtl8195a( + IN VOID *Data, + IN u8 I2CReg +){ + PHAL_DAC_INIT_DAT pHalDacInitData = (PHAL_DAC_INIT_DAT)Data; + + //DBG_8195A_DAC("dac read reg idx:%x\n",pHalDacInitData->DACIdx); + //DBG_8195A_DAC("dac read reg offset:%x\n",I2CReg); + + return (u32)HAL_DAC_READ32(pHalDacInitData->DACIdx, I2CReg); +} + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c new file mode 100644 index 0000000..640f787 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c @@ -0,0 +1,243 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "rtl8195a_gdma.h" +#include "hal_gdma.h" + +#ifndef CONFIG_CHIP_D_CUT +BOOL +HalGdmaChBlockSetingRtl8195a( + IN VOID *Data +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + PGDMA_CH_LLI_ELE pLliEle; + struct GDMA_CH_LLI *pGdmaChLli; + struct BLOCK_SIZE_LIST *pGdmaChBkLi; + u32 MultiBlockCount = pHalGdmaAdapter->MaxMuliBlock; + u32 CtlxLow, CtlxUp, CfgxLow, CfgxUp; + u8 GdmaIndex = pHalGdmaAdapter->GdmaIndex; + u8 ChNum = pHalGdmaAdapter->ChNum; + u32 ChEn = pHalGdmaAdapter->ChEn; + u8 GdmaChIsrBitmap = (ChEn & 0xFF); + u8 PendingIsrIndex; + + + pLliEle = pHalGdmaAdapter->pLlix->pLliEle; + pGdmaChLli = pHalGdmaAdapter->pLlix->pNextLli; + pGdmaChBkLi = pHalGdmaAdapter->pBlockSizeList; + + + //4 1) Check chanel is avaliable + if (HAL_GDMAX_READ32(GdmaIndex, REG_GDMA_CH_EN) & ChEn) { + //4 Disable Channel + DBG_GDMA_WARN("Channel had used; Disable Channel!!!!\n"); + + HalGdmaChDisRtl8195a(Data); + + } + + //4 2) Check if there are the pending isr; TFR, Block, Src Tran, Dst Tran, Error + for (PendingIsrIndex=0; PendingIsrIndex<5;PendingIsrIndex++) { + + u32 PendRaw, PendStstus; + PendRaw = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_RAW_INT_BASE + PendingIsrIndex*8)); + PendStstus = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_STATUS_INT_BASE + PendingIsrIndex*8)); + + if ((PendRaw & GdmaChIsrBitmap) || (PendStstus & GdmaChIsrBitmap)) { + //4 Clear Pending Isr + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CLEAR_INT_BASE + PendingIsrIndex*8), + (PendStstus & (GdmaChIsrBitmap)) + ); + + } + } + + //4 Fill in SARx register + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_SAR + ChNum*REG_GDMA_CH_OFF), + (pHalGdmaAdapter->ChSar) + ); + + + //4 Fill in DARx register + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_DAR + ChNum*REG_GDMA_CH_OFF), + (pHalGdmaAdapter->ChDar) + ); + + + + //4 3) Process CTLx + CtlxLow = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_CH_CTL + ChNum*REG_GDMA_CH_OFF)); + + //4 Clear Config low register bits + CtlxLow &= (BIT_INVC_CTLX_LO_INT_EN & + BIT_INVC_CTLX_LO_DST_TR_WIDTH & + BIT_INVC_CTLX_LO_SRC_TR_WIDTH & + BIT_INVC_CTLX_LO_DINC & + BIT_INVC_CTLX_LO_SINC & + BIT_INVC_CTLX_LO_DEST_MSIZE & + BIT_INVC_CTLX_LO_SRC_MSIZE & + BIT_INVC_CTLX_LO_TT_FC & + BIT_INVC_CTLX_LO_LLP_DST_EN & + BIT_INVC_CTLX_LO_LLP_SRC_EN); + + CtlxUp = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_CH_CTL + ChNum*REG_GDMA_CH_OFF + 4)); + + //4 Clear Config upper register bits + CtlxUp &= (BIT_INVC_CTLX_UP_BLOCK_BS & + BIT_INVC_CTLX_UP_DONE); + + + CtlxLow = BIT_CTLX_LO_INT_EN(pHalGdmaAdapter->GdmaCtl.IntEn) | + BIT_CTLX_LO_DST_TR_WIDTH(pHalGdmaAdapter->GdmaCtl.DstTrWidth) | + BIT_CTLX_LO_SRC_TR_WIDTH(pHalGdmaAdapter->GdmaCtl.SrcTrWidth) | + BIT_CTLX_LO_DINC(pHalGdmaAdapter->GdmaCtl.Dinc) | + BIT_CTLX_LO_SINC(pHalGdmaAdapter->GdmaCtl.Sinc) | + BIT_CTLX_LO_DEST_MSIZE(pHalGdmaAdapter->GdmaCtl.DestMsize) | + BIT_CTLX_LO_SRC_MSIZE(pHalGdmaAdapter->GdmaCtl.SrcMsize) | + BIT_CTLX_LO_TT_FC(pHalGdmaAdapter->GdmaCtl.TtFc) | + BIT_CTLX_LO_LLP_DST_EN(pHalGdmaAdapter->GdmaCtl.LlpDstEn) | + BIT_CTLX_LO_LLP_SRC_EN(pHalGdmaAdapter->GdmaCtl.LlpSrcEn) | + CtlxLow; + + CtlxUp = BIT_CTLX_UP_BLOCK_BS(pGdmaChBkLi->BlockSize) | + BIT_CTLX_UP_DONE(pHalGdmaAdapter->GdmaCtl.Done) | + CtlxUp; + + //4 Fill in CTLx register + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_CTL + ChNum*REG_GDMA_CH_OFF), + CtlxLow + ); + + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_CTL + ChNum*REG_GDMA_CH_OFF +4), + CtlxUp + ); + + //4 4) Program CFGx + + CfgxLow = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_CH_CFG + ChNum*REG_GDMA_CH_OFF)); + + CfgxLow &= (BIT_INVC_CFGX_LO_CH_PRIOR & + BIT_INVC_CFGX_LO_CH_SUSP & + BIT_INVC_CFGX_LO_HS_SEL_DST & + BIT_INVC_CFGX_LO_HS_SEL_SRC & + BIT_INVC_CFGX_LO_LOCK_CH_L & + BIT_INVC_CFGX_LO_LOCK_B_L & + BIT_INVC_CFGX_LO_LOCK_CH & + BIT_INVC_CFGX_LO_LOCK_B & + BIT_INVC_CFGX_LO_RELOAD_SRC & + BIT_INVC_CFGX_LO_RELOAD_DST); + + CfgxUp = HAL_GDMAX_READ32(GdmaIndex, + (REG_GDMA_CH_CFG + ChNum*REG_GDMA_CH_OFF + 4)); + + CfgxUp &= (BIT_INVC_CFGX_UP_FIFO_MODE & + BIT_INVC_CFGX_UP_DS_UPD_EN & + BIT_INVC_CFGX_UP_SS_UPD_EN & + BIT_INVC_CFGX_UP_SRC_PER & + BIT_INVC_CFGX_UP_DEST_PER); + + CfgxLow = BIT_CFGX_LO_CH_PRIOR(pHalGdmaAdapter->GdmaCfg.ChPrior) | + BIT_CFGX_LO_CH_SUSP(pHalGdmaAdapter->GdmaCfg.ChSusp) | + BIT_CFGX_LO_HS_SEL_DST(pHalGdmaAdapter->GdmaCfg.HsSelDst) | + BIT_CFGX_LO_HS_SEL_SRC(pHalGdmaAdapter->GdmaCfg.HsSelSrc) | + BIT_CFGX_LO_LOCK_CH_L(pHalGdmaAdapter->GdmaCfg.LockChL) | + BIT_CFGX_LO_LOCK_B_L(pHalGdmaAdapter->GdmaCfg.LockBL) | + BIT_CFGX_LO_LOCK_CH(pHalGdmaAdapter->GdmaCfg.LockCh) | + BIT_CFGX_LO_LOCK_B(pHalGdmaAdapter->GdmaCfg.LockB) | + BIT_CFGX_LO_RELOAD_SRC(pHalGdmaAdapter->GdmaCfg.ReloadSrc) | + BIT_CFGX_LO_RELOAD_DST(pHalGdmaAdapter->GdmaCfg.ReloadDst) | + CfgxLow; + + CfgxUp = BIT_CFGX_UP_FIFO_MODE(pHalGdmaAdapter->GdmaCfg.FifoMode) | + BIT_CFGX_UP_DS_UPD_EN(pHalGdmaAdapter->GdmaCfg.DsUpdEn) | + BIT_CFGX_UP_SS_UPD_EN(pHalGdmaAdapter->GdmaCfg.SsUpdEn) | + BIT_CFGX_UP_SRC_PER(pHalGdmaAdapter->GdmaCfg.SrcPer) | + BIT_CFGX_UP_DEST_PER(pHalGdmaAdapter->GdmaCfg.DestPer) | + CfgxUp; + + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_CFG + ChNum*REG_GDMA_CH_OFF), + CfgxLow + ); + + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_CFG + ChNum*REG_GDMA_CH_OFF +4), + CfgxUp + ); + + + + //4 Check 4 Bytes Alignment + if ((u32)(pLliEle) & 0x3) { + DBG_GDMA_WARN("LLi Addr: 0x%x not 4 bytes alignment!!!!\n", + pHalGdmaAdapter->pLli); + return _FALSE; + } + + HAL_GDMAX_WRITE32(GdmaIndex, + (REG_GDMA_CH_LLP + ChNum*REG_GDMA_CH_OFF), + pLliEle + ); + + //4 Update the first llp0 + pLliEle->CtlxLow = CtlxLow; + pLliEle->CtlxUp = CtlxUp; + pLliEle->Llpx = (u32)pGdmaChLli->pLliEle; + DBG_GDMA_INFO("Block Count %d\n", MultiBlockCount); + + pGdmaChBkLi = pGdmaChBkLi->pNextBlockSiz; + + while (MultiBlockCount > 1) { + MultiBlockCount--; + DBG_GDMA_INFO("Block Count %d\n", MultiBlockCount); + pLliEle = pGdmaChLli->pLliEle; + + if (NULL == pLliEle) { + DBG_8195A("pLliEle Null Point!!!!!\n"); + return _FALSE; + } + + //4 Clear the last element llp enable bit + if (1 == MultiBlockCount) { + if (((pHalGdmaAdapter->Rsvd4to7) & 0x01) == 0){ + CtlxLow &= (BIT_INVC_CTLX_LO_LLP_DST_EN & + BIT_INVC_CTLX_LO_LLP_SRC_EN); + } + } + //4 Update block size for transfer + CtlxUp &= (BIT_INVC_CTLX_UP_BLOCK_BS); + CtlxUp |= BIT_CTLX_UP_BLOCK_BS(pGdmaChBkLi->BlockSize); + + //4 Update tje Lli and Block size list point to next llp + pGdmaChLli = pGdmaChLli->pNextLli; + pGdmaChBkLi = pGdmaChBkLi->pNextBlockSiz; + + //4 Updatethe Llpx context + pLliEle->CtlxLow = CtlxLow; + pLliEle->CtlxUp = CtlxUp; + pLliEle->Llpx = pGdmaChLli->pLliEle; + + } + + return _TRUE; +} +#endif diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c new file mode 100644 index 0000000..6f67d90 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c @@ -0,0 +1,53 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "hal_gpio.h" +#include "rtl8195a_gpio.h" +#include "gpio_irq_api.h" + +extern PHAL_GPIO_ADAPTER _pHAL_Gpio_Adapter; + +/** + * @brief Clear the pending interrupt of a specified pin + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin. + * + * @retval None + */ +HAL_Status +HAL_GPIO_ClearISR_8195a( + HAL_GPIO_PIN *GPIO_Pin +) +{ + u8 port_num; + u8 pin_num; + HAL_GPIO_PIN_MODE pin_mode; + + port_num = HAL_GPIO_GET_PORT_BY_NAME(GPIO_Pin->pin_name); + pin_num = HAL_GPIO_GET_PIN_BY_NAME(GPIO_Pin->pin_name); + pin_mode = GPIO_Pin->pin_mode; + + if ((pin_mode & HAL_GPIO_PIN_INT_MODE)==0 || (port_num != GPIO_PORT_A)) { + DBG_GPIO_ERR("HAL_GPIO_ClearISR_8195a: This pin(%x:%x) is'nt an interrupt pin\n", GPIO_Pin->pin_name, GPIO_Pin->pin_mode); + return HAL_ERR_PARA; + } + + if (GPIO_Lock() != HAL_OK) { + return HAL_BUSY; + } + + // Clear pending interrupt before unmask it + HAL_WRITE32(GPIO_REG_BASE, GPIO_PORTA_EOI, (1<I2SIdx; + I2SEn = pHalI2SInitData->I2SEn; + I2SMaster = pHalI2SInitData->I2SMaster; + I2SWordLen = pHalI2SInitData->I2SWordLen; + I2SChNum = pHalI2SInitData->I2SChNum; + I2SPageNum = pHalI2SInitData->I2SPageNum; + I2SPageSize = pHalI2SInitData->I2SPageSize; + I2SRate = pHalI2SInitData->I2SRate; + I2STRxAct = pHalI2SInitData->I2STRxAct; + I2STxData = pHalI2SInitData->I2STxData; + I2SRxData = pHalI2SInitData->I2SRxData; + + + /* Disable the I2S first, and reset to default */ + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, BIT_CTRL_CTLX_I2S_EN(0) | + BIT_CTRL_CTLX_I2S_SW_RSTN(1)); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, BIT_CTRL_CTLX_I2S_EN(0) | + BIT_CTRL_CTLX_I2S_SW_RSTN(0)); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, BIT_CTRL_CTLX_I2S_EN(0) | + BIT_CTRL_CTLX_I2S_SW_RSTN(1)); + + Tmp = HAL_I2S_READ32(I2SIdx, REG_I2S_CTL); + Tmp |= BIT_CTRL_CTLX_I2S_ENDIAN_SWAP(1); + + if (I2SRate&0x10) + { + Tmp |= BIT_CTRL_CTLX_I2S_CLK_SRC(1); + } + + Tmp |= (BIT_CTRL_CTLX_I2S_WL(I2SWordLen) | BIT_CTRL_CTLX_I2S_CH_NUM(I2SChNum) | + BIT_CTRL_CTLX_I2S_SLAVE_MODE(I2SMaster) | BIT_CTRL_CTLX_I2S_TRX_ACT(I2STRxAct)); + /* set 44.1khz clock source, word length, channel number, master or slave, trx act */ + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, Tmp); + + Tmp = BIT_CTRL_SETTING_I2S_PAGE_SZ(I2SPageSize) | BIT_CTRL_SETTING_I2S_PAGE_NUM(I2SPageNum) | + BIT_CTRL_SETTING_I2S_SAMPLE_RATE(I2SRate); + /* set page size, page number, sample rate */ + HAL_I2S_WRITE32(I2SIdx, REG_I2S_SETTING, Tmp); + + /* need tx rx buffer? need rx page own bit */ + if (I2STxData != NULL) { + HAL_I2S_WRITE32(I2SIdx, REG_I2S_TX_PAGE_PTR, (u32)I2STxData); + } + + if (I2SRxData != NULL) { + HAL_I2S_WRITE32(I2SIdx, REG_I2S_RX_PAGE_PTR, (u32)I2SRxData); + } + + pHalI2SInitData->I2STxIdx = 0; + pHalI2SInitData->I2SRxIdx = 0; + pHalI2SInitData->I2SHWTxIdx = 0; + pHalI2SInitData->I2SHWRxIdx = 0; + /* I2S Clear all interrupts first */ + HalI2SClrAllIntrRtl8195a(pHalI2SInitData); + + /* I2S Disable all interrupts first */ + I2STxIntrMSK = pHalI2SInitData->I2STxIntrMSK; + I2SRxIntrMSK = pHalI2SInitData->I2SRxIntrMSK; + pHalI2SInitData->I2STxIntrMSK = 0; + pHalI2SInitData->I2SRxIntrMSK = 0; + HalI2SIntrCtrlRtl8195a(pHalI2SInitData); + pHalI2SInitData->I2STxIntrMSK = I2STxIntrMSK; + pHalI2SInitData->I2SRxIntrMSK = I2SRxIntrMSK; + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetRateRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_CTL); + reg_value &= ~(BIT_MASK_CTLX_I2S_CLK_SRC << BIT_SHIFT_CTLX_I2S_CLK_SRC); + if (pHalI2SInitData->I2SRate&0x10) + { + reg_value |= BIT_CTRL_CTLX_I2S_CLK_SRC(1); + } + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, reg_value); + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_SETTING); + reg_value &= ~(BIT_MASK_SETTING_I2S_SAMPLE_RATE << BIT_SHIFT_SETTING_I2S_SAMPLE_RATE); + reg_value |= BIT_CTRL_SETTING_I2S_SAMPLE_RATE(pHalI2SInitData->I2SRate); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_SETTING, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetWordLenRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_CTL); + reg_value &= ~(BIT_MASK_CTLX_I2S_WL << BIT_SHIFT_CTLX_I2S_WL); + reg_value |= BIT_CTRL_CTLX_I2S_WL(pHalI2SInitData->I2SWordLen); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetChNumRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_CTL); + reg_value &= ~(BIT_MASK_CTLX_I2S_CH_NUM << BIT_SHIFT_CTLX_I2S_CH_NUM); + reg_value |= BIT_CTRL_CTLX_I2S_CH_NUM(pHalI2SInitData->I2SChNum); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetPageNumRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_SETTING); + reg_value &= ~(BIT_MASK_SETTING_I2S_PAGE_NUM << BIT_SHIFT_SETTING_I2S_PAGE_NUM); + reg_value |= BIT_CTRL_SETTING_I2S_PAGE_NUM(pHalI2SInitData->I2SPageNum); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_SETTING, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetPageSizeRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_SETTING); + reg_value &= ~(BIT_MASK_SETTING_I2S_PAGE_SZ << BIT_SHIFT_SETTING_I2S_PAGE_SZ); + reg_value |= BIT_CTRL_SETTING_I2S_PAGE_SZ(pHalI2SInitData->I2SPageSize); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_SETTING, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetDirectionRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_CTL); + reg_value &= ~(BIT_MASK_CTLX_I2S_TRX_ACT << BIT_SHIFT_CTLX_I2S_TRX_ACT); + reg_value |= BIT_CTRL_CTLX_I2S_TRX_ACT(pHalI2SInitData->I2STRxAct); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_CTL, reg_value); + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SSetDMABufRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 reg_value; + u32 page_num; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg_value = HAL_I2S_READ32(I2SIdx, REG_I2S_SETTING); + reg_value &= ~(BIT_MASK_SETTING_I2S_PAGE_SZ << BIT_SHIFT_SETTING_I2S_PAGE_SZ); + reg_value &= ~(BIT_MASK_SETTING_I2S_PAGE_NUM << BIT_SHIFT_SETTING_I2S_PAGE_NUM); + reg_value |= BIT_CTRL_SETTING_I2S_PAGE_SZ(pHalI2SInitData->I2SPageSize); + reg_value |= BIT_CTRL_SETTING_I2S_PAGE_NUM(pHalI2SInitData->I2SPageNum); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_SETTING, reg_value); + + page_num = pHalI2SInitData->I2SPageNum + 1; + if (pHalI2SInitData->I2STxData) { + HAL_I2S_WRITE32(I2SIdx, REG_I2S_TX_PAGE_PTR, (uint32_t)pHalI2SInitData->I2STxData); + pHalI2SInitData->I2STxIntrMSK = (1<I2STxIntrMSK = 0; + } + + if (pHalI2SInitData->I2SRxData) { + HAL_I2S_WRITE32(I2SIdx, REG_I2S_RX_PAGE_PTR, (uint32_t)pHalI2SInitData->I2SRxData); + pHalI2SInitData->I2SRxIntrMSK = (1<I2SRxIntrMSK = 0; + + } + + // According to the page number to modify the ISR mask + HalI2SIntrCtrlRtl8195a(pHalI2SInitData); + + return _EXIT_SUCCESS; +} + +u8 +HalI2SGetTxPageRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + + u8 I2SIdx; + u16 I2STxIdx = pHalI2SInitData->I2STxIdx; + u32 reg; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg = HAL_I2S_READ32(I2SIdx, REG_I2S_TX_PAGE0_OWN+(I2STxIdx<<2)); + if ((reg & (1<<31)) == 0) { + return I2STxIdx; + } else { + return 0xFF; + } +} + +u8 +HalI2SGetRxPageRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + + u8 I2SIdx; + u16 I2SRxIdx = pHalI2SInitData->I2SRxIdx; + u32 reg; + + I2SIdx = pHalI2SInitData->I2SIdx; + + reg = HAL_I2S_READ32(I2SIdx, REG_I2S_RX_PAGE0_OWN+(I2SRxIdx << 2)); + if ((reg & (1<<31)) == 0) { + return I2SRxIdx; + } else { + return 0xFF; + } +} + +RTK_STATUS +HalI2SPageSendRtl8195a( + IN VOID *Data, + IN u8 PageIdx +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u16 I2STxIdx = pHalI2SInitData->I2STxIdx; + u8 I2SPageNum = pHalI2SInitData->I2SPageNum; + u8 I2SIdx; + + if (I2STxIdx != PageIdx) { + DBG_I2S_ERR("HalI2SPageSendRtl8195a: UnExpected Page Index. TxPage=%d, Expected:%d\r\n", + PageIdx, I2STxIdx); + } + + I2SIdx = pHalI2SInitData->I2SIdx; + + HAL_I2S_WRITE32(I2SIdx, REG_I2S_TX_PAGE0_OWN+4*PageIdx, 1<<31); + I2STxIdx = PageIdx+1; + if (I2STxIdx > I2SPageNum) { + I2STxIdx = 0; + } + pHalI2SInitData->I2STxIdx = I2STxIdx; + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SPageRecvRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u16 I2SRxIdx = pHalI2SInitData->I2SRxIdx; + u8 I2SPageNum = pHalI2SInitData->I2SPageNum; + u32 reg; + u8 I2SIdx; + + I2SIdx = pHalI2SInitData->I2SIdx; + reg = HAL_I2S_READ32(I2SIdx, REG_I2S_RX_PAGE0_OWN+(I2SRxIdx << 2)); + if ((reg & (1<<31)) != 0) { + DBG_I2S_ERR("HalI2SPageRecvRtl8195a: No Idle Rx Page\r\n"); + return _EXIT_FAILURE; + } + + HAL_I2S_WRITE32(I2SIdx, REG_I2S_RX_PAGE0_OWN+(I2SRxIdx<<2), 1<<31); + I2SRxIdx += 1; + if (I2SRxIdx > I2SPageNum) { + I2SRxIdx = 0; + } + pHalI2SInitData->I2SRxIdx = I2SRxIdx; + + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SClearAllOwnBitRtl8195a( + IN VOID *Data +) +{ + PHAL_I2S_INIT_DAT pHalI2SInitData = (PHAL_I2S_INIT_DAT)Data; + u8 I2SIdx; + u32 i; + + I2SIdx = pHalI2SInitData->I2SIdx; + + for (i=0;i<4;i++) { + HAL_I2S_WRITE32(I2SIdx, REG_I2S_TX_PAGE0_OWN+(i<<2), 0); + HAL_I2S_WRITE32(I2SIdx, REG_I2S_RX_PAGE0_OWN+(i<<2), 0); + } + return _EXIT_SUCCESS; +} + +RTK_STATUS +HalI2SDMACtrlRtl8195a( + IN VOID *Data +) +{ + + return _EXIT_SUCCESS; +} + + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_mii.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_mii.c new file mode 100644 index 0000000..b4e7139 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_mii.c @@ -0,0 +1,411 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "rtl8195a_mii.h" +#include "hal_mii.h" + + + +VOID MiiIrqHandle (IN VOID *Data); + +VOID MiiIrqHandle (IN VOID *Data) { + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + u32 RegValue = HalMiiGmacGetInterruptStatusRtl8195a(); +#ifdef CONFIG_MII_VERIFY + extern u8 isRxOK; + extern u8 isTxOK; + extern u8 RxIntCnt; + + +// DBG_8195A("ISR = 0x%08X\n", RegValue); + if(RegValue & GMAC_ISR_ROK) { + HalMiiGmacClearInterruptStatusRtl8195a(0x00410001); + isRxOK = 1; + RxIntCnt++; + } + + if(RegValue & GMAC_ISR_TOK_TI) { + HalMiiGmacClearInterruptStatusRtl8195a(0x00410040); + isTxOK = 1; + } +#else + +#endif +} + + +VOID +ConfigDebugPort_E4(u32 DebugSelect) { + u32 RegValue; + BOOL DebugMsg = _FALSE; + + if (DebugMsg) { + LOGI(ANSI_COLOR_YELLOW"Debug Port Select (0xE4)\n"ANSI_COLOR_RESET); + LOGD2("[P] PERI_ON_010: %X\n", HAL_READ32(PERI_ON_BASE, REG_SOC_FUNC_EN)); + LOGD2("[P] PERI_ON_014: %X\n", HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN)); + } + + RegValue = HAL_MII_READ32(REG_RTL_MII_CCR); + if (DebugMsg) { + LOGD2("[B] 0x00E4: %X\n", RegValue); + } + + RegValue |= DebugSelect << 2; + if (DebugMsg) { + LOGD2("[B] RegValue: %X\n", RegValue); + } + + HAL_MII_WRITE32(REG_RTL_MII_CCR, RegValue); + if (DebugMsg) { + LOGD2("[A] 0x00E4: %X\n", HAL_MII_READ32(REG_RTL_MII_CCR)); + } +} + + +/** + * MII Initialize. + * + * MII Initialize. + * + * Initialization Steps: + * I. Rtl8195A Board Configurations: + * 1. MII Function Enable & AHB mux + * + * @return runtime status value. + */ +BOOL +HalMiiGmacInitRtl8195a( + IN VOID *Data + ) +{ + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + u32 RegValue; + + + /* 1. enable MII Pinmux & disable SDIO Host/Device mode Pinmux */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL); + RegValue |= BIT24; + RegValue &= ~(BIT0 | BIT1); // Important! + HAL_WRITE32(PERI_ON_BASE, REG_HCI_PINMUX_CTRL, RegValue); + + /* 2. enable MII IP block (214, 12) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN); + RegValue |= BIT12; + HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, RegValue); + + /* 3. Lexra2AHB Function Enable (304, 11) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL); + RegValue |= BIT11; + HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue); + + /* 4. enable MII bus clock (240, 24|25) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0); + RegValue |= (BIT24 | BIT25); + HAL_WRITE32(PERI_ON_BASE, REG_PESOC_HCI_CLK_CTRL0, RegValue); + + /* 5. */ + RegValue = HAL_READ32(SYSTEM_CTRL_BASE, 0x74) & 0xFFFFC7FF; + HAL_WRITE32(SYSTEM_CTRL_BASE, 0x74, (RegValue | 0x00003000)); + + /* 6. AHB mux: select MII (214, 13) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN); + RegValue |= BIT13; + HAL_WRITE32(PERI_ON_BASE, REG_SOC_HCI_COM_FUNC_EN, RegValue); + + /* 7. Vendor Register Clock Enable (230, 6|7) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_CLK_CTRL); + RegValue |= (BIT6 | BIT7); + HAL_WRITE32(PERI_ON_BASE, REG_PESOC_CLK_CTRL, RegValue); + + /* 8. Enable GMAC Lexra Timeout (090, 16|17|18) */ + RegValue = HAL_READ32(VENDOR_REG_BASE, 0x0090); + RegValue |= (BIT16 | BIT17 | BIT18); + HAL_WRITE32(VENDOR_REG_BASE, 0x0090, RegValue); + + /* 9. Endian Swap Control (304, 12|13) */ + RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL); + RegValue |= (BIT12 | BIT13); + HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue); + + return _TRUE; +} + + +BOOL +HalMiiInitRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + u32 RegValue; + + return _TRUE; +} + + +BOOL +HalMiiGmacResetRtl8195a( + IN VOID *Data + ) +{ + HAL_MII_WRITE32(REG_RTL_MII_CR, (HAL_MII_READ32(REG_RTL_MII_CR) | BIT0)); + + return _TRUE; +} + + +BOOL +HalMiiGmacEnablePhyModeRtl8195a( + IN VOID *Data + ) +{ + return _TRUE; +} + + +u32 +HalMiiGmacXmitRtl8195a( + IN VOID *Data + ) +{ + return 0; +} + + +VOID +HalMiiGmacCleanTxRingRtl8195a( + IN VOID *Data + ) +{ +} + + +VOID +HalMiiGmacFillTxInfoRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + PTX_INFO pTx_Info = pMiiAdapter->pTx_Info; + VOID* TxBuffer = pMiiAdapter->TxBuffer; + u32 RegValue; + + LOGI(ANSI_COLOR_GREEN"==[ Tx Descriptor Configuration ]======\n"ANSI_COLOR_RESET); + + pTx_Info->opts1.dw = 0xBC8001FE; + /* pTx_Info->opts1.dw = 0xBC800080; // size: 128 */ + + pTx_Info->addr = (u32)TxBuffer; + pTx_Info->opts2.dw = 0x0400279F; + pTx_Info->opts3.dw = 0x00000000; + /* pTx_Info->opts4.dw = 0x57800000; */ + pTx_Info->opts4.dw = 0x1FE00000; + LOGI("pTx_Info->addr: %X\n", pTx_Info->addr); + + LOGI(ANSI_COLOR_YELLOW"TxFDP1 Register(0x%04X) (W: 0x%X)\n"ANSI_COLOR_RESET, REG_RTL_MII_TXFDP1, pTx_Info); + RegValue = HAL_MII_READ32(REG_RTL_MII_TXFDP1); + LOGD2("[B] REG_RTL_MII_TXFDP1: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_TXFDP1, pTx_Info); + LOGD2("[A] REG_RTL_MII_TXFDP1: %X\n", HAL_MII_READ32(REG_RTL_MII_TXFDP1)); +} + + +VOID +HalMiiGmacFillRxInfoRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + PRX_INFO pRx_Info = pMiiAdapter->pRx_Info; + VOID* RxBuffer = pMiiAdapter->RxBuffer; + u32 RegValue; + + LOGI(ANSI_COLOR_GREEN"==[ Rx Descriptor Configuration ]======\n"ANSI_COLOR_RESET); + + /* pRx_Info->opts1.dw = 0x80000200; //Data Length: 4095(FFF), 512(200) */ + pRx_Info->opts1.dw = 0x800001FC; //Data Length: 4095(FFF), 512(200) + /* pRx_Info->opts1.dw = 0x8000007F; */ + + pRx_Info->addr = (u32)RxBuffer; + pRx_Info->opts2.dw = 0x00000000; + pRx_Info->opts3.dw = 0x00000000; + + LOGI(ANSI_COLOR_YELLOW"RxFDP1 Register(0x%04X) (W: 0x%X)\n"ANSI_COLOR_RESET, REG_RTL_MII_RXFDP1, pRx_Info); + RegValue = HAL_MII_READ32(REG_RTL_MII_RXFDP1); + LOGD2("[B] REG_RTL_MII_RXFDP1: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_RXFDP1, pRx_Info); + LOGD2("[A] REG_RTL_MII_RXFDP1: %X\n", HAL_MII_READ32(REG_RTL_MII_RXFDP1)); +} + + +VOID +HalMiiGmacTxRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + PTX_INFO pTx_Info = pMiiAdapter->pTx_Info; + u32 RegValue; + + LOGI(ANSI_COLOR_GREEN"==[ Tx ]===============================\n"ANSI_COLOR_RESET); + + LOGI(ANSI_COLOR_YELLOW"REG_RTL_MII_IOCMD Register(0x%04X): Enable Tx (W: 0x10, BIT4)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD); + LOGD2("[B] REG_RTL_MII_IOCMD: %X\n", RegValue); + RegValue |= BIT_IOCMD_TXENABLE(1); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue); + LOGD2("[A] REG_RTL_MII_IOCMD: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD)); + LOGD4("--> Tx Descriptor opts1: %X\n", HAL_READ32(pTx_Info, 0)); + + LOGI(ANSI_COLOR_YELLOW"REG_RTL_MII_IOCMD Register(0x%04X): Enable 1st Tx (W: 0x1, BIT0)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD); + LOGD2("[B] REG_RTL_MII_IOCMD: %X\n", RegValue); + RegValue |= BIT_IOCMD_FIRST_DMATX_ENABLE(1); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue); + LOGD2("[A] REG_RTL_MII_IOCMD: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD)); + LOGD4("--> Tx Descriptor opts1: %X\n", HAL_READ32(pTx_Info, 0)); +} + + +VOID +HalMiiGmacRxRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + u32 RegValue; + + LOGI(ANSI_COLOR_GREEN"==[ Rx ]===============================\n"ANSI_COLOR_RESET); + LOGI(ANSI_COLOR_YELLOW"Transmit (Tx) Configuration Register (0x%04X) (W: 0x00000C00)\n"ANSI_COLOR_RESET, REG_RTL_MII_TCR); + RegValue = HAL_MII_READ32(REG_RTL_MII_TCR); + LOGD2("[B] REG_RTL_MII_TCR: %X\n", RegValue); + + HAL_MII_WRITE32(REG_RTL_MII_TCR, 0x00000D00); // loopback R2T mode + LOGD2("[A] REG_RTL_MII_TCR: %X\n", HAL_MII_READ32(REG_RTL_MII_TCR)); + + LOGI(ANSI_COLOR_YELLOW"Receive (Rx) Configuration Register (0x%04X) (W: 0x0000007F)\n"ANSI_COLOR_RESET, REG_RTL_MII_RCR); + RegValue = HAL_MII_READ32(REG_RTL_MII_RCR); + LOGD2("[B] REG_RTL_MII_RCR: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_RCR, 0x0000007F); + LOGD2("[A] REG_RTL_MII_RCR: %X\n", HAL_MII_READ32(REG_RTL_MII_RCR)); + + LOGI(ANSI_COLOR_YELLOW"EhtrntRxCPU_Des_Num1 (0x%04X) (W: 0x1F0A0F00)\n"ANSI_COLOR_RESET, REG_RTL_MII_ETNRXCPU1); + RegValue = HAL_MII_READ32(REG_RTL_MII_ETNRXCPU1); + LOGD2("[B] REG_RTL_MII_ETNRXCPU1: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_ETNRXCPU1, 0x1F0A0F00); + LOGD2("[A] REG_RTL_MII_ETNRXCPU1: %X\n", HAL_MII_READ32(REG_RTL_MII_ETNRXCPU1)); + + LOGI(ANSI_COLOR_YELLOW"Rx_Pse_Des_Thres_1_h (0x%04X) (W: 0x00000022)\n"ANSI_COLOR_RESET, REG_RTL_MII_RX_PSE1); + RegValue = HAL_MII_READ32(REG_RTL_MII_RX_PSE1); + LOGD2("[B] REG_RTL_MII_RX_PSE1: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_RX_PSE1, 0x00000022); + LOGD2("[A] REG_RTL_MII_RX_PSE1: %X\n", HAL_MII_READ32(REG_RTL_MII_RX_PSE1)); + + LOGI(ANSI_COLOR_YELLOW"Ethernet_IO_CMD1 Register(0x%04X): Enable Rx Ring1 (W: 0x10000, BIT16)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD1); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1); + LOGD2("[B] REG_RTL_MII_IOCMD1: %X\n", RegValue); + RegValue |= BIT_IOCMD1_FIRST_DMARX_ENABLE(1); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, RegValue); + LOGD2("[A] REG_RTL_MII_IOCMD1: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD1)); + + LOGI(ANSI_COLOR_YELLOW"REG_RTL_MII_IOCMD Register(0x%04X): Enable Rx (W: 0x20, BIT5)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD); + LOGD2("[B] REG_RTL_MII_IOCMD: %X\n", RegValue); + RegValue |= BIT_IOCMD_RXENABLE(1); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD, RegValue); + LOGD2("[A] REG_RTL_MII_IOCMD: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD)); +} + + +VOID +HalMiiGmacSetDefaultEthIoCmdRtl8195a( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + PHAL_MII_ADAPTER pHalMiiAdapter = pMiiAdapter->pHalMiiAdapter; + u32 RegValue; + + LOGI(ANSI_COLOR_YELLOW"REG_RTL_MII_IOCMD Register(0x%04X) (W: CMD_CONFIG)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD); + LOGD2("[B] REG_RTL_MII_IOCMD: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD, CMD_CONFIG); + LOGD2("[A] REG_RTL_MII_IOCMD: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD)); + + LOGI(ANSI_COLOR_YELLOW"Ethernet_IO_CMD1 Register(0x%04X) (W: CMD1_CONFIG)\n"ANSI_COLOR_RESET, REG_RTL_MII_IOCMD1); + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1); + LOGD2("[B] REG_RTL_MII_IOCMD1: %X\n", RegValue); + HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, CMD1_CONFIG); + LOGD2("[A] REG_RTL_MII_IOCMD1: %X\n", HAL_MII_READ32(REG_RTL_MII_IOCMD1)); + + //2014-04-29 yclin (disable 0x40051438[27] r_en_precise_dma) { + RegValue = HAL_MII_READ32(REG_RTL_MII_IOCMD1); + RegValue = RegValue & 0xF7FFFFFF; + HAL_MII_WRITE32(REG_RTL_MII_IOCMD1, RegValue); + // } +} + + +VOID +HalMiiGmacInitIrqRtl8195a( + IN VOID *Data + ) +{ + IRQ_HANDLE MiiIrqHandle_Master; + PMII_ADAPTER pMiiAdapter = (PMII_ADAPTER) Data; + + + MiiIrqHandle_Master.Data = (u32) (pMiiAdapter); + MiiIrqHandle_Master.IrqNum = GMAC_IRQ; + MiiIrqHandle_Master.IrqFun = (IRQ_FUN) MiiIrqHandle; + MiiIrqHandle_Master.Priority = 0; + InterruptRegister(&MiiIrqHandle_Master); + InterruptEn(&MiiIrqHandle_Master); +} + + +u32 +HalMiiGmacGetInterruptStatusRtl8195a( + VOID + ) +{ + DBG_ENTRANCE; + u32 RegValue; + + RegValue = HAL_MII_READ32(REG_RTL_MII_IMRISR); + LOGD("REG_RTL_MII_IMRISR: %X\n", RegValue); + return RegValue; +} + + +VOID +HalMiiGmacClearInterruptStatusRtl8195a( + u32 IsrStatus + ) +{ + DBG_ENTRANCE; + HAL_MII_WRITE32(REG_RTL_MII_IMRISR, IsrStatus); +} + + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c new file mode 100644 index 0000000..25cf1ec --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c @@ -0,0 +1,1919 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" + +//#include "autoconf.h" +//#include "diag.h" +//#include "hal_peri_on.h" +#include "rtl8195a_nfc.h" +//#include "hal_nfc.h" +//#include "rtl8195a_peri_on.h" +//#include "rtl8195a_sys_on.h" +//#include "hal_platform.h" + +#ifdef CONFIG_NFC_NORMAL + +extern void nfc_tagwrite_callback(PNFC_ADAPTER pNFCAdp, uint32_t page, uint32_t wr_data); +extern void nfc_event_callback(PNFC_ADAPTER pNFCAdp, uint32_t event); +extern void nfc_tagread_callback(PNFC_ADAPTER pNFCAdp, uint32_t page); +extern void nfc_cache_read_callback(PNFC_ADAPTER pNFCAdp, uint32_t start_pg, uint32_t *pbuf); + +extern VOID SpicLoadInitParaFromClockRtl8195A(u8 CpuClkMode, u8 BaudRate, PSPIC_INIT_PARA pSpicInitPara); +extern VOID SpicSectorEraseFlashRtl8195A(u32 Address); +extern VOID SpicWaitBusyDoneRtl8195A(VOID); +extern VOID SpicWaitWipDoneRtl8195A(SPIC_INIT_PARA SpicInitPara); + + +VOID WriteA2NMailbox(IN VOID *pNFCAdapte); +VOID A2NWriteInQueue(IN VOID *pNFCAdapte, IN VOID *pBuff); +VOID A2NWriteDeQueue(IN VOID *pNFCAdapte); +VOID N2AReadTag(IN VOID *pNFCAdapte, IN u8 N2ARPage); +u32 HalNFCRead32(IN u32 Addr); +VOID HalNFCWrite32(IN u32 Addr, IN u32 Data); +VOID N2AWriteTag(IN VOID *pNFCAdapte, IN u8 N2AWPage); +VOID N2AReadCatch(IN VOID *pNFCAdapte, IN u8 N2ACatchRPage); +VOID NFCReaderPresent(IN VOID *pNFCAdapte, IN u8 State); +VOID N2AMailboxState(IN VOID *pNFCAdapte, IN u8 State, IN u8 Seq); +VOID NFC25MClkReq(IN VOID *pNFCAdapte, IN u8 OP, IN u8 Seq); + +//demo +u8 NFCFWIMEM[]={ +0x01, 0x10, 0x00, 0x65, 0x00, 0x68, 0xA8, 0xB9, 0x00, 0x65, 0x40, 0xF0, 0x20, 0x69, 0x89, 0xB9, +0x00, 0x65, 0x13, 0xF7, 0x20, 0x6A, 0x80, 0xF0, 0x08, 0x4A, 0x7E, 0xF0, 0x2D, 0x68, 0x7E, 0xF0, +0x0C, 0x48, 0x00, 0xDA, 0x00, 0x65, 0x00, 0x65, 0x00, 0x65, 0x00, 0x65, 0x13, 0xF7, 0x20, 0x6A, +0x08, 0x4A, 0x00, 0x9A, 0x00, 0x65, 0x00, 0xF0, 0x20, 0x69, 0xFF, 0xF4, 0x1F, 0x49, 0x2C, 0xE8, +0x00, 0xDA, 0x00, 0x65, 0x13, 0xF7, 0x20, 0x6A, 0x80, 0xF0, 0x04, 0x4A, 0x00, 0x68, 0x00, 0xDA, +0x00, 0x65, 0x13, 0xF7, 0x20, 0x6A, 0x80, 0xF0, 0x00, 0x4A, 0x02, 0xF0, 0x20, 0x68, 0xFF, 0x48, +0x00, 0xDA, 0x00, 0x65, 0x10, 0xF0, 0x20, 0x68, 0x00, 0xF4, 0x01, 0x48, 0x00, 0xE8, 0x00, 0x65, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xDF, 0xF7, 0x04, 0x63, 0x0E, 0xD2, 0x0D, 0xD3, 0x0C, 0xD0, 0x0B, 0xD1, 0x0A, 0xD4, 0x09, 0xD5, +0x08, 0xD6, 0x07, 0xD7, 0x58, 0x67, 0x06, 0xD2, 0x05, 0x62, 0x8D, 0xB8, 0xAE, 0xB8, 0xC8, 0xB8, +0xEC, 0xB8, 0x00, 0x18, 0x28, 0x01, 0x00, 0x65, 0x00, 0x6A, 0xAA, 0xB9, 0x06, 0x92, 0x1A, 0x65, +0x05, 0x92, 0xFA, 0x65, 0x0E, 0x92, 0x0D, 0x93, 0x0C, 0x90, 0x0B, 0x91, 0x0A, 0x94, 0x09, 0x95, +0x08, 0x96, 0x07, 0x97, 0x20, 0xF0, 0x1C, 0x63, 0x00, 0xBA, 0x00, 0x65, 0x00, 0xF0, 0x20, 0x6A, +0xFF, 0x4A, 0x60, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x13, 0xF7, 0x20, 0x6A, 0x50, 0x4A, 0x00, 0x9A, 0x00, 0x65, 0x10, 0xF0, 0x20, 0x69, 0xFF, 0x49, +0x2C, 0xE8, 0x00, 0xDA, 0x00, 0x65, 0x34, 0xB8, 0x00, 0xF4, 0x00, 0x68, 0x2D, 0xE8, 0x90, 0xB9, +0x00, 0x65, 0x00, 0x65, 0x00, 0x65, 0x14, 0xB8, 0x00, 0xF4, 0x00, 0x69, 0x2C, 0xE8, 0xFB, 0x20, +0x00, 0x65, 0x10, 0xF0, 0x20, 0x68, 0x20, 0xF4, 0x1F, 0x48, 0x00, 0xE8, 0x00, 0x65, 0x10, 0xF0, +0x30, 0x6A, 0xC0, 0xF2, 0x00, 0x4A, 0x10, 0xF0, 0x30, 0x6B, 0xA0, 0xF6, 0x08, 0x4B, 0x00, 0x68, +0x00, 0xDA, 0x01, 0xDA, 0x02, 0xDA, 0x03, 0xDA, 0x10, 0x4A, 0x63, 0xEA, 0xF9, 0x61, 0x00, 0x65, +0x10, 0xF0, 0x30, 0x68, 0xE0, 0xF2, 0x00, 0x48, 0xB8, 0x65, 0x00, 0x65, 0x10, 0xF0, 0x30, 0x68, +0xE0, 0xF5, 0x08, 0x48, 0xB8, 0x65, 0x00, 0x68, 0xA8, 0xB9, 0x0C, 0xB8, 0x00, 0x65, 0x1F, 0xF7, +0x01, 0x69, 0x2D, 0xE8, 0xDF, 0xF7, 0x20, 0x69, 0xFF, 0x49, 0x2C, 0xE8, 0x88, 0xB9, 0x00, 0x65, +0x10, 0xF0, 0x20, 0x68, 0x40, 0xF5, 0x05, 0x48, 0x00, 0xE8, 0x00, 0x65, 0x20, 0xBA, 0x00, 0x65, +0xF9, 0x63, 0x0D, 0x62, 0x0C, 0xD1, 0x04, 0x01, 0x8A, 0xD9, 0xAB, 0xD9, 0xCC, 0xD9, 0xED, 0xD9, +0x6A, 0x99, 0x1F, 0xF7, 0x00, 0x6A, 0x6C, 0xEA, 0x43, 0x32, 0x45, 0xD9, 0x6D, 0x99, 0x1F, 0xF7, +0x00, 0x6A, 0x6C, 0xEA, 0x43, 0x32, 0x44, 0xD9, 0x6A, 0x99, 0x7C, 0x6A, 0x6C, 0xEA, 0x4B, 0x32, +0x43, 0xD9, 0x64, 0x99, 0x45, 0x99, 0x83, 0x67, 0xA2, 0x67, 0x00, 0x18, 0x3A, 0x01, 0xB9, 0x65, +0x09, 0x97, 0x08, 0x91, 0x05, 0x63, 0x00, 0xEF, 0xFB, 0x63, 0x09, 0x62, 0x08, 0xD1, 0x04, 0x01, +0x86, 0xD9, 0xA7, 0xD9, 0x66, 0x99, 0x47, 0x99, 0x6C, 0xEA, 0x43, 0xD9, 0x63, 0x99, 0x80, 0x6A, +0x6C, 0xEA, 0x02, 0x22, 0x00, 0x18, 0xCD, 0x02, 0x63, 0x99, 0x40, 0x6A, 0x6C, 0xEA, 0x02, 0x22, +0x00, 0x18, 0x99, 0x03, 0x63, 0x99, 0x20, 0x6A, 0x6C, 0xEA, 0x02, 0x22, 0x00, 0x18, 0xA0, 0x03, +0x63, 0x99, 0x10, 0x6A, 0x6C, 0xEA, 0x02, 0x22, 0x00, 0x18, 0x06, 0x03, 0x63, 0x99, 0x08, 0x6A, +0x6C, 0xEA, 0x02, 0x22, 0x00, 0x18, 0x21, 0x04, 0xB9, 0x65, 0x05, 0x97, 0x04, 0x91, 0x03, 0x63, +0x00, 0xEF, 0x00, 0x65, 0xFB, 0x63, 0x09, 0x62, 0x08, 0xD1, 0x04, 0x01, 0x20, 0x6A, 0x49, 0xC1, +0x00, 0x6A, 0x48, 0xC1, 0x60, 0x6C, 0x00, 0x18, 0x73, 0x02, 0x00, 0x18, 0x7E, 0x01, 0x00, 0x18, +0x6E, 0x01, 0x00, 0x18, 0x92, 0x01, 0x00, 0x18, 0x5E, 0x01, 0x00, 0x18, 0x66, 0x01, 0x0A, 0x6C, +0x00, 0x18, 0xFD, 0x06, 0xFF, 0x17, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x48, 0x9A, 0x00, 0x6B, 0x60, 0xCA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x4C, 0x9A, 0x00, 0xF4, 0x00, 0x6B, 0x60, 0xDA, +0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x01, 0x6B, 0x00, 0xF6, 0x64, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x50, 0x9A, 0x04, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x54, 0x9A, 0x04, 0x6B, 0x60, 0xDA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x58, 0x9A, 0x01, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x5C, 0x9A, 0x07, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x40, 0x9A, 0xFF, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x44, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, +0x60, 0x33, 0xC0, 0xF0, 0x6C, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF0, 0x50, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x7E, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x62, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x54, 0x9A, 0x40, 0x9A, 0x42, 0xD9, +0x62, 0x99, 0x00, 0xF2, 0x01, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF0, 0x54, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, 0x00, 0xF4, 0x03, 0x6A, +0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, +0x54, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, 0x00, 0xF4, 0x02, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x54, 0x9A, 0x62, 0x99, 0x60, 0xDA, +0x00, 0x6A, 0x41, 0xD9, 0x1D, 0x10, 0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, +0x40, 0xD9, 0x40, 0x99, 0x64, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF0, 0x58, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0xA0, 0x6A, 0x6C, 0xEA, +0x42, 0xD9, 0x42, 0x99, 0xA0, 0x6B, 0x6E, 0xEA, 0x08, 0x22, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, +0x41, 0x99, 0x0A, 0x5A, 0x58, 0x67, 0xDF, 0x2A, 0x01, 0x10, 0x00, 0x65, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x48, 0x9A, 0x01, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x5C, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0xE0, 0xF0, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, +0x44, 0x9A, 0x24, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, +0x48, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x01, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x62, 0x99, +0x0A, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, +0x48, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, +0x4C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF0, 0x50, 0x9A, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF0, 0x4C, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF0, 0x54, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF0, 0x58, 0x9A, 0x6C, 0xEA, 0x42, 0xD9, 0x62, 0x99, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, 0x5C, 0x9A, 0x6D, 0xEA, 0x42, 0xD9, 0x62, 0x99, 0x04, 0xF0, +0x01, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF0, 0x54, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF0, 0x54, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF1, 0x40, 0x9A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF0, 0x54, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x80, 0xF7, 0x00, 0x6A, +0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, +0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, 0x01, 0xF0, 0x00, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, +0x01, 0xF0, 0x01, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF1, 0x44, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x09, 0x6A, 0x6D, 0xEA, +0x42, 0xD9, 0x62, 0x99, 0x03, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x44, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, 0x10, 0x6A, +0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x44, 0x9A, +0x62, 0x99, 0x60, 0xDA, 0x62, 0x99, 0x11, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x44, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x48, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, +0xFF, 0x6A, 0x01, 0x4A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0xF1, 0x48, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF0, 0x50, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x7F, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, +0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x62, 0x99, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x40, 0x9A, +0x42, 0xD9, 0x62, 0x99, 0x10, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF0, 0x50, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF1, 0x4C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x07, 0x6A, 0x6D, 0xEA, +0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x4C, 0x9A, 0x62, 0x99, +0x60, 0xDA, 0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFC, 0x63, 0x07, 0xD1, +0x3D, 0x67, 0x44, 0x67, 0x20, 0xF0, 0x40, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0xF1, 0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x82, 0x6A, 0x44, 0xD9, 0x64, 0x99, 0xFF, 0x6A, +0x6C, 0xEA, 0x42, 0xD9, 0x64, 0x99, 0x1F, 0xF7, 0x00, 0x6A, 0x6C, 0xEA, 0x42, 0x32, 0x43, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x54, 0x9A, 0x80, 0x6B, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x58, 0x9A, 0x62, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x50, 0x9A, 0x63, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x54, 0x9A, 0x03, 0x6B, 0x60, 0xDA, +0xB9, 0x65, 0x07, 0x91, 0x04, 0x63, 0x20, 0xE8, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x00, 0x6A, +0x41, 0xC1, 0x26, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x5C, 0x9A, +0x40, 0x9A, 0x62, 0x67, 0x01, 0x6A, 0x4C, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0x0C, 0x22, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x58, 0x9A, 0x40, 0x9A, 0x00, 0xF6, 0x40, 0x32, +0x00, 0xF6, 0x43, 0x32, 0x13, 0x10, 0x00, 0x6A, 0x40, 0xC1, 0x03, 0x10, 0x40, 0xA1, 0x01, 0x4A, +0x40, 0xC1, 0x40, 0xA1, 0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x41, 0xA1, 0x01, 0x4A, 0x41, 0xC1, +0x41, 0xA1, 0x64, 0x5A, 0x58, 0x67, 0xD6, 0x2A, 0x01, 0x6A, 0x4B, 0xEA, 0xB9, 0x65, 0x03, 0x91, +0x02, 0x63, 0x20, 0xE8, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x44, 0x67, 0x50, 0xC1, 0x00, 0x6A, +0x41, 0xC9, 0x2C, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF1, 0x5C, 0x9A, +0x60, 0x9A, 0x20, 0x6A, 0x6C, 0xEA, 0x14, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0xF1, 0x58, 0x9A, 0x70, 0xA1, 0x60, 0xDA, 0x00, 0x6A, 0x40, 0xC9, 0x03, 0x10, 0x40, 0xA9, +0x01, 0x4A, 0x40, 0xC9, 0x40, 0x89, 0x00, 0x52, 0x58, 0x67, 0xF9, 0x22, 0x00, 0x6A, 0x15, 0x10, +0x00, 0x6A, 0x40, 0xC9, 0x03, 0x10, 0x40, 0xA9, 0x01, 0x4A, 0x40, 0xC9, 0x40, 0xA9, 0xE0, 0xF3, +0x08, 0x5A, 0x58, 0x67, 0xF8, 0x2A, 0x41, 0xA9, 0x01, 0x4A, 0x41, 0xC9, 0x41, 0xA9, 0x02, 0xF0, +0x00, 0x5A, 0x58, 0x67, 0xCF, 0x2A, 0x01, 0x6A, 0x4B, 0xEA, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, +0x20, 0xE8, 0x00, 0x65, 0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF1, 0x40, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x42, 0x99, 0x01, 0x6B, 0x6E, 0xEA, +0x20, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x50, 0x9A, 0x04, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x44, 0x9A, 0x40, 0x9A, +0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x01, 0x6B, 0x00, 0xF6, +0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x50, 0x9A, 0x03, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x48, 0x9A, 0x40, 0x9A, +0x42, 0xD9, 0x42, 0x99, 0x01, 0x6B, 0x6E, 0xEA, 0x33, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF1, 0x4C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF1, 0x50, 0x9A, 0x40, 0x9A, 0x40, 0xD9, 0x60, 0x99, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x54, 0x9A, 0x6D, 0xEA, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x50, 0x9A, 0x60, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF6, 0x5C, 0x9A, 0x2E, 0xF5, 0x10, 0x5A, 0x58, 0x67, 0x08, 0x22, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x54, 0x9A, 0x03, 0x6B, 0x60, 0xDA, +0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, 0x20, 0xE8, 0xF8, 0x63, 0x0F, 0x62, 0x0E, 0xD1, 0x04, 0x01, +0x00, 0xF3, 0x00, 0x6A, 0x49, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, +0x00, 0x4A, 0x40, 0xD9, 0x47, 0x41, 0x1D, 0x4A, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xA0, 0xF0, 0x4C, 0x9A, 0x40, 0x9A, 0x47, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xA0, 0xF0, 0x4C, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF1, 0x58, 0x9A, 0x40, 0x9A, 0x48, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF1, 0x5C, 0x9A, 0x40, 0x9A, 0x46, 0xD9, 0x66, 0x99, 0x80, 0x6A, 0x6D, 0xEA, +0x46, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x5C, 0x9A, 0x66, 0x99, +0x60, 0xDA, 0x68, 0x99, 0x01, 0xF0, 0x00, 0x6A, 0x6C, 0xEA, 0x1B, 0x22, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x40, 0x9A, 0x88, 0x99, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, +0x60, 0x33, 0x00, 0xF1, 0x60, 0x9B, 0x6D, 0xE4, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF1, 0x44, 0x9A, 0x40, 0x9A, 0x46, 0xD9, 0x66, 0x99, 0x0F, 0x6A, 0x6C, 0xEA, +0x44, 0xD9, 0x68, 0x99, 0x00, 0xF4, 0x00, 0x6A, 0x6C, 0xEA, 0x80, 0xF0, 0x1F, 0x22, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x44, 0x9A, 0x40, 0x9A, 0x46, 0xD9, 0x66, 0x99, +0x40, 0x6A, 0x6C, 0xEA, 0x80, 0xF0, 0x0D, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF1, 0x48, 0x9A, 0x40, 0x9A, 0x49, 0xD9, 0x42, 0x99, 0x60, 0xA2, 0x20, 0x6A, 0x4B, 0xEA, +0x6C, 0xEA, 0x46, 0xC1, 0x46, 0xA1, 0x56, 0x32, 0x46, 0xC1, 0x42, 0x99, 0x02, 0x4A, 0x40, 0xA2, +0x45, 0xC1, 0x46, 0xA1, 0x7B, 0x22, 0x42, 0x99, 0x02, 0x4A, 0x40, 0xA2, 0x45, 0xC1, 0x42, 0x99, +0x40, 0xA2, 0x62, 0x67, 0x1F, 0x6A, 0x6C, 0xEA, 0x02, 0x6B, 0x4E, 0xEB, 0x2F, 0x23, 0x03, 0x52, +0x78, 0x67, 0x02, 0x23, 0x20, 0x22, 0x6A, 0x10, 0x03, 0x6B, 0x4E, 0xEB, 0x31, 0x23, 0x04, 0x6B, +0x6E, 0xEA, 0x64, 0x2A, 0x42, 0x99, 0x02, 0x4A, 0x40, 0xA2, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, 0x80, 0xF6, 0x64, 0xC2, 0x42, 0x99, 0x02, 0x4A, 0x40, 0xA2, +0x01, 0x6B, 0x6E, 0xEA, 0x50, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x01, 0x6B, +0x80, 0xF6, 0x64, 0xC2, 0x4B, 0x10, 0x42, 0x99, 0x01, 0x4A, 0x40, 0xA2, 0x62, 0x67, 0x45, 0xA1, +0x00, 0x6C, 0xA3, 0x67, 0xC2, 0x67, 0x00, 0x18, 0x5B, 0x0E, 0x40, 0x10, 0x42, 0x99, 0x01, 0x4A, +0x40, 0xA2, 0x62, 0x67, 0x45, 0xA1, 0x02, 0x6C, 0xA3, 0x67, 0xC2, 0x67, 0x00, 0x18, 0x5B, 0x0E, +0x46, 0xA1, 0x05, 0x6B, 0x6E, 0xEA, 0x1C, 0x2A, 0x45, 0xA1, 0x26, 0x5A, 0x58, 0x67, 0x2D, 0x22, +0x00, 0x6A, 0x45, 0xD9, 0x11, 0x10, 0x65, 0xA1, 0x45, 0x99, 0x49, 0xE3, 0x48, 0x32, 0x60, 0x99, +0x49, 0xE3, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF1, 0x68, 0x9B, 0x60, 0x9B, +0x60, 0xDA, 0x45, 0x99, 0x01, 0x4A, 0x45, 0xD9, 0x45, 0x99, 0x04, 0x5A, 0x58, 0x67, 0xEB, 0x2A, +0x46, 0xA1, 0x02, 0x6B, 0x6E, 0xEA, 0x12, 0x2A, 0x45, 0xA1, 0x48, 0x32, 0x60, 0x99, 0x49, 0xE3, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF1, 0x68, 0x9B, 0x60, 0x9B, 0x60, 0xDA, +0x05, 0x10, 0x00, 0x65, 0x03, 0x10, 0x00, 0x65, 0x01, 0x10, 0x00, 0x65, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF1, 0x58, 0x9A, 0x68, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x40, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x20, 0xF1, 0x78, 0x9B, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xA0, 0xF0, 0x4C, 0x9A, 0x67, 0x99, 0x60, 0xDA, 0xB9, 0x65, 0x0B, 0x97, 0x0A, 0x91, 0x06, 0x63, +0x00, 0xEF, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF1, 0x4C, 0x9A, 0x40, 0x9A, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, +0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, +0x50, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x02, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x42, 0xD9, +0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, +0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x50, 0x9A, +0x62, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x54, 0x9A, +0x40, 0x9A, 0x40, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, +0x41, 0x99, 0x05, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF1, 0x54, 0x9A, 0x80, 0x99, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF1, +0x78, 0x9B, 0x8C, 0xEB, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, +0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF1, 0x5C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, +0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x40, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x02, 0x6B, 0x64, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x48, 0x9A, 0x00, 0x6B, 0x60, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x54, 0xA2, 0x61, 0x42, 0xFF, 0x6A, +0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x74, 0xC2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x5C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, +0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x32, 0x5A, +0x58, 0x67, 0xF9, 0x2A, 0x62, 0x99, 0x01, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x5C, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, +0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, 0x48, 0x9A, 0x40, 0x9A, 0x42, 0xD9, +0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, +0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF0, 0x48, 0x9A, +0x82, 0x99, 0x02, 0x6B, 0x6B, 0xEB, 0x8C, 0xEB, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, +0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x50, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x00, 0x6A, +0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x05, 0x5A, 0x58, 0x67, +0xF9, 0x2A, 0x62, 0x99, 0x01, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF1, 0x50, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, +0x20, 0xE8, 0x00, 0x65, 0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x00, 0x6A, 0x4E, 0xC1, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x44, 0x9A, 0x40, 0xAA, 0x46, 0xC9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x44, 0x9A, 0x00, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x48, 0x9A, 0x40, 0xAA, 0x45, 0xC9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x48, 0x9A, 0x65, 0xA9, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x50, 0xA2, 0x61, 0x42, 0xFF, 0x6A, 0x4C, 0xEB, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x70, 0xC2, 0x65, 0xA9, +0x08, 0x6A, 0x6C, 0xEA, 0x43, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, +0x04, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, +0x4C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x52, 0xA2, 0x61, 0x42, 0xFF, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x72, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF0, 0x48, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x65, 0xA9, 0x01, 0x6A, +0x4C, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0xE0, 0xF0, 0x0B, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x51, 0xA2, 0x61, 0x42, 0xFF, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x71, 0xC2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x54, 0x9A, 0x60, 0xAA, 0xFF, 0xF7, 0x1F, 0x6A, 0x4C, 0xEB, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x63, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x43, 0xAA, 0x62, 0x67, 0x1F, 0xF7, +0x00, 0x6A, 0x6C, 0xEA, 0x27, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x43, 0xAA, 0x42, 0x33, 0xFF, 0xF7, 0x1F, 0x6A, 0x4C, 0xEB, 0xFF, 0x6A, 0x4C, 0xEB, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x69, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x43, 0xAA, 0xFF, 0x6B, 0x6C, 0xEA, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x20, 0xF6, 0x04, 0x4B, 0x43, 0xCB, 0x01, 0x6A, +0x4E, 0xC1, 0x0A, 0x10, 0x00, 0x6A, 0x4E, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF6, 0x04, 0x4A, 0x00, 0x6B, 0x69, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF6, 0x04, 0x4A, 0x43, 0xAA, 0x0B, 0x5A, 0x58, 0x67, 0x3F, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x4C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF1, 0x40, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x20, 0xF6, 0x04, 0x4B, 0x63, 0xAB, 0x83, 0x67, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x60, 0xF1, 0x78, 0x9B, 0x6D, 0xE4, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF6, 0x04, 0x4A, 0x00, 0x6B, 0x63, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x01, 0x6B, 0x6B, 0xEB, 0x40, 0xF6, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xA0, 0xF0, 0x48, 0x9A, 0x01, 0x6B, 0x60, 0xCA, 0x43, 0x10, 0x00, 0x6A, 0x4F, 0xC1, 0x0F, 0x10, +0x4F, 0xA1, 0x48, 0x32, 0x61, 0x99, 0x49, 0xE3, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x60, 0xF1, 0x7C, 0x9B, 0x60, 0x9B, 0x60, 0xDA, 0x4F, 0xA1, 0x01, 0x4A, 0x4F, 0xC1, 0x8F, 0xA1, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x43, 0xAA, 0x62, 0x67, +0x4E, 0xA1, 0x49, 0xE3, 0x03, 0x4A, 0x00, 0x52, 0x78, 0x67, 0x01, 0x23, 0x03, 0x4A, 0x4B, 0x32, +0x42, 0xEC, 0x58, 0x67, 0xDD, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, +0x60, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x73, 0xC2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x4C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x03, 0x6B, 0x20, 0xF6, 0x64, 0xDA, +0x65, 0xA9, 0x02, 0x6A, 0x6C, 0xEA, 0x11, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF6, 0x04, 0x4A, 0x4C, 0xA2, 0x61, 0x42, 0xFF, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x6C, 0xC2, 0x65, 0xA9, 0x04, 0x6A, 0x6C, 0xEA, +0x11, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x4D, 0xA2, +0x61, 0x42, 0xFF, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x6D, 0xC2, 0x65, 0xA9, 0x00, 0xF2, 0x00, 0x6A, 0x6C, 0xEA, 0x11, 0x22, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x4F, 0xA2, 0x61, 0x42, 0xFF, 0x6A, +0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x6F, 0xC2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF1, 0x44, 0x9A, 0x66, 0xA9, 0x60, 0xCA, +0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, 0x20, 0xE8, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x82, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF1, 0x40, 0x9A, 0x62, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x50, 0x9A, 0x03, 0x6B, 0x60, 0xDA, +0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x82, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF1, 0x44, 0x9A, 0x62, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x54, 0x9A, 0x03, 0x6B, 0x60, 0xDA, +0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x50, 0x9A, 0x04, 0x6B, 0x60, 0xDA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x54, 0x9A, 0x04, 0x6B, 0x60, 0xDA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x84, 0xD9, +0xA5, 0xD9, 0xC6, 0xD9, 0x64, 0x99, 0x46, 0x99, 0x49, 0xE3, 0x40, 0xD9, 0x0A, 0x10, 0x44, 0x99, +0x65, 0x99, 0x60, 0xA3, 0x60, 0xC2, 0x44, 0x99, 0x01, 0x4A, 0x44, 0xD9, 0x45, 0x99, 0x01, 0x4A, +0x45, 0xD9, 0x65, 0x99, 0x40, 0x99, 0x43, 0xEB, 0x58, 0x67, 0xF1, 0x2A, 0x44, 0x99, 0xB9, 0x65, +0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x84, 0xD9, +0xA5, 0xD9, 0x64, 0x99, 0x45, 0x99, 0x49, 0xE3, 0x40, 0xD9, 0x06, 0x10, 0x44, 0x99, 0x00, 0x6B, +0x60, 0xC2, 0x44, 0x99, 0x01, 0x4A, 0x44, 0xD9, 0x64, 0x99, 0x40, 0x99, 0x43, 0xEB, 0x58, 0x67, +0xF5, 0x2A, 0x44, 0x99, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0xFE, 0x63, 0x03, 0xD1, +0x3D, 0x67, 0x84, 0xD9, 0xA5, 0xD9, 0xC6, 0xD9, 0x00, 0x6A, 0x40, 0xD9, 0x0F, 0x10, 0x64, 0x99, +0x40, 0x99, 0x49, 0xE3, 0x60, 0xA2, 0x85, 0x99, 0x40, 0x99, 0x49, 0xE4, 0x40, 0xA2, 0x6E, 0xEA, +0x02, 0x22, 0x00, 0x6A, 0x09, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x60, 0x99, 0x46, 0x99, +0x43, 0xEB, 0x58, 0x67, 0xEC, 0x2A, 0x01, 0x6A, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, +0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x44, 0x67, 0xA3, 0xD9, 0x48, 0xC1, 0x43, 0x99, 0x60, 0xAA, +0xFF, 0x6A, 0x4C, 0xEB, 0x48, 0xA1, 0x6E, 0xEA, 0x48, 0xC1, 0x48, 0xA1, 0x50, 0x32, 0x00, 0xF6, +0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, 0x48, 0x81, 0x6E, 0xEA, 0x00, 0xF6, 0x40, 0x32, 0x00, 0xF6, +0x43, 0x32, 0x48, 0xC1, 0x43, 0x99, 0x40, 0xAA, 0x42, 0x33, 0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, +0x40, 0x33, 0x60, 0x33, 0x63, 0x33, 0x63, 0x33, 0x48, 0xA1, 0x40, 0x32, 0x40, 0x32, 0x40, 0x32, +0x43, 0x32, 0x43, 0x32, 0x6E, 0xEA, 0x40, 0x33, 0x60, 0x33, 0x63, 0x33, 0x63, 0x33, 0x48, 0xA1, +0x4C, 0x32, 0x40, 0x32, 0x40, 0x32, 0x43, 0x32, 0x43, 0x32, 0x6E, 0xEA, 0x40, 0x33, 0x60, 0x33, +0x63, 0x33, 0x63, 0x33, 0x48, 0xA1, 0x52, 0x34, 0xFF, 0x6A, 0x8C, 0xEA, 0x6E, 0xEA, 0x40, 0x33, +0x60, 0x33, 0x63, 0x33, 0x63, 0x33, 0xFF, 0xF7, 0x1F, 0x6A, 0x4C, 0xEB, 0x43, 0x99, 0x60, 0xCA, +0x43, 0x99, 0x40, 0xAA, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0xFC, 0x63, 0x07, 0x62, +0x06, 0xD1, 0x04, 0x01, 0x84, 0xD9, 0xA5, 0xD9, 0xC6, 0xD9, 0xE7, 0xD9, 0x44, 0x99, 0x01, 0x6B, +0x4E, 0xEB, 0x04, 0x23, 0x02, 0x6B, 0x6E, 0xEA, 0x05, 0x22, 0x32, 0x10, 0x6C, 0xF3, 0x03, 0x6A, +0x41, 0xC9, 0x04, 0x10, 0x01, 0x6A, 0x4B, 0xEA, 0x41, 0xC9, 0x00, 0x65, 0x45, 0x99, 0x40, 0x82, +0x40, 0xC1, 0x45, 0x99, 0x01, 0x4A, 0x45, 0xD9, 0x60, 0xA1, 0x42, 0x41, 0x83, 0x67, 0xA2, 0x67, +0x00, 0x18, 0x58, 0x05, 0x46, 0x99, 0xFF, 0x4A, 0x46, 0xD9, 0x46, 0x99, 0xEF, 0x2A, 0x44, 0x99, +0x02, 0x6B, 0x6E, 0xEA, 0x06, 0x2A, 0x41, 0xA9, 0x4F, 0xEB, 0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, +0x41, 0xC9, 0x61, 0xA9, 0xFF, 0x6A, 0x4C, 0xEB, 0x47, 0x99, 0x60, 0xC2, 0x41, 0xA9, 0x42, 0x33, +0xFF, 0xF7, 0x1F, 0x6A, 0x4C, 0xEB, 0xFF, 0x6A, 0x4C, 0xEB, 0x48, 0x99, 0x60, 0xC2, 0x00, 0x65, +0xB9, 0x65, 0x03, 0x97, 0x02, 0x91, 0x02, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFB, 0x63, 0x09, 0x62, +0x08, 0xD1, 0x06, 0x01, 0x84, 0xD9, 0x65, 0x67, 0x46, 0x67, 0x74, 0xC1, 0x58, 0xC1, 0x58, 0xA1, +0x01, 0x6B, 0x6E, 0xEA, 0x0B, 0x2A, 0xA4, 0x99, 0x74, 0xA1, 0x43, 0x41, 0x84, 0x41, 0x04, 0xD4, +0x01, 0x6C, 0xC3, 0x67, 0xE2, 0x67, 0x00, 0x18, 0x7F, 0x05, 0x0A, 0x10, 0xA4, 0x99, 0x74, 0xA1, +0x43, 0x41, 0x84, 0x41, 0x04, 0xD4, 0x02, 0x6C, 0xC3, 0x67, 0xE2, 0x67, 0x00, 0x18, 0x7F, 0x05, +0x43, 0xA1, 0x40, 0xC9, 0x40, 0xA9, 0x40, 0x32, 0x40, 0xC9, 0x44, 0xA1, 0x62, 0x67, 0x40, 0xA9, +0x6D, 0xEA, 0x40, 0xC9, 0x40, 0xA9, 0xB9, 0x65, 0x03, 0x97, 0x02, 0x91, 0x02, 0x63, 0x00, 0xEF, +0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, 0x20, 0xE8, 0x00, 0x65, +0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, +0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, +0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x86, 0xD9, 0x46, 0x99, 0x40, 0xD9, 0x40, 0x99, 0x77, 0xF0, +0x24, 0x6B, 0x4D, 0xE3, 0x40, 0x9B, 0x62, 0xD9, 0x41, 0xD9, 0x41, 0x99, 0xB9, 0x65, 0x05, 0x91, +0x03, 0x63, 0x20, 0xE8, 0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x86, 0xD9, 0xA7, 0xD9, 0x46, 0x99, +0x40, 0xD9, 0x47, 0x99, 0x41, 0xD9, 0x41, 0x99, 0x60, 0x99, 0x77, 0xF0, 0x24, 0x6A, 0x69, 0xE2, +0x40, 0xDA, 0x42, 0xD9, 0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, 0x20, 0xE8, 0xFF, 0x63, 0x01, 0xD1, +0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x44, 0x9A, 0x61, 0x42, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF1, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0xC0, 0xF2, 0x64, 0x9B, 0x60, 0xDA, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, +0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, +0x0C, 0x4A, 0x02, 0x6B, 0x64, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, +0x0C, 0x4A, 0x00, 0x6B, 0x61, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, +0x4C, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, +0x50, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, +0x54, 0x9A, 0xA7, 0xF7, 0x07, 0x6B, 0x6B, 0xEB, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x60, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x0C, 0x4A, 0x6C, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, +0x00, 0x4A, 0x61, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x6D, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x62, 0xA2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6E, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x63, 0xA2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6F, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x64, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x0C, 0x4A, 0x70, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, +0x00, 0x4A, 0x65, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x71, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x66, 0xA2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x72, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x67, 0xA2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x73, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x68, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x0C, 0x4A, 0x74, 0xC2, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0x00, 0x65, +0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, +0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, +0x5C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, +0x58, 0x9A, 0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF1, 0x54, 0x9A, 0x87, 0xF7, 0x07, 0x6B, 0x6B, 0xEB, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, +0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x4C, 0x9A, 0x02, 0x6B, +0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x44, 0x6B, +0x60, 0xDA, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, +0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, +0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, 0x01, 0x6B, +0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, +0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x54, 0x9A, +0x87, 0xF7, 0x07, 0x6B, 0x6B, 0xEB, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x4C, 0x9A, 0x01, 0x6B, 0x60, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0xB9, 0x65, +0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x54, 0x9A, 0x87, 0xF7, 0x07, 0x6B, +0x6B, 0xEB, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, +0x00, 0xF4, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, +0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x4C, 0x9A, 0x01, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, +0x20, 0xE8, 0x00, 0x65, 0xF8, 0x63, 0x0F, 0x62, 0x0E, 0xD1, 0x04, 0x01, 0x44, 0x67, 0x20, 0xF0, +0x50, 0xC1, 0x00, 0x6A, 0x5C, 0xC1, 0x00, 0x6A, 0x43, 0xD9, 0x00, 0x6A, 0x42, 0xD9, 0x00, 0x6A, +0x44, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x40, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x54, 0x9A, 0x40, 0x9A, 0x46, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0xE0, 0xF1, 0x78, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xE0, 0xF1, +0x7C, 0x9B, 0x60, 0xDA, 0x00, 0x6A, 0x51, 0xC9, 0x03, 0x10, 0x51, 0xA9, 0x01, 0x4A, 0x51, 0xC9, +0x51, 0xA9, 0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x00, 0x18, 0xEC, 0x05, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x40, 0x9A, 0xFF, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x01, 0x6B, 0x60, 0xCA, 0x01, 0x10, 0x00, 0x65, +0x00, 0x18, 0x78, 0x08, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, +0x40, 0x9A, 0x45, 0xD9, 0x45, 0x99, 0x22, 0xF2, 0x14, 0x6B, 0x6E, 0xEA, 0x01, 0x5A, 0x58, 0x67, +0x62, 0x67, 0xFF, 0x6A, 0x4C, 0xEB, 0x45, 0x99, 0x22, 0xF2, 0x15, 0x6C, 0x8E, 0xEA, 0x01, 0x5A, +0x58, 0x67, 0x82, 0x67, 0xFF, 0x6A, 0x8C, 0xEA, 0x4D, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0x62, 0x67, +0x45, 0x99, 0x22, 0xF2, 0x16, 0x6C, 0x8E, 0xEA, 0x01, 0x5A, 0x58, 0x67, 0x6D, 0xEA, 0x2D, 0x22, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x4C, 0x9A, 0x40, 0x9A, 0x44, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x4C, 0x9A, 0x64, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x50, 0x9A, 0x80, 0xF4, 0x00, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x54, 0x9A, 0x40, 0x9A, +0x44, 0xD9, 0x64, 0x99, 0x80, 0x6A, 0x6D, 0xEA, 0x44, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF2, 0x54, 0x9A, 0x64, 0x99, 0x60, 0xDA, 0x45, 0x99, 0x02, 0xF1, 0x11, 0x6B, +0x6E, 0xEA, 0x12, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF1, 0x0C, 0x4A, +0x82, 0x67, 0x00, 0x18, 0x71, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x48, 0x9A, 0x24, 0xF2, 0x02, 0x6B, 0x60, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x12, 0x6C, 0x8E, 0xEA, +0x39, 0x2A, 0x00, 0x6A, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x4D, 0xA2, 0x45, 0xD9, 0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x53, 0xA2, 0x65, 0x99, 0x6D, 0xEA, 0x45, 0xD9, +0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x43, 0xAA, 0x65, 0x99, 0x6D, 0xEA, 0x45, 0xD9, 0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x54, 0xA2, 0x65, 0x99, +0x6D, 0xEA, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, +0x65, 0x99, 0x60, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x13, 0x6B, 0x6E, 0xEA, 0x0C, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x60, 0x99, 0x60, 0x9B, 0x60, 0xDA, +0x40, 0x99, 0x04, 0x4A, 0x40, 0xD9, 0x45, 0x99, 0x26, 0xF3, 0x14, 0x6C, 0x8E, 0xEA, 0x39, 0x2A, +0x00, 0x6A, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, +0x51, 0xA2, 0x45, 0xD9, 0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x52, 0xA2, 0x65, 0x99, 0x6D, 0xEA, 0x45, 0xD9, 0x45, 0x99, +0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, +0x50, 0xA2, 0x65, 0x99, 0x6D, 0xEA, 0x45, 0xD9, 0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x4C, 0xA2, 0x65, 0x99, 0x6D, 0xEA, +0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x65, 0x99, +0x60, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x15, 0x6B, 0x6E, 0xEA, 0x0D, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x80, 0xF6, 0x6C, 0x9B, 0x60, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x16, 0x6C, 0x8E, 0xEA, 0x0D, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0xA0, 0xF6, 0x64, 0x9B, 0x60, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x17, 0x6B, +0x6E, 0xEA, 0x0D, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF6, 0x68, 0x9B, 0x60, 0xDA, 0x45, 0x99, +0x26, 0xF3, 0x18, 0x6C, 0x8E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x01, 0x6B, 0xC0, 0xF2, 0x68, 0xDA, 0x45, 0x99, 0x26, 0xF3, 0x19, 0x6B, 0x6E, 0xEA, 0x07, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x02, 0x6B, 0xC0, 0xF2, 0x68, 0xDA, 0x45, 0x99, +0x46, 0xF3, 0x00, 0x6C, 0x8E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0x6B, 0xC0, 0xF2, 0x68, 0xDA, 0x45, 0x99, 0x46, 0xF3, 0x01, 0x6B, 0x6E, 0xEA, 0x0D, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x68, 0x9B, 0x60, 0xDA, 0x45, 0x99, 0x22, 0xF2, 0x14, 0x6C, +0x8E, 0xEA, 0x2E, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, +0xAA, 0xF5, 0x0A, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x00, 0xF2, 0x78, 0x9B, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x5C, 0x9A, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0x20, 0xF2, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF2, 0x44, 0x9A, 0x00, 0xF3, 0x00, 0x6B, 0x60, 0xDA, 0x00, 0x18, 0x27, 0x01, +0x45, 0x99, 0x22, 0xF2, 0x15, 0x6B, 0x6E, 0xEA, 0x37, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x93, 0xF1, 0x19, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF2, 0x48, 0x9A, 0xAF, 0xF3, 0x1C, 0x6B, 0x6B, 0xEB, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x4C, 0x9A, 0x00, 0x6B, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x5C, 0x9A, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0x20, 0xF2, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF2, 0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x20, 0xF2, +0x6C, 0x9B, 0x60, 0xDA, 0x00, 0x18, 0x27, 0x01, 0x45, 0x99, 0x22, 0xF2, 0x16, 0x6C, 0x8E, 0xEA, +0x3C, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x93, 0xF1, +0x19, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x5C, 0x9A, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x20, 0xF2, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, +0x60, 0x33, 0x20, 0xF2, 0x70, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF2, 0x48, 0x9A, 0xAF, 0xF3, 0x1C, 0x6B, 0x6B, 0xEB, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF2, 0x54, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x20, 0xF2, 0x78, 0x9B, 0x60, 0xDA, 0x00, 0x18, 0x27, 0x01, 0x45, 0x99, 0x24, 0xF2, 0x16, 0x6B, +0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x01, 0x6B, 0x80, 0xF6, +0x68, 0xDA, 0x45, 0x99, 0x24, 0xF2, 0x15, 0x6C, 0x8E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x02, 0x6B, 0x80, 0xF6, 0x68, 0xDA, 0x45, 0x99, 0x24, 0xF2, 0x14, 0x6B, +0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x03, 0x6B, 0x80, 0xF6, +0x68, 0xDA, 0x45, 0x99, 0x44, 0xF2, 0x00, 0x6C, 0x8E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x01, 0x6B, 0xC0, 0xF2, 0x6C, 0xDA, 0x45, 0x99, 0x44, 0xF2, 0x01, 0x6B, +0x6E, 0xEA, 0x0D, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x6C, 0x9B, 0x60, 0xDA, 0x45, 0x99, +0x44, 0xF2, 0x02, 0x6C, 0x8E, 0xEA, 0x0E, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0xF2, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF6, 0x0C, 0x4B, +0x64, 0xAB, 0x60, 0xDA, 0x45, 0x99, 0x44, 0xF2, 0x03, 0x6B, 0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x01, 0x6B, 0xA0, 0xF0, 0x64, 0xDA, 0x45, 0x99, 0x44, 0xF2, +0x04, 0x6C, 0x8E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, +0xA0, 0xF0, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x44, 0x9A, +0x12, 0xF0, 0x03, 0x6B, 0x6E, 0xEA, 0x7F, 0xF5, 0x12, 0x2A, 0x00, 0x18, 0x86, 0x09, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x68, 0x15, +0xFB, 0x63, 0x09, 0x62, 0x08, 0xD1, 0x04, 0x01, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x20, 0xF2, 0x5C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x10, 0x6A, 0x6C, 0xEA, 0x71, 0x22, +0x00, 0x6A, 0x40, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF6, 0x44, 0x9A, +0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF6, 0x64, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF6, 0x64, 0x9A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF2, 0x40, 0x9A, 0x63, 0xEA, 0x58, 0x67, 0x5C, 0x22, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, 0xA0, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xA0, 0xF0, 0x44, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x04, 0x2A, 0x04, 0x6C, 0x00, 0x6D, +0x00, 0x18, 0x5B, 0x0E, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x02, 0x6B, 0x64, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x48, 0x9A, +0x01, 0x6B, 0x6E, 0xEA, 0x09, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x48, 0x9A, 0x22, 0xF2, 0x16, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x09, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x22, 0xF2, 0x15, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x48, 0x9A, 0x03, 0x6B, 0x6E, 0xEA, 0x13, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x22, 0xF2, 0x14, 0x6B, 0x60, 0xDA, +0x09, 0x10, 0x01, 0x6A, 0x40, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, +0xA0, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x44, 0xAA, 0x02, 0x6B, 0x6E, 0xEA, 0xE0, 0xF0, 0x0B, 0x2A, 0x40, 0xA1, 0x01, 0x6B, 0x6E, 0xEA, +0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x41, 0x9A, +0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x61, 0xDA, +0x08, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x00, 0x6B, +0x61, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x41, 0x9A, +0x11, 0x5A, 0x58, 0x67, 0xE0, 0xF0, 0x12, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x0C, 0x4A, 0x00, 0x6B, 0x61, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF2, 0x44, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, 0x02, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, +0x42, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, +0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, +0x44, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, +0x41, 0xD9, 0x41, 0x99, 0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, +0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x82, 0x99, 0x01, 0x6B, 0x8D, 0xEB, +0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, +0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, +0x48, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, +0x41, 0xD9, 0x41, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF2, 0x48, 0x9A, 0x07, 0x6B, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x11, 0x10, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x4C, 0x9A, 0x40, 0x9A, 0x42, 0xD9, +0x62, 0x99, 0x01, 0x6A, 0x4C, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0x0E, 0x2A, 0x41, 0x99, 0x01, 0x4A, +0x41, 0xD9, 0x61, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x50, 0x9A, +0x43, 0xEB, 0x58, 0x67, 0xE5, 0x2A, 0x01, 0x10, 0x00, 0x65, 0x00, 0x18, 0x52, 0x09, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x01, 0x6B, 0x60, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x40, 0x9A, 0x42, 0xD9, 0x62, 0x99, +0x01, 0x6A, 0x6D, 0xEA, 0x42, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, 0x41, 0x99, 0x01, 0x4A, +0x41, 0xD9, 0x41, 0x99, 0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x62, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x41, 0xD9, 0x03, 0x10, +0x41, 0x99, 0x01, 0x4A, 0x41, 0xD9, 0x41, 0x99, 0x0A, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x2E, 0x10, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x4C, 0x9A, 0x27, 0x22, 0x01, 0x10, +0x00, 0x65, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF2, 0x5C, 0x9A, 0x40, 0x9A, +0x42, 0xD9, 0x62, 0x99, 0x10, 0x6A, 0x6C, 0xEA, 0x18, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x4C, 0x9A, 0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x4C, 0x9A, +0x02, 0xF0, 0x01, 0x5A, 0x58, 0x67, 0xDC, 0x2A, 0x01, 0x10, 0x00, 0x65, 0xB9, 0x65, 0x05, 0x97, +0x04, 0x91, 0x03, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFC, 0x63, 0x07, 0x62, 0x06, 0xD1, 0x04, 0x01, +0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x40, 0x99, 0x32, 0x5A, +0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x4C, 0x9A, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF2, 0x74, 0x9B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF0, 0x44, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x16, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x44, 0xAA, 0x02, 0x6B, +0x6E, 0xEA, 0x0C, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x01, 0x6B, 0x64, 0xCA, 0x04, 0x6C, 0x00, 0x6D, 0x00, 0x18, 0x5B, 0x0E, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x01, 0x6B, 0x64, 0xCA, 0xB9, 0x65, 0x03, 0x97, +0x02, 0x91, 0x02, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x02, 0x6B, 0x64, 0xCA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x4C, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0xB9, 0x65, +0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFC, 0x63, 0x07, 0x62, 0x06, 0xD1, 0x04, 0x01, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x4C, 0x9A, 0x1C, 0xF0, 0x00, 0x4A, +0x0E, 0x5A, 0x78, 0x67, 0x20, 0xF1, 0x00, 0x23, 0x48, 0x33, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF1, 0x14, 0x4A, 0x49, 0xE3, 0x40, 0x9A, 0x00, 0xEA, 0x00, 0x18, 0xDF, 0x0C, +0x44, 0xC1, 0x44, 0xA1, 0x01, 0x6B, 0x6E, 0xEA, 0x00, 0xF1, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x01, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x08, 0x11, 0x00, 0x18, +0x23, 0x0A, 0x44, 0xC1, 0x44, 0xA1, 0x02, 0x2A, 0x00, 0x6A, 0x02, 0x11, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x01, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0xF0, 0x10, 0x01, 0x6C, +0x00, 0x18, 0x33, 0x0A, 0x44, 0xC1, 0x44, 0xA1, 0x0A, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, 0xE1, 0x10, 0x44, 0xA1, +0x01, 0x6B, 0x6E, 0xEA, 0x0A, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, +0x01, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, 0xD3, 0x10, 0x44, 0xA1, 0x02, 0x6B, 0x6E, 0xEA, +0x0A, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x0C, 0x6B, 0x80, 0xF6, +0x6C, 0xDA, 0x00, 0x6A, 0xC5, 0x10, 0x44, 0xA1, 0x07, 0x6B, 0x6E, 0xEA, 0x0A, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x03, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, +0xB7, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, +0x6C, 0xDA, 0x00, 0x6A, 0xAD, 0x10, 0x02, 0x6C, 0x00, 0x18, 0x20, 0x0B, 0x44, 0xC1, 0x44, 0xA1, +0x0A, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, +0x6C, 0xDA, 0x00, 0x6A, 0x9D, 0x10, 0x44, 0xA1, 0x01, 0x6B, 0x6E, 0xEA, 0x0A, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x03, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, +0x8F, 0x10, 0x44, 0xA1, 0x02, 0x6B, 0x6E, 0xEA, 0x12, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x04, 0xF0, 0x0C, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x07, 0x6B, 0x60, 0xCA, 0x00, 0x6A, 0x79, 0x10, 0x44, 0xA1, +0x07, 0x6B, 0x6E, 0xEA, 0x12, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, +0x0C, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x44, 0x9A, 0x07, 0x6B, 0x60, 0xCA, 0x00, 0x6A, 0x63, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, 0x59, 0x10, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x4B, 0xA2, 0x12, 0x22, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x05, 0x6B, 0x60, 0xCA, 0x00, 0x6A, +0x3F, 0x10, 0x00, 0x18, 0xBF, 0x0C, 0x44, 0xC1, 0x44, 0xA1, 0x03, 0x6B, 0x6E, 0xEA, 0x12, 0x2A, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x44, 0x9A, 0x05, 0x6B, 0x60, 0xCA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x0D, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, +0x00, 0x6A, 0x26, 0x10, 0x44, 0xA1, 0x12, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x04, 0xF0, 0x00, 0x6B, 0x80, 0xF6, 0x6C, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0xF2, 0x44, 0x9A, 0x05, 0x6B, 0x60, 0xCA, 0x00, 0x6A, 0x12, 0x10, 0x44, 0xA1, 0x02, 0x6B, +0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x04, 0xF0, 0x0C, 0x6B, +0x80, 0xF6, 0x6C, 0xDA, 0x00, 0x6A, 0x04, 0x10, 0x00, 0x6A, 0x02, 0x10, 0x00, 0x65, 0x00, 0x10, +0xB9, 0x65, 0x03, 0x97, 0x02, 0x91, 0x02, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFD, 0x63, 0x05, 0x62, +0x04, 0xD1, 0x04, 0x01, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x40, 0xA2, +0x26, 0x6B, 0x6E, 0xEA, 0x02, 0x2A, 0x00, 0x18, 0x4C, 0x06, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x40, 0xA2, 0x52, 0x6B, 0x6E, 0xEA, 0x02, 0x2A, 0x00, 0x18, 0x4C, 0x06, +0x01, 0x6A, 0xB9, 0x65, 0x01, 0x97, 0x00, 0x91, 0x01, 0x63, 0x00, 0xEF, 0xFA, 0x63, 0x0B, 0x62, +0x0A, 0xD1, 0x04, 0x01, 0x44, 0x67, 0x20, 0xF0, 0x40, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x40, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x40, 0xA2, 0x93, 0x6B, 0x6E, 0xEA, 0xA0, 0xF1, 0x14, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x70, 0x6C, 0x8E, 0xEA, 0x05, 0x2A, +0x00, 0x6C, 0x00, 0x18, 0x41, 0x0C, 0x07, 0x6A, 0xA6, 0x11, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x20, 0x5A, 0x58, 0x67, 0x02, 0x22, 0x00, 0x6A, +0x9A, 0x11, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, +0x68, 0x5A, 0x58, 0x67, 0x02, 0x2A, 0x00, 0x6A, 0x8E, 0x11, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x20, 0x6B, 0x6E, 0xEA, 0x4D, 0x2A, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x78, 0x6B, 0x6B, 0xEB, 0x40, 0xF6, 0x60, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6C, 0xA2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x61, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6D, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x62, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, +0x0C, 0x4A, 0x6E, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, +0x63, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6F, 0xA2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x64, 0xC2, 0x40, 0xA1, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF6, 0x00, 0x4B, 0x83, 0x67, 0xA2, 0x67, +0x00, 0x18, 0xCE, 0x0B, 0x01, 0x6A, 0x37, 0x11, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x40, 0xA2, 0x30, 0x6C, 0x8E, 0xEA, 0x0E, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x04, 0x2A, 0x00, 0x18, 0x29, 0x0D, 0x02, 0x6A, +0x22, 0x11, 0x00, 0x6A, 0x20, 0x11, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, +0x00, 0x4A, 0x41, 0xA2, 0x62, 0x67, 0x0F, 0x6A, 0x6C, 0xEA, 0x16, 0x22, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x52, 0x33, 0xFF, 0x6A, 0x6C, 0xEA, +0x62, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x43, 0xAA, +0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x62, 0x67, 0x0F, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x49, 0xA2, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, +0xEA, 0x10, 0x40, 0xA1, 0xE0, 0x4A, 0x53, 0x32, 0x43, 0xC1, 0x00, 0x6A, 0x42, 0xC1, 0x2C, 0x10, +0x42, 0xA1, 0x0F, 0x2A, 0x42, 0xA1, 0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, 0x40, 0xA2, 0x88, 0x6B, 0x6E, 0xEA, 0x1A, 0x22, 0x00, 0x6A, +0xD2, 0x10, 0x42, 0xA1, 0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, +0x00, 0x4A, 0x49, 0xE3, 0x60, 0xA2, 0x42, 0xA1, 0x8F, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x49, 0xE4, 0x4C, 0xA2, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, +0xBA, 0x10, 0x42, 0xA1, 0x01, 0x4A, 0x42, 0xC1, 0x62, 0xA1, 0x43, 0xA1, 0x43, 0xEB, 0x58, 0x67, +0xCF, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x61, 0xA2, +0x0F, 0x6A, 0x6C, 0xEA, 0x48, 0xC1, 0x48, 0xA1, 0x40, 0x22, 0x48, 0xA1, 0x08, 0x6B, 0x4B, 0xE3, +0x48, 0xC1, 0x43, 0xA1, 0x14, 0x2A, 0x48, 0xA1, 0x88, 0x6B, 0x83, 0x67, 0x84, 0xEA, 0x44, 0x67, +0x4A, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x42, 0xA2, +0x62, 0x67, 0x48, 0xA1, 0x83, 0x67, 0x84, 0xEA, 0x44, 0x67, 0x49, 0xC1, 0x20, 0x10, 0x43, 0xA1, +0x6F, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x49, 0xE3, +0x4C, 0xA2, 0x62, 0x67, 0x48, 0xA1, 0x83, 0x67, 0x84, 0xEA, 0x44, 0x67, 0x4A, 0xC1, 0x43, 0xA1, +0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, +0x40, 0xA2, 0x62, 0x67, 0x48, 0xA1, 0x83, 0x67, 0x84, 0xEA, 0x44, 0x67, 0x49, 0xC1, 0x6A, 0xA1, +0x49, 0xA1, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x66, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6F, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x64, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, +0x0C, 0x4A, 0x6E, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, +0x63, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6D, 0xA2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x62, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x6C, 0xA2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x61, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x78, 0x6B, 0x6B, 0xEB, 0x40, 0xF6, 0x60, 0xC2, 0x00, 0x6A, 0x42, 0xC1, 0x12, 0x10, +0x42, 0xA1, 0x83, 0xA1, 0x62, 0xA1, 0x71, 0xE4, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x40, 0xF6, 0x00, 0x4B, 0x6D, 0xE4, 0x60, 0xA3, 0x91, 0x67, 0x49, 0xE4, 0x70, 0xC2, 0x42, 0xA1, +0x01, 0x4A, 0x42, 0xC1, 0x62, 0xA1, 0x43, 0xA1, 0x05, 0x6C, 0x4B, 0xE4, 0x42, 0xEB, 0x58, 0x67, +0xE7, 0x2A, 0x40, 0xA1, 0x67, 0x41, 0x09, 0x4B, 0x83, 0x67, 0xA2, 0x67, 0x00, 0x18, 0xCE, 0x0B, +0x01, 0x6A, 0x01, 0x10, 0x00, 0x6A, 0xB9, 0x65, 0x07, 0x97, 0x06, 0x91, 0x04, 0x63, 0x00, 0xEF, +0xFA, 0x63, 0x0B, 0x62, 0x0A, 0xD1, 0x04, 0x01, 0x44, 0x67, 0x20, 0xF0, 0x40, 0xC1, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x40, 0xC1, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x40, 0xA2, 0x95, 0x6B, 0x6E, 0xEA, 0x20, 0xF1, +0x1D, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, +0x70, 0x6C, 0x8E, 0xEA, 0x05, 0x2A, 0x01, 0x6C, 0x00, 0x18, 0x41, 0x0C, 0x07, 0x6A, 0x2F, 0x11, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x20, 0x6B, +0x6E, 0xEA, 0x52, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x70, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x60, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x71, 0xA2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x61, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x72, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x62, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, +0x0C, 0x4A, 0x73, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, +0x63, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x74, 0xA2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x64, 0xC2, 0x40, 0xA1, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x40, 0xF6, 0x00, 0x4B, 0x83, 0x67, 0xA2, 0x67, +0x00, 0x18, 0xCE, 0x0B, 0x01, 0x6A, 0xD3, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x40, 0xA2, 0x30, 0x6C, 0x8E, 0xEA, 0x0E, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x04, 0x2A, 0x00, 0x18, 0x29, 0x0D, 0x02, 0x6A, +0xBE, 0x10, 0x00, 0x6A, 0xBC, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, +0x00, 0x4A, 0x41, 0xA2, 0x62, 0x67, 0x01, 0x6A, 0x4C, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0x16, 0x22, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x52, 0x33, +0xFF, 0x6A, 0x6C, 0xEA, 0x62, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, +0x04, 0x4A, 0x43, 0xAA, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x99, 0x10, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x62, 0x67, 0x0F, 0x6A, 0x4C, 0xEB, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x04, 0x4A, 0x49, 0xA2, 0x6E, 0xEA, +0x02, 0x22, 0x00, 0x6A, 0x84, 0x10, 0x40, 0xA1, 0xE0, 0x4A, 0x53, 0x32, 0x42, 0xC1, 0x00, 0x6A, +0x41, 0xC1, 0x1B, 0x10, 0x41, 0xA1, 0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, 0x60, 0xA2, 0x41, 0xA1, 0x84, 0x42, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x49, 0xE4, 0x4C, 0xA2, 0x6E, 0xEA, 0x02, 0x22, +0x00, 0x6A, 0x65, 0x10, 0x41, 0xA1, 0x01, 0x4A, 0x41, 0xC1, 0x61, 0xA1, 0x42, 0xA1, 0x43, 0xEB, +0x58, 0x67, 0xE0, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, +0x61, 0xA2, 0x0F, 0x6A, 0x6C, 0xEA, 0x45, 0xC1, 0x45, 0xA1, 0x2A, 0x22, 0x45, 0xA1, 0x08, 0x6B, +0x4B, 0xE3, 0x45, 0xC1, 0x42, 0xA1, 0x64, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x0C, 0x4A, 0x49, 0xE3, 0x4C, 0xA2, 0x62, 0x67, 0x45, 0xA1, 0x83, 0x67, 0x84, 0xEA, +0x44, 0x67, 0x44, 0xC1, 0x42, 0xA1, 0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, 0x40, 0xA2, 0x62, 0x67, 0x45, 0xA1, 0x83, 0x67, 0x84, 0xEA, +0x44, 0x67, 0x43, 0xC1, 0x64, 0xA1, 0x43, 0xA1, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x27, 0x10, +0x00, 0x6A, 0x41, 0xC1, 0x13, 0x10, 0x41, 0xA1, 0x62, 0xA1, 0x84, 0x43, 0x61, 0xA1, 0x71, 0xE4, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF6, 0x0C, 0x4B, 0x6D, 0xE4, 0x6C, 0xA3, +0x91, 0x67, 0x49, 0xE4, 0x68, 0xC2, 0x41, 0xA1, 0x01, 0x4A, 0x41, 0xC1, 0x61, 0xA1, 0x42, 0xA1, +0x05, 0x6C, 0x4B, 0xE4, 0x42, 0xEB, 0x58, 0x67, 0xE6, 0x2A, 0x40, 0xA1, 0x67, 0x41, 0x01, 0x4B, +0x83, 0x67, 0xA2, 0x67, 0x00, 0x18, 0xCE, 0x0B, 0x01, 0x6A, 0x01, 0x10, 0x00, 0x6A, 0xB9, 0x65, +0x07, 0x97, 0x06, 0x91, 0x04, 0x63, 0x00, 0xEF, 0xFA, 0x63, 0x0B, 0xD1, 0x3D, 0x67, 0x8C, 0xD9, +0x45, 0x67, 0x20, 0xF0, 0x54, 0xC1, 0x00, 0x6A, 0x46, 0xC9, 0x00, 0x6A, 0x41, 0xD9, 0x4C, 0x99, +0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, +0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, +0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, +0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, +0x01, 0x6B, 0x60, 0xC2, 0x20, 0xF0, 0x74, 0xA1, 0x0F, 0x6A, 0x6C, 0xEA, 0x20, 0x22, 0x20, 0xF0, +0x54, 0xA1, 0x0F, 0x6B, 0x6C, 0xEA, 0x08, 0x6B, 0x4B, 0xE3, 0x46, 0xC9, 0x46, 0xA9, 0x40, 0x32, +0x46, 0xC9, 0x20, 0xF0, 0x54, 0xA1, 0xE0, 0x4A, 0x53, 0x33, 0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, +0x66, 0xA9, 0x4F, 0xE3, 0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, 0x04, 0x4A, 0x46, 0xC9, 0x66, 0xA9, +0xFF, 0x6A, 0x6C, 0xEA, 0x45, 0xC9, 0x45, 0xA9, 0x01, 0x4A, 0x45, 0xC9, 0x10, 0x10, 0x20, 0xF0, +0x54, 0xA1, 0xE0, 0x4A, 0x53, 0x33, 0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, 0x66, 0xA9, 0x4F, 0xE3, +0xFF, 0xF7, 0x1F, 0x6A, 0x6C, 0xEA, 0x05, 0x4A, 0x46, 0xC9, 0x46, 0xA9, 0x45, 0xC9, 0x46, 0xA9, +0x05, 0x6B, 0x6E, 0xEA, 0x09, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, +0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x08, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x40, 0x9A, 0x48, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x4C, 0x9A, 0x66, 0xA9, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x45, 0xA9, 0x05, 0x5A, +0x58, 0x67, 0x0A, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, +0x60, 0x99, 0x60, 0x9B, 0x60, 0xDA, 0x13, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x50, 0x9A, 0x60, 0x99, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x60, 0x99, 0x04, 0x4B, 0x60, 0x9B, 0x60, 0xDA, 0x00, 0x6A, +0x44, 0xD9, 0x0D, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x44, 0x9A, +0x12, 0xF0, 0x04, 0x6B, 0x6E, 0xEA, 0x09, 0x22, 0x44, 0x99, 0x01, 0x4A, 0x44, 0xD9, 0x44, 0x99, +0xE5, 0xF7, 0x1F, 0x5A, 0x58, 0x67, 0xEE, 0x2A, 0x01, 0x10, 0x00, 0x65, 0xB9, 0x65, 0x0B, 0x91, +0x06, 0x63, 0x20, 0xE8, 0xFC, 0x63, 0x07, 0x62, 0x06, 0xD1, 0x04, 0x01, 0x44, 0x67, 0x50, 0xC1, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x4C, 0x9A, 0x04, 0xF0, 0x01, 0x6B, 0x6E, 0xEA, +0x26, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x42, 0xA2, +0x88, 0x6B, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x3F, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x62, 0x67, 0x40, 0xF6, 0x03, 0x4B, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF6, 0x18, 0x4A, 0x83, 0x67, 0xA2, 0x67, 0x03, 0x6E, 0x00, 0x18, 0x47, 0x05, 0x01, 0x6B, +0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x28, 0x10, 0x00, 0x6C, 0x00, 0x18, 0x75, 0x0C, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x4C, 0x9A, 0x04, 0xF0, 0x03, 0x6B, 0x6E, 0xEA, +0x1A, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x62, 0x67, 0x40, 0xF6, 0x02, 0x4B, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x1C, 0x4A, 0x83, 0x67, 0xA2, 0x67, +0x04, 0x6E, 0x00, 0x18, 0x47, 0x05, 0x01, 0x6B, 0x6E, 0xEA, 0x02, 0x22, 0x00, 0x6A, 0x04, 0x10, +0x01, 0x6C, 0x00, 0x18, 0x75, 0x0C, 0x00, 0x10, 0xB9, 0x65, 0x03, 0x97, 0x02, 0x91, 0x02, 0x63, +0x00, 0xEF, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x44, 0x67, 0x50, 0xC1, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x07, 0x6B, 0x60, 0xCA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, +0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, 0x1B, 0x6B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, 0x02, 0x6B, +0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x48, 0x9A, +0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x44, 0x9A, +0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x4C, 0x9A, +0x01, 0x6B, 0x60, 0xCA, 0x50, 0xA1, 0x09, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x50, 0x9A, 0x04, 0x6B, 0x60, 0xDA, 0x08, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x00, 0x6A, 0x40, 0xD9, 0x0D, 0x10, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x44, 0x9A, 0x12, 0xF0, 0x04, 0x6B, +0x6E, 0xEA, 0x0E, 0x22, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x60, 0x99, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x58, 0x9A, 0x63, 0xEA, 0x58, 0x67, 0xE9, 0x22, 0x01, 0x10, +0x00, 0x65, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFF, 0x63, 0x01, 0xD1, +0x3D, 0x67, 0xB9, 0x65, 0x01, 0x91, 0x01, 0x63, 0x20, 0xE8, 0x00, 0x65, 0xFD, 0x63, 0x05, 0x62, +0x04, 0xD1, 0x04, 0x01, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x40, 0xA2, +0x50, 0x6B, 0x4E, 0xEB, 0x11, 0x23, 0x51, 0x52, 0x78, 0x67, 0x07, 0x23, 0x1A, 0x6B, 0x4E, 0xEB, +0x23, 0x23, 0x30, 0x6B, 0x6E, 0xEA, 0x18, 0x22, 0x23, 0x10, 0x60, 0x6B, 0x4E, 0xEB, 0x18, 0x23, +0xA2, 0x6B, 0x6E, 0xEA, 0x0D, 0x22, 0x1C, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x02, 0x22, 0x00, 0x6A, 0x13, 0x10, 0x03, 0x6A, 0x11, 0x10, +0x00, 0x18, 0xEB, 0x0C, 0x01, 0x6A, 0x0D, 0x10, 0x00, 0x18, 0x29, 0x0D, 0x02, 0x6A, 0x09, 0x10, +0x00, 0x18, 0x87, 0x06, 0x04, 0x6A, 0x05, 0x10, 0x00, 0x18, 0xC2, 0x06, 0x04, 0x6A, 0x01, 0x10, +0x00, 0x6A, 0xB9, 0x65, 0x01, 0x97, 0x00, 0x91, 0x01, 0x63, 0x00, 0xEF, 0xFD, 0x63, 0x05, 0x62, +0x04, 0xD1, 0x04, 0x01, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x40, 0xA2, +0x52, 0x6B, 0x6E, 0xEA, 0x04, 0x2A, 0x00, 0x18, 0x4C, 0x06, 0x01, 0x6A, 0x01, 0x10, 0x00, 0x6A, +0xB9, 0x65, 0x01, 0x97, 0x00, 0x91, 0x01, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFB, 0x63, 0x09, 0x62, +0x08, 0xD1, 0x04, 0x01, 0x01, 0x6A, 0x42, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x42, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, +0x00, 0x4A, 0x41, 0xA2, 0x41, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, +0x00, 0x4A, 0x4A, 0xA2, 0x40, 0xC1, 0x41, 0xA1, 0x2A, 0x5A, 0x58, 0x67, 0x01, 0x6B, 0x6E, 0xEA, +0x62, 0x67, 0xFF, 0x6A, 0x4C, 0xEB, 0x41, 0xA1, 0x02, 0x5A, 0x58, 0x67, 0x82, 0x67, 0xFF, 0x6A, +0x8C, 0xEA, 0x4D, 0xEB, 0xFF, 0x6A, 0x6C, 0xEA, 0x05, 0x22, 0x00, 0x6C, 0x00, 0x18, 0x82, 0x0D, +0x00, 0x6A, 0x42, 0x10, 0x42, 0xA1, 0x28, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x48, 0x32, 0x44, 0xC1, 0x00, 0x6A, 0x43, 0xC1, 0x18, 0x10, +0x64, 0xA1, 0x43, 0xA1, 0x51, 0xE3, 0x43, 0xA1, 0x62, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, 0x60, 0xA2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x49, 0xE4, 0x60, 0xC2, 0x43, 0xA1, 0x01, 0x4A, 0x43, 0xC1, +0x43, 0xA1, 0x04, 0x5A, 0x58, 0x67, 0xE4, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xA0, 0xF0, 0x44, 0x9A, 0x01, 0x6B, 0x6E, 0xEA, 0x0B, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x01, 0x6C, 0xA2, 0x67, 0x00, 0x18, 0x5B, 0x0E, +0x0A, 0x6C, 0x00, 0x18, 0x82, 0x0D, 0x01, 0x6A, 0xB9, 0x65, 0x05, 0x97, 0x04, 0x91, 0x03, 0x63, +0x00, 0xEF, 0x00, 0x65, 0xFB, 0x63, 0x09, 0x62, 0x08, 0xD1, 0x04, 0x01, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, 0x00, 0x4A, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, 0x01, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, 0x07, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, 0x08, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, 0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x2A, 0x5A, 0x58, 0x67, 0x07, 0x2A, +0x00, 0x6C, 0x00, 0x18, 0x82, 0x0D, 0x00, 0x6A, 0x62, 0x67, 0x43, 0x67, 0x6F, 0x10, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x48, 0x32, 0x61, 0x99, +0x49, 0xE3, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, +0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, +0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x4C, 0x9A, 0x10, 0x6B, 0x60, 0xCA, 0x00, 0x6A, 0x48, 0xC1, 0x0F, 0x10, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x68, 0xA1, 0x68, 0x33, 0x81, 0x99, +0x6D, 0xE4, 0x60, 0x9B, 0x60, 0xDA, 0x48, 0xA1, 0x01, 0x4A, 0x48, 0xC1, 0x48, 0xA1, 0x04, 0x5A, +0x58, 0x67, 0xED, 0x2A, 0x00, 0x6A, 0x40, 0xD9, 0x0D, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x20, 0xF6, 0x44, 0x9A, 0x12, 0xF0, 0x04, 0x6B, 0x6E, 0xEA, 0x0E, 0x22, 0x40, 0x99, +0x01, 0x4A, 0x40, 0xD9, 0x60, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, +0x58, 0x9A, 0x63, 0xEA, 0x58, 0x67, 0xE9, 0x22, 0x01, 0x10, 0x00, 0x65, 0xB9, 0x65, 0x05, 0x97, +0x04, 0x91, 0x03, 0x63, 0x00, 0xEF, 0x00, 0x65, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x44, 0x67, +0x50, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x5C, 0x9A, 0x01, 0x6B, +0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF1, 0x58, 0x9A, 0x00, 0xF4, +0x05, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, 0x40, 0x9A, +0x00, 0x6B, 0x60, 0xC2, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x12, 0xF0, 0x06, 0x6B, +0x20, 0xF6, 0x64, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x48, 0x9A, +0x01, 0x6B, 0x6E, 0xEA, 0x08, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xE0, 0xF1, +0x44, 0x9A, 0x1B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x48, 0x9A, 0x02, 0x6B, 0x6E, 0xEA, 0x10, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x48, 0x9A, 0x02, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x44, 0x9A, 0x0B, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xE0, 0xF1, 0x4C, 0x9A, 0x00, 0xF4, 0x00, 0x6B, 0x60, 0xCA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xE0, 0xF1, 0x50, 0x9A, 0x70, 0xA1, 0x60, 0xDA, 0x00, 0x6A, 0x40, 0xD9, 0x0D, 0x10, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x20, 0xF6, 0x44, 0x9A, 0x12, 0xF0, 0x04, 0x6B, +0x6E, 0xEA, 0x09, 0x22, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x40, 0x99, 0xE5, 0xF7, 0x1F, 0x5A, +0x58, 0x67, 0xEE, 0x2A, 0x01, 0x10, 0x00, 0x65, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, +0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, +0x44, 0x9A, 0x40, 0x9A, 0x41, 0xD9, 0x61, 0x99, 0x02, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x41, 0xD9, +0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x40, 0x99, 0x0A, 0x5A, +0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, +0x61, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, +0x40, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF2, 0x5C, 0x9A, 0x40, 0x9A, 0x41, 0xD9, 0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, +0x01, 0x4A, 0x40, 0xD9, 0x40, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x61, 0x99, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF2, 0x40, 0x9A, 0x6D, 0xEA, 0x41, 0xD9, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x5C, 0x9A, 0x61, 0x99, 0x60, 0xDA, 0x00, 0x6A, +0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, 0x40, 0x99, 0x32, 0x5A, 0x58, 0x67, +0xF9, 0x2A, 0x61, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF2, 0x44, 0x9A, +0x6C, 0xEA, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x5C, 0x9A, +0x61, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x40, 0xD9, 0x03, 0x10, 0x40, 0x99, 0x01, 0x4A, 0x40, 0xD9, +0x40, 0x99, 0x32, 0x5A, 0x58, 0x67, 0xF9, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x60, 0xF2, 0x48, 0x9A, 0x40, 0x9A, 0x41, 0xD9, 0x61, 0x99, 0x01, 0x6A, 0x6D, 0xEA, 0x41, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF2, 0x48, 0x9A, 0x61, 0x99, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x40, 0x9A, 0x41, 0xD9, +0x61, 0x99, 0x01, 0x6A, 0x6D, 0xEA, 0x41, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF2, 0x44, 0x9A, 0x61, 0x99, 0x60, 0xDA, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, +0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, +0x44, 0x9A, 0x40, 0x9A, 0x40, 0xD9, 0x60, 0x99, 0x02, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x40, 0xD9, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x60, 0x99, 0x60, 0xDA, +0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x40, 0x9A, 0x40, 0xD9, 0x60, 0x99, +0x03, 0x6A, 0x4B, 0xEA, 0x6C, 0xEA, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x40, 0xF2, 0x44, 0x9A, 0x60, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x60, 0xF2, 0x4C, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x60, 0xF2, 0x70, 0x9B, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF2, 0x48, 0x9A, 0x40, 0x9A, +0x40, 0xD9, 0x60, 0x99, 0x02, 0x6A, 0x6D, 0xEA, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x60, 0xF2, 0x48, 0x9A, 0x60, 0x99, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x60, 0xF2, 0x4C, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x60, 0xF2, +0x74, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, +0x40, 0x9A, 0x40, 0xD9, 0x60, 0x99, 0x02, 0x6A, 0x6D, 0xEA, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x60, 0x99, 0x60, 0xDA, 0xB9, 0x65, 0x03, 0x91, +0x02, 0x63, 0x20, 0xE8, 0xFE, 0x63, 0x03, 0xD1, 0x3D, 0x67, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, 0x40, 0x9A, 0x40, 0xD9, 0x60, 0x99, 0x03, 0x6A, 0x4B, 0xEA, +0x6C, 0xEA, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF2, 0x44, 0x9A, +0x60, 0x99, 0x60, 0xDA, 0xB9, 0x65, 0x03, 0x91, 0x02, 0x63, 0x20, 0xE8, 0xFC, 0x63, 0x07, 0xD1, +0x3D, 0x67, 0x64, 0x67, 0x45, 0x67, 0x20, 0xF0, 0x60, 0xC1, 0x20, 0xF0, 0x44, 0xC1, 0x00, 0x6A, +0x43, 0xD9, 0x00, 0x6A, 0x48, 0xC1, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x50, 0x9A, 0x40, 0x9A, 0x40, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, +0x50, 0x9A, 0x00, 0x6B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF0, +0x00, 0x4A, 0x41, 0xD9, 0x48, 0xA1, 0x45, 0xD9, 0x20, 0xF0, 0x40, 0xA1, 0x04, 0x6B, 0x6E, 0xEA, +0x20, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x50, 0xA2, 0x62, 0x67, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x44, 0xAA, 0x6E, 0xEA, +0xA0, 0xF1, 0x1D, 0x22, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, +0x64, 0xAA, 0xFF, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x70, 0xC2, 0x00, 0x6A, 0x43, 0xD9, 0x0E, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x60, 0xF2, 0x58, 0x9A, 0x40, 0x9A, 0x45, 0xD9, 0x65, 0x99, 0xFF, 0x6A, 0x6C, 0xEA, 0x40, 0x6B, +0x6E, 0xEA, 0x13, 0x22, 0x63, 0x99, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x60, 0xF2, +0x5C, 0x9A, 0x63, 0xEA, 0x58, 0x67, 0x01, 0x6B, 0x6E, 0xEA, 0x62, 0x67, 0xFF, 0x6A, 0x6C, 0xEA, +0x63, 0x99, 0x01, 0x4B, 0x63, 0xD9, 0xE0, 0x2A, 0x01, 0x10, 0x00, 0x65, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x48, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, +0x60, 0xF2, 0x78, 0x9B, 0x60, 0x9B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x40, 0x9A, 0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x40, 0x9A, 0x80, 0x6B, +0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, 0xC0, 0xF2, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, 0x40, 0x9A, 0x10, 0xF0, +0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x60, 0x9B, 0x60, 0x34, 0x10, 0xF0, 0x10, 0x6B, +0x60, 0x33, 0x60, 0x33, 0x80, 0xF2, 0x64, 0x9B, 0x6D, 0xE4, 0x60, 0xDA, 0x01, 0x6A, 0x48, 0xC1, +0x20, 0xF0, 0x40, 0xA1, 0x01, 0x6B, 0x4E, 0xEB, 0x39, 0x23, 0x02, 0x52, 0x78, 0x67, 0x02, 0x23, +0x0A, 0x22, 0xFE, 0x10, 0x02, 0x6B, 0x4E, 0xEB, 0x80, 0xF0, 0x0E, 0x23, 0x04, 0x6B, 0x6E, 0xEA, +0xA0, 0xF0, 0x0A, 0x22, 0xF5, 0x10, 0x20, 0xF0, 0x44, 0xA1, 0x45, 0xD9, 0x45, 0x99, 0x40, 0x32, +0x40, 0x32, 0x00, 0xF5, 0x00, 0x4A, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF2, 0x40, 0x9A, 0x65, 0x99, 0x60, 0xDA, 0x00, 0x6A, 0x44, 0xD9, 0x12, 0x10, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, 0x40, 0x9A, 0x20, 0xF0, 0x84, 0xA1, 0x64, 0x99, +0x6D, 0xE4, 0x68, 0x33, 0x81, 0x99, 0x6D, 0xE4, 0x60, 0x9B, 0x60, 0xDA, 0x44, 0x99, 0x01, 0x4A, +0x44, 0xD9, 0x44, 0x99, 0x04, 0x5A, 0x58, 0x67, 0xEA, 0x2A, 0xCA, 0x10, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x41, 0xA2, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x40, 0x9A, 0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x40, 0x9A, 0x80, 0x6B, 0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x00, 0x6B, 0xC0, 0xF2, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, +0x40, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x60, 0x9B, 0x60, 0x34, +0x65, 0x99, 0x60, 0x33, 0x60, 0x33, 0x6D, 0xE4, 0x41, 0x4B, 0x60, 0xDA, 0x00, 0x6A, 0x45, 0xD9, +0x00, 0x6A, 0x44, 0xD9, 0x14, 0x10, 0x45, 0x99, 0x40, 0x32, 0x45, 0xD9, 0x05, 0x6B, 0x44, 0x99, +0x4F, 0xE3, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x40, 0xF6, 0x00, 0x4A, 0x49, 0xE3, +0x40, 0xA2, 0x65, 0x99, 0x49, 0xE3, 0x45, 0xD9, 0x44, 0x99, 0x01, 0x4A, 0x44, 0xD9, 0x44, 0x99, +0x04, 0x5A, 0x58, 0x67, 0xE8, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, +0x40, 0x9A, 0x65, 0x99, 0x60, 0xDA, 0x6C, 0x10, 0x20, 0xF0, 0x44, 0xA1, 0x45, 0xD9, 0x45, 0x99, +0x40, 0x32, 0x40, 0x32, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, +0x40, 0x9A, 0x85, 0x99, 0x00, 0xF2, 0x02, 0x6B, 0x8D, 0xEB, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, 0x40, 0x9A, 0x20, 0xF0, 0x64, 0xA1, 0x68, 0x33, 0x81, 0x99, +0x6D, 0xE4, 0x60, 0x9B, 0x60, 0xDA, 0x4C, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0xC0, 0xF2, 0x40, 0x9A, 0x61, 0x42, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x40, 0x9A, 0x80, 0x6B, +0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, 0xC0, 0xF2, +0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF6, 0x0C, 0x4A, 0x44, 0xAA, +0x01, 0x6B, 0x6E, 0xEA, 0x16, 0x2A, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, +0x40, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x60, 0x9B, 0x60, 0x34, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF2, 0x68, 0x9B, 0x6D, 0xE4, 0x60, 0xDA, +0x0F, 0x10, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, 0x40, 0x9A, 0x10, 0xF0, +0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, 0x60, 0x9B, 0x60, 0x33, 0x24, 0x4B, 0x60, 0xDA, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x40, 0x9A, 0x61, 0x42, 0x10, 0xF0, +0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xC0, 0xF2, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0xC0, 0xF2, 0x40, 0x9A, 0x80, 0x6B, 0x6E, 0xEA, 0x07, 0x2A, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x00, 0x6B, 0xC0, 0xF2, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, +0x40, 0x32, 0x80, 0xF2, 0x40, 0x9A, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xC0, 0xF2, +0x60, 0x9B, 0x60, 0x33, 0x25, 0x4B, 0x60, 0xDA, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF2, 0x4C, 0x9A, 0x40, 0x9A, 0x45, 0xD9, 0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, +0x80, 0xF2, 0x4C, 0x9A, 0x85, 0x99, 0x01, 0x6B, 0x8D, 0xEB, 0x60, 0xDA, 0x01, 0x10, 0x00, 0x65, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0x00, 0xF2, 0x50, 0x9A, 0x60, 0x99, 0x60, 0xDA, +0xB9, 0x65, 0x07, 0x91, 0x04, 0x63, 0x20, 0xE8, 0xFD, 0x63, 0x05, 0xD1, 0x3D, 0x67, 0x86, 0xD9, +0xA7, 0xD9, 0x00, 0x6A, 0x41, 0xD9, 0x41, 0x99, 0x42, 0xD9, 0x46, 0x99, 0x43, 0xD9, 0x34, 0x10, +0x41, 0xA1, 0x30, 0x5A, 0x58, 0x67, 0x08, 0x2A, 0x41, 0xA1, 0x3A, 0x5A, 0x58, 0x67, 0x04, 0x22, +0x41, 0xA1, 0xD0, 0x4A, 0x40, 0xC1, 0x1A, 0x10, 0x41, 0xA1, 0x61, 0x5A, 0x58, 0x67, 0x08, 0x2A, +0x41, 0xA1, 0x67, 0x5A, 0x58, 0x67, 0x04, 0x22, 0x41, 0xA1, 0xA9, 0x4A, 0x40, 0xC1, 0x0E, 0x10, +0x41, 0xA1, 0x41, 0x5A, 0x58, 0x67, 0x08, 0x2A, 0x41, 0xA1, 0x47, 0x5A, 0x58, 0x67, 0x04, 0x22, +0x41, 0xA1, 0xC9, 0x4A, 0x40, 0xC1, 0x02, 0x10, 0x00, 0x6A, 0x1F, 0x10, 0x41, 0x99, 0x50, 0x33, +0x40, 0xA1, 0x49, 0xE3, 0x42, 0xD9, 0x62, 0x99, 0x41, 0x99, 0x43, 0xEB, 0x58, 0x67, 0x02, 0x22, +0x00, 0x6A, 0x13, 0x10, 0x42, 0x99, 0x41, 0xD9, 0x43, 0x99, 0x40, 0xA2, 0x41, 0xC1, 0x00, 0x6A, +0x61, 0xA1, 0x01, 0x23, 0x01, 0x6A, 0xFF, 0x6B, 0x6C, 0xEA, 0x63, 0x99, 0x01, 0x4B, 0x63, 0xD9, +0xBF, 0x2A, 0x47, 0x99, 0x61, 0x99, 0x60, 0xDA, 0x01, 0x6A, 0xB9, 0x65, 0x05, 0x91, 0x03, 0x63, +0x20, 0xE8, 0x00, 0x65, 0xF3, 0x63, 0x19, 0x62, 0x18, 0xD1, 0x04, 0x01, 0x96, 0xD9, 0xB7, 0xD9, +0xD8, 0xD9, 0x56, 0x99, 0x4B, 0xD9, 0xB5, 0x11, 0x57, 0x99, 0x40, 0x82, 0x25, 0x6B, 0x6E, 0xEA, +0x10, 0x22, 0x56, 0x99, 0x08, 0x22, 0x57, 0x99, 0x60, 0x82, 0x4B, 0x99, 0x60, 0xC2, 0x4B, 0x99, +0x01, 0x4A, 0x4B, 0xD9, 0xA3, 0x11, 0x57, 0x99, 0x40, 0x82, 0x82, 0x67, 0x00, 0x18, 0xAD, 0x02, +0x9D, 0x11, 0x57, 0x99, 0x01, 0x4A, 0x57, 0xD9, 0x57, 0x99, 0x40, 0x82, 0x73, 0x6B, 0x6E, 0xEA, +0x1D, 0x2A, 0x58, 0x99, 0x40, 0x9A, 0x4C, 0xD9, 0x58, 0x99, 0x04, 0x4A, 0x58, 0xD9, 0x12, 0x10, +0x56, 0x99, 0x08, 0x22, 0x4C, 0x99, 0x60, 0x82, 0x4B, 0x99, 0x60, 0xC2, 0x4B, 0x99, 0x01, 0x4A, +0x4B, 0xD9, 0x05, 0x10, 0x4C, 0x99, 0x40, 0x82, 0x82, 0x67, 0x00, 0x18, 0xAD, 0x02, 0x4C, 0x99, +0x01, 0x4A, 0x4C, 0xD9, 0x4C, 0x99, 0x40, 0x82, 0xEB, 0x2A, 0x78, 0x11, 0x47, 0x41, 0x2D, 0x4A, +0x4A, 0xD9, 0x00, 0x6A, 0x49, 0xD9, 0x04, 0x6A, 0x48, 0xD9, 0x57, 0x99, 0x40, 0x82, 0x23, 0x6B, +0x6E, 0xEA, 0x05, 0x2A, 0x01, 0x6A, 0x49, 0xD9, 0x57, 0x99, 0x01, 0x4A, 0x57, 0xD9, 0x57, 0x99, +0x40, 0x82, 0x68, 0x6B, 0x6E, 0xEA, 0x05, 0x2A, 0x0C, 0x6A, 0x48, 0xD9, 0x57, 0x99, 0x01, 0x4A, +0x57, 0xD9, 0x57, 0x99, 0x40, 0x82, 0x68, 0x6B, 0x6E, 0xEA, 0x05, 0x2A, 0x04, 0x6A, 0x48, 0xD9, +0x57, 0x99, 0x01, 0x4A, 0x57, 0xD9, 0x57, 0x99, 0x60, 0x82, 0x20, 0x6A, 0x6D, 0xEA, 0x00, 0xF6, +0x40, 0x32, 0x00, 0xF6, 0x43, 0x32, 0x78, 0x6B, 0x6E, 0xEA, 0x4F, 0x2A, 0x58, 0x99, 0x47, 0xD9, +0x47, 0x99, 0x40, 0x9A, 0x46, 0xD9, 0x47, 0x99, 0x04, 0x4A, 0x47, 0xD9, 0x57, 0x99, 0x60, 0x82, +0xFF, 0x6A, 0x6C, 0xEA, 0x62, 0x67, 0x20, 0x6A, 0x6C, 0xEA, 0x45, 0xD9, 0x47, 0x99, 0x58, 0xD9, +0x49, 0x99, 0x36, 0x22, 0x4A, 0x99, 0x30, 0x6B, 0x60, 0xC2, 0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, +0x45, 0x99, 0x00, 0xF6, 0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, 0x58, 0x6A, 0x6D, 0xEA, 0x00, 0xF6, +0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, 0x4A, 0x99, 0x60, 0xC2, 0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, +0x1F, 0x10, 0x66, 0x99, 0x48, 0x99, 0x67, 0xEA, 0x0F, 0x6A, 0x4C, 0xEB, 0x10, 0xF0, 0x10, 0x6A, +0x40, 0x32, 0x40, 0x32, 0x80, 0xF2, 0x1C, 0x4A, 0x49, 0xE3, 0x60, 0x82, 0x45, 0x99, 0x00, 0xF6, +0x40, 0x32, 0x00, 0xF6, 0x43, 0x32, 0x6D, 0xEA, 0x00, 0xF6, 0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, +0x4A, 0x99, 0x60, 0xC2, 0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, 0x48, 0x99, 0xFC, 0x4A, 0x48, 0xD9, +0x48, 0x99, 0x00, 0x52, 0x58, 0x67, 0xDD, 0x22, 0xDE, 0x10, 0x57, 0x99, 0x40, 0x82, 0x64, 0x6B, +0x6E, 0xEA, 0x62, 0x2A, 0x58, 0x99, 0x40, 0x9A, 0x44, 0xD9, 0x58, 0x99, 0x04, 0x4A, 0x58, 0xD9, +0x44, 0x99, 0x00, 0x52, 0x58, 0x67, 0x09, 0x22, 0x4A, 0x99, 0x2D, 0x6B, 0x60, 0xC2, 0x4A, 0x99, +0x01, 0x4A, 0x4A, 0xD9, 0x44, 0x99, 0x4B, 0xEA, 0x44, 0xD9, 0x4A, 0x99, 0x4C, 0xD9, 0x64, 0x99, +0x10, 0xF0, 0x10, 0x6A, 0x40, 0x32, 0x40, 0x32, 0xA0, 0xF2, 0x50, 0x9A, 0x58, 0xEB, 0x10, 0xEA, +0x4B, 0x34, 0xC0, 0xF7, 0x63, 0x32, 0x4B, 0xE4, 0x44, 0x32, 0x48, 0x34, 0x89, 0xE2, 0x4B, 0xE3, +0xFF, 0x6B, 0x6C, 0xEA, 0x67, 0x42, 0x29, 0x4B, 0xFF, 0x6A, 0x6C, 0xEA, 0x00, 0xF6, 0x40, 0x33, +0x00, 0xF6, 0x63, 0x33, 0x4A, 0x99, 0x60, 0xC2, 0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, 0x44, 0x99, +0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0xA0, 0xF2, 0x70, 0x9B, 0x78, 0xEA, 0x10, 0xEB, +0x6B, 0x33, 0xC0, 0xF7, 0x43, 0x32, 0x4B, 0xE3, 0x44, 0xD9, 0x44, 0x99, 0xD0, 0x2A, 0x4A, 0x99, +0x43, 0xD9, 0x11, 0x10, 0x43, 0x99, 0x40, 0x82, 0x44, 0xD9, 0x4C, 0x99, 0x60, 0x82, 0x43, 0x99, +0x60, 0xC2, 0x44, 0x99, 0x00, 0xF6, 0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, 0x4C, 0x99, 0x60, 0xC2, +0x4C, 0x99, 0x01, 0x4A, 0x4C, 0xD9, 0x43, 0x99, 0xFF, 0x4A, 0x43, 0xD9, 0x63, 0x99, 0x4C, 0x99, +0x63, 0xEA, 0x58, 0x67, 0xE7, 0x2A, 0x77, 0x10, 0x57, 0x99, 0x40, 0x82, 0x40, 0x6B, 0x6E, 0xEA, +0x2C, 0x2A, 0x58, 0x99, 0x41, 0xD9, 0x41, 0x99, 0x40, 0x9A, 0x52, 0xD9, 0x41, 0x99, 0x04, 0x4A, +0x41, 0xD9, 0x41, 0x99, 0x58, 0xD9, 0x47, 0x41, 0x41, 0x4A, 0x42, 0xD9, 0x13, 0x10, 0x42, 0x99, +0x40, 0xA2, 0x8A, 0x99, 0x10, 0xF0, 0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF2, 0x10, 0x4B, +0xA3, 0x67, 0xC2, 0x67, 0x00, 0x18, 0x7C, 0x10, 0x6A, 0x99, 0x49, 0xE3, 0x4A, 0xD9, 0x42, 0x99, +0x01, 0x4A, 0x42, 0xD9, 0x47, 0x41, 0x41, 0x4A, 0x64, 0x42, 0x42, 0x99, 0x63, 0xEA, 0x58, 0x67, +0xE6, 0x2A, 0x4A, 0x99, 0xFF, 0x4A, 0x4A, 0xD9, 0x46, 0x10, 0x57, 0x99, 0x40, 0x82, 0x21, 0x6B, +0x6E, 0xEA, 0x26, 0x2A, 0x58, 0x99, 0x40, 0x9A, 0x4C, 0xD9, 0x58, 0x99, 0x04, 0x4A, 0x58, 0xD9, +0x4C, 0x99, 0x06, 0x4A, 0x40, 0xD9, 0x13, 0x10, 0x4C, 0x99, 0x40, 0x82, 0x8A, 0x99, 0x10, 0xF0, +0x10, 0x6B, 0x60, 0x33, 0x60, 0x33, 0x80, 0xF2, 0x14, 0x4B, 0xA3, 0x67, 0xC2, 0x67, 0x00, 0x18, +0x7C, 0x10, 0x6A, 0x99, 0x49, 0xE3, 0x4A, 0xD9, 0x4C, 0x99, 0x01, 0x4A, 0x4C, 0xD9, 0x6C, 0x99, +0x40, 0x99, 0x43, 0xEB, 0x58, 0x67, 0xE8, 0x2A, 0x4A, 0x99, 0xFF, 0x4A, 0x4A, 0xD9, 0x1B, 0x10, +0x57, 0x99, 0x40, 0x82, 0x63, 0x6B, 0x6E, 0xEA, 0x0F, 0x2A, 0x58, 0x99, 0x40, 0x9A, 0x00, 0xF6, +0x40, 0x33, 0x00, 0xF6, 0x63, 0x33, 0x4A, 0x99, 0x60, 0xC2, 0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, +0x58, 0x99, 0x04, 0x4A, 0x58, 0xD9, 0x07, 0x10, 0x57, 0x99, 0x60, 0x82, 0x4A, 0x99, 0x60, 0xC2, +0x4A, 0x99, 0x01, 0x4A, 0x4A, 0xD9, 0x47, 0x41, 0x2D, 0x4A, 0x4C, 0xD9, 0x12, 0x10, 0x56, 0x99, +0x08, 0x22, 0x4C, 0x99, 0x60, 0x82, 0x4B, 0x99, 0x60, 0xC2, 0x4B, 0x99, 0x01, 0x4A, 0x4B, 0xD9, +0x05, 0x10, 0x4C, 0x99, 0x40, 0x82, 0x82, 0x67, 0x00, 0x18, 0xAD, 0x02, 0x4C, 0x99, 0x01, 0x4A, +0x4C, 0xD9, 0x6C, 0x99, 0x4A, 0x99, 0x43, 0xEB, 0x58, 0x67, 0xE9, 0x2A, 0x57, 0x99, 0x01, 0x4A, +0x57, 0xD9, 0x57, 0x99, 0x40, 0x82, 0x5F, 0xF6, 0x07, 0x2A, 0x56, 0x99, 0x03, 0x22, 0x4B, 0x99, +0x00, 0x6B, 0x60, 0xC2, 0x6B, 0x99, 0x56, 0x99, 0x4B, 0xE3, 0xB9, 0x65, 0x15, 0x97, 0x14, 0x91, +0x0B, 0x63, 0x00, 0xEF, 0xFD, 0x63, 0x05, 0x62, 0x04, 0xD1, 0x04, 0x01, 0xA3, 0xD9, 0xC4, 0xD9, +0xE5, 0xD9, 0x82, 0xD9, 0x62, 0x99, 0x47, 0x41, 0x01, 0x4A, 0x04, 0x4A, 0x00, 0x6C, 0xA3, 0x67, +0xC2, 0x67, 0x00, 0x18, 0x89, 0x0F, 0xB9, 0x65, 0x01, 0x97, 0x00, 0x91, 0x01, 0x63, 0x00, 0xEF, +0xFD, 0x63, 0x05, 0x62, 0x04, 0xD1, 0x04, 0x01, 0x82, 0xD9, 0xC4, 0xD9, 0xE5, 0xD9, 0xA3, 0xD9, +0x63, 0x99, 0x47, 0x41, 0x05, 0x4A, 0x04, 0x4A, 0x82, 0x99, 0xA3, 0x67, 0xC2, 0x67, 0x00, 0x18, +0x89, 0x0F, 0xB9, 0x65, 0x01, 0x97, 0x00, 0x91, 0x01, 0x63, 0x00, 0xEF, +}; + + +u8 NFCFWDMEM[]={ +0x58, 0x53, 0x9B, 0x18, 0x22, 0xB6, 0x28, 0x80, 0x3C, 0x48, 0x00, 0x00, 0xE1, 0x11, 0x12, 0x00, +0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x93, 0x84, 0x00, 0x00, 0x9F, +0x08, 0x10, 0x00, 0x90, 0x1C, 0x10, 0x00, 0x90, 0x60, 0x20, 0x00, 0x90, 0x48, 0x20, 0x00, 0x90, +0x04, 0x20, 0x00, 0x90, 0x00, 0x20, 0x00, 0x90, 0x54, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0x0F, 0x00, +0x50, 0x00, 0x00, 0x94, 0x74, 0x00, 0x00, 0x94, 0x70, 0x01, 0x00, 0x94, 0x58, 0x00, 0x00, 0x94, +0xFF, 0xAF, 0x0E, 0x00, 0x1C, 0x04, 0x00, 0x94, 0x5C, 0x00, 0x00, 0x94, 0x08, 0x08, 0x00, 0x94, +0xFF, 0xFF, 0xFE, 0xFF, 0x6C, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0x03, 0xFC, 0x00, 0x00, 0x34, 0x00, +0x00, 0x00, 0x00, 0x20, 0x04, 0x04, 0x00, 0x94, 0x70, 0x00, 0x00, 0x94, 0x28, 0x00, 0x00, 0x94, +0x04, 0x00, 0x00, 0x90, 0x0C, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x90, 0x14, 0x00, 0x00, 0x90, +0x10, 0x10, 0x00, 0x90, 0x0C, 0x10, 0x00, 0x90, 0x24, 0x10, 0x00, 0x90, 0x20, 0x10, 0x00, 0x90, +0x54, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x9F, 0x0C, 0x00, 0x00, 0x9F, +0xFC, 0x00, 0x00, 0x9F, 0x64, 0x00, 0x00, 0x9F, 0x60, 0x00, 0x00, 0x9F, 0x7C, 0x00, 0x00, 0x90, +0x2C, 0x00, 0x00, 0x94, 0x14, 0x00, 0x00, 0x9F, 0xFF, 0xFF, 0xFF, 0xFE, 0x20, 0x01, 0x00, 0x94, +0x24, 0x01, 0x00, 0x94, 0x22, 0x00, 0x00, 0x93, 0x20, 0x00, 0x00, 0x93, 0x10, 0x00, 0x00, 0x93, +0x0C, 0x00, 0x00, 0x94, 0x0A, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x30, 0x0C, 0x00, 0x00, 0x93, +0x00, 0x10, 0x00, 0x90, 0x14, 0x10, 0x00, 0x90, 0xC4, 0x01, 0x64, 0xB8, 0x68, 0x65, 0x6C, 0x6C, +0x6F, 0x0A, 0x00, 0x00, 0x6F, 0x26, 0x00, 0x80, 0x9F, 0x26, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, +0x27, 0x27, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, +0x79, 0x28, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, 0x79, 0x28, 0x00, 0x80, +0x79, 0x28, 0x00, 0x80, 0xCF, 0x27, 0x00, 0x80, 0x4D, 0x26, 0x00, 0x80, 0x14, 0x00, 0x00, 0x9F, +0x12, 0x00, 0x00, 0x93, 0x14, 0x00, 0x00, 0x93, 0x16, 0x00, 0x00, 0x93, 0x10, 0x00, 0x00, 0x93, +0x26, 0x00, 0x00, 0x93, 0x5C, 0x00, 0x00, 0x94, 0x0C, 0x00, 0x00, 0x94, 0x08, 0x00, 0x00, 0x93, +0x0C, 0x00, 0x00, 0x93, 0x20, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x34, 0x12, 0x01, 0x00, 0x34, 0x12, +0x22, 0x00, 0x00, 0x93, 0x18, 0x00, 0x00, 0x93, 0xFC, 0x00, 0x00, 0x9F, 0x80, 0x00, 0x00, 0x9F, +0x84, 0x00, 0x00, 0x9F, 0x0C, 0x00, 0x00, 0x9F, 0x03, 0x01, 0xD0, 0x00, 0x50, 0x00, 0x00, 0x9F, +0xF2, 0x00, 0x00, 0xA0, 0x08, 0x00, 0x00, 0x9F, 0x10, 0x00, 0x00, 0x9F, 0x02, 0x01, 0x70, 0x00, +0x01, 0x01, 0x70, 0x00, 0x88, 0x00, 0x00, 0x9F, 0x6C, 0xB0, 0x6C, 0xB0, 0x24, 0x01, 0x00, 0x94, +0x00, 0x00, 0x20, 0x00, 0x2C, 0x00, 0x00, 0x94, 0x28, 0x00, 0x00, 0x94, 0x68, 0x01, 0x00, 0x94, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0xFF, 0x02, 0x00, 0x6C, 0x00, 0x00, 0x94, +0x00, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFD, 0xFF, 0x20, 0x01, 0x00, 0x94, 0x70, 0x00, 0x00, 0x94, +0x00, 0xD0, 0x80, 0x3D, 0x00, 0x90, 0x80, 0x3D, 0x5C, 0x00, 0x00, 0x9F, 0xFF, 0xFF, 0xFF, 0x00, +0x58, 0x00, 0x00, 0x9F, 0x25, 0x00, 0x01, 0x00, 0x24, 0x00, 0x01, 0x00, 0x68, 0x00, 0x00, 0x9F, +0x25, 0x64, 0x2E, 0x00, 0x25, 0x68, 0x68, 0x58, 0x3A, 0x00, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, +0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00, 0x00, 0x00, 0x00, +0x67, 0x66, 0x66, 0x66, +}; + + +//NFC_ADAPTER NFCAdapter; +IRQ_FUN LpPeriIrqFunTable[32]; +u32 LpPeriIrqDataTable[32]; + +VOID +WriteA2NMailbox( + IN VOID *pNFCAdapte +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER) pNFCAdapte; + u8 i = 0; + u32 RegTemp; + u32 TimeIdx = 0; + + if (!(pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQRIdx].Response)) { + pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQRIdx].Content[0] = + (pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQRIdx].Content[0]|(pNFCAdp->A2NSeq << 8)); + pNFCAdp->A2NSeq++; + } + + for(i = 0; i < pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQRIdx].Length; i++) { + HalDelayUs(30); + HAL_WRITE32(NFC_INTERFACE_BASE, 0x10, pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQRIdx].Content[i]); + } + + HalDelayUs(30); + RegTemp = HAL_READ32(NFC_INTERFACE_BASE,0x24)|BIT1; + HAL_WRITE32(NFC_INTERFACE_BASE, 0x24, RegTemp); + + RegTemp = (HAL_READ32(NFC_INTERFACE_BASE,0x24)&(~BIT1)); + HAL_WRITE32(NFC_INTERFACE_BASE, 0x24, RegTemp); +} + +VOID +A2NWriteInQueue( + IN VOID *pNFCAdapte, + IN VOID *pBuff +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER) pNFCAdapte; + PA2N_MAILBOX_Q pA2NWData = (PA2N_MAILBOX_Q) pBuff; + u8 Idx; + + //Q full handle + if ((pNFCAdp->A2NWQWIdx == (pNFCAdp->A2NWQRIdx - 1))|| + ((pNFCAdp->A2NWQRIdx == 0)&&(pNFCAdp->A2NWQWIdx == N2A_Q_LENGTH - 1))){ + + DBG_8195A("A2N write Mailbox Queue full !!\n"); + } + + for (Idx = 0; Idx < pA2NWData->Length; Idx++) { + pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQWIdx].Content[Idx] = pA2NWData->Content[Idx]; + } + + pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQWIdx].Length = pA2NWData->Length; + pNFCAdp->A2NMAILQ[pNFCAdp->A2NWQWIdx].Response = pA2NWData->Response; + pNFCAdp->A2NWQWIdx++; + + if (pNFCAdp->A2NWQWIdx == N2A_Q_LENGTH){ + pNFCAdp->A2NWQWIdx = 0; + } + + //check qu and enable task + if (pNFCAdp->A2NWQWIdx != pNFCAdp->A2NWQRIdx){ + pNFCAdp->A2NWMailBox = TRUE; + RtlUpSema(&(pNFCAdp->VeriSema)); + } + + + #if 0 + { + u8 i = 0; + u8 j = 0; + DBG_8195A("A2N idx = 0x%x \n", pNFCAdp->A2NWMailBox); + DBG_8195A("A2N write R idx = 0x%x \n", pNFCAdp->A2NWQRIdx); + DBG_8195A("A2N write W idx = 0x%x \n", pNFCAdp->A2NWQWIdx); + + for(i = 0;iA2NWQWIdx;i++) { + + DBG_8195A("A2N write queue %d, length = 0x%x \n", i, pNFCAdp->A2NMAILQ[i].Length); + DBG_8195A("A2N write queue %d, response = 0x%x \n", i, pNFCAdp->A2NMAILQ[i].Response); + for(j = 0;j < 5; j++) { + DBG_8195A("A2N write queue %d, data = 0x%x \n", i, pNFCAdp->A2NMAILQ[i].Content[j]); + } + } + } + #endif +} + +VOID +A2NWriteDeQueue( + IN VOID *pNFCAdapte +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER) pNFCAdapte; + u32 TimeIdx = 0; + + while(((HAL_READ32(NFC_INTERFACE_BASE, 0x14)>>1) & 0xf)!= 0){ + DBG_8195A("A2N Mailbox W MISC 0x%08x\n", ((HAL_READ32(NFC_INTERFACE_BASE, 0x14)>>1) & 0xf)); + HalDelayUs(30); + TimeIdx++; + if (TimeIdx > 10000){ + + DBG_8195A("A2N Mailbox write timeout\n"); + + //check qu and enable task + if (pNFCAdp->A2NWQWIdx != pNFCAdp->A2NWQRIdx){ + pNFCAdp->A2NWMailBox = TRUE; + RtlUpSema(&(pNFCAdp->VeriSema)); + } + return; + } + }; + + WriteA2NMailbox(pNFCAdapte); + + pNFCAdp->A2NWQRIdx++; + if (pNFCAdp->A2NWQRIdx == N2A_Q_LENGTH) { + pNFCAdp->A2NWQRIdx = 0; + } + + //check qu and enable task + if (pNFCAdp->A2NWQWIdx != pNFCAdp->A2NWQRIdx){ + pNFCAdp->A2NWMailBox = TRUE; + RtlUpSema(&(pNFCAdp->VeriSema)); + } + else { + pNFCAdp->A2NWMailBox = FALSE; + } +} + + +//cmd 0 +VOID +N2AReadTag( + IN VOID *pNFCAdapte, + IN u8 N2ARPage +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + + nfc_tagread_callback (pNFCAdp, N2ARPage); +} + +VOID +A2NWriteCatch( + IN VOID *pNFCAdapte, + IN u8 N2AWPage, + IN u8 Length, + IN u32 *WData +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + u8 Idx; + A2N_MAILBOX_Q DataTemp; + + DataTemp.Length = Length+1; + DataTemp.Response = 0; + DataTemp.Content[0] = (CATCH_WRITE|(DataTemp.Length<<5)|(N2AWPage<<16)); + for (Idx = 0; Idx < Length; Idx++) { + DataTemp.Content[Idx+1] = WData[Idx]; + } + A2NWriteInQueue(pNFCAdp, &DataTemp); +} + + +VOID +A2NReadCatch( + IN VOID *pNFCAdapte, + IN u8 A2NRPage +) +{ + u8 Idx; + A2N_MAILBOX_Q DataTemp; + + DataTemp.Length = 1; + DataTemp.Response = 0; + DataTemp.Content[0] = (CATCH_READ|(DataTemp.Length<<5)|(A2NRPage<<16)); + + A2NWriteInQueue((PNFC_ADAPTER)pNFCAdapte, &DataTemp); +} + +//cmd 1 +VOID +N2AWriteTag( + IN VOID *pNFCAdapte, + IN u8 N2AWPage +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + u32 wr_data; + + wr_data = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + nfc_tagwrite_callback(pNFCAdp, N2AWPage, wr_data); +} + + +//cmd 3 +VOID +N2AReadCatch( + IN VOID *pNFCAdapte, + IN u8 N2ACatchRPage +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + u32 TagBuf[4]; + + TagBuf[0] = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + TagBuf[1] = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + TagBuf[2] = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + TagBuf[3] = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + + nfc_cache_read_callback(pNFCAdp, N2ACatchRPage, TagBuf); +} + +//cmd 4 +VOID +NFCReaderPresent( + IN VOID *pNFCAdapte, + IN u8 State +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + + if (State) { + DBG_8195A("NFC Reader Present\n"); + + //call app + nfc_event_callback(pNFCAdp, NFC_HAL_READER_PRESENT); + } +} + + +//cmd 5 +VOID +N2AMailboxState( + IN VOID *pNFCAdapte, + IN u8 State, + IN u8 Seq +) +{ + A2N_MAILBOX_Q MailData; + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + + pNFCAdp->N2ABoxOpen = State; + DBG_8195A("N2A Mailbox State = %d\n", pNFCAdp->N2ABoxOpen); + + if (State) { + MailData.Length = 1; + MailData.Response = 1; + MailData.Content[0] = (CONFIRM_N2A_BOX_STATE)|(0x1<<5)|((Seq|BIT7)<<8)|(ON<<16); + //WriteA2NMailbox(1, &MailData, 1); + A2NWriteInQueue(pNFCAdp, &MailData); + } +} + + +//cmd 6 +VOID +NFC25MClkReq( + IN VOID *pNFCAdapte, + IN u8 OP, + IN u8 Seq +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + A2N_MAILBOX_Q MailData; + u32 RegTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1); + if (OP) { + RegTemp = ((RegTemp & 0xFFFFC7FF)|(BIT_SYS_SYSPLL_CKSDR_EN|BIT_SYS_SYSPLL_CKSDR_DIV(2))); + } + else { + RegTemp = RegTemp & 0xFFFFC7FF; + } + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1, RegTemp); + + //write mailbox + MailData.Length = 1; + MailData.Response = 1; + MailData.Content[0] = (EXT_CLK_RSP)|(0x1<<5)|((Seq|BIT7)<<8)|(ON<<16); + //WriteA2NMailbox(pNFCAdp, &MailData); + A2NWriteInQueue(pNFCAdp, &MailData); +} + + +VOID +NFCRoutine( + IN VOID *pNFCAdapte +) +{ + u32 N2ARData, N2AMISC; + u8 N2ARB2; + u8 Seq; + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + + //N2A MEM DATA + if (pNFCAdp->NFCIsr & (BIT0|BIT6)) { + pNFCAdp->NFCIsr &= ~(BIT6|BIT0); + + N2AMISC = ((HAL_READ32(NFC_INTERFACE_BASE, 0x1c)>>1)&0xf); + //DBG_8195A("NFC 0x1C = 0x%x \n", N2AMISC); + while (N2AMISC != 0){ + + N2ARData = HAL_READ32(NFC_INTERFACE_BASE, 0x18); + N2ARB2 = ((u8)(N2ARData>>16)); + Seq = (u8)(N2ARData>>8); + //DBG_8195A("NFC 0x18 = 0x%x \n", N2ARData); + + switch(N2ARData & 0x1F) { + case TAG_READ: + //get data + N2AReadTag(pNFCAdapte, N2ARB2); + //call app + + break; + case TAG_WRITE: + //get data + N2AWriteTag(pNFCAdapte, N2ARB2); + //call app + + break; + case CATCH_READ_DATA: + //get data + N2AReadCatch(pNFCAdapte, N2ARB2); + //call app + + break; + case NFC_R_PRESENT: + NFCReaderPresent(pNFCAdapte, N2ARB2); + break; + case N2A_MAILBOX_STATE: + N2AMailboxState(pNFCAdapte, N2ARB2, Seq); + break; + case EXT_CLK_REQ: + NFC25MClkReq(pNFCAdapte, N2ARB2, Seq); + break; + default: + break; + } + + N2AMISC = ((HAL_READ32(NFC_INTERFACE_BASE, 0x1c)>>1)&0xf); + } + } + + if(pNFCAdp->A2NWMailBox){ + A2NWriteDeQueue(pNFCAdp); + } + //enable int + //A2NWRITE32(0x68, 0xff00); +} + + +VOID +NFCTaskHandle( + IN VOID *pNFCAdapte +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + + #if !TASK_SCHEDULER_DISABLED//#if !TASK_SCHEDULER_DISABLED(>>) + for (;;)//start of for(;;) + { + //4 waiting for start command + RtlDownSema(&(pNFCAdp->VeriSema)); + + if (pNFCAdp->TaskStop) { + break; // task stopping, break the for loop + } + NFCRoutine(pNFCAdapte); + + }//end of for(;;) + + pNFCAdp->TaskStop = 0; +#if ( INCLUDE_vTaskDelete == 1 ) + vTaskDelete(NULL); +#endif + #endif +} + +VOID +LPIrqHandle( + IN VOID *pNFCAdapte +) +{ + u32 LpIrqStatus, CheckIndex, ExactIrqStatus, TableIndex; + + //DBG_8195A("Enter ISR\n"); + + LpIrqStatus = HAL_READ32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_STATUS); + + //Save exact IRQ status + ExactIrqStatus = LpIrqStatus & + HAL_READ32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_EN); + + //Check exact IRQ function + for(CheckIndex = 0;CheckIndex<32;CheckIndex++) { + if (ExactIrqStatus & BIT_(CheckIndex)) { + TableIndex = CheckIndex; + LpPeriIrqFunTable[TableIndex]((VOID *)(LpPeriIrqDataTable[TableIndex])); + } + } + + //Clear sub-rout IRQ + HAL_WRITE32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_STATUS, LpIrqStatus); + +} + +VOID +NFCIrqHandle( + IN VOID *pNFCAdapte +) +{ + PNFC_ADAPTER pNFCAdp = (PNFC_ADAPTER)pNFCAdapte; + u32 ISR = HalNFCRead32(0x68); + pNFCAdp->NFCIsr = (((u8)((ISR&0xbf00)>>8))&((u8)ISR)); + //DBG_8195A("A2N 0x68 = 0x%x\n", ISR); + + //stop int + //ISR = ISR&0xff; + + HalNFCWrite32(0x68, ISR); + + //DBG_8195A("After clean ISR A2N 0x68 = 0x%x\n", A2NREAD32(0x68)); + RtlUpSema(&(pNFCAdp->VeriSema)); +} + +VOID HalNFCDmemInit( + IN u32 *pTagData, + IN u32 TagLen +) +{ + if (pTagData == NULL) { + return; + } + + u32 pgidx, dmemidx; + for (pgidx = 0, dmemidx = 0; pgidx> 0); + NFCFWDMEM[dmemidx++] = (u8)((pTagData[pgidx] & 0x0000FF00) >> 8); + NFCFWDMEM[dmemidx++] = (u8)((pTagData[pgidx] & 0x00FF0000) >> 16); + NFCFWDMEM[dmemidx++] = (u8)((pTagData[pgidx] & 0xFF000000) >> 24); + } +} + +VOID +HalNFCInit( + PNFC_ADAPTER pNFCAdp +) +{ + u32 Rtemp = 0; + + _memset(pNFCAdp, 0, sizeof(NFC_ADAPTER)); + + //Enable NFC clk 0x244[17:16] = 3 + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_PESOC_COM_CLK_CTRL1)|0x00030000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_PESOC_COM_CLK_CTRL1, Rtemp); + + //Enable A33 interface + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //Enabel NFC and release IOS33_Ameba + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009401); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //polling ISO33_NFC 0x134 [0] = 0 + while ( (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PERI_MONITOR)&BIT0) != 0 ){}; + + //DBG_8195A("NFC Initialization Finish\n"); + + //CLK 25M + { + u32 RegTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1); + + RegTemp = ((RegTemp & 0xFFFFC7FF)|(BIT_SYS_SYSPLL_CKSDR_EN|BIT_SYS_SYSPLL_CKSDR_DIV(2))); + //RegTemp = ((RegTemp & 0xFFFFC7FF)|BIT13|BIT12); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1, RegTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, 0x244, HAL_READ32(SYSTEM_CTRL_BASE, 0x244)|BIT18); + } + + + //Init INT + { + IRQ_HANDLE NfcHandle; + NfcHandle.Data = (u32) (pNFCAdp); + NfcHandle.IrqNum = LP_EXTENSION_IRQ;//NFC_IRQ; + NfcHandle.IrqFun = (IRQ_FUN) LPIrqHandle; + NfcHandle.Priority = 3; + + InterruptRegister(&NfcHandle); + InterruptEn(&NfcHandle); + + LpPeriIrqFunTable[1] = (IRQ_FUN)((u32)NFCIrqHandle | 0x1); + + LpPeriIrqDataTable[1] = (u32)(pNFCAdp); + //level trigger + HAL_WRITE32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_MODE,0); + //enable imr + HAL_WRITE32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_EN, + (HAL_READ32(VENDOR_REG_BASE, LP_PERI_EXT_IRQ_EN) | BIT1)); + HalNFCWrite32(0x68, 0xBF00); + + } + + #if !TASK_SCHEDULER_DISABLED + { + u32 NFCTmpSts; + //create task + RtlInitSema(&(pNFCAdp->VeriSema),0); + NFCTmpSts = xTaskCreate( NFCTaskHandle, (const signed char *)"NFC_TASK", + ((1024*4)/sizeof(portBASE_TYPE)), (void *)pNFCAdp, 1, &(pNFCAdp->NFCTask)); + } + #endif + +} + +VOID +HalNFCDeinit( + PNFC_ADAPTER pNFCAdp +) +{ + u32 i; + u32 Rtemp = 0; + + pNFCAdp->TaskStop = 1; + RtlUpSema(&(pNFCAdp->VeriSema)); + // wait sometime for the task be stooped + for(i=0;i<1000;i++) { + if (pNFCAdp->TaskStop == 0) { + break; + } + else { + RtlMsleepOS(100); + } + } + //4 free the task semaphore + RtlFreeSema(&(pNFCAdp->VeriSema)); + _memset(pNFCAdp, 0, sizeof(NFC_ADAPTER)); + + //4 Disable NFC clk + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_PESOC_COM_CLK_CTRL1)&0xFFFCFFFF; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_PESOC_COM_CLK_CTRL1, Rtemp); + + //4 Enable A33 interface + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009404); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); +} + + +u32 +HalNFCRead32( + IN u32 Addr +) +{ + //byte0: test type, byte1: Base address, byte2: offset + + u32 Rtemp = 0; + u32 Idxtemp = 0; + + //full addr + HAL_WRITE32(NFC_INTERFACE_BASE, 0x0, (0x1f000000+Addr)); + //R_cmd + HAL_WRITE32(NFC_INTERFACE_BASE, 0x4, 0x2f); + #if 1 + //polling 0x4[7]=1 + while(1) { + if( (HAL_READ32(NFC_INTERFACE_BASE, 0x4)&BIT7) == 0 ){ + Idxtemp++; + if((Idxtemp)%10000 == 0) { + DBG_8195A("Idxtemp: 0x%x\n", Idxtemp); + } + if (Idxtemp > 0x0fffff) { + DBG_8195A("A2N_OCP_MISC_R_IN_WHILE: 0x%x\r\n", HAL_READ32(NFC_INTERFACE_BASE, 0x4)); + DBG_8195A("Read FAIL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1\r\n"); + return 0; + } + } + else { + //DBG_8195A("A2N_OCP_MISC_R_IN_WHILE: 0x%x\n", HAL_READ32(NFC_INTERFACE_BASE, 0x4)); + HAL_WRITE32(NFC_INTERFACE_BASE, 0x4, BIT7); + Rtemp = HAL_READ32(NFC_INTERFACE_BASE, 0xc); + break; + } + } + #endif + //DBG_8195A("A2N_MEM_MISC: 0x%x\n", Rtemp); + return Rtemp; +} + +VOID +HalNFCWrite32( + IN u32 Addr, + IN u32 Data +) +{ + //byte0: test type, byte1: Base address, byte2: offset, byte3:value + u32 Idxtemp = 0; + + //full addr + HAL_WRITE32(NFC_INTERFACE_BASE, 0x0, 0x1f000000+Addr); + //full data + HAL_WRITE32(NFC_INTERFACE_BASE, 0x8, Data); + //W_cmd + HAL_WRITE32(NFC_INTERFACE_BASE, 0x4, 0x1f); + + //polling 0x4[7]=1 + while(1) { + if( (HAL_READ32(NFC_INTERFACE_BASE, 0x4)&BIT7) == 0 ){ + Idxtemp++; + if((Idxtemp)%10000 == 0) { + DBG_8195A("Idxtemp: 0x%x\n", Idxtemp); + } + if (Idxtemp > 0x0fffff) { + DBG_8195A("A2N_OCP_MISC_R_IN_WHILE: 0x%x\r\n", HAL_READ32(NFC_INTERFACE_BASE, 0x4)); + DBG_8195A("write FAIL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1\r\n"); + return; + } + } + else { + //DBG_8195A("A2N_OCP_MISC_W_IN_WHILE: 0x%x\n", HAL_READ32(NFC_INTERFACE_BASE, 0x4)); + HAL_WRITE32(NFC_INTERFACE_BASE, 0x4, BIT7); + break; + } + } +} + +VOID +HalNFCFwFullMEM( + IN VOID *Data, + IN u32 NFCFwLength +) +{ + u32 *NFCFW; + NFCFW = (u32*) Data; + u32 Idx, NFCFwTemp, RTemp; + u8 Temp = (NFCFwLength%4); + u32 IdxTemp = ((NFCFwLength-Temp)/4); + u8 MEMTemp = 0; + u8 MEMCnt = 0; + + u32 FWChkSum = 0; + + HalNFCWrite32(0x4C, 0x0); + + //write fw to MEM + for (Idx = 0; Idx < IdxTemp; Idx++ ) { + + NFCFwTemp = *(NFCFW+Idx); + HAL_WRITE32(NFC_INTERFACE_BASE, 0x10, NFCFwTemp); + + //cal chksum + FWChkSum ^= NFCFwTemp; + MEMTemp++; + HalDelayUs(1); + + if (MEMTemp == 8) { + + MEMTemp = 0; + + //check mem empty + while(1){ + + RTemp = HAL_READ32(NFC_INTERFACE_BASE, 0x14); + MEMCnt = (((u8)(RTemp & 0x1e)) >> 1); + + if ( RTemp & BIT6) { + break; + } + else { + } + } + } + } + + HalDelayUs(200); + + while(1){ + RTemp = HAL_READ32(NFC_INTERFACE_BASE, 0x14); + if ( RTemp & BIT6) { + break; + } + else { + } + } + + //write chksum + HAL_WRITE32(NFC_INTERFACE_BASE, 0x10, FWChkSum); + + while(1){ + RTemp = HAL_READ32(NFC_INTERFACE_BASE, 0x14); + if ( RTemp & BIT6) { + break; + } + else { + } + } +} + + +VOID +HalNFCFwDownload( + VOID +) +{ + u32 Rtemp = 0; + + //switch free run clock from 500K to 80M + HalNFCWrite32(0x88, 0xf06cf06c); + + //2 Download NFC FW + //3 Download IMEM + //setting fw download + HalNFCWrite32(0x48, 0x1); + + //reset NFC CPU + HalNFCWrite32(0x20, 0x1234FFFF); + + //switch IMEM + HAL_WRITE32(NFC_INTERFACE_BASE, 0x14, 0); + + //write fw to MEM + HalNFCFwFullMEM(NFCFWIMEM,sizeof(NFCFWIMEM)); + HalDelayUs(100); + + Rtemp = HalNFCRead32(0x48); + if(Rtemp & BIT1) { + //DBG_8195A("NFC FW Download IMEM SUCCESS \n"); + } + else { + DBG_8195A("NFC FW Download IMEM FAIL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n"); + return; + } + + //3 Download DMEM + //setting fw download + HalNFCWrite32(0x48, 0x1); + + //switch DMEM + HAL_WRITE32(NFC_INTERFACE_BASE, 0x14, 1); + + //write fw to MEM + HalNFCFwFullMEM(NFCFWDMEM,sizeof(NFCFWDMEM)); + + HalDelayUs(100); + Rtemp = HalNFCRead32(0x48); + if(Rtemp & BIT1) { + //DBG_8195A("NFC FW Download DMEM SUCCESS \n"); + } + else { + DBG_8195A("NFC FW Download DMEM FAIL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n"); + return; + } + + //Reset CPU + HalNFCWrite32(0x48, 0x80); + + DBG_8195A("NFC REBOOT SUCCESS \n"); +} + +#endif //CONFIG_NFC_EN diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pcm.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pcm.c new file mode 100644 index 0000000..abf6b18 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pcm.c @@ -0,0 +1,360 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "platform_autoconf.h" +#include "diag.h" +#include "rtl8195a_pcm.h" +#include "hal_pcm.h" + +extern void * +_memset( void *s, int c, SIZE_T n ); + +VOID +HalPcmOnOffRtl8195a ( + IN VOID *Data +) +{ + PHAL_PCM_ADAPTER pHalPcmAdapter = (PHAL_PCM_ADAPTER) Data; + //todo on off pcm + +} + +//default sampling rate 8khz, linear, 10ms frame size, time slot 0 , tx+rx +// master mode, enable endian swap +// Question: need local tx/rx page? +BOOL +HalPcmInitRtl8195a( + IN VOID *Data +) +{ + + PHAL_PCM_ADAPTER pHalPcmAdapter = (PHAL_PCM_ADAPTER) Data; + _memset((void *)pHalPcmAdapter, 0, sizeof(HAL_PCM_ADAPTER)); + + //4 1) Initial PcmChCNR03 Register + pHalPcmAdapter->PcmChCNR03.CH0MuA = 0; + pHalPcmAdapter->PcmChCNR03.CH0Band = 0; + + + //4 1) Initial PcmTSR03 Register + pHalPcmAdapter->PcmTSR03.CH0TSA = 0; + + //4 1) Initial PcmBSize03 Register + pHalPcmAdapter->PcmBSize03.CH0BSize = 39; // 40word= 8khz*0.01s*1ch*2byte/4byte + + + //4 2) Initial Ctl Register + + pHalPcmAdapter->PcmCtl.Pcm_En = 1; + pHalPcmAdapter->PcmCtl.SlaveMode = 0; + pHalPcmAdapter->PcmCtl.FsInv = 0; + pHalPcmAdapter->PcmCtl.LinearMode = 0; + pHalPcmAdapter->PcmCtl.LoopBack = 0; + pHalPcmAdapter->PcmCtl.EndianSwap = 1; + + return _TRUE; +} + + +BOOL +HalPcmSettingRtl8195a( + IN VOID *Data +) +{ + + PHAL_PCM_ADAPTER pHalPcmAdapter = (PHAL_PCM_ADAPTER) Data; + u8 PcmIndex = pHalPcmAdapter->PcmIndex; + u8 PcmCh = pHalPcmAdapter->PcmCh; + u32 RegCtl, RegChCNR03, RegTSR03, RegBSize03; + u32 Isr03; + + PcmCh=0; + //4 1) Check Pcm index is avaliable + if (HAL_PCMX_READ32(PcmIndex, REG_PCM_CHCNR03) & (BIT24|BIT25)) { + //4 Pcm index is running, stop first + DBG_8195A_DMA("Error, PCM %d ch%d is running; stop first!\n", PcmIndex, PcmCh); + + return _FALSE; + } + + //4 2) Check if there are the pending isr + + + Isr03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_ISR03); + Isr03 &= 0xff000000; + //4 Clear Pending Isr + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_ISR03, Isr03); + //} + + + //4 3) Process RegCtl + RegCtl = HAL_PCMX_READ32(PcmIndex, REG_PCM_CTL); + + //4 Clear Ctl register bits + RegCtl &= ( BIT_INV_CTLX_SLAVE_SEL & + BIT_INV_CTLX_FSINV & + BIT_INV_CTLX_PCM_EN & + BIT_INV_CTLX_LINEARMODE & + BIT_INV_CTLX_LOOP_BACK & + BIT_INV_CTLX_ENDIAN_SWAP); + + RegCtl = BIT_CTLX_SLAVE_SEL(pHalPcmAdapter->PcmCtl.SlaveMode) | + BIT_CTLX_FSINV(pHalPcmAdapter->PcmCtl.FsInv) | + BIT_CTLX_PCM_EN(pHalPcmAdapter->PcmCtl.Pcm_En) | + BIT_CTLX_LINEARMODE(pHalPcmAdapter->PcmCtl.LinearMode) | + BIT_CTLX_LOOP_BACK(pHalPcmAdapter->PcmCtl.LoopBack) | + BIT_CTLX_ENDIAN_SWAP(pHalPcmAdapter->PcmCtl.EndianSwap) | + RegCtl; + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_CTL, RegCtl); + //4 4) Program ChCNR03 Register + + RegChCNR03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_CHCNR03); + + RegChCNR03 &= (BIT_INV_CHCNR03_CH0RE & + BIT_INV_CHCNR03_CH0TE & + BIT_INV_CHCNR03_CH0MUA & + BIT_INV_CHCNR03_CH0BAND); + + RegChCNR03 = BIT_CHCNR03_CH0RE(pHalPcmAdapter->PcmChCNR03.CH0RE) | + BIT_CHCNR03_CH0TE(pHalPcmAdapter->PcmChCNR03.CH0TE) | + BIT_CHCNR03_CH0MUA(pHalPcmAdapter->PcmChCNR03.CH0MuA) | + BIT_CHCNR03_CH0BAND(pHalPcmAdapter->PcmChCNR03.CH0Band) | + RegChCNR03; + + DBG_8195A_DMA("RegChCNR03 data:0x%x\n", RegChCNR03); + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_CHCNR03, RegChCNR03); + // time slot + RegTSR03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_TSR03); + + RegTSR03 &= (BIT_INV_TSR03_CH0TSA); + RegTSR03 = BIT_TSR03_CH0TSA(pHalPcmAdapter->PcmTSR03.CH0TSA) | + RegTSR03; + + DBG_8195A_DMA("RegTSR03 data:0x%x\n", RegTSR03); + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_TSR03, RegTSR03); + + // buffer size + RegBSize03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_BSIZE03); + + RegBSize03 &= (BIT_INV_BSIZE03_CH0BSIZE); + RegBSize03 = BIT_BSIZE03_CH0BSIZE(pHalPcmAdapter->PcmBSize03.CH0BSize) | + RegBSize03; + + DBG_8195A_DMA("RegBSize03 data:0x%x\n", RegBSize03); + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_BSIZE03, RegBSize03); + + + + + return _TRUE; +} + +BOOL +HalPcmEnRtl8195a( + IN VOID *Data +) +{ + + PHAL_PCM_ADAPTER pHalPcmAdapter = (PHAL_PCM_ADAPTER) Data; + u8 PcmIndex = pHalPcmAdapter->PcmIndex; + u8 PcmCh = pHalPcmAdapter->PcmCh; + u32 RegChCNR03; + + PcmCh=0; + pHalPcmAdapter->Enable = 1; + + + //4 1) Check Pcm index is avaliable + RegChCNR03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_CHCNR03); + if (RegChCNR03 & (BIT24|BIT25)) { + //4 Pcm index is running, stop first + DBG_8195A_DMA("Error, PCM %d ch%d is running; stop first!\n", PcmIndex, PcmCh); + + return _FALSE; + } + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_CHCNR03, RegChCNR03|BIT24|BIT25); + pHalPcmAdapter->PcmChCNR03.CH0RE = 1; + pHalPcmAdapter->PcmChCNR03.CH0TE = 1; + + return _TRUE; +} + +BOOL +HalPcmDisRtl8195a( + IN VOID *Data +) +{ + + PHAL_PCM_ADAPTER pHalPcmAdapter = (PHAL_PCM_ADAPTER) Data; + u8 PcmIndex = pHalPcmAdapter->PcmIndex; + u8 PcmCh = pHalPcmAdapter->PcmCh; + u32 RegChCNR03; + + PcmCh=0; + pHalPcmAdapter->Enable = 0; + + + RegChCNR03 = HAL_PCMX_READ32(PcmIndex, REG_PCM_CHCNR03); + + HAL_PCMX_WRITE32(PcmIndex, REG_PCM_CHCNR03, RegChCNR03&(~(BIT24|BIT25))); + pHalPcmAdapter->PcmChCNR03.CH0RE = 0; + pHalPcmAdapter->PcmChCNR03.CH0TE = 0; + + return _TRUE; +} + + + +BOOL +HalPcmIsrEnAndDisRtl8195a ( + IN VOID *Data +) +{ +/* + PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + u32 IsrMask, Addr, IsrCtrl; + u8 IsrTypeIndex = 0; + + for (IsrTypeIndex=0; IsrTypeIndex<5; IsrTypeIndex++) { + + if (BIT_(IsrTypeIndex) & pHalGdmaAdapter->GdmaIsrType) { + Addr = (REG_GDMA_MASK_INT_BASE + IsrTypeIndex*8); + + IsrMask = HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, Addr); + + IsrCtrl = ((pHalGdmaAdapter->IsrCtrl)?(pHalGdmaAdapter->ChEn | IsrMask): + ((~pHalGdmaAdapter->ChEn) & IsrMask)); + + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + Addr, + IsrCtrl + ); + + } + } +*/ + return _TRUE; +} + + + +BOOL +HalPcmDumpRegRtl8195a ( + IN VOID *Data +) +{ +/* + PHAL_GDMA_ADAPTER pHalGdmaAdapter = Data; + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + REG_GDMA_CH_EN, + (HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, REG_GDMA_CH_EN)| + (pHalGdmaAdapter->ChEn)) + ); +*/ + return _TRUE; +} + +BOOL +HalPcmRtl8195a ( + IN VOID *Data +) +{ +/* PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + REG_GDMA_CH_EN, + (HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, REG_GDMA_CH_EN)& + ~(pHalGdmaAdapter->ChEn)) + ); +*/ + return _TRUE; +} +/* +u8 +HalGdmaChIsrCleanRtl8195a ( + IN VOID *Data +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + u32 IsrStatus; + u8 IsrTypeIndex = 0, IsrActBitMap = 0; + + for (IsrTypeIndex=0; IsrTypeIndex<5; IsrTypeIndex++) { + + IsrStatus = HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_RAW_INT_BASE + IsrTypeIndex*8)); + +// DBG_8195A_DMA("Isr Type %d: Isr Status 0x%x\n", IsrTypeIndex, IsrStatus); + + IsrStatus = (IsrStatus & (pHalGdmaAdapter->ChEn & 0xFF)); + + if (BIT_(IsrTypeIndex) & pHalGdmaAdapter->GdmaIsrType) { + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_CLEAR_INT_BASE+ (IsrTypeIndex*8)), + (IsrStatus)// & (pHalGdmaAdapter->ChEn & 0xFF)) + ); + IsrActBitMap |= BIT_(IsrTypeIndex); + + } + + } + return IsrActBitMap; + +} + + +VOID +HalGdmaChCleanAutoSrcRtl8195a ( + IN VOID *Data +) +{ + u32 CfgxLow; + PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + CfgxLow = HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF)); + + CfgxLow &= BIT_INVC_CFGX_LO_RELOAD_SRC; + + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF), + CfgxLow + ); + + DBG_8195A_DMA("CFG Low data:0x%x\n", + HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF))); +} + +VOID +HalGdmaChCleanAutoDstRtl8195a ( + IN VOID *Data +) +{ + u32 CfgxLow; + PHAL_GDMA_ADAPTER pHalGdmaAdapter = (PHAL_GDMA_ADAPTER) Data; + CfgxLow = HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF)); + + CfgxLow &= BIT_INVC_CFGX_LO_RELOAD_DST; + + HAL_GDMAX_WRITE32(pHalGdmaAdapter->GdmaIndex, + (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF), + CfgxLow + ); + DBG_8195A_DMA("CFG Low data:0x%x\n", + HAL_GDMAX_READ32(pHalGdmaAdapter->GdmaIndex, (REG_GDMA_CH_CFG + pHalGdmaAdapter->ChNum*REG_GDMA_CH_OFF))); + +} +*/ + + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c new file mode 100644 index 0000000..dd5163e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c @@ -0,0 +1,219 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "hal_peri_on.h" + +#ifdef CONFIG_PWM_EN +#include "rtl8195a_pwm.h" +#include "hal_pwm.h" + +extern HAL_PWM_ADAPTER PWMPin[]; + +extern HAL_TIMER_OP HalTimerOp; + +/** + * @brief Configure a G-Timer to generate a tick with certain time. + * + * @param pwm_id: the PWM pin index + * @param tick_time: the time (micro-second) of a tick + * + * @retval None + */ +void +Pwm_SetTimerTick_8195a( + HAL_PWM_ADAPTER *pPwmAdapt, + u32 tick_time +) +{ + TIMER_ADAPTER TimerAdapter; + + + if (tick_time <= MIN_GTIMER_TIMEOUT) { + tick_time = MIN_GTIMER_TIMEOUT; + } + else { + tick_time = (((tick_time-1)/TIMER_TICK_US)+1) * TIMER_TICK_US; + } + + // Initial a G-Timer for the PWM pin + if (pPwmAdapt->tick_time != tick_time) { + TimerAdapter.IrqDis = 1; // Disable Irq + TimerAdapter.IrqHandle.IrqFun = (IRQ_FUN) NULL; + TimerAdapter.IrqHandle.IrqNum = TIMER2_7_IRQ; + TimerAdapter.IrqHandle.Priority = 0; + TimerAdapter.IrqHandle.Data = (u32)NULL; + TimerAdapter.TimerId = pPwmAdapt->gtimer_id; + TimerAdapter.TimerIrqPriority = 0; + TimerAdapter.TimerLoadValueUs = tick_time-1; + TimerAdapter.TimerMode = 1; // auto-reload with user defined value + + HalTimerOp.HalTimerInit((VOID*) &TimerAdapter); + pPwmAdapt->tick_time = tick_time; + DBG_PWM_INFO("%s: Timer_Id=%d Count=%d\n", __FUNCTION__, pPwmAdapt->gtimer_id, tick_time); + } + +} + + +/** + * @brief Set the duty ratio of the PWM pin. + * + * @param pwm_id: the PWM pin index + * @param period: the period time, in micro-second. + * @param pulse_width: the pulse width time, in micro-second. + * + * @retval None + */ +void +HAL_Pwm_SetDuty_8195a( + HAL_PWM_ADAPTER *pPwmAdapt, + u32 period, + u32 pulse_width +) +{ + u32 RegAddr; + u32 RegValue; + u32 period_tick; + u32 pulsewidth_tick; + u32 tick_time; + u8 timer_id; + u8 pwm_id; + + pwm_id = pPwmAdapt->pwm_id; + // Adjust the tick time to a proper value + if (period < (MIN_GTIMER_TIMEOUT*2)) { + DBG_PWM_ERR ("HAL_Pwm_SetDuty_8195a: Invalid PWM period(%d), too short!!\n", period); + tick_time = MIN_GTIMER_TIMEOUT; + period = MIN_GTIMER_TIMEOUT*2; + } + else { + tick_time = period / 0x3fc; + if (tick_time < MIN_GTIMER_TIMEOUT) { + tick_time = MIN_GTIMER_TIMEOUT; + } + } + + Pwm_SetTimerTick_8195a(pPwmAdapt, tick_time); + tick_time = pPwmAdapt->tick_time; +#if 0 + // Check if current tick time needs adjustment + if ((pPwmAdapt->tick_time << 12) <= period) { + // need a longger tick time + } + else if ((pPwmAdapt->tick_time >> 2) >= period) { + // need a shorter tick time + } +#endif + period_tick = period/tick_time; + if (period_tick == 0) { + period_tick = 1; + } + + if (pulse_width >= period) { +// pulse_width = period-1; + pulse_width = period; + } + pulsewidth_tick = pulse_width/tick_time; + if (pulsewidth_tick == 0) { +// pulsewidth_tick = 1; + } + + timer_id = pPwmAdapt->gtimer_id; + + pPwmAdapt->period = period_tick & 0x3ff; + pPwmAdapt->pulsewidth = pulsewidth_tick & 0x3ff; + + RegAddr = REG_PERI_PWM0_CTRL + (pwm_id*4); + RegValue = BIT31 | (timer_id<<24) | (pulsewidth_tick<<12) | period_tick; + + HAL_WRITE32(PERI_ON_BASE, RegAddr, RegValue); +} + +/** + * @brief Initializes and enable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * @param sel: pin mux selection + * @param timer_id: the G-timer index assigned to this PWM + * + * @retval HAL_Status + */ +HAL_Status +HAL_Pwm_Init_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +) +{ + u32 pwm_id; + u32 pin_sel; + + pwm_id = pPwmAdapt->pwm_id; + pin_sel = pPwmAdapt->sel; + // Initial a G-Timer for the PWM pin + Pwm_SetTimerTick_8195a(pPwmAdapt, MIN_GTIMER_TIMEOUT); + + // Set default duty ration + HAL_Pwm_SetDuty_8195a(pPwmAdapt, 20000, 10000); + + // Configure the Pin Mux + PinCtrl((PWM0+pwm_id), pin_sel, 1); + + return HAL_OK; +} + + +/** + * @brief Enable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * + * @retval None + */ +void +HAL_Pwm_Enable_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +) +{ + u32 pwm_id; + + pwm_id = pPwmAdapt->pwm_id; + // Configure the Pin Mux + if (!pPwmAdapt->enable) { + PinCtrl((PWM0+pwm_id), pPwmAdapt->sel, 1); + HalTimerOp.HalTimerEn(pPwmAdapt->gtimer_id); + pPwmAdapt->enable = 1; + } +} + + +/** + * @brief Disable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * + * @retval None + */ +void +HAL_Pwm_Disable_8195a( + HAL_PWM_ADAPTER *pPwmAdapt +) +{ + u32 pwm_id; + + pwm_id = pPwmAdapt->pwm_id; + // Configure the Pin Mux + if (pPwmAdapt->enable) { + PinCtrl((PWM0+pwm_id), pPwmAdapt->sel, 0); + HalTimerOp.HalTimerDis(pPwmAdapt->gtimer_id); + pPwmAdapt->enable = 0; + } +} + +#endif //CONFIG_PWM_EN diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_sdio_device.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_sdio_device.c new file mode 100644 index 0000000..03260b2 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_sdio_device.c @@ -0,0 +1,2766 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "hal_sdio.h" +#include "mailbox.h" + +#ifndef CONFIG_INIC_EN +#define CONFIG_INIC_EN 0//for iNIC, disable by default +#endif +/****************************************************************************** + * Function Prototype Declaration + ******************************************************************************/ +BOOL SDIO_Device_Init( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Device_DeInit( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_IRQ_Handler( + IN VOID *pData +); + +VOID SDIO_Interrupt_Init( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Enable_Interrupt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +); + +VOID SDIO_Disable_Interrupt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +); + +VOID SDIO_Clear_ISR( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +); + +VOID SDIO_TxTask( + IN VOID *pData +); + +VOID SDIO_RxTask( + IN VOID *pData +); + +static __inline VOID SDIO_Wakeup_Task( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +static VOID SDIO_SetEvent( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +); + +static VOID SDIO_ClearEvent( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +); + +static BOOL SDIO_IsEventPending( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +); + +VOID SDIO_IRQ_Handler_BH( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_RX_IRQ_Handler_BH( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_TX_BD_Buf_Refill( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_TX_FIFO_DataReady( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +PSDIO_RX_PACKET SDIO_Alloc_Rx_Pkt( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Free_Rx_Pkt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN PSDIO_RX_PACKET pPkt +); + +VOID SDIO_Recycle_Rx_BD ( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Process_H2C_IOMsg( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Send_C2H_IOMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 *C2HMsg +); + +VOID SDIO_Process_H2C_PktMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *H2CMsg +); + +u8 SDIO_Send_C2H_PktMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *C2HMsg, + IN u16 MsgLen +); + +u8 SDIO_Process_RPWM( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Reset_Cmd( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Return_Rx_Data( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_Register_Tx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN s8 (*CallbackFun)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize), + IN VOID *pAdapter +); + +s8 SDIO_Rx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN VOID *pData, + IN u16 Offset, + IN u16 Length, + IN u8 CmdType +); + +s8 SDIO_Handle_MsgBlk( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN MSG_BLK *pMblk +); + +#if SDIO_MP_MODE +VOID SDIO_PeriodicalTimerCallback( + void *pContex +); + +u8 SDIO_MapMPCmd( + IN char *CmdStr, + IN u16 *Offset +); + +VOID SDIO_DumpMPStatus( + IN PHAL_SDIO_ADAPTER pSDIODev + ); + +VOID SDIO_StatisticDump( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +s8 SDIO_MP_Loopback( + IN VOID *pAdapter, + IN u8 *pData, + IN u16 Offset, + IN u16 PktSize +); + +s8 SDIO_MP_ContinueTx( + IN VOID *pAdapter, + IN u8 *pData, + IN u16 Offset, + IN u16 PktSize +); + +VOID SDIO_MP_ContinueRx( + IN PHAL_SDIO_ADAPTER pSDIODev +); + +VOID SDIO_DeviceMPApp( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u16 argc, + IN u8 *argv[] +); +#endif /* endof '#if SDIO_MP_MODE' */ + +/****************************************************************************** + * Global Variable Declaration + ******************************************************************************/ + +#if SDIO_MP_MODE +const SDIO_MP_CMD SDIO_MPCmdTable[] = { + {"mp_start", SDIO_MP_START}, + {"mp_stop", SDIO_MP_STOP}, + {"mp_loopback", SDIO_MP_LOOPBACK}, + {"status", SDIO_MP_STATUS}, + {"read_reg8", SDIO_MP_READ_REG8}, + {"read_reg16", SDIO_MP_READ_REG16}, + {"read_reg32", SDIO_MP_READ_REG32}, + {"write_reg8", SDIO_MP_WRITE_REG8}, + {"write_reg16", SDIO_MP_WRITE_REG16}, + {"write_reg32", SDIO_MP_WRITE_REG32}, + {"wakeup", SDIO_MP_WAKEUP}, + {"dump", SDIO_MP_DUMP}, + {"ctx", SDIO_MP_CTX}, + {"crx", SDIO_MP_CRX}, + {"crx_da", SDIO_MP_CRX_DA}, + {"crx_stop", SDIO_MP_CRX_STOP}, + {"dbg_msg", SDIO_MP_DBG_MSG} +}; + +const u8 MP_WlanHdr[]={ + 0x88,0x01,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00, + 0x00,0x01,0x10,0x00,0x06,0x00}; +#endif + +/****************************************************************************** + * External Function & Variable Declaration + ******************************************************************************/ +extern PHAL_SDIO_ADAPTER pgSDIODev; + +extern u32 Strtoul( + IN const u8 *nptr, + IN u8 **endptr, + IN u32 base +); + +/****************************************************************************** + * Function: SDIO_Device_Init + * Desc: SDIO device driver initialization. + * 1. Allocate SDIO TX FIFO buffer and initial TX related register. + * 2. Allocate SDIO RX Buffer Descriptor and RX Buffer. Initial RX related + * register. + * 3. Register the Interrupt function. + * 4. Create the SDIO Task and allocate resource(Semaphore). + * + ******************************************************************************/ +BOOL SDIO_Device_Init( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + int i; + SDIO_TX_PACKET *pTxPkt; + SDIO_RX_PACKET *pPkt; + SDIO_TX_BD_HANDLE *pTxBdHdl; + SDIO_RX_BD_HANDLE *pRxBdHdl; + int ret; + u32 reg; + + DBG_SDIO_INFO("SDIO_Device_Init==>\n"); + + /* SDIO Function Enable */ +#if 0 + reg = HAL_READ32(PERI_ON_BASE, 0x214); + reg |= (BIT_SOC_HCI_SDIOD_OFF_EN | BIT_SOC_HCI_SDIOD_ON_EN); + HAL_WRITE32(PERI_ON_BASE, 0x214, reg); // write REG_SOC_HCI_COM_FUNC_EN +#endif + SDIOD_ON_FCTRL(ON); + SDIOD_OFF_FCTRL(ON); + + /* Enable Clock for SDIO function */ +#if 0 + reg = HAL_READ32(PERI_ON_BASE, 0x240); +// reg |= (BIT_SOC_SLPCK_SDIO_DEV_EN | BIT_SOC_ACTCK_SDIO_DEV_EN); + reg |= BIT_SOC_ACTCK_SDIO_DEV_EN; + HAL_WRITE32(PERI_ON_BASE, 0x240, reg); // write REG_PESOC_HCI_CLK_CTRL0 +#endif + ACTCK_SDIOD_CCTRL(ON); + + // Reset SDIO DMA + HAL_SDIO_WRITE8(REG_SPDIO_CPU_RST_DMA, BIT_CPU_RST_SDIO_DMA); + + /* Initial SDIO TX BD */ + DBG_SDIO_INFO("Tx BD Init==>\n"); + +// TODO: initial TX BD + pSDIODev->pTXBDAddr = RtlZmalloc((SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD))+3); + if (NULL == pSDIODev->pTXBDAddr) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for TX_BD Err!!\n"); + goto SDIO_INIT_ERR; + } + pSDIODev->pTXBDAddrAligned = (PSDIO_TX_BD)(((((u32)pSDIODev->pTXBDAddr - 1) >> 2) + 1) << 2); // Make it 4-bytes aligned + HAL_SDIO_WRITE32(REG_SPDIO_TXBD_ADDR, pSDIODev->pTXBDAddrAligned); + HAL_SDIO_WRITE16(REG_SPDIO_TXBD_SIZE, SDIO_TX_BD_NUM); + /* Set TX_BUFF_UNIT_SIZE */ +#if 0 + reg = HAL_SDIO_READ32(REG_SPDIO_RXBD_CNT); + reg &= ~((0xff)<<8); + reg |= (SDIO_TX_BD_BUF_USIZE<<8); + HAL_SDIO_WRITE32(REG_SPDIO_RXBD_CNT, reg); +#endif + HAL_SDIO_WRITE8(REG_SPDIO_TX_BUF_UNIT_SZ, SDIO_TX_BD_BUF_USIZE); + + DBG_SDIO_INFO("Tx BD Buf Unit Size(%d), Reg=0x%x\n", SDIO_TX_BD_BUF_USIZE, HAL_SDIO_READ8(REG_SPDIO_TX_BUF_UNIT_SZ)); + + /* Set DISPATCH_TXAGG_PKT */ + HAL_SDIO_WRITE32(REG_SPDIO_AHB_DMA_CTRL, HAL_SDIO_READ32(REG_SPDIO_AHB_DMA_CTRL)|BIT31); + // Reset HW TX BD pointer + pSDIODev->TXBDWPtr = HAL_SDIO_READ32(REG_SPDIO_TXBD_WPTR); + pSDIODev->TXBDRPtr = pSDIODev->TXBDWPtr; + pSDIODev->TXBDRPtrReg = pSDIODev->TXBDWPtr; + HAL_SDIO_WRITE32(REG_SPDIO_TXBD_RPTR, pSDIODev->TXBDRPtrReg); + + pSDIODev->pTXBDHdl = (PSDIO_TX_BD_HANDLE)RtlZmalloc(SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD_HANDLE)); + if (NULL == pSDIODev->pTXBDHdl) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for TX_BD Handle Err!!\n"); + goto SDIO_INIT_ERR; + } + + for (i=0;ipTXBDHdl + i; + pTxBdHdl->pTXBD = pSDIODev->pTXBDAddrAligned + i; + // Allocate buffer for each TX BD + pTxBdHdl->pTXBD->Address = (u32)RtlMalloc(SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT); +#if SDIO_DEBUG + pSDIODev->MemAllocCnt++; +#endif + if (NULL == pTxBdHdl->pTXBD->Address) { + // Memory Allocate Failed + int j; + + for (j=0;jpTXBDHdl + j; + pTxBdHdl->pTXBD = pSDIODev->pTXBDAddrAligned + j; + if (pTxBdHdl->pTXBD->Address) { + RtlMfree((u8 *)pTxBdHdl->pTXBD->Address, (SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT)); + } + } + goto SDIO_INIT_ERR; + } + pTxBdHdl->isFree = 1; + DBG_SDIO_INFO("TX_BD%d @ 0x%x 0x%x\n", i, pTxBdHdl, pTxBdHdl->pTXBD); + } + + + RtlInitListhead(&pSDIODev->FreeTxPktList); // Init the list for free packet handler + /* Allocate memory for TX Packets handler */ + pSDIODev->pTxPktHandler = (SDIO_TX_PACKET *)(RtlZmalloc(sizeof(SDIO_TX_PACKET)*SDIO_TX_PKT_NUM)); + if (NULL == pSDIODev->pTxPktHandler) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for TX PKT Handler Err!!\n"); + goto SDIO_INIT_ERR; + } + /* Add all TX packet handler into the Free Queue(list) */ + for (i=0;ipTxPktHandler + i; + RtlListInsertTail(&pTxPkt->list, &pSDIODev->FreeTxPktList); + } + + /* Init RX BD and RX Buffer */ + pSDIODev->pRXBDAddr = RtlZmalloc((SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD))+7); + if (NULL == pSDIODev->pRXBDAddr) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for RX_BD Err!!\n"); + goto SDIO_INIT_ERR; + } + pSDIODev->pRXBDAddrAligned = (PSDIO_RX_BD)(((((u32)pSDIODev->pRXBDAddr - 1) >> 3) + 1) << 3); // Make it 8-bytes aligned + HAL_SDIO_WRITE32(REG_SPDIO_RXBD_ADDR, pSDIODev->pRXBDAddrAligned); + HAL_SDIO_WRITE16(REG_SPDIO_RXBD_SIZE, SDIO_RX_BD_NUM); + + // Set the threshold of free RX BD count to trigger interrupt + HAL_SDIO_WRITE16(REG_SPDIO_RX_BD_FREE_CNT, RX_BD_FREE_TH); + DBG_SDIO_INFO("Rx BD Free Cnt(%d), Reg=0x%x\n", RX_BD_FREE_TH, HAL_SDIO_READ16(REG_SPDIO_RX_BD_FREE_CNT)); + + pSDIODev->pRXBDHdl = (PSDIO_RX_BD_HANDLE)RtlZmalloc(SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD_HANDLE)); + if (NULL == pSDIODev->pRXBDHdl) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for RX_BD Handle Err!!\n"); + goto SDIO_INIT_ERR; + } + + for (i=0;ipRXBDHdl + i; + pRxBdHdl->pRXBD = pSDIODev->pRXBDAddrAligned + i; + pRxBdHdl->isFree = 1; + DBG_SDIO_INFO("RX_BD%d @ 0x%x 0x%x\n", i, pRxBdHdl, pRxBdHdl->pRXBD); + } + + + RtlInitListhead(&pSDIODev->FreeRxPktList); // Init the list for free packet handler + /* Allocate memory for RX Packets handler */ + pSDIODev->pRxPktHandler = (SDIO_RX_PACKET *)(RtlZmalloc(sizeof(SDIO_RX_PACKET)*SDIO_RX_PKT_NUM)); + if (NULL == pSDIODev->pRxPktHandler) { + DBG_SDIO_ERR("SDIO_Device_Init: Malloc for RX PKT Handler Err!!\n"); + goto SDIO_INIT_ERR; + } + /* Add all RX packet handler into the Free Queue(list) */ + for (i=0;ipRxPktHandler + i; + RtlListInsertTail(&pPkt->list, &pSDIODev->FreeRxPktList); + } + RtlInitListhead(&pSDIODev->RxPktList); // Init the list for RX packet to be send to the SDIO bus +// RtlInitListhead(&pSDIODev->RecyclePktList); // Init the list for packet to be recycled after the SDIO RX DMA is done + + RtlMutexInit(&pSDIODev->RxMutex); +#if SDIO_DEBUG + RtlMutexInit(&pSDIODev->StatisticMutex); +#endif + /* Create a Semaphone for SDIO Sync control */ +#if !TASK_SCHEDULER_DISABLED + RtlInitSema(&(pSDIODev->TxSema), 0); + if (NULL == pSDIODev->TxSema){ + DBG_SDIO_ERR("SDIO_Device_Init Create Semaphore Err!!\n"); + goto SDIO_INIT_ERR; + } + + RtlInitSema(&(pSDIODev->RxSema), 0); + if (NULL == pSDIODev->RxSema){ + DBG_SDIO_ERR("SDIO_Device_Init Create RX Semaphore Err!!\n"); + goto SDIO_INIT_ERR; + } + + /* create a Mailbox for other driver module to send message to SDIO driver */ + pSDIODev->pMBox = RtlMailboxCreate(MBOX_ID_SDIO, SDIO_MAILBOX_SIZE, &(pSDIODev->RxSema)); + if (NULL == pSDIODev->pMBox) { + DBG_SDIO_ERR("SDIO_Device_Init Create Mailbox Err!!\n"); + goto SDIO_INIT_ERR; + } +#if SDIO_MP_MODE + pSDIODev->pPeriodTimer = RtlTimerCreate("SDIO_Periodical", SDIO_PERIODICAL_TIMER_INTERVAL, SDIO_PeriodicalTimerCallback, pSDIODev, 1); +#endif + /* Create the SDIO task */ +#ifdef PLATFORM_FREERTOS + ret = xTaskCreate( SDIO_TxTask, "SDIO_TX_TASK", ((1024*2)/sizeof(portBASE_TYPE)), (void *)pSDIODev, SDIO_TASK_PRIORITY, &pSDIODev->xSDIOTxTaskHandle); + if (pdTRUE != ret ) + { + DBG_SDIO_ERR("SDIO_Device_Init: Create Task Err(%d)!!\n", ret); + goto SDIO_INIT_ERR; + } + + ret = xTaskCreate( SDIO_RxTask, "SDIO_RX_TASK", ((1024*1)/sizeof(portBASE_TYPE)), (void *)pSDIODev, SDIO_TASK_PRIORITY, &pSDIODev->xSDIORxTaskHandle); + if (pdTRUE != ret ) + { + DBG_SDIO_ERR("SDIO_Device_Init: Create RX Task Err(%d)!!\n", ret); + goto SDIO_INIT_ERR; + } + +#endif +#endif // end of "#if !TASK_SCHEDULER_DISABLED" +#if SDIO_MP_MODE +//1 for MP mode test only + pSDIODev->MP_ModeEn = 1; +// SDIO_Register_Tx_Callback(pSDIODev, (VOID *)SDIO_MP_Loopback, (VOID *) pSDIODev); +// pSDIODev->MP_LoopBackEn = 1; +//End +#endif +#if TASK_SCHEDULER_DISABLED + /* enable the interrupt */ + SDIO_Interrupt_Init(pSDIODev); + + /* Indicate the Host system that the TX/RX is ready */ + HAL_SDIO_WRITE8(REG_SPDIO_CPU_IND, \ + HAL_SDIO_READ8(REG_SPDIO_CPU_IND)|BIT_SYSTEM_TRX_RDY_IND); +#endif + DBG_SDIO_INFO("<==SDIO_Device_Init\n"); + + return SUCCESS; + + SDIO_INIT_ERR: +#if !TASK_SCHEDULER_DISABLED + if (pSDIODev->TxSema) { + RtlFreeSema(&pSDIODev->TxSema); + pSDIODev->TxSema = NULL; + } + + if (pSDIODev->RxSema) { + RtlFreeSema(&pSDIODev->RxSema); + pSDIODev->RxSema = NULL; + } +#endif + + if (pSDIODev->RxMutex) { + RtlMutexFree(&pSDIODev->RxMutex); + } +#if SDIO_DEBUG + if (pSDIODev->StatisticMutex) { + RtlMutexFree(&pSDIODev->StatisticMutex); + } +#endif + if (pSDIODev->pRxPktHandler) { + RtlMfree((u8*)pSDIODev->pRxPktHandler, sizeof(SDIO_RX_PACKET)*SDIO_RX_PKT_NUM); + pSDIODev->pRxPktHandler = NULL; + } + + if (pSDIODev->pRXBDHdl) { + RtlMfree((u8 *)pSDIODev->pRXBDHdl, SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD_HANDLE)); + pSDIODev->pRXBDHdl = NULL; + } + + if (pSDIODev->pRXBDAddr) { + RtlMfree((u8 *)pSDIODev->pRXBDAddr, (SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD))+7); + pSDIODev->pRXBDAddr = NULL; + } + + if (pSDIODev->pTxPktHandler) { + RtlMfree((u8 *)pSDIODev->pTxPktHandler, (sizeof(SDIO_TX_PACKET)*SDIO_TX_PKT_NUM)); + pSDIODev->pTxPktHandler = NULL; + } + + if ((pSDIODev->pTXBDHdl) && (pSDIODev->pTXBDAddr)) { + for (i=0;ipTXBDHdl + i; + if (pTxBdHdl->pTXBD->Address) { + RtlMfree((u8 *)pTxBdHdl->pTXBD->Address, (SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT)); + pTxBdHdl->pTXBD->Address = NULL; + } + } + } + + if (pSDIODev->pTXBDHdl) { + RtlMfree((u8 *)pSDIODev->pTXBDHdl, (SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD_HANDLE))); + pSDIODev->pTXBDHdl = NULL; + } + + if (pSDIODev->pTXBDAddr) { + RtlMfree(pSDIODev->pTXBDAddr, ((SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD))+3)); + pSDIODev->pTXBDAddr = NULL; + pSDIODev->pTXBDAddrAligned = NULL; + } + +#if !TASK_SCHEDULER_DISABLED + if (pSDIODev->pMBox) { + RtlMailboxDel(pSDIODev->pMBox); + pSDIODev->pMBox = NULL; + } +#if SDIO_MP_MODE + if (pSDIODev->pPeriodTimer) { + RtlTimerDelete(pSDIODev->pPeriodTimer); + pSDIODev->pPeriodTimer = NULL; + } +#endif +#endif + return FAIL; +} + + +/****************************************************************************** + * Function: SDIO_Device_DeInit + * Desc: SDIO device driver free resource. This function should be called in + * a task. + * 1. Free TX FIFO buffer + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +//TODO: Call this function in a task + +VOID SDIO_Device_DeInit( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + int i=0; + SDIO_TX_BD_HANDLE *pTxBdHdl; + + if (NULL == pSDIODev) + return; + +#if !TASK_SCHEDULER_DISABLED + /* Exit the SDIO task */ + SDIO_SetEvent(pSDIODev, SDIO_EVENT_EXIT); + SDIO_Wakeup_Task(pSDIODev); + + while (1) { + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_TX_STOPPED) && + SDIO_IsEventPending(pSDIODev, (u32)SDIO_EVENT_RX_STOPPED)) { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_EXIT); + break; // break the while loop + } + RtlMsleepOS(10); + i++; + if (i> 100) { + DBG_SDIO_ERR("SDIO_Device_DeInit: Delete SDIO Task Failed with Timeout\n"); + break; + } + } +#if SDIO_MP_MODE + if (pSDIODev->pPeriodTimer) { + RtlTimerDelete(pSDIODev->pPeriodTimer); + pSDIODev->pPeriodTimer = NULL; + } +#endif + /* Delete the Mailbox */ + if (pSDIODev->pMBox) { + RtlMailboxDel(pSDIODev->pMBox); + pSDIODev->pMBox = NULL; + } + + /* Delete the Semaphore */ + if (pSDIODev->TxSema) { + RtlFreeSema(&pSDIODev->TxSema); + pSDIODev->TxSema = NULL; + } + + if (pSDIODev->RxSema) { + RtlFreeSema(&pSDIODev->RxSema); + pSDIODev->RxSema = NULL; + } +#endif + + if (pSDIODev->RxMutex) { + RtlMutexFree(&pSDIODev->RxMutex); + } +#if SDIO_DEBUG + if (pSDIODev->StatisticMutex) { + RtlMutexFree(&pSDIODev->StatisticMutex); + } +#endif + if (pSDIODev->pRxPktHandler) { + RtlMfree((u8*)pSDIODev->pRxPktHandler, sizeof(SDIO_RX_PACKET)*SDIO_RX_PKT_NUM); + pSDIODev->pRxPktHandler = NULL; + } + + if (pSDIODev->pRXBDHdl) { + RtlMfree((u8 *)pSDIODev->pRXBDHdl, SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD_HANDLE)); + pSDIODev->pRXBDHdl = NULL; + } + + /* Free RX BD */ + if (pSDIODev->pRXBDAddr) { + RtlMfree((u8 *)pSDIODev->pRXBDAddr, (SDIO_RX_BD_NUM * sizeof(SDIO_RX_BD))+7); + pSDIODev->pRXBDAddr = NULL; + } + + /* Free TX FIFO Buffer */ + for (i=0;ipTXBDHdl + i; + if (pTxBdHdl->pTXBD->Address) { + RtlMfree((u8 *)pTxBdHdl->pTXBD->Address, (SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT)); + pTxBdHdl->pTXBD->Address = NULL; + } + } + + if (pSDIODev->pTxPktHandler) { + RtlMfree((u8 *)pSDIODev->pTxPktHandler, (sizeof(SDIO_TX_PACKET)*SDIO_TX_PKT_NUM)); + pSDIODev->pTxPktHandler = NULL; + } + + if (pSDIODev->pTXBDHdl) { + RtlMfree((u8 *)pSDIODev->pTXBDHdl, (SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD_HANDLE))); + pSDIODev->pTXBDHdl = NULL; + } + + if (pSDIODev->pTXBDAddr) { + RtlMfree(pSDIODev->pTXBDAddr, ((SDIO_TX_BD_NUM * sizeof(SDIO_TX_BD))+3)); + pSDIODev->pTXBDAddr = NULL; + pSDIODev->pTXBDAddrAligned = NULL; + } + +} + +#if TASK_SCHEDULER_DISABLED +/****************************************************************************** + * Function: SDIO_TaskUp + * Desc: For the Task scheduler no running case, use this function to run the + * SDIO task main loop. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_TaskUp( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u16 ISRStatus; + +// DiagPrintf("SDIO_TaskUp==>\n"); + pSDIODev->EventSema++; + if (pSDIODev->EventSema > 1000) { + pSDIODev->EventSema = 1000; + } + if (pSDIODev->EventSema == 1) { + while (pSDIODev->EventSema > 0) { + ISRStatus = HAL_SDIO_READ16(REG_SPDIO_CPU_INT_STAS); + pSDIODev->IntStatus |= ISRStatus; + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_STAS, ISRStatus); // clean the ISR + SDIO_SetEvent(pSDIODev, SDIO_EVENT_IRQ|SDIO_EVENT_C2H_DMA_DONE); + + SDIO_TxTask(pSDIODev); + SDIO_RxTask(pSDIODev); + + pSDIODev->EventSema--; + } + } +// DiagPrintf("<==SDIO_TaskUp\n"); +} +#endif + +/****************************************************************************** + * Function: SDIO_IRQ_Handler + * Desc: SDIO device interrupt service routine + * 1. Read & clean the interrupt status + * 2. Wake up the SDIO task to handle the IRQ event + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_IRQ_Handler( + IN VOID *pData +) +{ + PHAL_SDIO_ADAPTER pSDIODev = pData; + u16 ISRStatus; + + ISRStatus = HAL_SDIO_READ16(REG_SPDIO_CPU_INT_STAS); + DBG_SDIO_INFO("%s:ISRStatus=0x%x\n", __FUNCTION__, ISRStatus); + + pSDIODev->IntStatus |= ISRStatus; + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_STAS, ISRStatus); // clean the ISR +#if !TASK_SCHEDULER_DISABLED + if (ISRStatus & BIT_C2H_DMA_OK) { + SDIO_SetEvent(pSDIODev, SDIO_EVENT_C2H_DMA_DONE); + RtlUpSemaFromISR(&pSDIODev->RxSema); + } + + if (ISRStatus & ~BIT_C2H_DMA_OK) { + SDIO_SetEvent(pSDIODev, SDIO_EVENT_IRQ); + RtlUpSemaFromISR(&pSDIODev->TxSema); + } +#else + SDIO_SetEvent(pSDIODev, SDIO_EVENT_IRQ|SDIO_EVENT_C2H_DMA_DONE); + SDIO_TaskUp(pSDIODev); +#endif +} + +/****************************************************************************** + * Function: SDIO_Interrupt_Init + * Desc: SDIO device interrupt initialization. + * 1. Register the ISR + * 2. Initial the IMR register + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Interrupt_Init( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + IRQ_HANDLE SdioIrqHandle; + + pSDIODev->IntMask = BIT_H2C_DMA_OK | BIT_C2H_DMA_OK | BIT_H2C_MSG_INT | BIT_RPWM1_INT | \ + BIT_H2C_BUS_RES_FAIL | BIT_RXBD_FLAG_ERR_INT; + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_STAS, pSDIODev->IntMask); // Clean pending interrupt first + + SdioIrqHandle.Data = (u32) pSDIODev; + SdioIrqHandle.IrqNum = SDIO_DEVICE_IRQ; + SdioIrqHandle.IrqFun = (IRQ_FUN) SDIO_IRQ_Handler; + SdioIrqHandle.Priority = SDIO_IRQ_PRIORITY; + + InterruptRegister(&SdioIrqHandle); + InterruptEn(&SdioIrqHandle); + + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_MASK, pSDIODev->IntMask); +} + +/****************************************************************************** + * Function: SDIO_Enable_Interrupt + * Desc: SDIO enable interrupt by modify the interrupt mask + * + * Para: + * pSDIODev: The SDIO device data structor. + * IntMask: The bit map to enable the interrupt. + ******************************************************************************/ +__inline VOID SDIO_Enable_Interrupt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +) +{ + RtlEnterCritical(); + pSDIODev->IntMask |= IntMask; + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_MASK, pSDIODev->IntMask); + RtlExitCritical(); +} + +/****************************************************************************** + * Function: SDIO_Disable_Interrupt + * Desc: SDIO disable interrupt by modify the interrupt mask + * + * Para: + * pSDIODev: The SDIO device data structor. + * IntMask: The bit map to disable the interrupt. + ******************************************************************************/ +__inline VOID SDIO_Disable_Interrupt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +) +{ + RtlEnterCritical(); + pSDIODev->IntMask &= ~IntMask; + HAL_SDIO_WRITE16(REG_SPDIO_CPU_INT_MASK, pSDIODev->IntMask); + RtlExitCritical(); +} + +/****************************************************************************** + * Function: SDIO_Clear_ISR + * Desc: SDIO clear ISR bit map. + * + * Para: + * pSDIODev: The SDIO device data structor. + * IntMask: The bit map to be clean. + ******************************************************************************/ +__inline VOID SDIO_Clear_ISR( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 IntMask +) +{ + RtlEnterCritical(); + pSDIODev->IntStatus &= ~IntMask; + RtlExitCritical(); +} + +/****************************************************************************** + * Function: SDIO_TxTask + * Desc: The SDIO task handler. This is the main function of the SDIO device + * driver. + * 1. Handle interrupt events. + * * SDIO TX data ready + * * Error handling + * 2. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_TxTask( + IN VOID *pData +) +{ + PHAL_SDIO_ADAPTER pSDIODev = pData; + MSG_BLK Mblk; + + /* Initial resource */ +#if !TASK_SCHEDULER_DISABLED + /* enable the interrupt */ + SDIO_Interrupt_Init(pSDIODev); + + /* Indicate the Host system that the TX/RX is ready */ + HAL_SDIO_WRITE8(REG_SPDIO_CPU_IND, \ + HAL_SDIO_READ8(REG_SPDIO_CPU_IND)|BIT_SYSTEM_TRX_RDY_IND); +#if SDIO_MP_MODE + if (pSDIODev->pPeriodTimer) { + RtlTimerStart(pSDIODev->pPeriodTimer, 0); + } +#endif +#endif + +#if !TASK_SCHEDULER_DISABLED + for (;;) +#endif + { + /* Task blocked and wait the semaphore(events) here */ +#if !TASK_SCHEDULER_DISABLED + RtlDownSema(&pSDIODev->TxSema); +#endif + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_IRQ)) { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_IRQ); + SDIO_IRQ_Handler_BH(pSDIODev); + } + + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_TXBD_REFILL)) { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_TXBD_REFILL); + SDIO_TX_BD_Buf_Refill(pSDIODev); + } + +#if !TASK_SCHEDULER_DISABLED + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_EXIT)) { + break; // break the loop to exit the task + } +#endif + } + +#if !TASK_SCHEDULER_DISABLED +#if SDIO_MP_MODE + if (pSDIODev->pPeriodTimer) { + RtlTimerStop(pSDIODev->pPeriodTimer, 0); + } +#endif + SDIO_SetEvent(pSDIODev, SDIO_EVENT_TX_STOPPED); + DBG_SDIO_INFO("SDIO TX Task Stopped!\n"); +#if ( INCLUDE_vTaskDelete == 1 ) + vTaskDelete(NULL); +#endif +#endif +} + +/****************************************************************************** + * Function: SDIO_RxTask + * Desc: The SDIO RX task handler. This is the main function of the SDIO device + * driver to handle SDIO RX. + * 1. Handle interrupt events. + * * SDIO RX done + * 2. Send RX data back to the host by fill RX_BD to hardware. + * 3. Handle messages from mailbox + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_RxTask( + IN VOID *pData +) +{ + PHAL_SDIO_ADAPTER pSDIODev = pData; + MSG_BLK Mblk; + +#if !TASK_SCHEDULER_DISABLED + for (;;) +#endif + { + /* Task blocked and wait the semaphore(events) here */ +#if !TASK_SCHEDULER_DISABLED + RtlDownSema(&pSDIODev->RxSema); +#endif + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_C2H_DMA_DONE)) { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_C2H_DMA_DONE); + SDIO_RX_IRQ_Handler_BH(pSDIODev); + } + +#if SDIO_MP_MODE + if (pSDIODev->MP_ContinueRx) { + SDIO_MP_ContinueRx(pSDIODev); + } +#endif // end of "#if SDIO_MP_MODE" + + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_RX_PKT_RDY)) { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_RX_PKT_RDY); + SDIO_Return_Rx_Data(pSDIODev); + } + +#if !TASK_SCHEDULER_DISABLED + /* handle message block in the mailbox */ + do { + if (_SUCCESS == RtlMailboxReceive(MBOX_ID_SDIO, &Mblk, MBOX_WAIT_NONE, 0)) { + SDIO_Handle_MsgBlk(pSDIODev, &Mblk); + } + else { + break; // no more message pending, break the while loop + } + } while (1); + + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_EXIT)) { + break; // break the loop to exit the task + } +#endif + } + +#if !TASK_SCHEDULER_DISABLED + SDIO_SetEvent(pSDIODev, (u32)SDIO_EVENT_RX_STOPPED); + DBG_SDIO_INFO("SDIO RX Task Stopped!\n"); +#if ( INCLUDE_vTaskDelete == 1 ) + vTaskDelete(NULL); +#endif +#endif +} + + +/****************************************************************************** + * Function: SDIO_Wakeup_Task + * Desc: Send a semaphore to wake up the task. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +static __inline VOID SDIO_Wakeup_Task( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ +#if !TASK_SCHEDULER_DISABLED + RtlUpSema(&pSDIODev->TxSema); + RtlUpSema(&pSDIODev->RxSema); +#else + SDIO_TaskUp(pSDIODev); +#endif +} + +/****************************************************************************** + * Function: SDIO_SetEvent + * Desc: Set an event and wake up SDIO task to handle it. + * + * Para: + * pSDIODev: The SDIO device data structor. + * Event: The event to be set. + ******************************************************************************/ +static VOID SDIO_SetEvent( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +) +{ + RtlEnterCritical(); + pSDIODev->Events |= Event; + RtlExitCritical(); +} + +/****************************************************************************** + * Function: SDIO_ClearEvent + * Desc: Clean a SDIO event. + * + * Para: + * pSDIODev: The SDIO device data structor. + * Event: The event to be cleaned. + ******************************************************************************/ +static VOID SDIO_ClearEvent( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +) +{ + RtlEnterCritical(); + pSDIODev->Events &= ~Event; + RtlExitCritical(); +} + +/****************************************************************************** + * Function: SDIO_IsEventPending + * Desc: To check is a event pending. + * + * Para: + * pSDIODev: The SDIO device data structor. + * Event: The event to check. + ******************************************************************************/ +static BOOL SDIO_IsEventPending( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 Event +) +{ + BOOL ret; + + RtlEnterCritical(); + ret = (pSDIODev->Events & Event) ? 1:0; + RtlExitCritical(); + + return ret; +} + +/****************************************************************************** + * Function: SDIO_IRQ_Handler_BH + * Desc: Process the SDIO IRQ, the button helf. + * 1. SDIO TX data ready. + * 2. H2C command ready. + * 3. Host driver RPWM status updated. + * 4. SDIO RX data transfer done. + * 5. SDIO HW/BUS errors. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_IRQ_Handler_BH( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u32 IntStatus; + + DBG_SDIO_INFO("%s @1 IntStatus=0x%x\n", __FUNCTION__, pSDIODev->IntStatus); + + RtlEnterCritical(); + IntStatus = pSDIODev->IntStatus; + RtlExitCritical(); + + if (IntStatus & BIT_H2C_DMA_OK) { + SDIO_Clear_ISR(pSDIODev, BIT_H2C_DMA_OK); + SDIO_Disable_Interrupt(pSDIODev, BIT_H2C_DMA_OK); + SDIO_TX_FIFO_DataReady(pSDIODev); + SDIO_Enable_Interrupt(pSDIODev, BIT_H2C_DMA_OK); + } + + if (IntStatus & BIT_H2C_MSG_INT) { + SDIO_Clear_ISR(pSDIODev, BIT_H2C_MSG_INT); + SDIO_Process_H2C_IOMsg(pSDIODev); + } + + if (IntStatus & BIT_RPWM1_INT) { + SDIO_Clear_ISR(pSDIODev, BIT_RPWM1_INT); + SDIO_Process_RPWM(pSDIODev); + } + + if (IntStatus & BIT_SDIO_RST_CMD_INT) { + SDIO_Clear_ISR(pSDIODev, BIT_SDIO_RST_CMD_INT); + SDIO_Reset_Cmd(pSDIODev); + } + + DBG_SDIO_INFO("%s @2 IntStatus=0x%x\n", __FUNCTION__, pSDIODev->IntStatus); +} + +/****************************************************************************** + * Function: SDIO_RX_IRQ_Handler_BH + * Desc: Process the SDIO RX IRQ, the button helf. + * 1. SDIO RX data transfer done. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_RX_IRQ_Handler_BH( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u32 IntStatus; + + RtlEnterCritical(); + IntStatus = pSDIODev->IntStatus; + RtlExitCritical(); + + if (IntStatus & BIT_C2H_DMA_OK) { + SDIO_Clear_ISR(pSDIODev, BIT_C2H_DMA_OK); + SDIO_Recycle_Rx_BD(pSDIODev); +// SDIO_Return_Rx_Data(pSDIODev); + } +} + + +/****************************************************************************** + * Function: SDIO_TX_BD_Buf_Refill + * Desc: To refill all TX BD buffer. + * 1. Check all TX BD buffer + * 2. Allocate a new buffer for TX BD buffer is invalid + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_TX_BD_Buf_Refill( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u32 i,j; + u32 wait_loop; + PSDIO_TX_BD_HANDLE pTxBdHdl; + #define WAIT_TIMEOUT 100 + + for (i=0;ipTXBDHdl + pSDIODev->TXBDRPtrReg; + if (NULL == pTxBdHdl->pTXBD->Address) { + for (j=0;jpTXBD->Address = (u32)RtlMalloc(SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT); + if (NULL == pTxBdHdl->pTXBD->Address) { + DBG_SDIO_WARN("%s Alloc Mem(size=%d) Failed\n", __FUNCTION__, SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT); + RtlMsleepOS(20); + } + else { +#if SDIO_DEBUG + pSDIODev->MemAllocCnt++; +#endif + pSDIODev->TXBDRPtrReg++; + if (pSDIODev->TXBDRPtrReg >= SDIO_TX_BD_NUM) { + pSDIODev->TXBDRPtrReg = 0; + } + HAL_SDIO_WRITE16(REG_SPDIO_TXBD_RPTR, pSDIODev->TXBDRPtrReg); + break; // break the for loop + } + } + if (j == WAIT_TIMEOUT) { + break; // break the for loop + } + } + else { + break; // break the for loop + } + } + + if (pSDIODev->TXBDRPtrReg != pSDIODev->TXBDRPtr) { + DBG_SDIO_ERR("SDIO_TX_BD_Buf_Refill Err: TXBDRPtrReg=%d TXBDRPtr=%d\n", pSDIODev->TXBDRPtrReg, pSDIODev->TXBDRPtr); + } +} + + +/****************************************************************************** + * Function: SDIO_TX_FIFO_DataReady + * Desc: Handle the SDIO FIFO data ready interrupt. + * 1. Send those data to the target driver via callback fun., like WLan. + * 2. Allocate a buffer for the TX BD + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_TX_FIFO_DataReady( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + PSDIO_TX_BD_HANDLE pTxBdHdl; + PSDIO_TX_DESC pTxDesc; + volatile u16 TxBDWPtr=0; + u32 processed_pkt_cnt=0; + u8 isForceBreak=0; + s8 ret=FAIL; + u32 mem_alloc_failed=0; + + +// DBG_SDIO_INFO("SDIO_TX_FIFO_DataReady==>\n"); + + TxBDWPtr = HAL_SDIO_READ16(REG_SPDIO_TXBD_WPTR); + if (TxBDWPtr == pSDIODev->TXBDRPtr) { + if ((pSDIODev->IntStatus & BIT_TXFIFO_H2C_OVF) == 0) { + DBG_SDIO_WARN("SDIO TX Data Read False Triggered!!, TXBDWPtr=0x%x\n", TxBDWPtr); + return; + } + } + + do { + DBG_SDIO_INFO("SDIO_TX_DataReady: TxBDWPtr=%d TxBDRPtr=%d\n", TxBDWPtr, pSDIODev->TXBDRPtr); + pTxBdHdl = pSDIODev->pTXBDHdl + pSDIODev->TXBDRPtr; + pTxDesc = (PSDIO_TX_DESC)(pTxBdHdl->pTXBD->Address); + + DBG_SDIO_INFO("SDIO_TX_DataReady: PktSz=%d Offset=%d\n", pTxDesc->txpktsize, pTxDesc->offset); + if ((pTxDesc->txpktsize + pTxDesc->offset) > (SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT)) { + DBG_SDIO_WARN("SDIO_TX_DataReady Err: Incorrect TxDesc, PktSz=%d Offset=%d BufSize=%d\n", pTxDesc->txpktsize, pTxDesc->offset, \ + (SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT)); + } + // use the callback function to fordward this packet to target(WLan) driver + if (pSDIODev->Tx_Callback) { +#if 1 + ret = pSDIODev->Tx_Callback(pSDIODev->pTxCb_Adapter, (u8*)pTxBdHdl->pTXBD->Address, pTxDesc->offset, pTxDesc->txpktsize); // includes TX Desc +#else + ret = pSDIODev->Tx_Callback(pSDIODev->pTxCb_Adapter, // doesn't include TX Desc + (u8*)(pTxBdHdl->pTXBD->Address+pTxDesc->offset), + pTxDesc->txpktsize); +#endif + } + else { + ret = FAIL; + DBG_SDIO_ERR("SDIO TX_Callback is Null!\n"); + } + processed_pkt_cnt++; + if (SUCCESS != ret) { + // may be is caused by TX queue is full, so we skip it and try again later + isForceBreak = 1; + break; // break the while loop + } + else { +#if SDIO_MP_MODE + pSDIODev->MP_TxPktCnt++; + pSDIODev->MP_TxByteCnt += pTxDesc->txpktsize; + + pSDIODev->MP_TxPktCntInPeriod++; + pSDIODev->MP_TxByteCntInPeriod += pTxDesc->txpktsize; +#endif + pSDIODev->TXBDRPtr++; + if (pSDIODev->TXBDRPtr >= SDIO_TX_BD_NUM) { + pSDIODev->TXBDRPtr = 0; + } + + // allocate a new buffer for this TX BD + // buf once if the memory allocation failed, we will try it later + if (mem_alloc_failed == 0) { + pTxBdHdl->pTXBD->Address = (u32)RtlMalloc(SDIO_TX_BD_BUF_USIZE*SDIO_TX_BUF_SZ_UNIT); + if (NULL == pTxBdHdl->pTXBD->Address) { + // memory allocate error + // once memory allocate failed, stop to allocate new buffer for TX BD buffer + //, we refill TX BD buffer later + DBG_SDIO_WARN("%s: Alloc new TX BD Buf Failed\n", __FUNCTION__); + mem_alloc_failed++; + SDIO_SetEvent(pSDIODev, SDIO_EVENT_TXBD_REFILL); + } + else { +#if SDIO_DEBUG + pSDIODev->MemAllocCnt++; +#endif + pSDIODev->TXBDRPtrReg = pSDIODev->TXBDRPtr; + HAL_SDIO_WRITE16(REG_SPDIO_TXBD_RPTR, pSDIODev->TXBDRPtrReg); + } + } + else { + pTxBdHdl->pTXBD->Address = NULL; + } + } + + TxBDWPtr = HAL_SDIO_READ16(REG_SPDIO_TXBD_WPTR); + if (isForceBreak) { + break; // break the TX FIFO DMA Done processing + } + } while (pSDIODev->TXBDRPtr != TxBDWPtr); + + // if not all TX data were processed, set an event to trigger SDIO_Task to process them later + if (isForceBreak) { + DBG_SDIO_WARN("SDIO_TX Force Break: TXBDWP=0x%x TXBDRP=0x%x\n", TxBDWPtr, pSDIODev->TXBDRPtr); + RtlEnterCritical(); + if ((pSDIODev->IntStatus & BIT_TXFIFO_H2C_OVF) != 0) { + if (pSDIODev->TXBDRPtr != TxBDWPtr) { + pSDIODev->IntStatus &= ~BIT_TXFIFO_H2C_OVF; + } + } + pSDIODev->IntStatus |= BIT_H2C_DMA_OK; + RtlExitCritical(); + SDIO_SetEvent(pSDIODev, SDIO_EVENT_IRQ); +#if !TASK_SCHEDULER_DISABLED + RtlUpSema(&pSDIODev->TxSema); +#else + SDIO_TaskUp(pSDIODev); +#endif + } + else { + if ((pSDIODev->IntStatus & BIT_TXFIFO_H2C_OVF) != 0) { + SDIO_Clear_ISR(pSDIODev, BIT_TXFIFO_H2C_OVF); + } + } +} + +/****************************************************************************** + * Function: SDIO_Alloc_Rx_Pkt + * Desc: Allocate a RX Packet Handle from the queue. + * + * Para: + * pSDIODev: The SDIO device data structor. + * + * Return: + * The allocated RX packet handler. + ******************************************************************************/ +PSDIO_RX_PACKET SDIO_Alloc_Rx_Pkt( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + _LIST *plist; + SDIO_RX_PACKET *pPkt; + u32 loop_cnt; + + RtlDownMutex(&pSDIODev->RxMutex); + if (RtlIsListEmpty(&pSDIODev->FreeRxPktList)) { + RtlUpMutex(&pSDIODev->RxMutex); + loop_cnt = 0; + do { + pPkt =(SDIO_RX_PACKET *)RtlZmalloc(sizeof(SDIO_RX_PACKET)); + if (NULL != pPkt) { + pPkt->isDyna = 1; // this packet handler is dynamic allocated + DBG_SDIO_WARN("Warn! No Free RX PKT, Use Dyna Alloc\n"); + } + else { + RtlMsleepOS(10); + loop_cnt++; + if (loop_cnt > 100) { + DBG_SDIO_ERR("SDIO_Alloc_Rx_Pkt: Err!! Allocate RX PKT Failed!!\n"); + break; + } + } + }while (NULL == pPkt); + return pPkt; + } + + plist = RtlListGetNext(&pSDIODev->FreeRxPktList); + pPkt = CONTAINER_OF(plist, SDIO_RX_PACKET, list); + + RtlListDelete(&pPkt->list); + RtlUpMutex(&pSDIODev->RxMutex); + return pPkt; +} + +/****************************************************************************** + * Function: SDIO_Free_Rx_Pkt + * Desc: Put a RX Packet Handle back to the queue. + * + * Para: + * pSDIODev: The SDIO device data structor. + * pPkt: The packet handler to be free. + * + ******************************************************************************/ +VOID SDIO_Free_Rx_Pkt( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN PSDIO_RX_PACKET pPkt +) +{ + if (pPkt->isDyna) { + RtlMfree((u8 *)pPkt, sizeof(SDIO_RX_PACKET)); + } + else { + RtlDownMutex(&pSDIODev->RxMutex); + RtlListInsertTail(&pPkt->list, &pSDIODev->FreeRxPktList); + RtlUpMutex(&pSDIODev->RxMutex); + } +} + +/****************************************************************************** + * Function: SDIO_Recycle_Rx_BD + * Desc: To recycle some RX BD when C2H RX DMA done. + * 1. Free the RX packet. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Recycle_Rx_BD ( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + SDIO_RX_BD_HANDLE *pRxBdHdl; + SDIO_RX_BD *pRXBD; + u32 PktSize; + u32 FreeCnt=0; // for debugging + + DBG_SDIO_INFO("SDIO_Recycle_Rx_BD==> %d %d\n", HAL_SDIO_READ16(REG_SPDIO_RXBD_C2H_RPTR), pSDIODev->RXBDRPtr); + SDIO_Disable_Interrupt(pSDIODev, BIT_C2H_DMA_OK); + while (HAL_SDIO_READ16(REG_SPDIO_RXBD_C2H_RPTR) != pSDIODev->RXBDRPtr) + { + pRxBdHdl = pSDIODev->pRXBDHdl + pSDIODev->RXBDRPtr; + pRXBD = pSDIODev->pRXBDAddrAligned + pSDIODev->RXBDRPtr; + if (!pRxBdHdl->isFree) { + if (pRxBdHdl->isPktEnd && (NULL != pRxBdHdl->pPkt)) { + /* Free this packet */ + // TODO: The RX_DESC format may needs to be refined + PktSize = pRxBdHdl->pPkt->RxDesc.pkt_len; +#if SDIO_MP_MODE + if ((pSDIODev->MP_CRxPktPendingCnt > 0)) { + pSDIODev->MP_CRxPktPendingCnt--; + } + + if (((pSDIODev->MP_CRxPktCnt == 0) && (pSDIODev->MP_CRxPktPendingCnt == 0)) || + (SDIO_CRX_DYNA_BUF == pSDIODev->MP_ContinueRxMode)) +#endif + { +#if CONFIG_INIC_EN + RtlMfree((u8 *) (pRxBdHdl->pPkt->pData), (pRxBdHdl->pPkt->Offset+PktSize)); // free packet buffer +#else + RtlMfree((u8 *) (pRxBdHdl->pPkt->pData), (pRxBdHdl->pPkt->Offset+PktSize)); // free packet buffer +#if SDIO_DEBUG + RtlDownMutex(&pSDIODev->StatisticMutex); + pSDIODev->MemAllocCnt--; + RtlUpMutex(&pSDIODev->StatisticMutex); +#endif +#endif +#if SDIO_MP_MODE + pSDIODev->pMP_CRxBuf = NULL; +#endif + } + + _memset((void *)&(pRxBdHdl->pPkt->RxDesc), 0, sizeof(SDIO_RX_DESC)); + RtlListInsertTail(&pRxBdHdl->pPkt->list, &pSDIODev->FreeRxPktList); // Put packet handle to free queue + FreeCnt++; + pRxBdHdl->isPktEnd = 0; + pRxBdHdl->pPkt = NULL; + DBG_SDIO_INFO("SDIO_Recycle_Rx_BD: Recycle Pkt, RXBDRPtr=%d\n", pSDIODev->RXBDRPtr); +#if SDIO_MP_MODE + pSDIODev->MP_RxPktCnt++; + pSDIODev->MP_RxByteCnt += PktSize; + + pSDIODev->MP_RxPktCntInPeriod++; + pSDIODev->MP_RxByteCntInPeriod += PktSize; +#endif + } + _memset((void *)pRXBD , 0, sizeof(SDIO_RX_BD)); // clean this RX_BD + pRxBdHdl->isFree = 1; + } + else { + DBG_SDIO_WARN("SDIO_Recycle_Rx_BD: Warring, Recycle a Free RX_BD,RXBDRPtr=%d\n",pSDIODev->RXBDRPtr); + } + pSDIODev->RXBDRPtr++; + if (pSDIODev->RXBDRPtr >= SDIO_RX_BD_NUM) { + pSDIODev->RXBDRPtr -= SDIO_RX_BD_NUM; + } + } + SDIO_Enable_Interrupt(pSDIODev, BIT_C2H_DMA_OK); + DBG_SDIO_INFO("<==SDIO_Recycle_Rx_BD(%d)\n", FreeCnt); + +} + +/****************************************************************************** + * Function: SDIO_Process_H2C_IOMsg + * Desc: Handle the interrupt for HC2 message ready. Read the H2C_MSG register + * and process the H2C message. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Process_H2C_IOMsg( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u32 H2CMsg; + + // TODO: define H2C message type & format, currently we have 30 bits message only, may needs to extend the HW register + H2CMsg = HAL_SDIO_READ32(REG_SPDIO_CPU_H2C_MSG); + DBG_SDIO_INFO("H2C_MSG: 0x%x\n", H2CMsg); + // TODO: May needs to handle endian free + switch (H2CMsg) + { + default: + break; + } + // TODO: Some H2C message needs to be fordward to WLan driver +} + +/****************************************************************************** + * Function: SDIO_Send_C2H_IOMsg + * Desc: Send C2H message to the Host. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Send_C2H_IOMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u32 *C2HMsg +) +{ + u32 TmpC2HMsg; + + // TODO: define C2H message type & format, currently we have 30 bits message only, may needs to extend the HW register + + // TODO: May needs to handle endian free + TmpC2HMsg = HAL_SDIO_READ32(REG_SPDIO_CPU_C2H_MSG); + TmpC2HMsg = ((TmpC2HMsg ^ (u32)BIT(31)) & (u32)BIT(31)) | *C2HMsg; + HAL_SDIO_WRITE32(REG_SPDIO_CPU_C2H_MSG, TmpC2HMsg); +} + +/****************************************************************************** + * Function: SDIO_Process_H2C_PktMsg + * Desc: Handle the packet H2C message which from block write(CMD53). + * + * Para: + * pSDIODev: The SDIO device data structor. + * H2CMsg: point to the buffer of the H2C message received. + ******************************************************************************/ +VOID SDIO_Process_H2C_PktMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *H2CMsg +) +{ + + // TODO: define H2C message type & format + DBG_SDIO_INFO("H2C_MSG: 0x%x\n", *H2CMsg); + // TODO: May needs to handle endian free + // TODO: Some H2C message needs to be fordward to WLan driver +} + +/****************************************************************************** + * Function: SDIO_Send_C2H_PktMsg + * Desc: To send a C2H message to the Host through the block read command. + * + * Para: + * pSDIODev: The SDIO device data structor. + * H2CMsg: point to the buffer of the H2C message received. + * MsgLen: The length of this message. + ******************************************************************************/ +u8 SDIO_Send_C2H_PktMsg( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u8 *C2HMsg, + IN u16 MsgLen +) +{ + u8 *MsgBuf; + PSDIO_RX_DESC pRxDesc; + SDIO_RX_PACKET *pPkt; + + // TODO: define H2C message type & format + DBG_SDIO_INFO("C2H_MSG: 0x%x\n", *C2HMsg); + // TODO: May needs to handle endian free + + MsgBuf = RtlZmalloc(MsgLen); + if (NULL == MsgBuf) { + DBG_SDIO_ERR("SDIO_Send_C2H_PktMsg: Malloc Err!!\n"); + return FAIL; + } + _memcpy((void *)(MsgBuf), (void *)C2HMsg, MsgLen); + + pPkt = SDIO_Alloc_Rx_Pkt(pSDIODev); + if (pPkt == NULL) { + DBG_SDIO_ERR("RX Callback Err!! No Free RX PKT!\n"); + return FAIL; + } + pRxDesc = &pPkt->RxDesc; + pRxDesc->type = SDIO_CMD_C2H; + pRxDesc->pkt_len = MsgLen; + pRxDesc->offset = sizeof(SDIO_RX_DESC); + pPkt->pData = MsgBuf; + pPkt->Offset = 0; + RtlDownMutex(&pSDIODev->RxMutex); + RtlListInsertTail(&pPkt->list, &pSDIODev->RxPktList); + pSDIODev->RxInQCnt++; + RtlUpMutex(&pSDIODev->RxMutex); + SDIO_SetEvent(pSDIODev, SDIO_EVENT_RX_PKT_RDY); +#if !TASK_SCHEDULER_DISABLED + if (pSDIODev->RxInQCnt == 1) { + RtlUpSema(&pSDIODev->RxSema); + } +#else + SDIO_TaskUp(pSDIODev); +#endif + + return SUCCESS; + +} + +/****************************************************************************** + * Function: SDIO_Process_RPWM + * Desc: To handle RPWM interrupt. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +u8 SDIO_Process_RPWM( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u8 rpwm; + + rpwm = HAL_SDIO_READ8(REG_SPDIO_CRPWM); + + DBG_SDIO_INFO ("RPWM1: 0x%x\n", rpwm); + // TODO: forward this RPWM message to WLan + + return 0; +} + +/****************************************************************************** + * Function: SDIO_Reset_Cmd + * Desc: Handle the SDIO Reset Command interrupt. We did nothing currently. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Reset_Cmd( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + // TODO: + return; +} + +/****************************************************************************** + * Function: SDIO_Return_Rx_Data + * Desc: To send all packets in the RX packet list to the Host system via the + * SDIO bus. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_Return_Rx_Data( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + SDIO_RX_PACKET *pPkt=NULL; + SDIO_RX_DESC *pRxDesc; + SDIO_RX_BD_HANDLE *pRxBdHdl; + _LIST *plist; + SDIO_RX_BD *pRXBD; + u32 Offset=0; + u16 i; + u16 RxBdWrite=0; // to count how much RX_BD used in a Transaction + u16 RxBdRdPtr=0; // RX_BD read pointer + u16 AvailRxBdNum=0; + u32 pkt_size; + u8 isForceBreak=0; + u8 isListEmpty; +#if SDIO_RX_PKT_SIZE_OVER_16K + u8 needed_rxbd_num; +#endif + + DBG_SDIO_INFO("SDIO_Return_Rx_Data==> RXBDWPtr=%d\n", pSDIODev->RXBDWPtr); + RtlDownMutex(&pSDIODev->RxMutex); + + if (RtlIsListEmpty(&pSDIODev->RxPktList)) { + DBG_SDIO_INFO("SDIO_Return_Rx_Data: Queue is empty\n"); + RtlUpMutex(&pSDIODev->RxMutex); + return; + } + RtlUpMutex(&pSDIODev->RxMutex); + + RxBdRdPtr = pSDIODev->RXBDRPtr; + do { + /* Check if we shoule handle the RX_BD recycle ? */ + if (RxBdRdPtr != HAL_SDIO_READ16(REG_SPDIO_RXBD_C2H_RPTR)) { + SDIO_Recycle_Rx_BD(pSDIODev); + RxBdRdPtr = pSDIODev->RXBDRPtr; + } + + /* check if RX_BD available */ + RtlDownMutex(&pSDIODev->RxMutex); + plist = RtlListGetNext(&pSDIODev->RxPktList); + RtlUpMutex(&pSDIODev->RxMutex); + pPkt = CONTAINER_OF(plist, SDIO_RX_PACKET, list); + pRxDesc = &(pPkt->RxDesc); +#if SDIO_RX_PKT_SIZE_OVER_16K + needed_rxbd_num = ((pRxDesc->pkt_len - 1)/MAX_RX_BD_BUF_SIZE) + MIN_RX_BD_SEND_PKT; +#endif + if (RxBdRdPtr != pSDIODev->RXBDWPtr) { + if (pSDIODev->RXBDWPtr > RxBdRdPtr) { +#if SDIO_RX_PKT_SIZE_OVER_16K + if ((pSDIODev->RXBDWPtr - RxBdRdPtr) == (SDIO_RX_BD_NUM - needed_rxbd_num)) +#else + if ((pSDIODev->RXBDWPtr - RxBdRdPtr) == (SDIO_RX_BD_NUM - MIN_RX_BD_SEND_PKT)) +#endif + { + DBG_SDIO_WARN("SDIO_Return_Rx_Data: No Available RX_BD, ReadPtr=%d WritePtr=%d\n", \ + RxBdRdPtr, pSDIODev->RXBDWPtr); + isForceBreak = 1; + break; // break the while loop + } + } + else { +#if SDIO_RX_PKT_SIZE_OVER_16K + if ((RxBdRdPtr - pSDIODev->RXBDWPtr) == needed_rxbd_num) +#else + if ((RxBdRdPtr - pSDIODev->RXBDWPtr) == MIN_RX_BD_SEND_PKT) +#endif + { + DBG_SDIO_WARN("SDIO_Return_Rx_Data: No Available RX_BD, ReadPtr=%d WritePtr=%d\n", RxBdRdPtr, pSDIODev->RXBDWPtr); + isForceBreak = 1; + break; // break the while loop + } + } + } + + RtlDownMutex(&pSDIODev->RxMutex); + RtlListDelete(&pPkt->list); // remove it from the SDIO RX packet Queue + pSDIODev->RxInQCnt--; + RtlUpMutex(&pSDIODev->RxMutex); + + // TODO: Add RX_DESC before the packet + + /* a SDIO RX packet will use at least 2 RX_BD, the 1st one is for RX_Desc, + other RX_BDs are for packet payload */ + /* Use a RX_BD to transmit RX_Desc */ + pRXBD = pSDIODev->pRXBDAddrAligned + pSDIODev->RXBDWPtr; // get the RX_BD head + pRxBdHdl = pSDIODev->pRXBDHdl + pSDIODev->RXBDWPtr; + if (!pRxBdHdl->isFree) { + DBG_SDIO_ERR("SDIO_Return_Rx_Data: Allocated a non-free RX_BD\n"); + } + pRxBdHdl->isFree = 0; + pRxBdHdl->pPkt = pPkt; + pRXBD->FS = 1; + pRXBD->PhyAddr = (u32)((u8 *)pRxDesc); + pRXBD->BuffSize = sizeof(SDIO_RX_DESC); + pRxBdHdl->isPktEnd = 0; + pSDIODev->RXBDWPtr += 1; + if (pSDIODev->RXBDWPtr >= SDIO_RX_BD_NUM) { + pSDIODev->RXBDWPtr -= SDIO_RX_BD_NUM; + } + + /* Take RX_BD to transmit packet payload */ + pkt_size = pRxDesc->pkt_len; + Offset = 0; + do { + pRXBD = pSDIODev->pRXBDAddrAligned + pSDIODev->RXBDWPtr; // get the RX_BD head + pRxBdHdl = pSDIODev->pRXBDHdl + pSDIODev->RXBDWPtr; + pRxBdHdl->isFree = 0; + pRxBdHdl->pPkt = pPkt; + pRXBD->FS = 0; + pRXBD->PhyAddr = (u32)(((u8 *)pPkt->pData)+pPkt->Offset); +#if SDIO_RX_PKT_SIZE_OVER_16K + if ((pkt_size - Offset) <= MAX_RX_BD_BUF_SIZE) { + pRXBD->BuffSize = pkt_size - Offset; + pRxBdHdl->isPktEnd = 1; + } + else { + pRXBD->BuffSize = MAX_RX_BD_BUF_SIZE; + pRxBdHdl->isPktEnd = 0; + DBG_SDIO_INFO("SDIO_Return_Rx_Data: Split RX_BD, Offset=%d PktSize=%d\n", \ + Offset, pkt_size); + } +#else + if (pkt_size > MAX_RX_BD_BUF_SIZE) { + // if come to here, please enable "SDIO_RX_PKT_SIZE_OVER_16K" + DBG_SDIO_ERR("SDIO_Return_Rx_Data: The Packet Size bigger than 16K\n"); + pkt_size = MAX_RX_BD_BUF_SIZE; + } + pRXBD->BuffSize = pkt_size; + pRxBdHdl->isPktEnd = 1; +#endif + Offset += pRXBD->BuffSize; + // Move the RX_BD Write pointer forward + RxBdWrite++; + pSDIODev->RXBDWPtr += 1; + if (pSDIODev->RXBDWPtr >= SDIO_RX_BD_NUM) { + pSDIODev->RXBDWPtr -= SDIO_RX_BD_NUM; + } + + if (Offset >= pkt_size) { + pRXBD->LS = 1; + HAL_SDIO_WRITE16(REG_SPDIO_RXBD_C2H_WPTR, pSDIODev->RXBDWPtr); + DBG_SDIO_INFO("SDIO_Return_Rx_Data:RXBDWPtr=%d\n", pSDIODev->RXBDWPtr); + } + } while (Offset < pkt_size); + RtlDownMutex(&pSDIODev->RxMutex); + isListEmpty = RtlIsListEmpty(&pSDIODev->RxPktList); + RtlUpMutex(&pSDIODev->RxMutex); + } while(!isListEmpty); + + if (RxBdWrite > 0) { + HAL_SDIO_WRITE8(REG_SPDIO_HCI_RX_REQ, BIT_HCI_RX_REQ); + } + + if (isForceBreak) { +// SDIO_Recycle_Rx_BD(pSDIODev); + // function end with insufficient resource, set event to try again later + SDIO_SetEvent(pSDIODev, SDIO_EVENT_RX_PKT_RDY); +#if !TASK_SCHEDULER_DISABLED + RtlMsleepOS(10); // no resource, sleep a while + RtlUpSema(&pSDIODev->RxSema); +#else + SDIO_TaskUp(pSDIODev); +#endif + } + DBG_SDIO_INFO("SDIO_Return_Rx_Data(%d)<==\n", RxBdWrite); + +} + +/****************************************************************************** + * Function: SDIO_Register_Tx_Callback + * Desc: For a TX data target driver to register its TX callback function, so + * the SDIO driver can use it to fordward a TX packet to the target driver. + * + * Para: + * pSDIODev: point to the SDIO device handler + * CallbackFun: The function pointer of the callback + * pAdapter: a pointer will be use to call the registered CallBack function. + * It can be used to point to a handler of the caller, like WLan + * Adapter. + ******************************************************************************/ +VOID SDIO_Register_Tx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN s8 (*CallbackFun)(VOID *pAdapter, u8 *pPkt, u16 Offset, u16 PktSize), + IN VOID *pAdapter +) +{ + pSDIODev->Tx_Callback = CallbackFun; + pSDIODev->pTxCb_Adapter = pAdapter; +} + +/****************************************************************************** + * Function: SDIO_Rx_Callback + * Desc: The callback function for an packet receiving, which be called from + * the Target (WLan) driver to send a packet to the SDIO host. + * + * Para: + * pSDIODev: Point to the SDIO device data structer. + * pData: Point to the head of the data to be return to the Host system. + * Length: The length of the data to be send, in byte. + * + * Return: + * The result, SUCCESS or FAIL. + ******************************************************************************/ +s8 SDIO_Rx_Callback( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN VOID *pData, + IN u16 Offset, + IN u16 PktSize, + IN u8 CmdType +) +{ + PSDIO_RX_DESC pRxDesc; + SDIO_RX_PACKET *pPkt; + + pPkt = SDIO_Alloc_Rx_Pkt(pSDIODev); + if (pPkt == NULL) { + DBG_SDIO_ERR("RX Callback Err!! No Free RX PKT!\n"); + return FAIL; + } + pRxDesc = &pPkt->RxDesc; + pRxDesc->type = CmdType; + pRxDesc->pkt_len = PktSize; + pRxDesc->offset = sizeof(SDIO_RX_DESC); + pPkt->pData = pData; + pPkt->Offset = Offset; + RtlDownMutex(&pSDIODev->RxMutex); + RtlListInsertTail(&pPkt->list, &pSDIODev->RxPktList); + pSDIODev->RxInQCnt++; + RtlUpMutex(&pSDIODev->RxMutex); + SDIO_SetEvent(pSDIODev, SDIO_EVENT_RX_PKT_RDY); +#if !TASK_SCHEDULER_DISABLED + if (pSDIODev->RxInQCnt == 1) { + RtlUpSema(&pSDIODev->RxSema); + } +#else + SDIO_TaskUp(pSDIODev); +#endif + + return SUCCESS; +} + +/****************************************************************************** + * Function: SDIO_Handle_MsgBlk + * Desc: Process a message block. + * + * Para: + * pSDIODev: Point to the SDIO device data structer. + * MSG_BLK: The message block to be processed. + * Length: The length of the data to be send, in byte. + * + * Return: + * The result, SUCCESS or FAIL. + ******************************************************************************/ +s8 SDIO_Handle_MsgBlk( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN MSG_BLK *pMblk +) +{ + s8 ret; + + DBG_SDIO_INFO("SDIO_Handle_MsgBlk==> MsgType=%d\n", pMblk->MsgType); + switch (pMblk->MsgType) { + case MSG_SDIO_RX_PKT: + ret = SDIO_Rx_Callback(pSDIODev, pMblk->pBuf, pMblk->Reserved, pMblk->DateLen, SDIO_CMD_RX_ETH); //pMblk->Reserved = Offset + if (SUCCESS != ret) { + // failed to send this packet to the host, drop it + RtlMfree((u8 *) pMblk->pBuf, (pMblk->Reserved + pMblk->DateLen)); // pMblk->Reserved = Offset +#if SDIO_DEBUG + RtlDownMutex(&pSDIODev->StatisticMutex); + pSDIODev->MemAllocCnt--; + RtlUpMutex(&pSDIODev->StatisticMutex); +#endif +#if SDIO_MP_MODE + pSDIODev->MP_RxDropCnt++; +#endif + } + break; + + case MSG_SDIO_C2H: + break; + + case MSG_SDIO_RPWM: + break; + + default: + DBG_SDIO_WARN("SDIO_Handle_MsgBlk: UnKnown MsgType %d\n", pMblk->MsgType); + break; + } + + return ret; +} + +#if SDIO_MP_MODE + +/****************************************************************************** + * Function: SDIO_PeriodicalTimerCallback + * Desc: The callback function of the 1 Sec timer. It be used to statistic the + * throughput and update the status or something need to do periodically. + * + * Para: + * pContex: this pointer actually is the pointer of the SDIO device. + * + * Return: None + ******************************************************************************/ +VOID SDIO_PeriodicalTimerCallback( + void *pContex +) +{ + PHAL_SDIO_ADAPTER pSDIODev = pContex; + + if (SDIO_IsEventPending(pSDIODev, SDIO_EVENT_DUMP)) { + SDIO_StatisticDump(pSDIODev); +} +} + +#if !TASK_SCHEDULER_DISABLED +/****************************************************************************** + * Function: SDIO_MP_Task + * Desc: The SDIO MP test task handler. This is the main function of the SDIO + * device MP test mode. + * + * Para: + * pSDIODev: The SDIO device data structor. + ******************************************************************************/ +VOID SDIO_MP_Task( + IN VOID *pData +) +{ + PHAL_SDIO_ADAPTER pSDIODev = pData; + MSG_BLK Mblk_r; + MSG_BLK Mblk_w; + SDIO_MP_RX_PACKET *pRxPkt; + _LIST *plist; + int malloc_err_cnt=0; + + DiagPrintf("SDIO_MP_Task Started...\n"); + RtlInitListhead(&pSDIODev->MP_RxPktList); + + /* Initial resource */ + for (;;) + { + /* Task blocked and wait the semaphore(events) here */ + RtlDownSema(&pSDIODev->MP_EventSema); + /* handle message block in the mailbox */ + do { + if (_SUCCESS == RtlMailboxReceive(MBOX_ID_SDIO_MP, &Mblk_r, MBOX_WAIT_NONE, 0)) { + switch (Mblk_r.MsgType) { + case MSG_SDIO_MP_LOOP_TXPKT: + pRxPkt = NULL; + malloc_err_cnt = 0; + do { + pRxPkt = (SDIO_MP_RX_PACKET *)RtlZmalloc(sizeof(SDIO_MP_RX_PACKET)); + if (NULL != pRxPkt) { + pRxPkt->pData = Mblk_r.pBuf; + pRxPkt->Offset = Mblk_r.Reserved; + pRxPkt->DataLen = Mblk_r.DateLen; + RtlListInsertTail(&pRxPkt->list, &pSDIODev->MP_RxPktList); + } + else { + RtlMsleepOS(10); + malloc_err_cnt++; + if (malloc_err_cnt > 100) { + DBG_SDIO_ERR("SDIO_MP_Task: Malloc for Rx Pkt Failed\n"); + // no memory to handle this packet, drop it + RtlMfree(Mblk_r.pBuf, (Mblk_r.Reserved+Mblk_r.DateLen)); + pSDIODev->MP_RxDropCnt++; +#if SDIO_DEBUG + RtlDownMutex(&pSDIODev->StatisticMutex); + pSDIODev->MemAllocCnt--; + RtlUpMutex(&pSDIODev->StatisticMutex); +#endif + break; // break the while loop + } + } + }while (NULL == pRxPkt); + break; + + default: + DBG_SDIO_WARN("SDIO_MP_TASK: UnKnown MsgType %d\n", Mblk_r.MsgType); + break; + } + } + else { + break; // no more message pending, break the while loop + } + } while (1); + + while (!RtlIsListEmpty(&pSDIODev->MP_RxPktList)) { + plist = RtlListGetNext(&pSDIODev->MP_RxPktList); + pRxPkt = CONTAINER_OF(plist, SDIO_MP_RX_PACKET, list); + RtlListDelete(&pRxPkt->list); + + Mblk_w.MsgType = MSG_SDIO_RX_PKT; + Mblk_w.pBuf = pRxPkt->pData; + Mblk_w.Reserved = pRxPkt->Offset; + Mblk_w.DateLen = pRxPkt->DataLen; + if (_SUCCESS != RtlMailboxSendToBack(MBOX_ID_SDIO, &Mblk_w, 2000, 0)) { + DBG_SDIO_ERR("SDIO_MP_Task: Send MSG_SDIO_RX_PKT FAILED\n"); + RtlListInsertHead(&pRxPkt->list, &pSDIODev->MP_RxPktList); + break; + } + else { + RtlMfree((u8 *)pRxPkt, sizeof(SDIO_MP_RX_PACKET)); + } + } + + RtlEnterCritical(); + if (pSDIODev->MP_Events & SDIO_EVENT_EXIT) { + pSDIODev->MP_Events &= ~SDIO_EVENT_EXIT; + RtlExitCritical(); + DBG_SDIO_INFO("SDIO_MP_Task Exiting...\n"); + break; // break the loop to exit the task + } + RtlExitCritical(); + } + + RtlEnterCritical(); + pSDIODev->MP_Events |= SDIO_EVENT_MP_STOPPED; + RtlExitCritical(); + DBG_SDIO_INFO("SDIO_MP_Task Stoped!\n"); +#if ( INCLUDE_vTaskDelete == 1 ) + vTaskDelete(NULL); +#endif +} +#endif // end of "#if !TASK_SCHEDULER_DISABLED" + +/****************************************************************************** + * Function: SDIO_MapMPCmd + * Desc: Map a MP command string to a MP command type. + * + * Para: + * CmdStr: point to the command string buffer + * + * return: + * The MP command type + * + ******************************************************************************/ +u8 SDIO_MapMPCmd( + IN char *CmdStr, + IN u16 *Offset +) +{ + char cmd_str[16]; + u16 i; + u16 str_len=0; + u16 entry_num; + u8 mp_cmd=0xff; + + for (i=0;i<16;i++) { + if ((' ' != *(CmdStr+i)) && ('=' != *(CmdStr+i)) && (*(CmdStr+i))) { + cmd_str[i] = *(CmdStr+i); + str_len++; + } + else { + break; + } + } + + *Offset = str_len+1; + + entry_num = sizeof(SDIO_MPCmdTable)/sizeof(SDIO_MP_CMD); + + for (i=0;iMP_ModeEn); + DiagPrintf("MP_Loopback=%d\n", pSDIODev->MP_LoopBackEn); + DiagPrintf("TX: Packet Count=%d, Byte Count=%d\n", pSDIODev->MP_TxPktCnt, pSDIODev->MP_TxByteCnt); + DiagPrintf("TX: TX_BD_WPTR=%d, TX_BD_RPTR=%d\n", HAL_SDIO_READ16(REG_SPDIO_TXBD_WPTR), HAL_SDIO_READ16(REG_SPDIO_TXBD_RPTR)); + DiagPrintf("RX: Packet Count=%d, Byte Count=%d\n", pSDIODev->MP_RxPktCnt, pSDIODev->MP_RxByteCnt); + DiagPrintf("RX: RXBDWPtr=%d, RXBDRPtr=%d\n", pSDIODev->RXBDWPtr, pSDIODev->RXBDRPtr); +#if SDIO_DEBUG + DiagPrintf("RX: InQueueCount=%d MemAllocatedCnt=%d\n", pSDIODev->RxInQCnt, pSDIODev->MemAllocCnt); +#endif + DiagPrintf("TxDropPkt=%d RxDropPkt=%d\n", pSDIODev->MP_TxDropCnt, pSDIODev->MP_RxDropCnt); +} + +/****************************************************************************** + * Function: SDIO_StatisticDump + * Desc: Periodical dump SDIO throughput and other status. + * + * Para: + * pSDIODev: The SDIO device data structor. + * + * return: None + * + ******************************************************************************/ +VOID SDIO_StatisticDump( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + unsigned long tp; // throughput + u32 oldest_byte_cnt; + u32 tx_avg_tp=0; // in Kbps + u32 rx_avg_tp=0; // in Kbps + + // calculate the TX throughput + if (pSDIODev->TxAvgWinCnt >= SDIO_AVG_TP_WIN_SIZE) { + // flush the oldest one and add the newest one + oldest_byte_cnt = pSDIODev->MP_TxAvgTPWin[pSDIODev->OldestTxAvgWinIdx]; + + if (pSDIODev->MP_TxAvgTPWinSum >= oldest_byte_cnt) { + pSDIODev->MP_TxAvgTPWinSum -= oldest_byte_cnt; + pSDIODev->MP_TxAvgTPWin[pSDIODev->OldestTxAvgWinIdx] = pSDIODev->MP_TxByteCntInPeriod; + pSDIODev->OldestTxAvgWinIdx++; + if (SDIO_AVG_TP_WIN_SIZE <= pSDIODev->OldestTxAvgWinIdx) { + pSDIODev->OldestTxAvgWinIdx = 0; + } + + if (0 == pSDIODev->MP_TxAvgTPWinSum) { + // reset the statistic + pSDIODev->TxAvgWinCnt = 0; + pSDIODev->OldestTxAvgWinIdx = 0; + } + } + else { + pSDIODev->MP_TxAvgTPWinSum = 0; + // reset the statistic + if (pSDIODev->MP_TxByteCntInPeriod > 0) + pSDIODev->TxAvgWinCnt = 1; + else + pSDIODev->TxAvgWinCnt = 0; + pSDIODev->OldestTxAvgWinIdx = 0; + } + pSDIODev->MP_TxAvgTPWinSum += pSDIODev->MP_TxByteCntInPeriod; + tx_avg_tp = (pSDIODev->MP_TxAvgTPWinSum << 3) / (SDIO_PERIODICAL_TIMER_INTERVAL * SDIO_AVG_TP_WIN_SIZE); + } + else { + if ((pSDIODev->MP_TxAvgTPWinSum > 0) || (pSDIODev->MP_TxByteCntInPeriod > 0)) { + pSDIODev->MP_TxAvgTPWinSum += pSDIODev->MP_TxByteCntInPeriod; + pSDIODev->MP_TxAvgTPWin[pSDIODev->TxAvgWinCnt] = pSDIODev->MP_TxByteCntInPeriod; + pSDIODev->TxAvgWinCnt++; + tx_avg_tp = (pSDIODev->MP_TxAvgTPWinSum << 3) / (SDIO_PERIODICAL_TIMER_INTERVAL * pSDIODev->TxAvgWinCnt); + } + } + + // calculate the RX throughput + if (pSDIODev->RxAvgWinCnt >= SDIO_AVG_TP_WIN_SIZE) { + // flush the oldest one and add the newest one + oldest_byte_cnt = pSDIODev->MP_RxAvgTPWin[pSDIODev->OldestRxAvgWinIdx]; + if (pSDIODev->MP_RxAvgTPWinSum >= oldest_byte_cnt) { + pSDIODev->MP_RxAvgTPWinSum -= oldest_byte_cnt; + pSDIODev->MP_RxAvgTPWin[pSDIODev->OldestRxAvgWinIdx] = pSDIODev->MP_RxByteCntInPeriod; + pSDIODev->OldestRxAvgWinIdx++; + if (SDIO_AVG_TP_WIN_SIZE <= pSDIODev->OldestRxAvgWinIdx) { + pSDIODev->OldestRxAvgWinIdx = 0; + } + + if (0 == pSDIODev->MP_RxAvgTPWinSum) { + // reset the statistic + pSDIODev->RxAvgWinCnt = 0; + pSDIODev->OldestRxAvgWinIdx = 0; + } + } + else { + pSDIODev->MP_RxAvgTPWinSum = 0; + // reset the statistic + if (pSDIODev->MP_RxByteCntInPeriod > 0) + pSDIODev->RxAvgWinCnt = 1; + else + pSDIODev->RxAvgWinCnt = 0; + + pSDIODev->OldestRxAvgWinIdx = 0; + } + pSDIODev->MP_RxAvgTPWinSum += pSDIODev->MP_RxByteCntInPeriod; + + rx_avg_tp = (pSDIODev->MP_RxAvgTPWinSum << 3) / (SDIO_PERIODICAL_TIMER_INTERVAL * SDIO_AVG_TP_WIN_SIZE); + } + else { + if ((pSDIODev->MP_RxAvgTPWinSum > 0) || (pSDIODev->MP_RxByteCntInPeriod > 0)) { + pSDIODev->MP_RxAvgTPWinSum += pSDIODev->MP_RxByteCntInPeriod; + pSDIODev->MP_RxAvgTPWin[pSDIODev->RxAvgWinCnt] = pSDIODev->MP_RxByteCntInPeriod; + pSDIODev->RxAvgWinCnt++; + rx_avg_tp = (pSDIODev->MP_RxAvgTPWinSum << 3) / (SDIO_PERIODICAL_TIMER_INTERVAL * pSDIODev->RxAvgWinCnt); + } + } + + if ((pSDIODev->MP_TxByteCntInPeriod > 0) || (pSDIODev->MP_RxByteCntInPeriod > 0)) { + DiagPrintf("SDIO Dump:\n"); + tp = (pSDIODev->MP_TxByteCntInPeriod << 3)/(SDIO_PERIODICAL_TIMER_INTERVAL/1000); + if (tp > 1000) { + DiagPrintf("TX: Packet Count=%d, Byte Count=%d, TP=%d Kbps\n", pSDIODev->MP_TxPktCntInPeriod, pSDIODev->MP_TxByteCntInPeriod, tp/1000); + } + else { + DiagPrintf("TX: Packet Count=%d, Byte Count=%d, TP=%d bps\n", pSDIODev->MP_TxPktCntInPeriod, pSDIODev->MP_TxByteCntInPeriod, tp); + } + tp = (pSDIODev->MP_RxByteCntInPeriod << 3)/(SDIO_PERIODICAL_TIMER_INTERVAL/1000); + if (tp > 1000) { + DiagPrintf("RX: Packet Count=%d, Byte Count=%d, TP=%d Kbps\n", pSDIODev->MP_RxPktCntInPeriod, pSDIODev->MP_RxByteCntInPeriod, tp/1000); + } + else { + DiagPrintf("RX: Packet Count=%d, Byte Count=%d, TP=%d bps\n", pSDIODev->MP_RxPktCntInPeriod, pSDIODev->MP_RxByteCntInPeriod, tp); + } + + pSDIODev->MP_TxPktCntInPeriod = 0; + pSDIODev->MP_TxByteCntInPeriod = 0; + pSDIODev->MP_RxPktCntInPeriod = 0; + pSDIODev->MP_RxByteCntInPeriod = 0; + } + + if ((tx_avg_tp > 0) || (rx_avg_tp > 0)) { + DiagPrintf("TX Avg TP=%d Kbps, RX Avg TP=%d Kbps\n", tx_avg_tp, rx_avg_tp); + } +} + +/****************************************************************************** + * Function: SDIO_MP_Loopback + * Desc: The loopback test function for MP mode. + * + * Para: + * pAdapter: a pointer which got from the callback function register, + * here it point to the SDIO device itself. + * pData: The pointer of the SDIO TX data buffer. + * PktSize: The size (in byte) of this SDIO TX data. + * + * return: + * The result of this function, SUCCESS or FAILED. + * + ******************************************************************************/ +s8 SDIO_MP_Loopback( + IN VOID *pAdapter, + IN u8 *pData, + IN u16 Offset, + IN u16 PktSize +) +{ + PHAL_SDIO_ADAPTER pSDIODev=(PHAL_SDIO_ADAPTER)pAdapter; + s8 ret; + + MSG_BLK MBlk; + +// DBG_SDIO_INFO("SDIO_MP_Loopback==>\n"); + + +#if TASK_SCHEDULER_DISABLED + ret = SDIO_Rx_Callback(pSDIODev, pData, Offset, PktSize, SDIO_CMD_RX_ETH); +#else + // Mailbox test, use message to replace call SDIO_Rx_Callback directly + MBlk.MsgType = MSG_SDIO_MP_LOOP_TXPKT; + MBlk.pBuf = pData; + MBlk.Reserved = (u8)Offset; + MBlk.DateLen = PktSize; + if (_SUCCESS == RtlMailboxSendToBack(MBOX_ID_SDIO_MP, &MBlk, 2000, 0)) { + ret = SUCCESS; + } + else { + DBG_SDIO_ERR("SDIO_MP_Loopback FAILED\n"); + ret = FAIL; + } +#endif + return ret; +} + +/****************************************************************************** + * Function: SDIO_MP_ContinueTx + * Desc: The continue TX test function for MP mode. We just drop the TX packet. + * + * Para: + * pAdapter: a pointer which got from the callback function register, + * here it point to the SDIO device itself. + * pData: The pointer of the SDIO TX data buffer. + * PktSize: The size (in byte) of this SDIO TX data. + * + * return: + * The result of this function, SUCCESS or FAILED. + * + ******************************************************************************/ +s8 SDIO_MP_ContinueTx( + IN VOID *pAdapter, + IN u8 *pData, + IN u16 Offset, + IN u16 PktSize +) +{ + PHAL_SDIO_ADAPTER pSDIODev=(PHAL_SDIO_ADAPTER)pAdapter; + + RtlMfree(pData, (Offset+PktSize)); +#if SDIO_DEBUG + RtlDownMutex(&pSDIODev->StatisticMutex); + pSDIODev->MemAllocCnt--; + RtlUpMutex(&pSDIODev->StatisticMutex); +#endif + return SUCCESS; +} + +/****************************************************************************** + * Function: SDIO_MP_ContinueRx + * Desc: Process Continue RX test. + * + * Para: + * pSDIODev: The SDIO device adapter. + * + * return: None + * + ******************************************************************************/ +VOID SDIO_MP_ContinueRx( + IN PHAL_SDIO_ADAPTER pSDIODev +) +{ + u8 *pRxBuf; + s8 ret; + + while (1) { + if (pSDIODev->MP_CRxPktPendingCnt > 10) { + break; + } + + if (pSDIODev->MP_ContinueRxMode == SDIO_CRX_STATIC_BUF) { + pRxBuf = pSDIODev->pMP_CRxBuf; + } + else if (pSDIODev->MP_ContinueRxMode == SDIO_CRX_DYNA_BUF) { + pRxBuf = RtlMalloc(pSDIODev->MP_CRxSize+26); // 26: Wlan header +#if SDIO_DEBUG + pSDIODev->MemAllocCnt++; +#endif + if (NULL != pRxBuf) { + _memcpy(pRxBuf, MP_WlanHdr, 26); + _memset((pRxBuf+26), 0x3E, pSDIODev->MP_CRxSize); + } + } + + if (NULL != pRxBuf) { + ret = SDIO_Rx_Callback(pSDIODev, pRxBuf, 0, pSDIODev->MP_CRxSize+26, SDIO_CMD_RX_ETH); + if (SUCCESS == ret) { + if (pSDIODev->MP_CRxPktCnt > 0) { + pSDIODev->MP_CRxPktCnt--; + pSDIODev->MP_CRxPktPendingCnt++; + if (0 == pSDIODev->MP_CRxPktCnt) { + pSDIODev->MP_ContinueRx = 0; + break; // break the while loop + } + } + if (pSDIODev->MP_CRxPktPendingCnt > 10) { + break; + } + } + else { + if (pSDIODev->MP_ContinueRxMode == SDIO_CRX_DYNA_BUF) { + RtlMfree(pRxBuf, pSDIODev->MP_CRxSize+26); +#if SDIO_DEBUG + pSDIODev->MemAllocCnt--; +#endif + } +#if !TASK_SCHEDULER_DISABLED + RtlMsleepOS(10); // no resource, sleep a while + RtlUpSema(&pSDIODev->RxSema); +#else + SDIO_TaskUp(pSDIODev); +#endif + break; + } + } + else { +#if !TASK_SCHEDULER_DISABLED + RtlMsleepOS(10); // no resource, sleep a while + RtlUpSema(&pSDIODev->RxSema); +#else + SDIO_TaskUp(pSDIODev); +#endif + break; + } + } +} + +/****************************************************************************** + * Function: SDIO_DeviceMPApp + * Desc: To handle SDIO MP command + * + * Para: + * pData: point to the command buffer + * + ******************************************************************************/ +VOID SDIO_DeviceMPApp( + IN PHAL_SDIO_ADAPTER pSDIODev, + IN u16 argc, + IN u8 *argv[] +) +{ + u8 cmd_type; + u16 offset=0; + u32 arg1, arg2; + int ret; + int i; + + DBG_SDIO_INFO("==>MP_App: arg_num=%d cmd_str=%s\n", argc, (char *)argv[0]); + + cmd_type = SDIO_MapMPCmd((char *)argv[0], &offset); + DBG_SDIO_INFO("MP_App: MP_Cmdtype=%d\n", cmd_type); + + switch (cmd_type) + { + case SDIO_MP_START: + if (!pSDIODev->MP_ModeEn) { + pSDIODev->MP_ModeEn = 1; + pSDIODev->MP_TxPktCnt = 0; /* SDIO TX packet count */ + pSDIODev->MP_RxPktCnt = 0; /* SDIO RX packet count */ + pSDIODev->MP_TxByteCnt = 0; /* SDIO TX Byte count */ + pSDIODev->MP_RxByteCnt = 0; /* SDIO RX Byte count */ + DiagPrintf("SDIO MP Started!\n"); + } + else { + DiagPrintf("In SDIO MP Mode already!\n"); + } + break; + + case SDIO_MP_STOP: + pSDIODev->MP_ModeEn = 0; + DiagPrintf("SDIO MP Stoped!\n"); + break; + + case SDIO_MP_LOOPBACK: + DBG_SDIO_INFO("MP_App: argv[1]=%s\n", argv[1]); + if (pSDIODev->MP_ModeEn == 0) { + DiagPrintf("Not in MP mode!! Please start MP mode first.\n"); + break; + } + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 10); + if (arg1) { + if (pSDIODev->MP_LoopBackEn == 0) { + // Create a Task for MP loopback test +#if !TASK_SCHEDULER_DISABLED + RtlInitSema(&(pSDIODev->MP_EventSema), 0); + if (NULL == pSDIODev->MP_EventSema){ + DBG_SDIO_ERR("SDIO MP_Loopback Create Semaphore Err!!\n"); + break; // break the switch case + } + + /* create a Mailbox for other driver module to send message to SDIO driver */ + pSDIODev->pMP_MBox = RtlMailboxCreate(MBOX_ID_SDIO_MP, SDIO_MAILBOX_SIZE, &(pSDIODev->MP_EventSema)); + if (NULL == pSDIODev->pMBox) { + DBG_SDIO_ERR("SDIO MP_Loopback Create Mailbox Err!!\n"); + break; // break the switch case + } + + /* Create the SDIO task */ +#ifdef PLATFORM_FREERTOS + ret = xTaskCreate( SDIO_MP_Task, "SDIO_MP_TASK", ((256*4)/sizeof(portBASE_TYPE)), (void *)pSDIODev, SDIO_MP_TASK_PRIORITY, &pSDIODev->MP_TaskHandle); + if (pdTRUE != ret ) + { + DBG_SDIO_ERR("SDIO MP Create Task Err(%d)!!\n", ret); + break; + } +#endif + DiagPrintf("SDIO MP Task Created\n"); +#endif // end of "#if !TASK_SCHEDULER_DISABLED" + + // Backup origional TX Callback function + pSDIODev->pTxCallback_Backup = pSDIODev->Tx_Callback; + pSDIODev->pTxCb_Adapter_Backup = (VOID *)pSDIODev->pTxCb_Adapter; + DiagPrintf("Register SDIO TX Callback with Loopback function\n"); + SDIO_Register_Tx_Callback(pSDIODev, &SDIO_MP_Loopback, (VOID *) pSDIODev); + pSDIODev->MP_LoopBackEn = 1; + } + else { + DiagPrintf("SDIO MP LoopBack is On already!\n"); + } + } + else { + if (pSDIODev->MP_LoopBackEn) { + // Restore origional TX Callback function + DiagPrintf("Restore SDIO TX Callback...\n"); + SDIO_Register_Tx_Callback(pSDIODev, pSDIODev->pTxCallback_Backup, pSDIODev->pTxCb_Adapter_Backup); + pSDIODev->MP_LoopBackEn = 0; +#if !TASK_SCHEDULER_DISABLED + /* Exit the SDIO task */ + if (pSDIODev->MP_TaskHandle) { + RtlEnterCritical(); + pSDIODev->MP_Events |= SDIO_EVENT_EXIT; + RtlExitCritical(); + RtlUpSema(&pSDIODev->MP_EventSema); + i=0; + while (1) { + RtlEnterCritical(); + if (pSDIODev->MP_Events & SDIO_EVENT_MP_STOPPED) { + RtlExitCritical(); + break; + } + RtlExitCritical(); + RtlMsleepOS(10); + i++; + if (i> 100) { + DBG_SDIO_ERR("Delete SDIO MP Task Failed with Timeout\n"); + break; + } + } + } + + /* Delete the Mailbox */ + if (pSDIODev->pMP_MBox) { + RtlMailboxDel(pSDIODev->pMP_MBox); + pSDIODev->pMP_MBox = NULL; + } + + /* Delete the Semaphore */ + if (pSDIODev->MP_EventSema) { + RtlFreeSema(&pSDIODev->MP_EventSema); + pSDIODev->MP_EventSema = NULL; + } + DiagPrintf("SDIO MP Task Deleted\n"); +#endif + + } + } + DiagPrintf("SDIO MP LoopBack=%d\n", pSDIODev->MP_LoopBackEn); + break; + + case SDIO_MP_STATUS: + SDIO_DumpMPStatus(pSDIODev); + break; + + case SDIO_MP_READ_REG8: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + DiagPrintf("SDIO_Reg[%x]=%x\n", arg1, HAL_SDIO_READ8(arg1)); + break; + + case SDIO_MP_READ_REG16: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + DiagPrintf("SDIO_Reg[%x]=%x\n", arg1, HAL_SDIO_READ16(arg1)); + break; + + case SDIO_MP_READ_REG32: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + DiagPrintf("SDIO_Reg[%x]=%x\n", arg1, HAL_SDIO_READ32(arg1)); + break; + + case SDIO_MP_WRITE_REG8: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + arg2 = Strtoul((const u8*)(argv[2]), (u8 **)NULL, 16); + HAL_SDIO_WRITE8(arg1, arg2); + DiagPrintf("Write Reg[%x]=%x, Readback:%x\n", arg1, arg2, HAL_SDIO_READ8(arg1)); + break; + + case SDIO_MP_WRITE_REG16: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + arg2 = Strtoul((const u8*)(argv[2]), (u8 **)NULL, 16); + HAL_SDIO_WRITE16(arg1, arg2); + DiagPrintf("Write Reg[%x]=%x, Readback:%x\n", arg1, arg2, HAL_SDIO_READ16(arg1)); + break; + + case SDIO_MP_WRITE_REG32: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 16); + arg2 = Strtoul((const u8*)(argv[2]), (u8 **)NULL, 16); + HAL_SDIO_WRITE32(arg1, arg2); + DiagPrintf("Write Reg[%x]=%x, Readback:%x\n", arg1, arg2, HAL_SDIO_READ32(arg1)); + break; + + case SDIO_MP_WAKEUP: + SDIO_Wakeup_Task(pSDIODev); + break; + + case SDIO_MP_DUMP: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 10); + if (arg1) { + if (!SDIO_IsEventPending(pSDIODev, SDIO_EVENT_DUMP)) { + // reset statistic + for (i=0;iMP_TxAvgTPWin[i] = 0; + pSDIODev->MP_RxAvgTPWin[i] = 0; + } + pSDIODev->MP_TxAvgTPWinSum = 0; + pSDIODev->MP_RxAvgTPWinSum = 0; + pSDIODev->OldestTxAvgWinIdx = 0; + pSDIODev->OldestRxAvgWinIdx = 0; + pSDIODev->TxAvgWinCnt = 0; + pSDIODev->RxAvgWinCnt = 0; + } + SDIO_SetEvent(pSDIODev, SDIO_EVENT_DUMP); + } + else { + SDIO_ClearEvent(pSDIODev, SDIO_EVENT_DUMP); + } + DiagPrintf("SDIO Dump %d\n", arg1); + break; + + case SDIO_MP_CTX: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 10); + if (arg1) { + if (!pSDIODev->MP_ContinueTx) { + // Backup origional TX Callback function + pSDIODev->pTxCallback_Backup = pSDIODev->Tx_Callback; + pSDIODev->pTxCb_Adapter_Backup = (VOID *)pSDIODev->pTxCb_Adapter; + DiagPrintf("Register SDIO TX Callback with Continue TX Test function\n"); + SDIO_Register_Tx_Callback(pSDIODev, &SDIO_MP_ContinueTx, (VOID *) pSDIODev); + pSDIODev->MP_ContinueTx = 1; + } + DiagPrintf("SDIO Continue TX Test Enabled\n"); + } + else { + if (pSDIODev->MP_ContinueTx) { + // Restore origional TX Callback function + DiagPrintf("Restore SDIO TX Callback...\n"); + SDIO_Register_Tx_Callback(pSDIODev, pSDIODev->pTxCallback_Backup, pSDIODev->pTxCb_Adapter_Backup); + pSDIODev->MP_ContinueTx = 0; + DiagPrintf("SDIO Continue TX Test Disabled\n"); + } + else { + DiagPrintf("SDIO Continue TX Test Didn't Enabled\n"); + } + } + break; + + case SDIO_MP_CRX: + case SDIO_MP_CRX_DA: + if(SDIO_MP_CRX == cmd_type) { + pSDIODev->MP_ContinueRxMode = SDIO_CRX_STATIC_BUF; + } + else if (SDIO_MP_CRX_DA == cmd_type) { + pSDIODev->MP_ContinueRxMode = SDIO_CRX_DYNA_BUF; + } + + if (pSDIODev->MP_ContinueRx) { + DiagPrintf("SDIO Continue RX Test Running\n"); + break; + } + pSDIODev->MP_ContinueRx = 1; + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 10); + if (arg1 < 16) { + DiagPrintf("SDIO RX Test Min Pkt Size is 16\n"); + arg1 = 16; + } + + if (arg1 > 4096) { + DiagPrintf("SDIO RX Test Max Pkt Size is 4096\n"); + arg1 = 4096; + } + pSDIODev->MP_CRxSize = arg1; + arg2 = Strtoul((const u8*)(argv[2]), (u8 **)NULL, 10); + pSDIODev->MP_CRxPktCnt = arg2; + if (arg2 == 0) { + pSDIODev->MP_CRxInfinite = 1; + } + + if (SDIO_CRX_STATIC_BUF == pSDIODev->MP_ContinueRxMode) { + if (NULL == pSDIODev->pMP_CRxBuf) { + pSDIODev->pMP_CRxBuf = RtlMalloc(pSDIODev->MP_CRxSize+26); // 26: Wlan header + DiagPrintf("SDIO RX Test: pBuf @ 0x%x\n", (u32)pSDIODev->pMP_CRxBuf); + if (((u32)(pSDIODev->pMP_CRxBuf) & 0x03) != 0) { + DiagPrintf("SDIO RX Test: pBuf Not 4-bytes Aligned!!\n"); + } +#if SDIO_DEBUG + pSDIODev->MemAllocCnt++; +#endif + if (NULL != pSDIODev->pMP_CRxBuf) { + _memcpy(pSDIODev->pMP_CRxBuf, MP_WlanHdr, 26); + _memset((pSDIODev->pMP_CRxBuf+26), 0x3E, pSDIODev->MP_CRxSize); + } + } + + if (pSDIODev->pMP_CRxBuf) { + DiagPrintf("SDIO RX Test(Static RX Buf): PktSize=%d, PktCount=%d\n", pSDIODev->MP_CRxSize, pSDIODev->MP_CRxPktCnt); + SDIO_Wakeup_Task(pSDIODev); + } + else { + pSDIODev->MP_ContinueRx = 0; + pSDIODev->MP_CRxInfinite= 0; + pSDIODev->MP_CRxPktCnt = 0; + DiagPrintf("SDIO RX Test: Mem Allocate Failed\n"); + } + } + + if (SDIO_CRX_DYNA_BUF == pSDIODev->MP_ContinueRxMode) { + DiagPrintf("SDIO RX Test(Dyna-Allocate RX Buf): PktSize=%d, PktCount=%d\n", pSDIODev->MP_CRxSize, pSDIODev->MP_CRxPktCnt); + SDIO_Wakeup_Task(pSDIODev); + } + + break; + + case SDIO_MP_CRX_STOP: + pSDIODev->MP_ContinueRx = 0; + pSDIODev->MP_CRxPktCnt = 0; + pSDIODev->MP_CRxInfinite= 0; + DiagPrintf("SDIO RX Test Stopping...\n"); + break; + + case SDIO_MP_DBG_MSG: + arg1 = Strtoul((const u8*)(argv[1]), (u8 **)NULL, 10); + if (arg1) { + ConfigDebugInfo |= _DBG_SDIO_; + ConfigDebugWarn |= _DBG_SDIO_; + DiagPrintf("SDIO Debug Message On.\n"); + } + else { + ConfigDebugInfo &= ~_DBG_SDIO_; + ConfigDebugWarn &= ~_DBG_SDIO_; + DiagPrintf("SDIO Debug Message Off.\n"); + } + break; + + default: + DiagPrintf("SDIO_DeviceMPApp: Unknown Cmd=%s\n", argv[0]); + DiagPrintf("==== SDIO Command Help ====\n"); + DiagPrintf("SDIO mp_start : Enter MP mode\n"); + DiagPrintf("SDIO mp_stop : Exit MP mode(Switch back to Normal mode)\n"); + DiagPrintf("SDIO mp_loopback <1/0> : enable/disable data path loopback test\n"); + DiagPrintf("SDIO ctx <1/0> : Start/Stop SDIO continue TX test\n"); + DiagPrintf("SDIO crx : Start SDIO continue RX test with static RX Buffer\n"); + DiagPrintf("SDIO crx_da : Start SDIO continue RX test with Dynamic Allocate RX Buffer\n"); + DiagPrintf("SDIO crx_stop : Stop SDIO continue RX test\n"); + DiagPrintf("SDIO status : Dump current SDIO driver status\n"); + DiagPrintf("SDIO read_reg8 : Read SDIO register via 1-byte access\n"); + DiagPrintf("SDIO read_reg16 : Read SDIO register via 2-bytes access\n"); + DiagPrintf("SDIO read_reg32 : Read SDIO register via 4-bytes access\n"); + DiagPrintf("SDIO write_reg8 : Write SDIO register via 1-byte access\n"); + DiagPrintf("SDIO write_reg16 : Write SDIO register via 2-bytes access\n"); + DiagPrintf("SDIO write_reg32 : Write SDIO register via 4-bytes access\n"); + DiagPrintf("SDIO dump <1/0> : Start/Stop to dump SDIO throughput statistic periodically.\n"); + break; + } +} +#endif /* endof '#if SDIO_MP_MODE' */ + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c new file mode 100644 index 0000000..b040c6e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c @@ -0,0 +1,1188 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "rtl8195a_ssi.h" +#include "hal_ssi.h" + +extern _LONG_CALL_ +HAL_Status HalSsiInitRtl8195a(VOID *Adaptor); + +extern _LONG_CALL_ +u32 HalGetCpuClk(VOID); + + +VOID _SsiReadInterruptRtl8195a(VOID *Adapter) +{ + SSI_DBG_ENTRANCE("_SsiReadInterrupt()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + u32 ReceiveLevel; + volatile u32 Readable = HalSsiReadableRtl8195a(Adapter); + u8 Index = pHalSsiAdapter->Index; + u32 dummy_read_data; + + while (Readable) { + ReceiveLevel = HalSsiGetRxFifoLevelRtl8195a(Adapter); + + while (ReceiveLevel--) { + if (pHalSsiAdapter->RxData != NULL) { + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + *((u16*)(pHalSsiAdapter->RxData)) = (u16)(HAL_SSI_READ32(Index, REG_DW_SSI_DR)); + pHalSsiAdapter->RxData = (VOID*)(((u16*)pHalSsiAdapter->RxData) + 1); + } + else { + // 8~4 bits mode + *((u8*)(pHalSsiAdapter->RxData)) = (u8)(HAL_SSI_READ32(Index, REG_DW_SSI_DR)); + pHalSsiAdapter->RxData = (VOID*)(((u8*)pHalSsiAdapter->RxData) + 1); + } + } + else { + // for Master mode, doing TX also will got RX data, so drop the dummy data + dummy_read_data = (u16)(HAL_SSI_READ32(Index, REG_DW_SSI_DR)); + } + + if (pHalSsiAdapter->RxLength > 0) { + pHalSsiAdapter->RxLength--; + } +#if 0 + else if (pHalSsiAdapter->RxLengthRemainder > 0) { + pHalSsiAdapter->RxLengthRemainder--; + } + + // Fixed length receive Complete. (RxLength & RxLengthRemainder == 0) + if ((pHalSsiAdapter->RxLength == 0) && (pHalSsiAdapter->RxLengthRemainder == 0)) { + break; + } +#endif + if (pHalSsiAdapter->RxLength == 0) { + break; + } + } + + if (pHalSsiAdapter->RxLength == 0) { + break; + } + + Readable = HalSsiReadableRtl8195a(Adapter); + } + + if ((pHalSsiAdapter->RxLength > 0) && + (pHalSsiAdapter->RxLength < (pHalSsiAdapter->RxThresholdLevel+1))) { + SSI_DBG_INT_READ("Setting Rx FIFO Threshold Level to 1\n"); + pHalSsiAdapter->RxThresholdLevel = 0; + HalSsiSetRxFifoThresholdLevelRtl8195a((VOID*)pHalSsiAdapter); + } + + if (pHalSsiAdapter->RxLength == 0) { + DBG_SSI_INFO("_SsiReadInterruptRtl8195a: RX_Done\r\n"); + pHalSsiAdapter->InterruptMask &= ~(BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM); + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); +// if (pHalSsiAdapter->RxData != NULL) { + if (pHalSsiAdapter->RxCompCallback != NULL) { + pHalSsiAdapter->RxCompCallback(pHalSsiAdapter->RxCompCbPara); + } +// } + } + +} + + +VOID _SsiWriteInterruptRtl8195a(VOID *Adapter) +{ + SSI_DBG_ENTRANCE("_SsiWriteInterrupt()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + u32 Writeable = HalSsiWriteableRtl8195a(Adapter); + u32 TxWriteMax = SSI_TX_FIFO_DEPTH - pHalSsiAdapter->TxThresholdLevel; + u32 ReadData; + u8 Index = pHalSsiAdapter->Index; + + if (Writeable) { + /* Disable Tx FIFO Empty IRQ */ + pHalSsiAdapter->InterruptMask &= ~ BIT_IMR_TXEIM; + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); + + while (TxWriteMax--) { + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + if (pHalSsiAdapter->TxData != NULL) { + HAL_SSI_WRITE16(Index, REG_DW_SSI_DR, *((u16*)(pHalSsiAdapter->TxData))); + pHalSsiAdapter->TxData = (VOID*)(((u16*)pHalSsiAdapter->TxData) + 1); + } + else { + // For master mode: Push a dummy to TX FIFO for Read + if (pHalSsiAdapter->Role == SSI_MASTER) { + HAL_SSI_WRITE16(Index, REG_DW_SSI_DR, (u16)SSI_DUMMY_DATA); // Dummy byte + } + } + } + else { + // 8~4 bits mode + if (pHalSsiAdapter->TxData != NULL) { + HAL_SSI_WRITE8(Index, REG_DW_SSI_DR, *((u8*)(pHalSsiAdapter->TxData))); + pHalSsiAdapter->TxData = (VOID*)(((u8*)pHalSsiAdapter->TxData) + 1); + } + else { + // For master mode: Push a dummy to TX FIFO for Read + if (pHalSsiAdapter->Role == SSI_MASTER) { + HAL_SSI_WRITE8(Index, REG_DW_SSI_DR, (u8)SSI_DUMMY_DATA); // Dummy byte + } + } + } + + pHalSsiAdapter->TxLength--; + + if (pHalSsiAdapter->TxLength == 0) + break; + } + + /* Enable Tx FIFO Empty IRQ */ + pHalSsiAdapter->InterruptMask |= BIT_IMR_TXEIM; + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); + } + + if (pHalSsiAdapter->TxLength == 0) { + DBG_SSI_INFO("_SsiWriteInterruptRtl8195a: TX_Done\r\n"); + pHalSsiAdapter->InterruptMask &= ~(BIT_IMR_TXOIM | BIT_IMR_TXEIM); + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); + // If it's not a dummy TX for master read SPI, then call the TX_done callback + if (pHalSsiAdapter->TxData != NULL) { + if (pHalSsiAdapter->TxCompCallback != NULL) { + pHalSsiAdapter->TxCompCallback(pHalSsiAdapter->TxCompCbPara); + } + } + } +} + + +u32 _SsiIrqHandleRtl8195a(VOID *Adaptor) +{ + SSI_DBG_ENTRANCE("_SsiIrqHandle()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor; + u32 InterruptStatus = HalSsiGetInterruptStatusRtl8195a(Adaptor); + u8 Index = pHalSsiAdaptor->Index; + + if (InterruptStatus & BIT_ISR_TXOIS) { + SSI_DBG_INT_HNDLR("[INT][SSI%d] Transmit FIFO Overflow Interrupt\n", Index); + HAL_SSI_READ32(Index, REG_DW_SSI_TXOICR); + } + + if (InterruptStatus & BIT_ISR_RXUIS) { + SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Underflow Interrupt\n", Index); + HAL_SSI_READ32(Index, REG_DW_SSI_RXUICR); + } + + if (InterruptStatus & BIT_ISR_RXOIS) { + SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Overflow Interrupt\n", Index); + HAL_SSI_READ32(Index, REG_DW_SSI_RXOICR); + } + + if (InterruptStatus & BIT_ISR_MSTIS) { + SSI_DBG_INT_HNDLR("[INT][SSI%d] Multi-Master Contention Interrupt\n", Index); + /* Another master is actively transferring data */ + /* TODO: Do reading data... */ + HAL_SSI_READ32(Index, REG_DW_SSI_MSTICR); + } + + if ((InterruptStatus & BIT_ISR_RXFIS) ) { + SSI_DBG_INT_HNDLR("[INT][SSI%d] Receive FIFO Full Interrupt\n", Index); + _SsiReadInterruptRtl8195a(Adaptor); + } + + if (InterruptStatus & BIT_ISR_TXEIS) { + /* Tx FIFO is empty, need to transfer data */ + SSI_DBG_INT_HNDLR("[INT][SSI%d] Transmit FIFO Empty Interrupt\n", Index); + _SsiWriteInterruptRtl8195a(Adaptor); + } + + return 0; +} + +HAL_Status HalSsiInitRtl8195a_Patch(VOID *Adaptor) +{ + SSI_DBG_ENTRANCE("HalSsiInitRtl8195a()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor; + volatile IRQn_Type IrqNum; + u32 IRQ_UNKNOWN = -999; + u32 Ctrlr0Value = 0; + u32 Ctrlr1Value = 0; + u32 SerValue = 0; + u32 BaudrValue = 0; + u32 TxftlrValue = 0; + u32 RxftlrValue = 0; + u32 DmacrValue = 0; + u32 MwcrValue = 0; + u32 DmatdlrValue = 0; + u32 DmardlrValue = 0; + u8 MicrowireTransferMode = pHalSsiAdaptor->MicrowireTransferMode; + u8 DataFrameFormat = pHalSsiAdaptor->DataFrameFormat; + u8 TransferMode = pHalSsiAdaptor->TransferMode; + u8 Index = pHalSsiAdaptor->Index; + u8 Role = pHalSsiAdaptor->Role; + + if (Index > 2) { + DBG_SSI_ERR("HalSsiInitRtl8195a: Invalid SSI Idx %d\r\n", Index); + return HAL_ERR_PARA; + } + + HalSsiDisableRtl8195a(Adaptor); + + /* REG_DW_SSI_CTRLR0 */ + Ctrlr0Value |= BIT_CTRLR0_DFS(pHalSsiAdaptor->DataFrameSize); + Ctrlr0Value |= BIT_CTRLR0_FRF(pHalSsiAdaptor->DataFrameFormat); + Ctrlr0Value |= BIT_CTRLR0_SCPH(pHalSsiAdaptor->SclkPhase); + Ctrlr0Value |= BIT_CTRLR0_SCPOL(pHalSsiAdaptor->SclkPolarity); + Ctrlr0Value |= BIT_CTRLR0_TMOD(pHalSsiAdaptor->TransferMode); + Ctrlr0Value |= BIT_CTRLR0_CFS(pHalSsiAdaptor->ControlFrameSize); + + if (Role == SSI_SLAVE) + Ctrlr0Value |= BIT_CTRLR0_SLV_OE(pHalSsiAdaptor->SlaveOutputEnable); + + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_CTRLR0 Value: %X\n", Index, Ctrlr0Value); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_CTRLR0, Ctrlr0Value); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_CTRLR0(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_CTRLR0, + HAL_SSI_READ32(Index, REG_DW_SSI_CTRLR0)); + + /* REG_DW_SSI_TXFTLR */ + TxftlrValue = BIT_TXFTLR_TFT(pHalSsiAdaptor->TxThresholdLevel); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_TXFTLR Value: %X\n", Index, TxftlrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_TXFTLR, TxftlrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_TXFTLR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_TXFTLR, + HAL_SSI_READ32(Index, REG_DW_SSI_TXFTLR)); + + /* REG_DW_SSI_RXFTLR */ + RxftlrValue = BIT_RXFTLR_RFT(pHalSsiAdaptor->RxThresholdLevel); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_RXFTLR Value: %X\n", Index, RxftlrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_RXFTLR, RxftlrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_RXFTLR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_RXFTLR, + HAL_SSI_READ32(Index, REG_DW_SSI_RXFTLR)); + /** + * Master Only + * REG_DW_SSI_CTRLR1, REG_DW_SSI_SER, REG_DW_SSI_BAUDR + */ + if (Role & SSI_MASTER) { + if ((TransferMode == TMOD_RO) || (TransferMode == TMOD_EEPROM_R) || + ((DataFrameFormat == FRF_NS_MICROWIRE) && (MicrowireTransferMode == MW_TMOD_SEQUENTIAL))) { + Ctrlr1Value = BIT_CTRLR1_NDF(pHalSsiAdaptor->DataFrameNumber); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_CTRLR1 Value: %X\n", Index, Ctrlr1Value); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_CTRLR1, Ctrlr1Value); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_CTRLR1(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_CTRLR1, + HAL_SSI_READ32(Index, REG_DW_SSI_CTRLR1)); + } + + SerValue = BIT_SER_SER(1 << (pHalSsiAdaptor->SlaveSelectEnable)); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_SER Value: %X\n", Index, SerValue); + + //HAL_SSI_WRITE32(Index, REG_DW_SSI_SER, SerValue); + HalSsiSetSlaveEnableRegisterRtl8195a(Adaptor, pHalSsiAdaptor->SlaveSelectEnable); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_SER(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_SER, + HalSsiGetSlaveEnableRegisterRtl8195a(Adaptor)); + + BaudrValue = BIT_BAUDR_SCKDV(pHalSsiAdaptor->ClockDivider); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_BAUDR Value: %X\n", Index, BaudrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_BAUDR, BaudrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_BAUDR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_BAUDR, + HAL_SSI_READ32(Index, REG_DW_SSI_BAUDR)); + } +#if 0 + /** + * DMA Configurations + */ + DmacrValue = pHalSsiAdaptor->DmaControl; + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_DMACR Value: %X\n", Index, DmacrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_DMACR, DmacrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_DMACR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_DMACR, + HAL_SSI_READ32(Index, REG_DW_SSI_DMACR)); + + DmatdlrValue = BIT_DMATDLR_DMATDL(pHalSsiAdaptor->DmaTxDataLevel); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_DMATDLR Value: %X\n", Index, DmatdlrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_DMATDLR, DmatdlrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_DMATDLR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_DMATDLR, + HAL_SSI_READ32(Index, REG_DW_SSI_DMATDLR)); + + DmardlrValue = BIT_DMARDLR_DMARDL(pHalSsiAdaptor->DmaRxDataLevel); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_DMARDLR Value: %X\n", Index, DmardlrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_DMARDLR, DmardlrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_DMARDLR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_DMARDLR, + HAL_SSI_READ32(Index, REG_DW_SSI_DMARDLR)); +#endif + // Microwire + MwcrValue |= BIT_MWCR_MWMOD(pHalSsiAdaptor->MicrowireTransferMode); + MwcrValue |= BIT_MWCR_MDD(pHalSsiAdaptor->MicrowireDirection); + MwcrValue |= BIT_MWCR_MHS(pHalSsiAdaptor->MicrowireHandshaking); + SSI_DBG_INIT("[1] Set SSI%d REG_DW_SSI_MWCR Value: %X\n", Index, MwcrValue); + + HAL_SSI_WRITE32(Index, REG_DW_SSI_MWCR, MwcrValue); + + SSI_DBG_INIT("[2] SSI%d REG_DW_SSI_MWCR(%X) = %X\n", Index, + SSI0_REG_BASE + (SSI_REG_OFF * Index) + REG_DW_SSI_MWCR, + HAL_SSI_READ32(Index, REG_DW_SSI_MWCR)); + + SSI_DBG_INIT("SSI%d TransferMechanism: %d\n", Index, pHalSsiAdaptor->TransferMechanism); + if (pHalSsiAdaptor->TransferMechanism == SSI_DTM_INTERRUPT) + { + SSI_DBG_INIT("SSI%d Interrupt initialize, Interrupt: %X\n", Index, pHalSsiAdaptor->InterruptMask); + switch (Index) { + case 0: + IrqNum = SPI0_IRQ; + break; + case 1: + IrqNum = SPI1_IRQ; + break; + case 2: + IrqNum = SPI2_IRQ; + break; + default: + IrqNum = IRQ_UNKNOWN; + break; + } + + if (likely(IrqNum != IRQ_UNKNOWN)) { + /* REG_DW_SSI_IMR */ + HalSsiSetInterruptMaskRtl8195a(Adaptor); + + pHalSsiAdaptor->IrqHandle.Data = (u32)pHalSsiAdaptor; + pHalSsiAdaptor->IrqHandle.IrqFun = (IRQ_FUN)_SsiIrqHandleRtl8195a; + pHalSsiAdaptor->IrqHandle.IrqNum = (IRQn_Type)IrqNum; + pHalSsiAdaptor->IrqHandle.Priority = pHalSsiAdaptor->InterruptPriority; + + InterruptRegister(&pHalSsiAdaptor->IrqHandle); + InterruptEn(&pHalSsiAdaptor->IrqHandle); + } + else { + SSI_DBG_INIT("Unknown SSI Index.\n"); + pHalSsiAdaptor->TransferMechanism = SSI_DTM_BASIC; + } + } + + HalSsiEnableRtl8195a(Adaptor); + + return HAL_OK; +} + +HAL_Status HalSsiPinmuxEnableRtl8195a_Patch(VOID *Adaptor) +{ + SSI_DBG_ENTRANCE("HalSsiPinmuxEnableRtl8195a()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor; + volatile HAL_Status Result; + u32 PinmuxSelect = pHalSsiAdaptor->PinmuxSelect; + u8 Index = pHalSsiAdaptor->Index; + + SSI_DBG_PINMUX("[1] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index, + PERI_ON_BASE + REG_SPI_MUX_CTRL, + HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL)); + + switch (Index) + { + case 0: + { + ACTCK_SPI0_CCTRL(ON); + PinCtrl(SPI0, PinmuxSelect, ON); + SPI0_FCTRL(ON); + Result = HAL_OK; + break; + } + case 1: + { + ACTCK_SPI1_CCTRL(ON); + PinCtrl(SPI1, PinmuxSelect, ON); + SPI1_FCTRL(ON); + Result = HAL_OK; + break; + } + case 2: + { + ACTCK_SPI2_CCTRL(ON); + PinCtrl(SPI2, PinmuxSelect, ON); + SPI2_FCTRL(ON); + Result = HAL_OK; + break; + } + default: + { + DBG_SSI_ERR("Invalid SSI Index %d!\n", Index); + Result = HAL_ERR_PARA; + break; + } + } + + SSI_DBG_PINMUX("[2] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index, + PERI_ON_BASE + REG_SPI_MUX_CTRL, + HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL)); + + return Result; +} + +HAL_Status HalSsiPinmuxDisableRtl8195a(VOID *Adaptor) +{ + SSI_DBG_ENTRANCE("HalSsiPinmuxEnableRtl8195a()\n"); + PHAL_SSI_ADAPTOR pHalSsiAdaptor = (PHAL_SSI_ADAPTOR) Adaptor; + volatile HAL_Status Result; + u32 PinmuxSelect = pHalSsiAdaptor->PinmuxSelect; + u8 Index = pHalSsiAdaptor->Index; + + SSI_DBG_PINMUX("[1] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index, + PERI_ON_BASE + REG_SPI_MUX_CTRL, + HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL)); + + switch (Index) + { + case 0: + { + ACTCK_SPI0_CCTRL(OFF); + PinCtrl(SPI0, PinmuxSelect, OFF); + SPI0_FCTRL(OFF); + Result = HAL_OK; + break; + } + case 1: + { + ACTCK_SPI1_CCTRL(OFF); + PinCtrl(SPI1, PinmuxSelect, OFF); + SPI1_FCTRL(OFF); + Result = HAL_OK; + break; + } + case 2: + { + ACTCK_SPI2_CCTRL(OFF); + PinCtrl(SPI2, PinmuxSelect, OFF); + SPI2_FCTRL(OFF); + Result = HAL_OK; + break; + } + default: + { + DBG_SSI_ERR("Invalid SSI Index %d!\n", Index); + Result = HAL_ERR_PARA; + break; + } + } + + SSI_DBG_PINMUX("[2] SSI%d REG_SSI_MUX_CTRL(%X) = %X\n", Index, + PERI_ON_BASE + REG_SPI_MUX_CTRL, + HAL_READ32(PERI_ON_BASE, REG_SPI_MUX_CTRL)); + + return Result; +} + + +VOID HalSsiSetSclkRtl8195a(VOID *Adapter, u32 ClkRate) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + u32 ssi_clk; + u32 ClockDivider; + u32 SsiEn; + u32 RegValue; + u32 SystemClock; + u8 spi_idx = pHalSsiAdapter->Index; + + // Set SCLK Freq only available for Master mode + // For Slave mode, the baud rate is depends on the Master side, max rate = ssi_clk/2 + // Fsclk_out = Fssi_clk/SCKDV + SystemClock = HalGetCpuClk(); + + if (spi_idx == 1) { + ssi_clk = SystemClock >> 1; + RegValue = HAL_READ32(SYSTEM_CTRL_BASE, 0x250); + if (ClkRate > (ssi_clk/2)) { + // Use High speed clock: Fixed Freq. + RegValue |= BIT18; + ssi_clk = (200000000*5/6) >> 1; + } + else { + // Use Normal speed clock: CPU_Clk/2 + RegValue &= ~BIT18; + } + HAL_WRITE32(SYSTEM_CTRL_BASE, 0x250, RegValue); + } + else { + ssi_clk = SystemClock >> 2; + } + + if (pHalSsiAdapter->Role == SSI_MASTER) { + if (ClkRate > (ssi_clk/2)) { + DBG_SSI_ERR("spi_frequency: Freq %d is too high, available highest Freq=%d\r\n", ClkRate, (ssi_clk/2)); + ClockDivider = 2; + } + else { + ClockDivider = ssi_clk/ClkRate + 1; + if ((ssi_clk%ClkRate) > (ClkRate/2)) { + ClockDivider++; + } + if (ClockDivider >= 0xFFFF) { + // devider is 16 bits + ClockDivider = 0xFFFE; + } + ClockDivider &= 0xFFFE; // bit 0 always is 0 + } + DBG_SSI_INFO("spi_frequency: Set SCLK Freq=%d\r\n", (ssi_clk/ClockDivider)); + pHalSsiAdapter->ClockDivider = ClockDivider; + + SsiEn = HAL_SSI_READ32(spi_idx, REG_DW_SSI_SSIENR); // Backup SSI_EN register + + // Disable SSI first, so we can modify the Clock Divider + RegValue = SsiEn & BIT_INVC_SSIENR_SSI_EN; + HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_SSIENR, RegValue); + HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_BAUDR, (pHalSsiAdapter->ClockDivider & 0xFFFF)); + // recover the SSI_EN setting + HAL_SSI_WRITE32(spi_idx, REG_DW_SSI_SSIENR, SsiEn); + } + else { + if (ClkRate > (ssi_clk/10)) { + DBG_SSI_ERR("spi_frequency: Freq %d is too high, available highest Freq=%d\r\n", ClkRate, (ssi_clk/10)); + } + } +} + +HAL_Status HalSsiIntReadRtl8195a(VOID *Adapter, VOID *RxData, u32 Length) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + u32 RxLengthRemainder; + u32 RxFifoThresholdLevel; + u8 Index = pHalSsiAdapter->Index; + + DBG_SSI_INFO("HalSsiIntReadRtl8195a: Idx=%d, RxData=0x%x, Len=0x%x\r\n", Index, RxData, Length); + if (HalSsiBusyRtl8195a(Adapter)) { + // As a Slave mode, if the peer(Master) side is power off, the BUSY flag is always on + DBG_SSI_WARN("HalSsiIntReadRtl8195a: SSI%d is busy\n", Index); + return HAL_BUSY; + } + + if (Length == 0) { + SSI_DBG_INT_READ("SSI%d RxData addr: 0x%X, Length: %d\n", Index, RxData, Length); + return HAL_ERR_PARA; + } + + if (Length > (pHalSsiAdapter->DefaultRxThresholdLevel)) { + RxFifoThresholdLevel = pHalSsiAdapter->DefaultRxThresholdLevel; + } + else { + RxFifoThresholdLevel = 0; + } + + if (pHalSsiAdapter->RxThresholdLevel != RxFifoThresholdLevel) { + DBG_SSI_INFO("Setting Rx FIFO Threshold Level to %d\n", RxFifoThresholdLevel); + pHalSsiAdapter->RxThresholdLevel = RxFifoThresholdLevel; + HalSsiSetRxFifoThresholdLevelRtl8195a((VOID*)pHalSsiAdapter); + } + + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + pHalSsiAdapter->RxLength = Length >> 1; // 2 bytes(16 bit) every transfer + } + else { + // 8~4 bits mode + pHalSsiAdapter->RxLength = Length; // 1 byte(8 bit) every transfer + } + + pHalSsiAdapter->RxData = RxData; + DBG_SSI_INFO("SSI%d RxData addr: 0x%X, Length: %d\n", Index, + pHalSsiAdapter->RxData, pHalSsiAdapter->RxLength); + + pHalSsiAdapter->InterruptMask |= BIT_IMR_RXFIM | BIT_IMR_RXOIM | BIT_IMR_RXUIM; + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); + + return HAL_OK; +} + + +HAL_Status +HalSsiIntWriteRtl8195a( + IN VOID *Adapter, // PHAL_SSI_ADAPTOR + IN u8 *pTxData, // the Buffer to be send + IN u32 Length // the length of data to be send +) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + + DBG_SSI_INFO("HalSsiIntWriteRtl8195a: Idx=%d, RxData=0x%x, Len=0x%x\r\n", pHalSsiAdapter->Index, pTxData, Length); + if ((Length == 0)) { + DBG_SSI_ERR("HalSsiIntSendRtl8195a: Err: pTxData=0x%x, Length=%d\n", pTxData, Length); + return HAL_ERR_PARA; + } + + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + pHalSsiAdapter->TxLength = Length >> 1; // 2 bytes(16 bit) every transfer + } + else { + // 8~4 bits mode + pHalSsiAdapter->TxLength = Length; // 1 byte(8 bit) every transfer + } + + pHalSsiAdapter->TxData = (void*)pTxData; + pHalSsiAdapter->InterruptMask |= BIT_IMR_TXOIM | BIT_IMR_TXEIM; + HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter); + + return HAL_OK; +} + +#ifdef CONFIG_GDMA_EN +/** + * GDMA IRQ Handler + */ +VOID SsiTxGdmaIrqHandle (VOID *Data) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Data; + PSSI_DMA_CONFIG pDmaConfig = &pHalSsiAdapter->DmaConfig;; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PHAL_GDMA_OP pHalGdmaOp; + u8 IsrTypeMap = 0; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + + /* Clear Pending ISR */ + IsrTypeMap = pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter); + /* Maintain Block Count */ +#if 0 + if (IsrTypeMap & BlockType) { + DBG_SSI_WARN("DMA Block %d\n",pHalGdmaAdapter->MuliBlockCunt); + pHalGdmaAdapter->MuliBlockCunt++; + } +#endif + +#if 0 + /* Set SSI DMA Disable */ + HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \ + (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) & ~SSI_TXDMA_ENABLE)); +#endif + pHalGdmaOp->HalGdmaChDis((VOID*)(pHalGdmaAdapter)); + // Call user TX complete callback + if (NULL != pHalSsiAdapter->TxCompCallback) { + pHalSsiAdapter->TxCompCallback(pHalSsiAdapter->TxCompCbPara); + } +} + +VOID SsiRxGdmaIrqHandle (VOID *Data) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Data; + PSSI_DMA_CONFIG pDmaConfig = &pHalSsiAdapter->DmaConfig;; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PHAL_GDMA_OP pHalGdmaOp; + u8 IsrTypeMap = 0; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + /* Clear Pending ISR */ + IsrTypeMap = pHalGdmaOp->HalGdmaChIsrClean((VOID*)pHalGdmaAdapter); + + /* Maintain Block Count */ +#if 0 + if (IsrTypeMap & BlockType) { + DBG_SSI_WARN("DMA Block %d\n",pHalGdmaAdapter->MuliBlockCunt); + pHalGdmaAdapter->MuliBlockCunt++; + } +#endif + + /* Set SSI DMA Disable */ + HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \ + (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) & ~SSI_RXDMA_ENABLE)); + pHalGdmaOp->HalGdmaChDis((VOID*)(pHalGdmaAdapter)); + // Call user RX complete callback + if (NULL != pHalSsiAdapter->RxCompCallback) { + pHalSsiAdapter->RxCompCallback(pHalSsiAdapter->RxCompCbPara); + } + +} + +VOID +HalSsiTxGdmaLoadDefRtl8195a( + IN VOID *Adapter +) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + u8 *pDst; + u8 DmaIdx; + u8 DmaCh; + u8 DstPer; + u8 ssi_idx; + u32 DmaChEn; + IRQn_Type IrqNum; + + if ((NULL == pHalSsiAdapter)) { + return; + } + pDmaConfig = &pHalSsiAdapter->DmaConfig; + + ssi_idx = pHalSsiAdapter->Index; + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + if (NULL == pHalGdmaAdapter) { + DBG_SSI_ERR("HalSsiTxGdmaLoadDefRtl8195a: HalGdmaAdapter is NULL\r\n"); + return; + } + _memset((void *)pHalGdmaAdapter, 0, sizeof(HAL_GDMA_ADAPTER)); + + pHalSsiAdapter->DmaControl |= SSI_TXDMA_ENABLE; + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + pHalSsiAdapter->DmaTxDataLevel = 48; // When TX FIFO entity number <=48 then DMA Request asserted + } + else { + // 8~4 bits mode + pHalSsiAdapter->DmaTxDataLevel = 56; // When TX FIFO entity number <=56 then DMA Request asserted + } + + switch (ssi_idx) { + case 0: + pDst = (u8*) (SSI0_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 0; + DmaCh = 1; + DmaChEn = GdmaCh1; + IrqNum = GDMA0_CHANNEL1_IRQ; + DstPer = GDMA_HANDSHAKE_SSI0_TX; + break; + + case 1: + pDst = (u8*) (SSI1_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 1; + DmaCh = 1; + DmaChEn = GdmaCh1; + IrqNum = GDMA1_CHANNEL1_IRQ; + DstPer = GDMA_HANDSHAKE_SSI1_TX; + break; + + case 2: + pDst = (u8*) (SSI2_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 0; // SPI2 TX only can use GDMA0 + DmaCh = 3; + DmaChEn = GdmaCh3; + IrqNum = GDMA0_CHANNEL3_IRQ; + DstPer = GDMA_HANDSHAKE_SSI2_TX; + break; + + default: + return; + } + + pHalGdmaAdapter->GdmaCtl.TtFc = TTFCMemToPeri; + pHalGdmaAdapter->GdmaCtl.Done = 1; + pHalGdmaAdapter->MuliBlockCunt = 0; + pHalGdmaAdapter->MaxMuliBlock = 1; + pHalGdmaAdapter->GdmaCfg.DestPer = DstPer; + pHalGdmaAdapter->ChDar = (u32)pDst; + pHalGdmaAdapter->GdmaIndex = DmaIdx; + pHalGdmaAdapter->ChNum = DmaCh; + pHalGdmaAdapter->ChEn = DmaChEn; + pHalGdmaAdapter->GdmaIsrType = (BlockType|TransferType|ErrType); + pHalGdmaAdapter->IsrCtrl = ENABLE; + pHalGdmaAdapter->GdmaOnOff = ON; + + pHalGdmaAdapter->GdmaCtl.IntEn = 1; + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeOne; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte; + pHalGdmaAdapter->GdmaCtl.Dinc = NoChange; + pHalGdmaAdapter->GdmaCtl.Sinc = IncType; + + pDmaConfig->TxGdmaIrqHandle.Data = (u32)pHalSsiAdapter; + pDmaConfig->TxGdmaIrqHandle.IrqNum = IrqNum; + pDmaConfig->TxGdmaIrqHandle.IrqFun = (IRQ_FUN)SsiTxGdmaIrqHandle; + pDmaConfig->TxGdmaIrqHandle.Priority = 0x10; +} + + +VOID +HalSsiRxGdmaLoadDefRtl8195a( + IN VOID *Adapter +) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + u8 *pSrc; + u8 DmaIdx; + u8 DmaCh; + u8 SrcPer; + u8 ssi_idx; + u32 DmaChEn; + IRQn_Type IrqNum; + + if ((NULL == pHalSsiAdapter)) { + return; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + + ssi_idx = pHalSsiAdapter->Index; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + if (NULL == pHalGdmaAdapter) { + return; + } + + _memset((void *)pHalGdmaAdapter, 0, sizeof(HAL_GDMA_ADAPTER)); + + pHalSsiAdapter->DmaControl |= SSI_RXDMA_ENABLE; + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + pHalSsiAdapter->DmaRxDataLevel = 7; // RX FIFO stored bytes > (DMARDLR(7) + 1) then request DMA transfer + } + else { + // 8~4 bits mode + pHalSsiAdapter->DmaRxDataLevel = 3; // RX FIFO stored bytes > (DMARDLR(3) + 1) then request DMA transfer + } + switch (ssi_idx) { + case 0: + pSrc = (u8*) (SSI0_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 0; + DmaCh = 2; + DmaChEn = GdmaCh2; + SrcPer = GDMA_HANDSHAKE_SSI0_RX; + IrqNum = GDMA0_CHANNEL2_IRQ; + break; + + case 1: + pSrc = (u8*) (SSI1_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 1; + DmaCh = 2; + DmaChEn = GdmaCh2; + SrcPer = GDMA_HANDSHAKE_SSI1_RX; + IrqNum = GDMA1_CHANNEL2_IRQ; + break; + + case 2: + pSrc = (u8*) (SSI2_REG_BASE + REG_DW_SSI_DR); + DmaIdx = 1; // SSI2 RX only can use GDMA1 + DmaCh = 3; + DmaChEn = GdmaCh3; + SrcPer = GDMA_HANDSHAKE_SSI2_RX; + IrqNum = GDMA1_CHANNEL3_IRQ; + break; + + default: + return; + } + + pHalGdmaAdapter->GdmaCtl.TtFc = TTFCPeriToMem; + pHalGdmaAdapter->GdmaCtl.Done = 1; + pHalGdmaAdapter->GdmaCfg.ReloadSrc = 1; + pHalGdmaAdapter->GdmaCfg.SrcPer = SrcPer; + pHalGdmaAdapter->MuliBlockCunt = 0; + pHalGdmaAdapter->MaxMuliBlock = 1; + pHalGdmaAdapter->ChSar = (u32)pSrc; + pHalGdmaAdapter->GdmaIndex = DmaIdx; + pHalGdmaAdapter->ChNum = DmaCh; + pHalGdmaAdapter->ChEn = DmaChEn; + pHalGdmaAdapter->GdmaIsrType = (BlockType|TransferType|ErrType); + pHalGdmaAdapter->IsrCtrl = ENABLE; + pHalGdmaAdapter->GdmaOnOff = ON; + + pHalGdmaAdapter->GdmaCtl.IntEn = 1; + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.Dinc = IncType; + pHalGdmaAdapter->GdmaCtl.Sinc = NoChange; + + pDmaConfig->RxGdmaIrqHandle.Data = (u32)pHalSsiAdapter; + pDmaConfig->RxGdmaIrqHandle.IrqNum = IrqNum; + pDmaConfig->RxGdmaIrqHandle.IrqFun = (IRQ_FUN)SsiRxGdmaIrqHandle; + pDmaConfig->RxGdmaIrqHandle.Priority = 0x10; +} + +VOID +HalSsiDmaInitRtl8195a( + IN VOID *Adapter +) +{ + u32 RegValue; + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pTxHalGdmaAdapter; + PHAL_GDMA_ADAPTER pRxHalGdmaAdapter; + u8 ssi_idx; + u32 hdk_tx_bit; + u32 hdk_rx_bit; + u32 DmatdlrValue = 0; + u32 DmardlrValue = 0; + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + pTxHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + pRxHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + ssi_idx = pHalSsiAdapter->Index; + + // Set REG_PESOC_SOC_CTRL[28:16] to configure the GDMA handshake connection + // SSI2 handshake connection is hardware fixed + if (ssi_idx != 2) { + hdk_tx_bit = 16+pTxHalGdmaAdapter->GdmaCfg.DestPer; + hdk_rx_bit = 16+pRxHalGdmaAdapter->GdmaCfg.SrcPer; + } + else { + hdk_tx_bit = 0; + hdk_rx_bit = 0; + } + + HalSsiDisableRtl8195a(pHalSsiAdapter); + + RegValue = HAL_READ32(PERI_ON_BASE, REG_PESOC_SOC_CTRL); + if (pHalSsiAdapter->DmaControl & SSI_TXDMA_ENABLE) { + // TX DMA is enabled + if (pTxHalGdmaAdapter->GdmaIndex ==0) { + ACTCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + if (hdk_tx_bit != 0) { + RegValue &= ~(1<DmaTxDataLevel); + HAL_SSI_WRITE32(ssi_idx, REG_DW_SSI_DMATDLR, DmatdlrValue); + + /* Set SSI DMA Enable */ + HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \ + (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_TXDMA_ENABLE)); + } + + if (pHalSsiAdapter->DmaControl & SSI_RXDMA_ENABLE) { + // RX DMA is enabled + if (pRxHalGdmaAdapter->GdmaIndex ==0) { + ACTCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + if (hdk_rx_bit != 0) { + RegValue &= ~(1<DmaRxDataLevel); + HAL_SSI_WRITE32(ssi_idx, REG_DW_SSI_DMARDLR, DmardlrValue); + // the RX DMA will be enabled at read start. + } + + HAL_WRITE32(PERI_ON_BASE, REG_PESOC_SOC_CTRL, RegValue); + + HalSsiEnableRtl8195a(pHalSsiAdapter); +} + +HAL_Status +HalSsiDmaSendRtl8195a( + IN VOID *Adapter, // PHAL_SSI_ADAPTOR + IN u8 *pTxData, // the Buffer to be send + IN u32 Length // the length of data to be send +) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PHAL_GDMA_OP pHalGdmaOp; + + + if ((pTxData == NULL) || (Length == 0)) { + DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Err: pTxData=0x%x, Length=%d\n", pTxData, Length); + return HAL_ERR_PARA; + } + pDmaConfig = &pHalSsiAdapter->DmaConfig; + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + + pHalSsiAdapter->TxLength = Length; + pHalSsiAdapter->TxData = (void*)pTxData; + + // Cofigure GDMA transfer + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + if (((Length & 0x03)==0) && + (((u32)(pTxData) & 0x03)==0)) { + // 4-bytes aligned, move 4 bytes each transfer + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 2; + } + else if (((Length & 0x01)==0) && + (((u32)(pTxData) & 0x01)==0)) { + // 2-bytes aligned, move 2 bytes each transfer + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 1; + } + else { + DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Aligment Err: pTxData=0x%x, Length=%d\n", pTxData, Length); + return HAL_ERR_PARA; + } + } + else { + // 8~4 bits mode + if (((Length & 0x03)==0) && + (((u32)(pTxData) & 0x03)==0)) { + // 4-bytes aligned, move 4 bytes each transfer + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeOne; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 2; + } + else { + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthOneByte; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length; + } + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte; + } + + DBG_SSI_INFO("SrcMsize=%d SrcTrWidth=%d DestMsize=%d DstTrWidth=%d BlockSize=%d\n", \ + pHalGdmaAdapter->GdmaCtl.SrcMsize, pHalGdmaAdapter->GdmaCtl.SrcTrWidth, \ + pHalGdmaAdapter->GdmaCtl.DestMsize, pHalGdmaAdapter->GdmaCtl.DstTrWidth, \ + pHalGdmaAdapter->GdmaCtl.BlockSize); + + if (pHalGdmaAdapter->GdmaCtl.BlockSize > 4096) { + // over Maximum block size 4096 + DBG_SSI_ERR("HalSsiDmaSendRtl8195a: GDMA Block Size(%d) too big\n", pHalGdmaAdapter->GdmaCtl.BlockSize); + return HAL_ERR_PARA; + } + + pHalGdmaAdapter->ChSar = (u32)pTxData; + + // Enable GDMA for TX + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + pHalGdmaOp->HalGdmaChSeting((VOID*)(pHalGdmaAdapter)); + pHalGdmaOp->HalGdmaChEn((VOID*)(pHalGdmaAdapter)); + +#if 0 + /* Set SSI DMA Enable */ + // TODO: protect the enable DMA register, it may collision with the DMA disable in the GDMA done ISR + HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \ + (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_TXDMA_ENABLE)); +#endif + + return HAL_OK; +} + +HAL_Status +HalSsiDmaRecvRtl8195a( + IN VOID *Adapter, // PHAL_SSI_ADAPTOR + IN u8 *pRxData, ///< Rx buffer + IN u32 Length // buffer length +) +{ + PHAL_SSI_ADAPTOR pHalSsiAdapter = (PHAL_SSI_ADAPTOR) Adapter; + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PHAL_GDMA_OP pHalGdmaOp; + + if ((pRxData == NULL) || (Length == 0)) { + DBG_SSI_ERR("HalRuartDmaRecvRtl8195a: Null Err: pRxData=0x%x, Length=%d\n", pRxData, Length); + return HAL_ERR_PARA; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + + pHalSsiAdapter->RxLength = Length; + pHalSsiAdapter->RxData = (void*)pRxData; + + // Cofigure GDMA transfer + if ((pHalSsiAdapter->DataFrameSize+1) > 8) { + // 16~9 bits mode + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthTwoBytes; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length >> 1; + if (((Length & 0x03)==0) && + (((u32)(pRxData) & 0x03)==0)) { + // 4-bytes aligned, move 4 bytes each transfer + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes; + } + else if (((Length & 0x01)==0) && + (((u32)(pRxData) & 0x01)==0)) { + // 2-bytes aligned, move 2 bytes each transfer + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthTwoBytes; + } + else { + DBG_SSI_ERR("HalSsiDmaSendRtl8195a: Aligment Err: pTxData=0x%x, Length=%d\n", pRxData, Length); + return HAL_ERR_PARA; + } + } + else { + // 8~4 bits mode + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthOneByte; + pHalGdmaAdapter->GdmaCtl.BlockSize = Length; + if (((Length & 0x03)==0) && + (((u32)(pRxData) & 0x03)==0)) { + // 4-bytes aligned, move 4 bytes each transfer + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeOne; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes; + } + else { + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeFour; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte; + } + } + + if (pHalGdmaAdapter->GdmaCtl.BlockSize > 4096) { + // over Maximum block size 4096 + DBG_SSI_ERR("HalRuartDmaRecvRtl8195a: GDMA Block Size(%d) too big\n", pHalGdmaAdapter->GdmaCtl.BlockSize); + return HAL_ERR_PARA; + } + + pHalGdmaAdapter->ChDar = (u32)pRxData; + + // Enable GDMA for RX + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + pHalGdmaOp->HalGdmaChSeting((VOID*)(pHalGdmaAdapter)); + pHalGdmaOp->HalGdmaChEn((VOID*)(pHalGdmaAdapter)); + + /* Set SSI DMA Enable */ + // TODO: protect the enable DMA register, it may collision with the DMA disable in the GDMA TX done ISR + HAL_SSI_WRITE32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR, \ + (HAL_SSI_READ32(pHalSsiAdapter->Index, REG_DW_SSI_DMACR) | SSI_RXDMA_ENABLE)); + + return HAL_OK; +} + +#endif // end of "#ifdef CONFIG_GDMA_EN" + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_timer.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_timer.c new file mode 100644 index 0000000..911c5b8 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_timer.c @@ -0,0 +1,317 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#include "rtl8195a.h" +#include "rtl8195a_timer.h" + +extern u32 gTimerRecord; +extern IRQ_FUN Timer2To7VectorTable[MAX_TIMER_VECTOR_TABLE_NUM]; + +#ifdef CONFIG_CHIP_C_CUT +extern u32 Timer2To7HandlerData[MAX_TIMER_VECTOR_TABLE_NUM]; +#else +u32 Timer2To7HandlerData[MAX_TIMER_VECTOR_TABLE_NUM]; +#endif + +VOID +HalTimerIrq2To7Handle_Patch( + IN VOID *Data +) +{ + u32 TimerIrqStatus = 0, CheckIndex; + IRQ_FUN pHandler; + + TimerIrqStatus = HAL_TIMER_READ32(TIMERS_INT_STATUS_OFF); + + DBG_TIMER_INFO("%s:TimerIrqStatus: 0x%x\n",__FUNCTION__, TimerIrqStatus); + + for (CheckIndex = 2; CheckIndex<8; CheckIndex++) { + + //3 Check IRQ status bit and Timer X IRQ enable bit + if ((TimerIrqStatus & BIT_(CheckIndex)) && + (HAL_TIMER_READ32(TIMER_INTERVAL*CheckIndex + TIMER_CTL_REG_OFF) & BIT0)) { + //3 Execute Timer callback function + pHandler = Timer2To7VectorTable[CheckIndex-2]; + if (pHandler != NULL) { + pHandler((void*)Timer2To7HandlerData[CheckIndex-2]); + } + //3 Clear Timer ISR + HAL_TIMER_READ32(TIMER_INTERVAL*CheckIndex + TIMER_EOI_OFF); + } + } +} + +HAL_Status +HalTimerIrqRegisterRtl8195a_Patch( + IN VOID *Data +) +{ + PTIMER_ADAPTER pHalTimerAdap = (PTIMER_ADAPTER) Data; + IRQ_HANDLE TimerIrqHandle; + IRQ_FUN BackUpIrqFun = NULL; + + if (pHalTimerAdap->TimerId > 7) { + DBG_TIMER_ERR("%s: No Support Timer ID %d!\r\n", __FUNCTION__, pHalTimerAdap->TimerId); + return HAL_ERR_PARA; + } + else { + if (pHalTimerAdap->TimerId > 1) { + + TimerIrqHandle.IrqNum = TIMER2_7_IRQ; + TimerIrqHandle.IrqFun = (IRQ_FUN) HalTimerIrq2To7Handle_Patch; + + Timer2To7VectorTable[pHalTimerAdap->TimerId-2] = + (IRQ_FUN) pHalTimerAdap->IrqHandle.IrqFun; + Timer2To7HandlerData[pHalTimerAdap->TimerId-2] = + (uint32_t) pHalTimerAdap->IrqHandle.Data; + } + else { + TimerIrqHandle.IrqNum = (pHalTimerAdap->TimerId ? TIMER1_IRQ : TIMER0_IRQ); + TimerIrqHandle.IrqFun = (IRQ_FUN) pHalTimerAdap->IrqHandle.IrqFun; + } + TimerIrqHandle.Data = (u32)pHalTimerAdap; + InterruptRegister(&TimerIrqHandle); + } + + return HAL_OK; +} + +HAL_Status +HalTimerInitRtl8195a_Patch( + IN VOID *Data +) +{ + PTIMER_ADAPTER pHalTimerAdap = (PTIMER_ADAPTER) Data; + HAL_Status ret=HAL_OK; + u32 ControlReg = 0, LoadCount = 0; + u32 LoadUsX4; + + if ((gTimerRecord & (1<TimerId)) != 0) { + DBG_TIMER_ERR ("%s:Error! Timer %d is occupied!\r\n", __FUNCTION__, pHalTimerAdap->TimerId); + return HAL_BUSY; + } + + ControlReg = ((u32)pHalTimerAdap->TimerMode<<1)|((u32)pHalTimerAdap->IrqDis<<2); + + if (pHalTimerAdap->TimerMode) { + //User-defined Mode + LoadUsX4 = pHalTimerAdap->TimerLoadValueUs << 2; + if (LoadUsX4 < TIMER_TICK_US_X4) { + DBG_TIMER_WARN("%s : Timer Load Count = 1!\r\n", __FUNCTION__); + LoadCount = 1; + } + else { + LoadCount = (LoadUsX4-(TIMER_TICK_US_X4>>1))/TIMER_TICK_US_X4; // to get the most closed integer + if (LoadCount == 0) { + LoadCount = 1; + } + } + } + else { + LoadCount = 0xFFFFFFFF; + } + + + //4 1) Config Timer Setting + /* + set TimerControlReg + 0: Timer enable (0,disable; 1,enable) + 1: Timer Mode (0, free-running mode; 1, user-defined count mode) + 2: Timer Interrupt Mask (0, not masked; 1,masked) + */ + HAL_TIMER_WRITE32((TIMER_INTERVAL*pHalTimerAdap->TimerId + TIMER_CTL_REG_OFF), + ControlReg); + + // set TimerLoadCount Register + HAL_TIMER_WRITE32((TIMER_INTERVAL*pHalTimerAdap->TimerId + TIMER_LOAD_COUNT_OFF), + LoadCount); + + //4 2) Setting Timer IRQ + if (!pHalTimerAdap->IrqDis) { + if (pHalTimerAdap->IrqHandle.IrqFun != NULL) { + //4 2.1) Initial TimerIRQHandle + ret = HalTimerIrqRegisterRtl8195a_Patch(pHalTimerAdap); + if (HAL_OK != ret) { + DBG_TIMER_ERR ("%s: Timer %d Register IRQ Err!\r\n", __FUNCTION__, pHalTimerAdap->TimerId); + return ret; + } + //4 2.2) Enable TimerIRQ for Platform + InterruptEn((PIRQ_HANDLE)&pHalTimerAdap->IrqHandle); + + } + else { + DBG_TIMER_ERR ("%s: Timer %d ISR Handler is NULL!\r\n", __FUNCTION__, pHalTimerAdap->TimerId); + return HAL_ERR_PARA; + } + + } + + //4 4) Enable Timer +// HAL_TIMER_WRITE32((TIMER_INTERVAL*pHalTimerAdap->TimerId + TIMER_CTL_REG_OFF), +// (ControlReg|0x1)); + + gTimerRecord |= (1<TimerId); + + return ret; +} + + +HAL_Status +HalTimerIrqUnRegisterRtl8195a_Patch( + IN VOID *Data +) +{ + PTIMER_ADAPTER pHalTimerAdap = (PTIMER_ADAPTER) Data; + PIRQ_HANDLE pTimerIrqHandle; + u32 i; + + pTimerIrqHandle = &pHalTimerAdap->IrqHandle; + + if (pHalTimerAdap->TimerId > 7) { + DBG_TIMER_ERR("%s:Error: No Support Timer ID!\n", __FUNCTION__); + return HAL_ERR_PARA; + } + else { + if (pHalTimerAdap->TimerId > 1) { + pTimerIrqHandle->IrqNum = TIMER2_7_IRQ; + Timer2To7VectorTable[pHalTimerAdap->TimerId-2] = NULL; + for (i=0;iIrqHandle); + InterruptUnRegister(pTimerIrqHandle); + } + } + else { + pTimerIrqHandle->IrqNum = (pHalTimerAdap->TimerId ? TIMER1_IRQ : TIMER0_IRQ); + InterruptUnRegister(pTimerIrqHandle); + } + + } + + return HAL_OK; +} + + +VOID +HalTimerDeInitRtl8195a_Patch( + IN VOID *Data +) +{ + PTIMER_ADAPTER pHalTimerAdap = (PTIMER_ADAPTER) Data; + u32 timer_id; + + timer_id = pHalTimerAdap->TimerId; + HalTimerDisRtl8195a (timer_id); + if (!pHalTimerAdap->IrqDis) { + if (pHalTimerAdap->IrqHandle.IrqFun != NULL) { + HalTimerIrqUnRegisterRtl8195a_Patch(pHalTimerAdap); + } + } + + gTimerRecord &= ~(1<TimerId); +} + +VOID +HalTimerReLoadRtl8195a_Patch( + IN u32 TimerId, + IN u32 LoadUs +) +{ + u32 LoadCount = 0; + u32 LoadUsX4; + + //User-defined Mode + LoadUsX4 = LoadUs << 2; // time 4 + if (LoadUsX4 < TIMER_TICK_US_X4) { + DBG_TIMER_WARN("HalTimerReLoadRtl8195a Warning : Timer Load Count = 1!!!!!!!\n"); + LoadCount = 1; + } + else { + LoadCount = (LoadUsX4-(TIMER_TICK_US_X4>>1))/TIMER_TICK_US_X4; // to get the most closed integer + if (LoadCount == 0) { + LoadCount = 1; + } + } + + DBG_TIMER_INFO("%s: Load Count=0x%x\r\n", __FUNCTION__, LoadCount); + // set TimerLoadCount Register + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_LOAD_COUNT_OFF), + LoadCount); +} + +u32 +HalTimerReadCountRtl8195a_Patch( + IN u32 TimerId +) +{ + u32 TimerCountOld; + u32 TimerCountNew; + u32 TimerRDCnt; + + TimerRDCnt = 0; + TimerCountOld = HAL_TIMER_READ32(TimerId*TIMER_INTERVAL + TIMER_CURRENT_VAL_OFF); + while(1) { + TimerCountNew = HAL_TIMER_READ32(TimerId*TIMER_INTERVAL + TIMER_CURRENT_VAL_OFF); + + if (TimerCountOld == TimerCountNew) { + return (u32)TimerCountOld; + } + else { + TimerRDCnt++; + TimerCountOld = TimerCountNew; + + if (TimerRDCnt >= 2){ + return (u32)TimerCountOld; + } + } + } +} + +VOID +HalTimerIrqEnRtl8195a( + IN u32 TimerId +) +{ + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF), + HAL_TIMER_READ32(TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF) & (~(BIT2))); +} + +VOID +HalTimerIrqDisRtl8195a( + IN u32 TimerId +) +{ + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF), + HAL_TIMER_READ32(TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF) | (BIT2)); +} + +VOID +HalTimerEnRtl8195a_Patch( + IN u32 TimerId +) +{ + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF), + HAL_TIMER_READ32(TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF) | (BIT0)); +} + +VOID +HalTimerDisRtl8195a_Patch( + IN u32 TimerId +) +{ + // Disable Timer will alos disable the IRQ, so need to re-enable the IRQ when re-enable the timer + HAL_TIMER_WRITE32((TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF), + HAL_TIMER_READ32(TIMER_INTERVAL*TimerId + TIMER_CTL_REG_OFF) & (~BIT0)); +} + diff --git a/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_uart.c b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_uart.c new file mode 100644 index 0000000..e1bc13a --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_uart.c @@ -0,0 +1,399 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "rtl8195a_uart.h" +#include "hal_uart.h" +#include "hal_gdma.h" + +#if (CONFIG_CHIP_A_CUT | CONFIG_CHIP_B_CUT) + +const u32 BAUDRATE_PATCH[] = { + 1200, 2400, 4800, 9600, + 14400, 19200, 28800, 38400, + 57600, 76800, 115200, 128000, + 153600, 230400, 460800, 500000, + 921600, 1000000, 1382400, 1444400, + 1500000, 1843200, 2000000, 2100000, + 2764800, 3000000, 3250000, 3692300, + 3750000, 4000000, 6000000, + + // For UART to IR Carrier + 66000, 72000, 73400, 76000, + 80000, 112000 +}; + +#if 0 +const u32 OVSR_PATCH[] = { + 11, 13, 19, 10, + 20, 10, 20, 17, + 11, 10, 17, 10, + 10, 13, 18, 13, + 20, 19, 13, 15, + 13, 13, 15, 13, + 12, 11, 11, 20, + 13 +}; + +const u32 DIV_PATCH[] = { + 6105, 643, 293, 434, + 142, 217, 71, 62, + 65, 62, 31, 34, + 17, 12, 5, 6, + 3, 3, 4, 3, + 3, 3, 2, 2, + 2, 2, 2, 1, + 1 +}; + +const u32 OVSR_ADJ_PATCH[] = { + 0x24A, 0x555, 0x3BB, 0x000, + 0x24A, 0x000, 0x24A, 0x555, + 0x008, 0x555, 0x555, 0x5AD, + 0x5AD, 0x7EF, 0x020, 0x7EF, + 0x020, 0x444, 0x7EF, 0x080, + 0x7EF, 0x444, 0x080, 0x7EF, + 0x6FB, 0x122, 0x010, 0x5F7, + 0x7EF +}; +#else +const u8 OVSR_PATCH[] = { + 10, 10, 10, 15, + 10, 13, 15, 11, + 12, 11, 12, 11, + 11, 18, 11, 18, + 11, 10, 11, 11, + 18, 11, 10, 13, + 14, 13, 12, 11, + 10, 10, 13, + + // For UART to IR Carrier + 11, 18, 14, 10, + 10, 11 +}; + +const u16 DIV_PATCH[] = { + 6516, 3258, 1629, 543, + 543, 311, 181, 188, + 114, 94, 57, 57, + 47, 19, 15, 9, + 8, 8, 5, 5, + 3, 4, 4, 3, + 2, 2, 2, 2, + 2, 2, 1, + + // For UART to IR Carrier + 111, 63, 79, 102, + 98, 64 +}; + +const u16 OVSR_ADJ_PATCH[] = { + 0x555, 0x555, 0x555, 0x3BB, + 0x555, 0x5DD, 0x3BB, 0x252, + 0x555, 0x252, 0x555, 0x222, + 0x252, 0x3BB, 0x7EF, 0x444, + 0x008, 0x222, 0x7EF, 0x252, + 0x444, 0x008, 0x222, 0x000, + 0x5F7, 0x76D, 0x5AD, 0x010, + 0x5FF, 0x222, 0x76D, + + // For UART to IR Carrier + 0x24A, 0x252, 0x252, 0x5DD, + 0x5AD, 0x5AD +}; +#endif +#if 0 +static s32 +FindElementIndex( + u32 Element, ///< RUART Baudrate + u32* Array ///< Pre-defined Baudrate Array + ) +{ + /* DBG_ENTRANCE; */ + u32 BaudRateNumber = 29; + s32 Result = -1; + u32 Index = 0; + + for (Index = 0; Index < BaudRateNumber && Result == -1; Index++) { + if (Element == Array[Index]) + Result = Index; + } + return Result; //TODO: Error handling +} +#endif +s32 +FindElementIndex_Patch( + u32 Element, ///< RUART Baudrate + u32* Array, ///< Pre-defined Baudrate Array + u32 ElementNo + ) +{ + /* DBG_ENTRANCE; */ + s32 Result = -1; + u32 Index = 0; + + for (Index = 0; Index < ElementNo && Result == -1; Index++) { + if (Element == Array[Index]) + Result = Index; + } + return Result; //TODO: Error handling +} + +HAL_Status +HalRuartInitRtl8195a_Patch( + IN VOID *Data ///< RUART Adapter + ) +{ + /* DBG_ENTRANCE; */ + u32 RegValue; + u32 Divisor; + u32 Dll; + u32 Dlm; + u8 UartIndex; + s32 ElementIndex; + RUART_SPEED_SETTING RuartSpeedSetting; + u8 PinmuxSelect; + + PHAL_RUART_ADAPTER pHalRuartAdapter = (PHAL_RUART_ADAPTER) Data; + + UartIndex = pHalRuartAdapter->UartIndex; + PinmuxSelect = pHalRuartAdapter->PinmuxSelect; + + if (UartIndex > 2) { + DBG_UART_ERR(ANSI_COLOR_MAGENTA"HalRuartInitRtl8195a: Invalid UART Index\n"ANSI_COLOR_RESET); + return HAL_ERR_PARA; + } + + DBG_UART_INFO("%s==>\n", __FUNCTION__); + DBG_UART_INFO("HalRuartInitRtl8195a: [UART %d] PinSel=%d\n", UartIndex, PinmuxSelect); + if(( PinmuxSelect == RUART0_MUX_TO_GPIOE ) && ((UartIndex == 0) || (UartIndex == 1))) { + DBG_UART_WARN(ANSI_COLOR_MAGENTA"UART Pin may conflict with JTAG\r\n"ANSI_COLOR_RESET); + } + + // switch Pin from EEPROM to UART0 + if(( PinmuxSelect == RUART0_MUX_TO_GPIOC ) && (UartIndex == 0)) { + RegValue = HAL_READ32(SYSTEM_CTRL_BASE, 0xa4); + if (RegValue & 0x10) { + DBG_UART_WARN("Disable EEPROM Pin for UART 0\n"); + HAL_WRITE32(SYSTEM_CTRL_BASE, 0xa4, (RegValue & (~0x10))); + } + } + + switch (UartIndex) { + case 0: + /* UART 0 */ + ACTCK_UART0_CCTRL(ON); + SLPCK_UART0_CCTRL(ON); + PinCtrl(UART0, PinmuxSelect, ON); + UART0_FCTRL(ON); + UART0_BD_FCTRL(ON); + break; + + case 1: + /* UART 1 */ + ACTCK_UART1_CCTRL(ON); + SLPCK_UART1_CCTRL(ON); + PinCtrl(UART1, PinmuxSelect, ON); + UART1_FCTRL(ON); + UART1_BD_FCTRL(ON); + break; + + case 2: + /* UART 1 */ + ACTCK_UART2_CCTRL(ON); + SLPCK_UART2_CCTRL(ON); + PinCtrl(UART2, PinmuxSelect, ON); + UART2_FCTRL(ON); + UART2_BD_FCTRL(ON); + break; + + default: + DBG_UART_ERR("Invalid UART Index(%d)\n", UartIndex); + return HAL_ERR_PARA; + } + + /* Reset RX FIFO */ + HalRuartResetRxFifoRtl8195a(Data); + DBG_UART_INFO(ANSI_COLOR_CYAN"HAL UART Init[UART %d]\n"ANSI_COLOR_RESET, UartIndex); + + /* Disable all interrupts */ + HAL_RUART_WRITE32(UartIndex, RUART_INTERRUPT_EN_REG_OFF, 0x00); + + /* Set DLAB bit to 1 to access DLL/DLM */ + RegValue = HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF); + RegValue |= RUART_LINE_CTL_REG_DLAB_ENABLE; + HAL_RUART_WRITE32(UartIndex, RUART_LINE_CTL_REG_OFF, RegValue); + DBG_UART_INFO("[R] RUART_LINE_CTL_REG_OFF(0x0C) = %x\n", HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF)); + + /* Set Baudrate Division */ +#if 1 + ElementIndex = FindElementIndex_Patch(pHalRuartAdapter->BaudRate, (uint32_t*)BAUDRATE_PATCH, sizeof(BAUDRATE_PATCH)/sizeof(u32)); + if (ElementIndex < 0) { + ElementIndex = 5; + DBG_UART_ERR("Invalid BaudRate(%d), Force it as default(%d)\n", pHalRuartAdapter->BaudRate, BAUDRATE_PATCH[ElementIndex]); + } + + RuartSpeedSetting.BaudRate = BAUDRATE_PATCH[ElementIndex]; + RuartSpeedSetting.Ovsr = OVSR_PATCH[ElementIndex]; + RuartSpeedSetting.Div = DIV_PATCH[ElementIndex]; + RuartSpeedSetting.Ovsr_adj = OVSR_ADJ_PATCH[ElementIndex]; +#else + RuartSpeedSetting.BaudRate = 38400; + RuartSpeedSetting.Ovsr = 10; + RuartSpeedSetting.Div = 217; + RuartSpeedSetting.Ovsr_adj = 0x0; +#endif + + DBG_UART_INFO("Baud %d, Ovsr %d, Div %d, OvsrAdj 0x%X\n", + RuartSpeedSetting.BaudRate, + RuartSpeedSetting.Ovsr, + RuartSpeedSetting.Div, + RuartSpeedSetting.Ovsr_adj + ); + /* Divisor = (SYSTEM_CLK / ((ovsr + 5 + ovsr_adj/11) * (UartAdapter.BaudRate))); */ + + /* Set Divisor */ + Divisor = RuartSpeedSetting.Div; + + Dll = Divisor & 0xFF; + Dlm = (Divisor & 0xFF00) >> 8; + +// DBG_UART_INFO("Calculated Dll, Dlm = %02x, %02x\n", Dll, Dlm); +// DBG_UART_INFO("---- Before setting baud rate ----\n"); +// DBG_UART_INFO(" [R] RUART_DLL_OFF(0x00) = %x\n", HAL_RUART_READ32(UartIndex, RUART_DLL_OFF)); +// DBG_UART_INFO(" [R] RUART_DLM_OFF(0x04) = %x\n", HAL_RUART_READ32(UartIndex, RUART_DLM_OFF)); + + HAL_RUART_WRITE32(UartIndex, RUART_DLL_OFF, Dll); + HAL_RUART_WRITE32(UartIndex, RUART_DLM_OFF, Dlm); + +// DBG_UART_INFO("---- After setting baud rate ----\n"); +// DBG_UART_INFO(" [R] RUART_DLL_OFF(0x00) = %x\n", HAL_RUART_READ32(UartIndex, RUART_DLL_OFF)); +// DBG_UART_INFO(" [R] RUART_DLM_OFF(0x04) = %x\n", HAL_RUART_READ32(UartIndex, RUART_DLM_OFF)); + +// DBG_UART_INFO(ANSI_COLOR_CYAN"---- Befor OVSR & OVSR_ADJ ----\n"ANSI_COLOR_RESET); +// RegValue = HAL_RUART_READ32(UartIndex, RUART_SCRATCH_PAD_REG_OFF); +// DBG_UART_INFO("UART%d SPR(0x1C) = %X\n", UartIndex, RegValue); +// RegValue = HAL_RUART_READ32(UartIndex, RUART_STS_REG_OFF); +// DBG_UART_INFO("UART%d SIS(0x20) = %X\n", UartIndex, RegValue); + + /** + * Clean Rx break signal interrupt status at initial stage. + */ + RegValue = HAL_RUART_READ32(UartIndex, RUART_SCRATCH_PAD_REG_OFF); + RegValue |= RUART_SP_REG_RXBREAK_INT_STATUS; + HAL_RUART_WRITE32(UartIndex, RUART_SCRATCH_PAD_REG_OFF, RegValue); + + /* Set OVSR(xfactor) */ + RegValue = HAL_RUART_READ32(UartIndex, RUART_STS_REG_OFF); + RegValue &= ~(RUART_STS_REG_XFACTOR); + RegValue |= (((RuartSpeedSetting.Ovsr - 5) << 4) & RUART_STS_REG_XFACTOR); + HAL_RUART_WRITE32(UartIndex, RUART_STS_REG_OFF, RegValue); + + /* Set OVSR_ADJ[10:0] (xfactor_adj[26:16]) */ + RegValue = HAL_RUART_READ32(UartIndex, RUART_SCRATCH_PAD_REG_OFF); + RegValue &= ~(RUART_SP_REG_XFACTOR_ADJ); + RegValue |= ((RuartSpeedSetting.Ovsr_adj << 16) & RUART_SP_REG_XFACTOR_ADJ); + HAL_RUART_WRITE32(UartIndex, RUART_SCRATCH_PAD_REG_OFF, RegValue); + +// DBG_UART_INFO(ANSI_COLOR_CYAN"---- After OVSR & OVSR_ADJ ----\n"ANSI_COLOR_RESET); +// RegValue = HAL_RUART_READ32(UartIndex, RUART_SCRATCH_PAD_REG_OFF); +// DBG_UART_INFO("UART%d SPR(0x1C) = %X\n", UartIndex, RegValue); +// RegValue = HAL_RUART_READ32(UartIndex, RUART_STS_REG_OFF); +// DBG_UART_INFO("UART%d SIS(0x20) = %X\n", UartIndex, RegValue); + + /* clear DLAB bit */ + RegValue = HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF); + RegValue &= ~(RUART_LINE_CTL_REG_DLAB_ENABLE); + HAL_RUART_WRITE32(UartIndex, RUART_LINE_CTL_REG_OFF, RegValue); +// DBG_UART_INFO("[R] RUART_LINE_CTL_REG_OFF(0x0C) = %x\n", HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF)); +// DBG_UART_INFO("[R] UART%d INT_EN(0x04) = %x\n", UartIndex, pHalRuartAdapter->Interrupts); + RegValue = ((pHalRuartAdapter->Interrupts) & 0xFF); + HAL_RUART_WRITE32(UartIndex, RUART_INTERRUPT_EN_REG_OFF, RegValue); +// DBG_UART_INFO("[W] UART%d INT_EN(0x04) = %x\n", UartIndex, RegValue); + + /* Configure FlowControl */ + if (pHalRuartAdapter->FlowControl == AUTOFLOW_ENABLE) { + RegValue = HAL_RUART_READ32(UartIndex, RUART_MODEM_CTL_REG_OFF); + RegValue |= RUART_MCL_AUTOFLOW_ENABLE; + HAL_RUART_WRITE32(UartIndex, RUART_MODEM_CTL_REG_OFF, RegValue); + } + + /* RUART DMA Initialization */ + HalRuartDmaInitRtl8195a(pHalRuartAdapter); + + DBG_UART_INFO("[R] UART%d LCR(0x%02X): %X\n", UartIndex, RUART_LINE_CTL_REG_OFF, HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF)); + RegValue = HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF); + + /* PARITY CONTROL */ + RegValue &= BIT_CLR_LCR_WLS; + RegValue |= BIT_LCR_WLS(pHalRuartAdapter->WordLen); + + RegValue &= BIT_INVC_LCR_STB_EN; + RegValue |= BIT_LCR_STB_EN(pHalRuartAdapter->StopBit); + + RegValue &= BIT_INVC_LCR_PARITY_EN; + RegValue |= BIT_LCR_PARITY_EN(pHalRuartAdapter->Parity); + + /* PARITY TYPE SELECT */ + RegValue &= BIT_INVC_LCR_PARITY_TYPE; + RegValue |= BIT_LCR_PARITY_TYPE(pHalRuartAdapter->ParityType); + + /* STICK PARITY CONTROL */ + RegValue &= BIT_INVC_LCR_STICK_PARITY_EN; + RegValue |= BIT_LCR_STICK_PARITY_EN(pHalRuartAdapter->StickParity); + + HAL_RUART_WRITE32(UartIndex, RUART_LINE_CTL_REG_OFF, RegValue); + DBG_UART_INFO("[W] UART%d LCR(0x%02X): %X\n", UartIndex, RUART_LINE_CTL_REG_OFF, HAL_RUART_READ32(UartIndex, RUART_LINE_CTL_REG_OFF)); + + /* Need to assert RTS during initial stage. */ + if (pHalRuartAdapter->FlowControl == AUTOFLOW_ENABLE) { + HalRuartRTSCtrlRtl8195a(Data, 1); + } + pHalRuartAdapter->State = HAL_UART_STATE_READY; + + return HAL_OK; +} + +#endif + +/** + * Reset RUART Tx FIFO. + * + * Reset RUART Receiver and Rx FIFO wrapper function. + * It will check LINE_STATUS_REG until reset action completion. + * + * @return BOOL + */ +HAL_Status +HalRuartResetTxFifoRtl8195a( + IN VOID *Data ///< RUART Adapter + ) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter = (PHAL_RUART_ADAPTER) Data; + u8 UartIndex = pHalRuartAdapter->UartIndex; + u32 rx_trigger_lv; + u32 RegValue; + + // Backup the RX FIFO trigger Level setting + rx_trigger_lv = HAL_RUART_READ32(UartIndex, RUART_FIFO_CTL_REG_OFF); + rx_trigger_lv &= 0xC0; // only keep the bit[7:6] + + /* Step 2: Enable clear_txfifo */ + RegValue = (FIFO_CTL_DEFAULT_WITH_FIFO_DMA | RUART_FIFO_CTL_REG_CLEAR_TXFIFO) & (~0xC0); + RegValue |= rx_trigger_lv; + HAL_RUART_WRITE32(UartIndex, RUART_FIFO_CTL_REG_OFF, RegValue); + + //TODO: Check Defautl Value + RegValue = (FIFO_CTL_DEFAULT_WITH_FIFO_DMA & (~0xC0)) | rx_trigger_lv; + HAL_RUART_WRITE32(UartIndex, RUART_FIFO_CTL_REG_OFF, RegValue); + + return HAL_OK; +} + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_32k.c b/component/soc/realtek/8195a/fwlib/src/hal_32k.c new file mode 100644 index 0000000..70d7a59 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_32k.c @@ -0,0 +1,293 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" + +#ifdef CONFIG_TIMER_MODULE + +VOID +En32KCalibration( + VOID +) +{ + u32 Rtemp; + u32 Ttemp = 0; + + //DiagPrintf("32K clock source calibration\n"); + + //set parameter + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + //offset 1 = 0x1500 + Rtemp = 0x811500; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + HalDelayUs(40); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + + //offset 2 = 0x01c0 + Rtemp = 0x8201c0; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + HalDelayUs(40); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + + //offset 4 = 0x0100 + Rtemp = 0x840100; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + HalDelayUs(40); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + + //offset 0 = 0xf980 + Rtemp = 0x80f980; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + HalDelayUs(40); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + + while(1) { + //Polling LOCK + Rtemp = 0x110000; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + //DiagPrintf("Polling lock\n"); + HalDelayUs(40); + + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL1); + if ((Rtemp & 0x3000) != 0x0){ + //DiagPrintf("32.768 Calibration Success\n", Ttemp); + break; + } + else { + Ttemp++; + HalDelayUs(30); + //DiagPrintf("Check lock: %d\n", Ttemp); + //DiagPrintf("0x278: %x\n", Rtemp); + if (Ttemp > 100000) { /*Delay 100ms*/ + DiagPrintf("32K Calibration Fail!!\n", Ttemp); + break; + } + } + } +} + +#if CONFIG_WDG +WDG_ADAPTER WDGAdapter; +extern HAL_TIMER_OP HalTimerOp; + +#ifdef CONFIG_WDG_NORMAL +VOID +WDGInitial( + IN u32 Period +) +{ + u8 CountId; + u16 DivFactor; + u32 CountTemp; + u32 CountProcess = 0; + u32 DivFacProcess = 0; + u32 PeriodProcess = 100*Period; + u32 MinPeriodTemp = 0xFFFFFFFF; + u32 PeriodTemp = 0; + u32 *Reg = (u32*)&(WDGAdapter.Ctrl); + + DBG_8195A(" Period = 0x%08x\n", Period); + + for (CountId = 0; CountId < 12; CountId++) { + CountTemp = ((0x00000001 << (CountId+1))-1); + DivFactor = (u16)((PeriodProcess)/(CountTemp*3)); + + if (DivFactor > 0) { + PeriodTemp = 3*(DivFactor+1)*CountTemp; + if (PeriodProcess < PeriodTemp) { + if (MinPeriodTemp > PeriodTemp) { + MinPeriodTemp = PeriodTemp; + CountProcess = CountId; + DivFacProcess = DivFactor; + } + } + } + } + + DBG_8195A("WdgScalar = 0x%08x\n", DivFacProcess); + DBG_8195A("WdgCunLimit = 0x%08x\n", CountProcess); + + WDGAdapter.Ctrl.WdgScalar = DivFacProcess; + WDGAdapter.Ctrl.WdgEnByte = 0; + WDGAdapter.Ctrl.WdgClear = 1; + WDGAdapter.Ctrl.WdgCunLimit = CountProcess; + WDGAdapter.Ctrl.WdgMode = RESET_MODE; + WDGAdapter.Ctrl.WdgToISR = 0; + + HAL_WRITE32(VENDOR_REG_BASE, 0, (*Reg)); + +} + +VOID +WDGIrqHandle +( + IN VOID *Data +) +{ + u32 temp; + WDG_REG *CtrlReg; + + if (NULL != WDGAdapter.UserCallback) { + WDGAdapter.UserCallback(WDGAdapter.callback_id); + } + + // Clear ISR + temp = HAL_READ32(VENDOR_REG_BASE, 0); + CtrlReg = (WDG_REG*)&temp; + CtrlReg->WdgToISR = 1; // write 1 clear + HAL_WRITE32(VENDOR_REG_BASE, 0, (temp)); +} + +VOID +WDGIrqInitial( + VOID +) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + + WDGAdapter.IrqHandle.Data = (u32)&WDGAdapter; + WDGAdapter.IrqHandle.IrqFun = (IRQ_FUN)WDGIrqHandle; + WDGAdapter.IrqHandle.IrqNum = WDG_IRQ; + WDGAdapter.IrqHandle.Priority = 0; + + InterruptRegister(&(WDGAdapter.IrqHandle)); + InterruptEn(&(WDGAdapter.IrqHandle)); + + WDGAdapter.Ctrl.WdgToISR = 1; // clear ISR first + WDGAdapter.Ctrl.WdgMode = INT_MODE; + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); + WDGAdapter.Ctrl.WdgToISR = 0; +} + +VOID +WDGStart( + VOID +) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + WDGAdapter.Ctrl.WdgEnByte = 0xA5; + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); +} + +VOID +WDGStop( + VOID +) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + WDGAdapter.Ctrl.WdgEnByte = 0; + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); +} + +VOID +WDGRefresh( + VOID +) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + WDGAdapter.Ctrl.WdgClear = 1; + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); +} + +VOID +WDGIrqCallBackReg( + IN VOID *CallBack, + IN u32 Id +) +{ + WDGAdapter.UserCallback = (VOID (*)(u32))CallBack; + WDGAdapter.callback_id = Id; +} + +#endif + +#ifdef CONFIG_GDMA_TEST +VOID +WDGIrqHandle +( + IN VOID *Data +) +{ +} + + +VOID +WDGGtimerHandle +( + IN VOID *Data +) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + WDGAdapter.Ctrl.WdgClear = 1; + DBG_8195A("reset WDG\n"); + if (HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO2) == 0) { + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); + } +} + + +VOID +InitWDGIRQ(VOID) +{ + u32 *Temp = (u32*)&(WDGAdapter.Ctrl); + + WDGAdapter.Ctrl.WdgScalar = 0x96; + WDGAdapter.Ctrl.WdgEnByte = 0xA5; + WDGAdapter.Ctrl.WdgClear = 1; + WDGAdapter.Ctrl.WdgCunLimit = CNTFFFH; + WDGAdapter.Ctrl.WdgMode = RESET_MODE; + WDGAdapter.Ctrl.WdgToISR = 0; + + if (WDGAdapter.Ctrl.WdgMode == INT_MODE) { + + WDGAdapter.IrqHandle.Data = NULL; + WDGAdapter.IrqHandle.IrqFun = (IRQ_FUN)WDGIrqHandle; + WDGAdapter.IrqHandle.IrqNum = WDG_IRQ; + WDGAdapter.IrqHandle.Priority = 0; + + InterruptRegister(&(WDGAdapter.IrqHandle)); + InterruptEn(&(WDGAdapter.IrqHandle)); + } + else { + + WDGAdapter.WdgGTimer.TimerIrqPriority = 0; + WDGAdapter.WdgGTimer.TimerMode = USER_DEFINED; + WDGAdapter.WdgGTimer.IrqDis = OFF; + WDGAdapter.WdgGTimer.TimerId = 2;// + WDGAdapter.WdgGTimer.IrqHandle.IrqFun = (IRQ_FUN)WDGGtimerHandle; + WDGAdapter.WdgGTimer.IrqHandle.IrqNum = TIMER2_7_IRQ; + WDGAdapter.WdgGTimer.IrqHandle.Priority = 0; + WDGAdapter.WdgGTimer.IrqHandle.Data = NULL; + + if ((WDGAdapter.Ctrl.WdgCunLimit == CNTFFFH)&&(WDGAdapter.Ctrl.WdgScalar >= 0x8429)){ + WDGAdapter.WdgGTimer.TimerLoadValueUs = 0xFFFFFFFF - WDGTIMERELY; + } + else { + WDGAdapter.WdgGTimer.TimerLoadValueUs = (BIT0 << (WDGAdapter.Ctrl.WdgCunLimit+1)) + *WDGAdapter.Ctrl.WdgScalar*TIMER_TICK_US - WDGTIMERELY; + } + + HalTimerOp.HalTimerInit((VOID*) &(WDGAdapter.WdgGTimer)); + } + //fill reg + HAL_WRITE32(VENDOR_REG_BASE, 0, ((*Temp))); +} + + +//WDG +VOID HalWdgInit( + VOID +) +{ + +} +#endif //WDG_Verify_Mode +#endif //CONFIG_WDG +#endif //#ifdef CONFIG_TIMER_MODULE diff --git a/component/soc/realtek/8195a/fwlib/src/hal_adc.c b/component/soc/realtek/8195a/fwlib/src/hal_adc.c new file mode 100644 index 0000000..1cae2f0 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_adc.c @@ -0,0 +1,1428 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "basic_types.h" +#include "diag.h" +#include "rand.h" +#include "section_config.h" +#include "rtl_utility.h" +#include "osdep_api.h" +#include "hal_adc.h" +#include "hal_gdma.h" +#include "hal_timer.h" + +#define ADC_STATIC_ALLOC 0 + +static volatile u32 ADCDatBuf[2]; +static volatile u8 ADCFullStsFlag; +static RTK_STATUS +RtkADCPinMuxDeInit( + IN PSAL_ADC_HND pSalADCHND +); + +static RTK_STATUS +RtkADCIrqDeInit( + IN PSAL_ADC_HND pSalADCHND +); + +static RTK_STATUS +RtkADCDMADeInit( + IN PSAL_ADC_HND pSalADCHND +); +/* DAC SAL global variables declaration when kernel disabled */ +#ifndef CONFIG_KERNEL + SRAM_BF_DATA_SECTION + HAL_ADC_OP HalADCOpSAL; +#endif + +#if ADC0_USED /*#if ADC0_USED*/ +#if ADC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + SAL_ADC_MNGT_ADPT SalADC0MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_ADC_HND_PRIV SalADC0HndPriv; + + SRAM_BF_DATA_SECTION + HAL_ADC_INIT_DAT HalADC0InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC0IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalADC0GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalADC0GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC0GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_ADC_USER_CB SalADC0UserCB; + + SRAM_BF_DATA_SECTION + SAL_ADC_USERCB_ADPT SalADC0UserCBAdpt[SAL_ADC_USER_CB_NUM]; +#endif +#endif /*#if ADC0_USED*/ + +#if ADC1_USED /*#if ADC1_USED*/ +#if ADC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + SAL_ADC_MNGT_ADPT SalADC1MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_ADC_HND_PRIV SalADC1HndPriv; + + SRAM_BF_DATA_SECTION + HAL_ADC_INIT_DAT HalADC1InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC1IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalADC1GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalADC1GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC1GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_ADC_USER_CB SalADC1UserCB; + + SRAM_BF_DATA_SECTION + SAL_ADC_USERCB_ADPT SalADC1UserCBAdpt[SAL_ADC_USER_CB_NUM]; +#endif +#endif /*#if ADC1_USED*/ + +#if ADC2_USED /*#if ADC2_USED*/ +#if ADC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + SAL_ADC_MNGT_ADPT SalADC2MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_ADC_HND_PRIV SalADC2HndPriv; + + SRAM_BF_DATA_SECTION + HAL_ADC_INIT_DAT HalADC2InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC2IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalADC2GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalADC2GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC2GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_ADC_USER_CB SalADC2UserCB; + + SRAM_BF_DATA_SECTION + SAL_ADC_USERCB_ADPT SalADC2UserCBAdpt[SAL_ADC_USER_CB_NUM]; +#endif +#endif /*#if ADC2_USED*/ + +#if ADC3_USED /*#if ADC3_USED*/ +#if ADC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + SAL_ADC_MNGT_ADPT SalADC3MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_ADC_HND_PRIV SalADC3HndPriv; + + SRAM_BF_DATA_SECTION + HAL_ADC_INIT_DAT HalADC3InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC3IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalADC3GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalADC3GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE ADC3GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_ADC_USER_CB SalADC3UserCB; + + SRAM_BF_DATA_SECTION + SAL_ADC_USERCB_ADPT SalADC3UserCBAdpt[SAL_ADC_USER_CB_NUM]; +#endif +#endif /*#if ADC3_USED*/ + +/* Global variables */ +u8 SalAdcInitialFlag = 0; +HAL_ADC_INIT_DAT SalAdcInitialDatKeep = {.ADCIdx = 0, + .ADCEn = 0, + .ADCEndian = 0, + .ADCBurstSz = 0, + .ADCCompOnly = 0, + .ADCOneShotEn = 0, + .ADCOverWREn = 0, + .ADCOneShotTD = 0, + .ADCCompCtrl = 0, + .ADCCompTD = 0, + .ADCDataRate = 0, + .ADCAudioEn = 0, + .ADCEnManul = 0, + .ADCDbgSel = 0, + .RSVD0 = 0, + .ADCData = (u32 *)NULL, + .ADCPWCtrl = 0, + .ADCIntrMSK = 0}; + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetMngtAdpt +// +// Description: +// According to the input index, all the memory space are allocated and all the +// related pointers are assigned. The management adapter pointer will be +// returned. +// +// Arguments: +// [in] u8 I2CIdx - +// I2C module index +// +// Return: +// PSAL_I2C_MNGT_ADPT +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +VOID HalADCOpInit( + IN VOID *Data +) +{ + PHAL_ADC_OP pHalAdcOp = (PHAL_ADC_OP) Data; + + pHalAdcOp->HalADCInit = HalADCInit8195a; + pHalAdcOp->HalADCDeInit = HalADCDeInit8195a; + pHalAdcOp->HalADCEnable = HalADCEnableRtl8195a; + pHalAdcOp->HalADCReceive = HalADCReceiveRtl8195a; + pHalAdcOp->HalADCIntrCtrl = HalADCIntrCtrl8195a; + pHalAdcOp->HalADCReadReg = HalADCReadRegRtl8195a; +} + +#ifndef CONFIG_MBED_ENABLED +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetMngtAdpt +// +// Description: +// According to the input index, all the memory space are allocated and all the +// related pointers are assigned. The management adapter pointer will be +// returned. +// +// Arguments: +// [in] u8 I2CIdx - +// I2C module index +// +// Return: +// PSAL_I2C_MNGT_ADPT +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +PSAL_ADC_MNGT_ADPT +RtkADCGetMngtAdpt( + IN u8 ADCIdx +){ + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_USERCB_ADPT pSalADCUserCBAdpt = NULL; + + /* If the kernel is available, Memory-allocation is used. */ +#if (!ADC_STATIC_ALLOC) + pSalADCMngtAdpt = (PSAL_ADC_MNGT_ADPT)RtlZmalloc(sizeof(SAL_ADC_MNGT_ADPT)); + pSalADCMngtAdpt->pSalHndPriv = (PSAL_ADC_HND_PRIV)RtlZmalloc(sizeof(SAL_ADC_HND_PRIV)); + pSalADCMngtAdpt->pHalInitDat = (PHAL_ADC_INIT_DAT)RtlZmalloc(sizeof(HAL_ADC_INIT_DAT)); + pSalADCMngtAdpt->pHalOp = (PHAL_ADC_OP)RtlZmalloc(sizeof(HAL_ADC_OP)); + pSalADCMngtAdpt->pIrqHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalADCMngtAdpt->pUserCB = (PSAL_ADC_USER_CB)RtlZmalloc(sizeof(SAL_ADC_USER_CB)); + pSalADCMngtAdpt->pHalGdmaAdp = (PHAL_GDMA_ADAPTER)RtlZmalloc(sizeof(HAL_GDMA_ADAPTER)); + pSalADCMngtAdpt->pHalGdmaOp = (PHAL_GDMA_OP)RtlZmalloc(sizeof(HAL_GDMA_OP)); + pSalADCMngtAdpt->pIrqGdmaHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalADCUserCBAdpt = (PSAL_ADC_USERCB_ADPT)RtlZmalloc((sizeof(SAL_ADC_USERCB_ADPT)*SAL_ADC_USER_CB_NUM)); +#else + switch (ADCIdx){ + case ADC0_SEL: + { + pSalADCMngtAdpt = &SalADC0MngtAdpt; + pSalADCMngtAdpt->pSalHndPriv = &SalADC0HndPriv; + pSalADCMngtAdpt->pHalInitDat = &HalADC0InitData; + pSalADCMngtAdpt->pHalOp = &HalADCOpSAL; + pSalADCMngtAdpt->pIrqHnd = &ADC0IrqHandleDat; + pSalADCMngtAdpt->pHalGdmaAdp = &HalADC0GdmaAdpt; + pSalADCMngtAdpt->pHalGdmaOp = &HalADC0GdmaOp; + pSalADCMngtAdpt->pIrqGdmaHnd = &ADC0GDMAIrqHandleDat; + pSalADCMngtAdpt->pUserCB = &SalADC0UserCB; + pSalADCUserCBAdpt = &SalADC0UserCBAdpt; + break; + } + + case ADC1_SEL: + { + pSalADCMngtAdpt = &SalADC1MngtAdpt; + pSalADCMngtAdpt->pSalHndPriv = &SalADC1HndPriv; + pSalADCMngtAdpt->pHalInitDat = &HalADC1InitData; + pSalADCMngtAdpt->pHalOp = &HalADCOpSAL; + pSalADCMngtAdpt->pIrqHnd = &ADC1IrqHandleDat; + pSalADCMngtAdpt->pHalGdmaAdp = &HalADC1GdmaAdpt; + pSalADCMngtAdpt->pHalGdmaOp = &HalADC1GdmaOp; + pSalADCMngtAdpt->pIrqGdmaHnd = &ADC1GDMAIrqHandleDat; + pSalADCMngtAdpt->pUserCB = &SalADC1UserCB; + pSalADCUserCBAdpt = &SalADC1UserCBAdpt; + break; + } + + case ADC2_SEL: + { + pSalADCMngtAdpt = &SalADC2MngtAdpt; + pSalADCMngtAdpt->pSalHndPriv = &SalADC2HndPriv; + pSalADCMngtAdpt->pHalInitDat = &HalADC2InitData; + pSalADCMngtAdpt->pHalOp = &HalADCOpSAL; + pSalADCMngtAdpt->pIrqHnd = &ADC2IrqHandleDat; + pSalADCMngtAdpt->pHalGdmaAdp = &HalADC2GdmaAdpt; + pSalADCMngtAdpt->pHalGdmaOp = &HalADC2GdmaOp; + pSalADCMngtAdpt->pIrqGdmaHnd = &ADC2GDMAIrqHandleDat; + pSalADCMngtAdpt->pUserCB = &SalADC2UserCB; + pSalADCUserCBAdpt = &SalADC2UserCBAdpt; + break; + } + + case ADC3_SEL: + { + pSalADCMngtAdpt = &SalADC3MngtAdpt; + pSalADCMngtAdpt->pSalHndPriv = &SalADC3HndPriv; + pSalADCMngtAdpt->pHalInitDat = &HalADC3InitData; + pSalADCMngtAdpt->pHalOp = &HalADCOpSAL; + pSalADCMngtAdpt->pIrqHnd = &ADC3IrqHandleDat; + pSalADCMngtAdpt->pHalGdmaAdp = &HalADC3GdmaAdpt; + pSalADCMngtAdpt->pHalGdmaOp = &HalADC3GdmaOp; + pSalADCMngtAdpt->pIrqGdmaHnd = &ADC3GDMAIrqHandleDat; + pSalADCMngtAdpt->pUserCB = &SalADC3UserCB; + pSalADCUserCBAdpt = &SalADC3UserCBAdpt; + break; + } + default + break; + } +#endif + + /*To assign user callback pointers*/ + pSalADCMngtAdpt->pUserCB->pTXCB = pSalADCUserCBAdpt; + pSalADCMngtAdpt->pUserCB->pTXCCB = (pSalADCUserCBAdpt+1); + pSalADCMngtAdpt->pUserCB->pRXCB = (pSalADCUserCBAdpt+2); + pSalADCMngtAdpt->pUserCB->pRXCCB = (pSalADCUserCBAdpt+3); + pSalADCMngtAdpt->pUserCB->pRDREQCB = (pSalADCUserCBAdpt+4); + pSalADCMngtAdpt->pUserCB->pERRCB = (pSalADCUserCBAdpt+5); + pSalADCMngtAdpt->pUserCB->pDMATXCB = (pSalADCUserCBAdpt+6); + pSalADCMngtAdpt->pUserCB->pDMATXCCB = (pSalADCUserCBAdpt+7); + pSalADCMngtAdpt->pUserCB->pDMARXCB = (pSalADCUserCBAdpt+8); + pSalADCMngtAdpt->pUserCB->pDMARXCCB = (pSalADCUserCBAdpt+9); + + /*To assign the rest pointers*/ + pSalADCMngtAdpt->pSalHndPriv->ppSalADCHnd = (void**)&(pSalADCMngtAdpt->pSalHndPriv); + + /* To assign the default (ROM) HAL OP initialization function */ + pSalADCMngtAdpt->pHalOpInit = &HalADCOpInit; + + /* To assign the default (ROM) HAL GDMA OP initialization function */ + pSalADCMngtAdpt->pHalGdmaOpInit = &HalGdmaOpInit; + + /* To assign the default (ROM) SAL interrupt function */ + pSalADCMngtAdpt->pSalIrqFunc = &ADCISRHandle; + + /* To assign the default (ROM) SAL DMA TX interrupt function */ + pSalADCMngtAdpt->pSalDMAIrqFunc = &ADCGDMAISRHandle; + + return pSalADCMngtAdpt; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CFreeMngtAdpt +// +// Description: +// Free all the previous allocated memory space. +// +// Arguments: +// [in] PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt - +// I2C SAL management adapter pointer +// +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the RtkI2CFreeMngtAdpt succeeded. +// _EXIT_FAILURE if the RtkI2CFreeMngtAdpt failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkADCFreeMngtAdpt( + IN PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt +){ +#ifdef CONFIG_KERNEL + RtlMfree((u8 *)pSalADCMngtAdpt->pUserCB->pTXCB, (sizeof(SAL_ADC_USERCB_ADPT)*SAL_ADC_USER_CB_NUM)); + RtlMfree((u8 *)pSalADCMngtAdpt->pIrqGdmaHnd, sizeof(IRQ_HANDLE)); + RtlMfree((u8 *)pSalADCMngtAdpt->pHalGdmaOp, sizeof(HAL_GDMA_OP)); + RtlMfree((u8 *)pSalADCMngtAdpt->pHalGdmaAdp, sizeof(HAL_GDMA_ADAPTER)); + RtlMfree((u8 *)pSalADCMngtAdpt->pUserCB, sizeof(SAL_ADC_USER_CB)); + RtlMfree((u8 *)pSalADCMngtAdpt->pIrqHnd, sizeof(IRQ_HANDLE)); + RtlMfree((u8 *)pSalADCMngtAdpt->pHalOp, sizeof(HAL_ADC_OP)); + RtlMfree((u8 *)pSalADCMngtAdpt->pHalInitDat, sizeof(HAL_ADC_INIT_DAT)); + RtlMfree((u8 *)pSalADCMngtAdpt->pSalHndPriv, sizeof(SAL_ADC_HND_PRIV)); + RtlMfree((u8 *)pSalADCMngtAdpt, sizeof(SAL_ADC_MNGT_ADPT)); +#else + ; +#endif + + return _EXIT_SUCCESS; +} +#endif + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// I2CISRHandle +// +// Description: +// I2C Interrupt Service Routine. +// According to the input pointer to SAL_I2C_HND, all the rest pointers will be +// found and be used to the rest part of this servie routine. +// The following types of interrupt will be taken care: +// - General Call (providing General Call Callback). Slave receives a general call. +// - STOP Bit (NOT providing General Call Callback) +// - START Bit (NOTproviding General Call Callback) +// - I2C Activity (NOTproviding General Call Callback) +// - RX Done (providing Error Callback). The slave transmitter does NOT +// receive a proper NACK for the end of whole transfer. +// - TX Abort (providing Error Call Callback). The Master/Slave +// transmitting is terminated. +// - RD Req (providing TX and TXC Callback). Slave gets a Read Request +// and starts a slave-transmitter operation. The slave transmit +// data will be written into slave TX FIFO from user data buffer. +// - TX Empty (providing TX and TXC Callback). Master TX FIFO is empty. +// The user transmit data will be written into master TX FIFO +// from user data buffer. +// - TX Over (providing Error Callback). Master TX FIFO is Overflow. +// - RX Full (providing RX and RXC Callback). Master/Slave RX FIFO contains +// data. And the received data will be put into Master/Slave user +// receive data buffer. +// - RX Over (providing Error Callback). Master/Slave RX FIFO is Overflow. +// - RX Under (providing Error Callback). Master/Slave RX FIFO is Underflow. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// NA +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//---------------------------------------------------------------------------------------------------- +VOID +ADCISRHandle( + IN VOID *Data +){ +#ifdef CONFIG_DEBUG_LOG_ADC_HAL + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + PSAL_ADC_USER_CB pSalADCUserCB = NULL; + u8 ADCIrqIdx; + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + ADCIrqIdx = pHalADCInitDat->ADCIdx; + pSalADCUserCB = pSalADCHND->pUserCB; + + DBG_8195A_ADC_LVL(HAL_ADC_LVL,"ADC INTR STS:%x\n",pHalADCOP->HalADCReadReg(pHalADCInitDat, REG_ADC_INTR_STS)); +#else + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + PSAL_ADC_USER_CB pSalADCUserCB = NULL; + u8 ADCIrqIdx; + u32 ADCTemp; + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + ADCIrqIdx = pHalADCInitDat->ADCIdx; + pSalADCUserCB = pSalADCHND->pUserCB; + + if (pSalADCHND->OpType == ADC_RDREG_TYPE){ + ADCFullStsFlag = 1; + ADCDatBuf[0] = (u32)HAL_ADC_READ32(REG_ADC_FIFO_READ); + ADCDatBuf[1] = (u32)HAL_ADC_READ32(REG_ADC_FIFO_READ); + pSalADCHND->pInitDat->ADCIntrMSK = 0; + pHalADCOP->HalADCIntrCtrl(pSalADCHND->pInitDat); + } + else + pHalADCOP->HalADCReadReg(pHalADCInitDat, REG_ADC_INTR_STS); + +#endif +} + +VOID +ADCGDMAISRHandle( + IN VOID *Data +){ + + /* DBG_ENTRANCE; */ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + PSAL_ADC_USER_CB pSalADCUserCB = NULL; + + PHAL_GDMA_ADAPTER pHalADCGdmaAdapter; + PHAL_GDMA_OP pHalADCGdmaOp; + + u8 ADCIrqIdx; + u8 IsrTypeMap = 0; + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + ADCIrqIdx = pHalADCInitDat->ADCIdx; + pSalADCUserCB = pSalADCHND->pUserCB; + + pHalADCGdmaAdapter = pSalADCMngtAdpt->pHalGdmaAdp; + pHalADCGdmaOp = pSalADCMngtAdpt->pHalGdmaOp; + + DBG_8195A_ADC_LVL(HAL_ADC_LVL,"%s\n",__func__); + + if ((pHalADCGdmaAdapter->MaxMuliBlock) == pHalADCGdmaAdapter->MuliBlockCunt+1) { + pSalADCHND->pInitDat->ADCIntrMSK = 0; + pHalADCOP->HalADCIntrCtrl(pSalADCHND->pInitDat); + + /* Clear ADC Status */ + HAL_ADC_READ32(REG_ADC_INTR_STS); + + pSalADCHND->pInitDat->ADCEn = ADC_DISABLE; + pHalADCOP->HalADCEnable(pSalADCHND->pInitDat); + pHalADCGdmaOp->HalGdmaChCleanAutoSrc(pHalADCGdmaAdapter); + pHalADCGdmaOp->HalGdmaChDis(pHalADCGdmaAdapter); + pSalADCHND->DevSts = ADC_STS_IDLE; + + if (pSalADCUserCB->pDMARXCCB->USERCB != NULL) { + pSalADCUserCB->pDMARXCCB->USERCB((VOID*)pSalADCUserCB->pDMARXCCB->USERData); + } + } + + //3 Clear Pending ISR + IsrTypeMap = pHalADCGdmaOp->HalGdmaChIsrClean((VOID*)pHalADCGdmaAdapter); + + //3 Maintain Block Count + if (IsrTypeMap & BlockType) { + pHalADCGdmaAdapter->MuliBlockCunt++; + } +} + +static RTK_STATUS +RtkADCPinMuxInit( + IN PSAL_ADC_HND pSalADCHND +){ + + u32 ADCLocalTemp; + + /* Check the I2C index first */ + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + + ADCLocalTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL2); + ADCLocalTemp |= BIT25; + + /* To release DAC delta sigma clock gating */ + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_SYSPLL_CTRL2,ADCLocalTemp); + + /* Turn on DAC active clock */ + ACTCK_ADC_CCTRL(ON); + + /* Enable DAC0 module */ + ADC0_FCTRL(ON); + + return _EXIT_SUCCESS; + +} + +static RTK_STATUS +RtkADCPinMuxDeInit( + IN PSAL_ADC_HND pSalADCHND +){ + + u32 ADCLocalTemp; + + /* Check the I2C index first */ + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + + /* Turn on DAC active clock */ + ACTCK_ADC_CCTRL(OFF); + + /* Enable DAC1 module */ + ADC0_FCTRL(OFF); + + ADCLocalTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL2); + ADCLocalTemp &= (~BIT25); + + /* To release DAC delta sigma clock gating */ + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_SYSPLL_CTRL2,ADCLocalTemp); + return _EXIT_SUCCESS; +} + + +#if ADC_INTR_OP_TYPE +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqInit +// +// Description: +// I2C interrupt initialization function. +// For I2C interrupt operation mode, I2C module MUST register itself to the platform +// by providing the interrupt handler which contains interrupt input data (arguments), +// interrupt service routine, interrupt number, interrupt priority. And then the interrupt +// should be enabled. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkADCIrqInit( + IN PSAL_ADC_HND pSalADCHND +){ + + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PIRQ_HANDLE pIrqHandle = NULL; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + pIrqHandle = pSalADCMngtAdpt->pIrqHnd; +/* + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; +*/ + + pIrqHandle->Data = (u32)(pSalADCHND); + pIrqHandle->IrqNum = ADC_IRQ; + pIrqHandle->IrqFun = (IRQ_FUN)pSalADCMngtAdpt->pSalIrqFunc; + pIrqHandle->Priority = 5; + InterruptRegister(pIrqHandle); + InterruptEn(pIrqHandle); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqDeInit +// +// Description: +// I2C interrupt de-initialization function. +// According to the given I2C device number, the I2C interrupt will be unreigster +// from the platform and the relative interrupt handler will be cleared. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt de-initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqDeInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqDeInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkADCIrqDeInit( + IN PSAL_ADC_HND pSalADCHND +){ + + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PIRQ_HANDLE pIrqHandle = NULL; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + pIrqHandle = pSalADCMngtAdpt->pIrqHnd; +/* + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; +*/ + InterruptUnRegister(pIrqHandle); + return _EXIT_SUCCESS; + +} + +#endif + + +#if ADC_DMA_OP_TYPE +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqInit +// +// Description: +// I2C interrupt initialization function. +// For I2C interrupt operation mode, I2C module MUST register itself to the platform +// by providing the interrupt handler which contains interrupt input data (arguments), +// interrupt service routine, interrupt number, interrupt priority. And then the interrupt +// should be enabled. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkADCDMAInit( + IN PSAL_ADC_HND pSalADCHND +){ + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_GDMA_ADAPTER pHALADCGdmaAdpt = NULL; + PHAL_GDMA_OP pHALADCGdmaOp = NULL; + PIRQ_HANDLE pIrqHandleADCGdma = NULL; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHALADCGdmaAdpt = pSalADCMngtAdpt->pHalGdmaAdp; + pHALADCGdmaOp = pSalADCMngtAdpt->pHalGdmaOp; + pIrqHandleADCGdma = pSalADCMngtAdpt->pIrqGdmaHnd; +/* + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; +*/ + //HalGdmaOpInit(pHALADCGdmaOp); + pSalADCMngtAdpt->pHalGdmaOpInit(pHALADCGdmaOp); + _memset((void *)pHALADCGdmaAdpt, 0, sizeof(HAL_GDMA_ADAPTER)); + + pHALADCGdmaAdpt->GdmaCtl.IntEn = 1; + + //ADC RX DMA + pHALADCGdmaAdpt->GdmaCtl.SrcTrWidth = TrWidthFourBytes; + pHALADCGdmaAdpt->GdmaCtl.DstTrWidth = TrWidthFourBytes; + pHALADCGdmaAdpt->GdmaCtl.SrcMsize = MsizeEight; + pHALADCGdmaAdpt->GdmaCtl.DestMsize = MsizeEight; + + pHALADCGdmaAdpt->GdmaCtl.Sinc = NoChange; + pHALADCGdmaAdpt->GdmaCtl.Dinc = IncType; + + pHALADCGdmaAdpt->GdmaCtl.Done = 1; + pHALADCGdmaAdpt->GdmaCtl.TtFc = (GDMA_CTL_TT_FC_TYPE)0x2; + + pHALADCGdmaAdpt->GdmaCfg.SrcPer = 12; + pHALADCGdmaAdpt->GdmaCfg.ReloadSrc = 1; + + pHALADCGdmaAdpt->MuliBlockCunt = 0; + pHALADCGdmaAdpt->MaxMuliBlock = 1;//MaxLlp; + + pHALADCGdmaAdpt->GdmaIsrType = (BlockType|TransferType|ErrType); + pHALADCGdmaAdpt->IsrCtrl = ENABLE; + pHALADCGdmaAdpt->GdmaOnOff = ON; + + pHALADCGdmaAdpt->ChNum = 4; + pHALADCGdmaAdpt->ChEn = GdmaCh4; + + pHALADCGdmaAdpt->TestItem = 3; + DBG_ADC_INFO("pSalADCHND->DevNum:%x\n",pSalADCHND->DevNum); + + pHALADCGdmaAdpt->GdmaIndex = 1; + pIrqHandleADCGdma->IrqNum = GDMA1_CHANNEL4_IRQ; + + /* GDMA interrupt register */ + pIrqHandleADCGdma->Data = (u32) (pSalADCHND); + pIrqHandleADCGdma->IrqFun = (IRQ_FUN) pSalADCMngtAdpt->pSalDMAIrqFunc; + pIrqHandleADCGdma->Priority = 2; + InterruptRegister(pIrqHandleADCGdma); + InterruptEn(pIrqHandleADCGdma); + + /* GDMA initialization */ + /* Enable the whole GDMA module first */ + if (pHALADCGdmaAdpt->GdmaIndex == 0) { + ACTCK_GDMA0_CCTRL(ON); + SLPCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + } + else { + ACTCK_GDMA1_CCTRL(ON); + SLPCK_GDMA1_CCTRL(ON); + GDMA1_FCTRL(ON); + } + + pHALADCGdmaOp->HalGdmaOnOff((VOID*)pHALADCGdmaAdpt); + pHALADCGdmaOp->HalGdmaChIsrEnAndDis((VOID*)pHALADCGdmaAdpt); + //pHALADCGdmaOp->HalGdmaChSeting((VOID*)pHALADCGdmaAdpt); + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqDeInit +// +// Description: +// I2C interrupt de-initialization function. +// According to the given I2C device number, the I2C interrupt will be unreigster +// from the platform and the relative interrupt handler will be cleared. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt de-initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqDeInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqDeInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkADCDMADeInit( + IN PSAL_ADC_HND pSalADCHND +){ + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + + PHAL_GDMA_ADAPTER pHALADCGdmaAdpt = NULL; + PHAL_GDMA_OP pHALADCGdmaOp = NULL; + PIRQ_HANDLE pIrqHandleADCGdma = NULL; + + /*To Get the SAL_I2C_MNGT_ADPT Pointer*/ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHALADCGdmaAdpt = pSalADCMngtAdpt->pHalGdmaAdp; + pHALADCGdmaOp = pSalADCMngtAdpt->pHalGdmaOp; + pIrqHandleADCGdma = pSalADCMngtAdpt->pIrqGdmaHnd; + + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + + //HalGdmaOpInit(pHALADCGdmaOp); + pSalADCMngtAdpt->pHalGdmaOpInit(pHALADCGdmaOp); + + pHALADCGdmaAdpt->IsrCtrl = DISABLE; + pHALADCGdmaOp->HalGdmaChIsrEnAndDis((VOID*)pHALADCGdmaAdpt); + pHALADCGdmaOp->HalGdmaChIsrClean((VOID*)pHALADCGdmaAdpt); + pHALADCGdmaOp->HalGdmaChDis((VOID*)pHALADCGdmaAdpt); + + InterruptUnRegister(pIrqHandleADCGdma); +#if 0 + _memset((void *)pIrqHandleDACGdma , 0, sizeof(IRQ_HANDLE)); + _memset((void *)pHALDACGdmaOp , 0, sizeof(HAL_GDMA_OP)); + _memset((void *)pHALDACGdmaAdpt , 0, sizeof(HAL_GDMA_ADAPTER)); +#endif + return _EXIT_SUCCESS; + +} +#endif + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CInit +// +// Description: +// According to the given I2C index, the related SAL_I2C_MNGT_ADPT pointer used +// for retrieving each I2C data sturcture pointer will be reversely parsed first. +// Then, initializing I2C HAL operation, initializing I2C interrupt (if needed), +// initializing I2C DMA (if needed) and initializing I2C pinmux will be done. +// User specified I2C configuration will be assigned to I2C initial data structure +// (PHAL_I2C_INIT_DAT pHalI2CInitDat). I2C HAL initialization is executed after +// all the configuration data taken. +// In the end, I2C module is enabled as a final step of the whole initialization. +// For a slave ack General Call support, an additional step may be followed after +// the above steps. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C initialization process. +// _EXIT_SUCCESS if the RtkI2CInit succeeded. +// _EXIT_FAILURE if the RtkI2CInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkADCInit( + IN VOID *Data +){ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + + DBG_ADC_INFO("%s\n",__func__); + /* To Get the SAL_ADC_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + + /* Check the input I2C index first */ + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + +#if 0 + /* Check the input I2C operation type */ + if (RtkI2COpTypeChk(pSalI2CHND)) + return _EXIT_FAILURE; +#endif + + /* ADC Initial data check, if the setting is different from the previous initial data, + an warning is shown on Log-Uart and directly return from this function */ + if (SalAdcInitialFlag != 0) { + if (_memcmp(pHalADCInitDat, &SalAdcInitialDatKeep, sizeof(HAL_ADC_INIT_DAT))) { + pSalADCMngtAdpt->pHalOpInit(pHalADCOP); + /* DAC Device Status Update */ + pSalADCHND->DevSts = ADC_STS_IDLE; + DBG_8195A("The DAC initial value is different from the previous value.\n"); + } + } + else { + /* ADC Initialize HAL Operations */ + //HalADCOpInit(pHalADCOP); + pSalADCMngtAdpt->pHalOpInit(pHalADCOP); + + /* ADC Interrupt Initialization */ +#if ADC_INTR_OP_TYPE + RtkADCIrqInit(pSalADCHND); +#endif + + /* ADC DMA Initialization */ +#if ADC_DMA_OP_TYPE + RtkADCDMAInit(pSalADCHND); +#endif + + /* ADC Function and Clock Enable*/ + RtkADCPinMuxInit(pSalADCHND); + pHalADCOP->HalADCInit(pSalADCHND->pInitDat); + + if (pSalADCHND->OpType == ADC_DMA_TYPE){ + pSalADCHND->pInitDat->ADCIntrMSK = (BIT_ADC_FIFO_RD_REQ_EN | + BIT_ADC_FIFO_RD_ERROR_EN); + } + else if (pSalADCHND->OpType == ADC_INTR_TYPE){ + pSalADCHND->pInitDat->ADCIntrMSK = (BIT_ADC_FIFO_FULL_EN | + BIT_ADC_FIFO_RD_REQ_EN | + BIT_ADC_FIFO_RD_ERROR_EN); + } + else{ + pSalADCHND->pInitDat->ADCIntrMSK = 0; + } + + + if (pHalADCInitDat->ADCOneShotEn == ADC_FEATURE_ENABLED) { + pSalADCHND->pInitDat->ADCIntrMSK |= BIT_ADC_AWAKE_CPU_EN; + } + pHalADCOP->HalADCIntrCtrl(pSalADCHND->pInitDat); + + //pSalADCHND->pInitDat->ADCEn = ADC_ENABLE; + //pHalADCOP->HalADCEnable(pSalADCHND->pInitDat); + + if (pHalADCInitDat->ADCOneShotEn == ADC_FEATURE_ENABLED) { + HAL_TIMER_WRITE32(TIMER_INTERVAL, 30); + HAL_TIMER_WRITE32(0x1C, 3); + } + + SalAdcInitialFlag |= (0x01 << pSalADCHND->DevNum); + _memcpy(&SalAdcInitialDatKeep, pSalADCHND->pInitDat, sizeof(HAL_ADC_INIT_DAT)); + } + + /* DAC Device Status Update */ + pSalADCHND->DevSts = ADC_STS_IDLE; + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CInit +// +// Description: +// According to the given I2C index, the related SAL_I2C_MNGT_ADPT pointer used +// for retrieving each I2C data sturcture pointer will be reversely parsed first. +// Then, initializing I2C HAL operation, initializing I2C interrupt (if needed), +// initializing I2C DMA (if needed) and initializing I2C pinmux will be done. +// User specified I2C configuration will be assigned to I2C initial data structure +// (PHAL_I2C_INIT_DAT pHalI2CInitDat). I2C HAL initialization is executed after +// all the configuration data taken. +// In the end, I2C module is enabled as a final step of the whole initialization. +// For a slave ack General Call support, an additional step may be followed after +// the above steps. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C initialization process. +// _EXIT_SUCCESS if the RtkI2CInit succeeded. +// _EXIT_FAILURE if the RtkI2CInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkADCDeInit( + IN VOID *Data +){ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + + /* To Get the SAL_ADC_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + + /* Check the input ADC index first */ + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + + SalAdcInitialFlag &= (~(0x01 << pSalADCHND->DevNum)); + + if (SalAdcInitialFlag == 0) { + + /* ADC Initialize HAL Operations */ + HalADCOpInit(pHalADCOP); + + RtkADCPinMuxDeInit(pSalADCHND); + + /* ADC Interrupt Initialization */ +#if ADC_INTR_OP_TYPE + RtkADCIrqDeInit(pSalADCHND); +#endif + + /* ADC DMA Initialization */ +#if ADC_DMA_OP_TYPE + RtkADCDMADeInit(pSalADCHND); +#endif + + pHalADCInitDat->ADCEn = ADC_DISABLE; + pHalADCOP->HalADCEnable(pHalADCInitDat); + pHalADCOP->HalADCDeInit(pHalADCInitDat); + + /* ADC Function and Clock Enable*/ + RtkADCPinMuxDeInit(pSalADCHND); + } + + return _EXIT_SUCCESS; +} + +u32 +RtkADCReceive( + IN VOID *Data +){ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_GDMA_ADAPTER pHALADCGdmaAdpt = NULL; + PHAL_GDMA_OP pHALADCGdmaOp = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + + //PIRQ_HANDLE pIrqHandleADCGdma = NULL; + u32 AdcTempDat; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHALADCGdmaAdpt = pSalADCMngtAdpt->pHalGdmaAdp; + pHALADCGdmaOp = pSalADCMngtAdpt->pHalGdmaOp; + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + + if (pSalADCHND->OpType == ADC_DMA_TYPE) { + /* Clear ADC Status */ + HAL_ADC_READ32(REG_ADC_INTR_STS); + + HalGdmaOpInit(pHALADCGdmaOp); + pHALADCGdmaAdpt->GdmaCtl.BlockSize = pSalADCHND->pRXBuf->DataLen; + pHALADCGdmaAdpt->ChSar = (u32)(ADC_REG_BASE); + pHALADCGdmaAdpt->ChDar = (u32)pSalADCHND->pRXBuf->pDataBuf; + pHALADCGdmaAdpt->MuliBlockCunt = 0; + + pHALADCGdmaOp->HalGdmaChSeting(pHALADCGdmaAdpt); + pHALADCGdmaOp->HalGdmaChEn(pHALADCGdmaAdpt); + + pSalADCHND->DevSts = ADC_STS_RX_ING; + AdcTempDat = HAL_ADC_READ32(REG_ADC_POWER); + AdcTempDat |= BIT_ADC_PWR_AUTO; + HAL_ADC_WRITE32(REG_ADC_POWER, AdcTempDat); + return _EXIT_SUCCESS; + } + return _EXIT_FAILURE; +} + +extern u32 +HalDelayUs( + IN u32 us +); + +u32 +RtkADCReceiveBuf( + IN VOID *Data, + IN u32 *pBuf +){ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PHAL_GDMA_ADAPTER pHALADCGdmaAdpt = NULL; + PHAL_GDMA_OP pHALADCGdmaOp = NULL; + PHAL_ADC_INIT_DAT pHalADCInitDat = NULL; + PHAL_ADC_OP pHalADCOP = NULL; + + //PIRQ_HANDLE pIrqHandleADCGdma = NULL; + u32 AdcTempDat; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + pHALADCGdmaAdpt = pSalADCMngtAdpt->pHalGdmaAdp; + pHALADCGdmaOp = pSalADCMngtAdpt->pHalGdmaOp; + pHalADCInitDat = pSalADCMngtAdpt->pHalInitDat; + pHalADCOP = pSalADCMngtAdpt->pHalOp; + + + + /* Clear ADC Status */ + HAL_ADC_READ32(REG_ADC_INTR_STS); + ADCFullStsFlag = 0; + HalDelayUs(2000); + + pSalADCHND->pInitDat->ADCIntrMSK = (BIT_ADC_FIFO_FULL_EN); + pHalADCOP->HalADCIntrCtrl(pSalADCHND->pInitDat); + pSalADCHND->DevSts = ADC_STS_IDLE; + + pSalADCHND->pInitDat->ADCEn = ADC_ENABLE; + pHalADCOP->HalADCEnable(pSalADCHND->pInitDat); + AdcTempDat = (u32)HAL_ADC_READ32(REG_ADC_POWER); + while((AdcTempDat & BIT_ADC_FIFO_ON_ST) == 0){ + AdcTempDat = (u32)HAL_ADC_READ32(REG_ADC_POWER); + } + while (ADCFullStsFlag == 0){ + } + + pSalADCHND->pInitDat->ADCEn = ADC_DISABLE; + pHalADCOP->HalADCEnable(pSalADCHND->pInitDat); + + AdcTempDat = (u32)HAL_ADC_READ32(REG_ADC_POWER); + while((AdcTempDat & BIT_ADC_FIFO_ON_ST) != 0){ + AdcTempDat = (u32)HAL_ADC_READ32(REG_ADC_POWER); + } + + /* Clear ADC Status */ + HAL_ADC_READ32(REG_ADC_INTR_STS); + ADCFullStsFlag = 0; + + *pBuf = (u32)ADCDatBuf[0]; + *(pBuf+1) = (u32)ADCDatBuf[1]; + ADCDatBuf[0] = 0; + ADCDatBuf[1] = 0; + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetSalHnd +// +// Description: +// Allocation of lower layer memory spaces will be done by invoking RtkI2CGetMngtAdpt +// in this function and return a SAL_I2C_HND pointer to upper layer. +// According to the given I2C index, RtkI2CGetMngtAdpt will allocate all the memory +// space such as SAL_I2C_HND, HAL_I2C_INIT_DAT, SAL_I2C_USER_CB etc. +// +// +// Arguments: +// [in] u8 I2CIdx - +// I2C Index +// +// Return: +// PSAL_I2C_HND +// A pointer to SAL_I2C_HND which is allocated in the lower layer. +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +PSAL_ADC_HND +RtkADCGetSalHnd( + IN u8 ADCIdx +){ + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_HND pSalADCHND = NULL; + + /* Check the user define setting and the given index */ + if (RtkADCIdxChk(ADCIdx)) { + return (PSAL_ADC_HND)NULL; + } + + /* Invoke RtkI2CGetMngtAdpt to get the I2C SAL management adapter pointer */ + pSalADCMngtAdpt = RtkADCGetMngtAdpt(ADCIdx); + + /* Assign the private SAL handle to public SAL handle */ + pSalADCHND = &(pSalADCMngtAdpt->pSalHndPriv->SalADCHndPriv); + + /* Assign the internal HAL initial data pointer to the SAL handle */ + pSalADCHND->pInitDat = pSalADCMngtAdpt->pHalInitDat; + + /* Assign the internal user callback pointer to the SAL handle */ + pSalADCHND->pUserCB = pSalADCMngtAdpt->pUserCB; + + return &(pSalADCMngtAdpt->pSalHndPriv->SalADCHndPriv); + +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetSalHnd +// +// Description: +// Allocation of lower layer memory spaces will be done by invoking RtkI2CGetMngtAdpt +// in this function and return a SAL_I2C_HND pointer to upper layer. +// According to the given I2C index, RtkI2CGetMngtAdpt will allocate all the memory +// space such as SAL_I2C_HND, HAL_I2C_INIT_DAT, SAL_I2C_USER_CB etc. +// +// +// Arguments: +// [in] u8 I2CIdx - +// I2C Index +// +// Return: +// PSAL_I2C_HND +// A pointer to SAL_I2C_HND which is allocated in the lower layer. +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkADCFreeSalHnd( + IN PSAL_ADC_HND pSalADCHND +){ + PSAL_ADC_MNGT_ADPT pSalADCMngtAdpt = NULL; + PSAL_ADC_HND_PRIV pSalADCHNDPriv = NULL; + + /* To get the SAL_DAC_MNGT_ADPT pointer */ + pSalADCHNDPriv = CONTAINER_OF(pSalADCHND, SAL_ADC_HND_PRIV, SalADCHndPriv); + pSalADCMngtAdpt = CONTAINER_OF(pSalADCHNDPriv->ppSalADCHnd, SAL_ADC_MNGT_ADPT, pSalHndPriv); + + /* Invoke RtkDACFreeMngtAdpt to free all the lower layer memory space */ + return (RtkADCFreeMngtAdpt(pSalADCMngtAdpt)); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CLoadDefault +// +// Description: +// Accrording the given I2C index, the default I2C configuration is done. +// +// +// Arguments: +// [in] PSAL_I2C_HND pSalI2CHND - +// SAL I2C handle +// +// Return: +// The status of the loading I2C default configuration. +// _EXIT_SUCCESS if the RtkI2CLoadDefault succeeded. +// _EXIT_FAILURE if the RtkI2CLoadDefault failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkADCLoadDefault( + IN VOID *Data +){ + PSAL_ADC_HND pSalADCHND = (PSAL_ADC_HND) Data; + + /* Check the input ADC index first */ + if (RtkADCIdxChk(pSalADCHND->DevNum)) + return _EXIT_FAILURE; + + /* Load SAL handle default value */ + pSalADCHND->PinMux = 0; + pSalADCHND->OpType = ADC_RDREG_TYPE; + pSalADCHND->DevSts = ADC_STS_UNINITIAL; + pSalADCHND->ADCExd = 0; + pSalADCHND->ErrType = (u32)NULL; + + /* Load HAL initial data structure default value */ + pSalADCHND->pInitDat->ADCIdx = pSalADCHND->DevNum; + pSalADCHND->pInitDat->ADCEn = ADC_DISABLE; + pSalADCHND->pInitDat->ADCEndian = ADC_DATA_ENDIAN_LITTLE; + pSalADCHND->pInitDat->ADCBurstSz = 8; + pSalADCHND->pInitDat->ADCCompOnly = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCOneShotEn = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCOverWREn = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCOneShotTD = 8; + pSalADCHND->pInitDat->ADCCompCtrl = ADC_COMP_SMALLER_THAN; + pSalADCHND->pInitDat->ADCCompTD = 8; + pSalADCHND->pInitDat->ADCDataRate = 0; + pSalADCHND->pInitDat->ADCAudioEn = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCEnManul = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCDbgSel = ADC_DBG_SEL_DISABLE; + pSalADCHND->pInitDat->ADCPWCtrl = 0; + pSalADCHND->pInitDat->ADCIntrMSK = ADC_FEATURE_DISABLED; + pSalADCHND->pInitDat->ADCAnaParAd3 = 0; + return _EXIT_SUCCESS; +} + + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_common.c b/component/soc/realtek/8195a/fwlib/src/hal_common.c new file mode 100644 index 0000000..6594168 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_common.c @@ -0,0 +1,23 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "hal_common.h" + +extern HAL_TIMER_OP HalTimerOp; + +HAL_Status +HalCommonInit(void){ + +#ifdef CONFIG_TIMER_MODULE + HalTimerOpInit_Patch((VOID*)(&HalTimerOp)); +#endif + + return HAL_OK; +} diff --git a/component/soc/realtek/8195a/fwlib/src/hal_dac.c b/component/soc/realtek/8195a/fwlib/src/hal_dac.c new file mode 100644 index 0000000..e149c2a --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_dac.c @@ -0,0 +1,1452 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "rtl_utility.h" +#include "osdep_api.h" +#include "hal_dac.h" +#include "hal_gdma.h" + +#define DAC_STATIC_ALLOC 0 + +/* DAC SAL global variables declaration when kernel disabled */ + +#if DAC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + HAL_DAC_OP HalDACOpSAL; +#endif + + +#if DAC0_USED /*#if DAC0_USED*/ +#if DAC_STATIC_ALLOC + SRAM_BF_DATA_SECTION + SAL_DAC_MNGT_ADPT SalDAC0MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_DAC_HND_PRIV SalDAC0HndPriv; + + SRAM_BF_DATA_SECTION + HAL_DAC_INIT_DAT HalDAC0InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE DAC0IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalDAC0GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalDAC0GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE DAC0GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_DAC_USER_CB SalDAC0UserCB; + + SRAM_BF_DATA_SECTION + SAL_DAC_DMA_USER_DEF SalDAC0DmaUserDef; + + SRAM_BF_DATA_SECTION + SAL_DAC_USERCB_ADPT SalDAC0UserCBAdpt[SAL_DAC_USER_CB_NUM]; +#endif +#endif /*#if DAC0_USED*/ + +#if DAC1_USED /*#if DAC1_USED*/ +#if DAC_STATIC_ALLOC + + SRAM_BF_DATA_SECTION + SAL_DAC_MNGT_ADPT SalDAC1MngtAdpt; + + SRAM_BF_DATA_SECTION + SAL_DAC_HND_PRIV SalDAC1HndPriv; + + SRAM_BF_DATA_SECTION + HAL_DAC_INIT_DAT HalDAC1InitData; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE DAC1IrqHandleDat; + + SRAM_BF_DATA_SECTION + HAL_GDMA_ADAPTER HalDAC1GdmaAdpt; + + SRAM_BF_DATA_SECTION + HAL_GDMA_OP HalDAC1GdmaOp; + + SRAM_BF_DATA_SECTION + IRQ_HANDLE DAC1GDMAIrqHandleDat; + + SRAM_BF_DATA_SECTION + SAL_DAC_USER_CB SalDAC1UserCB; + + SRAM_BF_DATA_SECTION + SAL_DAC_DMA_USER_DEF SalDAC1DmaUserDef; + + SRAM_BF_DATA_SECTION + SAL_DAC_USERCB_ADPT SalDAC1UserCBAdpt[SAL_DAC_USER_CB_NUM]; +#endif +#endif /*#if DAC1_USED*/ + +/* Function prototype */ +VOID DACISRHandle(IN VOID *Data); +VOID DACGDMAISRHandle(IN VOID * Data); +VOID DACGDMALLPISRHandle(IN VOID *Data); + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetMngtAdpt +// +// Description: +// According to the input index, all the memory space are allocated and all the +// related pointers are assigned. The management adapter pointer will be +// returned. +// +// Arguments: +// [in] u8 I2CIdx - +// I2C module index +// +// Return: +// PSAL_I2C_MNGT_ADPT +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +VOID HalDACOpInit( + IN VOID *Data +) +{ + PHAL_DAC_OP pHalDacOp = (PHAL_DAC_OP) Data; + + pHalDacOp->HalDACInit = HalDACInit8195a; + pHalDacOp->HalDACDeInit = HalDACDeInit8195a; + pHalDacOp->HalDACEnable = HalDACEnableRtl8195a; + pHalDacOp->HalDACSend = HalDACSendRtl8195a; + pHalDacOp->HalDACIntrCtrl = HalDACIntrCtrl8195a; + pHalDacOp->HalDACReadReg = HalDACReadRegRtl8195a; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetMngtAdpt +// +// Description: +// According to the input index, all the memory space are allocated and all the +// related pointers are assigned. The management adapter pointer will be +// returned. +// +// Arguments: +// [in] u8 I2CIdx - +// I2C module index +// +// Return: +// PSAL_I2C_MNGT_ADPT +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +PSAL_DAC_MNGT_ADPT +RtkDACGetMngtAdpt( + IN u8 DACIdx +){ + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PSAL_DAC_USERCB_ADPT pSalDACUserCBAdpt = NULL; + + /* If the kernel is available, Memory-allocation is used. */ +#if !DAC_STATIC_ALLOC + + pSalDACMngtAdpt = (PSAL_DAC_MNGT_ADPT)RtlZmalloc(sizeof(SAL_DAC_MNGT_ADPT)); + pSalDACMngtAdpt->pSalHndPriv = (PSAL_DAC_HND_PRIV)RtlZmalloc(sizeof(SAL_DAC_HND_PRIV)); + pSalDACMngtAdpt->pHalInitDat = (PHAL_DAC_INIT_DAT)RtlZmalloc(sizeof(HAL_DAC_INIT_DAT)); + pSalDACMngtAdpt->pHalOp = (PHAL_DAC_OP)RtlZmalloc(sizeof(HAL_DAC_OP)); + pSalDACMngtAdpt->pIrqHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalDACMngtAdpt->pUserCB = (PSAL_DAC_USER_CB)RtlZmalloc(sizeof(SAL_DAC_USER_CB)); + pSalDACMngtAdpt->pDMAConf = (PSAL_DAC_DMA_USER_DEF)RtlZmalloc(sizeof(SAL_DAC_DMA_USER_DEF)); + pSalDACMngtAdpt->pHalGdmaAdp = (PHAL_GDMA_ADAPTER)RtlZmalloc(sizeof(HAL_GDMA_ADAPTER)); + pSalDACMngtAdpt->pHalGdmaOp = (PHAL_GDMA_OP)RtlZmalloc(sizeof(HAL_GDMA_OP)); + pSalDACMngtAdpt->pIrqGdmaHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalDACUserCBAdpt = (PSAL_DAC_USERCB_ADPT)RtlZmalloc((sizeof(SAL_DAC_USERCB_ADPT)*SAL_DAC_USER_CB_NUM)); +#else + switch (DACIdx){ + case DAC0_SEL: + { + pSalDACMngtAdpt = &SalDAC0MngtAdpt; + pSalDACMngtAdpt->pSalHndPriv = &SalDAC0HndPriv; + pSalDACMngtAdpt->pHalInitDat = &HalDAC0InitData; + pSalDACMngtAdpt->pHalOp = &HalDACOpSAL; + pSalDACMngtAdpt->pIrqHnd = &DAC0IrqHandleDat; + pSalDACMngtAdpt->pUserCB = &SalDAC0UserCB; + pSalDACMngtAdpt->pDMAConf = &SalDAC0DmaUserDef; + pSalDACMngtAdpt->pHalGdmaAdp = &HalDAC0GdmaAdpt; + pSalDACMngtAdpt->pHalGdmaOp = &HalDAC0GdmaOp; + pSalDACMngtAdpt->pIrqGdmaHnd = &DAC0IrqHandleDat; + pSalDACUserCBAdpt = &SalDAC0UserCBAdpt; + break; + } + + case DAC1_SEL: + { + pSalDACMngtAdpt = &SalDAC1MngtAdpt; + pSalDACMngtAdpt->pSalHndPriv = &SalDAC1HndPriv; + pSalDACMngtAdpt->pHalInitDat = &HalDAC1InitData; + pSalDACMngtAdpt->pHalOp = &HalDACOpSAL; + pSalDACMngtAdpt->pIrqHnd = &DAC1IrqHandleDat; + pSalDACMngtAdpt->pUserCB = &SalDAC1UserCB; + pSalDACMngtAdpt->pDMAConf = &SalDAC1DmaUserDef; + pSalDACMngtAdpt->pHalGdmaAdp = &HalDAC1GdmaAdpt; + pSalDACMngtAdpt->pHalGdmaOp = &HalDAC1GdmaOp; + pSalDACMngtAdpt->pIrqGdmaHnd = &DAC1IrqHandleDat; + pSalDACUserCBAdpt = &SalDAC1UserCBAdpt; + break; + } + + default: + break; + } +#endif + + /*To assign user callback pointers*/ + pSalDACMngtAdpt->pUserCB->pTXCB = pSalDACUserCBAdpt; + pSalDACMngtAdpt->pUserCB->pTXCCB = (pSalDACUserCBAdpt+1); + pSalDACMngtAdpt->pUserCB->pRXCB = (pSalDACUserCBAdpt+2); + pSalDACMngtAdpt->pUserCB->pRXCCB = (pSalDACUserCBAdpt+3); + pSalDACMngtAdpt->pUserCB->pRDREQCB = (pSalDACUserCBAdpt+4); + pSalDACMngtAdpt->pUserCB->pERRCB = (pSalDACUserCBAdpt+5); + pSalDACMngtAdpt->pUserCB->pDMATXCB = (pSalDACUserCBAdpt+6); + pSalDACMngtAdpt->pUserCB->pDMATXCCB = (pSalDACUserCBAdpt+7); + pSalDACMngtAdpt->pUserCB->pDMARXCB = (pSalDACUserCBAdpt+8); + pSalDACMngtAdpt->pUserCB->pDMARXCCB = (pSalDACUserCBAdpt+9); + + /*To assign the rest pointers*/ + pSalDACMngtAdpt->pSalHndPriv->ppSalDACHnd = (void**)&(pSalDACMngtAdpt->pSalHndPriv); + + /* To assign the default HAL OP initialization function */ + pSalDACMngtAdpt->pHalOpInit = &HalDACOpInit; + + /* To assign the default HAL GDMA OP initialization function */ + pSalDACMngtAdpt->pHalGdmaOpInit = &HalGdmaOpInit; + + /* To assign the default SAL interrupt function */ + pSalDACMngtAdpt->pSalIrqFunc = &DACISRHandle; + + /* To assign the default SAL DMA interrupt function */ + pSalDACMngtAdpt->pSalDMAIrqFunc = &DACGDMAISRHandle; + + return pSalDACMngtAdpt; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CFreeMngtAdpt +// +// Description: +// Free all the previous allocated memory space. +// +// Arguments: +// [in] PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt - +// I2C SAL management adapter pointer +// +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the RtkI2CFreeMngtAdpt succeeded. +// _EXIT_FAILURE if the RtkI2CFreeMngtAdpt failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACFreeMngtAdpt( + IN PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt +){ +#if !DAC_STATIC_ALLOC + RtlMfree((u8 *)pSalDACMngtAdpt->pUserCB->pTXCB, (sizeof(SAL_DAC_USERCB_ADPT)*SAL_DAC_USER_CB_NUM)); + RtlMfree((u8 *)pSalDACMngtAdpt->pIrqGdmaHnd, sizeof(IRQ_HANDLE)); + RtlMfree((u8 *)pSalDACMngtAdpt->pHalGdmaOp, sizeof(HAL_GDMA_OP)); + RtlMfree((u8 *)pSalDACMngtAdpt->pHalGdmaAdp, sizeof(HAL_GDMA_ADAPTER)); + RtlMfree((u8 *)pSalDACMngtAdpt->pDMAConf, sizeof(SAL_DAC_DMA_USER_DEF)); + RtlMfree((u8 *)pSalDACMngtAdpt->pUserCB, sizeof(SAL_DAC_USER_CB)); + RtlMfree((u8 *)pSalDACMngtAdpt->pIrqHnd, sizeof(IRQ_HANDLE)); + RtlMfree((u8 *)pSalDACMngtAdpt->pHalOp, sizeof(HAL_DAC_OP)); + RtlMfree((u8 *)pSalDACMngtAdpt->pHalInitDat, sizeof(HAL_DAC_INIT_DAT)); + RtlMfree((u8 *)pSalDACMngtAdpt->pSalHndPriv, sizeof(SAL_DAC_HND_PRIV)); + RtlMfree((u8 *)pSalDACMngtAdpt, sizeof(SAL_DAC_MNGT_ADPT)); +#else + ; +#endif + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// I2CISRHandle +// +// Description: +// I2C Interrupt Service Routine. +// According to the input pointer to SAL_I2C_HND, all the rest pointers will be +// found and be used to the rest part of this servie routine. +// The following types of interrupt will be taken care: +// - General Call (providing General Call Callback). Slave receives a general call. +// - STOP Bit (NOT providing General Call Callback) +// - START Bit (NOTproviding General Call Callback) +// - I2C Activity (NOTproviding General Call Callback) +// - RX Done (providing Error Callback). The slave transmitter does NOT +// receive a proper NACK for the end of whole transfer. +// - TX Abort (providing Error Call Callback). The Master/Slave +// transmitting is terminated. +// - RD Req (providing TX and TXC Callback). Slave gets a Read Request +// and starts a slave-transmitter operation. The slave transmit +// data will be written into slave TX FIFO from user data buffer. +// - TX Empty (providing TX and TXC Callback). Master TX FIFO is empty. +// The user transmit data will be written into master TX FIFO +// from user data buffer. +// - TX Over (providing Error Callback). Master TX FIFO is Overflow. +// - RX Full (providing RX and RXC Callback). Master/Slave RX FIFO contains +// data. And the received data will be put into Master/Slave user +// receive data buffer. +// - RX Over (providing Error Callback). Master/Slave RX FIFO is Overflow. +// - RX Under (providing Error Callback). Master/Slave RX FIFO is Underflow. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// NA +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//---------------------------------------------------------------------------------------------------- +VOID +DACISRHandle( + IN VOID *Data +){ +#ifdef CONFIG_DEBUG_LOG_DAC_HAL + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PHAL_DAC_INIT_DAT pHalDACInitDat = NULL; + PHAL_DAC_OP pHalDACOP = NULL; + PSAL_DAC_USER_CB pSalDACUserCB = NULL; + u8 DACIrqIdx; + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + pHalDACInitDat = pSalDACMngtAdpt->pHalInitDat; + pHalDACOP = pSalDACMngtAdpt->pHalOp; + DACIrqIdx = pHalDACInitDat->DACIdx; + pSalDACUserCB = pSalDACHND->pUserCB; + + DBG_DAC_INFO("DAC INTR STS:%x\n",pHalDACOP->HalDACReadReg(pHalDACInitDat, REG_DAC_INTR_STS)); + if ((pHalDACOP->HalDACReadReg(pHalDACInitDat, REG_DAC_INTR_STS)) & BIT_DAC_FIFO_STOP_ST){ + pHalDACInitDat->DACEn = DAC_DISABLE; + pHalDACOP->HalDACEnable((void *)pHalDACInitDat); + } +#else + /* To reduce warning */ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PHAL_DAC_INIT_DAT pHalDACInitDat = NULL; + PHAL_DAC_OP pHalDACOP = NULL; + PSAL_DAC_USER_CB pSalDACUserCB = NULL; + u8 DACIrqIdx; + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + pHalDACInitDat = pSalDACMngtAdpt->pHalInitDat; + pHalDACOP = pSalDACMngtAdpt->pHalOp; + DACIrqIdx = pHalDACInitDat->DACIdx; + pSalDACUserCB = pSalDACHND->pUserCB; + + pHalDACOP->HalDACReadReg(pHalDACInitDat, REG_DAC_INTR_STS); +#endif +} + +VOID +DACGDMAISRHandle( + IN VOID *Data +){ + + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + + PHAL_GDMA_ADAPTER pHalDACGdmaAdapter = NULL; + PHAL_GDMA_OP pHalDACGdmaOp = NULL; + PSAL_DAC_USER_CB pSalDACUserCB = NULL; + + u8 IsrTypeMap = 0; + DBG_8195A_DAC_LVL(HAL_DAC_LVL,"%s\n",__func__); + + + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHalDACGdmaAdapter = pSalDACMngtAdpt->pHalGdmaAdp; + pHalDACGdmaOp = pSalDACMngtAdpt->pHalGdmaOp; + pSalDACUserCB = pSalDACMngtAdpt->pUserCB; + + pSalDACMngtAdpt->pHalGdmaOpInit(pHalDACGdmaOp); + + if ((pHalDACGdmaAdapter->MaxMuliBlock) == pHalDACGdmaAdapter->MuliBlockCunt+1) { + pHalDACGdmaOp->HalGdmaChCleanAutoSrc(pHalDACGdmaAdapter); + pHalDACGdmaOp->HalGdmaChDis(pHalDACGdmaAdapter); + pSalDACHND->DevSts = DAC_STS_IDLE; + if (pSalDACUserCB->pDMATXCCB->USERCB != NULL) + { + pSalDACUserCB->pDMATXCCB->USERCB((void*)pSalDACUserCB->pDMATXCCB->USERData); + } + } + else { + //pHalDACGdmaOp->HalGdmaChCleanAutoSrc(pHalDACGdmaAdapter); + pSalDACHND->DevSts = DAC_STS_TX_ING; + + if (pSalDACUserCB->pDMATXCB->USERCB != NULL){ + pSalDACUserCB->pDMATXCB->USERCB((void*)pSalDACUserCB->pDMATXCB->USERData);} + } + + //3 Clear Pending ISR + IsrTypeMap = pHalDACGdmaOp->HalGdmaChIsrClean((VOID*)pHalDACGdmaAdapter); + + //3 Maintain Block Count + if (IsrTypeMap & BlockType) { + pHalDACGdmaAdapter->MuliBlockCunt++; + } + +} + +VOID +DACGDMALLPISRHandle( + IN VOID *Data +){ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + + PHAL_GDMA_ADAPTER pHalDACGdmaAdapter = NULL; + PHAL_GDMA_OP pHalDACGdmaOp = NULL; + PSAL_DAC_USER_CB pSalDACUserCB = NULL; + u8 IsrTypeMap; + + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHalDACGdmaAdapter = pSalDACMngtAdpt->pHalGdmaAdp; + pHalDACGdmaOp = pSalDACMngtAdpt->pHalGdmaOp; + pSalDACUserCB = pSalDACMngtAdpt->pUserCB; + + pSalDACMngtAdpt->pHalGdmaOpInit(pHalDACGdmaOp); +#if 0 + PGDMA_ADAPTER pGdmaAdapte = (PGDMA_ADAPTER) Data; + PHAL_GDMA_ADAPTER pHalGdmaAdapter = pGdmaAdapte->pHalGdmaAdapter; + PGDMA_CH_LLI_ELE pGdmaChLliEle; + struct GDMA_CH_LLI *pGdmaChLli = pHalGdmaAdapter->pLlix; + struct BLOCK_SIZE_LIST *pBlockSizeList = pHalGdmaAdapter->pBlockSizeList; + u32 TotalBlockSize = 0; + u8 IsrTypeMap, BlockIndex; + u8 *pSrc = NULL, *pDst = NULL; + DBG_8195A_DMA("Enter Gdma0 Channel 5 ISr =====>\n"); +#endif + + + + if ((pHalDACGdmaAdapter->MaxMuliBlock) == pHalDACGdmaAdapter->MuliBlockCunt) { + //HalGdmaOp.HalGdmaChCleanAutoSrc(pHalGdmaAdapter); + //DAC0_FCTRL(OFF); + + //HalGdmaOp.HalGdmaChCleanAutoDst(pHalGdmaAdapter); + pHalDACGdmaOp->HalGdmaChDis(pHalDACGdmaAdapter); + + DBG_8195A("dma done\n"); + } + + + IsrTypeMap = pHalDACGdmaOp->HalGdmaChIsrClean((VOID*)pHalDACGdmaAdapter); + + if (IsrTypeMap & BlockType) { + pHalDACGdmaAdapter->MuliBlockCunt++; + } +} + +static RTK_STATUS +RtkDACPinMuxInit( + IN PSAL_DAC_HND pSalDACHND +){ + u32 DACLocalTemp; + + /* Check the I2C index first */ + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + DACLocalTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL2); + DACLocalTemp |= BIT26; + + /* To release DAC delta sigma clock gating */ + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_SYSPLL_CTRL2,DACLocalTemp); + + switch (pSalDACHND->DevNum){ +#if DAC0_USED + case DAC0_SEL: + { + /* Turn on DAC active clock */ + ACTCK_DAC_CCTRL(ON); + + /* Enable DAC0 module */ + DAC0_FCTRL(ON); + break; + } +#endif +#if DAC1_USED + case DAC1_SEL: + { + /* Turn on DAC active clock */ + ACTCK_DAC_CCTRL(ON); + + /* Enable DAC1 module */ + DAC1_FCTRL(ON); + break; + } +#endif + default: + return _EXIT_FAILURE; + } + + return _EXIT_SUCCESS; +} + +static RTK_STATUS +RtkDACPinMuxDeInit( + IN PSAL_DAC_HND pSalDACHND +){ + + u32 DACLocalTemp; + + /* Check the I2C index first */ + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + switch (pSalDACHND->DevNum){ +#if DAC0_USED + case DAC0_SEL: + { + /* Turn on DAC active clock */ + ACTCK_DAC_CCTRL(OFF); + + /* Enable DAC0 module */ + DAC0_FCTRL(OFF); + break; + } +#endif +#if DAC1_USED + case DAC1_SEL: + { + /* Turn on DAC active clock */ + ACTCK_DAC_CCTRL(OFF); + + /* Enable DAC1 module */ + DAC1_FCTRL(OFF); + break; + } +#endif + default: + return _EXIT_FAILURE; + } + + + DACLocalTemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL2); + DACLocalTemp &= (~BIT26); + + /* To release DAC delta sigma clock gating */ + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_SYSPLL_CTRL2,DACLocalTemp); + + return _EXIT_SUCCESS; +} + + +#if DAC_INTR_OP_TYPE +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqInit +// +// Description: +// I2C interrupt initialization function. +// For I2C interrupt operation mode, I2C module MUST register itself to the platform +// by providing the interrupt handler which contains interrupt input data (arguments), +// interrupt service routine, interrupt number, interrupt priority. And then the interrupt +// should be enabled. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkDACIrqInit( + IN PSAL_DAC_HND pSalDACHND +){ + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PIRQ_HANDLE pIrqHandle = NULL; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + pIrqHandle = pSalDACMngtAdpt->pIrqHnd; + + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + switch (pSalDACHND->DevNum){ +#if DAC0_USED + case DAC0_SEL: + { + pIrqHandle->Data = (u32) (pSalDACHND); + pIrqHandle->IrqNum = DAC0_IRQ; + pIrqHandle->IrqFun = (IRQ_FUN) pSalDACMngtAdpt->pSalIrqFunc; + pIrqHandle->Priority = 5; + InterruptRegister(pIrqHandle); + InterruptEn(pIrqHandle); + break; + } +#endif +#if DAC1_USED + case DAC1_SEL: + { + pIrqHandle->Data = (u32) (pSalDACHND); + pIrqHandle->IrqNum = DAC1_IRQ; + pIrqHandle->IrqFun = (IRQ_FUN) pSalDACMngtAdpt->pSalIrqFunc;; + pIrqHandle->Priority = 5; + InterruptRegister(pIrqHandle); + InterruptEn(pIrqHandle); + break; + } +#endif + default: + return _EXIT_FAILURE; + } + + + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqDeInit +// +// Description: +// I2C interrupt de-initialization function. +// According to the given I2C device number, the I2C interrupt will be unreigster +// from the platform and the relative interrupt handler will be cleared. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt de-initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqDeInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqDeInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkDACIrqDeInit( + IN PSAL_DAC_HND pSalDACHND +){ + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PIRQ_HANDLE pIrqHandle = NULL; + + /*To Get the SAL_I2C_MNGT_ADPT Pointer*/ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + pIrqHandle = pSalDACMngtAdpt->pIrqHnd; + + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + InterruptUnRegister(pIrqHandle); + return _EXIT_SUCCESS; +} + +#endif + + +#if DAC_DMA_OP_TYPE +const u16 DACDmaChNo[10] = {GdmaNoCh ,GdmaCh0, + GdmaCh1 ,GdmaCh2, + GdmaCh3 ,GdmaCh4, + GdmaCh5 ,GdmaCh6, + GdmaCh7 ,GdmaAllCh}; + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqInit +// +// Description: +// I2C interrupt initialization function. +// For I2C interrupt operation mode, I2C module MUST register itself to the platform +// by providing the interrupt handler which contains interrupt input data (arguments), +// interrupt service routine, interrupt number, interrupt priority. And then the interrupt +// should be enabled. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkDACDMAInit( + IN PSAL_DAC_HND pSalDACHND +){ + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PHAL_GDMA_ADAPTER pHALDACGdmaAdpt = NULL; + PHAL_GDMA_OP pHALDACGdmaOp = NULL; + PIRQ_HANDLE pIrqHandleDACGdma = NULL; + PSAL_DAC_DMA_USER_DEF pSalDACDmaUserDef = NULL; + + u32 I2CLocalTemp; + u32 I2CLocalMask; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHALDACGdmaAdpt = pSalDACMngtAdpt->pHalGdmaAdp; + pHALDACGdmaOp = pSalDACMngtAdpt->pHalGdmaOp; + pIrqHandleDACGdma = pSalDACMngtAdpt->pIrqGdmaHnd; + pSalDACDmaUserDef = pSalDACHND->pDMAConf; + + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + if (pSalDACHND->DACInType == DAC_INPUT_SINGLE_WR) + return _EXIT_SUCCESS; + + /* GDMA operation initialization */ + //HalGdmaOpInit(pHalI2CGdmaOp); + _memset((void *)pHALDACGdmaAdpt, 0, sizeof(HAL_GDMA_ADAPTER)); + pSalDACMngtAdpt->pHalGdmaOpInit(pHALDACGdmaOp); + pHALDACGdmaOp->HalGdamChInit((VOID*)(pHALDACGdmaAdpt)); + + + + pHALDACGdmaAdpt->GdmaIndex = pSalDACHND->DevNum; + pHALDACGdmaAdpt->GdmaCtl.IntEn = 1; + pHALDACGdmaAdpt->ChNum = pSalDACDmaUserDef->TxChNo; + + pHALDACGdmaAdpt->ChEn = DACDmaChNo[pHALDACGdmaAdpt->ChNum+1]; + pHALDACGdmaAdpt->IsrCtrl = ENABLE; + pHALDACGdmaAdpt->GdmaOnOff = ON; + + + /* GDMA initialization */ + /* Enable the whole GDMA module first */ + if (pHALDACGdmaAdpt->GdmaIndex == 0) { + ACTCK_GDMA0_CCTRL(ON); + SLPCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + } + else { + ACTCK_GDMA1_CCTRL(ON); + SLPCK_GDMA1_CCTRL(ON); + GDMA1_FCTRL(ON); + } + + if (pSalDACHND->DACInType == DAC_INPUT_DMA_ONEBLK) { + //DAC TX DMA + pHALDACGdmaAdpt->GdmaCtl.SrcTrWidth = pSalDACDmaUserDef->TxDatSrcWdth; + pHALDACGdmaAdpt->GdmaCtl.DstTrWidth = pSalDACDmaUserDef->TxDatDstWdth; + pHALDACGdmaAdpt->GdmaCtl.SrcMsize = pSalDACDmaUserDef->TxDatSrcBstSz; + pHALDACGdmaAdpt->GdmaCtl.DestMsize = pSalDACDmaUserDef->TxDatDstBstSz; + + pHALDACGdmaAdpt->GdmaCtl.Sinc = IncType; + pHALDACGdmaAdpt->GdmaCtl.Dinc = NoChange; + + pHALDACGdmaAdpt->GdmaCtl.Done = 1; + pHALDACGdmaAdpt->GdmaCtl.TtFc = 0x01; + + pHALDACGdmaAdpt->GdmaCfg.DestPer = 13; + pHALDACGdmaAdpt->GdmaCfg.ReloadSrc = 1; + + pHALDACGdmaAdpt->MuliBlockCunt = 1; + pHALDACGdmaAdpt->MaxMuliBlock = pSalDACHND->pDMAConf->MaxMultiBlk; + + pHALDACGdmaAdpt->GdmaIsrType = (BlockType|TransferType|ErrType); + + + pHALDACGdmaAdpt->TestItem = 3; + + + //pSalDACMngtAdpt->pSalDMAIrqFunc = &DACGDMAISRHandle; + } + else if (pSalDACHND->DACInType == DAC_INPUT_DMA_LLP) { + //DAC TX DMA + pHALDACGdmaAdpt->GdmaCtl.SrcTrWidth = pSalDACDmaUserDef->TxDatSrcWdth; + pHALDACGdmaAdpt->GdmaCtl.DstTrWidth = pSalDACDmaUserDef->TxDatDstWdth; + pHALDACGdmaAdpt->GdmaCtl.SrcMsize = pSalDACDmaUserDef->TxDatSrcBstSz; + pHALDACGdmaAdpt->GdmaCtl.DestMsize = pSalDACDmaUserDef->TxDatDstBstSz; + + pHALDACGdmaAdpt->GdmaCtl.Dinc = NoChange; + + pHALDACGdmaAdpt->GdmaCtl.Done = 1; + pHALDACGdmaAdpt->GdmaCtl.TtFc = 0x01; + pHALDACGdmaAdpt->GdmaCtl.LlpSrcEn = 1; + + pHALDACGdmaAdpt->GdmaCfg.DestPer = 13; + + pHALDACGdmaAdpt->GdmaIsrType = (BlockType|ErrType); + + /* Enable LLP control */ + pHALDACGdmaAdpt->Llpctrl = pSalDACDmaUserDef->LlpCtrl; + + pHALDACGdmaAdpt->MuliBlockCunt = 1; + pHALDACGdmaAdpt->MaxMuliBlock = pSalDACDmaUserDef->MaxMultiBlk; + + pHALDACGdmaAdpt->TestItem = 9; + + //pSalDACMngtAdpt->pSalDMAIrqFunc = &DACGDMALLPISRHandle; + } + + + /* GDMA interrupt register */ + pIrqHandleDACGdma->Data = (u32) (pSalDACHND); + pIrqHandleDACGdma->IrqNum = GDMA0_CHANNEL0_IRQ + pHALDACGdmaAdpt->ChNum + + ((pHALDACGdmaAdpt->GdmaIndex)*6); + pIrqHandleDACGdma->IrqFun = (IRQ_FUN) pSalDACMngtAdpt->pSalDMAIrqFunc; + pIrqHandleDACGdma->Priority = 2; + InterruptRegister(pIrqHandleDACGdma); + InterruptEn(pIrqHandleDACGdma); + + + pHALDACGdmaOp->HalGdmaOnOff((VOID*)pHALDACGdmaAdpt); + pHALDACGdmaOp->HalGdmaChIsrEnAndDis((VOID*)pHALDACGdmaAdpt); + +#if 0 + /* Enable GDMA according to the DMA type */ + if (pSalDACHND->DACInType == DAC_INPUT_DMA_ONEBLK) { + pHALDACGdmaOp->HalGdmaChSeting((VOID*)pHALDACGdmaAdpt); + } + else if (pSalDACHND->DACInType == DAC_INPUT_DMA_LLP){ + //pHALDACGdmaOp->HalGdmaChBlockSeting((VOID*)(pHALDACGdmaAdpt)); + } +#endif + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CIrqDeInit +// +// Description: +// I2C interrupt de-initialization function. +// According to the given I2C device number, the I2C interrupt will be unreigster +// from the platform and the relative interrupt handler will be cleared. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C interrupt de-initialization process. +// _EXIT_SUCCESS if the RtkI2CIrqDeInit succeeded. +// _EXIT_FAILURE if the RtkI2CIrqDeInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +static RTK_STATUS +RtkDACDMADeInit( + IN PSAL_DAC_HND pSalDACHND +){ + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + + PHAL_GDMA_ADAPTER pHALDACGdmaAdpt = NULL; + PHAL_GDMA_OP pHALDACGdmaOp = NULL; + PIRQ_HANDLE pIrqHandleDACGdma = NULL; + + /*To Get the SAL_I2C_MNGT_ADPT Pointer*/ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHALDACGdmaAdpt = pSalDACMngtAdpt->pHalGdmaAdp; + pHALDACGdmaOp = pSalDACMngtAdpt->pHalGdmaOp; + pIrqHandleDACGdma = pSalDACMngtAdpt->pIrqGdmaHnd; + + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + HalGdmaOpInit(pHALDACGdmaOp); + + pHALDACGdmaAdpt->IsrCtrl = DISABLE; + pHALDACGdmaOp->HalGdmaChIsrEnAndDis((VOID*)pHALDACGdmaAdpt); + pHALDACGdmaOp->HalGdmaChIsrClean((VOID*)pHALDACGdmaAdpt); + pHALDACGdmaOp->HalGdmaChDis((VOID*)pHALDACGdmaAdpt); + + InterruptUnRegister(pIrqHandleDACGdma); +#if 0 + _memset((void *)pIrqHandleDACGdma , 0, sizeof(IRQ_HANDLE)); + _memset((void *)pHALDACGdmaOp , 0, sizeof(HAL_GDMA_OP)); + _memset((void *)pHALDACGdmaAdpt , 0, sizeof(HAL_GDMA_ADAPTER)); +#endif + return _EXIT_SUCCESS; +} + +#endif + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CInit +// +// Description: +// According to the given I2C index, the related SAL_I2C_MNGT_ADPT pointer used +// for retrieving each I2C data sturcture pointer will be reversely parsed first. +// Then, initializing I2C HAL operation, initializing I2C interrupt (if needed), +// initializing I2C DMA (if needed) and initializing I2C pinmux will be done. +// User specified I2C configuration will be assigned to I2C initial data structure +// (PHAL_I2C_INIT_DAT pHalI2CInitDat). I2C HAL initialization is executed after +// all the configuration data taken. +// In the end, I2C module is enabled as a final step of the whole initialization. +// For a slave ack General Call support, an additional step may be followed after +// the above steps. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C initialization process. +// _EXIT_SUCCESS if the RtkI2CInit succeeded. +// _EXIT_FAILURE if the RtkI2CInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACInit( + IN VOID *Data +){ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + //PHAL_DAC_INIT_DAT pHalDACInitDat = NULL; + PHAL_DAC_OP pHalDACOP = NULL; + + u32 DacTemp; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + //pHalDACInitDat = pSalDACMngtAdpt->pHalInitDat; + pHalDACOP = pSalDACMngtAdpt->pHalOp; + + /* Check the input I2C index first */ + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + +#if 0 + /* Check the input I2C operation type */ + if (RtkI2COpTypeChk(pSalI2CHND)) + return _EXIT_FAILURE; +#endif + + /* DAC Initialize HAL Operations */ + HalDACOpInit(pHalDACOP); + + /* DAC Interrupt Initialization */ +#if DAC_INTR_OP_TYPE + RtkDACIrqInit(pSalDACHND); +#endif + + /* DAC DMA Initialization */ +#if DAC_DMA_OP_TYPE + RtkDACDMAInit(pSalDACHND); +#endif + + + /* DAC Function and Clock Enable*/ + RtkDACPinMuxInit(pSalDACHND); + + pHalDACOP->HalDACInit(pSalDACHND->pInitDat); + + #if 1 + HAL_DAC_WRITE32(pSalDACHND->DevNum, REG_DAC_INTR_CTRL, + (BIT_DAC_FIFO_FULL_EN | + BIT_DAC_FIFO_OVERFLOW_EN | + BIT_DAC_FIFO_STOP_EN | + BIT_DAC__WRITE_ERROR_EN | + BIT_DAC_DSC_OVERFLOW0_EN | + BIT_DAC_DSC_OVERFLOW1_EN)); + #else + HAL_DAC_WRITE32(pSalDACHND->DevNum, REG_DAC_INTR_CTRL, + (BIT_DAC_FIFO_FULL_EN| + BIT_DAC_FIFO_OVERFLOW_EN| + BIT_DAC_FIFO_STOP_EN| + BIT_DAC__WRITE_ERROR_EN| + BIT_DAC_DSC_OVERFLOW0_EN| + BIT_DAC_DSC_OVERFLOW1_EN)); + #endif + DBG_DAC_INFO("INTR MSK:%x\n", HAL_DAC_READ32(pSalDACHND->DevNum,REG_DAC_INTR_CTRL)); + + + + DacTemp = HAL_DAC_READ32(pSalDACHND->DevNum, REG_DAC_ANAPAR_DA1); + DacTemp |= (BIT31); + HAL_DAC_WRITE32(pSalDACHND->DevNum, REG_DAC_ANAPAR_DA1, DacTemp); + //DBG_DAC_INFO("REG_DAC_ANAPAR_DA1:%08x\n",DacTemp); + DBG_DAC_INFO("REG_DAC_ANAPAR_DA1:%08x\n",HAL_DAC_READ32(pSalDACHND->DevNum, REG_DAC_ANAPAR_DA1)); + DacTemp = HAL_DAC_READ32(pSalDACHND->DevNum, REG_DAC_CTRL); + DacTemp |= BIT3; + HAL_DAC_WRITE32(pSalDACHND->DevNum, REG_DAC_CTRL, DacTemp); + DBG_DAC_INFO("REG_DAC_CTRL:%08x\n",DacTemp); + + pSalDACHND->pInitDat->DACEn = DAC_ENABLE; + pHalDACOP->HalDACEnable(pSalDACHND->pInitDat); + + /* DAC Device Status Update */ + pSalDACHND->DevSts = DAC_STS_IDLE; + + return _EXIT_SUCCESS; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CInit +// +// Description: +// According to the given I2C index, the related SAL_I2C_MNGT_ADPT pointer used +// for retrieving each I2C data sturcture pointer will be reversely parsed first. +// Then, initializing I2C HAL operation, initializing I2C interrupt (if needed), +// initializing I2C DMA (if needed) and initializing I2C pinmux will be done. +// User specified I2C configuration will be assigned to I2C initial data structure +// (PHAL_I2C_INIT_DAT pHalI2CInitDat). I2C HAL initialization is executed after +// all the configuration data taken. +// In the end, I2C module is enabled as a final step of the whole initialization. +// For a slave ack General Call support, an additional step may be followed after +// the above steps. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C initialization process. +// _EXIT_SUCCESS if the RtkI2CInit succeeded. +// _EXIT_FAILURE if the RtkI2CInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACDeInit( + IN VOID *Data +){ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PHAL_DAC_INIT_DAT pHalDACInitDat = NULL; + PHAL_DAC_OP pHalDACOP = NULL; + + /* To Get the SAL_DAC_MNGT_ADPT Pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHalDACInitDat = pSalDACMngtAdpt->pHalInitDat; + pHalDACOP = pSalDACMngtAdpt->pHalOp; + + /* Check the input DAC index first */ + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + { + /* DAC Initialize HAL Operations */ + HalDACOpInit(pHalDACOP); + + /* DAC Interrupt Initialization */ +#if DAC_INTR_OP_TYPE + RtkDACIrqDeInit(pSalDACHND); +#endif + + /* DAC DMA Initialization */ +#if DAC_DMA_OP_TYPE + RtkDACDMADeInit(pSalDACHND); +#endif + + pHalDACInitDat->DACEn = DAC_DISABLE; + pHalDACOP->HalDACEnable(pHalDACInitDat); + pHalDACOP->HalDACDeInit(pHalDACInitDat); + + /* DAC Function and Clock Enable*/ + RtkDACPinMuxDeInit(pSalDACHND); + } + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CInit +// +// Description: +// According to the given I2C index, the related SAL_I2C_MNGT_ADPT pointer used +// for retrieving each I2C data sturcture pointer will be reversely parsed first. +// Then, initializing I2C HAL operation, initializing I2C interrupt (if needed), +// initializing I2C DMA (if needed) and initializing I2C pinmux will be done. +// User specified I2C configuration will be assigned to I2C initial data structure +// (PHAL_I2C_INIT_DAT pHalI2CInitDat). I2C HAL initialization is executed after +// all the configuration data taken. +// In the end, I2C module is enabled as a final step of the whole initialization. +// For a slave ack General Call support, an additional step may be followed after +// the above steps. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C initialization process. +// _EXIT_SUCCESS if the RtkI2CInit succeeded. +// _EXIT_FAILURE if the RtkI2CInit failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACSend( + IN VOID *Data +){ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PHAL_GDMA_ADAPTER pHALDACGdmaAdpt = NULL; + PHAL_GDMA_OP pHALDACGdmaOp = NULL; + PSAL_DAC_DMA_USER_DEF pSalDACDmaUserDef = NULL; + //PIRQ_HANDLE pIrqHandleDACGdma = NULL; + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + pHALDACGdmaAdpt = pSalDACMngtAdpt->pHalGdmaAdp; + pHALDACGdmaOp = pSalDACMngtAdpt->pHalGdmaOp; + pSalDACDmaUserDef = pSalDACMngtAdpt->pDMAConf; + + switch (pSalDACHND->DACInType) { + case DAC_INPUT_SINGLE_WR: + { + break; + } + case DAC_INPUT_DMA_ONEBLK: + { + HalGdmaOpInit(pHALDACGdmaOp); + + pHALDACGdmaAdpt->GdmaCtl.BlockSize = pSalDACHND->pTXBuf->DataLen; + pHALDACGdmaAdpt->ChSar = (u32)pSalDACHND->pTXBuf->pDataBuf; + pHALDACGdmaAdpt->ChDar = (u32)(DAC_REG_BASE+(pSalDACHND->DevNum*0x800)); + + DBG_DAC_INFO("src addr:%x\n", pHALDACGdmaAdpt->ChSar); + DBG_DAC_INFO("dst addr:%x\n", pHALDACGdmaAdpt->ChDar); + pHALDACGdmaOp->HalGdmaChSeting(pHALDACGdmaAdpt); + + pHALDACGdmaOp->HalGdmaChEn(pHALDACGdmaAdpt); + break; + } + case DAC_INPUT_DMA_LLP: + { + pHALDACGdmaAdpt->Rsvd4to7 = 1; + pHALDACGdmaAdpt->pLlix = (struct GDMA_CH_LLI *)pSalDACDmaUserDef->pLlix; + pHALDACGdmaAdpt->pBlockSizeList = (struct BLOCK_SIZE_LIST *)pSalDACDmaUserDef->pBlockSizeList; + pHALDACGdmaAdpt->ChDar = (u32)(DAC_REG_BASE+(pSalDACHND->DevNum*0x800)); + pHALDACGdmaOp->HalGdmaChBlockSeting(pHALDACGdmaAdpt); + + pHALDACGdmaOp->HalGdmaChEn(pHALDACGdmaAdpt); + break; + } + + default: + return _EXIT_FAILURE; + } + + + return _EXIT_SUCCESS; +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetSalHnd +// +// Description: +// Allocation of lower layer memory spaces will be done by invoking RtkI2CGetMngtAdpt +// in this function and return a SAL_I2C_HND pointer to upper layer. +// According to the given I2C index, RtkI2CGetMngtAdpt will allocate all the memory +// space such as SAL_I2C_HND, HAL_I2C_INIT_DAT, SAL_I2C_USER_CB etc. +// +// +// Arguments: +// [in] u8 I2CIdx - +// I2C Index +// +// Return: +// PSAL_I2C_HND +// A pointer to SAL_I2C_HND which is allocated in the lower layer. +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +PSAL_DAC_HND +RtkDACGetSalHnd( + IN u8 DACIdx +){ + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PSAL_DAC_HND pSalDACHND = NULL; + + /* Check the user define setting and the given index */ + if (RtkDACIdxChk(DACIdx)) { + return (PSAL_DAC_HND)NULL; + } + + /* Invoke RtkI2CGetMngtAdpt to get the I2C SAL management adapter pointer */ + pSalDACMngtAdpt = RtkDACGetMngtAdpt(DACIdx); + + /* Assign the private SAL handle to public SAL handle */ + pSalDACHND = &(pSalDACMngtAdpt->pSalHndPriv->SalDACHndPriv); + + /* Assign the internal HAL initial data pointer to the SAL handle */ + pSalDACHND->pInitDat = pSalDACMngtAdpt->pHalInitDat; + + /* Assign the internal user callback pointer to the SAL handle */ + pSalDACHND->pUserCB = pSalDACMngtAdpt->pUserCB; + + /* Assign the internal user DMA config to the SAL handle */ + pSalDACHND->pDMAConf = pSalDACMngtAdpt->pDMAConf; + + return &(pSalDACMngtAdpt->pSalHndPriv->SalDACHndPriv); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetSalHnd +// +// Description: +// Allocation of lower layer memory spaces will be done by invoking RtkI2CGetMngtAdpt +// in this function and return a SAL_I2C_HND pointer to upper layer. +// According to the given I2C index, RtkI2CGetMngtAdpt will allocate all the memory +// space such as SAL_I2C_HND, HAL_I2C_INIT_DAT, SAL_I2C_USER_CB etc. +// +// +// Arguments: +// [in] u8 I2CIdx - +// I2C Index +// +// Return: +// PSAL_I2C_HND +// A pointer to SAL_I2C_HND which is allocated in the lower layer. +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACFreeSalHnd( + IN PSAL_DAC_HND pSalDACHND +){ + PSAL_DAC_MNGT_ADPT pSalDACMngtAdpt = NULL; + PSAL_DAC_HND_PRIV pSalDACHNDPriv = NULL; + + /* To get the SAL_DAC_MNGT_ADPT pointer */ + pSalDACHNDPriv = CONTAINER_OF(pSalDACHND, SAL_DAC_HND_PRIV, SalDACHndPriv); + pSalDACMngtAdpt = CONTAINER_OF(pSalDACHNDPriv->ppSalDACHnd, SAL_DAC_MNGT_ADPT, pSalHndPriv); + + /* Invoke RtkDACFreeMngtAdpt to free all the lower layer memory space */ + return (RtkDACFreeMngtAdpt(pSalDACMngtAdpt)); + +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CLoadDefault +// +// Description: +// Accrording the given I2C index, the default I2C configuration is done. +// +// +// Arguments: +// [in] PSAL_I2C_HND pSalI2CHND - +// SAL I2C handle +// +// Return: +// The status of the loading I2C default configuration. +// _EXIT_SUCCESS if the RtkI2CLoadDefault succeeded. +// _EXIT_FAILURE if the RtkI2CLoadDefault failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +RTK_STATUS +RtkDACLoadDefault( + IN VOID *Data +){ + PSAL_DAC_HND pSalDACHND = (PSAL_DAC_HND) Data; + + /* Check the input DAC index first */ + if (RtkDACIdxChk(pSalDACHND->DevNum)) + return _EXIT_FAILURE; + + /* Load SAL handle default value */ + pSalDACHND->PinMux = 0; + pSalDACHND->OpType = DAC_POLL_TYPE; + pSalDACHND->DevSts = DAC_STS_UNINITIAL; + pSalDACHND->DACExd = 0; + pSalDACHND->ErrType = (u32)NULL; + + /* Load HAL initial data structure default value */ + pSalDACHND->pInitDat->DACIdx = pSalDACHND->DevNum; + pSalDACHND->pInitDat->DACEn = DAC_DISABLE; + pSalDACHND->pInitDat->DACDataRate = DAC_DATA_RATE_250K; + pSalDACHND->pInitDat->DACEndian = DAC_DATA_ENDIAN_LITTLE; + pSalDACHND->pInitDat->DACBurstSz = 7; + pSalDACHND->pInitDat->DACDbgSel = DAC_DBG_SEL_DISABLE; + pSalDACHND->pInitDat->DACDscDbgSel = DAC_DSC_DBG_SEL_DISABLE; + pSalDACHND->pInitDat->DACBPDsc = DAC_BYPASS_DSC_SEL_DISABLE; + pSalDACHND->pInitDat->DACDeltaSig = 0; + pSalDACHND->pInitDat->DACAnaCtrl0 = 0; + pSalDACHND->pInitDat->DACAnaCtrl1 = 0; + pSalDACHND->pInitDat->DACIntrMSK = DAC_FEATURE_DISABLED; + + /* Load DAC DMA user configuration default value */ + pSalDACHND->pDMAConf->MaxMultiBlk = 5000; + pSalDACHND->pDMAConf->TxDatSrcWdth = TrWidthFourBytes; + pSalDACHND->pDMAConf->TxDatSrcBstSz = MsizeFour; + pSalDACHND->pDMAConf->TxDatDstWdth = TrWidthFourBytes; + pSalDACHND->pDMAConf->TxDatDstBstSz = MsizeFour; + pSalDACHND->pDMAConf->TxChNo = 4; + + return _EXIT_SUCCESS; +} diff --git a/component/soc/realtek/8195a/fwlib/src/hal_gdma.c b/component/soc/realtek/8195a/fwlib/src/hal_gdma.c new file mode 100644 index 0000000..2014cc9 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_gdma.c @@ -0,0 +1,358 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "hal_gdma.h" + +#define MAX_GDMA_INDX 1 +#define MAX_GDMA_CHNL 6 + +static u8 HalGdmaReg[MAX_GDMA_INDX+1]; + +const HAL_GDMA_CHNL GDMA_Chnl_Option[] = { + {0,0,GDMA0_CHANNEL0_IRQ,0}, + {1,0,GDMA1_CHANNEL0_IRQ,0}, + {0,1,GDMA0_CHANNEL1_IRQ,0}, + {1,1,GDMA1_CHANNEL1_IRQ,0}, + {0,2,GDMA0_CHANNEL2_IRQ,0}, + {1,2,GDMA1_CHANNEL2_IRQ,0}, + {0,3,GDMA0_CHANNEL3_IRQ,0}, + {1,3,GDMA1_CHANNEL3_IRQ,0}, + {0,4,GDMA0_CHANNEL4_IRQ,0}, + {1,4,GDMA1_CHANNEL4_IRQ,0}, + {0,5,GDMA0_CHANNEL5_IRQ,0}, + {1,5,GDMA1_CHANNEL5_IRQ,0}, + + {0xff,0,0,0} // end +}; + +const u16 HalGdmaChnlEn[6] = { + GdmaCh0, GdmaCh1, GdmaCh2, GdmaCh3, + GdmaCh4, GdmaCh5 +}; + +VOID HalGdmaOpInit( + IN VOID *Data +) +{ + PHAL_GDMA_OP pHalGdmaOp = (PHAL_GDMA_OP) Data; + + pHalGdmaOp->HalGdmaOnOff = HalGdmaOnOffRtl8195a; + pHalGdmaOp->HalGdamChInit = HalGdamChInitRtl8195a; + pHalGdmaOp->HalGdmaChDis = HalGdmaChDisRtl8195a; + pHalGdmaOp->HalGdmaChEn = HalGdmaChEnRtl8195a; + pHalGdmaOp->HalGdmaChSeting = HalGdmaChSetingRtl8195a; + pHalGdmaOp->HalGdmaChBlockSeting = HalGdmaChBlockSetingRtl8195a; + pHalGdmaOp->HalGdmaChIsrEnAndDis = HalGdmaChIsrEnAndDisRtl8195a; + pHalGdmaOp->HalGdmaChIsrClean = HalGdmaChIsrCleanRtl8195a; + pHalGdmaOp->HalGdmaChCleanAutoSrc = HalGdmaChCleanAutoSrcRtl8195a; + pHalGdmaOp->HalGdmaChCleanAutoDst = HalGdmaChCleanAutoDstRtl8195a; +} + +VOID HalGdmaOn(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + pHalGdmaAdapter->GdmaOnOff = ON; + HalGdmaOnOffRtl8195a((VOID*)pHalGdmaAdapter); +} + +VOID HalGdmaOff(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + pHalGdmaAdapter->GdmaOnOff = OFF; + HalGdmaOnOffRtl8195a((VOID*)pHalGdmaAdapter); +} + +BOOL HalGdmaChInit(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + return (HalGdamChInitRtl8195a((VOID*)pHalGdmaAdapter)); +} + +VOID HalGdmaChDis(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + HalGdmaChDisRtl8195a((VOID*)pHalGdmaAdapter); +} + +VOID HalGdmaChEn(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + HalGdmaChEnRtl8195a((VOID*)pHalGdmaAdapter); +} + +BOOL HalGdmaChSeting(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + return (HalGdmaChSetingRtl8195a((VOID*)pHalGdmaAdapter)); +} + +BOOL HalGdmaChBlockSeting(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + return (HalGdmaChBlockSetingRtl8195a((VOID*)pHalGdmaAdapter)); +} + +VOID HalGdmaChIsrEn(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + pHalGdmaAdapter->IsrCtrl = ENABLE; + HalGdmaChIsrEnAndDisRtl8195a((VOID*)pHalGdmaAdapter); +} + +VOID HalGdmaChIsrDis(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + pHalGdmaAdapter->IsrCtrl = DISABLE; + HalGdmaChIsrEnAndDisRtl8195a((VOID*)pHalGdmaAdapter); +} + +u8 HalGdmaChIsrClean(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + return (HalGdmaChIsrCleanRtl8195a((VOID*)pHalGdmaAdapter)); +} + +VOID HalGdmaChCleanAutoSrc(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + HalGdmaChCleanAutoSrcRtl8195a((VOID*)pHalGdmaAdapter); +} + +VOID HalGdmaChCleanAutoDst(PHAL_GDMA_ADAPTER pHalGdmaAdapter) +{ + HalGdmaChCleanAutoDstRtl8195a((VOID*)pHalGdmaAdapter); +} + +HAL_Status HalGdmaChnlRegister (u8 GdmaIdx, u8 ChnlNum) +{ + u32 mask; + + if ((GdmaIdx > MAX_GDMA_INDX) || (ChnlNum > MAX_GDMA_CHNL)) { + // Invalid GDMA Index or Channel Number + return HAL_ERR_PARA; + } + + mask = 1 << ChnlNum; + + if ((HalGdmaReg[GdmaIdx] & mask) != 0) { + return HAL_BUSY; + } + else { +#if 1 + if (HalGdmaReg[GdmaIdx] == 0) { + if (GdmaIdx == 0) { + ACTCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + } + else { + ACTCK_GDMA1_CCTRL(ON); + GDMA1_FCTRL(ON); + } + } +#endif + HalGdmaReg[GdmaIdx] |= mask; + return HAL_OK; + } +} + +VOID HalGdmaChnlUnRegister (u8 GdmaIdx, u8 ChnlNum) +{ + u32 mask; + + if ((GdmaIdx > MAX_GDMA_INDX) || (ChnlNum > MAX_GDMA_CHNL)) { + // Invalid GDMA Index or Channel Number + return; + } + + mask = 1 << ChnlNum; + + HalGdmaReg[GdmaIdx] &= ~mask; +#if 1 + if (HalGdmaReg[GdmaIdx] == 0) { + if (GdmaIdx == 0) { + ACTCK_GDMA0_CCTRL(OFF); + GDMA0_FCTRL(OFF); + } + else { + ACTCK_GDMA1_CCTRL(OFF); + GDMA1_FCTRL(OFF); + } + } +#endif +} + +PHAL_GDMA_CHNL HalGdmaChnlAlloc (HAL_GDMA_CHNL *pChnlOption) +{ + HAL_GDMA_CHNL *pgdma_chnl; + + pgdma_chnl = pChnlOption; + if (pChnlOption == NULL) { + // Use default GDMA Channel Option table + pgdma_chnl = (HAL_GDMA_CHNL*)&GDMA_Chnl_Option[0]; + } + + while (pgdma_chnl->GdmaIndx <= MAX_GDMA_INDX) { + if (HalGdmaChnlRegister(pgdma_chnl->GdmaIndx, pgdma_chnl->GdmaChnl) == HAL_OK) { + // This GDMA Channel is available + break; + } + pgdma_chnl += 1; + } + + if (pgdma_chnl->GdmaIndx > MAX_GDMA_INDX) { + pgdma_chnl = NULL; + } + + return pgdma_chnl; +} + +VOID HalGdmaChnlFree (HAL_GDMA_CHNL *pChnl) +{ + IRQ_HANDLE IrqHandle; + + IrqHandle.IrqNum = pChnl->IrqNum; + InterruptDis(&IrqHandle); + InterruptUnRegister(&IrqHandle); + HalGdmaChnlUnRegister(pChnl->GdmaIndx, pChnl->GdmaChnl); +} + +VOID HalGdmaMemIrqHandler(VOID *pData) +{ + PHAL_GDMA_OBJ pHalGdmaObj=(PHAL_GDMA_OBJ)pData; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PIRQ_HANDLE pGdmaIrqHandle; + u8 IsrTypeMap; + + pHalGdmaAdapter = &(pHalGdmaObj->HalGdmaAdapter); + pGdmaIrqHandle = &(pHalGdmaObj->GdmaIrqHandle); + // Clean Auto Reload Bit + HalGdmaChCleanAutoDst((VOID*)pHalGdmaAdapter); + + // Clear Pending ISR + IsrTypeMap = HalGdmaChIsrClean((VOID*)pHalGdmaAdapter); + + HalGdmaChDis((VOID*)(pHalGdmaAdapter)); + pHalGdmaObj->Busy = 0; + + if (pGdmaIrqHandle->IrqFun != NULL) { + pGdmaIrqHandle->IrqFun((VOID*)pGdmaIrqHandle->Data); + } +} + +BOOL HalGdmaMemCpyInit(PHAL_GDMA_OBJ pHalGdmaObj) +{ + HAL_GDMA_CHNL *pgdma_chnl; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PIRQ_HANDLE pGdmaIrqHandle; + IRQ_HANDLE IrqHandle; + + pgdma_chnl = HalGdmaChnlAlloc(NULL); // get a whatever GDMA channel + if (NULL == pgdma_chnl) { + DBG_GDMA_ERR("%s: Cannot allocate a GDMA Channel\n", __FUNCTION__); + return _FALSE; + } + + pHalGdmaAdapter = &(pHalGdmaObj->HalGdmaAdapter); + pGdmaIrqHandle = &(pHalGdmaObj->GdmaIrqHandle); + + DBG_GDMA_INFO("%s: Use GDMA%d CH%d\n", __FUNCTION__, pgdma_chnl->GdmaIndx, pgdma_chnl->GdmaChnl); +#if 0 + if (pgdma_chnl->GdmaIndx == 0) { + ACTCK_GDMA0_CCTRL(ON); + GDMA0_FCTRL(ON); + } + else if (pgdma_chnl->GdmaIndx == 1) { + ACTCK_GDMA1_CCTRL(ON); + GDMA1_FCTRL(ON); + } +#endif + _memset((void *)pHalGdmaAdapter, 0, sizeof(HAL_GDMA_ADAPTER)); + +// pHalGdmaAdapter->GdmaCtl.TtFc = TTFCMemToMem; + pHalGdmaAdapter->GdmaCtl.Done = 1; +// pHalGdmaAdapter->MuliBlockCunt = 0; +// pHalGdmaAdapter->MaxMuliBlock = 1; + pHalGdmaAdapter->ChNum = pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->GdmaIndex = pgdma_chnl->GdmaIndx; + pHalGdmaAdapter->ChEn = 0x0101 << pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->GdmaIsrType = (TransferType|ErrType); + pHalGdmaAdapter->IsrCtrl = ENABLE; + pHalGdmaAdapter->GdmaOnOff = ON; + + pHalGdmaAdapter->GdmaCtl.IntEn = 1; +// pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; +// pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; +// pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes; +// pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes; +// pHalGdmaAdapter->GdmaCtl.Dinc = IncType; +// pHalGdmaAdapter->GdmaCtl.Sinc = IncType; + + pGdmaIrqHandle->IrqNum = pgdma_chnl->IrqNum; + pGdmaIrqHandle->Priority = 10; + + IrqHandle.IrqFun = (IRQ_FUN) HalGdmaMemIrqHandler; + IrqHandle.Data = (u32) pHalGdmaObj; + IrqHandle.IrqNum = pGdmaIrqHandle->IrqNum; + IrqHandle.Priority = pGdmaIrqHandle->Priority; + + InterruptRegister(&IrqHandle); + InterruptEn(&IrqHandle); + pHalGdmaObj->Busy = 0; + + return _TRUE; +} + +VOID HalGdmaMemCpyDeInit(PHAL_GDMA_OBJ pHalGdmaObj) +{ + HAL_GDMA_CHNL GdmaChnl; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PIRQ_HANDLE pGdmaIrqHandle; + + pHalGdmaAdapter = &(pHalGdmaObj->HalGdmaAdapter); + pGdmaIrqHandle = &(pHalGdmaObj->GdmaIrqHandle); + + GdmaChnl.GdmaIndx = pHalGdmaAdapter->GdmaIndex; + GdmaChnl.GdmaChnl = pHalGdmaAdapter->ChNum; + GdmaChnl.IrqNum = pGdmaIrqHandle->IrqNum; + HalGdmaChnlFree(&GdmaChnl); +} + +// If multi-task using the same GDMA Object, then it needs a mutex to protect this procedure +VOID* HalGdmaMemCpy(PHAL_GDMA_OBJ pHalGdmaObj, void* pDest, void* pSrc, u32 len) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + + if (pHalGdmaObj->Busy) { + DBG_GDMA_ERR("%s: ==> GDMA is Busy\r\n", __FUNCTION__); + return 0; + } + pHalGdmaObj->Busy = 1; + pHalGdmaAdapter = &(pHalGdmaObj->HalGdmaAdapter); + + DBG_GDMA_INFO("%s: ==> Src=0x%x Dst=0x%x Len=%d\r\n", __FUNCTION__, pSrc, pDest, len); + if ((((u32)pSrc & 0x03)==0) && + (((u32)pDest & 0x03)==0) && + ((len & 0x03)== 0)) { + // 4-bytes aligned, move 4 bytes each transfer + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthFourBytes; + pHalGdmaAdapter->GdmaCtl.BlockSize = len >> 2; + } + else { + pHalGdmaAdapter->GdmaCtl.SrcMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.SrcTrWidth = TrWidthOneByte; + pHalGdmaAdapter->GdmaCtl.DestMsize = MsizeEight; + pHalGdmaAdapter->GdmaCtl.DstTrWidth = TrWidthOneByte; + pHalGdmaAdapter->GdmaCtl.BlockSize = len; + } + + pHalGdmaAdapter->ChSar = (u32)pSrc; + pHalGdmaAdapter->ChDar = (u32)pDest; + pHalGdmaAdapter->PacketLen = len; + + HalGdmaOn((pHalGdmaAdapter)); + HalGdmaChIsrEn((pHalGdmaAdapter)); + HalGdmaChSeting((pHalGdmaAdapter)); + HalGdmaChEn((pHalGdmaAdapter)); + + return (pDest); +} diff --git a/component/soc/realtek/8195a/fwlib/src/hal_gpio.c b/component/soc/realtek/8195a/fwlib/src/hal_gpio.c new file mode 100644 index 0000000..7c647d7 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_gpio.c @@ -0,0 +1,144 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" + +#ifdef CONFIG_GPIO_EN + +HAL_GPIO_DATA_SECTION HAL_GPIO_ADAPTER gHAL_Gpio_Adapter; +extern PHAL_GPIO_ADAPTER _pHAL_Gpio_Adapter; + +extern VOID GPIO_PullCtrl_8195a(u32 chip_pin, u8 pull_type); + +/** + * @brief To get the GPIO IP Pin name for the given chip pin name + * + * @param chip_pin: The chip pin name. + * + * @retval The gotten GPIO IP pin name + */ +HAL_GPIO_TEXT_SECTION u32 +HAL_GPIO_GetPinName( + u32 chip_pin +) +{ + return HAL_GPIO_GetIPPinName_8195a((u32)chip_pin); +} + +/** + * @brief Set the GPIO pad Pull type + * + * @param pin: The pin for pull type control. + * @param mode: the pull type for the pin. + * @return None + */ +VOID +HAL_GPIO_PullCtrl( + u32 pin, + u32 mode +) +{ + u8 pull_type; + + DBG_GPIO_INFO("%s: pin=0x%x mode=%d\n ", __FUNCTION__, (u32)pin, (u32)mode); + + switch (mode) { + case hal_PullNone: + pull_type = DIN_PULL_NONE; + break; + + case hal_PullDown: + pull_type = DIN_PULL_LOW; + break; + + case hal_PullUp: + pull_type = DIN_PULL_HIGH; + break; + + case hal_OpenDrain: + default: + pull_type = DIN_PULL_NONE; + break; + } + +// HAL_GPIO_PullCtrl_8195a (pin, pull_type); + GPIO_PullCtrl_8195a (pin, pull_type); +} + + +/** + * @brief Initializes a GPIO Pin by the GPIO_Pin parameters. + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin initialization. + * + * @retval HAL_Status + */ +HAL_GPIO_TEXT_SECTION VOID +HAL_GPIO_Init( + HAL_GPIO_PIN *GPIO_Pin +) +{ + if (_pHAL_Gpio_Adapter == NULL) { + _pHAL_Gpio_Adapter = &gHAL_Gpio_Adapter; + DBG_GPIO_INFO("%s: Initial GPIO Adapter\n ", __FUNCTION__); + } + + HAL_GPIO_Init_8195a(GPIO_Pin); +} + +/** + * @brief Initializes a GPIO Pin as a interrupt signal + * + * @param GPIO_Pin: The data structer which contains the parameters for the GPIO Pin initialization. + * + * @retval HAL_Status + */ +VOID +HAL_GPIO_Irq_Init( + HAL_GPIO_PIN *GPIO_Pin +) +{ + if (_pHAL_Gpio_Adapter == NULL) { + _pHAL_Gpio_Adapter = &gHAL_Gpio_Adapter; + DBG_GPIO_INFO("%s: Initial GPIO Adapter\n ", __FUNCTION__); + } + + if (_pHAL_Gpio_Adapter->IrqHandle.IrqFun == NULL) { + _pHAL_Gpio_Adapter->IrqHandle.IrqFun = HAL_GPIO_MbedIrqHandler_8195a; + _pHAL_Gpio_Adapter->IrqHandle.Priority = 0x10; + HAL_GPIO_RegIrq_8195a(&_pHAL_Gpio_Adapter->IrqHandle); + InterruptEn(&_pHAL_Gpio_Adapter->IrqHandle); + DBG_GPIO_INFO("%s: Initial GPIO IRQ Adapter\n ", __FUNCTION__); + } + + DBG_GPIO_INFO("%s: GPIO(name=0x%x)(mode=%d)\n ", __FUNCTION__, GPIO_Pin->pin_name, + GPIO_Pin->pin_mode); + HAL_GPIO_Init_8195a(GPIO_Pin); +} + +/** + * @brief UnInitial GPIO Adapter + * + * + * @retval HAL_Status + */ +VOID +HAL_GPIO_IP_DeInit( + VOID +) +{ + if (_pHAL_Gpio_Adapter != NULL) { + InterruptDis(&_pHAL_Gpio_Adapter->IrqHandle); + HAL_GPIO_UnRegIrq_8195a(&_pHAL_Gpio_Adapter->IrqHandle); + _pHAL_Gpio_Adapter = NULL; + } + +} + +#endif // CONFIG_GPIO_EN diff --git a/component/soc/realtek/8195a/fwlib/src/hal_i2c.c b/component/soc/realtek/8195a/fwlib/src/hal_i2c.c new file mode 100644 index 0000000..0b4e628 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_i2c.c @@ -0,0 +1,2805 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include +#include "hal_i2c.h" + +//--------------------------------------------------------------------------------------------------- +//External functions +//--------------------------------------------------------------------------------------------------- +extern HAL_TIMER_OP HalTimerOp; + +#define I2C_STATIC_ALLOC 1 +/* I2C SAL global variables declaration when kernel disabled */ +#ifdef I2C_STATIC_ALLOC + HAL_I2C_OP HalI2COpSAL; +#endif + +#if I2C0_USED /*#if I2C0_USED*/ +#ifdef I2C_STATIC_ALLOC + SAL_I2C_MNGT_ADPT SalI2C0MngtAdpt; + + SAL_I2C_HND_PRIV SalI2C0HndPriv; + + HAL_I2C_INIT_DAT HalI2C0InitData; + + IRQ_HANDLE I2C0IrqHandleDat; + + HAL_GDMA_ADAPTER HalI2C0TxGdmaAdpt; + + HAL_GDMA_ADAPTER HalI2C0RxGdmaAdpt; + + HAL_GDMA_OP HalI2C0GdmaOp; + + IRQ_HANDLE I2C0TxGdmaIrqHandleDat; + + IRQ_HANDLE I2C0RxGdmaIrqHandleDat; + + SAL_I2C_USER_CB SalI2C0UserCB; + + SAL_I2C_USERCB_ADPT SalI2C0UserCBAdpt[SAL_USER_CB_NUM]; + + SAL_I2C_DMA_USER_DEF SalI2C0DmaUserDef; +#endif +#endif /*#if I2C0_USED*/ + +#if I2C1_USED /*#if I2C1_USED*/ +#ifdef I2C_STATIC_ALLOC + SAL_I2C_MNGT_ADPT SalI2C1MngtAdpt; + + SAL_I2C_HND_PRIV SalI2C1HndPriv; + + HAL_I2C_INIT_DAT HalI2C1InitData; + + IRQ_HANDLE I2C1IrqHandleDat; + + HAL_GDMA_ADAPTER HalI2C1TxGdmaAdpt; + + HAL_GDMA_ADAPTER HalI2C1RxGdmaAdpt; + + HAL_GDMA_OP HalI2C1GdmaOp; + + IRQ_HANDLE I2C1TxGdmaIrqHandleDat; + + IRQ_HANDLE I2C1RxGdmaIrqHandleDat; + + SAL_I2C_USER_CB SalI2C1UserCB; + + SAL_I2C_USERCB_ADPT SalI2C1UserCBAdpt[SAL_USER_CB_NUM]; + + SAL_I2C_DMA_USER_DEF SalI2C1DmaUserDef; +#endif +#endif /*#if I2C1_USED*/ + +#if I2C2_USED /*#if I2C2_USED*/ +#ifdef I2C_STATIC_ALLOC + + SAL_I2C_MNGT_ADPT SalI2C2MngtAdpt; + + SAL_I2C_HND_PRIV SalI2C2HndPriv; + + HAL_I2C_INIT_DAT HalI2C2InitData; + + IRQ_HANDLE I2C2IrqHandleDat; + + HAL_GDMA_ADAPTER HalI2C2TxGdmaAdpt; + + HAL_GDMA_ADAPTER HalI2C2RxGdmaAdpt; + + HAL_GDMA_OP HalI2C2GdmaOp; + + IRQ_HANDLE I2C2TxGdmaIrqHandleDat; + + IRQ_HANDLE I2C2RxGdmaIrqHandleDat; + + SAL_I2C_USER_CB SalI2C2UserCB; + + SAL_I2C_USERCB_ADPT SalI2C2UserCBAdpt[SAL_USER_CB_NUM]; + + SAL_I2C_DMA_USER_DEF SalI2C2DmaUserDef; +#endif +#endif /*#if I2C2_USED*/ + +#if I2C3_USED /*#if I2C3_USED*/ +#ifdef I2C_STATIC_ALLOC + + SAL_I2C_MNGT_ADPT SalI2C3MngtAdpt; + + SAL_I2C_HND_PRIV SalI2C3HndPriv; + + HAL_I2C_INIT_DAT HalI2C3InitData; + + IRQ_HANDLE I2C3IrqHandleDat; + + HAL_GDMA_ADAPTER HalI2C3TxGdmaAdpt; + + HAL_GDMA_ADAPTER HalI2C3RxGdmaAdpt; + + HAL_GDMA_OP HalI2C3GdmaOp; + + IRQ_HANDLE I2C3TxGdmaIrqHandleDat; + + IRQ_HANDLE I2C3RxGdmaIrqHandleDat; + + SAL_I2C_USER_CB SalI2C3UserCB; + + SAL_I2C_USERCB_ADPT SalI2C3UserCBAdpt[SAL_USER_CB_NUM]; + + SAL_I2C_DMA_USER_DEF SalI2C3DmaUserDef; +#endif +#endif /*#if I2C3_USED*/ + +/* Used only for A~C Version */ +#ifndef CONFIG_CHIP_D_CUT +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CSendRtl8195a +// +// Description: +// Send one byte to the I2C internal fifo, it will generate START and STOP bit +// automatically. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// _EXIT_SUCCESS if the sending succeeded. +// _EXIT_FAILURE if the sending failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +HAL_Status +HalI2CSendRtl8195a( + IN VOID *Data +){ + PHAL_I2C_INIT_DAT pHalI2CInitData = (PHAL_I2C_INIT_DAT)Data; + u8 I2CIdx = pHalI2CInitData->I2CIdx; + u8 *pDat = pHalI2CInitData->I2CRWData; + u8 I2CCmd = pHalI2CInitData->I2CCmd; + u8 I2CStop = pHalI2CInitData->I2CStop; + u8 I2CReSTR= pHalI2CInitData->I2CReSTR; + + DBG_I2C_INFO("HalI2CSendRtl8195a\n"); + DBG_I2C_INFO("I2C Index: %x\n",I2CIdx); + + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_DATA_CMD, + *(pDat) | + BIT_CTRL_IC_DATA_CMD_RESTART(I2CReSTR)| + BIT_CTRL_IC_DATA_CMD_CMD(I2CCmd) | + BIT_CTRL_IC_DATA_CMD_STOP(I2CStop)); + + return (HAL_OK); +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CInit8195a +// +// Description: +// To initialize I2C module by using the given data. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the DeInit process. +// _EXIT_SUCCESS if the initialization succeeded. +// _EXIT_FAILURE if the initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +HAL_Status +HalI2CInit8195a( + IN VOID *Data +) +{ + PHAL_I2C_INIT_DAT pHalI2CInitData = (PHAL_I2C_INIT_DAT)Data; + + u8 Master; + u8 I2CIdx; + u8 SpdMd; + u8 AddrMd; + u8 ReSTR; + u8 StartByte; + u8 Specical; + u8 GC; + u16 I2CAckAddr; + u16 SdaHd; + u8 SdaSetup; + u8 RXTL; + u8 TXTL; + u8 SlvNoAck; + u32 INTRMsk; + u8 TxDMARqLv; + u8 RxDMARqLv; + + /* Get the I2C parameters*/ + I2CIdx = pHalI2CInitData->I2CIdx; + SpdMd = pHalI2CInitData->I2CSpdMod; + AddrMd = pHalI2CInitData->I2CAddrMod; + I2CAckAddr = pHalI2CInitData->I2CAckAddr; + Master = pHalI2CInitData->I2CMaster; + SdaHd = pHalI2CInitData->I2CSdaHd; + SdaSetup = pHalI2CInitData->I2CSetup; + + ReSTR = pHalI2CInitData->I2CReSTR; + GC = pHalI2CInitData->I2CGC; + StartByte = pHalI2CInitData->I2CStartB; + SlvNoAck = pHalI2CInitData->I2CSlvNoAck; + + RXTL = pHalI2CInitData->I2CRXTL; + TXTL = pHalI2CInitData->I2CTXTL; + + TxDMARqLv = pHalI2CInitData->I2CTxDMARqLv; + RxDMARqLv = pHalI2CInitData->I2CRxDMARqLv; + + /* Disable the IC first */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_ENABLE,BIT_CTRL_IC_ENABLE(0)); + + /* Master case*/ + if (Master) { + /*RESTART MUST be set in these condition in Master mode. + But it might be NOT compatible in old slaves.*/ + if ((AddrMd == I2C_ADDR_10BIT) || (SpdMd == I2C_HS_MODE)) + ReSTR = 1; + + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_CON, + (BIT_CTRL_IC_CON_IC_SLAVE_DISABLE(1) | + BIT_CTRL_IC_CON_IC_RESTART_EN(ReSTR) | + BIT_CTRL_IC_CON_IC_10BITADDR_MASTER(AddrMd) | + BIT_CTRL_IC_CON_SPEED(SpdMd) | + BIT_CTRL_IC_CON_MASTER_MODE(Master))); + + DBG_I2C_INFO("Init master, IC_CON%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_CON, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_CON)); + + + /* To set target addr.*/ + Specical = 0; + if ((GC!=0) || (StartByte!=0)) + Specical = 1; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_TAR, + (BIT_CTRL_IC_TAR_IC_10BITADDR_MASTER(AddrMd) | + BIT_CTRL_IC_TAR_SPECIAL(Specical) | + BIT_CTRL_IC_TAR_GC_OR_START(StartByte) | + BIT_CTRL_IC_TAR(I2CAckAddr))); + + /* To Set I2C clock*/ + HalI2CSetCLKRtl8195a(pHalI2CInitData); + + + DBG_I2C_INFO("Init master, IC_TAR%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_TAR, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_TAR)); + + } /*if (Master)*/ + else { + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_CON, + BIT_CTRL_IC_CON_IC_10BITADDR_SLAVE(AddrMd) | + BIT_CTRL_IC_CON_IC_SLAVE_DISABLE(Master) | + BIT_CTRL_IC_CON_SPEED(SpdMd)| + BIT_CTRL_IC_CON_MASTER_MODE(Master)); + + DBG_I2C_INFO("Init slave, IC_CON%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_CON, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_CON)); + + + /* To set slave addr. */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SAR,BIT_CTRL_IC_SAR(I2CAckAddr)); + + DBG_I2C_INFO("Init slave, IC_SAR%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_SAR, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_SAR)); + + + /* To set slave no ack */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SLV_DATA_NACK_ONLY,BIT_CTRL_IC_SLV_DATA_NACK_ONLY(SlvNoAck)); + + /* Set ack general call. */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_ACK_GENERAL_CALL,BIT_CTRL_IC_ACK_GENERAL_CALL(pHalI2CInitData->I2CSlvAckGC)); + + + + DBG_I2C_INFO("Init slave, I2C_IC_ACK_GC%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_ACK_GENERAL_CALL, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_ACK_GENERAL_CALL)); + + /* to set SDA hold time */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SDA_HOLD,BIT_CTRL_IC_SDA_HOLD(SdaHd)); + //4 + /* to set SDA setup time */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SDA_SETUP,BIT_CTRL_IC_SDA_SETUP(SdaSetup)); + } + + /* To set TX_Empty Level */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_TX_TL,TXTL); + + /* To set RX_Full Level */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_RX_TL,RXTL); + + /* To set TX/RX FIFO level */ + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_DMA_TDLR,TxDMARqLv); + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_DMA_RDLR,RxDMARqLv); + + + DBG_I2C_INFO("Init i2c dev, I2C_IC_DMA_TDLR%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_DMA_TDLR, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_DMA_TDLR)); + DBG_I2C_INFO("Init i2c dev, I2C_IC_DMA_RDLR%d[%2x]: %x\n", I2CIdx, REG_DW_I2C_IC_DMA_RDLR, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_DMA_RDLR)); + + + /*I2C Clear all interrupts first*/ + HalI2CClrAllIntrRtl8195a(pHalI2CInitData); + + /*I2C Disable all interrupts first*/ + INTRMsk = pHalI2CInitData->I2CIntrMSK; + pHalI2CInitData->I2CIntrMSK = 0; + HalI2CIntrCtrl8195a(pHalI2CInitData); + pHalI2CInitData->I2CIntrMSK = INTRMsk; + + return HAL_OK; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// HalI2CSetCLKRtl8195a +// +// Description: +// To set I2C bus clock rate. +// +// Arguments: +// [in] VOID *Data - +// The I2C parameter data struct. +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the de-initialization succeeded. +// _EXIT_FAILURE if the de-initialization failed. +// +// Note: +// None +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-02-18. +// +//--------------------------------------------------------------------------------------------------- +HAL_Status +HalI2CSetCLKRtl8195a( + IN VOID *Data +) +{ + PHAL_I2C_INIT_DAT pHalI2CInitData = (PHAL_I2C_INIT_DAT)Data; + u8 SpdMd = pHalI2CInitData->I2CSpdMod; + u32 I2CClk = pHalI2CInitData->I2CClk; + u8 I2CIdx = pHalI2CInitData->I2CIdx; + u32 ICHLcnt; + u32 ICHtime; + u32 ICLtime; + + /* Get the IC-Clk setting first for the following process*/ +#ifdef CONFIG_FPGA + u32 IcClk = SYSTEM_CLK/1000000; +#else + u32 IcClk; + u32 ClkSELTmp = 0; + u32 CpuClkTmp = 0; + + #if CONFIG_CHIP_A_CUT + CpuClkTmp = StartupHalGetCpuClk(); + #elif CONFIG_CHIP_B_CUT + CpuClkTmp = HalGetCpuClk(); + #endif + + DBG_I2C_INFO("%s, CPU Clk:%x\n",__func__, CpuClkTmp); + + ClkSELTmp = HAL_READ32(PERI_ON_BASE, REG_PESOC_CLK_SEL); + ClkSELTmp &= (~(BIT_PESOC_PERI_SCLK_SEL(3))); + HAL_WRITE32(PERI_ON_BASE,REG_PESOC_CLK_SEL,ClkSELTmp); + IcClk = (CpuClkTmp/1000000)>>1; + +#if 0 + if ((I2CClk > 0) && (I2CClk <= 400)) { + ClkSELTmp &= (~(BIT_PESOC_PERI_SCLK_SEL(3))); + HAL_WRITE32(PERI_ON_BASE,REG_PESOC_CLK_SEL,ClkSELTmp); + IcClk = ClkSELTmp/1000000; /*actually it's 12.5MHz*/ + } + else { + ClkSELTmp &= (~(BIT_PESOC_PERI_SCLK_SEL(3))); + HAL_WRITE32(PERI_ON_BASE,REG_PESOC_CLK_SEL,ClkSELTmp); + IcClk = 100; + } +#endif +#endif + + switch (SpdMd) + { + case I2C_SS_MODE: + { + ICHtime = ((1000000/I2CClk)*I2C_SS_MIN_SCL_HTIME)/(I2C_SS_MIN_SCL_HTIME+I2C_SS_MIN_SCL_LTIME); + ICLtime = ((1000000/I2CClk)*I2C_SS_MIN_SCL_LTIME)/(I2C_SS_MIN_SCL_HTIME+I2C_SS_MIN_SCL_LTIME); + + ICHLcnt = (ICHtime * IcClk)/1000; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SS_SCL_HCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_SS_SCL_HCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_SS_SCL_HCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_SS_SCL_HCNT)); + + + ICHLcnt = (ICLtime * IcClk)/1000; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SS_SCL_LCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_SS_SCL_LCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_SS_SCL_LCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_SS_SCL_LCNT)); + + break; + } + + case I2C_FS_MODE: + { + ICHtime = ((1000000/I2CClk)*I2C_FS_MIN_SCL_HTIME)/(I2C_FS_MIN_SCL_HTIME+I2C_FS_MIN_SCL_LTIME); + ICLtime = ((1000000/I2CClk)*I2C_FS_MIN_SCL_LTIME)/(I2C_FS_MIN_SCL_HTIME+I2C_FS_MIN_SCL_LTIME); + + ICHLcnt = (ICHtime * IcClk)/1000; + if (ICHLcnt>4)/*this part is according to the fine-tune result*/ + ICHLcnt -= 4; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_FS_SCL_HCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_FS_SCL_HCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_FS_SCL_HCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_FS_SCL_HCNT)); + + + ICHLcnt = (ICLtime * IcClk)/1000; + if (ICHLcnt>3)/*this part is according to the fine-tune result*/ + ICHLcnt -= 3; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_FS_SCL_LCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_FS_SCL_LCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_FS_SCL_LCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_FS_SCL_LCNT)); + + break; + } + + case I2C_HS_MODE: + { + ICHLcnt = 400; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SS_SCL_HCNT,ICHLcnt); + + ICHLcnt = 470; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_SS_SCL_LCNT,ICHLcnt); + + ICHLcnt = 60; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_FS_SCL_HCNT,ICHLcnt); + + ICHLcnt = 130; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_FS_SCL_LCNT,ICHLcnt); + + ICHtime = ((1000000/I2CClk)*I2C_HS_MIN_SCL_HTIME_100)/(I2C_HS_MIN_SCL_HTIME_100+I2C_HS_MIN_SCL_LTIME_100); + ICLtime = ((1000000/I2CClk)*I2C_HS_MIN_SCL_LTIME_100)/(I2C_HS_MIN_SCL_HTIME_100+I2C_HS_MIN_SCL_LTIME_100); + + + DBG_I2C_INFO("ICHtime:%x\n",ICHtime); + DBG_I2C_INFO("ICLtime:%x\n",ICLtime); + + + ICHLcnt = (ICHtime * IcClk)/1000; + if (ICHLcnt>8)/*this part is according to the fine-tune result*/ + ICHLcnt -= 3; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_HS_SCL_HCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_HS_SCL_HCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_HS_SCL_HCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_HS_SCL_HCNT)); + + + ICHLcnt = (ICLtime * IcClk)/1000; + if (ICHLcnt>6)/*this part is according to the fine-tune result*/ + ICHLcnt -= 6; + HAL_I2C_WRITE32(I2CIdx,REG_DW_I2C_IC_HS_SCL_LCNT,ICHLcnt); + + + DBG_I2C_INFO("IC_HS_SCL_LCNT%d[%2x]: %x\n", I2CIdx, + REG_DW_I2C_IC_HS_SCL_LCNT, HAL_I2C_READ32(I2CIdx,REG_DW_I2C_IC_HS_SCL_LCNT)); + + + break; + } + + default: + break; + } + + return HAL_OK; +} + + +VOID +HalI2COpInit( + IN VOID *Data +) +{ + PHAL_I2C_OP pHalI2COp = (PHAL_I2C_OP) Data; + + pHalI2COp->HalI2CInit = HalI2CInit8195a; + DBG_I2C_INFO("HalOpInit->HalI2CInit:%x\n",pHalI2COp->HalI2CInit); + + pHalI2COp->HalI2CDeInit = HalI2CDeInit8195a; + DBG_I2C_INFO("HalOpInit->HalI2CDeInit:%x\n",pHalI2COp->HalI2CDeInit); + + pHalI2COp->HalI2CSend = HalI2CSendRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CSend:%x\n",pHalI2COp->HalI2CSend); + + pHalI2COp->HalI2CReceive = HalI2CReceiveRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CReceive:%x\n",pHalI2COp->HalI2CReceive); + + pHalI2COp->HalI2CEnable = HalI2CEnableRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CEnable:%x\n",pHalI2COp->HalI2CEnable); + + pHalI2COp->HalI2CIntrCtrl = HalI2CIntrCtrl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CIntrCtrl:%x\n",pHalI2COp->HalI2CIntrCtrl); + + pHalI2COp->HalI2CReadReg = HalI2CReadRegRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CReadReg:%x\n",pHalI2COp->HalI2CReadReg); + + pHalI2COp->HalI2CWriteReg = HalI2CWriteRegRtl8195a; + DBG_I2C_INFO("pHalI2COp->HalI2CWriteReg:%x\n",pHalI2COp->HalI2CWriteReg); + + pHalI2COp->HalI2CSetCLK = HalI2CSetCLKRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CSetCLK:%x\n",pHalI2COp->HalI2CSetCLK); + + pHalI2COp->HalI2CMassSend = HalI2CMassSendRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CMassSend:%x\n",pHalI2COp->HalI2CMassSend); + + pHalI2COp->HalI2CClrIntr = HalI2CClrIntrRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CClrIntr:%x\n",pHalI2COp->HalI2CClrIntr); + + pHalI2COp->HalI2CClrAllIntr = HalI2CClrAllIntrRtl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CClrAllIntr:%x\n",pHalI2COp->HalI2CClrAllIntr); + + pHalI2COp->HalI2CDMACtrl = HalI2CDMACtrl8195a; + DBG_I2C_INFO("HalOpInit->HalI2CDMACtrl:%x\n",pHalI2COp->HalI2CDMACtrl); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// I2CISRHandle +// +// Description: +// I2C Interrupt Service Routine. +// According to the input pointer to SAL_I2C_HND, all the rest pointers will be +// found and be used to the rest part of this servie routine. +// The following types of interrupt will be taken care: +// - General Call (providing General Call Callback). Slave receives a general call. +// - STOP Bit (NOT providing General Call Callback) +// - START Bit (NOTproviding General Call Callback) +// - I2C Activity (NOTproviding General Call Callback) +// - RX Done (providing Error Callback). The slave transmitter does NOT +// receive a proper NACK for the end of whole transfer. +// - TX Abort (providing Error Call Callback). The Master/Slave +// transmitting is terminated. +// - RD Req (providing TX and TXC Callback). Slave gets a Read Request +// and starts a slave-transmitter operation. The slave transmit +// data will be written into slave TX FIFO from user data buffer. +// - TX Empty (providing TX and TXC Callback). Master TX FIFO is empty. +// The user transmit data will be written into master TX FIFO +// from user data buffer. +// - TX Over (providing Error Callback). Master TX FIFO is Overflow. +// - RX Full (providing RX and RXC Callback). Master/Slave RX FIFO contains +// data. And the received data will be put into Master/Slave user +// receive data buffer. +// - RX Over (providing Error Callback). Master/Slave RX FIFO is Overflow. +// - RX Under (providing Error Callback). Master/Slave RX FIFO is Underflow. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// NA +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//---------------------------------------------------------------------------------------------------- +VOID +I2CISRHandle( + IN VOID *Data +){ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + PSAL_I2C_HND_PRIV pSalI2CHNDPriv = NULL; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PHAL_I2C_INIT_DAT pHalI2CInitDat = NULL; + PHAL_I2C_OP pHalI2COP = NULL; + PSAL_I2C_USER_CB pSalI2CUserCB = NULL; + u32 I2CLocalTemp = 0; + u32 I2CInTOTcnt = 0; + u32 I2CIrqIdx = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + volatile u32 I2CLocalRawSts = 0; + + + /* To get the SAL_I2C_MNGT_ADPT pointer, and parse the rest pointers */ + pSalI2CHNDPriv = CONTAINER_OF(pSalI2CHND, SAL_I2C_HND_PRIV, SalI2CHndPriv); + pSalI2CMngtAdpt = CONTAINER_OF(pSalI2CHNDPriv->ppSalI2CHnd, SAL_I2C_MNGT_ADPT, pSalHndPriv); + pHalI2CInitDat = pSalI2CMngtAdpt->pHalInitDat; + pHalI2COP = pSalI2CMngtAdpt->pHalOp; + I2CInTOTcnt = pSalI2CMngtAdpt->InnerTimeOut; + I2CIrqIdx = pHalI2CInitDat->I2CIdx; + pSalI2CUserCB = pSalI2CHND->pUserCB; + //DBG_8195A("NEW ISR\n"); + /* I2C General Call Intr*/ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_GEN_CALL(1)) { + + DBG_I2C_WARN("I2C%d INTR_GEN_CALL\n",I2CIrqIdx); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_GEN_CALL; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* Invoke I2C General Call callback if available*/ + if (pSalI2CUserCB->pGENCALLCB->USERCB != NULL) { + pSalI2CUserCB->pGENCALLCB->USERCB((void *)pSalI2CUserCB->pGENCALLCB->USERData); + } + } + + /* I2C START DET Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_START_DET(1)) { + + DBG_I2C_WARN("I2C%d INTR_START_DET\n",I2CIrqIdx); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_START_DET; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + } + + /* I2C STOP DET Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_STOP_DET(1)) { + + DBG_I2C_WARN("I2C%d INTR_STOP_DET\n",I2CIrqIdx); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_STOP_DET; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + } + + /* I2C Activity Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_ACTIVITY(1)) { + + DBG_I2C_WARN("I2C%d INTR_ACTIVITY\n",I2CIrqIdx); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_ACTIVITY; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + } + + /* I2C RX Done Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_RX_DONE(1)) { + //DBG_8195A("rxdone\n"); + DBG_I2C_ERR("I2C%d INTR_RX_DONE\n",I2CIrqIdx); + DBG_I2C_ERR("I2C%d IC_TXFLR:%2x\n",I2CIrqIdx, + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_TXFLR)); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RX_DONE; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_ERROR; + + /* Update I2C error type */ + pSalI2CHND->ErrType |= I2C_ERR_SLV_TX_NACK; + + /* Invoke I2C error callback if available */ + if (pSalI2CUserCB->pERRCB->USERCB != NULL) + pSalI2CUserCB->pERRCB->USERCB((void *)pSalI2CUserCB->pERRCB->USERData); + } + + /* I2C TX Abort Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_TX_ABRT(1)) { + //DBG_8195A("abort\n"); + DBG_I2C_ERR("!!!I2C%d INTR_TX_ABRT!!!\n",I2CIrqIdx); + DBG_I2C_ERR("I2C%d IC_TX_ABRT_SOURCE[%2x]: %x\n", I2CIrqIdx, REG_DW_I2C_IC_TX_ABRT_SOURCE, + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_TX_ABRT_SOURCE)); + DBG_I2C_ERR("Dev Sts:%x\n",pSalI2CHND->DevSts); + DBG_I2C_ERR("rx len:%x\n",pSalI2CHND->pRXBuf->DataLen); + DBG_I2C_ERR("tx len:%x\n",pSalI2CHND->pTXBuf->DataLen); + DBG_I2C_ERR("raw sts:%x\n",pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT)); + DBG_I2C_ERR("ic sts:%x\n",pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS)); + /* Clear I2C Interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_ABRT; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + I2CLocalTemp = pSalI2CHND->DevSts; + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_ERROR; + + /* Update I2C error type */ + pSalI2CHND->ErrType |= I2C_ERR_TX_ABRT; + + /* Invoke I2C error callback */ + if (pSalI2CUserCB->pERRCB->USERCB != NULL) + pSalI2CUserCB->pERRCB->USERCB((void *)pSalI2CUserCB->pERRCB->USERData); + + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_RTY) { + if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE) { + if ((I2CLocalTemp == I2C_STS_RX_READY) || (I2CLocalTemp == I2C_STS_RX_ING)) { + /* Clear Abort source */ + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_CLR_TX_ABRT); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_RX_ING; + + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & BIT_IC_STATUS_TFNF) { + if (pSalI2CMngtAdpt->MstRDCmdCnt > 0) { + pHalI2CInitDat->I2CCmd = I2C_READ_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pRXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + if ((pSalI2CMngtAdpt->MstRDCmdCnt == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + //DBG_8195A("A0\n"); + pSalI2CMngtAdpt->MstRDCmdCnt--; + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + } + + } + } + else if ((I2CLocalTemp == I2C_STS_TX_READY) || (I2CLocalTemp == I2C_STS_TX_ING)){ + /* Clear Abort source */ + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_CLR_TX_ABRT); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Return to the former transfer status */ + pSalI2CHND->pTXBuf->pDataBuf--; + pSalI2CHND->pTXBuf->DataLen++; + + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & BIT_IC_STATUS_TFNF) { + pHalI2CInitDat->I2CCmd = I2C_WRITE_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + if ((pSalI2CHND->pTXBuf->DataLen == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + } + } + } + } + } + + /* I2C RD REQ Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_RD_REQ(1)) { + /* Confirm it's slave mode */ + if (pSalI2CHND->I2CMaster == I2C_SLAVE_MODE) { + //DBG_8195A("rq\n"); + if (pSalI2CHND->pTXBuf->DataLen>0) { + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Invoke I2C TX callback if available */ + if (pSalI2CUserCB->pTXCB->USERCB != NULL) + pSalI2CUserCB->pTXCB->USERCB((void *)pSalI2CUserCB->pTXCB->USERData); + + /* I2C Slave transmits data to Master. If the TX FIFO is NOT full, + write one byte from slave TX buffer to TX FIFO. */ + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_TFNF)) == BIT_IC_STATUS_TFNF) { + pHalI2CInitDat->I2CCmd = I2C_WRITE_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + if ((pSalI2CHND->pTXBuf->DataLen == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + } + } + + /* To clear Read Request Intr */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RD_REQ; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* To check I2C slave TX data length. If all the data are transmitted, + mask all the interrupts and invoke the user callback */ + if (!pSalI2CHND->pTXBuf->DataLen) { + /* This is a software patch */ + pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_RAW_INTR_STAT); + HalDelayUs(1000); + + /* Disable I2C TX Related Interrupts */ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp &= ~(BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_OVER | + BIT_IC_INTR_MASK_M_RX_DONE | + BIT_IC_INTR_MASK_M_RD_REQ); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + + /* Invoke I2C TX complete callback if available */ + if (pSalI2CUserCB->pTXCCB->USERCB != NULL) + pSalI2CUserCB->pTXCCB->USERCB((void *)pSalI2CUserCB->pTXCCB->USERData); + } + } + } + + /* I2C TX Empty Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_TX_EMPTY(1)) { + /* Confirm it's master mode */ + if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE) { + + /* To check I2C master TX data length. If all the data are transmitted, + mask all the interrupts and invoke the user callback */ + if (!pSalI2CHND->pTXBuf->DataLen) { + /* I2C Disable TX Related Interrupts */ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp &= ~(BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_EMPTY | + BIT_IC_INTR_MASK_M_TX_OVER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + /* Clear all I2C pending interrupts */ + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + + /* Invoke I2C TX Complete callback */ + if (pSalI2CUserCB->pTXCCB->USERCB != NULL) + pSalI2CUserCB->pTXCCB->USERCB((void *)pSalI2CUserCB->pTXCCB->USERData); + } + + if (pSalI2CHND->pTXBuf->DataLen > 0) { + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Invoke I2C TX callback if available */ + if (pSalI2CUserCB->pTXCB->USERCB != NULL) + pSalI2CUserCB->pTXCB->USERCB((void *)pSalI2CUserCB->pTXCB->USERData); + + /* Check I2C TX FIFO status. If it's not full, one byte data will be written into it. */ + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_TFNF)) == BIT_IC_STATUS_TFNF) { + pHalI2CInitDat->I2CCmd = I2C_WRITE_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + pHalI2CInitDat->I2CReSTR = 1; + if ((pSalI2CHND->pTXBuf->DataLen == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + + } + } + }/*if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE)*/ + } + + /* I2C TX Over Run Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_TX_OVER(1)) { + + DBG_I2C_ERR("!!!I2C%d INTR_TX_OVER!!!\n",I2CIrqIdx); + + /* Clear I2C interrupt */ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_OVER; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_ERROR; + + /* Update I2C error type */ + pSalI2CHND->ErrType |= I2C_ERR_TX_OVER; + + /* Invoke I2C error callback if available */ + if (pSalI2CUserCB->pERRCB->USERCB != NULL) + pSalI2CUserCB->pERRCB->USERCB((void *)pSalI2CUserCB->pERRCB->USERData); + } + + /* I2C RX Full Intr */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_RX_FULL(1)) { + /* Check if it's Master */ + if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE){ + + //DBG_8195A("full\n"); + /* Check if the receive transfer is NOT finished. If it is not, check if there + is data in the RX FIFO and move the data from RX FIFO to user data buffer*/ + if (pSalI2CHND->pRXBuf->DataLen > 0) { + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_RX_ING; + + /* Invoke I2C RX callback if available */ + if (pSalI2CUserCB->pRXCB->USERCB != NULL) + pSalI2CUserCB->pRXCB->USERCB((void *)pSalI2CUserCB->pRXCB->USERData); + + I2CInTOTcnt = (u32)pSalI2CMngtAdpt->InnerTimeOut; + InTimeoutCount = 0; + /* Calculate internal time out parameters */ + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + while (1) { + I2CLocalRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + if ((I2CLocalRawSts & (BIT_IC_STATUS_RFNE | BIT_IC_STATUS_RFF)) != 0) { + *(pSalI2CHND->pRXBuf->pDataBuf) = + pHalI2COP->HalI2CReceive(pHalI2CInitDat); + //DBG_8195A("rx:%x\n",*(pSalI2CHND->pRXBuf->pDataBuf)); + pSalI2CHND->pRXBuf->pDataBuf++; + pSalI2CHND->pRXBuf->DataLen--; + + if ((pSalI2CHND->pRXBuf->DataLen) == 0) + break; + } + else if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT) + & (BIT_IC_RAW_INTR_STAT_RX_OVER | BIT_IC_RAW_INTR_STAT_RX_UNDER)) != 0) { + break; + } + else { + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) & BIT_IC_STATUS_RFNE) + == 0){ + break; + } + } + + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RX Full Timeout, I2C%2x,1\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + break; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RX Full Timeout, I2C%2x,2\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + break; + } + } + } + } + + /* To check I2C master RX data length. If all the data are received, + mask all the interrupts and invoke the user callback. + Otherwise, the master should send another Read Command to slave for + the next data byte receiving. */ + if (!pSalI2CHND->pRXBuf->DataLen) { + /* I2C Disable RX Related Interrupts */ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp &= ~(BIT_IC_INTR_MASK_M_RX_FULL | + BIT_IC_INTR_MASK_M_RX_OVER | + BIT_IC_INTR_MASK_M_RX_UNDER| + BIT_IC_INTR_MASK_M_TX_ABRT); + + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + /* Clear all I2C pending interrupts */ + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + + /* Invoke I2C RX complete callback if available */ + if (pSalI2CUserCB->pRXCCB->USERCB != NULL) + pSalI2CUserCB->pRXCCB->USERCB((void *)pSalI2CUserCB->pRXCCB->USERData); + } + else { + /* If TX FIFO is not full, another Read Command is written into it. */ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & BIT_IC_STATUS_TFNF) { + if (pSalI2CMngtAdpt->MstRDCmdCnt > 0) { + pHalI2CInitDat->I2CCmd = I2C_READ_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pRXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + pHalI2CInitDat->I2CReSTR = 1; + if ((pSalI2CMngtAdpt->MstRDCmdCnt == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)){ + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + } + pSalI2CMngtAdpt->MstRDCmdCnt--; + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + } + } + } + + }/*(pSalI2CHND->I2CMaster == I2C_MASTER_MODE)*/ + else{ + /* To check I2C master RX data length. If all the data are received, + mask all the interrupts and invoke the user callback. + Otherwise, if there is data in the RX FIFO and move the data from RX + FIFO to user data buffer*/ + if (pSalI2CHND->pRXBuf->DataLen > 0){ + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_RX_ING; + + /* Invoke I2C RX callback if available */ + if (pSalI2CUserCB->pRXCB->USERCB != NULL) + pSalI2CUserCB->pRXCB->USERCB((void *)pSalI2CUserCB->pRXCB->USERData); + + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_RFNE | BIT_IC_STATUS_RFF)) != 0) { + *(pSalI2CHND->pRXBuf->pDataBuf) = + pHalI2COP->HalI2CReceive(pHalI2CInitDat); + pSalI2CHND->pRXBuf->pDataBuf++; + pSalI2CHND->pRXBuf->DataLen--; + } + } + + /* All data are received. Mask all related interrupts. */ + if (!pSalI2CHND->pRXBuf->DataLen){ + /*I2C Disable RX Related Interrupts*/ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp &= ~(BIT_IC_INTR_MASK_M_RX_FULL | + BIT_IC_INTR_MASK_M_RX_OVER | + BIT_IC_INTR_MASK_M_RX_UNDER); + + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + + /* Invoke I2C RX complete callback if available */ + if (pSalI2CUserCB->pRXCCB->USERCB != NULL) + pSalI2CUserCB->pRXCCB->USERCB((void *)pSalI2CUserCB->pRXCCB->USERData); + } + } + } + + /*I2C RX Over Run Intr*/ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_RX_OVER(1)) { + + DBG_I2C_ERR("I2C%d INTR_RX_OVER\n",I2CIrqIdx); + + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RX_OVER; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_ERROR; + + /* Update I2C error type */ + pSalI2CHND->ErrType |= I2C_ERR_RX_OVER; + + /* Invoke I2C error callback if available */ + if (pSalI2CUserCB->pERRCB->USERCB != NULL) + pSalI2CUserCB->pERRCB->USERCB((void *)pSalI2CUserCB->pERRCB->USERData); + } + + /*I2C RX Under Run Intr*/ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_INTR_STAT) & + BIT_CTRL_IC_INTR_STAT_R_RX_UNDER(1)) { + + DBG_I2C_ERR("!!!I2C%d INTR_RX_UNDER!!!\n",I2CIrqIdx); + + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RX_UNDER; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + + /* Update I2C device status */ + pSalI2CHND->DevSts = I2C_STS_ERROR; + + /* Update I2C error type */ + pSalI2CHND->ErrType |= I2C_ERR_RX_UNDER; + + /* Invoke I2C error callback if available */ + if (pSalI2CUserCB->pERRCB->USERCB != NULL) + pSalI2CUserCB->pERRCB->USERCB((void *)pSalI2CUserCB->pERRCB->USERData); + } +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CSend +// +// Description: +// To execute Master-Transmitter and Slave-Transmitter operation. +// There are 3 operation mode in this function which are separated by compile-time +// flag. +// For Master-Transmitter, the User Register Address flag is checked first. +// User Register Address may be sent before any formal transfer, no matter in +// Poll-, Intr- or DMA- Mode. +// +// In Poll-Mode, no matter it's master or slave mode, the transfer will be done in +// this function by checking the transfer length. +// -Master in Poll-Mode: +// a. Send the User Register Address if needed. +// b. Check if all the data are transmitted. If it's NOT, checking the TX FIFO +// status is done for writing data from user TX buffer to I2C TX FIFO when +// TX FIFO is NOT full. +// TX data length decrements one after writing one byte into TX FIFO. +// c. b is executed circularly till the TX buffer data length is zero. +// +// -Slave in Poll-Mode: +// Slave could send data only when it received a Read Commmand matched +// with its own I2C address from other I2C master. Once a slave correctly +// received a Read Command matched with its own addr., a Read-Request +// flag is set at the same time. +// In this Poll-Mode, the slave checks the Read-Request flag to decide +// if it could send its TX buffer data. +// a. Check if the Read-Request flag is set or not. If the flag is set, it should +// check if TX buffer data length is zero. If it's NOT, +// the I2C TX FIFO status will be checked for the following operation. +// b. If the TX FIFO is NOT empty, slave will write one byte data from TX data +// buffer to TX FIFO. +// c. a and b are executed circularly till the TX buffer data length is zero. +//---------------------------------------------------------------------- +// In Intr-Mode, this function is used to unmask the realted I2C interrupt for +// the following interrupt operations. +// -Master in Intr-Mode: +// a. Send the User Register Address if needed. +// b. Unmask the TX-Empty and realted error interrupts. +// +// -Slave in Intr-Mode: +// a. Unmask the RD-Req and realted error interrupts. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C send process. +// _EXIT_SUCCESS if the RtkI2CSend succeeded. +// _EXIT_FAILURE if the RtkI2CSend failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +HAL_Status +RtkI2CSend( + IN VOID *Data +){ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + PSAL_I2C_HND_PRIV pSalI2CHNDPriv = NULL; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PHAL_I2C_INIT_DAT pHalI2CInitDat = NULL; + PHAL_I2C_OP pHalI2COP = NULL; + + PHAL_GDMA_ADAPTER pHalI2CTxGdmaAdpt = NULL; + PHAL_GDMA_OP pHalI2CGdmaOp = NULL; + + u32 I2CLocalTemp = 0; + u32 I2CInTOTcnt = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + u32 I2CChkRawSts = 0; + u32 I2CChkRawSts2 = 0; + u32 I2CDataLenBak = 0; + u32 I2CDataPtrBak = 0; + + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalI2CHNDPriv = CONTAINER_OF(pSalI2CHND, SAL_I2C_HND_PRIV, SalI2CHndPriv); + pSalI2CMngtAdpt = CONTAINER_OF(pSalI2CHNDPriv->ppSalI2CHnd, SAL_I2C_MNGT_ADPT, pSalHndPriv); + + + + pHalI2CInitDat = pSalI2CMngtAdpt->pHalInitDat; + + pHalI2COP = pSalI2CMngtAdpt->pHalOp; + + + pHalI2CTxGdmaAdpt = pSalI2CMngtAdpt->pHalTxGdmaAdp; + pHalI2CGdmaOp = pSalI2CMngtAdpt->pHalGdmaOp; + + /* Check if it's Master Mode */ + if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE) { + //DBG_8195A("m\n"); + /* Master run-time update target address */ + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_UPD) { + + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Check Master activity status */ + while ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS)) & BIT_IC_STATUS_MST_ACTIVITY) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,1\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,2\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Check TX FIFO status */ + while (!((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS)) & BIT_IC_STATUS_TFE)) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,3\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_ADD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,4\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + + I2CLocalTemp = 0; + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_TAR); + I2CLocalTemp &= (~BIT_MASK_IC_TAR); + I2CLocalTemp |= BIT_CTRL_IC_TAR(pSalI2CHND->pTXBuf->TargetAddr); + /* Update Master Target address */ + pHalI2COP->HalI2CWriteReg(pHalI2CInitDat, REG_DW_I2C_IC_TAR, I2CLocalTemp); + } + + RtkI2CSendUserAddr(pSalI2CHND, 0); + + /* #if I2C_POLL_OP_TYPE */ + if (pSalI2CHND->OpType == I2C_POLL_TYPE) { /* if (pSalI2CHND->OpType == I2C_POLL_TYPE) */ + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_READY; + + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Send data till the TX buffer data length is zero */ + for (;pSalI2CHND->pTXBuf->DataLen>0;) { + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Check I2C TX FIFO status */ + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_TFNF)) == BIT_IC_STATUS_TFNF) { + /* Wrtie data into I2C TX FIFO */ + pHalI2CInitDat->I2CCmd = I2C_WRITE_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + if ((pSalI2CHND->pTXBuf->DataLen == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + } + else { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_CMD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,5\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_CMD_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,6\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_RTY) { + HalDelayUs(((1000*30)/pHalI2CInitDat->I2CClk)); //the 10 is for ten bit time + + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT) & + BIT_IC_RAW_INTR_STAT_TX_ABRT) { + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_CLR_TX_ABRT); + pSalI2CHND->pTXBuf->pDataBuf--; + pSalI2CHND->pTXBuf->DataLen++; + } + } + } + + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CMngtAdpt->InnerTimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + /* I2C Wait TX FIFO Empty */ + while (1) { + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_TFE | BIT_IC_STATUS_TFNF)) == + (BIT_IC_STATUS_TFE | BIT_IC_STATUS_TFNF)){ + break; + } + else { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,7\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,8\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + } + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + }/* if (pSalI2CHND->OpType == I2C_POLL_TYPE) */ + /* #if I2C_POLL_OP_TYPE */ + +#if I2C_INTR_OP_TYPE + if (pSalI2CHND->OpType == I2C_INTR_TYPE) { /* if (pSalI2CHND->OpType == I2C_INTR_TYPE) */ + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + I2CDataLenBak = (u32)(pSalI2CHND->pTXBuf->DataLen); + I2CDataPtrBak = (u32)(pSalI2CHND->pTXBuf->pDataBuf); + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_ABRT; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + + /* Send data till the TX buffer data length is zero */ + for (;;) { +SEND_I2C_WR_CMD_INTR: + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Check I2C TX FIFO status */ + /* Fill TX FIFO only when it's completely empty */ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + if ((I2CChkRawSts & BIT_IC_STATUS_TFE) == BIT_IC_STATUS_TFE) { + if (pSalI2CHND->pTXBuf->DataLen > 0) { + /* Wrtie data into I2C TX FIFO */ + pHalI2CInitDat->I2CCmd = I2C_WRITE_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + + if ((pSalI2CHND->pTXBuf->DataLen == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)) + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + } + } + + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_RTY) { + u32 I2CInTOTcntRty = 0; + u32 InTimeoutCountRty = 0; + u32 InStartCountRty = 0; + /* Calculate user time out parameters */ + InTimeoutCountRty = 0; + InStartCountRty = 0; + I2CInTOTcntRty = pSalI2CHND->TimeOut; + + if ((I2CInTOTcntRty != 0) && (I2CInTOTcntRty!= I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCountRty= (I2CInTOTcntRty*1000/TIMER_TICK_US); + InStartCountRty= HalTimerOp.HalTimerReadCount(1); + } + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + while ((I2CChkRawSts & BIT_IC_STATUS_TFE) == 0) { + I2CChkRawSts2 = pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT); + if ((I2CChkRawSts2 & BIT_IC_RAW_INTR_STAT_TX_ABRT) != 0){ + break; + } + + /* Time-Out check */ + if (InTimeoutCountRty > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCountRty, InTimeoutCountRty)) { + break; + } + } + else { + if (I2CInTOTcntRty == 0) { + break; + } + } + + /* Read I2C IC status again */ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + } + + HalDelayUs((u32)((1000*30)/pHalI2CInitDat->I2CClk)); //the 10 is for ten bit time + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT); + if (I2CChkRawSts & BIT_IC_RAW_INTR_STAT_TX_ABRT) { +#if 1 + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + + pSalI2CHND->DevSts = I2C_STS_TX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pTXBuf->DataLen = (u16)I2CDataLenBak; + pSalI2CHND->pTXBuf->pDataBuf= (u8*)I2CDataPtrBak; + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + goto SEND_I2C_WR_CMD_INTR; +#else + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + while ((I2CChkRawSts & BIT_IC_STATUS_ACTIVITY) != 0) { + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + } + + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_CLR_TX_ABRT); + + /* Clear all I2C pending interrupts */ + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + + pSalI2CHND->pTXBuf->pDataBuf--; + pSalI2CHND->pTXBuf->DataLen++; + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + while ((I2CChkRawSts & BIT_IC_STATUS_ACTIVITY) != 0) { + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + } +#endif + } + else if (((u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) & BIT_IC_STATUS_TFE) != BIT_IC_STATUS_TFE) { + { + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + + pSalI2CHND->DevSts = I2C_STS_TX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pTXBuf->DataLen = (u16)I2CDataLenBak; + pSalI2CHND->pTXBuf->pDataBuf= (u8 *)I2CDataPtrBak; + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + goto SEND_I2C_WR_CMD_INTR; + } + } + else { + /* I2C Enable TX Related Interrupts */ + I2CLocalTemp = 0; + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_EMPTY | + BIT_IC_INTR_MASK_M_TX_OVER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + break; + } + } + else { + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + while ((I2CChkRawSts & BIT_IC_STATUS_TFE) == 0) { + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + } + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + if (I2CChkRawSts & BIT_IC_STATUS_TFE) { + /* I2C Enable TX Related Interrupts */ + I2CLocalTemp = 0; + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_EMPTY | + BIT_IC_INTR_MASK_M_TX_OVER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + break; + } + } +#if 1 + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + //DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,7\n",pSalI2CHND->DevNum); + //DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } +#if 0 + pSalI2CHND->DevSts = I2C_STS_TX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pTXBuf->DataLen = I2CDataLenBak; + pSalI2CHND->pTXBuf->pDataBuf= I2CDataPtrBak; + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } +#endif + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } +#if 0 + pSalI2CHND->DevSts = I2C_STS_TX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pTXBuf->DataLen = I2CDataLenBak; + pSalI2CHND->pTXBuf->pDataBuf= I2CDataPtrBak; + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } +#endif + return HAL_TIMEOUT; + } + } +#endif + } + } /* if (pSalI2CHND->OpType == I2C_INTR_TYPE) */ +#endif + + /* if (pSalI2CHND->OpType == I2C_DMA_TYPE) */ + if (pSalI2CHND->OpType == I2C_DMA_TYPE) { + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_READY; + + /* I2C Enable TX Related Interrupts */ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_OVER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + //HalGdmaOpInit(pHalI2CGdmaOp); + pSalI2CMngtAdpt->pHalGdmaOpInit(pHalI2CGdmaOp); + pHalI2CTxGdmaAdpt->GdmaCtl.BlockSize = pSalI2CHND->pTXBuf->DataLen; + pHalI2CTxGdmaAdpt->ChSar = (u32)pSalI2CHND->pTXBuf->pDataBuf; + pHalI2CTxGdmaAdpt->ChDar = (u32)(I2C0_REG_BASE+REG_DW_I2C_IC_DATA_CMD+ + pSalI2CHND->DevNum*0x400); + pHalI2CGdmaOp->HalGdmaChSeting(pHalI2CTxGdmaAdpt); + pHalI2CGdmaOp->HalGdmaChEn(pHalI2CTxGdmaAdpt); + pSalI2CHND->DevSts = I2C_STS_TX_ING; + pHalI2CInitDat->I2CDMACtrl = BIT_CTRL_IC_DMA_CR_TDMAE(1); + pHalI2COP->HalI2CDMACtrl(pHalI2CInitDat); + + } + /* if (pSalI2CHND->OpType == I2C_DMA_TYPE) */ + + }/* if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE) */ + else{ + /* #if I2C_POLL_OP_TYPE */ + if (pSalI2CHND->OpType == I2C_POLL_TYPE) { + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_READY; + + /* Send data till the TX buffer data length is zero */ + for (;pSalI2CHND->pTXBuf->DataLen>0;) { + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_TX_ING; + + /* Check I2C RD Request flag */ + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_RAW_INTR_STAT)) + & BIT_IC_RAW_INTR_STAT_RD_REQ) { + + /* Check I2C TX FIFO status */ + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_TFNF)) == BIT_IC_STATUS_TFNF) { + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pTXBuf->pDataBuf; + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RD_REQ; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pSalI2CHND->pTXBuf->pDataBuf++; + pSalI2CHND->pTXBuf->DataLen--; + } + } + else { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,9\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + DBG_I2C_ERR("RtkI2CSend Timeout, I2C%2x,10\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + } + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + } + /* #if I2C_POLL_OP_TYPE */ + + /* #if I2C_INTR_OP_TYPE */ + if (pSalI2CHND->OpType == I2C_INTR_TYPE) { + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_ABRT; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_OVER; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_RD_REQ; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_ACTIVITY; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + pSalI2CHND->DevSts = I2C_STS_TX_READY; + + /* I2C Enable TX Related Interrupts. In Slave-Transmitter, the below + interrupts should be enabled. */ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_TX_ABRT | + BIT_IC_INTR_MASK_M_TX_OVER | + BIT_IC_INTR_MASK_M_RX_DONE | + BIT_IC_INTR_MASK_M_RD_REQ); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + } + /* #if I2C_INTR_OP_TYPE */ + + /* #if I2C_DMA_OP_TYPE */ + ; + /* #if I2C_DMA_OP_TYPE */ + } + + return HAL_OK; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CReceive +// +// Description: +// To execute Master-Receiver and Slave-Receiver operation. +// There are 3 operation mode in this function which are separated by compile-time +// flag. +// For Master-Receiver, the User Register Address flag is checked first. +// User Register Address may be sent before any formal transfer, no matter in +// Poll-, Intr- or DMA- Mode. +// +// For Master-Receiver, the I2C master have to send a Read Command for receiving +// one byte from the other I2C slave. +// +// In Poll-Mode, no matter it's master or slave mode, the transfer will be done in +// this function by checking the transfer length. +// -Master in Poll-Mode: +// a. Send the User Register Address if needed. +// b. Check if all the data are received. If it's NOT, checking the TX FIFO +// status will be done. If the TX FIFO it's full, a Read Command will be +// wirtten into the TX FIFO. +// c. After b, the I2C master contineously polls the RX FIFO status to see +// if there is a received data. If it received one, it will move the data from +// I2C RX FIFO into user RX data buffer. +// d. b and c are executed circularly till the RX buffer data length is zero. +// +// -Slave in Poll-Mode: +// a. Check if all the data are received. +// b. The I2C slave contineously polls the RX FIFO status to see +// if there is a received data. If it received one, it will move the data from +// I2C RX FIFO into user RX data buffer. +// c. a and b are executed circularly till the RX buffer data length is zero. +// +//---------------------------------------------------------------------- +// In Intr-Mode, this function is used to unmask the realted I2C interrupt for +// the following interrupt operations. +// -Master in Intr-Mode: +// a. Send the User Register Address if needed. +// b. Unmask the RX-Full and realted error interrupts. +// c. Write one or two Read Command into master TX FIFO for requesting +// another slave providing data. +// +// -Slave in Intr-Mode: +// a. Unmask the RX-Full and realted error interrupts. +// +// Arguments: +// [in] VOID *Data - +// I2C SAL handle +// +// Return: +// The status of the I2C receive process. +// _EXIT_SUCCESS if the RtkI2CReceive succeeded. +// _EXIT_FAILURE if the RtkI2CReceive failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +HAL_Status +RtkI2CReceive( + IN VOID *Data +){ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + PSAL_I2C_HND_PRIV pSalI2CHNDPriv = NULL; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PHAL_I2C_INIT_DAT pHalI2CInitDat = NULL; + PHAL_I2C_OP pHalI2COP = NULL; +#if I2C_DMA_OP_TYPE + PHAL_GDMA_ADAPTER pHalI2CRxGdmaAdpt = NULL; + PHAL_GDMA_OP pHalI2CGdmaOp = NULL; +#endif + + u32 I2CLocalTemp = 0; + u32 I2CInTOTcnt = 0; + u32 InTimeoutCount = 0; + u32 InStartCount = 0; + u32 I2CLocalLen = 0; + u32 I2CChkRawSts = 0; + u32 I2CChkRawSts2 = 0; + u32 I2CDataLenBak = 0; + u32 I2CDataPtrBak = 0; + u32 I2CInTOTcntRty = 0; + u32 InTimeoutCountRty = 0; + u32 InStartCountRty = 0; + + + /*To Get the SAL_I2C_MNGT_ADPT Pointer*/ + pSalI2CHNDPriv = CONTAINER_OF(pSalI2CHND, SAL_I2C_HND_PRIV, SalI2CHndPriv); + pSalI2CMngtAdpt = CONTAINER_OF(pSalI2CHNDPriv->ppSalI2CHnd, SAL_I2C_MNGT_ADPT, pSalHndPriv); + pHalI2CInitDat = pSalI2CMngtAdpt->pHalInitDat; + pHalI2COP = pSalI2CMngtAdpt->pHalOp; +#if I2C_DMA_OP_TYPE + pHalI2CRxGdmaAdpt = pSalI2CMngtAdpt->pHalRxGdmaAdp; + pHalI2CGdmaOp = pSalI2CMngtAdpt->pHalGdmaOp; +#endif + + if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE)/*if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE)*/ + { + /* Master run-time update target address */ + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_UPD) { + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Check Master activity status */ + while ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS)) & BIT_IC_STATUS_MST_ACTIVITY) { + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + //DBG_8195A("~\n"); + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,1\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,2\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Check TX FIFO status */ + while (!((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS)) & BIT_IC_STATUS_TFE)) { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,3\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_ADD_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,4\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_TAR); + I2CLocalTemp &= (~BIT_MASK_IC_TAR); + I2CLocalTemp |= BIT_CTRL_IC_TAR(pSalI2CHND->pRXBuf->TargetAddr); + /* Update Master Target address */ + pHalI2COP->HalI2CWriteReg(pHalI2CInitDat, REG_DW_I2C_IC_TAR, I2CLocalTemp); + } + + +#if I2C_USER_REG_ADDR /*I2C_USER_REG_ADDR*/ + RtkI2CSendUserAddr(pSalI2CHND, 1); +#endif /*I2C_USER_REG_ADDR*/ + +#if I2C_POLL_OP_TYPE/*I2C_POLL_OP_TYPE*/ + if (pSalI2CHND->OpType == I2C_POLL_TYPE) + { + //DBG_8195A("p\n"); + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_RX_READY; + + pSalI2CMngtAdpt->MstRDCmdCnt = pSalI2CHND->pRXBuf->DataLen; + I2CLocalTemp = pSalI2CHND->pRXBuf->DataLen; + + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Receive data till the RX buffer data length is zero */ + for ( ;pSalI2CHND->pRXBuf->DataLen>0; ) { +SEND_I2C_RD_CMD: + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_RX_ING; + /* Check I2C TX FIFO status. If it's NOT full, a Read command is written + into the TX FIFO.*/ + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & BIT_IC_STATUS_TFNF) { + if (pSalI2CMngtAdpt->MstRDCmdCnt > 0) { + pHalI2CInitDat->I2CCmd = I2C_READ_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pRXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + if ((pSalI2CMngtAdpt->MstRDCmdCnt == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)){ + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + } + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + if (pSalI2CMngtAdpt->MstRDCmdCnt > 0) + pSalI2CMngtAdpt->MstRDCmdCnt--; + } + } + + if (I2CLocalTemp == pSalI2CHND->pRXBuf->DataLen){ + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_RTY) { + HalDelayUs(((1000*30)/pHalI2CInitDat->I2CClk)); //the 10 is for ten bit time + if (pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT) & + BIT_IC_RAW_INTR_STAT_TX_ABRT) { + pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_CLR_TX_ABRT); + pSalI2CMngtAdpt->MstRDCmdCnt++; + goto SEND_I2C_RD_CMD; + } + } + } + + /* Contineously poll the I2C RX FIFO status */ + while (1) { + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_RFNE | BIT_IC_STATUS_RFF)) != 0) { + *(pSalI2CHND->pRXBuf->pDataBuf) = + pHalI2COP->HalI2CReceive(pHalI2CInitDat); + + pSalI2CHND->pRXBuf->pDataBuf++; + pSalI2CHND->pRXBuf->DataLen--; + + if (!pSalI2CHND->pRXBuf->DataLen) { + break; + } + } + else { + break; + } + } + + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,5\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,6\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + + } + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + } +#endif/*I2C_POLL_OP_TYPE*/ + +#if I2C_INTR_OP_TYPE/*I2C_INTR_OP_TYPE*/ + if (pSalI2CHND->OpType == I2C_INTR_TYPE) { + /* Calculate user time out parameters */ + InTimeoutCount= 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CMngtAdpt->InnerTimeOut; + if ((I2CInTOTcnt!= 0) && (I2CInTOTcnt!= I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount= HalTimerOp.HalTimerReadCount(1); + } + I2CDataLenBak = (u32)(pSalI2CHND->pRXBuf->DataLen); + I2CDataPtrBak = (u32)(pSalI2CHND->pRXBuf->pDataBuf); + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_RX_READY; + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_ABRT; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_ACTIVITY; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + pHalI2COP->HalI2CClrAllIntr(pHalI2CInitDat); + + /* Clear RX FIFO */ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RXFLR); + while (I2CChkRawSts > 0){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RXFLR); + } + + /* To fill the Master Read Command into TX FIFO */ + pSalI2CMngtAdpt->MstRDCmdCnt = pSalI2CHND->pRXBuf->DataLen; + I2CLocalLen = 2;//pSalI2CHND->pRXBuf->DataLen; + pSalI2CHND->DevSts = I2C_STS_RX_READY; + + + while (1) { +SEND_I2C_RD_CMD_INTR: + + /* Calculate user time out parameters */ + InTimeoutCountRty = 0; + InStartCountRty = 0; + I2CInTOTcntRty = pSalI2CHND->TimeOut; + + if ((I2CInTOTcntRty != 0) && (I2CInTOTcntRty!= I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCountRty= (I2CInTOTcntRty*1000/TIMER_TICK_US); + InStartCountRty= HalTimerOp.HalTimerReadCount(1); + } + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + + if ((pSalI2CMngtAdpt->MstRDCmdCnt > 0) && (I2CLocalLen > 0)){ + pHalI2CInitDat->I2CCmd = I2C_READ_CMD; + pHalI2CInitDat->I2CDataLen= 1; + pHalI2CInitDat->I2CRWData = pSalI2CHND->pRXBuf->pDataBuf; + pHalI2CInitDat->I2CStop = I2C_STOP_DIS; + + if ((pSalI2CMngtAdpt->MstRDCmdCnt == 1) && ((pSalI2CHND->I2CExd & I2C_EXD_MTR_HOLD_BUS) == 0)){ + pHalI2CInitDat->I2CStop = I2C_STOP_EN; + } + + pHalI2COP->HalI2CMassSend(pHalI2CInitDat); + } + + if (pSalI2CHND->I2CExd & I2C_EXD_MTR_ADDR_RTY) { + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + while ((I2CChkRawSts & BIT_IC_STATUS_TFE) == 0) { + I2CChkRawSts2 = pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT); + if ((I2CChkRawSts2 & BIT_IC_RAW_INTR_STAT_TX_ABRT) != 0){ + break; + } + + /* Time-Out check */ + if (InTimeoutCountRty > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCountRty, InTimeoutCountRty)) { + break; + } + } + else { + if (I2CInTOTcntRty == 0) { + break; + } + } + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + + } + + HalDelayUs(((1000*30)/pHalI2CInitDat->I2CClk)); //the 10 is for ten bit time + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_RAW_INTR_STAT); + I2CChkRawSts2 = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + if (I2CChkRawSts & BIT_IC_RAW_INTR_STAT_TX_ABRT) { + + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + pSalI2CHND->DevSts = I2C_STS_RX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pRXBuf->DataLen = (u16)I2CDataLenBak; + pSalI2CHND->pRXBuf->pDataBuf= (u8 *)I2CDataPtrBak; + + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CMngtAdpt->InnerTimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + goto SEND_I2C_RD_CMD_INTR; + } + else if ((I2CChkRawSts2 & BIT_IC_STATUS_TFE) != BIT_IC_STATUS_TFE){ + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + pSalI2CHND->DevSts = I2C_STS_RX_READY; + pSalI2CHND->ErrType = 0; + pSalI2CHND->pRXBuf->DataLen = (u16)I2CDataLenBak; + pSalI2CHND->pRXBuf->pDataBuf= (u8 *)I2CDataPtrBak; + + /* Calculate user time out parameters */ + InTimeoutCount = 0; + InStartCount = 0; + I2CInTOTcnt = pSalI2CMngtAdpt->InnerTimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + goto SEND_I2C_RD_CMD_INTR; + } + else { + I2CChkRawSts2 = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS); + //if ((I2CChkRawSts2 & BIT_IC_STATUS_RFNE) == BIT_IC_STATUS_RFNE) + { + //DBG_8195A("h\n"); + if (I2CLocalLen>0){ + I2CLocalLen--; + pSalI2CMngtAdpt->MstRDCmdCnt --; + } + } + } + } + else { + if (I2CLocalLen>0) { + I2CLocalLen--; + pSalI2CMngtAdpt->MstRDCmdCnt --; + } + } + + if ((I2CLocalLen == 0) || (pSalI2CHND->pRXBuf->DataLen == 1)){ + pHalI2CInitDat->I2CIntrClr = REG_DW_I2C_IC_CLR_TX_ABRT; + pHalI2COP->HalI2CClrIntr(pHalI2CInitDat); + I2CLocalTemp = 0; + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_RX_FULL | + BIT_IC_INTR_MASK_M_RX_OVER | + BIT_IC_INTR_MASK_M_RX_UNDER|BIT_IC_INTR_MASK_M_TX_ABRT); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + break; + } + + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_TX_FF_TO; + + RtkI2CDeInit(pSalI2CHND); + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) == BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + RtkI2CInit(pSalI2CHND); + + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + while((I2CChkRawSts & BIT_IC_ENABLE_STATUS_IC_EN) != BIT_IC_ENABLE_STATUS_IC_EN){ + I2CChkRawSts = (u32)pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_ENABLE_STATUS); + } + return HAL_TIMEOUT; + } + } + } + + } +#endif/*I2C_INTR_OP_TYPE*/ + }/*if (pSalI2CHND->I2CMaster == I2C_MASTER_MODE)*/ + else + { +#if I2C_POLL_OP_TYPE + if (pSalI2CHND->OpType == I2C_POLL_TYPE) { + /* Calculate user time out parameters */ + I2CInTOTcnt = pSalI2CHND->TimeOut; + if ((I2CInTOTcnt != 0) && (I2CInTOTcnt != I2C_TIMEOOUT_ENDLESS)) { + InTimeoutCount = (I2CInTOTcnt*1000/TIMER_TICK_US); + InStartCount = HalTimerOp.HalTimerReadCount(1); + } + + /* Receive data till the RX buffer data length is zero */ + for (;pSalI2CHND->pRXBuf->DataLen>0; ) { + if ((pHalI2COP->HalI2CReadReg(pHalI2CInitDat,REG_DW_I2C_IC_STATUS) + & (BIT_IC_STATUS_RFNE | BIT_IC_STATUS_RFF)) != 0) { + *(pSalI2CHND->pRXBuf->pDataBuf) = + pHalI2COP->HalI2CReceive(pHalI2CInitDat); + pSalI2CHND->pRXBuf->pDataBuf++; + pSalI2CHND->pRXBuf->DataLen--; + } + else { + /* Time-Out check */ + if (InTimeoutCount > 0) { + if (HAL_TIMEOUT == I2CIsTimeout(InStartCount, InTimeoutCount)) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,9\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + else { + if (I2CInTOTcnt == 0) { + pSalI2CHND->DevSts = I2C_STS_TIMEOUT; + pSalI2CHND->ErrType = I2C_ERR_RX_FF_TO; + DBG_I2C_ERR("RtkI2CReceive Timeout, I2C%2x,10\n",pSalI2CHND->DevNum); + DBG_I2C_ERR("DevSts:%x, ErrType:%x\n", pSalI2CHND->DevSts, pSalI2CHND->ErrType); + return HAL_TIMEOUT; + } + } + } + } + + /* I2C Device Status Update */ + pSalI2CHND->DevSts = I2C_STS_IDLE; + } +#endif + +#if I2C_INTR_OP_TYPE/*I2C_INTR_OP_TYPE*/ + if (pSalI2CHND->OpType == I2C_INTR_TYPE) { + pSalI2CHND->DevSts = I2C_STS_RX_READY; + + /*I2C Enable RX Related Interrupts*/ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_RX_FULL | + BIT_IC_INTR_MASK_M_RX_OVER | + BIT_IC_INTR_MASK_M_RX_UNDER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + } +#endif/*I2C_INTR_OP_TYPE*/ + +#if I2C_DMA_OP_TYPE/*I2C_INTR_OP_TYPE*/ + if (pSalI2CHND->OpType == I2C_DMA_TYPE) { + pSalI2CHND->DevSts = I2C_STS_RX_READY; + + /*I2C Enable RX Related Interrupts*/ + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_INTR_MASK); + I2CLocalTemp |= (BIT_IC_INTR_MASK_M_RX_OVER | + BIT_IC_INTR_MASK_M_RX_UNDER); + pHalI2CInitDat->I2CIntrMSK = I2CLocalTemp; + pHalI2COP->HalI2CIntrCtrl(pHalI2CInitDat); + + //HalGdmaOpInit(pHalI2CGdmaOp); + pSalI2CMngtAdpt->pHalGdmaOpInit(pHalI2CGdmaOp); + + pHalI2CRxGdmaAdpt->GdmaCtl.BlockSize = pSalI2CHND->pRXBuf->DataLen; + pHalI2CRxGdmaAdpt->ChSar = (u32)(I2C0_REG_BASE+REG_DW_I2C_IC_DATA_CMD+ + pSalI2CHND->DevNum*0x400); + pHalI2CRxGdmaAdpt->ChDar = (u32)pSalI2CHND->pRXBuf->pDataBuf; + + pHalI2CGdmaOp->HalGdmaChSeting(pHalI2CRxGdmaAdpt); + pHalI2CGdmaOp->HalGdmaChEn(pHalI2CRxGdmaAdpt); + pSalI2CHND->DevSts = I2C_STS_RX_ING; + pHalI2CInitDat->I2CDMACtrl = BIT_CTRL_IC_DMA_CR_RDMAE(1); + pHalI2COP->HalI2CDMACtrl(pHalI2CInitDat); + } +#endif/*I2C_INTR_OP_TYPE*/ + + } + + return HAL_OK; +} +#endif + +#ifndef CONFIG_MBED_ENABLED +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetMngtAdpt +// +// Description: +// According to the input index, all the memory space are allocated and all the +// related pointers are assigned. The management adapter pointer will be +// returned. +// +// Arguments: +// [in] u8 I2CIdx - +// I2C module index +// +// Return: +// PSAL_I2C_MNGT_ADPT +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +PSAL_I2C_MNGT_ADPT +RtkI2CGetMngtAdpt( + IN u8 I2CIdx +){ + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_USERCB_ADPT pSalI2CUserCBAdpt = NULL; + + /* If the kernel is available, Memory-allocation is used. */ +#ifdef I2C_STATIC_ALLOC + + pSalI2CMngtAdpt = (PSAL_I2C_MNGT_ADPT)RtlZmalloc(sizeof(SAL_I2C_MNGT_ADPT)); + pSalI2CMngtAdpt->pSalHndPriv = (PSAL_I2C_HND_PRIV)RtlZmalloc(sizeof(SAL_I2C_HND_PRIV)); + pSalI2CMngtAdpt->pHalInitDat = (PHAL_I2C_INIT_DAT)RtlZmalloc(sizeof(HAL_I2C_INIT_DAT)); + pSalI2CMngtAdpt->pHalOp = (PHAL_I2C_OP)RtlZmalloc(sizeof(HAL_I2C_OP)); + pSalI2CMngtAdpt->pIrqHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalI2CMngtAdpt->pHalTxGdmaAdp = (PHAL_GDMA_ADAPTER)RtlZmalloc(sizeof(HAL_GDMA_ADAPTER)); + pSalI2CMngtAdpt->pHalRxGdmaAdp = (PHAL_GDMA_ADAPTER)RtlZmalloc(sizeof(HAL_GDMA_ADAPTER)); + pSalI2CMngtAdpt->pHalGdmaOp = (PHAL_GDMA_OP)RtlZmalloc(sizeof(HAL_GDMA_OP)); + pSalI2CMngtAdpt->pIrqTxGdmaHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalI2CMngtAdpt->pIrqRxGdmaHnd = (PIRQ_HANDLE)RtlZmalloc(sizeof(IRQ_HANDLE)); + pSalI2CMngtAdpt->pUserCB = (PSAL_I2C_USER_CB)RtlZmalloc(sizeof(SAL_I2C_USER_CB)); + pSalI2CMngtAdpt->pDMAConf = (PSAL_I2C_DMA_USER_DEF)RtlZmalloc(sizeof(SAL_I2C_DMA_USER_DEF)); + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)RtlZmalloc((sizeof(SAL_I2C_USERCB_ADPT)*SAL_USER_CB_NUM)); +#else + switch (I2CIdx){ + case I2C0_SEL: + { + pSalI2CMngtAdpt = &SalI2C0MngtAdpt; + pSalI2CMngtAdpt->pSalHndPriv = &SalI2C0HndPriv; + pSalI2CMngtAdpt->pHalInitDat = &HalI2C0InitData; + pSalI2CMngtAdpt->pHalOp = &HalI2COpSAL; + pSalI2CMngtAdpt->pIrqHnd = &I2C0IrqHandleDat; + pSalI2CMngtAdpt->pHalTxGdmaAdp = &HalI2C0TxGdmaAdpt; + pSalI2CMngtAdpt->pHalRxGdmaAdp = &HalI2C0RxGdmaAdpt; + pSalI2CMngtAdpt->pHalGdmaOp = &HalI2C0GdmaOp; + pSalI2CMngtAdpt->pIrqTxGdmaHnd = &I2C0TxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pIrqRxGdmaHnd = &I2C0RxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pUserCB = &SalI2C0UserCB; + pSalI2CMngtAdpt->pDMAConf = &SalI2C0DmaUserDef; + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)&SalI2C0UserCBAdpt; + break; + } + + case I2C1_SEL: + { + pSalI2CMngtAdpt = &SalI2C1MngtAdpt; + pSalI2CMngtAdpt->pSalHndPriv = &SalI2C1HndPriv; + pSalI2CMngtAdpt->pHalInitDat = &HalI2C1InitData; + pSalI2CMngtAdpt->pHalOp = &HalI2COpSAL; + pSalI2CMngtAdpt->pIrqHnd = &I2C1IrqHandleDat; + pSalI2CMngtAdpt->pHalTxGdmaAdp = &HalI2C1TxGdmaAdpt; + pSalI2CMngtAdpt->pHalRxGdmaAdp = &HalI2C1RxGdmaAdpt; + pSalI2CMngtAdpt->pHalGdmaOp = &HalI2C1GdmaOp; + pSalI2CMngtAdpt->pIrqTxGdmaHnd = &I2C1TxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pIrqRxGdmaHnd = &I2C1RxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pUserCB = &SalI2C1UserCB; + pSalI2CMngtAdpt->pDMAConf = &SalI2C1DmaUserDef; + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)&SalI2C1UserCBAdpt; + break; + } + + case I2C2_SEL: + { + pSalI2CMngtAdpt = &SalI2C2MngtAdpt; + pSalI2CMngtAdpt->pSalHndPriv = &SalI2C2HndPriv; + pSalI2CMngtAdpt->pHalInitDat = &HalI2C2InitData; + pSalI2CMngtAdpt->pHalOp = &HalI2COpSAL; + pSalI2CMngtAdpt->pIrqHnd = &I2C2IrqHandleDat; + pSalI2CMngtAdpt->pHalTxGdmaAdp = &HalI2C2TxGdmaAdpt; + pSalI2CMngtAdpt->pHalRxGdmaAdp = &HalI2C2RxGdmaAdpt; + pSalI2CMngtAdpt->pHalGdmaOp = &HalI2C2GdmaOp; + pSalI2CMngtAdpt->pIrqTxGdmaHnd = &I2C2TxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pIrqRxGdmaHnd = &I2C2RxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pUserCB = &SalI2C2UserCB; + pSalI2CMngtAdpt->pDMAConf = &SalI2C2DmaUserDef; + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)&SalI2C2UserCBAdpt; + break; + } + + case I2C3_SEL: + { + pSalI2CMngtAdpt = &SalI2C3MngtAdpt; + pSalI2CMngtAdpt->pSalHndPriv = &SalI2C3HndPriv; + pSalI2CMngtAdpt->pHalInitDat = &HalI2C3InitData; + pSalI2CMngtAdpt->pHalOp = &HalI2COpSAL; + pSalI2CMngtAdpt->pIrqHnd = &I2C3IrqHandleDat; + pSalI2CMngtAdpt->pHalTxGdmaAdp = &HalI2C3TxGdmaAdpt; + pSalI2CMngtAdpt->pHalRxGdmaAdp = &HalI2C3RxGdmaAdpt; + pSalI2CMngtAdpt->pHalGdmaOp = &HalI2C3GdmaOp; + pSalI2CMngtAdpt->pIrqTxGdmaHnd = &I2C3TxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pIrqRxGdmaHnd = &I2C3RxGdmaIrqHandleDat; + pSalI2CMngtAdpt->pUserCB = &SalI2C3UserCB; + pSalI2CMngtAdpt->pDMAConf = &SalI2C3DmaUserDef; + pSalI2CUserCBAdpt = (PSAL_I2C_USERCB_ADPT)&SalI2C3UserCBAdpt; + break; + } + + default + break; + } +#endif + + /*To assign user callback pointers*/ + pSalI2CMngtAdpt->pUserCB->pTXCB = pSalI2CUserCBAdpt; + pSalI2CMngtAdpt->pUserCB->pTXCCB = (pSalI2CUserCBAdpt+1); + pSalI2CMngtAdpt->pUserCB->pRXCB = (pSalI2CUserCBAdpt+2); + pSalI2CMngtAdpt->pUserCB->pRXCCB = (pSalI2CUserCBAdpt+3); + pSalI2CMngtAdpt->pUserCB->pRDREQCB = (pSalI2CUserCBAdpt+4); + pSalI2CMngtAdpt->pUserCB->pERRCB = (pSalI2CUserCBAdpt+5); + pSalI2CMngtAdpt->pUserCB->pDMATXCB = (pSalI2CUserCBAdpt+6); + pSalI2CMngtAdpt->pUserCB->pDMATXCCB = (pSalI2CUserCBAdpt+7); + pSalI2CMngtAdpt->pUserCB->pDMARXCB = (pSalI2CUserCBAdpt+8); + pSalI2CMngtAdpt->pUserCB->pDMARXCCB = (pSalI2CUserCBAdpt+9); + pSalI2CMngtAdpt->pUserCB->pGENCALLCB= (pSalI2CUserCBAdpt+10); + + /*To assign the rest pointers*/ + pSalI2CMngtAdpt->MstRDCmdCnt = 0; + pSalI2CMngtAdpt->InnerTimeOut = 2000; // inner time-out count, 2000 ms + pSalI2CMngtAdpt->pSalHndPriv->ppSalI2CHnd = (void**)&(pSalI2CMngtAdpt->pSalHndPriv); + + /* To assign the default (ROM) HAL OP initialization function */ +#if defined(CONFIG_CHIP_A_CUT) || defined(CONFIG_CHIP_B_CUT) + pSalI2CMngtAdpt->pHalOpInit = &HalI2COpInit; +#elif defined(CONFIG_CHIP_C_CUT) + pSalI2CMngtAdpt->pHalOpInit = &HalI2COpInitV02; +#endif + + /* To assign the default (ROM) HAL GDMA OP initialization function */ + pSalI2CMngtAdpt->pHalGdmaOpInit = &HalGdmaOpInit; + + /* To assign the default (ROM) SAL interrupt function */ +#ifndef CONFIG_CHIP_D_CUT + pSalI2CMngtAdpt->pSalIrqFunc = &I2CISRHandle; +#endif + + /* To assign the default (ROM) SAL DMA TX interrupt function */ + pSalI2CMngtAdpt->pSalDMATxIrqFunc = &I2CTXGDMAISRHandle; + + /* To assign the default (ROM) SAL DMA RX interrupt function */ + pSalI2CMngtAdpt->pSalDMARxIrqFunc = &I2CRXGDMAISRHandle; + + return pSalI2CMngtAdpt; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CFreeMngtAdpt +// +// Description: +// Free all the previous allocated memory space. +// +// Arguments: +// [in] PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt - +// I2C SAL management adapter pointer +// +// +// Return: +// The status of the enable process. +// _EXIT_SUCCESS if the RtkI2CFreeMngtAdpt succeeded. +// _EXIT_FAILURE if the RtkI2CFreeMngtAdpt failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-02. +// +//--------------------------------------------------------------------------------------------------- +HAL_Status +RtkI2CFreeMngtAdpt( + IN PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt +){ +#ifdef I2C_STATIC_ALLOC + RtlMfree((u8 *)pSalI2CMngtAdpt->pUserCB->pTXCB, (sizeof(SAL_I2C_USERCB_ADPT)*SAL_USER_CB_NUM)); + RtlMfree((u8 *)pSalI2CMngtAdpt->pDMAConf, (sizeof(SAL_I2C_DMA_USER_DEF))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pIrqRxGdmaHnd, (sizeof(IRQ_HANDLE))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pIrqTxGdmaHnd, (sizeof(IRQ_HANDLE))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pHalGdmaOp, (sizeof(HAL_GDMA_OP))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pHalRxGdmaAdp, (sizeof(HAL_GDMA_ADAPTER))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pHalTxGdmaAdp, (sizeof(HAL_GDMA_ADAPTER))); + RtlMfree((u8 *)pSalI2CMngtAdpt->pUserCB, sizeof(SAL_I2C_USER_CB)); + RtlMfree((u8 *)pSalI2CMngtAdpt->pIrqHnd, sizeof(IRQ_HANDLE)); + RtlMfree((u8 *)pSalI2CMngtAdpt->pHalOp, sizeof(HAL_I2C_OP)); + RtlMfree((u8 *)pSalI2CMngtAdpt->pHalInitDat, sizeof(HAL_I2C_INIT_DAT)); + RtlMfree((u8 *)pSalI2CMngtAdpt->pSalHndPriv, sizeof(SAL_I2C_HND_PRIV)); + RtlMfree((u8 *)pSalI2CMngtAdpt, sizeof(SAL_I2C_MNGT_ADPT)); +#else + ; +#endif + + return HAL_OK; +} + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CGetSalHnd +// +// Description: +// Allocation of lower layer memory spaces will be done by invoking RtkI2CGetMngtAdpt +// in this function and return a SAL_I2C_HND pointer to upper layer. +// According to the given I2C index, RtkI2CGetMngtAdpt will allocate all the memory +// space such as SAL_I2C_HND, HAL_I2C_INIT_DAT, SAL_I2C_USER_CB etc. +// +// +// Arguments: +// [in] u8 I2CIdx - +// I2C Index +// +// Return: +// PSAL_I2C_HND +// A pointer to SAL_I2C_HND which is allocated in the lower layer. +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +PSAL_I2C_HND +RtkI2CGetSalHnd( + IN u8 I2CIdx +){ + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND pSalI2CHND = NULL; + + /* Check the user define setting and the given index */ + if (RtkI2CIdxChk(I2CIdx)) { + return (PSAL_I2C_HND)NULL; + } + + /* Invoke RtkI2CGetMngtAdpt to get the I2C SAL management adapter pointer */ + pSalI2CMngtAdpt = RtkI2CGetMngtAdpt(I2CIdx); + + /* Assign the private SAL handle to public SAL handle */ + pSalI2CHND = &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); + + /* Assign the internal HAL initial data pointer to the SAL handle */ + pSalI2CHND->pInitDat = pSalI2CMngtAdpt->pHalInitDat; + + /* Assign the internal user callback pointer to the SAL handle */ + pSalI2CHND->pUserCB = pSalI2CMngtAdpt->pUserCB; + + /* Assign the internal user define DMA configuration to the SAL handle */ + pSalI2CHND->pDMAConf = pSalI2CMngtAdpt->pDMAConf; + + return &(pSalI2CMngtAdpt->pSalHndPriv->SalI2CHndPriv); +} + + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkI2CFreeSalHnd +// +// Description: +// Based on the given pSalI2CHND, the top layer management adapter pointer could +// be reversely parsed. And free memory space is done by RtkI2CFreeMngtAdpt. +// +// +// Arguments: +// [in] PSAL_I2C_HND pSalI2CHND - +// SAL I2C handle +// +// Return: +// The status of the free SAL memory space process. +// _EXIT_SUCCESS if the RtkI2CFreeSalHnd succeeded. +// _EXIT_FAILURE if the RtkI2CFreeSalHnd failed. +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//---------------------------------------------------------------------------------------------------- +HAL_Status +RtkI2CFreeSalHnd( + IN PSAL_I2C_HND pSalI2CHND +){ + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PSAL_I2C_HND_PRIV pSalI2CHNDPriv = NULL; + + /* To get the SAL_I2C_MNGT_ADPT pointer */ + pSalI2CHNDPriv = CONTAINER_OF(pSalI2CHND, SAL_I2C_HND_PRIV, SalI2CHndPriv); + pSalI2CMngtAdpt = CONTAINER_OF(pSalI2CHNDPriv->ppSalI2CHnd, SAL_I2C_MNGT_ADPT, pSalHndPriv); + + /* Invoke RtkI2CFreeMngtAdpt to free all the lower layer memory space */ + return (RtkI2CFreeMngtAdpt(pSalI2CMngtAdpt)); +} + +#endif // end of "#ifndef CONFIG_MBED_ENABLED" + +//--------------------------------------------------------------------------------------------------- +//Function Name: +// RtkSalI2CSts +// +// Description: +// Get i2c status +// +// Arguments: +// A SAL operation adapter pointer +// +// Return: +// NA +// +// Note: +// NA +// +// See Also: +// NA +// +// Author: +// By Jason Deng, 2014-04-03. +// +//--------------------------------------------------------------------------------------------------- +u32 +RtkSalI2CSts( + IN VOID *Data +){ + PSAL_I2C_HND pSalI2CHND = (PSAL_I2C_HND) Data; + PSAL_I2C_HND_PRIV pSalI2CHNDPriv = NULL; + PSAL_I2C_MNGT_ADPT pSalI2CMngtAdpt = NULL; + PHAL_I2C_INIT_DAT pHalI2CInitDat = NULL; + PHAL_I2C_OP pHalI2COP = NULL; + u32 I2CLocalTemp; + + + /* To Get the SAL_I2C_MNGT_ADPT Pointer */ + pSalI2CHNDPriv = CONTAINER_OF(pSalI2CHND, SAL_I2C_HND_PRIV, SalI2CHndPriv); + pSalI2CMngtAdpt = CONTAINER_OF(pSalI2CHNDPriv->ppSalI2CHnd, SAL_I2C_MNGT_ADPT, pSalHndPriv); + + pHalI2CInitDat = pSalI2CMngtAdpt->pHalInitDat; + pHalI2COP = pSalI2CMngtAdpt->pHalOp; + + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_RAW_INTR_STAT); + + if (I2CLocalTemp & BIT_IC_RAW_INTR_STAT_GEN_CALL) { + return 2; + } + else if (I2CLocalTemp & BIT_IC_RAW_INTR_STAT_RD_REQ) { + return 1; + } + + I2CLocalTemp = pHalI2COP->HalI2CReadReg(pHalI2CInitDat, REG_DW_I2C_IC_STATUS); + + if (I2CLocalTemp & BIT_IC_STATUS_RFNE) { + return 3; + } + + return 0; +} diff --git a/component/soc/realtek/8195a/fwlib/src/hal_i2s.c b/component/soc/realtek/8195a/fwlib/src/hal_i2s.c new file mode 100644 index 0000000..3361540 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_i2s.c @@ -0,0 +1,415 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "hal_i2s.h" +#include "rand.h" +#include "rtl_utility.h" + + +//1 need to be modified + + +/*====================================================== + Local used variables +*/ +SRAM_BF_DATA_SECTION +HAL_I2S_OP HalI2SOpSAL={0}; + + +VOID +I2SISRHandle( + IN VOID *Data +) +{ + PHAL_I2S_ADAPTER pI2SAdp = (PHAL_I2S_ADAPTER) Data; + PHAL_I2S_OP pHalI2SOP = &HalI2SOpSAL; + PHAL_I2S_INIT_DAT pI2SCfg = pI2SAdp->pInitDat; + u8 I2SIrqIdx = pI2SCfg->I2SIdx; + u32 I2STxIsr, I2SRxIsr; + u8 I2SPageNum = pI2SCfg->I2SPageNum+1; +// u32 I2SPageSize = (pI2SAdp->I2SPageSize+1)<<2; + u32 i; + u32 pbuf; + + I2STxIsr = pHalI2SOP->HalI2SReadReg(pI2SCfg, REG_I2S_TX_STATUS_INT); + I2SRxIsr = pHalI2SOP->HalI2SReadReg(pI2SCfg, REG_I2S_RX_STATUS_INT); + + pI2SCfg->I2STxIntrClr = I2STxIsr; + pI2SCfg->I2SRxIntrClr = I2SRxIsr; + pHalI2SOP->HalI2SClrIntr(pI2SCfg); + + for (i=0 ; iI2SHWTxIdx)) { +// pbuf = ((u32)(pI2SCfg->I2STxData)) + (I2SPageSize*pI2SCfg->I2SHWTxIdx); + pbuf = (u32)pI2SAdp->TxPageList[pI2SCfg->I2SHWTxIdx]; + pI2SAdp->UserCB.TxCCB(pI2SAdp->UserCB.TxCBId, (char*)pbuf); + I2STxIsr &= ~(1<I2SHWTxIdx); + pI2SCfg->I2SHWTxIdx += 1; + if (pI2SCfg->I2SHWTxIdx == I2SPageNum) { + pI2SCfg->I2SHWTxIdx = 0; + } + } + + if (I2SRxIsr & (1<I2SHWRxIdx)) { +// pbuf = ((u32)(pI2SCfg->I2SRxData)) + (I2SPageSize*pI2SCfg->I2SHWRxIdx); + pbuf = (u32)pI2SAdp->RxPageList[pI2SCfg->I2SHWRxIdx]; + pI2SAdp->UserCB.RxCCB(pI2SAdp->UserCB.RxCBId, (char*)pbuf); + I2SRxIsr &= ~(1<I2SHWRxIdx); + pI2SCfg->I2SHWRxIdx += 1; + if (pI2SCfg->I2SHWRxIdx == I2SPageNum) { + pI2SCfg->I2SHWRxIdx = 0; + } + } + } +} + + +static HAL_Status +RtkI2SIrqInit( + IN PHAL_I2S_ADAPTER pI2SAdapter +) +{ + PIRQ_HANDLE pIrqHandle; + + if (pI2SAdapter->DevNum > I2S_MAX_ID) { + DBG_I2S_ERR("RtkI2SIrqInit: Invalid I2S Index(&d)\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + pIrqHandle = &pI2SAdapter->IrqHandle; + + switch (pI2SAdapter->DevNum){ + case I2S0_SEL: + pIrqHandle->IrqNum = I2S0_PCM0_IRQ; + break; + + case I2S1_SEL: + pIrqHandle->IrqNum = I2S1_PCM1_IRQ; + break; + + default: + return HAL_ERR_PARA; + } + + pIrqHandle->Data = (u32) (pI2SAdapter); + pIrqHandle->IrqFun = (IRQ_FUN) I2SISRHandle; + pIrqHandle->Priority = 3; + InterruptRegister(pIrqHandle); + InterruptEn(pIrqHandle); + + return HAL_OK; +} + +static HAL_Status +RtkI2SIrqDeInit( + IN PHAL_I2S_ADAPTER pI2SAdapter +) +{ + if (pI2SAdapter->DevNum > I2S_MAX_ID) { + DBG_I2S_ERR("RtkI2SIrqDeInit: Invalid I2S Index(&d)\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + InterruptDis(&pI2SAdapter->IrqHandle); + InterruptUnRegister(&pI2SAdapter->IrqHandle); + + return HAL_OK; +} + +static HAL_Status +RtkI2SPinMuxInit( + IN PHAL_I2S_ADAPTER pI2SAdapter +) +{ + u32 I2Stemp; + + if (pI2SAdapter->DevNum > I2S_MAX_ID) { + DBG_I2S_ERR("RtkI2SPinMuxInit: Invalid I2S Index(&d)\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + // enable system pll + I2Stemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1) | (1<<9) | (1<<10); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SYSPLL_CTRL1, I2Stemp); + + switch (pI2SAdapter->DevNum){ + case I2S0_SEL: + ACTCK_I2S_CCTRL(ON); + LXBUS_FCTRL(ON); // enable lx bus for i2s + + /*I2S0 Pin Mux Setting*/ + PinCtrl(I2S0, pI2SAdapter->PinMux, ON); + if (pI2SAdapter->PinMux == I2S_S0) { + DBG_I2S_WARN(ANSI_COLOR_MAGENTA"I2S0 Pin may conflict with JTAG\r\n"ANSI_COLOR_RESET); + } + I2S0_MCK_CTRL(ON); + I2S0_PIN_CTRL(ON); + I2S0_FCTRL(ON); + + break; + case I2S1_SEL: + ACTCK_I2S_CCTRL(ON); + LXBUS_FCTRL(ON); // enable lx bus for i2s + + /*I2S1 Pin Mux Setting*/ + PinCtrl(I2S1, pI2SAdapter->PinMux, ON); + if (pI2SAdapter->PinMux == I2S_S2) { + DBG_I2S_WARN(ANSI_COLOR_MAGENTA"I2S1 Pin may conflict with JTAG\r\n"ANSI_COLOR_RESET); + } + I2S1_MCK_CTRL(ON); + I2S1_PIN_CTRL(ON); + I2S0_FCTRL(ON); //i2s 1 is control by bit 24 BIT_PERI_I2S0_EN + I2S1_FCTRL(ON); + break; + default: + return HAL_ERR_PARA; + } + + return HAL_OK; +} + + +static HAL_Status +RtkI2SPinMuxDeInit( + IN PHAL_I2S_ADAPTER pI2SAdapter +) +{ + if (pI2SAdapter->DevNum > I2S_MAX_ID) { + DBG_I2S_ERR("RtkI2SPinMuxDeInit: Invalid I2S Index(&d)\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + switch (pI2SAdapter->DevNum){ + case I2S0_SEL: + /*I2S0 Pin Mux Setting*/ + //ACTCK_I2C0_CCTRL(OFF); + PinCtrl(I2S0, pI2SAdapter->PinMux, OFF); + I2S0_MCK_CTRL(OFF); + I2S0_PIN_CTRL(OFF); + //I2S0_FCTRL(OFF); + + break; + case I2S1_SEL: + /*I2S1 Pin Mux Setting*/ + //ACTCK_I2C1_CCTRL(OFF); + PinCtrl(I2S1, pI2SAdapter->PinMux, OFF); + I2S1_MCK_CTRL(OFF); + I2S1_PIN_CTRL(OFF); + //I2S1_FCTRL(OFF); + break; + default: + return HAL_ERR_PARA; + } + + return HAL_OK; +} + + +HAL_Status +RtkI2SInit( + IN VOID *Data +) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) Data; + PHAL_I2S_OP pHalI2SOP = &HalI2SOpSAL; + PHAL_I2S_INIT_DAT pI2SCfg; + + if (pI2SAdapter == 0) { + DBG_I2S_ERR("RtkI2SInit: Null Pointer\r\n"); + return HAL_ERR_PARA; + } + + if (pI2SAdapter->DevNum > I2S_MAX_ID) { + DBG_I2S_ERR("RtkI2SInit: Invalid I2S Index(&d)\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + pI2SCfg = pI2SAdapter->pInitDat; + + /*I2S Initialize HAL Operations*/ + HalI2SOpInit(pHalI2SOP); + + /*I2S Interrupt Initialization*/ + RtkI2SIrqInit(pI2SAdapter); + + /*I2S Pin Mux Initialization*/ + RtkI2SPinMuxInit(pI2SAdapter); + + /*I2S Load User Setting*/ + pI2SCfg->I2SIdx = pI2SAdapter->DevNum; + + /*I2S HAL Initialization*/ + pHalI2SOP->HalI2SInit(pI2SCfg); + + /*I2S Device Status Update*/ + pI2SAdapter->DevSts = I2S_STS_INITIALIZED; + + /*I2S Enable Module*/ + pI2SCfg->I2SEn = I2S_ENABLE; + pHalI2SOP->HalI2SEnable(pI2SCfg); + + /*I2S Device Status Update*/ + pI2SAdapter->DevSts = I2S_STS_IDLE; + + return HAL_OK; +} + +HAL_Status +RtkI2SDeInit( + IN VOID *Data +) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) Data; + PHAL_I2S_OP pHalI2SOP = &HalI2SOpSAL; + PHAL_I2S_INIT_DAT pI2SCfg; + + if (pI2SAdapter == 0) { + DBG_I2S_ERR("RtkI2SDeInit: Null Pointer\r\n"); + return HAL_ERR_PARA; + } + + pI2SCfg = pI2SAdapter->pInitDat; + + /*I2S Disable Module*/ + pI2SCfg->I2SEn = I2S_DISABLE; + pHalI2SOP->HalI2SEnable(pI2SCfg); + + /*I2C HAL DeInitialization*/ + //pHalI2SOP->HalI2SDeInit(pI2SCfg); + + /*I2S Interrupt DeInitialization*/ + RtkI2SIrqDeInit(pI2SAdapter); + + /*I2S Pin Mux DeInitialization*/ + RtkI2SPinMuxDeInit(pI2SAdapter); + + /*I2S HAL DeInitialization*/ + pHalI2SOP->HalI2SDeInit(pI2SCfg); + + /*I2S Device Status Update*/ + pI2SAdapter->DevSts = I2S_STS_UNINITIAL; + + return HAL_OK; +} + +HAL_Status +RtkI2SEnable( + IN VOID *Data +) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) Data; + PHAL_I2S_OP pHalI2SOP = &HalI2SOpSAL; + PHAL_I2S_INIT_DAT pI2SCfg; + + pI2SCfg = pI2SAdapter->pInitDat; + pI2SCfg->I2SEn = I2S_ENABLE; + pHalI2SOP->HalI2SEnable(pI2SCfg); + + return HAL_OK; +} + +HAL_Status +RtkI2SDisable( + IN VOID *Data +) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) Data; + PHAL_I2S_OP pHalI2SOP = &HalI2SOpSAL; + PHAL_I2S_INIT_DAT pI2SCfg; + + pI2SCfg = pI2SAdapter->pInitDat; + pI2SCfg->I2SEn = I2S_DISABLE; + pHalI2SOP->HalI2SEnable(pI2SCfg); + + HalI2SClearAllOwnBit((VOID*)pI2SCfg); + + return HAL_OK; +} + +RTK_STATUS +RtkI2SIoCtrl( + IN VOID *Data +) +{ + return _EXIT_SUCCESS; +} + +RTK_STATUS +RtkI2SPowerCtrl( + IN VOID *Data +) +{ + return _EXIT_SUCCESS; +} + +HAL_Status +RtkI2SLoadDefault( + IN VOID *Adapter, + IN VOID *Setting +) +{ + PHAL_I2S_ADAPTER pI2SAdapter = (PHAL_I2S_ADAPTER) Adapter; + PHAL_I2S_INIT_DAT pI2SCfg = pI2SAdapter->pInitDat; + PHAL_I2S_DEF_SETTING pLoadSetting = (PHAL_I2S_DEF_SETTING)Setting; + + if (pI2SAdapter == 0) { + DBG_I2S_ERR("RtkI2SLoadDefault: Null Pointer\r\n"); + return HAL_ERR_PARA; + } + + if (pI2SAdapter->pInitDat == NULL) { + DBG_I2S_ERR("RtkI2SLoadDefault: pInitDat is NULL!\r\n", pI2SAdapter->DevNum); + return HAL_ERR_PARA; + } + + pI2SAdapter->DevSts = pLoadSetting->DevSts; + pI2SAdapter->ErrType = 0; + pI2SAdapter->TimeOut = 0; + + pI2SCfg->I2SIdx = pI2SAdapter->DevNum; + pI2SCfg->I2SEn = I2S_DISABLE; + pI2SCfg->I2SMaster = pLoadSetting->I2SMaster; + pI2SCfg->I2SWordLen = pLoadSetting->I2SWordLen; + pI2SCfg->I2SChNum = pLoadSetting->I2SChNum; + pI2SCfg->I2SPageNum = pLoadSetting->I2SPageNum; + pI2SCfg->I2SPageSize = pLoadSetting->I2SPageSize; + pI2SCfg->I2SRate = pLoadSetting->I2SRate; + pI2SCfg->I2STRxAct = pLoadSetting->I2STRxAct; + pI2SCfg->I2STxIntrMSK = pLoadSetting->I2STxIntrMSK; + pI2SCfg->I2SRxIntrMSK = pLoadSetting->I2SRxIntrMSK; + + return HAL_OK; +} + +VOID HalI2SOpInit( + IN VOID *Data +) +{ + PHAL_I2S_OP pHalI2SOp = (PHAL_I2S_OP) Data; + + pHalI2SOp->HalI2SInit = HalI2SInitRtl8195a_Patch; + pHalI2SOp->HalI2SDeInit = HalI2SDeInitRtl8195a; + pHalI2SOp->HalI2STx = HalI2STxRtl8195a; + pHalI2SOp->HalI2SRx = HalI2SRxRtl8195a; + pHalI2SOp->HalI2SEnable = HalI2SEnableRtl8195a; + pHalI2SOp->HalI2SIntrCtrl = HalI2SIntrCtrlRtl8195a; + pHalI2SOp->HalI2SReadReg = HalI2SReadRegRtl8195a; + pHalI2SOp->HalI2SSetRate = HalI2SSetRateRtl8195a; + pHalI2SOp->HalI2SSetWordLen = HalI2SSetWordLenRtl8195a; + pHalI2SOp->HalI2SSetChNum = HalI2SSetChNumRtl8195a; + pHalI2SOp->HalI2SSetPageNum = HalI2SSetPageNumRtl8195a; + pHalI2SOp->HalI2SSetPageSize = HalI2SSetPageSizeRtl8195a; + pHalI2SOp->HalI2SClrIntr = HalI2SClrIntrRtl8195a; + pHalI2SOp->HalI2SClrAllIntr = HalI2SClrAllIntrRtl8195a; + pHalI2SOp->HalI2SDMACtrl = HalI2SDMACtrlRtl8195a; +} + + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_mii.c b/component/soc/realtek/8195a/fwlib/src/hal_mii.c new file mode 100644 index 0000000..7af663e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_mii.c @@ -0,0 +1,43 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "hal_mii.h" + +VOID +HalMiiOpInit( + IN VOID *Data + ) +{ + DBG_ENTRANCE; + PHAL_MII_OP pHalMiiOp = (PHAL_MII_OP) Data; + + pHalMiiOp->HalMiiGmacInit = HalMiiGmacInitRtl8195a; + pHalMiiOp->HalMiiInit = HalMiiInitRtl8195a; + pHalMiiOp->HalMiiGmacReset = HalMiiGmacResetRtl8195a; + pHalMiiOp->HalMiiGmacEnablePhyMode = HalMiiGmacEnablePhyModeRtl8195a; + pHalMiiOp->HalMiiGmacXmit = HalMiiGmacXmitRtl8195a; + pHalMiiOp->HalMiiGmacCleanTxRing = HalMiiGmacCleanTxRingRtl8195a; + pHalMiiOp->HalMiiGmacFillTxInfo = HalMiiGmacFillTxInfoRtl8195a; + pHalMiiOp->HalMiiGmacFillRxInfo = HalMiiGmacFillRxInfoRtl8195a; + pHalMiiOp->HalMiiGmacTx = HalMiiGmacTxRtl8195a; + pHalMiiOp->HalMiiGmacRx = HalMiiGmacRxRtl8195a; + pHalMiiOp->HalMiiGmacSetDefaultEthIoCmd = HalMiiGmacSetDefaultEthIoCmdRtl8195a; + pHalMiiOp->HalMiiGmacInitIrq = HalMiiGmacInitIrqRtl8195a; + pHalMiiOp->HalMiiGmacGetInterruptStatus = HalMiiGmacGetInterruptStatusRtl8195a; + pHalMiiOp->HalMiiGmacClearInterruptStatus = HalMiiGmacClearInterruptStatusRtl8195a; +#if 0 + pHalMiiOp-> = Rtl8195a; + pHalMiiOp-> = Rtl8195a; + pHalMiiOp-> = Rtl8195a; + pHalMiiOp-> = Rtl8195a; + pHalMiiOp-> = Rtl8195a; +#endif +} + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_nfc.c b/component/soc/realtek/8195a/fwlib/src/hal_nfc.c new file mode 100644 index 0000000..bafa43e --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_nfc.c @@ -0,0 +1,20 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "hal_nfc.h" + +VOID HalNFCOpInit( + IN VOID *Data +) +{ + +} + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_pcm.c b/component/soc/realtek/8195a/fwlib/src/hal_pcm.c new file mode 100644 index 0000000..5ccf96a --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_pcm.c @@ -0,0 +1,28 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "hal_pcm.h" + +VOID HalPcmOpInit( + IN VOID *Data +) +{ + PHAL_PCM_OP pHalPcmOp = (PHAL_PCM_OP) Data; + + pHalPcmOp->HalPcmOnOff = HalPcmOnOffRtl8195a; + pHalPcmOp->HalPcmInit = HalPcmInitRtl8195a; + pHalPcmOp->HalPcmSetting = HalPcmSettingRtl8195a; + pHalPcmOp->HalPcmEn = HalPcmEnRtl8195a; + pHalPcmOp->HalPcmIsrEnAndDis= HalPcmIsrEnAndDisRtl8195a; + pHalPcmOp->HalPcmDumpReg= HalPcmDumpRegRtl8195a; + pHalPcmOp->HalPcm= HalPcmRtl8195a; +} + + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_pwm.c b/component/soc/realtek/8195a/fwlib/src/hal_pwm.c new file mode 100644 index 0000000..0e8432d --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_pwm.c @@ -0,0 +1,131 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" + +#ifdef CONFIG_PWM_EN +#include "hal_pwm.h" +#include "hal_timer.h" + +HAL_PWM_ADAPTER PWMPin[MAX_PWM_CTRL_PIN]; +const u8 PWMTimerIdx[MAX_PWM_CTRL_PIN]= {3,4,5,6}; // the G-timer ID used for PWM pin 0~3 + +/** + * @brief Initializes and enable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * @param sel: pin mux selection + * + * @retval HAL_Status + */ +HAL_Status +HAL_Pwm_Init( + u32 pwm_id, + u32 sel +) +{ + HAL_PWM_ADAPTER *pPwmAdapt; + u32 timer_id; + + DBG_PWM_INFO("%s: Init PWM for PWM %d, Sel %d\n", __FUNCTION__, pwm_id, sel); + + if ((pwm_id >= MAX_PWM_CTRL_PIN) || (sel > 3)) { + DBG_PWM_ERR ("HAL_Pwm_Init: Invalid PWM index(%d), sel(%d)\n", pwm_id, sel); + return HAL_ERR_PARA; + } + + pPwmAdapt = &PWMPin[pwm_id]; + pPwmAdapt->pwm_id = pwm_id; + pPwmAdapt->sel = sel; + timer_id = PWMTimerIdx[pwm_id]; + pPwmAdapt->gtimer_id = timer_id; + + return HAL_Pwm_Init_8195a (pPwmAdapt); +} + + +/** + * @brief Disable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * + * @retval None + */ +void +HAL_Pwm_Enable( + u32 pwm_id +) +{ + HAL_PWM_ADAPTER *pPwmAdapt; + + if (pwm_id >= MAX_PWM_CTRL_PIN) { + DBG_PWM_ERR ("HAL_Pwm_Enable: Invalid PWM index(%d)\n", pwm_id); + return; + } + pPwmAdapt = &PWMPin[pwm_id]; + + HAL_Pwm_Enable_8195a(pPwmAdapt); +} + + +/** + * @brief Disable a PWM control pin. + * + * @param pwm_id: the PWM pin index + * + * @retval None + */ +void +HAL_Pwm_Disable( + u32 pwm_id +) +{ + HAL_PWM_ADAPTER *pPwmAdapt; + + if (pwm_id >= MAX_PWM_CTRL_PIN) { + DBG_PWM_ERR ("HAL_Pwm_Disable: Invalid PWM index(%d)\n", pwm_id); + return; + } + pPwmAdapt = &PWMPin[pwm_id]; + + HAL_Pwm_Disable_8195a(pPwmAdapt); +} + +/** + * @brief Set the duty ratio of the PWM pin. + * + * @param pwm_id: the PWM pin index + * @param period: the period time, in micro-second. + * @param pulse_width: the pulse width time, in micro-second. + * + * @retval None + */ +void +HAL_Pwm_SetDuty( + u32 pwm_id, + u32 period, + u32 pulse_width +) +{ + HAL_PWM_ADAPTER *pPwmAdapt; + + if (pwm_id >= MAX_PWM_CTRL_PIN) { + DBG_PWM_ERR ("HAL_Pwm_SetDuty: Invalid PWM index(%d)\n", pwm_id); + return; + } + +// DBG_PWM_INFO("%s: Period%d Pulse%d\n", __FUNCTION__, period, pulse_width); + pPwmAdapt = &PWMPin[pwm_id]; + + HAL_Pwm_SetDuty_8195a(pPwmAdapt, period, pulse_width); +} + + +#endif // end of "#ifdef CONFIG_PWM_EN" diff --git a/component/soc/realtek/8195a/fwlib/src/hal_sdr_controller.c b/component/soc/realtek/8195a/fwlib/src/hal_sdr_controller.c new file mode 100644 index 0000000..ba52e49 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_sdr_controller.c @@ -0,0 +1,962 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#include "rtl8195a.h" +#include "hal_sdr_controller.h" +#include "rtl8195a_sdr.h" +#if 0 +#define HAL_SDR_WRITE32(addr, value32) HAL_WRITE32(SDR_CTRL_BASE, addr, value32) +#define HAL_SDR_WRITE16(addr, value16) HAL_WRITE16(SDR_CTRL_BASE, addr, value16) +#define HAL_SDR_WRITE8(addr, value8) HAL_WRITE8(SDR_CTRL_BASE, addr, value8) +#define HAL_SDR_READ32(addr) HAL_READ32(SDR_CTRL_BASE, addr) +#define HAL_SDR_READ16(addr) HAL_READ16(SDR_CTRL_BASE, addr) +#define HAL_SDR_READ8(addr) HAL_READ8(SDR_CTRL_BASE, addr) + +#define HAL_SDRAM_WRITE32(addr, value32) HAL_WRITE32(SDR_SDRAM_BASE, addr, value32) +#define HAL_SDRAM_WRITE16(addr, value16) HAL_WRITE16(SDR_SDRAM_BASE, addr, value16) +#define HAL_SDRAM_WRITE8(addr, value8) HAL_WRITE8(SDR_SDRAM_BASE, addr, value8) +#define HAL_SDRAM_READ32(addr) HAL_READ32(SDR_SDRAM_BASE, addr) +#define HAL_SDRAM_READ16(addr) HAL_READ16(SDR_SDRAM_BASE, addr) +#define HAL_SDRAM_READ8(addr) HAL_READ8(SDR_SDRAM_BASE, addr) +#endif + + + +HAL_CUT_B_RAM_DATA_SECTION +DRAM_INFO SdrDramDev = { + DRAM_INFO_TYPE, + DRAM_INFO_COL_ADDR_WTH, + DRAM_INFO_BANK_SZ, + DRAM_INFO_DQ_WTH +}; + + +HAL_CUT_B_RAM_DATA_SECTION +DRAM_MODE_REG_INFO SdrDramModeReg = { + BST_LEN_4, + SENQUENTIAL, + 0x3, // Mode0Cas: 3 + 0x0, // Mode0Wr + 0, // Mode1DllEnN + 0, // Mode1AllLat + 0 // Mode2Cwl +}; + +HAL_CUT_B_RAM_DATA_SECTION +DRAM_TIMING_INFO SdrDramTiming = { + DRAM_TIMING_TRFC, // TrfcPs; + DRAM_TIMING_TREFI, // TrefiPs; + DRAM_TIMING_TWRMAXTCK, // WrMaxTck; + DRAM_TIMING_TRCD, // TrcdPs; + DRAM_TIMING_TRP, // TrpPs; + DRAM_TIMING_TRAS, // TrasPs; + DRAM_TIMING_TRRD, // TrrdTck; + DRAM_TIMING_TWR, // TwrPs; + DRAM_TIMING_TWTR, // TwtrTck; + //13090, // TrtpPs; + DRAM_TIMING_TMRD, // TmrdTck; + DRAM_TIMING_TRTP, // TrtpTck; + DRAM_TIMING_TCCD, // TccdTck; + DRAM_TIMING_TRC // TrcPs; +}; + +HAL_CUT_B_RAM_DATA_SECTION +DRAM_DEVICE_INFO SdrDramInfo = { + &SdrDramDev, + &SdrDramModeReg, + &SdrDramTiming, + DRAM_TIMING_TCK, + DFI_RATIO_1 +}; + + +#define FPGA +#define FPGA_TEMP +#define SDR_CLK_DLY_CTRL 0x40000300 +#define MIN_RD_PIPE 0x0 +#define MAX_RD_PIPE 0x7 + +#define SUPPORT_DRAM_KED + + +#ifdef FPGA +#ifdef FPGA_TEMP +#define MAX_TAP_DLY 0xC +#else +#define MAX_TAP_DLY 0x7F +#define SPEC_MAX_TAP 0xFF +#endif +#else +#define MAX_TAP_DLY 99 // 0~99 +#define SPEC_MAX_TAP 99 +#define WINDOW_COMBIN // combine window [0~a] and [b~99] (for asic mode) +#endif + +#define TAP_DLY 0x1 +#define REC_NUM 512 + + +u32 SdrControllerInit(VOID); +VOID DramInit(DRAM_DEVICE_INFO *); +s32 MemTest(u32 loop_cnt); +u32 SdrCalibration(VOID); +u32 Sdr_Rand2(VOID); + +//3 Note: stack overfloat if the arrary is declared in the task +HAL_CUT_B_RAM_DATA_SECTION +u32 AvaWds[2][REC_NUM]; + +HAL_CUT_B_RAM_DATA_SECTION +unsigned int rand_x = 123456789; + +#ifdef CONFIG_SDR_EN + +#ifdef CONFIG_SDR_VERIFY +enum{ + LLT, + TXRPT, + RXBUFF, + TXBUFF, +}; +#define REPORT_OFFSET 0x8000 +#define RAMASK_OFFSET 0x8800 +#define LLT_H_ADDR 0x650 +#define TXREPORT_H_ADDR 0x660 +#define RXBUFF_H_ADDR 0x670 +#define TXBUFF_H_ADDR 0x680 + +#define REG_PKTBUF_DBG_CTRL_8723B 0x0140 + +int +rt_rpt_h_addr(u8 rpt) +{ + u32 r_val, offset; + + if (rpt == LLT){ + offset = LLT_H_ADDR; + } + else if (rpt == TXRPT){ + offset = TXREPORT_H_ADDR; + } + else if (rpt == RXBUFF){ + offset = RXBUFF_H_ADDR; + } + else if (rpt == TXBUFF){ + offset = TXBUFF_H_ADDR; + } + else { + } + + r_val = ((HAL_READ32(WIFI_REG_BASE, REG_PKTBUF_DBG_CTRL_8723B)&0xFFFFF000)|offset); + HAL_WRITE32(WIFI_REG_BASE, REG_PKTBUF_DBG_CTRL_8723B, r_val); +} + + + +int +rt_txrpt_read32(u8 macid, u8 offset) +{ + u32 r_val; + + rt_rpt_h_addr(TXRPT); + r_val = HAL_READ32(WIFI_REG_BASE, (REPORT_OFFSET + macid*4 + offset)); + + return r_val; +} + +int +rt_txrpt_read16(u8 macid, u8 offset) +{ + u16 r_val; + + rt_rpt_h_addr(TXRPT); + r_val = HAL_READ16(WIFI_REG_BASE, (REPORT_OFFSET + macid*8 + offset)); + + return r_val; +} + +int +rt_txrpt_read8(u8 macid, u8 offset) +{ + u8 r_val; + + rt_rpt_h_addr(TXRPT); + r_val = HAL_READ8(WIFI_REG_BASE, (REPORT_OFFSET + macid*16 + offset)); + + return r_val; +} + +int +rt_txrpt_read_1b(u8 macid, u8 offset, u8 bit_offset) +{ + u8 r_val = ((rt_txrpt_read8(macid, offset) & BIT(bit_offset))?1:0); + + return r_val; +} + + +int +rt_txrpt_write32(u8 macid, u8 offset, u32 val) +{ + rt_rpt_h_addr(TXRPT); + HAL_WRITE32(WIFI_REG_BASE, (REPORT_OFFSET + macid*4 + offset), val); +} + +int +rt_txrpt_write16(u8 macid, u8 offset, u16 val) +{ + rt_rpt_h_addr(TXRPT); + HAL_WRITE16(WIFI_REG_BASE, (REPORT_OFFSET + macid*8 + offset), val); +} + +int +rt_txrpt_write8(u8 macid, u8 offset, u8 val) +{ + rt_rpt_h_addr(TXRPT); + DBG_8195A("Write addr %x %x\n", (REPORT_OFFSET + macid*16 + offset), val); + HAL_WRITE8(WIFI_REG_BASE, (REPORT_OFFSET + macid*16 + offset), val); +} + +int +rt_txrpt_write_1b(u8 macid, u8 offset, u8 bit_offset, u8 val) +{ + u8 r_val = rt_txrpt_read8(macid, offset); + + if (val){ + r_val |= BIT(bit_offset); + } + else { + r_val &= (~BIT(bit_offset)); + } + + HAL_WRITE8(WIFI_REG_BASE, (REPORT_OFFSET + macid*16 + offset), r_val); +} + + +u8 +ReadTxrptsdr8( + IN u8 Macid, + IN u8 Offset) +{ + u8 r_val; + + r_val = rt_txrpt_read8(Macid, Offset); + return r_val; +} + +VOID +WriteTxrptsdr8( + IN u8 Macid, + IN u8 Offset, + IN u8 Val) +{ + rt_txrpt_write8(Macid, Offset, Val); +} + +VOID +SdrTestApp( + IN VOID *Data +) +{ + u32 *Cmd =(u32*)Data; + u32 Loop, LoopIndex, Value32, Addr, Loop1, LoopIndex1; + + switch (Cmd[0]) { + case 1: + DBG_8195A("Initial SDR\n"); + + //1 "SdrControllerInit" is located in Image1, so we shouldn't call it in Image2 + if (!SdrControllerInit()) { + DBG_8195A("SDR Calibartion Fail!!!!\n"); + } + break; + case 2: + Loop = Cmd[1]; + Loop1 = Cmd[2]; + DBG_8195A("Verify SDR: Loop = 0x%08x Loop1 = 0x%08x\n",Loop, Loop1); + + for (LoopIndex1=0; LoopIndex1 < Loop1; LoopIndex1++) { + + for (LoopIndex=0; LoopIndex < Loop; LoopIndex++) { + Value32 = Rand2(); + Addr = Rand2(); + Addr &= 0x1FFFFF; + Addr &= (~0x3); + + if (!(LoopIndex & 0xFFFFF)) { + DBG_8195A("Alive: LOOP = 0x%08x, LOOP = 0x%08x\n",LoopIndex1, LoopIndex); + } + + // DBG_8195A("Value: 0x%x; Addr: 0x%x\n", Value32, Addr+SDR_SDRAM_BASE); + HAL_SDRAM_WRITE32(Addr, Value32); + + if (Value32 != HAL_SDRAM_READ32(Addr)) { + DBG_8195A("Loop:%d; Addr: 0x%08x => CheckData error: W: 0x%08x /R:0x%x\n" + ,LoopIndex + ,Addr + ,Value32 + ,HAL_SDRAM_READ32(Addr)); + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSTBY_INFO2, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSTBY_INFO2)+1); + break; + } + } + } + DBG_8195A("Verify SDR Success\n"); + break; + + case 3: + DBG_8195A("WL read RPT MACID %x\n", Cmd[1]); + { + u8 i =0; + for(i=0;i<16;i++) { + DBG_8195A("WL RPT offset %d = %x\n", i, ReadTxrptsdr8(Cmd[1],i)); + } + } + break; + case 4: + DBG_8195A("WL write RPT MACID %x\n", Cmd[1]); + { + u8 i =0; + for(i=0;i<16;i++) { + WriteTxrptsdr8(Cmd[1],i,Cmd[2]); + //DBG_8195A("WL RPT offset %d = %x\n", i, ReadTxrptsdr8(Cmd[1],i)); + } + } + break; + default: + break; + } + +} + +#endif + +HAL_SDRC_TEXT_SECTION +u32 +SdrControllerInit( +VOID +) +{ + DBG_8195A("SDR Controller Init\n"); + + SRAM_MUX_CFG(0x2); + + SDR_CLK_SEL(SDR_CLOCK_SEL_VALUE); + + HAL_PERI_ON_WRITE32(REG_GPIO_PULL_CTRL4,0); + + ACTCK_SDR_CCTRL(ON); + + SLPCK_SDR_CCTRL(ON); + + PinCtrl(SDR, 0, ON); + + HAL_PERI_ON_WRITE32(REG_GPIO_PULL_CTRL4,0); + + //MEM_CTRL_FCTRL(OFF); + + MEM_CTRL_FCTRL(ON); + + LDO25M_CTRL(ON); + + HalDelayUs(3000); + + // sdr initialization + DramInit(&SdrDramInfo); + + // sdr calibration + if(!SdrCalibration()) { + return 0; + } + else { + return 1; + } +} + + +HAL_SDRC_TEXT_SECTION +VOID +DramInit ( + IN DRAM_DEVICE_INFO *DramInfo +) +{ + u32 CsBstLen = 0; // 0:bst_4, 1:bst_8 + u32 CasWr = 0;//, CasWrT; // cas write latency + u32 CasRd = 0, CasRdT = 0, CrlSrt = 0; // cas read latency + u32 AddLat; + u32 DramEmr2 = 0, DramMr0 = 0; + u32 CrTwr, DramMaxWr, DramWr; + u32 CrTrtw = 0, CrTrtwT = 0; + u32 DrmaPeriod; + DRAM_TYPE DdrType; + DRAM_DQ_WIDTH DqWidth; + DRAM_COLADDR_WTH Page; + u32 DfiRate; + volatile struct ms_rxi310_portmap *ms_ctrl_0_map; + ms_ctrl_0_map = (struct ms_rxi310_portmap*) SDR_CTRL_BASE; + ms_ctrl_0_map = ms_ctrl_0_map; + + DfiRate = 1 << (u32) (DramInfo->DfiRate); + DrmaPeriod = (DramInfo->DdrPeriodPs)*(DfiRate); // according DFI_RATE to setting + + // In PHY, write latency == 3 + DramMaxWr= (DramInfo->Timing->WrMaxTck)/(DfiRate) +1; + DramWr = ((DramInfo->Timing->TwrPs) / DrmaPeriod)+1; + CrTwr = ((DramInfo->Timing->TwrPs) / DrmaPeriod) + 3; + + if (CrTwr < DramMaxWr) { + CrTwr = CrTwr; + } + else { + CrTwr = DramMaxWr; + } + + if ((DramInfo->Dev->DeviceType) == DRAM_DDR_2) { + DdrType = DRAM_DDR_2; + if (DramInfo->ModeReg->BstLen == BST_LEN_4) { + CsBstLen = 0; //bst_4 + CrTrtwT = 2+2; //4/2+2 + DramMr0 = 0x2; + } + else { // BST_LEN_8 + CsBstLen = 1; // bst_8 + CrTrtwT = 4+2; // 8/2+2 + DramMr0 = 0x3; + } + CasRd = DramInfo->ModeReg->Mode0Cas; + AddLat = DramInfo->ModeReg ->Mode1AllLat; + CasWr = CasRd + AddLat -1; + DramEmr2 = 0; + + DramMr0 =(((DramWr%6)-1) << (PCTL_MR_OP_BFO+1)) | // write_recovery + (0 << PCTL_MR_OP_BFO ) | // dll + (DramInfo->ModeReg->Mode0Cas << PCTL_MR_CAS_BFO ) | + (DramInfo->ModeReg->BstType << PCTL_MR_BT_BFO ) | + DramMr0; + } + else if ((DramInfo->Dev->DeviceType) == DRAM_DDR_3) { + DdrType = DRAM_DDR_3; + if (DramInfo->ModeReg->BstLen == BST_LEN_4) { + CsBstLen = 0; //bst_4 + DramMr0 = 0x2; + } + else { // BST_LEN_8 + CsBstLen = 1; // bst_8 + DramMr0 = 0x0; + } + + CrlSrt = (DramInfo->ModeReg->Mode0Cas >> 1); + if (((DramInfo->ModeReg->Mode0Cas) & 0x1) ) { + CasRdT = CrlSrt+ 12; + } + else { + CasRdT = CrlSrt+ 4; + } + + if (DramInfo->ModeReg->Mode1AllLat == 1) { // CL-1 + AddLat = CasRd -1; + } + else if (DramInfo->ModeReg->Mode1AllLat == 2){ // CL-2 + AddLat = CasRd -2; + } + else { + AddLat = 0; + } + + CasRd = CasRdT + AddLat; + + CasWr = DramInfo->ModeReg->Mode2Cwl + 5 + AddLat; + + DramEmr2 = DramInfo->ModeReg->Mode2Cwl << 3; + + if (DramWr == 16) { + DramWr = 0; + } + else if (DramWr <= 9) { // 5< wr <= 9 + DramWr = DramWr - 4; + } + else { + DramWr = (DramWr + 1) / 2; + } + + DramMr0 =(DramWr << (PCTL_MR_OP_BFO+1) ) | // write_recovery + (0 << PCTL_MR_OP_BFO ) | // dll + ((DramInfo->ModeReg->Mode0Cas >>1 ) << PCTL_MR_CAS_BFO ) | + (DramInfo->ModeReg->BstType << PCTL_MR_BT_BFO ) | + ((DramInfo->ModeReg->Mode0Cas & 0x1) << 2 ) | + DramMr0; + + CrTrtwT = (CasRdT + 6) - CasWr; + + } // ddr2/ddr3 + else if ((DramInfo->Dev->DeviceType) == DRAM_SDR) { + DdrType = DRAM_SDR; + if (DramInfo->ModeReg->BstLen == BST_LEN_4) { + DramMr0 = 2; // bst_4 + CsBstLen = 0; //bst_4 + CasRd = 0x2; + } + else { // BST_LEN_8 + DramMr0 = 3; // bst_8 + CsBstLen = 1; // bst_8 + CasRd = 0x3; + } + + CasWr = 0; + + DramMr0 =(CasRd << PCTL_MR_CAS_BFO) | + (DramInfo->ModeReg->BstType << PCTL_MR_BT_BFO ) | + DramMr0; + + CrTrtwT = 0; // tic: CasRd + rd_rtw + rd_pipe + } // SDR + + + // countting tRTW + if ((CrTrtwT & 0x1)) { + CrTrtw = (CrTrtwT+1) /(DfiRate); + } + else { + CrTrtw = CrTrtwT /(DfiRate); + } + + DqWidth = (DramInfo->Dev->DqWidth); + Page = DramInfo->Dev->ColAddrWth +1; // DQ16 -> memory:byte_unit *2 + if (DqWidth == DRAM_DQ_32) { // paralle dq_16 => Page + 1 + Page = Page +1; + } +#if 1 + + // WRAP_MISC setting + HAL_SDR_WRITE32(REG_SDR_MISC,( + (Page << WRAP_MISC_PAGE_SIZE_BFO) | + (DramInfo->Dev->Bank << WRAP_MISC_BANK_SIZE_BFO) | + (CsBstLen << WRAP_MISC_BST_SIZE_BFO ) | + (DqWidth << WRAP_MISC_DDR_PARAL_BFO) + )); + // PCTL setting + HAL_SDR_WRITE32(REG_SDR_DCR,( + (0x2 << PCTL_DCR_DFI_RATE_BFO) | + (DqWidth << PCTL_DCR_DQ32_BFO ) | + (DdrType << PCTL_DCR_DDR3_BFO ) + )); + + HAL_SDR_WRITE32(REG_SDR_IOCR,( + ((CasRd -4)/(DfiRate) << PCTL_IOCR_TPHY_RD_EN_BFO ) | + (0 << PCTL_IOCR_TPHY_WL_BFO ) | + (((CasWr -3)/(DfiRate)) << PCTL_IOCR_TPHY_WD_BFO ) | + (0 << PCTL_IOCR_RD_PIPE_BFO ) + )); + + if ((DramInfo->Dev->DeviceType) != SDR) { // DDR2/3 + HAL_SDR_WRITE32(REG_SDR_EMR2,DramEmr2); + HAL_SDR_WRITE32(REG_SDR_EMR1,( + (1 << 2 ) | //RTT + (1 << 1 ) | //D.I.C + (DramInfo->ModeReg->Mode1DllEnN ) + )); + } // DDR2/3 + + HAL_SDR_WRITE32(REG_SDR_MR,DramMr0); + + HAL_SDR_WRITE32(REG_SDR_DRR, ( + (0 << PCTL_DRR_REF_DIS_BFO) | + (9 << PCTL_DRR_REF_NUM_BFO) | + ((((DramInfo->Timing->TrefiPs)/DrmaPeriod)+1) << PCTL_DRR_TREF_BFO ) | + ((((DramInfo->Timing->TrfcPs)/DrmaPeriod)+1) << PCTL_DRR_TRFC_BFO ) + )); + + HAL_SDR_WRITE32(REG_SDR_TPR0,( + ((((DramInfo->Timing->TrtpTck)/DfiRate)+1) << PCTL_TPR0_TRTP_BFO) | + (CrTwr << PCTL_TPR0_TWR_BFO ) | + ((((DramInfo->Timing->TrasPs)/DrmaPeriod)+1) << PCTL_TPR0_TRAS_BFO) | + ((((DramInfo->Timing->TrpPs)/DrmaPeriod)+1) << PCTL_TPR0_TRP_BFO ) + )); + + HAL_SDR_WRITE32(REG_SDR_TPR1, ( + (CrTrtw << PCTL_TPR1_TRTW_BFO) | + ((((DramInfo->Timing->TwtrTck)/DfiRate)+3) << PCTL_TPR1_TWTR_BFO) | + ((((DramInfo->Timing->TccdTck)/DfiRate)+1) << PCTL_TPR1_TCCD_BFO) | + ((((DramInfo->Timing->TrcdPs)/DrmaPeriod)+1) << PCTL_TPR1_TRCD_BFO) | + ((((DramInfo->Timing->TrcPs)/DrmaPeriod)+1) << PCTL_TPR1_TRC_BFO ) | + (((DramInfo->Timing->TrrdTck/DfiRate)+1) << PCTL_TPR1_TRRD_BFO) + )); + + HAL_SDR_WRITE32(REG_SDR_TPR2, ( + (DramInfo->Timing->TmrdTck << PCTL_TPR2_TMRD_BFO ) | + (0 << PCTL_TPR2_INIT_NS_EN_BFO ) | + (2 << PCTL_TPR2_INIT_REF_NUM_BFO) + )); + + // set all_mode _idle + HAL_SDR_WRITE32(REG_SDR_CSR,0x700); + + // start to init + HAL_SDR_WRITE32(REG_SDR_CCR,0x01); + while ((HAL_SDR_READ32(REG_SDR_CCR)& 0x1) == 0x0); + + // enter mem_mode + HAL_SDR_WRITE32(REG_SDR_CSR,0x600); +#else + // WRAP_MISC setting + ms_ctrl_0_map->misc = //0x12; + ( + (Page << WRAP_MISC_PAGE_SIZE_BFO) | + (DramInfo->Dev->Bank << WRAP_MISC_BANK_SIZE_BFO) | + (CsBstLen << WRAP_MISC_BST_SIZE_BFO ) | + (DqWidth << WRAP_MISC_DDR_PARAL_BFO) + ); + // PCTL setting + ms_ctrl_0_map->dcr = //0x208; + ( + (0x2 << PCTL_DCR_DFI_RATE_BFO) | + (DqWidth << PCTL_DCR_DQ32_BFO ) | + (DdrType << PCTL_DCR_DDR3_BFO ) + ); + + ms_ctrl_0_map->iocr = ( + ((CasRd -4)/(DfiRate) << PCTL_IOCR_TPHY_RD_EN_BFO ) | + (0 << PCTL_IOCR_TPHY_WL_BFO ) | + (((CasWr -3)/(DfiRate)) << PCTL_IOCR_TPHY_WD_BFO ) | + (0 << PCTL_IOCR_RD_PIPE_BFO ) + ); + + if ((DramInfo->Dev->DeviceType) != SDR) { // DDR2/3 + ms_ctrl_0_map->emr2 = DramEmr2; + + ms_ctrl_0_map->emr1 = ( + (1 << 2 ) | //RTT + (1 << 1 ) | //D.I.C + (DramInfo->ModeReg->Mode1DllEnN ) + ); + } // DDR2/3 + + ms_ctrl_0_map->mr = DramMr0; + + ms_ctrl_0_map->drr = ( + (0 << PCTL_DRR_REF_DIS_BFO) | + (9 << PCTL_DRR_REF_NUM_BFO) | + ((((DramInfo->Timing->TrefiPs)/DrmaPeriod)+1)<< PCTL_DRR_TREF_BFO ) | + ((((DramInfo->Timing->TrfcPs)/DrmaPeriod)+1) << PCTL_DRR_TRFC_BFO ) + ); + + ms_ctrl_0_map->tpr0= ( + ((((DramInfo->Timing->TrtpTck)/DfiRate)+1) << PCTL_TPR0_TRTP_BFO) | + (CrTwr << PCTL_TPR0_TWR_BFO ) | + ((((DramInfo->Timing->TrasPs)/DrmaPeriod)+1) << PCTL_TPR0_TRAS_BFO) | + ((((DramInfo->Timing->TrpPs)/DrmaPeriod)+1) << PCTL_TPR0_TRP_BFO ) + ); + + ms_ctrl_0_map->tpr1= ( + (CrTrtw << PCTL_TPR1_TRTW_BFO) | + ((((DramInfo->Timing->TwtrTck)/DfiRate)+3) << PCTL_TPR1_TWTR_BFO) | + ((((DramInfo->Timing->TccdTck)/DfiRate)+1) << PCTL_TPR1_TCCD_BFO) | + ((((DramInfo->Timing->TrcdPs)/DrmaPeriod)+1) << PCTL_TPR1_TRCD_BFO) | + ((((DramInfo->Timing->TrcPs)/DrmaPeriod)+1) << PCTL_TPR1_TRC_BFO ) | + (((DramInfo->Timing->TrrdTck/DfiRate)+1) << PCTL_TPR1_TRRD_BFO) + ); + + ms_ctrl_0_map->tpr2= ( + (DramInfo->Timing->TmrdTck << PCTL_TPR2_TMRD_BFO ) | + (0 << PCTL_TPR2_INIT_NS_EN_BFO ) | + (2 << PCTL_TPR2_INIT_REF_NUM_BFO) + ); + // set all_mode _idle + ms_ctrl_0_map->csr = 0x700; + + // start to init + ms_ctrl_0_map->ccr = 0x1; + while (((ms_ctrl_0_map->ccr)& 0x1) == 0x0); + + // enter mem_mode + ms_ctrl_0_map->csr= 0x600; +#endif +} // DramInit + + //3 +extern void * +_memset( void *s, int c, SIZE_T n ); + +HAL_SDRC_TEXT_SECTION +u32 +SdrCalibration( + VOID +) +{ +#ifdef FPGA +#ifdef FPGA_TEMP +// u32 Value32; +#endif +#else +// u32 Value32; +#endif + u32 RdPipe = 0, TapCnt = 0, Pass = 0, AvaWdsCnt = 0; + u32 RdPipeCounter, RecNum[2], RecRdPipe[2];//, AvaWds[2][REC_NUM]; + BOOL RdPipeFlag, PassFlag = 0, Result; + + Result = _FALSE; + +#ifdef SUPPORT_DRAM_KED + // read calibration data from system data 0x5d~0x6c + SPIC_INIT_PARA SpicInitPara; + u32 valid; + union { u8 b[4]; u32 l;} value; + u32 CpuType = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_CLK_CTRL1) & (0x70)) >> 4); + + valid = RdPipe = TapCnt = 0xFFFFFFFF; + value.l = HAL_READ32(SPI_FLASH_BASE, FLASH_SDRC_PARA_BASE+8*CpuType); + if((value.b[0]^value.b[1])==0xFF) + valid = value.b[0]; + //DiagPrintf("dump1 %x, %x %x %x %x \n\r", value.l, value.b[0], value.b[1], value.b[2], value.b[3]); + value.l = HAL_READ32(SPI_FLASH_BASE, FLASH_SDRC_PARA_BASE+8*CpuType+4); + if((value.b[0]^value.b[1])==0xFF) + RdPipe = value.b[0]; + if((value.b[2]^value.b[3])==0xFF) + TapCnt = value.b[2]; + //DiagPrintf("dump2 %x, %x %x %x %x \n\r", value.l, value.b[0], value.b[1], value.b[2], value.b[3]); + + if((valid==1)&&(RdPipe!=0xFFFFFFFF)&&(TapCnt!=0xFFFFFFFF)){ + // wait DRAM settle down + HalDelayUs(10); + // load previous dram Ked data + HAL_SDR_WRITE32(REG_SDR_IOCR, ((HAL_SDR_READ32(REG_SDR_IOCR) & 0xff) | (RdPipe << PCTL_IOCR_RD_PIPE_BFO))); + SDR_DDL_FCTRL(TapCnt); + if(MemTest(3)) + return _TRUE; + } +#endif + + _memset((u8*)AvaWds, 0, sizeof(u32)*REC_NUM*2); + + volatile struct ms_rxi310_portmap *ms_ctrl_0_map; + ms_ctrl_0_map = (struct ms_rxi310_portmap*) SDR_CTRL_BASE; + ms_ctrl_0_map = ms_ctrl_0_map; + PassFlag = PassFlag; + RdPipeCounter =0; + +// DBG_8195A("%d\n",__LINE__); + + for(RdPipe=MIN_RD_PIPE; RdPipe<=MAX_RD_PIPE; RdPipe++) { +// ms_ctrl_0_map->iocr = (ms_ctrl_0_map->iocr & 0xff) | (RdPipe << PCTL_IOCR_RD_PIPE_BFO); + HAL_SDR_WRITE32(REG_SDR_IOCR, ((HAL_SDR_READ32(REG_SDR_IOCR) & 0xff) | (RdPipe << PCTL_IOCR_RD_PIPE_BFO))); + + DBG_SDR_INFO("IOCR: 0x%x; Write: 0x%x\n",HAL_SDR_READ32(REG_SDR_IOCR), (RdPipe << PCTL_IOCR_RD_PIPE_BFO)); +// DBG_8195A("IOCR: 0x%x; Write: 0x%x\n",ms_ctrl_0_map->iocr, (RdPipe << PCTL_IOCR_RD_PIPE_BFO)); + + RdPipeFlag = _FALSE; + PassFlag = _FALSE; + AvaWdsCnt = 0; + + for(TapCnt=0; TapCnt < (MAX_TAP_DLY+1); TapCnt++) { + // Modify clk delay +#ifdef FPGA +#ifdef FPGA_TEMP + SDR_DDL_FCTRL(TapCnt); +// Value32 = (RD_DATA(SDR_CLK_DLY_CTRL) & 0xFF00FFFF); +// Value32 = (Value32 | (TapCnt << 16)); +// WR_DATA(SDR_CLK_DLY_CTRL, Value32); +#else + HAL_SDR_WRITE32(REG_SDR_DLY0, TapCnt); +// ms_ctrl_0_map->phy_dly0 = TapCnt; +#endif + DBG_SDR_INFO("DLY: 0x%x; Write: 0x%x\n",HAL_PERI_ON_READ32(REG_PESOC_MEM_CTRL), TapCnt); +#else + SDR_DDL_FCTRL(TapCnt); +// Value32 = (RD_DATA(SDR_CLK_DLY_CTRL) & 0xFF00FFFF); +// Value32 = (Value32 | (TapCnt << 16)); +// WR_DATA(SDR_CLK_DLY_CTRL, Value32); +#endif + + Pass = MemTest(10000); + PassFlag = _FALSE; + + if(Pass==_TRUE) { // PASS + + if (!RdPipeFlag) { + DBG_SDR_INFO("%d Time Pass\n", RdPipeCounter); + RdPipeCounter++; + RdPipeFlag = _TRUE; + RecRdPipe[RdPipeCounter - 1] = RdPipe; + } + + AvaWds[RdPipeCounter-1][AvaWdsCnt] = TapCnt; + AvaWdsCnt++; + + RecNum[RdPipeCounter-1] = AvaWdsCnt; + + if((TapCnt+TAP_DLY)>=MAX_TAP_DLY) { + break; + } + + PassFlag = _TRUE; + + DBG_SDR_INFO("Verify Pass => RdPipe:%d; TapCnt: %d\n", RdPipe, TapCnt); + + } + else { // FAIL +// if(PassFlag==_TRUE) { +// break; +// } +// else { + if (RdPipeCounter > 0) { + RdPipeCounter++; + if (RdPipeCounter < 3) { + RecNum[RdPipeCounter-1] = 0; + RecRdPipe[RdPipeCounter - 1] = RdPipe; + } + break; + } +// } + } + } + + + if (RdPipeCounter > 2) { + u8 BestRangeIndex, BestIndex; + u32 i; + + #ifdef CONFIG_SDR_VERIFY //to reduce log + DBG_SDR_INFO("Avaliable RdPipe 0\n"); + + for (i=0;i<256;i++) { + DBG_SDR_INFO("%d\n", AvaWds[0][i]); + } + DBG_SDR_INFO("Avaliable RdPipe 1\n"); + for (i=0;i<256;i++) { + DBG_SDR_INFO("%d\n", AvaWds[1][i]); + } + #endif + + DBG_SDR_INFO("Rec 0 => total counter %d; RdPipe:%d;\n", RecNum[0], RecRdPipe[0]); + DBG_SDR_INFO("Rec 1 => total counter %d; RdPipe:%d;\n", RecNum[1], RecRdPipe[1]); + + BestRangeIndex = (RecNum[0] > RecNum[1]) ? 0 : 1; + + BestIndex = RecNum[BestRangeIndex]>>1; + + DBG_SDR_INFO("The Finial RdPipe: %d; TpCnt: 0x%x\n", RecRdPipe[BestRangeIndex], AvaWds[BestRangeIndex][BestIndex]); + + // set RdPipe and tap_dly +// ms_ctrl_0_map->iocr = (ms_ctrl_0_map->iocr & 0xff) | (RecRdPipe[BestRangeIndex] << PCTL_IOCR_RD_PIPE_BFO); + HAL_SDR_WRITE32(REG_SDR_IOCR, ((HAL_SDR_READ32(REG_SDR_IOCR) & 0xff) | (RecRdPipe[BestRangeIndex] << PCTL_IOCR_RD_PIPE_BFO))); + +#ifdef FPGA +#ifdef FPGA_TEMP + SDR_DDL_FCTRL(AvaWds[BestRangeIndex][BestIndex]); + +// Value32 = (RD_DATA(SDR_CLK_DLY_CTRL) & 0xFF00FFFF); +// Value32 = Value32 | (AvaWds[BestRangeIndex][BestIndex] << 16); +// WR_DATA(SDR_CLK_DLY_CTRL, Value32); +#else + HAL_SDR_WRITE32(REG_SDR_DLY0, AvaWds[BestRangeIndex][BestIndex]); +// ms_ctrl_0_map->phy_dly0 = AvaWds[BestRangeIndex][BestIndex]; +#endif +#else + SDR_DDL_FCTRL(AvaWds[BestRangeIndex][BestIndex]); +// Value32 = (RD_DATA(SDR_CLK_DLY_CTRL) & 0xFF00FFFF); +// Value32 = Value32 | (AvaWds[BestRangeIndex][BestIndex] << 16); +// WR_DATA(SDR_CLK_DLY_CTRL, Value32); +#endif + #ifdef SUPPORT_DRAM_KED + RdPipe = RecRdPipe[BestRangeIndex]; + TapCnt = AvaWds[BestRangeIndex][BestIndex]; + + value.b[0] = (u8)RdPipe; + value.b[1] = ~value.b[0]; + value.b[2] = (u8)TapCnt; + value.b[3] = ~value.b[2]; + //DiagPrintf("dump1w %x, %x %x %x %x \n\r", value.l, value.b[0], value.b[1], value.b[2], value.b[3]); + HAL_WRITE32(SPI_FLASH_BASE, FLASH_SDRC_PARA_BASE+8*CpuType+4, value.l); + SpicWaitWipDoneRefinedRtl8195A(SpicInitPara); + + valid = 1; + value.b[0] = (u8)valid; + value.b[1] = ~value.b[0]; + value.b[2] = 0xFF; + value.b[3] = 0xFF; + //DiagPrintf("dump1w %x, %x %x %x %x \n\r", value.l, value.b[0], value.b[1], value.b[2], value.b[3]); + HAL_WRITE32(SPI_FLASH_BASE, FLASH_SDRC_PARA_BASE+8*CpuType, value.l); + SpicWaitWipDoneRefinedRtl8195A(SpicInitPara); + #endif + Result = _TRUE; + break; + } + + if (RdPipeCounter == 0) { + + DBG_SDR_INFO("NOT Find RdPipe\n"); + } + } + + return Result; +} // SdrCalibration + + + + +HAL_SDRC_TEXT_SECTION +VOID +ChangeRandSeed( + IN u32 Seed +) +{ + rand_x = Seed; +} + +HAL_SDRC_TEXT_SECTION +u32 +Sdr_Rand2( + VOID +) +{ + HAL_RAM_DATA_SECTION static unsigned int y = 362436; + + HAL_RAM_DATA_SECTION static unsigned int z = 521288629; + + HAL_RAM_DATA_SECTION static unsigned int c = 7654321; + + unsigned long long t, a= 698769069; + + rand_x = 69069 * rand_x + 12345; + y ^= (y << 13); y ^= (y >> 17); y ^= (y << 5); + t = a * z + c; c = (t >> 32); z = t; + + return rand_x + y + z; +} + +HAL_SDRC_TEXT_SECTION +s32 +MemTest( + u32 LoopCnt +) +{ + u32 LoopIndex = 0; + u32 Value32, Addr; + for (LoopIndex = 0; LoopIndex 0x%x != 0x%x\n",LoopIndex, + Addr, Value32, HAL_SDRAM_READ32(Addr)); + return _FALSE; + } + else { + // HAL_SDRAM_WRITE32(Addr, 0); + } + } + return _TRUE; + +} // MemTest + +#endif // end of "#ifdef CONFIG_SDR_EN" diff --git a/component/soc/realtek/8195a/fwlib/src/hal_soc_ps_monitor.c b/component/soc/realtek/8195a/fwlib/src/hal_soc_ps_monitor.c new file mode 100644 index 0000000..fef35df --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_soc_ps_monitor.c @@ -0,0 +1,2487 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ +#include "rtl8195a.h" +#include "hal_soc_ps_monitor.h" + +#ifdef CONFIG_SOC_PS_MODULE +START_RAM_FUN_B_SECTION +RAM_START_FUNCTION gRamWakeupFun = {WakeFromSLPPG}; + +extern VOID UartLogIrqHandleRam(VOID * Data); +#if defined (__ICCARM__) +extern void xPortPendSVHandler( void ); +#elif defined (__GNUC__) +extern void xPortPendSVHandler( void ) __attribute__ (( naked )); +#endif +extern void xPortSysTickHandler( void ); +extern void vPortSVCHandler( void ); +extern u32 HalGetCpuClk(VOID); +extern _LONG_CALL_ u32 HalDelayUs(u32 us); + +extern COMMAND_TABLE UartLogRomCmdTable[]; +extern HAL_TIMER_OP HalTimerOp; +extern u32 STACK_TOP; // which is defined in vectors.s + +SYS_ADAPTER SYSAdapte; + +Power_Mgn PwrAdapter; + +VOID ReFillCpuClk(VOID); + +u32 +PatchHalLogUartInit( + IN LOG_UART_ADAPTER UartAdapter +) +{ + u32 SetData; + u32 Divisor; + u32 Dlh; + u32 Dll; + u32 SysClock; + + /* + Interrupt enable Register + 7: THRE Interrupt Mode Enable + 2: Enable Receiver Line Status Interrupt + 1: Enable Transmit Holding Register Empty Interrupt + 0: Enable Received Data Available Interrupt + */ + // disable all interrupts + HAL_UART_WRITE32(UART_INTERRUPT_EN_REG_OFF, 0); + + /* + Line Control Register + 7: DLAB, enable reading and writing DLL and DLH register, and must be cleared after + initial baud rate setup + 3: PEN, parity enable/disable + 2: STOP, stop bit + 1:0 DLS, data length + */ + + // set DLAB bit to 1 + HAL_UART_WRITE32(UART_LINE_CTL_REG_OFF, 0x80); + + // set up buad rate division + +#if 0//def CONFIG_FPGA + SysClock = SYSTEM_CLK; + Divisor = (SysClock / (16 * (UartAdapter.BaudRate))); +#else + { + u32 SampleRate,Remaind; + + SysClock = (HalGetCpuClk()>>2); + + SampleRate = (16 * (UartAdapter.BaudRate)); + + Divisor= SysClock/SampleRate; + + Remaind = ((SysClock*10)/SampleRate) - (Divisor*10); + + if (Remaind>4) { + Divisor++; + } + } +#endif + + + Dll = Divisor & 0xff; + Dlh = (Divisor & 0xff00)>>8; + HAL_UART_WRITE32(UART_DLL_OFF, Dll); + HAL_UART_WRITE32(UART_DLH_OFF, Dlh); + + // clear DLAB bit + HAL_UART_WRITE32(UART_LINE_CTL_REG_OFF, 0); + + // set data format + SetData = UartAdapter.Parity | UartAdapter.Stop | UartAdapter.DataLength; + HAL_UART_WRITE32(UART_LINE_CTL_REG_OFF, SetData); + + /* FIFO Control Register + 7:6 level of receive data available interrupt + 5:4 level of TX empty trigger + 2 XMIT FIFO reset + 1 RCVR FIFO reset + 0 FIFO enable/disable + */ + // FIFO setting, enable FIFO and set trigger level (2 less than full when receive + // and empty when transfer + HAL_UART_WRITE32(UART_FIFO_CTL_REG_OFF, UartAdapter.FIFOControl); + + /* + Interrupt Enable Register + 7: THRE Interrupt Mode enable + 2: Enable Receiver Line status Interrupt + 1: Enable Transmit Holding register empty INT32 + 0: Enable received data available interrupt + */ + HAL_UART_WRITE32(UART_INTERRUPT_EN_REG_OFF, UartAdapter.IntEnReg); + + if (UartAdapter.IntEnReg) { + // Enable Peripheral_IRQ Setting for Log_Uart + HAL_WRITE32(VENDOR_REG_BASE, PERIPHERAL_IRQ_EN, 0x1000000); + + // Enable ARM Cortex-M3 IRQ + NVIC_SetPriorityGrouping(0x3); + NVIC_SetPriority(PERIPHERAL_IRQ, 14); + NVIC_EnableIRQ(PERIPHERAL_IRQ); + } + + + return 0; +} + + +VOID +PSHalInitPlatformLogUart( + VOID +) +{ + IRQ_HANDLE UartIrqHandle; + LOG_UART_ADAPTER UartAdapter; + + //4 Release log uart reset and clock + LOC_UART_FCTRL(OFF); + LOC_UART_FCTRL(ON); + ACTCK_LOG_UART_CCTRL(ON); + + PinCtrl(LOG_UART,S0,ON); + + //4 Register Log Uart Callback function + UartIrqHandle.Data = (u32)NULL;//(u32)&UartAdapter; + UartIrqHandle.IrqNum = UART_LOG_IRQ; + UartIrqHandle.IrqFun = (IRQ_FUN) UartLogIrqHandleRam; + UartIrqHandle.Priority = 0; + + //4 Inital Log uart + UartAdapter.BaudRate = UART_BAUD_RATE_38400; + UartAdapter.DataLength = UART_DATA_LEN_8BIT; + UartAdapter.FIFOControl = 0xC1; + UartAdapter.IntEnReg = 0x00; + UartAdapter.Parity = UART_PARITY_DISABLE; + UartAdapter.Stop = UART_STOP_1BIT; + + //4 Initial Log Uart + PatchHalLogUartInit(UartAdapter); + + //4 Register Isr handle + InterruptRegister(&UartIrqHandle); + + UartAdapter.IntEnReg = 0x05; + + //4 Initial Log Uart for Interrupt + PatchHalLogUartInit(UartAdapter); + + //4 initial uart log parameters before any uartlog operation + //RtlConsolInit(ROM_STAGE,GetRomCmdNum(),(VOID*)&UartLogRomCmdTable);// executing boot seq., + //pUartLogCtl->TaskRdy = 1; +} + + +VOID +SYSIrqHandle +( + IN VOID *Data +) +{ + u32 Rtemp; + + //change cpu clk + ReFillCpuClk(); + HalDelayUs(100); + + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN) | 0x40000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN, Rtemp); + + //disable DSTBY timer + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, 0); + + //clear wake event IMR + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, 0); + + //clear wake event ISR + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0, Rtemp); + + //set event flag + PwrAdapter.WakeEventFlag = _TRUE; +} + +VOID +InitSYSIRQ(VOID) +{ + IRQ_HANDLE SysHandle; + PSYS_ADAPTER pSYSAdapte; + pSYSAdapte = &SYSAdapte; + SysHandle.Data = (u32) (pSYSAdapte); + SysHandle.IrqNum = SYSTEM_ON_IRQ; + SysHandle.IrqFun = (IRQ_FUN) SYSIrqHandle; + SysHandle.Priority = 0; + + InterruptRegister(&SysHandle); + InterruptEn(&SysHandle); + PwrAdapter.WakeEventFlag = _FALSE; +} + +void vWFSSVCHandler( void ) +{ +#if defined (__ICCARM__) + // TODO: IAR has different way using assembly +#elif defined (__GNUC__) + asm volatile + ( + "svcing:\n" + " mov r0, %0 \n" + " ldmia r0!, {r4-r7} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " ldmia r0!, {r8-r11} \n" + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " orr r14, #0xd \n" + " bx r14 \n" + ::"r"(PwrAdapter.CPUPSP):"r0" + ); +#endif +} + + +VOID +WakeFromSLPPG( + VOID +) +{ + //release shutdone + HAL_WRITE32(PERI_ON_BASE, REG_GPIO_SHTDN_CTRL, 0x7FF); + //HAL_WRITE32(PERI_ON_BASE, REG_CPU_PERIPHERAL_CTRL, 0x110001); + //JTAG rst pull high + HAL_WRITE32(PERI_ON_BASE, REG_GPIO_PULL_CTRL2, 0x05555556); + + ReFillCpuClk(); + + //3 Need Modify + VectorTableInitRtl8195A(0x1FFFFFFC); + + //3 Make PendSV, CallSV and SysTick the same priroity as the kernel. + HAL_WRITE32(0xE000ED00, 0x20, 0xF0F00000); + + //3 Initial Log Uart + PSHalInitPlatformLogUart(); + +#ifdef CONFIG_KERNEL + InterruptForOSInit((VOID*)vWFSSVCHandler, + (VOID*)xPortPendSVHandler, + (VOID*)xPortSysTickHandler); +#endif + //CPURegbackup[13] = CPURegbackup[13]-4; + PwrAdapter.CPURegbackup[16] |= 0x1000000 ; + + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-4) ) )= PwrAdapter.CPURegbackup[16]; //PSR + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-8) ) )= PwrAdapter.CPURegbackup[15]; //PC + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-12) ) )= PwrAdapter.CPURegbackup[14]; //LR + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-16) ) )= PwrAdapter.CPURegbackup[12]; //R12 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-20) ) )= PwrAdapter.CPURegbackup[3]; //R3 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-24) ) )= PwrAdapter.CPURegbackup[2]; //R2 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-28) ) )= PwrAdapter.CPURegbackup[1]; //R1 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-32) ) )= PwrAdapter.CPURegbackup[0]; //R0 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-36) ) )= PwrAdapter.CPURegbackup[11]; //R11 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-40) ) )= PwrAdapter.CPURegbackup[10]; //R10 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-44) ) )= PwrAdapter.CPURegbackup[9]; //R9 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-48) ) )= PwrAdapter.CPURegbackup[8]; //R8 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-52) ) )= PwrAdapter.CPURegbackup[7]; //R7 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-56) ) )= PwrAdapter.CPURegbackup[6]; //R6 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-60) ) )= PwrAdapter.CPURegbackup[5]; //R5 + ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[13]-64) ) )= PwrAdapter.CPURegbackup[4]; //R4 + PwrAdapter.CPURegbackup[13] = PwrAdapter.CPURegbackup[13]-64; //PSP + PwrAdapter.CPUPSP = PwrAdapter.CPURegbackup[13]; + CPURegBackUp(); + + asm volatile( + " cpsie i \n" /* Globally enable interrupts. */ + " svc 0 \n" /* System call to start first task. */ + " nop \n" + ); +} + +VOID +DurationScaleAndPeriodOP( + IN u32 SDuration, + OUT u32 *ScaleTemp, + OUT u32 *PeriodTemp +) +{ + u8 Idx = 0; + if (SDuration > 8355){ + SDuration = 0x20A3; + } + + //in unit 128us + SDuration = ((SDuration*125)/16); + + for (Idx = 8; Idx < 32; Idx++) { + + if ( (SDuration & 0xFFFFFF00) > 0 ) { + (*ScaleTemp) = (*ScaleTemp) + 1; + SDuration = (SDuration >> 1); + } + else { + break; + } + } + + *ScaleTemp = ((*ScaleTemp) << 8); + *PeriodTemp = SDuration; +} + + +u32 +CLKCal( + IN u8 ClkSel +) +{ + u32 Rtemp = 0; + u32 RRTemp = 0; + + if( ClkSel ){ + //a33_ck + Rtemp |= 0x10000; + } + + //Enable cal + Rtemp |= 0x800000; + HAL_WRITE32(VENDOR_REG_BASE, REG_VDR_ANACK_CAL_CTRL, Rtemp); + + while( (HAL_READ32(VENDOR_REG_BASE, REG_VDR_ANACK_CAL_CTRL) & BIT23) != 0 ); + Rtemp = ((HAL_READ32(VENDOR_REG_BASE, REG_VDR_ANACK_CAL_CTRL) & 0x3FFF))+1; + + if( ClkSel ){ + //a33_ck + RRTemp = (Rtemp); + } + else { + //anack + RRTemp = ((2133/Rtemp) - 1); + } + + //DiagPrintf("CAL : 0x%x\n", RRTemp); + + return RRTemp; +} + +VOID +BackupCPUClk( + VOID +) +{ + u32 Cpubp; + Cpubp = ((HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0)&0xFFFFFFF0)|((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_CLK_CTRL1) & (0x70)) >> 4)); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0,Cpubp); +} + +VOID +ReFillCpuClk( + VOID +) +{ + u8 CpuClk = ((u8)(HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0)& (0xF))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_CLK_CTRL1, + ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_CLK_CTRL1) & (~0x70)) + |(CpuClk << 4))); +} + +VOID +SleepClkGatted( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 ScaleTemp = 0; + u32 PeriodTemp = 0; + u32 CalTemp = 0; + + //Backup CPU CLK + BackupCPUClk(); + + //truncate duration + SDuration &= 0x0003FFFC; + //2 CSleep + //3 1.1 Set TU timer timescale + //0x4000_0090[21:16] = 6'h1F + //0x4000_0090[15] = 1'b0 => Disable timer + CalTemp = (CLKCal(ANACK) << 16); + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL)& 0xffff7fff & 0xffc0ffff) | CalTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[11:8] => Time scale + //0x4000_0090[7:0] => Time period + //max duration 0x7FFFFF us, min 0x80 + DurationScaleAndPeriodOP(SDuration, &ScaleTemp, &PeriodTemp); + + Rtemp = (((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) & 0xfffff000) | ScaleTemp) | PeriodTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //3 1.2 Configure platform wake event + //0x4000_0100[0] = 1'b1 => Enable timer and GT as wakeup event to wakeup CPU + Rtemp = 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + + //3 1.3 Configure power state option: + // 1.4.3 0x120[15:8]: sleep power mode option0 [11] = 1 + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION) & 0xffff00ff) | 0x74000A00);//A + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION, Rtemp); + + // 1.4.4 0x124[7:0]: sleep power mode option1 [0] =1 + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT) & 0xffffff00) | 0x00000001); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT, Rtemp); + + //3 1.5 Enable low power mode + // 1.5.1 0x4000_0118[2] = 1 => for sleep mode + Rtemp = 0x00000004;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000004; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //3 1.6 Wait CHIP enter low power mode + // 1.7 Wait deep standby timer timeout + // 1.8 Wait CHIP resume to norm power mode + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + +} + + +VOID SleepPwrGatted( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 ScaleTemp = 0; + u32 PeriodTemp = 0; + u32 CalTemp = 0; + + //Backup CPU CLK + BackupCPUClk(); + + //truncate duration + SDuration &= 0x0003FFFC; + + //2 PSleep + //3 1.1 Set TU timer timescale + //0x4000_0090[21:16] = 6'h1F + //0x4000_0090[15] = 1'b0 => Disable timer + CalTemp = (CLKCal(ANACK) << 16); + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL)& 0xffff7fff & 0xffc0ffff) | CalTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[11:8] => Time scale + //0x4000_0090[7:0] => Time period + //max duration 0x7FFFFF us, min 0x80 + DurationScaleAndPeriodOP(SDuration, &ScaleTemp, &PeriodTemp); + + Rtemp = (((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) & 0xfffff000) | ScaleTemp) | PeriodTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //3 1.2 Configure platform wake event + //0x4000_0100[0] = 1'b1 => Enable timer and GT as wakeup event to wakeup CPU + Rtemp = 0x00000003; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + + //3 1.4 Configure power state option: + // 1.4.3 0x120[15:8]: sleep power mode option0: + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION) & 0x00ff00ff) | 0x74000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION, Rtemp); + + // 1.4.4 0x124[7:0]: sleep power mode option1: + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT) & 0xffffff00) | 0x00000003); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT, Rtemp); + + //3 1.5 Enable low power mode + // 1.5.1 0x4000_0118[2] = 1 => for sleep mode + Rtemp = 0x00000004;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000004; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //3 1.6 Wait CHIP enter low power mode + // 1.7 Wait deep standby timer timeout + // 1.8 Wait CHIP resume to norm power mode + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + DiagPrintf("YOU CAN'T SEE ME ~~~~!!!!!!!!!!!!!!!!!!!~~~~~slppg~~~~!!!!!!!!!!"); +} + + +VOID +DStandby( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 ScaleTemp = 0; + u32 PeriodTemp = 0; + u32 CalTemp = 0; + + //Backup CPU CLK + BackupCPUClk(); + + //Clear A33 timer event + //Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_SLP_WAKE_EVENT_STATUS0); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_SLP_WAKE_EVENT_STATUS0, Rtemp); + + //2 Deep Standby mode + //3 1.1 Set TU timer timescale + //0x4000_0090[21:16] = 6'h1F + //0x4000_0090[15] = 1'b0 => Disable timer + CalTemp = (CLKCal(ANACK) << 16); + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL)& 0xffff7fff & 0xffc0ffff) | CalTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[11:8] => Time scale + //0x4000_0090[7:0] => Time period + //max duration 0x7FFFFF us, min 0x80 + DurationScaleAndPeriodOP(SDuration, &ScaleTemp, &PeriodTemp); + + Rtemp = (((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) & 0xfffff000) | ScaleTemp) | PeriodTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //3 1.3 Configure platform wake event + // 1.3.1 0x4000_0100[0] = 1'b1 => Enable deep standby timer wakeup event to wakeup CPU + Rtemp = 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + + //3 1.4 Configure power state option: + // 1.4.4 0x120[7:0]: deep standby power mode option: + Rtemp = 0x74000000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION, Rtemp); + + // 1.4.5 0x124[7:0]: sleep power mode option1 [0] =1 + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT) & 0xffffff00) | 0x00000001); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT, Rtemp); + + //3 1.5 Enable low power mode + // [0x4000_0118[1] = 1 => for deep standby mode] + Rtemp = 0x00000002; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //3 1.6 Wait CHIP enter low power mode + // 1.7 Wait deep standby timer timeout + // 1.8 Wait CHIP resume to norm power mode + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + + DiagPrintf("YOU CAN'T SEE ME ~~~~!!!!!!!!!!!!!!!!!!!~~~~~~~~~~~~~~~!!!!!!!!!!"); +} + + +VOID +DSleep( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + //u32 ScaleTemp = 0; + //u32 PeriodTemp = 0; + u32 UTemp = 0; + u32 MaxTemp = 0; + + u32 Reada335 = 0; + + //2 Deep Sleep mode: + //3 2.1 Set TU timer timescale + + //3 2.2 Configure deep sleep timer: + //2.2.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2 Calibration A33 CLK + UTemp = CLKCal(A33CK); + + //Calculate the max value base on the a33 duration + MaxTemp = 0x7FFFFF*0x100/100000*UTemp/100*0x80; + //DiagPrintf("MaxTemp : 0x%x\n", MaxTemp); + + if ( SDuration >= MaxTemp ) { + SDuration = 0x7FFFFF; + } + else { + //In unit of A33 CLK : max num is bounded by anaclk = 1.5k + SDuration = ((((SDuration)/UTemp)*25/16*25/16*125)); + //DiagPrintf("SDuration : 0x%x\n", SDuration); + } + + DiagPrintf("SDuration : 0x%x\n", SDuration); + + //3 2.2.2 Initialize deep sleep counter + //2.2.2.0 0x4000_0094[15:0] = 16'hD300 => Disable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D300); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + //2.2.2.0.1 Clear event + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT)); + //2.2.2.1 0x4000_0094[15:0] = 16'h9008 => set counter[7:0] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009000 | ((u8)SDuration)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.2 0x4000_0094[15:0] = 16'h9100 => set counter[15:8] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009100 | ((u8)(SDuration >> 8))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.3 0x4000_0094[15:0] = 16'h9200 => set counter[22:16] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009200 | ((u8)(SDuration >> 16))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.4 0x4000_0094[15:0] = 16'hD380 => Enable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D380); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + HalDelayUs(1000); + Reada335 = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CAL_CTRL); + DiagPrintf("a33 timer : 0x%x\n", Reada335); + + HalDelayUs(8000); + + //3 2.2.3 + //2.3 Enable low power mode: 0x4000_0118[0] = 1'b1; + Rtemp = 0x00000001;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //2.4 Wait CHIP enter deep sleep mode + //2.5 Wait deep sleep counter timeout + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + + DiagPrintf("YOU CAN'T SEE ME ~~~~!!!!!!!!!!!!!!!!!!!~~~~~~~~~~~~~~~!!!!!!!!!!"); +} + +VOID +MSBackupProcess( + void +) +{ + + u8 i = 0; + + //backup main stack + for (i = 0; i < (MAX_BACKUP_SIZE-1); i++) { + PwrAdapter.MSPbackup[i] = HAL_READ32(0x1FFFFE00, (0x1FC - (i*4))); + } + + asm volatile + ( + "MRS r0, MSP\n" + "MOV %0, r0\n" + :"=r"(PwrAdapter.MSPbackup[MAX_BACKUP_SIZE-1]) + ::"memory" + ); +} + + +VOID +MSReFillProcess( + VOID +) +{ + u8 i = 0; + + for (i = 0; i < (MAX_BACKUP_SIZE-1); i++) { + + HAL_WRITE32(0x1FFFFE00, (0x1FC - (i*4)), PwrAdapter.MSPbackup[i]); + } + + asm volatile + ( + "MSR MSP, %0\n" + ::"r"(PwrAdapter.MSPbackup[MAX_BACKUP_SIZE-1]):"memory" + ); +} + + +VOID +SoCPSGPIOCtrl( + VOID +) +{ + HAL_WRITE32(PERI_ON_BASE,0x330,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x334,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x338,0x05555555); + HAL_WRITE32(PERI_ON_BASE,0x33c,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x340,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x344,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x348,0x55555555); + HAL_WRITE32(PERI_ON_BASE,0x320,0x0); +} + + +VOID +InitSoCPW( + VOID +) +{ + u8 Idx = 0; + + PwrAdapter.ActFuncCount = 0; + PwrAdapter.CurrentState = ACT; + for (Idx = 0; Idx < MAXSTATE; Idx++) { + PwrAdapter.PwrState[Idx].FuncIdx = 0xFF; + PwrAdapter.PwrState[Idx].PowerState = 0xFF; + } +} + +u8 +ChangeSoCPwrState( + IN u8 RequestState, + IN u32 ReqCount +) +{ + + //DiagPrintf("Go to sleep"); + + while(1) { + + HalDelayUs(100); + + if (HAL_READ8(LOG_UART_REG_BASE,0x14)&BIT6){ + + break; + } + } + + switch (RequestState) { + + case ACT: + break; + + case WFE: + __WFE(); + break; + + case WFI: + __WFI(); + break; + + //case SNOOZE: + //break; + + case SLPCG: + SleepClkGatted(ReqCount); + break; + + case SLPPG: + //Resume jump to wakeup function + //HAL_WRITE32(PERI_ON_BASE, 0x218, (HAL_READ32(PERI_ON_BASE,0x218)|BIT31)); + + SoCPSGPIOCtrl(); + SleepPwrGatted(ReqCount); + break; + + case DSTBY: + SoCPSGPIOCtrl(); + DStandby(ReqCount); + break; + + case DSLP: + case INACT: + SoCPSGPIOCtrl(); + DSleep(ReqCount); + break; + } + return 0; +} + + +VOID +SoCPwrDecision( + void +) +{ + u8 Idx = 0; + u8 StateIdx = 0; + u8 State = _TRUE; + u8 NextState = 0; + u32 CurrentCount, RemainCount, PTTemp; + + if ( PwrAdapter.ActFuncCount ) { + + //update remaining count + CurrentCount = HalTimerOp.HalTimerReadCount(1); + + for (Idx = 0; Idx < PwrAdapter.ActFuncCount; Idx++) { + + if (PwrAdapter.PwrState[Idx].RegCount < CurrentCount) { + PTTemp = (0xFFFFFFFF - CurrentCount + PwrAdapter.PwrState[Idx].RegCount); + } + else { + PTTemp = (PwrAdapter.PwrState[Idx].RegCount - CurrentCount); + } + + if ( PTTemp < PwrAdapter.PwrState[Idx].ReqDuration ) { + PwrAdapter.PwrState[Idx].RemainDuration = PwrAdapter.PwrState[Idx].ReqDuration - PTTemp; + } + else { + //active this function + if ( PwrAdapter.PwrState[Idx].PowerState > SLPPG ) { + //Todo: re-initial function as GPIO wake + } + PwrAdapter.PwrState[Idx].PowerState = ACT; + PwrAdapter.PwrState[Idx].RemainDuration = 0; + PwrAdapter.PwrState[Idx].ReqDuration = 0; + } + } + + //Select next power mode + for (StateIdx = DSLP; StateIdx >= ACT; StateIdx--) { + + for (Idx = 0; Idx < PwrAdapter.ActFuncCount; Idx++) { + + State = _TRUE; + if (PwrAdapter.PwrState[Idx].PowerState < StateIdx) { + State = _FALSE; + break; + } + } + + if ( State ) { + NextState = StateIdx; + break; + } + } + + //fine min sleep time + RemainCount = PwrAdapter.PwrState[0].RemainDuration; + for (Idx = 0; Idx < PwrAdapter.ActFuncCount; Idx++) { + + if ( RemainCount > PwrAdapter.PwrState[Idx].RemainDuration ) { + + RemainCount = PwrAdapter.PwrState[Idx].RemainDuration; + } + } + + //for debug + #if 1 + DiagPrintf("RemainCount : 0x%x \n", RemainCount); + DiagPrintf("NextState : 0x%x \n", NextState); + #endif + #if 0 + //Change state + if ( NextState > SLPCG ) { + if ( RemainCount > 640 ) { + ChangeSoCPwrState(NextState, RemainCount); + } + else { + ChangeSoCPwrState(SLPCG, RemainCount); + } + } + else { + if (NextState != ACT ) { + ChangeSoCPwrState(NextState, RemainCount); + } + } + #endif + } + else { + //todo: go to DSLP + } +} + + +VOID +RegPowerState( + REG_POWER_STATE RegPwrState +) +{ + u8 Idx = 0; + u8 StateIdx; + u8 FState = 0; + + for (Idx = 0; Idx < PwrAdapter.ActFuncCount; Idx++) { + if (PwrAdapter.PwrState[Idx].FuncIdx == RegPwrState.FuncIdx) { + StateIdx = Idx; + FState = _TRUE; + } + } + + switch (RegPwrState.PwrState) { + + case INACT : + if (FState) { + for (Idx = StateIdx; Idx < PwrAdapter.ActFuncCount; Idx++) { + PwrAdapter.PwrState[Idx].FuncIdx = PwrAdapter.PwrState[Idx+1].FuncIdx; + PwrAdapter.PwrState[Idx].PowerState = PwrAdapter.PwrState[Idx+1].PowerState; + PwrAdapter.PwrState[Idx].ReqDuration = PwrAdapter.PwrState[Idx+1].ReqDuration; + PwrAdapter.PwrState[Idx].RegCount = PwrAdapter.PwrState[Idx+1].RegCount; + } + PwrAdapter.ActFuncCount--; + } + else { + } + break; + + default: + + if (FState) { + PwrAdapter.PwrState[StateIdx].PowerState = RegPwrState.PwrState; + PwrAdapter.PwrState[StateIdx].ReqDuration = RegPwrState.ReqDuration; + PwrAdapter.PwrState[StateIdx].RegCount = HalTimerOp.HalTimerReadCount(1); + } + else { + PwrAdapter.PwrState[PwrAdapter.ActFuncCount].FuncIdx = RegPwrState.FuncIdx; + PwrAdapter.PwrState[PwrAdapter.ActFuncCount].PowerState = RegPwrState.PwrState; + PwrAdapter.PwrState[PwrAdapter.ActFuncCount].ReqDuration = RegPwrState.ReqDuration; + PwrAdapter.PwrState[PwrAdapter.ActFuncCount].RegCount = HalTimerOp.HalTimerReadCount(1); + PwrAdapter.ActFuncCount++; + } + + break; + } + + //for debug + #if 1 + for (Idx = 0; Idx < PwrAdapter.ActFuncCount; Idx++) { + DiagPrintf("RegPwrIdx : %d \n", Idx); + DiagPrintf("FuncIdx : %d \n", PwrAdapter.PwrState[Idx].FuncIdx); + DiagPrintf("PowerState : 0x%x \n", PwrAdapter.PwrState[Idx].PowerState); + DiagPrintf("ReqDuration : 0x%x \n", PwrAdapter.PwrState[Idx].ReqDuration); + DiagPrintf("RegCount : 0x%x \n", PwrAdapter.PwrState[Idx].RegCount); + } + #endif +} + + +VOID +SetSYSTimer( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 ScaleTemp = 0; + u32 PeriodTemp = 0; + u32 CalTemp = 0; + + //0x4000_0090[15] = 1'b0 => Disable timer + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, 0); + + //calculate scale and period + CalTemp = (CLKCal(ANACK) << 16); + DurationScaleAndPeriodOP(SDuration, &ScaleTemp, &PeriodTemp); + + Rtemp = ((CalTemp | ScaleTemp) | PeriodTemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); +} + +VOID +SleepCG( + IN u8 Option, + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 WakeEvent = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0); + + //Backup CPU CLK + BackupCPUClk(); + + //Clear event + PwrAdapter.WakeEventFlag = _FALSE; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0)); + + //3 2 Configure power state option: + // 2.1 power mode option: + Rtemp = 0x74000900; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION, Rtemp); + + // 2.2 sleep power mode option1 + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT) & 0xffffff00)|0x2); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT, Rtemp); + + if (Option & SLP_STIMER) { + + //Set TU timer timescale + SetSYSTimer(SDuration); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //Enable wake event + WakeEvent |= BIT0; + } + + if (Option & SLP_GTIMER) { + + //Enable wake event + WakeEvent |= BIT1; + } + + if (Option & SLP_GPIO) { + + //Enable wake event + WakeEvent |= BIT4; + } + + if (Option & SLP_WL) { + + //Enable wake event + WakeEvent |= BIT8; + } + + if (Option & SLP_NFC) { + + //Enable wake event + WakeEvent |= BIT28; + } + + if (Option & SLP_SDIO) { + + //Enable wake event + //WakeEvent |= BIT14; + } + + if (Option & SLP_USB) { + + //Enable wake event + //WakeEvent |= BIT16; + } + + if (Option & SLP_TIMER33) { + + //Enable wake event + WakeEvent |= BIT28; + } + + while(1) { + + HalDelayUs(100); + + if (HAL_READ8(LOG_UART_REG_BASE,0x14)&BIT6){ + + break; + } + } + + //Set Event + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, WakeEvent); + + //3 Enable low power mode + //Enable low power mode: + if (PwrAdapter.WakeEventFlag != _TRUE){ + + Rtemp = 0x00000004; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //3 Wait CHIP enter low power mode + // Wait deep standby timer timeout + // Wait CHIP resume to norm power mode + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + } +} + + +VOID +DSTBYGpioCtrl( + IN u8 PinEn, + IN u8 WMode +) +{ + u32 Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_GPIO_DSTBY_WAKE_CTRL0)|PinEn|(PinEn<<16)|(PinEn<<24)); + u32 Stemp = (PinEn<<8); + + if (WMode) { + Rtemp = (Rtemp|Stemp); + } + else { + Rtemp = (Rtemp & (~Stemp)); + } + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_GPIO_DSTBY_WAKE_CTRL0, Rtemp); + + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_GPIO_DSTBY_WAKE_CTRL1)|PinEn|(PinEn<<16)|BIT9); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_GPIO_DSTBY_WAKE_CTRL1, Rtemp); +} + + +VOID +DeepStandby( + IN u8 Option, + IN u32 SDuration, + IN u8 GpioOption +) +{ + u32 Rtemp = 0; + + //Clear event + PwrAdapter.WakeEventFlag = _FALSE; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0)); + + //3 2 Configure power state option: + // 2.1 deep standby power mode option: + Rtemp = 0x74000100; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION, Rtemp); + + // 2.2 sleep power mode option1 + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT) & 0xffffff00)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_OPTION_EXT, Rtemp); + + if (Option & DSTBY_STIMER) { + + //3 3.1 Set TU timer timescale + SetSYSTimer(SDuration); + + //3 3.2 Configure platform wake event + // 1.3.1 0x4000_0100[0] = 1'b1 => Enable deep standby timer wakeup event to wakeup CPU + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0)|BIT0); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + } + + if (Option & DSTBY_NFC){ + //Enable wake event + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0)|BIT28); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + } + + if (Option & DSTBY_TIMER33){ + //Enable wake event + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0)|BIT28); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + } + + if (Option & DSTBY_GPIO){ + + if (GpioOption & BIT0) { + DSTBYGpioCtrl(BIT0, (GpioOption & BIT4)); + } + + if (GpioOption & BIT1) { + DSTBYGpioCtrl(BIT1, (GpioOption & BIT5)); + } + + if (GpioOption & BIT2) { + DSTBYGpioCtrl(BIT2, (GpioOption & BIT6)); + } + + if (GpioOption & BIT3) { + DSTBYGpioCtrl(BIT3, (GpioOption & BIT7)); + } + + //Enable wake event + if (GpioOption & 0xF){ + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0)|BIT29); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + } + } + + + //3 Enable low power mode + //Enable low power mode: + if (PwrAdapter.WakeEventFlag != _TRUE){ + + SpicDeepPowerDownFlashRtl8195A(); + + LDO25M_CTRL(OFF); + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_GPIO_SHTDN_CTRL, 0x0); + + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN) & 0xBFFFFFFF); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN, Rtemp); + + Rtemp = 0x00000002; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //3 Wait CHIP enter low power mode + // Wait deep standby timer timeout + // Wait CHIP resume to norm power mode + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + } +} + + + +VOID +DeepSleep( + IN u8 Option, + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 UTemp = 0; + u32 MaxTemp = 0; + + //1.1.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //1.1.2 0x4000_0094[15:0] = 16'hD300 => Disable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D300); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + + //1.1.3 Clear event + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT)); + + if (Option & DS_TIMER33){ + //2.1.1 Calibration A33 CLK + UTemp = CLKCal(A33CK); + + //Calculate the max value base on the a33 duration + MaxTemp = ((((0x7FFFFF/3)*500)/UTemp)*25); + + if ( SDuration >= MaxTemp ) { + SDuration = 0x7FFFFF; + } + else { + //In unit of A33 CLK : max num is bounded by anaclk = 1.5k + SDuration = ((((SDuration/3)*500)/UTemp)*25); + } + + //2.1.2 Initialize deep sleep counter + //2.1.3.1 0x4000_0094[15:0] = 16'h9008 => set counter[7:0] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009000 | ((u8)SDuration)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + + //2.1.3.2 0x4000_0094[15:0] = 16'h9100 => set counter[15:8] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009100 | ((u8)(SDuration >> 8))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + + //2.1.3.3 0x4000_0094[15:0] = 16'h9200 => set counter[22:16] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009200 | ((u8)(SDuration >> 16))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + + //2.1.3.4 0x4000_0094[15:0] = 16'hD380 => Enable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D380); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + } + + if (Option & DS_GPIO) { + //2.2 en GPIO + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009410); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + while(HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15){}; + } + + + //0x4000_0100[28] = 1'b1 => Enable A33 wakeup event to wakeup CPU + PwrAdapter.WakeEventFlag = _FALSE; + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0)|BIT28); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); + + //3 2.3 + //2.3 Enable low power mode: 0x4000_0118[0] = 1'b1; + if (PwrAdapter.WakeEventFlag != _TRUE){ + + SpicDeepPowerDownFlashRtl8195A(); + + LDO25M_CTRL(OFF); + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_GPIO_SHTDN_CTRL, 0x0); + + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN) & 0xBFFFFFFF); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_FUNC_EN, Rtemp); + + Rtemp = 0x00000001;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //2.4 Wait CHIP enter deep sleep mode + //2.5 Wait deep sleep counter timeout + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); + } +} + + +VOID +DSleep_GPIO( + VOID +) +{ + u32 Rtemp = 0; + + //1.1 Clear event + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT)); + + //2 Deep Sleep mode: + //3 2.2 Configure GPIO: + //2.2.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009410); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2 + //2.3 Enable low power mode: 0x4000_0118[0] = 1'b1; + Rtemp = 0x00000001;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //2.4 Wait CHIP enter deep sleep mode + //2.5 Wait deep sleep counter timeout + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); +} + +VOID +DSleep_Timer( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + u32 UTemp = 0; + u32 MaxTemp = 0; + + //2 Deep Sleep mode: + //3 2.1 Set TU timer timescale + + //3 2.2 Configure deep sleep timer: + //2.2.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2 0x4000_0094[15:0] = 16'hD300 => Disable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D300); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.3 Clear event + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT, HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_WEVENT)); + + //2.2.4 Calibration A33 CLK + UTemp = CLKCal(A33CK); + + //Calculate the max value base on the a33 duration + MaxTemp = 0x7FFFFF*0x100/100000*UTemp/100*0x80; + + if ( SDuration >= MaxTemp ) { + SDuration = 0x7FFFFF; + } + else { + //In unit of A33 CLK : max num is bounded by anaclk = 1.5k + SDuration = ((((SDuration)/UTemp)*25/16*25/16*125)); + } + + //DiagPrintf("SDuration : 0x%x\n", SDuration); + + //2.2.5 Initialize deep sleep counter + //2.2.5.1 0x4000_0094[15:0] = 16'h9008 => set counter[7:0] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009000 | ((u8)SDuration)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.5.2 0x4000_0094[15:0] = 16'h9100 => set counter[15:8] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009100 | ((u8)(SDuration >> 8))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.5.3 0x4000_0094[15:0] = 16'h9200 => set counter[22:16] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009200 | ((u8)(SDuration >> 16))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.5.4 0x4000_0094[15:0] = 16'hD380 => Enable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D380); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //HalDelayUs(1000); + //Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CAL_CTRL); + //DiagPrintf("a33 timer : 0x%x\n", Rtemp); + HalDelayUs(8000); + + //3 2.3 + //2.3 Enable low power mode: 0x4000_0118[0] = 1'b1; + Rtemp = 0x00000001;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //2.4 Wait CHIP enter deep sleep mode + //2.5 Wait deep sleep counter timeout + HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL); + __WFI(); +} + + +#if 0 +VOID +En32KCalibration( + VOID +) +{ + u32 Rtemp; + u32 Ttemp = 0; + + while(1) { + + //set parameter + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, 0); + Rtemp = 0x80f880; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + + DiagPrintf("cal en\n"); + + //Polling LOCK + Rtemp = 0x110000; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); + DiagPrintf("polling lock\n"); + + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL1); + if ((Rtemp & 0x3000) != 0x0){ + break; + } + else { + Ttemp++; + DiagPrintf("check lock: %d\n", Ttemp); + } + } + + Rtemp = 0x884000; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_OSC32K_REG_CTRL0, Rtemp); +} +#endif + + +VOID +SoCPwrReinitProcess( + VOID +) +{ + //clear resume jumping condition + HAL_WRITE32(PERI_ON_BASE, 0x218, (HAL_READ32(PERI_ON_BASE,0x218)&(~BIT31))); + + #ifdef CONFIG_KERNEL + InterruptForOSInit((VOID*)vPortSVCHandler, + (VOID*)xPortPendSVHandler, + (VOID*)xPortSysTickHandler); + #endif + + //msp stack + MSReFillProcess(); + + //init sys timer + ( * ( ( volatile unsigned long * ) 0xe000e014 ) ) = 0xc34f;//portNVIC_SYSTICK_LOAD_REG + ( * ( ( volatile unsigned long * ) 0xe000e010 ) ) = 0x10007;//portNVIC_SYSTICK_CTRL_REG + + //3 Reinit SYS int + { + IRQ_HANDLE SysHandle; + PSYS_ADAPTER pSYSAdapte; + pSYSAdapte = &SYSAdapte; + SysHandle.Data = (u32) (pSYSAdapte); + SysHandle.IrqNum = SYSTEM_ON_IRQ; + SysHandle.IrqFun = (IRQ_FUN) SYSIrqHandle; + SysHandle.Priority = 0; + + InterruptRegister(&SysHandle); + InterruptEn(&SysHandle); + } + //DiagPrintf("REINIT IRQ0!!!!!!!!!!\n"); + //HAL_WRITE32(0xE000ED00, 0x14, 0x200); + +} + + +VOID +SoCEnterPS( + VOID +) +{ +} + + +VOID +SoCPWRIdleTaskHandle( + VOID +) +{ + //static u32 IdleLoopCount = 0; + static u32 IdleCount = 0; + //u8 Chktemp = 0; + //u32 CMDTemp[6]; + //u32 Rtemp,Rtemp1,Rtemp2; + + //IdleCount++; + //HalDelayUs(1000); + //if ((IdleCount > 5000)||(HAL_READ8(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0+1) == 0x12)) + if (HAL_READ8(SYSTEM_CTRL_BASE, 0xf2) == 0xda) {// { + + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SOC_FUNC_EN, (HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_FUNC_EN)|BIT29)); + + HAL_WRITE32(SYSTEM_CTRL_BASE,0xf0,0); + + #if 0 //slp pg + //backup cpu reg + CPURegBackUp(); + + //backup main stack + MSBackupProcess(); + + //Wait for LogUart print out + while(1) { + HalDelayUs(100); + if (HAL_READ8(LOG_UART_REG_BASE,0x14)&BIT6){ + break; + } + } + + SoCPSGPIOCtrl(); + + ChangeSoCPwrState(SLPPG, 0xFFFFF); + + asm volatile + ( + "SLPPG_WAKEUP_POINT:\n" + ); + + SoCPwrReinitProcess(); + + //DiagPrintf("idle~~~~~~~~~~~~~~~~~\n"); + DiagPrintf("SLP_PG = %d\n", HAL_READ32(SYSTEM_CTRL_BASE,0xf8)); + #endif + asm volatile + ( + "SLPPG_WAKEUP_POINT:\n" + ); + + #if 1 //dslp + //Wait for LogUart print out + while(1) { + HalDelayUs(100); + if (HAL_READ8(LOG_UART_REG_BASE,0x14)&BIT6){ + break; + } + } + + ChangeSoCPwrState(DSTBY, 0xFFFFF); + #endif + + } + + if (IdleCount>500){ + IdleCount = 0; + if (HAL_READ32(SYSTEM_CTRL_BASE,0xf4) ==0) { + HAL_WRITE32(SYSTEM_CTRL_BASE,0xf0,HAL_READ32(SYSTEM_CTRL_BASE,0xf0)|0xda0000); + HAL_WRITE32(SYSTEM_CTRL_BASE,0xf8,HAL_READ32(SYSTEM_CTRL_BASE,0xf8)+1); + DiagPrintf("DSTBY = %d\n", HAL_READ32(SYSTEM_CTRL_BASE,0xf8)); + } + //DiagPrintf("idle~~~~~~~~~~~~~~~~~\n"); + } + else { + HalDelayUs(100000); + IdleCount++; + } +} + +#ifdef CONFIG_SOC_PS_VERIFY +VOID +SYSTestIrqHandle +( + IN VOID *Data +) +{ + u32 Rtemp; + static u32 Ttemp = 0; + + //change cpu clk + ReFillCpuClk(); + HalDelayUs(100); + + //JTAG rst pull high + HAL_WRITE32(PERI_ON_BASE, REG_GPIO_PULL_CTRL2, 0x0202aaaa); + + //release shutdone + //HAL_WRITE32(PERI_ON_BASE, REG_GPIO_SHTDN_CTRL, 0x7ff); + + //disable DSTBY timer + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, 0); + + //clear wake event IMR + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, 0); + + //clear wake event ISR + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_STATUS0, Rtemp); + + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO0, Ttemp); + + //DiagPrintf("Ttemp : %d\n", Ttemp); + + Ttemp++; + //Rtemp = HalTimerOp.HalTimerReadCount(1); + //DiagPrintf("32k counter : %x\n", Rtemp);//32k counter : + //DiagPrintf("\n"); + + //PwrAdapter.SleepFlag = 1; + //DiagPrintf("\n"); + //DiagPrintf("0x234 after slp : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,0x234)+1); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO0, HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0)+1); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO1, HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO1)+1); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO2, HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO2)+1); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO3, HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO3)+1); + //DiagPrintf("f0 counter : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0)); + //DiagPrintf("f1 counter : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO1)); + //DiagPrintf("f2 counter : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO2)); + //DiagPrintf("f3 counter : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO3)); + //DiagPrintf("\n"); + //DiagPrintf("ya ~~~~\n"); + + PwrAdapter.WakeEventFlag = _TRUE; +} + +VOID +InitSYSTestIRQ(VOID) +{ + IRQ_HANDLE SysHandle; + PSYS_ADAPTER pSYSAdapte; + pSYSAdapte = &SYSAdapte; + SysHandle.Data = (u32) (pSYSAdapte); + SysHandle.IrqNum = SYSTEM_ON_IRQ; + SysHandle.IrqFun = (IRQ_FUN) SYSIrqHandle; + SysHandle.Priority = 0; + + InterruptRegister(&SysHandle); + InterruptEn(&SysHandle); + PwrAdapter.WakeEventFlag = _FALSE; +} + +VOID +SetA33Timer( + IN u32 SDuration +) +{ + u32 Rtemp = 0; + //u32 ScaleTemp = 0; + //u32 PeriodTemp = 0; + u32 UTemp = 0; + u32 MaxTemp = 0; + + //2 Deep Sleep mode: + //3 2.1 Set TU timer timescale + + //3 2.2 Configure deep sleep timer: + //2.2.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2 Calibration A33 CLK + UTemp = CLKCal(A33CK); + DiagPrintf("CAL : 0x%x\n", UTemp); + + //Calculate the max value base on the a33 duration + MaxTemp = 0x7FFFFF*0x100/100000*UTemp/100*0x80; + DiagPrintf("MaxTemp : 0x%x\n", MaxTemp); + + if ( SDuration >= MaxTemp ) { + SDuration = 0x7FFFFF; + } + else { + //In unit of A33 CLK : max num is bounded by anaclk = 1.5k + SDuration = ((((SDuration)/UTemp)*25/16*25/16*125)); + DiagPrintf("SDuration : 0x%x\n", SDuration); + + } + + //3 2.2.2 Initialize deep sleep counter + //2.2.2.1 0x4000_0094[15:0] = 16'h9008 => set counter[7:0] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009000 | ((u8)SDuration)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.2 0x4000_0094[15:0] = 16'h9100 => set counter[15:8] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009100 | ((u8)(SDuration >> 8))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.3 0x4000_0094[15:0] = 16'h9200 => set counter[22:16] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009200 | ((u8)(SDuration >> 16))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.4 0x4000_0094[15:0] = 16'hD380 => Enable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D380); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + DiagPrintf("a33 timer : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CAL_CTRL)); +} + + +VOID +PrintCPU( + VOID +) +{ + + #if 0 + DiagPrintf("r13 : 0x%x\n", PwrAdapter.CPURegbackup[24]); + DiagPrintf("pc : 0x%x\n", PwrAdapter.CPURegbackup[23]); + DiagPrintf("control : 0x%x\n", PwrAdapter.CPURegbackup[22]); + DiagPrintf("psp : 0x%x\n", PwrAdapter.CPURegbackup[21]); + DiagPrintf("msp : 0x%x\n", PwrAdapter.CPURegbackup[20]); + #endif + + #if 0 + u8 i; + for (i = 0; i < 21; i++){ + PwrAdapter.CPURegbackup[i] = ( * ( ( volatile unsigned long * ) (PwrAdapter.CPURegbackup[24]+(i*4)) ) ); + } + #endif + + u8 i; + for (i = 0; i < 25; i++){ + DiagPrintf("CPURegbackup_idx : %d , 0x%x\n", i, PwrAdapter.CPURegbackup[i]); + } + + + #if 1 + for (i = 0; i < 21; i++) { + DiagPrintf("backup_idx : 0x%x , 0x%x\n", PwrAdapter.CPUPSP+(i*4),( * ( ( volatile unsigned long * ) (PwrAdapter.CPUPSP+(i*4)) ) ));//CPURegbackup[1] + } + #endif + + #if 0 + { + u32 cpupspc; + asm volatile + ( + "MRS %0, PSP\n" + :"=r"(cpupspc) + ::"memory" + ); + for (i = 0; i < 21; i++) { + DiagPrintf("stack addr : 0x%x , 0x%x\n", (cpupspc+(i*4)),( * ( ( volatile unsigned long * ) (cpupspc+(i*4)) ) ));//CPURegbackup[1] + } + } + #endif +} + + +VOID +SoCPSMEMTestInit( + IN u32 StartAddr, + IN u32 Length, + IN u32 Pattern +) +{ + u32 Idx; + for( Idx = 0; Idx < Length; Idx += 4 ){ + + HAL_WRITE32(StartAddr,Idx,Pattern); + } +} + +u8 +SoCPSMEMTestChk( + IN u32 StartAddr, + IN u32 Length, + IN u32 Pattern +) +{ + u32 Idx; + + for( Idx = 0; Idx < Length; Idx += 4 ){ + if (HAL_READ32(StartAddr,Idx) != Pattern) { + DiagPrintf("addr 0x%x fail\n", (StartAddr+Idx)); + return 0; + } + } + DiagPrintf("addr 0x%x pass\n", StartAddr); + return 1; +} + + +VOID +SoCPWRIdleTaskHandleTest( + VOID +) +{ + static u32 IdleTemp = 0; + u32 Rtemp,Rtemp1,Rtemp2; + u8 RRtemp,CMDTemp[8],Chktemp; + + if (0){//(HAL_READ8(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0) == 0x0) { + + IdleTemp++; + HalDelayUs(1000); + + if (IdleTemp >= 15000) { + DiagPrintf("\n"); + DiagPrintf("Go to sleep ~~~~ \n"); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0,0x12345678); + DiagPrintf("0xf0 : 0x%x\n",HAL_READ32(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0)); + //a33 reg chk + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, 0x80008400); + HalDelayUs(1000); + if ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15)==0){ + RRtemp = ((u8)HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL))+1; + } + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009400|RRtemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + DiagPrintf("a33 0x4 : 0x%x\n",RRtemp); + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, 0x80008500); + HalDelayUs(1000); + if ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)&BIT15)==0){ + DiagPrintf("a33 0x5 before : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL)); + RRtemp = ((u8)HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL))+1; + } + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009500|RRtemp); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + DiagPrintf("a33 0x5 : 0x%x\n",RRtemp); + + ChangeSoCPwrState(7,0xE8800); + } + } + + ////debug + if (PwrAdapter.SleepFlag) { + PwrAdapter.SleepFlag = 0; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x234, 0xdddddddd); + DiagPrintf("0x234 before slp : %x\n", HAL_READ32(SYSTEM_CTRL_BASE,0x234)); + //cal 32k + //En32KCalibration(); + HalDelayUs(1000); + + ChangeSoCPwrState(5,0xb000); + //HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_DSTBY_INFO1, PwrAdapter.SleepFlag); + } + + if (0){//(HAL_READ8(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0) == 0x0) { + + IdleTemp++; + HalDelayUs(1000); + if (IdleTemp > 0xfffff){ + IdleTemp = 0; + __WFI(); + } + } + + if (0){ //((HAL_READ8(SYSTEM_CTRL_BASE,REG_SOC_SYSON_DSTBY_INFO0) == 0x0)) { + IdleTemp++; + HalDelayUs(1000); + if ((IdleTemp > 5000)||(HAL_READ8(SYSTEM_CTRL_BASE,REG_SYS_DSTBY_INFO0+1) == 0x12)){ + + DiagPrintf("\n"); + DiagPrintf("0x20080000 : 0x%x\n", HAL_READ32(0x20080000,0)); + DiagPrintf("0x20080004 : 0x%x\n", HAL_READ32(0x20080000,4)); + DiagPrintf("0x2009F404 : 0x%x\n", HAL_READ32(0x2009F400,4)); + DiagPrintf("0x2009F408 : 0x%x\n", HAL_READ32(0x2009F400,8)); + DiagPrintf("\n"); + + HAL_WRITE32(0x40000000,0x330,0x55559555);//0x55552a2a + //slp pg GPIOD GPIOE + HAL_WRITE32(0x40000000,0x334,0x55555555); + HAL_WRITE32(0x40000000,0x338,0x05555555); + HAL_WRITE32(0x40000000,0x33c,0x55555555); + HAL_WRITE32(0x40000000,0x340,0x55555555); + HAL_WRITE32(0x40000000,0x344,0x55555555); + HAL_WRITE32(0x40000000,0x320,0x0); + + HAL_WRITE32(0x20080000, 0, (HAL_READ32(0x20080000,0)+1)); + HAL_WRITE32(0x20080000, 4, (HAL_READ32(0x20080000,4)+1)); + HAL_WRITE32(0x2009F404, 0, (HAL_READ32(0x2009F400,4)+1)); + HAL_WRITE32(0x2009F408, 0, (HAL_READ32(0x2009F400,8)+1)); + HalDelayUs(10000); + ChangeSoCPwrState(SLPPG, 0xFFFFF); + } + } + //mem test + if (HAL_READ8(0x40000000,0xf1) == 0xaa) { + + CMDTemp[0] = 8; + SOCPSTestApp((VOID*)CMDTemp); + Rtemp = HAL_READ32(0x40080000,0x824); + Rtemp2 = Rtemp; + Rtemp2 = ((Rtemp2 & 0x807fffff) | 0x80000000); + HAL_WRITE32(0x40080000,0x824,Rtemp&0x7fffffff); + HAL_WRITE32(0x40080000,0x824,Rtemp2); + HAL_WRITE32(0x40080000,0x824,(Rtemp|0x80000000)); + Rtemp1 = HAL_READ32(0x40080000,0x820)&BIT8; + if (Rtemp1) { + Rtemp = HAL_READ32(0x40080000,0x8b8)&0xfffff; + } + else { + Rtemp = HAL_READ32(0x40080000,0x8a0)&0xfffff; + } + if(Rtemp== 0x00045678){ + Chktemp = 1; + } + + Chktemp &= SoCPSMEMTestChk(0x20010000,0x20000,0x12345678)&SoCPSMEMTestChk(0x200a0000,0x0FFE0,0x12345678) + &SoCPSMEMTestChk(0x1FFF4000,0x5000,0x12345678); + + if (Chktemp) { + HAL_WRITE32(0x40080000,0x4,(HAL_READ32(0x40080000,0x4)&0xFFFFFFF0)); + HAL_WRITE32(0x40000000,0xfc,(HAL_READ32(0x40000000,0xfc)+1)); + DiagPrintf("run %d times\n", HAL_READ32(0x40000000,0xfc)); + CMDTemp[0] = 1; + CMDTemp[1] = 5; + CMDTemp[2] = 0xff; + SOCPSTestApp((VOID*)CMDTemp); + } + else { + HAL_WRITE32(0x40000000,0xf0,0); + } + + } +} + +//30 +VOID +TimerHandleTset( + IN VOID *Data +) +{ + #if 0 + //static u32 temp = 0; + TIMER_ADAPTER TimerAdapter; + + TimerAdapter.IrqDis = OFF; + //TimerAdapter.IrqHandle = (IRQ_FUN) TimerHandleTset; + TimerAdapter.IrqHandle.IrqFun = (IRQ_FUN) TimerHandleTset; + //DBG_8195A("IrqFun : 0x%x\n", TimerAdapter.IrqHandle.IrqFun); + TimerAdapter.IrqHandle.IrqNum = TIMER2_7_IRQ; + TimerAdapter.IrqHandle.Priority = 0; + TimerAdapter.IrqHandle.Data = NULL; + TimerAdapter.TimerId = 2; + TimerAdapter.TimerIrqPriority = 0; + TimerAdapter.TimerLoadValueUs = 4000; + TimerAdapter.TimerMode = USER_DEFINED; + //temp++; + //DBG_8195A("time : 0x%x\n", temp); + + HalTimerOp.HalTimerInit((VOID*) &TimerAdapter); + #endif + DBG_8195A("<<< time out >>>\n"); +} + +extern IRQ_FUN Timer2To7VectorTable[6]; + +//30 +VOID +InitTimerTest( + //IN VOID *Data + VOID +) +{ + //[0]:type, [1]: timerID, [2]: timerMode, [3]: IrqDIS, [4]:period + TIMER_ADAPTER TimerAdapter; + //u32 *TestParameter; + + //TestParameter = (u32*) Data; + + TimerAdapter.IrqDis = 0; // off :0 + //TimerAdapter.IrqHandle = (IRQ_FUN) TimerHandleTset; + TimerAdapter.IrqHandle.IrqFun = (IRQ_FUN) TimerHandleTset; + //DBG_8195A("IrqFun : 0x%x\n", TimerAdapter.IrqHandle.IrqFun); + TimerAdapter.IrqHandle.IrqNum = TIMER2_7_IRQ; + TimerAdapter.IrqHandle.Priority = 0; + TimerAdapter.IrqHandle.Data = NULL; + TimerAdapter.TimerId = 5; + TimerAdapter.TimerIrqPriority = 0; + TimerAdapter.TimerLoadValueUs = 0x4EC14; + TimerAdapter.TimerMode = 1; // user_define :1 + + + //mer2To7VectorTable[0] = (IRQ_FUN) TimerHandleTset; + + HalTimerOp.HalTimerInit((VOID*) &TimerAdapter); + //mer2To7VectorTable[0] = (IRQ_FUN) TimerHandleTset; + + +} + +VOID +SOCPSTestApp( + VOID *Data +) +{ + u32 *TestParameter; + TestParameter = (u32*) Data; + unsigned int Rtemp, Rtemp1, Rtemp2;//, CalTemp32k, CalTempa33; + static u32 Read32k5 = 0; + static u32 Reada335 = 0; + DiagPrintf("TestParameter[0]: 0x%x\n",TestParameter[0]); + + switch (TestParameter[0]) { + + case 0: + DiagPrintf("SoC PWR Init wlan\n"); + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE,0x214)|BIT16; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x214,Rtemp); + + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE,0x244)|BIT0; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x244,Rtemp); + + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE,0x210)|BIT2; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x210,Rtemp); + + HalDelayUs(100); + + Rtemp = HAL_READ32(WIFI_REG_BASE,0x0)|BIT0; + HAL_WRITE32(WIFI_REG_BASE,0x0,Rtemp); + #if 0 + DiagPrintf("SoC PWR debug setting\n"); + Rtemp = 0; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x33c,Rtemp); + + Rtemp = 0; + HAL_WRITE32(SYSTEM_CTRL_BASE,0x334,Rtemp); + + #if 0 + //en debug + Rtemp = 1;//HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYS_DEBUG_CTRL); + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SOC_SYS_DEBUG_CTRL,Rtemp); + + //debug port sel + Rtemp = 0xf0f10004;//HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_EFUSE_SYSCFG3)|0xf0000000; + HAL_WRITE32(SYSTEM_CTRL_BASE,REG_SOC_EFUSE_SYSCFG3, Rtemp); + #endif + + //cal 32k + //En32KCalibration(); + + //en gpio + GPIO_FCTRL(ON); + SLPCK_GPIO_CCTRL(ON); + ACTCK_GPIO_CCTRL(ON); + + //DiagPrintf("debug sel 0x2C : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_EFUSE_SYSCFG3)); + //DiagPrintf("debug EN 0xA0 : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYS_DEBUG_CTRL)); + //DiagPrintf("PULL CTRL 0x33c: 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,0x33C)); + //DiagPrintf("debug port : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,REG_SOC_SYS_DEBUG_REG)); + DiagPrintf("0x90 : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,0x90)); + #endif + break; + + case 1: + DiagPrintf("SoC PWR TEST : Enter = %d, Period = %d\n",TestParameter[1], TestParameter[2]); + Rtemp = HalTimerOp.HalTimerReadCount(1); + //GPIO + //HAL_WRITE32(0x40001000,0x4,0x4000000); + + //SIC EN + //HAL_WRITE32(0x40000000,0x8,0x81000010); + //HAL_WRITE32(0x40000000,0xA4,0x00000001); + + //Wait for LogUart print out + while(1) { + HalDelayUs(100); + if (HAL_READ8(LOG_UART_REG_BASE,0x14)&BIT6){ + break; + } + } + + #if 0 + + HAL_WRITE32(0x40000000,0x330,0x55559555);//0x55552a2a + //slp pg GPIOD GPIOE + HAL_WRITE32(0x40000000,0x334,0x55555555); + HAL_WRITE32(0x40000000,0x338,0x05555555); + HAL_WRITE32(0x40000000,0x33c,0x55555555); + HAL_WRITE32(0x40000000,0x340,0x55555555); + HAL_WRITE32(0x40000000,0x344,0x55555555); + HAL_WRITE32(0x40000000,0x320,0x0); + #endif + + ChangeSoCPwrState(TestParameter[1], TestParameter[2]); + + Rtemp2 = HalTimerOp.HalTimerReadCount(1); + DiagPrintf("before : %x\n", Rtemp); + DiagPrintf("after : %x\n", Rtemp2); + DiagPrintf("period : %d\n", Rtemp-Rtemp2); + DiagPrintf("0x90 : 0x%x\n", HAL_READ32(SYSTEM_CTRL_BASE,0x90)); + break; + + case 2: + #if 1 + if (HAL_READ32(0x40000000,0xf8) == 0x11) { + HAL_WRITE32(0x40000000,0x320,0x7ff); + } + HAL_WRITE32(0x40000000,0x320,0x7ff); + HAL_WRITE32(0x40000000,0x330,0x5565A555); + //slp pg GPIOD GPIOE + HAL_WRITE32(0x40000000,0x334,0x55555555); + HAL_WRITE32(0x40000000,0x338,0x05555555); + HAL_WRITE32(0x40000000,0x33c,0x55555555); + HAL_WRITE32(0x40000000,0x340,0x55555555); + HAL_WRITE32(0x40000000,0x344,0x55555555); + HAL_WRITE32(0x40000000,0x348,0x55555555); + HAL_WRITE32(0x40000000,0x320,0x0); + + if (HAL_READ32(0x40000000,0xf4) == 0x11) { + HAL_WRITE32(0x40000000,0x8,0x80000011); + } + HAL_WRITE32(0x40000000,0x8,0x80000011); + #endif + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X120, TestParameter[1]); + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X124, TestParameter[2]); + + if (TestParameter[4] == 0xff) { + //SIC EN + HAL_WRITE32(0x40000000,0x8,0x81000010); + HAL_WRITE32(0x40000000,0xA4,0x00000001); + } + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, TestParameter[3]); + #if 0 + //clear isr + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SOC_SYS_ANA_TIM_CTRL)& 0xffff7fff)); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYS_ANA_TIM_CTRL, Rtemp); + + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_SLP_WAKE_EVENT_STATUS0); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SOC_SYSON_SLP_WAKE_EVENT_STATUS0, Rtemp); + #endif + break; + + case 3: + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X120, 0x74000e00); + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X124, 2); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, TestParameter[1]); + #if 0 + { + u32 targetunit = 0; + + //cal a33 + Rtemp = CLKCal(A33CK); + + targetunit = (TestParameter[1]/3*250)/Rtemp*50; + if (targetunit > 0x7fffff) { + targetunit = 0x7fffff; + } + + DBG_8195A("targeunit = 0x%08x\n",targetunit); + + targetunit = (50*TestParameter[1]/Rtemp)*250/3; + if (targetunit > 0x7fffff) { + targetunit = 0x7fffff; + } + + DBG_8195A("targeunit = 0x%08x\n",targetunit); + + + } + #endif + #if 0 + //cal a33 + Rtemp = CLKCal(A33CK); + Rtemp1 = (((((TestParameter[1] & 0x0FFFFFFF)<<4)/Rtemp)*20)-1); + DiagPrintf("Rtemp : 0x%x\n", Rtemp); + DiagPrintf("Vendor 0xA0 : 0x%x\n", HAL_READ32(VENDOR_REG_BASE,0xA0)); + DiagPrintf("way1 : 0x%x\n", Rtemp1); + Rtemp2 = (((((TestParameter[1] & 0x0FFFFFFF))/Rtemp)*320)-1); + DiagPrintf("way2 : 0x%x\n", Rtemp2); + + Rtemp = Rtemp1/6; + DiagPrintf("Rtemp1 : %d\n", Rtemp); + Rtemp = 0x7fffffff; + DiagPrintf("Rtemp1 : %d\n", Rtemp); + #endif + break; + + case 4: + DiagPrintf("set timer\n"); + SetA33Timer(TestParameter[1]); + Rtemp = HalTimerOp.HalTimerReadCount(1); + DiagPrintf("32k timer : 0x%x\n", Rtemp); + break; + + case 5: + DiagPrintf("read timer\n"); + Reada335 = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CAL_CTRL); + DiagPrintf("a33 timer : 0x%x\n", Reada335); + Read32k5 = HalTimerOp.HalTimerReadCount(1); + DiagPrintf("32k timer : 0x%x\n", Read32k5); + break; + + case 6: + #if 0 + DiagPrintf("interval cal\n"); + Rtemp1 = HAL_READ32(SYSTEM_CTRL_BASE, REG_SOC_SYS_DSLP_TIM_CAL_CTRL); + Rtemp2 = HalTimerOp.HalTimerReadCount(1); + DiagPrintf("Reada335 : 0x%x\n", Reada335); + DiagPrintf("Read32k5 : 0x%x\n", Read32k5); + DiagPrintf("a33 timer : 0x%x\n", Rtemp1); + DiagPrintf("32k timer : 0x%x\n", Rtemp2); + CalTemp32k = (Read32k5 - Rtemp2); + CalTempa33 = (((Reada335 - Rtemp1)*((HAL_READ32(VENDOR_REG_BASE, REG_VDR_ANACK_CAL_CTRL) & 0x3FFF)+1))/5); + DiagPrintf("a33 timer interval : 0x%x\n", CalTempa33); + DiagPrintf("32k timer interval : 0x%x\n", CalTemp32k); + Read32k5 = Rtemp2; + Reada335 = Rtemp1; + #endif + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) & 0xffff7000) | 0x7ff); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + //0x4000_0090[15] = 1'b1 => Enable timer + Rtemp = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL) | 0x00008000; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_ANA_TIM_CTRL, Rtemp); + + Rtemp = 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_SLP_WAKE_EVENT_MSK0, Rtemp); +#if 0 + HAL_WRITE32(0x40000000,0x330,0x55559555);//0x55552a2a + HAL_WRITE32(0x40000000,0x2C0,0x100001); + //slp pg GPIOD GPIOE + HAL_WRITE32(0x40000000,0x334,0x55555555); + HAL_WRITE32(0x40000000,0x338,0x05555555); + HAL_WRITE32(0x40000000,0x33c,0x55555555); + HAL_WRITE32(0x40000000,0x340,0x55555555); + HAL_WRITE32(0x40000000,0x344,0x55555555); + HAL_WRITE32(0x40000000,0x320,0x0); +#endif + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X120, TestParameter[1]); + HAL_WRITE32(SYSTEM_CTRL_BASE, 0X124, TestParameter[2]); + + if (TestParameter[4] == 0xff) { + //SIC EN + HAL_WRITE32(0x40000000,0x8,0x81000010); + HAL_WRITE32(0x40000000,0xA4,0x00000001); + } + + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, TestParameter[3]); + break; + + case 7: + { + u32 Rtemp = 0; + u32 UTemp = 0; + u32 MaxTemp = 0; + u32 Reada335 = 0; + + //2 Deep Sleep mode: + //3 2.1 Set TU timer timescale + + //3 2.2 Configure deep sleep timer: + //2.2.1 Enable REGU access interface 0x4000_0094[31] = 1 + Rtemp = (HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) | 0x80000000); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + DiagPrintf("SDuration : 0x%x\n", TestParameter[1]); + + //3 2.2.2 Initialize deep sleep counter + //2.2.2.0 0x4000_0094[15:0] = 16'hD300 => Disable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D300); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + //2.2.2.1 0x4000_0094[15:0] = 16'h9008 => set counter[7:0] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009000 | ((u8)TestParameter[1])); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.2 0x4000_0094[15:0] = 16'h9100 => set counter[15:8] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009100 | ((u8)(TestParameter[1] >> 8))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.3 0x4000_0094[15:0] = 16'h9200 => set counter[22:16] + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x00009200 | ((u8)(TestParameter[1] >> 16))); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + //2.2.2.4 0x4000_0094[15:0] = 16'hD380 => Enable deep sleep counter + Rtemp = ((HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL) & 0xffff0000) | 0x0000D380); + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CTRL, Rtemp); + + HalDelayUs(1000); + Reada335 = HAL_READ32(SYSTEM_CTRL_BASE, REG_SYS_DSLP_TIM_CAL_CTRL); + DiagPrintf("a33 timer : 0x%x\n", Reada335); + + HalDelayUs(8000); + + //3 2.2.3 + //2.3 Enable low power mode: 0x4000_0118[0] = 1'b1; + Rtemp = 0x00000001;//HAL_READ32(SYSTEM_CTRL_BASE, REG_SYSON_PWRMGT_CTRL) | 0x00000001; + HAL_WRITE32(SYSTEM_CTRL_BASE, REG_SYS_PWRMGT_CTRL, Rtemp); + + //2.4 Wait CHIP enter deep sleep mode + //2.5 Wait deep sleep counter timeout + //__WFI(); + + DiagPrintf("YOU CAN'T SEE ME ~~~~!!!!!!!!!!!!!!!!!!!~~~~~~~~~~~~~~~!!!!!!!!!!"); + } + break; + + case 8: + DiagPrintf("enable wifi\n"); + + Rtemp = HAL_READ32(0x40000000,0x214)|0x10000; + HAL_WRITE32(0x40000000,0x214,Rtemp); + Rtemp = HAL_READ32(0x40000000,0x244)|0x1; + HAL_WRITE32(0x40000000,0x244,Rtemp); + Rtemp = HAL_READ32(0x40000000,0x210)|0x4; + HAL_WRITE32(0x40000000,0x210,Rtemp); + + Rtemp = HAL_READ32(0x40080000,0x0)&0xFFFFFFDF; + HAL_WRITE32(0x40080000,0x0,Rtemp); + Rtemp = HAL_READ32(0x40080000,0x4)|0x1; + HAL_WRITE32(0x40080000,0x4,Rtemp); + Rtemp = HAL_READ32(0x40080000,0x20)|0x1; + HAL_WRITE32(0x40080000,0x20,Rtemp); + while( (HAL_READ32(0x40080000,0x20)&BIT0)!=0); + + Rtemp = HAL_READ32(0x40080000,0x4)|0x30000; + HAL_WRITE32(0x40080000,0x4,Rtemp); + Rtemp = HAL_READ32(0x40080000,0x4)|0x7000000; + HAL_WRITE32(0x40080000,0x4,Rtemp); + Rtemp = HAL_READ32(0x40080000,0x50)&0xFFFFFF00; + HAL_WRITE32(0x40080000,0x50,Rtemp); + break; + + case 9: + #if 0 + PwrAdapter.CPURegbackup[13] = 0x12340; + PwrAdapter.CPUPSP = PwrAdapter.CPURegbackup[13]; + + asm volatile + ( + + " ldr r3, pxCPUPSPConst23 \n" /* Restore the context. */ + "MOV %0, r3\n" + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + "MOV %1, r1\n" + " ldr r0, [r1] \n" + "MOV %2, r0\n" + " .align 2 \n" + "pxCPUPSPConst23: .word PwrAdapter.CPUPSP \n" + :"=r"(PwrAdapter.CPURegbackup[0]),"=r"(PwrAdapter.CPURegbackup[1]),"=r"(PwrAdapter.CPURegbackup[2]),"=r"(PwrAdapter.CPURegbackup[3]) + :"r"(PwrAdapter.CPUPSP) + :"memory" + ); + PrintCPU(); + #endif + break; + + case 10: + Rtemp = HAL_READ32(0x40080000,0x824); + Rtemp2 = Rtemp; + Rtemp2 = Rtemp2 & 0x807fffff | (TestParameter[1]<<23) | 0x80000000; + HAL_WRITE32(0x40080000,0x824,Rtemp&0x7fffffff); + HAL_WRITE32(0x40080000,0x824,Rtemp2); + HAL_WRITE32(0x40080000,0x824,Rtemp|0x80000000); + Rtemp1 = HAL_READ32(0x40080000,0x820)&BIT8; + if (Rtemp1) { + Rtemp = HAL_READ32(0x40080000,0x8b8)&0xfffff; + } + else { + Rtemp = HAL_READ32(0x40080000,0x8a0)&0xfffff; + } + DiagPrintf("rf offset: 0x%x, 0x%x\n", TestParameter[1], Rtemp); + break; + + case 11://addr [1]; date [2] + TestParameter[1] &= 0x3f; + Rtemp = (TestParameter[1]<<20)|(TestParameter[2]&0x000fffff)&0x0fffffff; + HAL_WRITE32(0x40080000,0x840,Rtemp); + + //SoCPWRIdleTaskHandle(); + break; + + case 12: + SoCPSMEMTestInit(TestParameter[1],TestParameter[2],TestParameter[3]); + break; + + case 13: + Rtemp = SoCPSMEMTestChk(TestParameter[1],TestParameter[2],TestParameter[3]); + break; + + case 14: + HAL_WRITE32(0x40000000,TestParameter[1],0x12345678); + DiagPrintf("w32: 0x%x\n", HAL_READ32(0x40000000,TestParameter[1])); + HAL_WRITE32(0x40000000,TestParameter[1],0); + HAL_WRITE16(0x40000000,TestParameter[1],0x1234); + DiagPrintf("w16: 0x%x\n", HAL_READ32(0x40000000,TestParameter[1])); + HAL_WRITE32(0x40000000,TestParameter[1],0); + HAL_WRITE8(0x40000000,TestParameter[1],0x12); + DiagPrintf("w8: 0x%x\n", HAL_READ32(0x40000000,TestParameter[1])); + HAL_WRITE32(0x40000000,TestParameter[1],0x12345678); + DiagPrintf("R32: 0x%x\n", HAL_READ32(0x40000000,TestParameter[1])); + DiagPrintf("R16: 0x%x\n", HAL_READ16(0x40000000,TestParameter[1])); + DiagPrintf("R8: 0x%x\n", HAL_READ8(0x40000000,TestParameter[1])); + Rtemp = ((HAL_READ32(0x40000000,0xf4))?1:0); + DiagPrintf("R: 0x%x\n", Rtemp); + break; + + case 15: + asm volatile + ( + "MRS R0, BASEPRI\n" + "MOV %0, R0\n" + :"=r"(Rtemp) + ::"memory" + ); + DiagPrintf("basepri: 0x%x\n", Rtemp); + break; + case 16: + HalDelayUs(10000000); + DSleep_GPIO(); + break; + case 17: + DSleep_Timer(TestParameter[1]); + break; + case 18: + DiagPrintf("WDG CAL\n"); + { + u8 CountId; + u16 DivFactor; + u32 CountTemp; + u32 CountProcess = 0; + u32 DivFacProcess = 0; + u32 MinPeriodTemp = 0xFFFFFFFF; + u32 PeriodTemp = 0; + + DBG_8195A(" Period = %d\n", TestParameter[1]); + + for (CountId = 0; CountId < 12; CountId++) { + CountTemp = ((0x00000001 << (CountId+1))-1); + DivFactor = (u16)((100*TestParameter[1])/(CountTemp*3)); + + if (DivFactor > 0) { + PeriodTemp = 3*(DivFactor+1)*CountTemp; + DBG_8195A("PeriodTemp = %d\n", PeriodTemp); + if ((100*TestParameter[1]) PeriodTemp) { + MinPeriodTemp = PeriodTemp; + CountProcess = CountTemp; + DivFacProcess = DivFactor; + } + } + } + } + DBG_8195A("MinPeriodTemp = %d\n", MinPeriodTemp); + DBG_8195A("WdgScalar = 0x%08x\n", DivFacProcess); + DBG_8195A("WdgCunLimit = 0x%08x\n", CountProcess); + } + break; + + case 19: + DBG_8195A("DeepStandby~~~\n"); + DeepStandby(TestParameter[1],TestParameter[2],TestParameter[3]); + break; + + case 20: + DBG_8195A("SleepCG~~~\n"); + if (TestParameter[1]&BIT1){ + InitTimerTest(); + } + SleepCG(TestParameter[1],TestParameter[2]); + break; + + default: + break; + } + + +} +#endif //CONFIG_SOC_PS_VERIFY +#endif //CONFIG_SOC_PS_MODULE + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_ssi.c b/component/soc/realtek/8195a/fwlib/src/hal_ssi.c new file mode 100644 index 0000000..6bfe332 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_ssi.c @@ -0,0 +1,281 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +#include "hal_ssi.h" + +const HAL_GDMA_CHNL Ssi2_TX_GDMA_Chnl_Option[] = { + {0,4,GDMA0_CHANNEL4_IRQ,0}, + {0,5,GDMA0_CHANNEL5_IRQ,0}, + {0,3,GDMA0_CHANNEL3_IRQ,0}, + {0,0,GDMA0_CHANNEL0_IRQ,0}, + {0,1,GDMA0_CHANNEL1_IRQ,0}, + {0,2,GDMA0_CHANNEL2_IRQ,0}, + + {0xff,0,0,0} // end +}; + +const HAL_GDMA_CHNL Ssi2_RX_GDMA_Chnl_Option[] = { + {1,4,GDMA1_CHANNEL4_IRQ,0}, + {1,5,GDMA1_CHANNEL5_IRQ,0}, + {1,3,GDMA1_CHANNEL3_IRQ,0}, + {1,0,GDMA1_CHANNEL0_IRQ,0}, + {1,1,GDMA1_CHANNEL1_IRQ,0}, + {1,2,GDMA1_CHANNEL2_IRQ,0}, + + {0xff,0,0,0} // end +}; + +//TODO: Load default Setting: It should be loaded from external setting file. +const DW_SSI_DEFAULT_SETTING SpiDefaultSetting = +{ + .RxCompCallback = NULL, + .RxCompCbPara = NULL, + .RxData = NULL, + .TxCompCallback = NULL, + .TxCompCbPara = NULL, + .TxData = NULL, + .DmaRxDataLevel = 7, // RX FIFO stored bytes > (DMARDLR(7) + 1) then trigger DMA transfer + .DmaTxDataLevel = 48, // TX FIFO free space > (FIFO_SPACE(64)-DMATDLR(48)) then trigger DMA transfer + .InterruptPriority = 0x20, + .RxLength = 0, + .RxLengthRemainder = 0, + .RxThresholdLevel = 7, // if number of entries in th RX FIFO >= (RxThresholdLevel+1), RX interrupt asserted + .TxLength = 0, + .TxThresholdLevel = 8, // if number of entries in th TX FIFO <= TxThresholdLevel, TX interrupt asserted + .SlaveSelectEnable = 0, + .ClockDivider = SSI_CLK_SPI0_2/1000000, // SCLK=1M + .DataFrameNumber = 0, + .ControlFrameSize = CFS_1_BIT, + .DataFrameFormat = FRF_MOTOROLA_SPI, + .DataFrameSize = DFS_8_BITS, + .DmaControl = 0, // default DMA is disable + .InterruptMask = 0x0, + .MicrowireDirection = MW_DIRECTION_MASTER_TO_SLAVE, + .MicrowireHandshaking = MW_HANDSHAKE_DISABLE, + .MicrowireTransferMode = MW_TMOD_NONSEQUENTIAL, + .SclkPhase = SCPH_TOGGLES_AT_START, + .SclkPolarity = SCPOL_INACTIVE_IS_HIGH, + .SlaveOutputEnable = SLV_TXD_ENABLE, // Slave + .TransferMode = TMOD_TR, + .TransferMechanism = SSI_DTM_INTERRUPT +}; + +extern HAL_Status HalSsiInitRtl8195a_Patch(VOID *Adaptor); +extern HAL_Status HalSsiPinmuxEnableRtl8195a_Patch(VOID *Adaptor); +extern HAL_Status HalSsiPinmuxDisableRtl8195a(VOID *Adaptor); +extern HAL_Status HalSsiIntReadRtl8195a(VOID *Adapter, VOID *RxData, u32 Length); +extern HAL_Status HalSsiIntWriteRtl8195a(VOID *Adapter, u8 *pTxData, u32 Length); +extern VOID HalSsiSetSclkRtl8195a(VOID *Adapter, u32 ClkRate); +#ifdef CONFIG_GDMA_EN +extern VOID HalSsiDmaInitRtl8195a(VOID *Adapter); +#endif + +VOID HalSsiOpInit(VOID *Adaptor) +{ + PHAL_SSI_OP pHalSsiOp = (PHAL_SSI_OP) Adaptor; + +// pHalSsiOp->HalSsiPinmuxEnable = HalSsiPinmuxEnableRtl8195a; + pHalSsiOp->HalSsiPinmuxEnable = HalSsiPinmuxEnableRtl8195a_Patch; + pHalSsiOp->HalSsiPinmuxDisable = HalSsiPinmuxDisableRtl8195a; + pHalSsiOp->HalSsiEnable = HalSsiEnableRtl8195a; + pHalSsiOp->HalSsiDisable = HalSsiDisableRtl8195a; +// pHalSsiOp->HalSsiInit = HalSsiInitRtl8195a; + pHalSsiOp->HalSsiInit = HalSsiInitRtl8195a_Patch; + pHalSsiOp->HalSsiSetSclkPolarity = HalSsiSetSclkPolarityRtl8195a; + pHalSsiOp->HalSsiSetSclkPhase = HalSsiSetSclkPhaseRtl8195a; + pHalSsiOp->HalSsiWrite = HalSsiWriteRtl8195a; + pHalSsiOp->HalSsiRead = HalSsiReadRtl8195a; + pHalSsiOp->HalSsiGetRxFifoLevel = HalSsiGetRxFifoLevelRtl8195a; + pHalSsiOp->HalSsiGetTxFifoLevel = HalSsiGetTxFifoLevelRtl8195a; + pHalSsiOp->HalSsiGetStatus = HalSsiGetStatusRtl8195a; + pHalSsiOp->HalSsiGetInterruptStatus = HalSsiGetInterruptStatusRtl8195a; + pHalSsiOp->HalSsiLoadSetting = HalSsiLoadSettingRtl8195a; + pHalSsiOp->HalSsiSetInterruptMask = HalSsiSetInterruptMaskRtl8195a; + pHalSsiOp->HalSsiGetInterruptMask = HalSsiGetInterruptMaskRtl8195a; + pHalSsiOp->HalSsiSetDeviceRole = HalSsiSetDeviceRoleRtl8195a; + pHalSsiOp->HalSsiWriteable = HalSsiWriteableRtl8195a; + pHalSsiOp->HalSsiReadable = HalSsiReadableRtl8195a; + pHalSsiOp->HalSsiBusy = HalSsiBusyRtl8195a; + pHalSsiOp->HalSsiInterruptEnable = HalSsiInterruptEnableRtl8195a; + pHalSsiOp->HalSsiInterruptDisable = HalSsiInterruptDisableRtl8195a; +// pHalSsiOp->HalSsiReadInterrupt = HalSsiReadInterruptRtl8195a; + pHalSsiOp->HalSsiReadInterrupt = HalSsiIntReadRtl8195a; + pHalSsiOp->HalSsiSetRxFifoThresholdLevel = HalSsiSetRxFifoThresholdLevelRtl8195a; + pHalSsiOp->HalSsiSetTxFifoThresholdLevel = HalSsiSetTxFifoThresholdLevelRtl8195a; +// pHalSsiOp->HalSsiWriteInterrupt = HalSsiWriteInterruptRtl8195a; + pHalSsiOp->HalSsiWriteInterrupt = HalSsiIntWriteRtl8195a; + pHalSsiOp->HalSsiGetRawInterruptStatus = HalSsiGetRawInterruptStatusRtl8195a; + pHalSsiOp->HalSsiGetSlaveEnableRegister = HalSsiGetSlaveEnableRegisterRtl8195a; + pHalSsiOp->HalSsiSetSlaveEnableRegister = HalSsiSetSlaveEnableRegisterRtl8195a; +} + + +#ifdef CONFIG_GDMA_EN + +HAL_Status +HalSsiTxGdmaInit( + IN PHAL_SSI_OP pHalSsiOp, + IN PHAL_SSI_ADAPTOR pHalSsiAdapter +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PSSI_DMA_CONFIG pDmaConfig; + u8 gdma_idx; + u8 gdma_chnum; + HAL_GDMA_CHNL *pgdma_chnl; + PHAL_GDMA_OP pHalGdmaOp; + + if ((NULL == pHalSsiOp) || (NULL == pHalSsiAdapter)) { + return HAL_ERR_PARA; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + + // Load default setting + HalSsiTxGdmaLoadDefRtl8195a((void*)pHalSsiAdapter); + + // Start to patch the default setting + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + if (HalGdmaChnlRegister(pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum) != HAL_OK) { + // The default GDMA Channel is not available, try others + if (pHalSsiAdapter->Index == 2) { + // SSI2 TX Only can use GDMA 0 + pgdma_chnl = HalGdmaChnlAlloc((HAL_GDMA_CHNL*)Ssi2_TX_GDMA_Chnl_Option); + } + else { + pgdma_chnl = HalGdmaChnlAlloc(NULL); + } + + if (pgdma_chnl == NULL) { + // No Available DMA channel + return HAL_BUSY; + } + else { + pHalGdmaAdapter->GdmaIndex = pgdma_chnl->GdmaIndx; + pHalGdmaAdapter->ChNum = pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->ChEn = 0x0101 << pgdma_chnl->GdmaChnl; + pDmaConfig->TxGdmaIrqHandle.IrqNum = pgdma_chnl->IrqNum; + } + } + + DBG_SSI_INFO("HalSsiTxGdmaInit: GdmaIndex=%d ChNum=%d \r\n", pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum); + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + pHalGdmaOp->HalGdmaOnOff((VOID*)(pHalGdmaAdapter)); + pHalGdmaOp->HalGdmaChIsrEnAndDis((VOID*)(pHalGdmaAdapter)); + + HalSsiDmaInit(pHalSsiAdapter); + InterruptRegister(&pDmaConfig->TxGdmaIrqHandle); + InterruptEn(&pDmaConfig->TxGdmaIrqHandle); + + return HAL_OK; +} + +VOID +HalSsiTxGdmaDeInit( + IN PHAL_SSI_ADAPTOR pHalSsiAdapter +) +{ + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + HAL_GDMA_CHNL GdmaChnl; + + if (NULL == pHalSsiAdapter) { + return; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pTxHalGdmaAdapter; + GdmaChnl.GdmaIndx = pHalGdmaAdapter->GdmaIndex; + GdmaChnl.GdmaChnl = pHalGdmaAdapter->ChNum; + GdmaChnl.IrqNum = pDmaConfig->TxGdmaIrqHandle.IrqNum; + HalGdmaChnlFree(&GdmaChnl); +} + + +HAL_Status +HalSsiRxGdmaInit( + IN PHAL_SSI_OP pHalSsiOp, + IN PHAL_SSI_ADAPTOR pHalSsiAdapter +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + PSSI_DMA_CONFIG pDmaConfig; + HAL_GDMA_CHNL *pgdma_chnl; + PHAL_GDMA_OP pHalGdmaOp; + + if ((NULL == pHalSsiOp) || (NULL == pHalSsiAdapter)) { + return HAL_ERR_PARA; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + // Load default setting + HalSsiRxGdmaLoadDefRtl8195a((void*)pHalSsiAdapter); + + // Start to patch the default setting + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + if (HalGdmaChnlRegister(pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum) != HAL_OK) { + // The default GDMA Channel is not available, try others + if (pHalSsiAdapter->Index == 2) { + // SSI2 RX Only can use GDMA 1 + pgdma_chnl = HalGdmaChnlAlloc((HAL_GDMA_CHNL*)Ssi2_RX_GDMA_Chnl_Option); + } + else { + pgdma_chnl = HalGdmaChnlAlloc(NULL); + } + + if (pgdma_chnl == NULL) { + // No Available DMA channel + return HAL_BUSY; + } + else { + pHalGdmaAdapter->GdmaIndex = pgdma_chnl->GdmaIndx; + pHalGdmaAdapter->ChNum = pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->ChEn = 0x0101 << pgdma_chnl->GdmaChnl; + pDmaConfig->RxGdmaIrqHandle.IrqNum = pgdma_chnl->IrqNum; + } + } + + DBG_SSI_INFO("HalSsiRxGdmaInit: GdmaIndex=%d ChNum=%d \r\n", pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum); + pHalGdmaOp = (PHAL_GDMA_OP)pDmaConfig->pHalGdmaOp; + pHalGdmaOp->HalGdmaOnOff((VOID*)(pHalGdmaAdapter)); + pHalGdmaOp->HalGdmaChIsrEnAndDis((VOID*)(pHalGdmaAdapter)); + + HalSsiDmaInit(pHalSsiAdapter); + InterruptRegister(&pDmaConfig->RxGdmaIrqHandle); + InterruptEn(&pDmaConfig->RxGdmaIrqHandle); + + return HAL_OK; +} + +VOID +HalSsiRxGdmaDeInit( + IN PHAL_SSI_ADAPTOR pHalSsiAdapter +) +{ + PSSI_DMA_CONFIG pDmaConfig; + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + HAL_GDMA_CHNL GdmaChnl; + + if (NULL == pHalSsiAdapter) { + return; + } + + pDmaConfig = &pHalSsiAdapter->DmaConfig; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pDmaConfig->pRxHalGdmaAdapter; + GdmaChnl.GdmaIndx = pHalGdmaAdapter->GdmaIndex; + GdmaChnl.GdmaChnl = pHalGdmaAdapter->ChNum; + GdmaChnl.IrqNum = pDmaConfig->RxGdmaIrqHandle.IrqNum; + HalGdmaChnlFree(&GdmaChnl); +} + +#endif // end of "#ifdef CONFIG_GDMA_EN" + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_timer.c b/component/soc/realtek/8195a/fwlib/src/hal_timer.c new file mode 100644 index 0000000..617b593 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_timer.c @@ -0,0 +1,32 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" + +VOID +HalTimerOpInit_Patch( + IN VOID *Data +) +{ + PHAL_TIMER_OP pHalTimerOp = (PHAL_TIMER_OP) Data; + + pHalTimerOp->HalGetTimerId = HalGetTimerIdRtl8195a; + pHalTimerOp->HalTimerInit = (BOOL (*)(void*))HalTimerInitRtl8195a_Patch; +#ifdef CONFIG_CHIP_C_CUT + pHalTimerOp->HalTimerReadCount = HalTimerReadCountRtl8195aV02; +#else + pHalTimerOp->HalTimerReadCount = HalTimerReadCountRtl8195a_Patch; +#endif + pHalTimerOp->HalTimerIrqClear = HalTimerIrqClearRtl8195a; + pHalTimerOp->HalTimerDis = HalTimerDisRtl8195a_Patch; + pHalTimerOp->HalTimerEn = HalTimerEnRtl8195a_Patch; + pHalTimerOp->HalTimerDumpReg = HalTimerDumpRegRtl8195a; +} + diff --git a/component/soc/realtek/8195a/fwlib/src/hal_uart.c b/component/soc/realtek/8195a/fwlib/src/hal_uart.c new file mode 100644 index 0000000..aac0619 --- /dev/null +++ b/component/soc/realtek/8195a/fwlib/src/hal_uart.c @@ -0,0 +1,332 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + + +#include "rtl8195a.h" +#include "rtl8195a_uart.h" +#include "hal_uart.h" +#include "hal_gdma.h" + +extern u32 _UartIrqHandle(VOID *Data); + +#if (CONFIG_CHIP_A_CUT | CONFIG_CHIP_B_CUT) +HAL_Status +HalRuartInitRtl8195a_Patch( + IN VOID *Data ///< RUART Adapter + ); +#else +extern _LONG_CALL_ HAL_Status +HalRuartInitRtl8195aV02( + IN VOID *Data ///< RUART Adapter + ); +#endif + +const HAL_GDMA_CHNL Uart2_TX_GDMA_Chnl_Option[] = { + {0,0,GDMA0_CHANNEL0_IRQ,0}, + {0,1,GDMA0_CHANNEL1_IRQ,0}, + {0,2,GDMA0_CHANNEL2_IRQ,0}, + {0,3,GDMA0_CHANNEL3_IRQ,0}, + {0,4,GDMA0_CHANNEL4_IRQ,0}, + {0,5,GDMA0_CHANNEL5_IRQ,0}, + + {0xff,0,0,0} // end +}; + +const HAL_GDMA_CHNL Uart2_RX_GDMA_Chnl_Option[] = { + {1,0,GDMA1_CHANNEL0_IRQ,0}, + {1,1,GDMA1_CHANNEL1_IRQ,0}, + {1,2,GDMA1_CHANNEL2_IRQ,0}, + {1,3,GDMA1_CHANNEL3_IRQ,0}, + {1,4,GDMA1_CHANNEL4_IRQ,0}, + {1,5,GDMA1_CHANNEL5_IRQ,0}, + + {0xff,0,0,0} // end +}; + +VOID +HalRuartOpInit( + IN VOID *Data +) +{ + PHAL_RUART_OP pHalRuartOp = (PHAL_RUART_OP) Data; + + pHalRuartOp->HalRuartAdapterLoadDef = HalRuartAdapterLoadDefRtl8195a; + pHalRuartOp->HalRuartTxGdmaLoadDef = HalRuartTxGdmaLoadDefRtl8195a; + pHalRuartOp->HalRuartRxGdmaLoadDef = HalRuartRxGdmaLoadDefRtl8195a; + pHalRuartOp->HalRuartResetRxFifo = HalRuartResetRxFifoRtl8195a; +#if (CONFIG_CHIP_A_CUT | CONFIG_CHIP_B_CUT) + pHalRuartOp->HalRuartInit = HalRuartInitRtl8195a_Patch; // Hardware Init ROM code patch +#else + pHalRuartOp->HalRuartInit = HalRuartInitRtl8195aV02; // Hardware Init +#endif + pHalRuartOp->HalRuartDeInit = HalRuartDeInitRtl8195a; // Hardware Init + pHalRuartOp->HalRuartPutC = HalRuartPutCRtl8195a; // Send a byte + pHalRuartOp->HalRuartSend = HalRuartSendRtl8195a; // Polling mode Tx + pHalRuartOp->HalRuartIntSend = HalRuartIntSendRtl8195a; // Interrupt mode Tx + pHalRuartOp->HalRuartDmaSend = HalRuartDmaSendRtl8195a; // DMA mode Tx + pHalRuartOp->HalRuartStopSend = HalRuartStopSendRtl8195a; // Stop non-blocking TX + pHalRuartOp->HalRuartGetC = HalRuartGetCRtl8195a; // get a byte + pHalRuartOp->HalRuartRecv = HalRuartRecvRtl8195a; // Polling mode Rx + pHalRuartOp->HalRuartIntRecv = HalRuartIntRecvRtl8195a; // Interrupt mode Rx + pHalRuartOp->HalRuartDmaRecv = HalRuartDmaRecvRtl8195a; // DMA mode Rx + pHalRuartOp->HalRuartStopRecv = HalRuartStopRecvRtl8195a; // Stop non-blocking Rx + pHalRuartOp->HalRuartGetIMR = HalRuartGetIMRRtl8195a; + pHalRuartOp->HalRuartSetIMR = HalRuartSetIMRRtl8195a; + pHalRuartOp->HalRuartGetDebugValue = HalRuartGetDebugValueRtl8195a; + pHalRuartOp->HalRuartDmaInit = HalRuartDmaInitRtl8195a; + pHalRuartOp->HalRuartRTSCtrl = HalRuartRTSCtrlRtl8195a; + pHalRuartOp->HalRuartRegIrq = HalRuartRegIrqRtl8195a; + pHalRuartOp->HalRuartIntEnable = HalRuartIntEnableRtl8195a; + pHalRuartOp->HalRuartIntDisable = HalRuartIntDisableRtl8195a; +} + + + +/** + * Load UART HAL default setting + * + * Call this function to load the default setting for UART HAL adapter + * + * + */ +VOID +HalRuartAdapterInit( + PRUART_ADAPTER pRuartAdapter, + u8 UartIdx +) +{ + PHAL_RUART_OP pHalRuartOp; + PHAL_RUART_ADAPTER pHalRuartAdapter; + + if (NULL == pRuartAdapter) { + return; + } + + pHalRuartOp = pRuartAdapter->pHalRuartOp; + pHalRuartAdapter = pRuartAdapter->pHalRuartAdapter; + + if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter)) { + return; + } + + // Load default setting + if (pHalRuartOp->HalRuartAdapterLoadDef != NULL) { + pHalRuartOp->HalRuartAdapterLoadDef (pHalRuartAdapter, UartIdx); + } + else { + // Initial your UART HAL adapter here + } + + // Start to modify the defualt setting + pHalRuartAdapter->PinmuxSelect = RUART0_MUX_TO_GPIOC; + pHalRuartAdapter->BaudRate = 38400; + +// pHalRuartAdapter->IrqHandle.IrqFun = (IRQ_FUN)_UartIrqHandle; +// pHalRuartAdapter->IrqHandle.Data = (void *)pHalRuartAdapter; +// pHalRuartAdapter->IrqHandle.Priority = 0x20; + + // Register IRQ + InterruptRegister(&pHalRuartAdapter->IrqHandle); + +} + +/** + * Load UART HAL GDMA default setting + * + * Call this function to load the default setting for UART GDMA + * + * + */ +HAL_Status +HalRuartTxGdmaInit( + PHAL_RUART_OP pHalRuartOp, + PHAL_RUART_ADAPTER pHalRuartAdapter, + PUART_DMA_CONFIG pUartGdmaConfig +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + u8 gdma_idx; + u8 gdma_chnum; + HAL_GDMA_CHNL *pgdma_chnl; + + if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter) || (NULL == pUartGdmaConfig)) { + return HAL_ERR_PARA; + } + + // Load default setting + if (pHalRuartOp->HalRuartTxGdmaLoadDef != NULL) { + pHalRuartOp->HalRuartTxGdmaLoadDef (pHalRuartAdapter, pUartGdmaConfig); + } + else { + // Initial your GDMA setting here + } + + // Start to patch the default setting + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pUartGdmaConfig->pTxHalGdmaAdapter; + if (HalGdmaChnlRegister(pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum) != HAL_OK) { + // The default GDMA Channel is not available, try others + if (pHalRuartAdapter->UartIndex == 2) { + // UART2 TX Only can use GDMA 0 + pgdma_chnl = HalGdmaChnlAlloc((HAL_GDMA_CHNL*)Uart2_TX_GDMA_Chnl_Option); + } + else { + pgdma_chnl = HalGdmaChnlAlloc(NULL); + } + + if (pgdma_chnl == NULL) { + // No Available DMA channel + return HAL_BUSY; + } + else { + pHalGdmaAdapter->GdmaIndex = pgdma_chnl->GdmaIndx; + pHalGdmaAdapter->ChNum = pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->ChEn = 0x0101 << pgdma_chnl->GdmaChnl; + pUartGdmaConfig->TxGdmaIrqHandle.IrqNum = pgdma_chnl->IrqNum; + } + } + + // User can assign a Interrupt Handler here +// pUartGdmaConfig->TxGdmaIrqHandle.Data = pHalRuartAdapter; +// pUartGdmaConfig->TxGdmaIrqHandle.IrqFun = (IRQ_FUN)_UartTxDmaIrqHandle +// pUartGdmaConfig->TxGdmaIrqHandle.Priority = 0x20; + + pHalRuartOp->HalRuartDmaInit (pHalRuartAdapter); + InterruptRegister(&pUartGdmaConfig->TxGdmaIrqHandle); + InterruptEn(&pUartGdmaConfig->TxGdmaIrqHandle); + + return HAL_OK; +} + +VOID +HalRuartTxGdmaDeInit( + PUART_DMA_CONFIG pUartGdmaConfig +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + HAL_GDMA_CHNL GdmaChnl; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pUartGdmaConfig->pTxHalGdmaAdapter; + GdmaChnl.GdmaIndx = pHalGdmaAdapter->GdmaIndex; + GdmaChnl.GdmaChnl = pHalGdmaAdapter->ChNum; + GdmaChnl.IrqNum = pUartGdmaConfig->TxGdmaIrqHandle.IrqNum; + HalGdmaChnlFree(&GdmaChnl); +} + +/** + * Load UART HAL GDMA default setting + * + * Call this function to load the default setting for UART GDMA + * + * + */ +HAL_Status +HalRuartRxGdmaInit( + PHAL_RUART_OP pHalRuartOp, + PHAL_RUART_ADAPTER pHalRuartAdapter, + PUART_DMA_CONFIG pUartGdmaConfig +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + HAL_GDMA_CHNL *pgdma_chnl; + + if ((NULL == pHalRuartOp) || (NULL == pHalRuartAdapter) || (NULL == pUartGdmaConfig)) { + return HAL_ERR_PARA; + } + + // Load default setting + if (pHalRuartOp->HalRuartRxGdmaLoadDef != NULL) { + pHalRuartOp->HalRuartRxGdmaLoadDef (pHalRuartAdapter, pUartGdmaConfig); + } + else { + // Initial your GDMA setting here + } + + // Start to patch the default setting + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pUartGdmaConfig->pRxHalGdmaAdapter; + if (HalGdmaChnlRegister(pHalGdmaAdapter->GdmaIndex, pHalGdmaAdapter->ChNum) != HAL_OK) { + // The default GDMA Channel is not available, try others + if (pHalRuartAdapter->UartIndex == 2) { + // UART2 RX Only can use GDMA 1 + pgdma_chnl = HalGdmaChnlAlloc((HAL_GDMA_CHNL*)Uart2_RX_GDMA_Chnl_Option); + } + else { + pgdma_chnl = HalGdmaChnlAlloc(NULL); + } + + if (pgdma_chnl == NULL) { + // No Available DMA channel + return HAL_BUSY; + } + else { + pHalGdmaAdapter->GdmaIndex = pgdma_chnl->GdmaIndx; + pHalGdmaAdapter->ChNum = pgdma_chnl->GdmaChnl; + pHalGdmaAdapter->ChEn = 0x0101 << pgdma_chnl->GdmaChnl; + pUartGdmaConfig->RxGdmaIrqHandle.IrqNum = pgdma_chnl->IrqNum; + } + } + +// pUartGdmaConfig->RxGdmaIrqHandle.Data = pHalRuartAdapter; +// pUartGdmaConfig->RxGdmaIrqHandle.IrqFun = (IRQ_FUN)_UartTxDmaIrqHandle; +// pUartGdmaConfig->RxGdmaIrqHandle.Priority = 0x20; + + pHalRuartOp->HalRuartDmaInit (pHalRuartAdapter); + InterruptRegister(&pUartGdmaConfig->RxGdmaIrqHandle); + InterruptEn(&pUartGdmaConfig->RxGdmaIrqHandle); + + return HAL_OK; +} + +VOID +HalRuartRxGdmaDeInit( + PUART_DMA_CONFIG pUartGdmaConfig +) +{ + PHAL_GDMA_ADAPTER pHalGdmaAdapter; + HAL_GDMA_CHNL GdmaChnl; + + pHalGdmaAdapter = (PHAL_GDMA_ADAPTER)pUartGdmaConfig->pRxHalGdmaAdapter; + GdmaChnl.GdmaIndx = pHalGdmaAdapter->GdmaIndex; + GdmaChnl.GdmaChnl = pHalGdmaAdapter->ChNum; + GdmaChnl.IrqNum = pUartGdmaConfig->RxGdmaIrqHandle.IrqNum; + HalGdmaChnlFree(&GdmaChnl); +} + +/** + * Hook a RX indication callback + * + * To hook a callback function which will be called when a got a RX byte + * + * + */ +VOID +HalRuartRxIndHook( + PRUART_ADAPTER pRuartAdapter, + VOID *pCallback, + VOID *pPara +) +{ + PHAL_RUART_ADAPTER pHalRuartAdapter = pRuartAdapter->pHalRuartAdapter; + + pHalRuartAdapter->RxDRCallback = (void (*)(void*))pCallback; + pHalRuartAdapter->RxDRCbPara = pPara; + + // enable RX data ready interrupt + pHalRuartAdapter->Interrupts |= RUART_IER_ERBI | RUART_IER_ELSI; + pRuartAdapter->pHalRuartOp->HalRuartSetIMR(pHalRuartAdapter); +} + + +HAL_Status +HalRuartResetTxFifo( + IN VOID *Data +) +{ + return (HalRuartResetTxFifoRtl8195a(Data)); +} + diff --git a/component/soc/realtek/8195a/misc/bsp/image/ram_1.p.bin b/component/soc/realtek/8195a/misc/bsp/image/ram_1.p.bin new file mode 100644 index 0000000000000000000000000000000000000000..4c52ae270e00fb2e55ecfb48fcaf3a8df440d25e GIT binary patch literal 13052 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 literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_mdns.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_mdns.a new file mode 100644 index 0000000000000000000000000000000000000000..e6e94efbcc7b7cf8b560c00898877ada6180a3f2 GIT binary patch literal 665098 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

xwjJzs# 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 BjDi`x9)prgY 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 literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_platform.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_platform.a new file mode 100644 index 0000000000000000000000000000000000000000..236a172ab1ccee84abc7add7f669914d1b1fcc25 GIT binary patch literal 480232 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

kxCH4(dt@aknhy#yv?eQi6H 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 literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_rtlstd.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_rtlstd.a new file mode 100644 index 0000000000000000000000000000000000000000..78710703d10887b4b1e114413ac9b2fe2c58af14 GIT binary patch literal 138684 zcmeEv31A$>m3DRaj5IneTb3*_N@uw~e_3qmeYS#+EdSX5@o# zUoqhbNjMU+5Rw3a5RL>$AmL2dz$TCo2;?FO*$qd?v5?I{fZ+doRn^^9rI8t9vuo#1 z+oS6D>Z^BDzpAe8uI`$0WNfgr=cxR7VSS4<)Ysx)-GZiYxRz&$zl{wIoQl-eHHs4L z`HZn<#sYU8_}>GUGPaLDt7ba;U;F)`iCO+{Fx?yNO>}fd6a9%aBdRan*V!}Z?e-0L zy92x2-M-}ZxVxW6j9Wp$_0AWP33Z8dGRD>Q zx;sNI1UmvVtW-i{1T9Bj&-XLKL$)q6C^z9SrN&o>TEIySf^ zo~}vG%cok8$2t33 zS*)7*6l1UK=KNp-3uC96%^_?}ss{}q9qYi}u0D}Ux-;3=7w=EAnnY}{W-#5C?27lY znyz@q&=y2@7+5})jJ zcl%*yGaZ&#J6xdTHT)D1w6Z;a3L?NKbw$joH5$@40*2GX_&D_jpfjyTN9 zU-f7Q*c*F#lspFdEJ+8DS}eqH`tdf0<*`qC6k`q>#Xhgl4EZ;KF7|kN>~?RT@MY`) z&>!`9dF(N7pYkWLXF*Ra)~+dN(#+wARAem#qWZufY3tl!(G z{3doL=q`_!$1d{rDSt7$3UrUh%VXDj`;@hn>P^fIdgbuhH_` z*<#QaDETY2{5abTdbg4v)$+HpBq9g+1*-x z0sD?d7qVY!^vBtMYjiGqN27CCp5se**cGf&qfckEHF^wNpwXjQt45cy6Eyn6EUwXK zu(U>(v-321JiAh(E7<2X`b>70Mo(l9X>=8PTB9eiUuyJO?DrZymA$3WQ&=H&L~?W3 zG&Vt_&t}ycJ&QGJ^h~x=qpR6wjXsBUY4qW2P^0It3p9Ez`-DcH%Wlx<2)je0YuO_j zUB|w!(dV%jG`f+!rqT1+I~u)!E7#~F*i4OH#F{kvNVY;#QYux^dM zjHNZYkDag4z3gg@?q@e?^yTbsjUHs*(&!Jd=QKLSexuPJV}H`<9n1=;{%>bvHF_tT zrqE*mJ<_1p8+4aJ?=a|(8uaxB{S||L%%Fd4(61TvKMcAg&-ah)pKj1i2EEFlqXs=_ z&=(r?zbW)+=)1+B?^9^9=NZs+ili~%7>oyB1bsXh)Qu7SJJ2U7^j|@rs?ZkhrNk7v z2sDk6-16n1e+^lSq`YaMM`8aAN!NnD1pAvMeI)3FQvMjweG0t+^oJDsRM0~T-2-}; zLZ?8}n9Q~3Y|!T_^d+D#ROo9!U#8HX2Ys zMwgPfQ9<_@+UO2V8?Kq?M~u#zPF!%|LL{2nl}g9^Qq!m7N^S^O^^_UyiKXP_7hQ5J zbT5qfHB4kan~uvl%7`{-Idu`qp0BeTB^#-@qTzDtUGxH-?V_95^e$Nt25@zeT6YI! z6;TbYs7Rg8cV$KDHU4}LuRAc`!|NV2dU)N1Mh~z1u)xDNX?hoUc)d!STznlIRQzgk z@l>-4f1!uh{axtcb$=tZUIE?VNUc{w_c#*qN&um#jzmCH-1wLBI4b7FJW87^9#8)9 z5f{~w22U+r&3d&+HxfL(@^U=V;Mv}r?2OcTt`j(diuNz7ImZ)5k|SP)^Riy`@Vq)- zFhmihi>lM$b)LywluuDvs4>cV5kRWyz2nQvdM=W|^~3bmYkKQ_P2|4k9xppgZ@s3s zLD%c$QCX^1-R1^OuYZWR&3;8v*rgy%kcfRH?^%f!w$>#aP z^v>7xHtKq{P-z^dw^7sEsO$CeNN?jXy^WgQ1-f2n@@-x)Oz#3uHr3SvU9V3;?}A}^ z7ifB$biGBH9@sU4;~QmbpH^N=inDr?ZfD-s#5-9PPI>sB>yTCsM+>S$AB;rwRGh(=pmT3Vv3S~qQ4)e>!4v%Ix=O?YlHoc(}& zU@*Bg-kElX;!!*q=!iw4+heuS_|6zTHfw7+0p8Bt(6Xwfd1Fi4a(ZymwzgwyTX^nV z-pG;SxnUyKzqmJ%PWQ$^cMc3KUX~oh7bkY(r;_&>xg%C#k;~MBnP*3#Bq%gUQkop78~qq zYhAOft+Q?UDhOQ01xD8-2hq*c^h^!UZT%qYp{AxS(cg(m_@K2w5&mbsaH2rKMO9xa zk!tITZ%=f_|BnsIa3zOY>K*Dj2n|CNZt28DX1Z5Y<3VL8T~Ght#+E#kWQclUe$drY zCgX4g)`PLh!Ciy6k#TSuh=$QdEdw3w+<0#{f4tw;P}5XXPhH-@)0oZBd~dtwb|-em zyV^v}ukASgMIQ*b=ymwCj>LD2K$IKBiv#z}8+%E)+_-J`~ygP=OWT~1S zW^?wRaycJk@vfoHw8u_x?xr4^kLQD5v3nDJ2~qlL61rh0hULjFR2;4wwVXw}@JOI@ z8wupd-d)>cy<8F}9}nWnVGDAd8^7|(t-)JzI%26rXEeQQAWopb6~ukRiN^X<3DCj& z?*e^=73~uCd%zr?@t|ceLWy`h-aUYl0&t6yPV~h+veeZhawql2ci>R8%yI_yDu6WN++q_P_pRuciz4=c!$Npcm^e=VFaw~ zuXZzEMP;~X@wyttpMAbTP)LIY+HAxO3k2=Xp4cFs{s!-{>>T=3Y)hmSV<2?v>aJK4a%v82IFV6LWrY@1eXW@W&Qb26JB^&dv1aUKGjsA1ipF z6MVobp|Y%m3>uBuTqT4U{VSV|Aqlrz*d6`0#m0~*mm2+mMbI%M(c?YKd8K~_?;$*N z73IeAU0};-;@0!lIL69CFb{h+D;tBoui4leKM8wOFT|}D`IyaDhrN5Oak;GQNU=Ab zmC*z(@XA@)I%ZfG+Fnb#2{86QaDsBe{?Pmzb9Nfxza0CqWdP8wA0<4@4S(Gq_jrYYdE4ROM7dT^iZs*Y8AUZJ2%}vL* zBr(3Vr`1+G3l(k#x|SH8q1w|LgxRUC1Ufj(i?^pWRh(~=BY2eCKST~5S+N@obWJ#K zfLy{$2r{}2jO9FkV15ewD=RJowi{0JYO<%T683Dz{h@y3ujBRBxjo%6)D3b|xxmGibDZeQ9Q;5UE-D46-SGAj|KsDh!W*KLNKa4Mk3XPz_O=>Svk2KMQ2PE z5ygz_6s7`J{02^S_j8JDC9*4(#1ruTE*wc`+BsI`3%oMB`+4V2EL*Y?sc7fU7&sT$ zto$;PE^GM;hu3*OO?-B1Ac2+Oy6~D=|bc38}nAuoPodIf^4rZw06~q_L z;uKY)DpQHmW#rQ#0&Yd?sutG6fxP8#->IHNRZ|RvDqe$_?%o)jrV8d1?hk?}uDGlK z8Ypf=MfY}eOBLl6Z&Pq5liL!6tg848k664)fcmWXEeP?>fIV$`#a}6a=uHHBT6M+S zAjBID_O!#f{GkqH)N&hn^fz%M`SCxp;$6pz4vElRQt@XH{eoD=i}q5{URd!nNYaj7ee+-m*z&YhaRqoo{X?mE7ni^bQSXP-Iz5Ky#r%Kp0u|qZ)L06`h`xAKR7wzMI;+3(g zokG&@sJ3a`%P|*^agRC&O^mKuyX?5=hL&Z=Z`jznxh1*@XYUEQMH45Yu_Ab*Y4+xr zvW=BfC+YSd>+*RFExZ;Wx)%O#Iepa};{hl}2+Q74yglpT^ zzP_h+(u-^VY!BT2CdP4xkA1j(5)0c`1g~J%P~Pot7PG)LQ*K+jg56rXg36b?VU2p= z;Y+4O+Q)u{UCXA79;m$Q!B=0mPJJ`Q5!qXZp+3*fAJee2EsN1Nd8A|IWL934(={~kbkRT z@*Zpsv(G3VQ^ax^hWK`_6AFZap`1`|C=|*I<%bG#3q!?az`7evk8CbI>vC&(Df@mc z2pebl21+q5pe|Y@%&p?#T&Y{2BJ=hQOT93Ie9MWV%OJ2R= znut7P!3j=HanLRu2k$M5UipzB9QYtj+V9IxI*9I8fSu4U@kd^^%U58n`kXP|u~YCk z%AEa0I8P!zj?$FgOz%)(!%zZOTa4WyX17v)DbZhGY&@mFl~W3E1*LZ~Hi=T;rcw&r zOiF>9MJddqP^H(g4yP2jd6a&Fu{uhDpHC_9jg%HKwt&)aGIj)|z#l~^aLtqgx0F&C zwT#kR7(0eiydJiaQd~#2QhF<6t0}#bu{D&!=(Uu7iLs58Ud7lZN+)2JFr~L)b}*$^ zGj=kiRSfS4Vf!+MR+L`DSUaU~HbyDlxQz)D7~Ap9h80sZ!=Lk6SICPy$7>>Dg8Ib&Y~2Rcs8Z?GQ4|%?K-@j zMCoCe^-Jm3FuRx1>*ZT+sO(FK{yJkHqx7@55T>+-S1Dun^D1EMbKGUd&=tT%#_r>$ zFm?l1%?}b(G4>4(W%L>v9nWmL9)8TJJhn6NYc4%|Y3Z&DaxcueDEN`UZs&5lrS!DH z(p^hS&wA|Nzw`JLyhjSj9tpAd3zV^_FppLnRx8~S&k(ubzefiZv)NI$Z9O?;RTYPv zfaN^#7glzdW1+Vky`dyO5ZqPuKdEV~EwLlj$@%43|W3RSbA{rN$=cw8{x7)8ga<&Z=M1_UwiZvH^%OcrYM}YueQ7Wno1O)MM#~BY zae-(Zof9HquMh(--&;%a3+Tm!w}|d$R&#FdcyzzxVMPH=?y{DSE{%d{#IJA*{!n3b zWh{&zwtZ=evqcg9cQgKC6wvH9#mk))rKh!*?rJVQlNus#hp>c?3}E0BC;;`?=&Z3n z?QoUN=GeCVyCJKrkhjBE{C2oJCE8*9aJL=Sov`lZiH~i(eDxBo>Fp{m%*!7*@~!pO zSlQ(4?3>&ptrcy~ajL1!ZKgIiyL@uV^l-_H34Exs0SDud zXkip-?#5dfy_3URnEZ@|)fOD3H83h68d%%-3a^3j=G0bw7&WJU{6up)O*AJu)A8mM z70n4JLEfC&MRUSfLN=$EXiiUqQMd_)P;(lKUlo4X_N6(6c?t1n5fU_<=F*+w%yxO; zq|!6cjCd<5l&uKfe-Aj(igK_wVk;65Al~DYcC5`N+O~84cB@1*r9IS?=!E7rq}D&V z4XHMmGs-`ARlQRbnD-SiE+5SKaPeX5trFRgoZ$^gp0RN5%F)hUbPaj1HKf<6Ayx5) zG#L%)zO&tybTqZ3hlz9^W#uKa!@M0uP}he0^%f; zu9`%xrU^gMYPO43L*riFYIcZL(?c{r>+BTGga(#{_0U7jqy#@Y^I+STW`c39w_)sw zW^(!k!3zTyIUlhvx7L+@cxfr4^NqL^EtXBCh>yo*Qwg9{rlvw--=l#v-~p0t2d>#} z<(Ket%_skg29h7ZxunuR$K>Uo&T^d3ZFg8nPJZJp>#bth6avGWg5wS2 z3#kbdE|hHnaY`4OwCzhb7!VU63<~uJ{Hg3w; z!5g<*_F+88Z=fL0%Reipta1k~D`i{;4w5@Amh*AMrCoqKK$cYsM-PeamkyV^@xU7g z%c=X7pBb~RvO=S+w_ONQDp8yo?2PgU{KyZ_!b3G?tt-ZDsH?1b^C+UM^~HGPu_fI@ ztVKuD#e=os00h-|8B_>Ty6ZvVB>wJOQ03f;pbGP#y2%Zyf`c=tuJ(h9zLauk234UF zRQQsdb**Kb)Ok?#MJQ+DBpFV9bwtjq<>dO}exi;z;(&!xg$yN}`}Zf5a_>MW&Elc- zB{!7D9Gs!_89$Ut4%twmp4IK2@Fha)vl;s*OiI96@Uzy0iPUwWZ$gK~88s0ZPUxOE z+~CSPID_jZ zKe%!a+2G3egNr88Shr^EuKFlr!UP#q7)RY|O`R&oQ8@42YR#Q{zyfKq45Uf>7fAUJ zB9QPz6b~HkcLOPOa0b$s{6L}_J~Vr&cVRidy~y7KXVx7QNOTv`-Nd*YL#R8f36+#V zXFUv|?ywG@OP7Kc?&t6KMcot`JmLKdo`P>7c&d2tJmdyX?!g&6clyEe7HaX(44!|$ zayNMJQD^J!5rgM$YjT)J%evwO;i{`eH*^4EXPS&1JUQN(m489dYXFL;WcMDR@E9nTYP@Dv`L z9nbxK@a#EcJ09E&^Ew{7T6@sSEg62+5?5;vTIJq(i(johXw5lr0W?bn5ayxmPw!Lw zLj=$S9zajI0Tetq1Lz??fc|jE2GHyNnTy`Vu)alSE*cBFn;7=cl!9+rlc$L8=K!3n zX3KEFh-rVqWz_RtfAfqRE(d9U^KCy|=%VG&9OxAJ{S9A{!g`#phF88bS(ORgtz*IF6* zJZaU;6C)prr6;XLiw;;U&6Tl)`G@-xOQT;zEY0Spu@~G}8hdb_#-8$Hi6;IYn*CCl z+Aq;S=!Y5mB{2~Cp;bA_?V0#M=!e$4IvG#mO-&SE+TmEVyc?Q z)UVx`Dm^%3>KQ+#at_&;qQ`;qqMlFNd_MCKcWHw`d}^LcCXOxa2G;+;dhtWLbf zBSmB#fH6}<#t|O>?@t_+zUIZz%LjQJ{rI0Pj<7{H7fqze*`0A!& z`Ak-JW`I(fBM592rD^;ecIIx2c6Q1PO3x7cAtZdG9df{8Cs0CeiN8l81(Omg<8OY6 zO?ru5U@W$Yl95Urua{`!C61>=FVQYJ7F&lszR|>Ff!{bBPd3quJr4W$n`hCEE-oJ& znuunx&6M~~ZWT;Q^b(JOBLn>4{}#%q$6+l6HGi*?e`{j8@&cL~FLW_Lu|=sGFI3Gu zUT6n%pm-orO4W`6-sCw7G&+c=N=hi(-zalAeux4}^vso#qa4$tJp9JtcoIeu_%TJw zH#*Ql!|}sQ5F&%Kd@Z3yo_A)LQkt9#pU8i{k*4YR!E8z>+uz7b;vmyy;-RF(Zyb&% z3q`CQPl+y!B&Zfn<@A~5l+G0UV=1MQun>R6MrDLh&j_DIGl}`;v$UEIAX61a*njb| zs{@Bkcg7YtqecgulJQQUIN$^;0)c>2HpV%%(kTuHoKa;?VUd$t=-{oa{M>+35ONBT zthAk3R=}xq0Di#E;k?2Sh`)nFn^QC);4BN%UbH0ObYjmLH`bXp&Y4TQ(;>QclG8%_ zM^D6`^74RFf%m{Fr#S`rPI;*_K1gK?^PHktFpdkVn(CCy2soo>I)N%DZ?ZEALdJ|D zZ2@QElt2KlHeuFhYbn0=!*|Ls?>5Ms(4tXCj9NJAwKbz!OKdcTVv~nEnPY>D%8f>X zQk6xZ>I$3?tvgczqRb_7M{!&b32-?5IiVS$(t?tlWe^;2Nt_j}lAM><u5&u-CH_4Raz~?VE z9x2`(uov?eW0{Ta_Flq4ZkSU182c4|_v5FZKPZRWbhNMYC*aw+jJ+V!(@?oYcgU36 z0Giu?^l8FD*N-S&!q{&q1x@$)?IrjO6s4f)!I8ZL6TC?dXqxs%^h8Ndk@Q?i(_15y zf25?BNP4xTF=LzOpG;}9tb?Vp4wm_KFqt7AKe1BK{Yd%1(0%uiGI$j%_p9K?;MsYM zFHcG7aijw2TAA`H0DY^({eV)q^em++ z^lVAkgi9jL#up;eo6(3#JkOu){+9G^hOV*+##ggA*8$|!9d>P zRx`d5DH;4nk^`C_I?(tS1E=|+15I5I2?OGnm7N7nj|_|bR-koQA69UAY= z3ee)1V1VWx8y=158-GrwAmyeb<)$Omq7M^mxXCbu*FDlv(zM&DJO+DTp>(5fl3%y< zN~~*=oO2JXFa#qK&7g78Af((Nq}(8+nnB0=&EapP7zPDZgEmWpPRL{sJwj0p!kur= zpc4&)%B4X_xj{&|K}a=&PVx_~+cmje60f0?r9r1;GH9$}5N@A(2DSMH{RB*Vg6u<) za)Xd^gOF-{=BbSRH{nq;cz=YabRdZ42Aw7iiuwlm4Sce05T7*aS=8=Z#Dl0xRuNKe z5mIguQmu+&euMstw9HX0>W~(7`WE>?RG?c#GYvh1x(tKh2ajr`+#safAf%c>@!>(l ztEO8TvL%xtbi%nGKi>C=nQERPJ%%A+X$Vqo2vTkcQq7QrZ^&Pna*Vc0L$+lyX@*Rfh9KpJ zAmxT2)eQO2@aDt~fseeSNo6vGqDie0wHYB?G(%=eLy&Sqka9ziYK9E?Ll17ic4@$l zOa{E69};?;?;Vnzz5%=)SIc&clpBDQ8-P@6$Gdz3-h?n}zjQ<2YsaTcgFfsVggBR{ zH9DUtHNqbldlsExScHq!7pNpsZV^&$5mL>fGvC9av!q35XJwHZE$0{(9WE_G$}K|5 zEkdeUbgmpCpl;qF50JWNQhw0RlNOzy$s&3kOsgX`Y%cIE;(c9>3>&1}BBb0Rq*~Zq z=$|lYf{=H#E|Qjf#J9xn<7fbdGxG>?!kVGsW5t%2E zfOD-YM?@A#WT8Z?8WC9}kr2pbt{V}F@f){utVCd#rTgxAgn<=Dj+clNM?{uOWQ9bu zjflihgv+dyi1rbYRT7Cd!0qcA5s5yH%bY9`JtHE+5;;X8dPhX!?8jwJlL$ol2(>g_ zB4BIilO?h%o- z5*d+*OGiYa$@BWGmk6}b%g}P@R)gElMiGtng}`L?$7McRmD{>_{&JZQ9DY z^HZ>ECt4%NBjxx_(tfl(&PU4mXnVZA(9$>`Dd(f5aX#7==Og9(cF9L8;(Vl>-zE7l zg7cAbevjmbB_Ap0qlIw!Xb&8Zl=IOZIDfk2BjxZ4<#PX59l~TkTiZa3>1UT+`;sf`2N`d=6rNF&R`M|wODdfFQ zeBk~}DR6Y@MK~Icae4IEwA6K)Cd$Kde<(n-LtL~7%-wu(}Rv2~Q*h!5XU z{wEnbka&57;C5WYR0-Ky@s(KN96Y1v|yOh!!P#=_%^Y~r?Hrx~Dsrm<*^ke|W@=p<(nTN`ZOS*{4f<#io=8={8N9$l*$OF=2b#;n2X?_>VpXW zUYT)}06hY9Qu@#-<&&_465P zF+rej!H>5EnRhjC6dzl5*A2CkiUjLkg0IgR{Fk_L(~)8Ctb`_(X% z_(-s7-vtZMOTjp@kGD;vWS{BplMBV8cT=S4Z}lSI-!ci#cm%j7Wxkr2eq4GKV~?o-xo`Th z?r1#m&cctIm*8xYL7wTyUx9P|z@VFNnf%CU78iOF;w7dZ&jI&sFW28O{rJ>UaW~#a zh&}UoJhe=I2G#(YeoR^J`!QW&Gx-q#?oLA%k)|In0{2h`oax8)XykM=-rP(-e!2n= zqf8KJ(~q)ad_QJOd?r6;0JkxNJkyVzz~MwcY%~2BzfyeE(gd1*)Zhe=jUQWqbK|N; z3eDul>A+><$5X&%<45nY7)xYvJa!$IogeoC=lbEEax(ex@4#i_NBOGk{P;R>^t`~_ z%;WLB)!F%xixaZzN7G)8$9Ukf@nb7+*&L6&wT#hY26Hq0s9u+yA6>w?eoze4mg#t; zfy>5^$AQbnkM8vtvu5GPhc;yA$L+wmexNJlTP8mq1TGstN&w2nk2`_eBlW3?c|7jG zz$}|~{u*$uALwfMmdTH|fXl{@)yHS&$M1oAS9L({n|{2uIXgdQp5Xg|rpLETel!A? zjUN{Sm(B60J~6xFF$;r|Y>r1KaPILy$d2eo3b<_i_zrN{_z^!D*92L#kDgPq^W#R~ zTt9FKNA%+!;Ii=}w=FwAZU*iRsZUMJxVq-l?EH8ZIM)xD%C}5$^^=M{OIe%y@@Q2$N62^`SCDtt{+?X;>Qnw%f^qXsElmUR4vhiapaM}2g*OQ$erHSnPSPPtcJkopdBMMwLetZ?UZ2VY{gHMmo z%XhEOEzXi90Pz++dcti?YOB znKnvDftu&ydue`^R$WkE^sTsxA7}ga5D_OW6+sRm-%W!GbvTQjlhu}l6R#9 zX3~2Ua6gf_3I%8C9XBMN%1MZqn0gli=jy#ik!Skb4BQjQC;KKTI8$%lcK(<_BK*YE zI|n#d@3o3NQ*S+Rw;-SNPE&BE-k$?EMdquCsrMa4?{$hiQ*U4gV>ck5^j0f4Q}2zy zxpqkyPfWd!1LyjCgCft=`vc&5kWYH&DmYW`f}PpMT zEcO0Y(TfLnd?Pzdy>BRb7brMW?@7C|^Y?V%Tz_v>z7M||w{qkP)js?!u+pOSBy=MTIO*~(x=)F^sXZm{+aP7#axWcVezGd<^_~GpI z&IFFilf17Aahx{w)&SQoak$0Dw@i93&rOB^?9;r8~;7q+=1umO*^dm*@ql!FJ?+c1vT%z$UQ(P@SD?7d2z`6cDrpPn(_5Q}1VhD@Q)*MU&zi zl{0a7pX1|l1&qsUwe)f4_kbHC^VNi2oKnYszW~nF`y&Y?y{5ds0GExt+;e?-&+kRv zWZ<%q7g6N>WH0hsvy^v=BJXE=k(bI+-i3<17xp6Wx-8}0smS}~UgSNVrM#ai@?PAF zyf?Cx=bYys-~Zf;yh`A*IlglhdB53IJbX!eJ}Ewvy`_{k@u&)$lI2s zyfYPff8L9{tFn~$MMd6U_ag7%Eam-Bk@x0amb#v}KYP2d)-*Hh$v?gpF{feOD>+CUR6} zdDkiOsuY|l?}nw z&QyObz?DdObM_+dctu`B!I}1*s>q|`LR+Te(Wl636tIlPcSw;pU%{FFUZ}`BLcy8# zU8cxeq~J_>*D3OrC^%EzO^Upu6`U#WUPWGuf-~hksK{Hc;7oZ>De{h0aHhQH6nU)* z&Xo6(B5$pNGv)n3k+(*{nezUj$lIjgOnJeJ{P^3b;7oaCz)>7gJ32|hnerwo@=jE6 zro6d|ywen%DQ~_a?^Fe6%4<>Nbt*Vh-fBf&hk`TZovO&|QE;ZbxFQda*!Y&Iy$vby z`V^cg?@UEruYxn}yG)TcsNhU_S1IyvOPz0-?7K;kw?o01@@`k;ZC7xgG|n>*0>@|V z1LIx~k>2Zo!+&op+Rdl8d$hZAn;~x&aE~C9;*Y-MKpV-+pH~~!wn#&LE&kOlXbOi# zmiXJ)(7G)2R>qw<|VSKwF9z}jve2_(>%U887jjn6iuxaInmgeQd7*YP!Dy}n?j&*Ju zO2xK#s=5bbeIN$oz0wZJAPZH)2tYhlLi2{zHL-MhFwrrTj>9mCPj;&EdWjS{gR%ZC zg!7fT9FgA_>lB^~GS<;C7~ihK)!c!hj((V~iSv2B`HF)XnbK%p`>vBFPgFh?hqNui z3K#2fC_uDLjg$a;gGTns_Qsx1Nh*NU!V4Z>P;wmv?^iTBkKL!yrTlv~)gCX;cpR1| zJ`of74Znol3;F?99P;Tk7L|S!^dn0CcR@d<(9eQ?LZM#({iH&_1o~-({v&Aj z77*FtReD?z`i(9=P?m#8FvE@=0Xi|9ts?<`gL!&#`r5c^fKCRI?>}HLQv9D|NX!h?Ky@EZX(JR@1YV4UMj5do+3$8%-UN+#C$zCTa9McDP2@ zu%k4(maWq0li5ic-Nq6c-N1HebUnLRqvx}0H2O4lqed@eU)AU)_LxR5V$W!Fl>JJh zk79q&=*4W0Mjy?Jf^Kb4e6+I(8oi8FYxGjKNTZjtl^Q*YZPw@#)}_(KT-j`?(nfov z&3zQ?(({!3L8Uxh((hL2(V(w1=vx(<@b`eG_8!7-H1vH7H1!WI{R7a{Ke+TSWO;N; zz=zncL4O+i)A1XF@^6EtJZg4C7w1sDiQya3Q$W+DeZI_}51KBeUHnSWbO}l42*RHX zI-%tEg1!*+SebtoXw0T!6C}-+FKX*Z4YYM&RDlm(r`m9ej>l5*wk`4g_+YHJZF_Bf z+xGgJ`dAwVVgt#348__~$xh&6z3KQi;`Sx`+r+19y6B6aZTM~JO{O3#-P;sdI6u~g z@e=(gm*+$WOgXKqsFeUm26kQ0zF$7Qm^MW)XBmTND+e{ZcRgj z%a7=MS4ji~75sb;U$65$JOB#5(ZkPY(@{IzuLv9yJ1|o0$f}BZwNvMMTL;59rp|St z4ypym74Fqooh!M{m0agau7ibi?EG} z*}+AjZhTRmZ$ea)XC;ZMhgMxwy=OIvs`m_mRZU(@N>QFx64f+JRFfvE$rlB51I#cK zt5!Gp1c9MQ2RC>XLRL0-KEld{o`b^5g`O2;<@{lXVZL@4=BtNcp=R>@VOGr7teCG^ z;nfvsZ5*bxQPbL}Y4xP?qQkT{dimsgqopca#s?Fz-gQIi&JD7M zL>r+VG(I@kpJancwWmb4*%uq|z72+N8%3kIzy&l)*K&eN^eaSiC@m=JJ?T0&npvXS zA$Ov~jx()~?#5+nT4yCvxOCm6^LkV9_%@xD9KctqG+8)Xbm^Q_$DmPa$6z8I$3xua z4dLcB8(YK6<}I5uC%g=2okVYZFuZv|O?_=meN9tsUBkQ+>g&Q-iXn`v>HqUHSa|O8 z@UpdQRGZ{&rOE2KkW_- zCbz~r)9wj3ieugpi$u4_YNPR;vA%)ccw5T}sE4^5T2{3*Z)|B>PPazd)^==d3(uX) z`yiwUg+#1>ac?4>?u~=)92i>ceH=bKmss6kt-%SNO5sEu?M(J}C$@yAE?d;LZZNq8 z$1}VFMJ+%<@cl7rJgG>kTC8R%f=#(TS?wC+b+LrqgnJ#~2pPh&Pi^S$k!+nv}M?`jh@ zzqaRe3~$6q1}Big*x)WkXA)MLOIN@}_=^V(!F>D`4;&7}sfz8N5Y#`;cl$jk$#CA- zZ<$JeaUf-7YTh5Te7F1fp!HB3eo#8zyD=Ov)gR;`Qf0%X40nDGR0Y0=rexo~)K?b6 zYHG^(d>T3&bLh&fD=#|t3&A3uw>;h*8|qD`YI>N>*?-FAd<>eqhC0(8JHfe|dgxln z2ft!tDX@eneKiT)uoL6wWLKh}9&c(ni+14-UFS9u$dSFfw#Rz8Bu+ja#1MZAa-AE$ z^2)8jTXH&LsYGWqy=x#&puiQxeZz^y`cnze!Taw5eT7B0-g*-$vQx0Cqg}!yuV@a3 zK4=+?P$C|WcMqVT0NmoF6Mb=yEOj-D+)4fM9f%K4%_9~DEQx+!?}Y(Re(pEOwo^J` z2$?R(?*zpaiO0Cs=GV&}{fN~akEMqOxx1pwOBU`%P>l()d@zptmmYa_4qf7U2dA^RA`pzRdD$&cB`*4B& ze$~=1)!o~qJ*Iy~G}Eu@X}AxHUS5l@J(vxopf% z++gSTKaMQ@2zC*xXcgO;J&l##E$IYq)!5VQ(mzO=W+s3UDE)Iba|*7>|Ha?m_w_ zq)LU<0TT8gof0x#h13I5Eg2oX+obBlJq{XD3Xs~dU-mUOfYHFpqEtE2G<(q+J9boA znIPT_RNZflE<+~VM&@s=vE}8HK*DQ5R)Rz=a-!-pR(VyKKwcHB`UaI0z#D^A-z17x zr@;QwLP@G`kZ)U6KefhHmPM1@-5@Wq$CebAmXU*;sycnl_%f08PO$1et7I&D_Z6$U z{9(JAJsrM(?Vs7YmF+A%aLk_8yzslPIMvqAEKu*h!XDn!dSW=#zV`J!wFAeL+#6VN z=br-Bp1-}jXZKfVFT8^GL-$+Zk_T1V22Ev39(?zefqM&Al&xShofm6QCG_^HJC^?a z=hZ6)9^7;6=$^d$_q2{~AKkO0yrey}0v4>?(`vQvX>G>uT>Kuz&x(P@?_B&IjSU>* zwvlYpdAyTlj8fbepI{XB3Yq!Z&kstz0|T2?avIP);Z}6bj{q@^9w%%gTVc8_hX2G4KduQVzJzbxJ!PDnv*vPWk16wV79A9VlzSmgU zVL4Xijyx6&0F)Oh82jWs5M{G#>92N)pZ$&V%r|5+-2WWmszrZZp**p+_)zRsxErL} z!74#cMRCwBfT5N}->Z?GYVd!})&n~R<4%!d*b4B=qi-B)D#X(wYhrMmMGey3^1Bng zU6@^HO{|)t@Z}T-`aTSHY1WkZQZJ^?(zFKBL278h)KQcoU}*L}!z+|DZGk@TTt+F% zS4f(sFVH7fFyW5pPo@<3X_BVt6>La;ji3m#TZu+_J~ae1pQ4BIb;JiH_`F1$w*YWc*j9p25;8syOjEq*pCw1HC8aX+Q_y}wpkkNa!3Z9TTt3YGI#`Uthsd?)8S ztnwo{Cu~@gmw(2>TO)rGZ8lgKDyYq&cdieeHan#_7%U9tO|J|F$KiYEmNn_nZ@Ro) z@MA8U?i91>il@5P45R55xJ`G4Xu1^m1*tT%X4KV}LdHp4==(j1P&4P@i8BiiPqg=K z;JyFwti{O%UyA?HR;y?f56`E0c-oF#F*7g!%z4F*73q`blQDEYd5Eh!RL&<6oKGAq zZ|;l><~UY4KeJFTe2AY}cz4euNrop)ht|$4a)yC5KR8}HwNQM6Hm zjn`O?U3u2RcV%oBQ*6_Ki*OFh|EBa^N(3%I@~j{$d~$2h zE{5JGATLmP+2jhTcQoN1AkPkwF`DgEO>|}zJCo4sJI(pL!+#p~AL1>F;?LdqAZot@ zd9(~(iYX15dx8qPbDLjLPus@YkPGLTrv^T& z%-S3*(@qRjPM#b#PYjo%P#B#2?%voIT!?J0KAg@M`TX9jwYgydQTrCy883!$J;Eb; zEMx7E_x{H*;$k>>@izBj_`=`m7sCbA(ca5-94c}8GR3L58m8grAs(3G*x~?Sqrl9Y zZU!cw+-Pkz0+TL=F=E+TIf+N$09IbKwpJfT5t>h{cv@TQ8umRJ$8$Tx9~$g8m!5G! z;6mpjx2r`y4@_Kp;xhF8cD0N7#b3^q+pN5N-ql_~QJI$?bgbr$jdvBYN=c zEIls&jNsYfPv!3no-ya8y!;(yQxC0P^$m*Fi9x3j@mSOM>CBIz?HzoCFr#wE;u~wf zGVj!Rr`G0DnwP&l=hWG!9a{1GG2G*@3nrBDEohb5GtlqiZpWcMXrROIZWOpwTr623JKq*dGw|yzb|!xagiOBC)uTNEPeR0pbZ}RLtg>-YNE^wb+ti%E zAChCzZXUG?8|Bduyz+E*8A-1xhj8jf_mC{MjuQ3kCLkp|b3G+`CUM9L)eAzn+5*_$ zS1-tfk-R{bw~=gEObHe7H*S=yQp$0SA%uEi&#MqP+^3Qq2q9?uGJfK0h39L^MA-ke zCr`%hyS+RmA&V;cz#HO?9lofMbt$3MM#311K0z04(AnyK>QsX+MJ?_fU|5e z-*qaTyh+X|NE=fUa4M^u7VNGVO{n}bXD)%9z(i69%~JwS&NL^O@06E1DC`vG zIYrZ99v8?;FbANt6?abZgUksn8g;~|g`-|uGpZGJQDAUvRCj3+Yb~uSa6(OZmN^zg znM>r1LJl7LB7roV|D4c_P-#I)&N37Wx+G4ER!PpwYjR#$0}W$Jq1*}4y2@qapuV-t zGXO8(oYoRIFE3->@vyPD z!K{EHgF_V%AYN;UqSelObq$RskjZR}Pk&4)8s(Fe;!!pq`_+pzF>N$N3QLM~#(sg{ z4<%g#3ds=gX{1hyX%TFqxnZ~% z<%S)V$uK$v4KqwU74-}|+As{2&J9EQ20*0nFO)7}?C+E|BO0Y4v~C~qxgkrXAkokZB|3^@Wt%hg1uId+lB%BVUL8B-IJyFt2Wy%c(pI7+F(%@5kgZ%)=XfVHZ;8l2=Z!fRtO5~6p zq})!V+)ku#(t$Wt>OD>Bjrw{Ir+gC3+aOic$d`N^g+F$?um44&4-;!l(*fc7>!tn% z1RCYz$JopGk@tM+t2-x@GI&hKe38Ef&z{5B^OR-~874k{jJ<-NC(@gMNjY3*hanTb z?xRfaiv^y{PG6?!DqZT(Nl+{hMpenJ2#QN6%Har{K}JC096|#$nh?biM~{d^r_Pau z5`ivv1esuSB*qFHfxd7Ag@x&qvDXTuCE!^`T;zjT(j%%to!y)rJi{hf(UvK|OOhNO`%nVi0X_m2x43=Og9$ zkfYg&I_3FDc|PhCP-btD%o7g!7Sdeme?b>+TYRu7Zi;YN0zW?l^RJ z%dY?;pB}1HKBWx&CM5HF#Z<$5*u9kKJ~5S$=nX_~6H^3E`6h-(|F`70%@CY0J zIW2@8?@j60v9O$OiaHxjH0lhZ_*_ynUw!~7CTMJnM#>K)MDx#Zp(*kMiSXcaOet0= zKqpW-N5opD6%HFHKf`8$$9)lvxPPflsA`s=@F-rXE<&UT9}Tw_j`KRvVf@~?Dz6K;@vzU_Fi_XzeHd{(OEp*) zXhllqxLKdaF;eBb`aebn^BW8&@HF38X zxNCqr(gcCN7eC&1W!|&EQT$SU@-~Q+j{kS?Q}zBAa9!Y;TLcUhcT6?Yz69&y&HCE| zoVZ)=B1HN;64kyUv~jw&Ap3YBlv|eM^UM z$~jG0>eR2}`UOls&aTg{UcL#OTQ6t=e9KfX-ve%$ug=>u+c|E8dGZj7e9KfX zlYqP2lj84~_3~xlp7681J+ofY^F{mh0CLBymm3?itCtsnbL)j{qAimjuLGA&f3;}= zWA_U&+F#R;q9(BjqXq(D`hhPsvSUmTX!CgN0?zdVrt&S59~T3+OXjPIc|7R4;RzKW z_f0>(3mjdlo15v!ICQ4j^rssDa{V|$3eDt42XLpFN|ZQ$3sYk^-&-GN@jT zm$EX|%lW`n%X~F4>*Y2@-U*64)4p#4w?P#o_f1?8f<2q|(F&aF$H|IbGyYBjE>pdj zeq5}`Yg6Qz_I(;S+>#i!nYbLBWN_$)Z6@wm;KmL^;o&ikxq8@71MZs{aLWu_#Y)DG zRSU{8m=dV<2c50r_);8qMJ+ZwGFj#PNFu z*w3W*AHdBp?!*H&MZv{$34SHhLdc^lnw;nf^Ww+=)`} zR0U`1oqU|QpC=(+V*1+(oa?U}hnf6c4_rH>kiVOxG{TvBzXDt~{{BqSJD|uj{ryki zsJ)S1S8gWz#-NiYIfNrTZJF#l54dWPt^GCYF?W$-qRI%rd|gpzjKgJdNJhT zTPA-K|$GyQ!VIQRG?+uKaNbJqHL&ylE1 zdh3C!Mn35srQl4xrvNui=BtUR_bf&4`HDQ#--{HzB?`{edk1jSRfTfj)ccI0_aa4} zsrP4!-eLu3>b2Kpr*|fBRIgNj7c25iy*0q$Rwl>gD!2=c^T!I{GL46>HqIX>D)KJf zi@bp><(;RP5uFX>3mlb(e>_y&VS;~7}k@tzc$opfK@@TG-%G;#K`_x|K^=2vWEJfb6dy#i_mhx^<&s-?(5#ZeRF;2mK(}?FM z6nSL|&Xo6}BCkrpnetv$zeLSdq6;!I|=^ z6&!Z?mdU~c}o@C4~)2f6FB#IK%Rp8DRB7jZPgf*y7BHYvGa-{Z#i&JgHL|kErI06yt?^e zb$hRM85^3K=8F>Vy)NT1;)Lt1%ed8oQ3q^YMp|8lFCe0<7K7O<2jMD=V%3)YSw~Sw z@D^tDS54vT1Bta6GcrX%T5-e#`8=;8_iI_jGh|SSkajc4!4vuU$oDVsLwuP<7wzEdP;?X5eSk%t&@zke z=HP2lU7*7*1NpSfqPsa*kn0T4{(v{cms#}l7qKgmU+pqbo>p*mHwTw2Uj}`+%RoLY ztLSbHE-fAdO(zPmh1gSCek-G86WxxS?0-qiU&H)dRV|CpBlA@*6&=tI5ow`lp>*?pi-bhA;OKG5oJ4*M|sA?P-jf&8Co`RB0L zK({OT|D)w!NS|WyZU~Y8g`nNd!ACHvG#blFDm2})s#R!w)rBq7=mKNe#5-B9mY>UZ zY4jEBBN|=8uF~jH?28&bn%%3>A2*gw9A_+>SY|AnINn$`@e{_fiIuF#?>x}nC$PyH zUB%{V^e5R78Xab>8a~ov(XwIVUsnwmPItWjvcMhpJ8h>`dSv#=<8Tgqnp^7 z8ohvhOrsaF>ood$cAG{oX7_9Kk?eaKeH8ntMt_#QqR~s)Uo^Uz<>sjMv5b{z^bKs9 zMjyi>8oh$in;-7xpl4aH(Vt^cjb6q2H2OGpwnndJmud9p*=IC*J-bz-*RgME^al1H z8vOng8g2jx3RZ1`WBYw4<=9_ zNmj1W{cMg#53ofVeJfk3(P?&qMyJ?TjUHmVH2O>IBO1MnU9HhO*{vFVI=fG!Z)4xl z=(E^!8hs{vRin>lf79qMv!Hub!Q+=#Q{&jlP%- zY4jcJe2u=0eL|xzWjAT`{-!~H-=KeG(61Y`RiOJf#-OJebb~>+81!a??lI`o4f;~h z%`kyZQ)4h*xe7Gyy?FEupifok+d!YB&|d>h4_4jskAlX%dXN4t=vIY(7Ice3zW{o! zLcau>hNP~%KZ2%HhD*N*I-<}PE+*m%T?jg+(BnbV4FR`&7&P6OaOv5g>A{Fg*MXj` z&_{rtq0q}g7b)~A&=j(6`AwiJK+~|0>ho04bOX!H?*>gb$Xq%JdbC3C1U*He&jURf zv@7p}T2iG`S#1q(?(ucS=o19d&KOoB0B6Jnd-?jY-a43KUEX?sJzj5dO_778MfrNO z-r}B|T?dl0y%jVC+eLdzX>vBLi^|d75~zahqIF}uWqG|-O*xttG37#xReWhhQo;8Y z3FUZNn^f?TyUi@ADMU{#q2MU+rY zcV}n?X8)!R=V(Q>Wr`yWULGO$Xz6a1-KWs_d zde46uXyvR)LD&9rp3ZgYdCx53v7bcEWX4v)MCd%-Zh{yN#DS6L++Hv7vW^RmPA8cjmi>w5sbO~ds1 zD_@gde{pI$ipm1aRCbtNf9Y#pR@dtvF_c|6Os~HnHI-GCqb9vxJ(9l*hv{AD<@2(- zUZDNhh|~^K9I4fgaHQ6+AX4oMgX##r?_(U}NUf$kQtKC>;#zb?u==`L+&`<4?GbAKny{O4U)XWfs?crv>Ph;=VA_ zP92v~yk4()>g<%_dKW`|mswobPW_!yT#L5|bzeqtT3lV=x$o5F$>K2AiyHZmpbkBl zSOL5%*3}i=9*H7sB%`x$ApW6YtS#wbtls!=mVxR@Y}YY;L%lj9HPoRq(#dXBK?7%^>1BLER!rd7XdsMxgC92ii z?G;j~8U@dlO4TMfs0?>UC`{A|MxsurgQGEyApiKzkPE?%z;NG19)S*;1>3W@cKO#* z?f+V;{a;J9M`L~Qj#wnRJysiy?~GB`)YfuB^M=)PH?*v3Y2MgE%h~U3QTq|rP*NM< zLAMO`zm{qrG4Pdjq3H~Tl>)VOCbv;Xj677GBspGL9oOP(L9OU!4t zRC^9eX8^bRI8e8& zN7D1JrP^!iV{LmqL~8F&iyme=VIQc&v^QP!WMLolWYJSoQ^t&=RsUp5wU4wAJ73gS zT{%6&!so!e*V^QCjP$2FJuIKETYj>UmF&Yh<07krvif4_o~T@IUa+e@w!4VB;CC95 z_#>nOuH;wtURN_O!~;Mv4e0}0&0K8&BR%eV|Ld4LSaG=nm;IrDBi1i>yk*P9BIS-; zq}-7Uo(EpDe2B=)b-H_#F)V%_U|2x<;Y>d)RE-?*H&gz;^6;3E(Gs#^_1J^;Z#Ar> z?;7vxdbae$m1a)b*r6Gc^l2-+jU}gPBjlL<6#meLw}1FLwt>v+*j9>lY_Fd0F5UBq z`LssLS+tVv9Vl+IEAbbfA5um=*$+?OcSp`USAKg&Z`z&AlC2Jp()n~ntXn(5Td9XC zK(;pff62;BJ;VEJZFErG4Ko9d;I8`WP=g!DkQ2SB)^kqm=09 z@#!l{ACxna@g$#S2C$#YS8`zipJ`kyp767u$yf9qku!)f4Vq>iu%F0RB!4PjnZ>l3 zIm8FeUjcbo&IleWm#+AQn9;yqV62gFcyDI`rJ#?X^jl2K@WnJ~ni;{K6Ei^A19Ej+ zOpjhh{2$3rhkR4c*sT&XIp_2tF0&nFp_I2xu3gZ)r`(&9e;l|260wJntYZLxGH`+x0S34B$>)t~!b zHWDE0tA+;zkS&A+5U5xXf=1azafybPPhJGFd@-sR=fZ5=P4#&!C9!aS=mtGh#Ekt%*Z753@ZZux{+7>`ewV5KrX zrQMHw8ps>HyJbG5b)4~uUL5Yedh-jkcIO$NX!b(m6Q6~4CTDgJ8K5TbJA^id?J_{+ z%7p5kT0*7&z)PqHI$lCe>HGRBVyl8w;at<$=HY3#>_VNyg-#G7ZFg{rk6DKJ6>>2}jyf$)~Pwc=_~d$IGX;{d}T*zs@s0X{V-;H}ye# zvdFXg{{44g%xb@*w?kx7;WSMuN!9ILB$az~^w=8gJ^i8 zNB0Ie%|S^OhrVAwcaX}cKxN1ft}Jecnfy?hT(Q)WtNVvua=q8_lI!D6pImewiL*W= zxz_8+wZ0vbYq(6VVW}lo?%%!S`l#b2*JqtRx#(^sXJbfmZPb%%V>>1nF0J5w!Ti*c ztH7F=elM|9%n;bp8HaYE>U!!DxjZ7`SdY(H) zO}oyV;$CIefu>y-I^IcyzMU$+=JN(%^&Au*mh7DuY zk}J{hdtKyZ+Ibx>(=O}unZ|RltHLtvs@9oyRl8@}Sea?K z!Y-9e>wUSGX%~0AOxxV)GmR%?Tf#DJOY2PA((aiyUS=Au*-RzV`dsa0+7%ry)2{3E znZ~oTo5C{frq-EuQ@dx{1es|CsbyN<>yc@L`JB)-GSf0T&f&3}{Y>lMspe^e)I807 zI)`(clit1c9RZlB-R2}6ccq!yZO+i4?K@MOD02-b!BZL4`hCyKwHrEKuHF7^$Td8Y z#ko5y*Y0keYj?MMt`*B%D@rZb`rqN@+ASR~*Y5dNd*a)W zVKka;3(K%=Ng1|H%dlBA0LAGhYup!wud|B^@Bq*B*$s_&GV%EN#Udj+bAFddH>ao5 zD>A?t=nQklIk@Ik;7O6f$;FpQB+bc+WJl;Ug?m?W#Jwk#s$A%DwXajx9sF$EHHS}} zWramtp>8~{2MYJD@kj(qoAF1N?<5zQKr)jOw02EFAbqN*5GoE{l(EXdfs0KGMu z4}zTB1Ch_^1&-5OWBYDSX^)v11c#4c5Rn6gc#|Z0@Vipt?t89plX4fy@8ZQABxefo z2}#sXY-J+ zkRIk2JS3Zk+(q;Vip?)I%pW1mA8BD;i+-7Jp3R}dusKMM>L^Kj zv`@?HOm&oA(p^aYJtY4gGRa*$*n%#mC--SWPLke4NRsUwEA5o|cKp^G38ZR0zGnhV zZtERqxZg*bhh*C0C2hG+tK5H!+`$bt?@7t-qt5O_zCwDq#~p3C5Oh~N)oDsXa)^{{ zCob(&_;&n`HwsAQzWaz^P`UnB`u=nB;l>MI`;hDpB>MxoOWNnPk2RPy?j)pr^s3g$ zw+Fve_W9a>)Lru{4D;wNY#x%$L$Z0uUHA=!&E{7c=4+(+RTk#8t&1JY$ zv&&lvbS;@#e z0l5pm@p55Zh#Tvr8yoiO2Axi&!fu@8yTLs)U-nQ)b_0^#fP6)|!9Db3e>}|8SIQ~U zjg3KW(92Q1l|tv*JU31?+!!g{fMhoy*$v1fH%{~2pf&F0#_7_HGlJY0sJX!x%y@2W zGThitx&g^KChjkEXaMpAB^W4JL!x&g^LN>jq~^F zMpABEV7M_(x&g^JbNjf?l{MpABE;=94Kqyiz{#~O=3vKx@>1|-jt zFh1<2<(K-~K(!IsLHf(28<+cT>`Qw5;oVq{pQ7>+`lQalU*rnIkwWPRBs&7hjzA_k za%G4kS4l@U+c<(VIG!U{8;%r7MCx=Wv}jINGza34NOY8w7A=aVrA42LWU#Jl7Al`YgA(nZPM=-7M7#A( zi}vVFm43az(jzT8uxDCya4sGP$(eAVjPEWP-`$wGp;2hp%6)+}jr)v@3cN-bOVT7_ zb|?~XMrKOH!J)`3iR>Z~*u@F44npS4kqGRLgdn?0WH*VxoP$Z_6>?daW?BD88BxZn&+*cwRLXnuPF>;7RV0IM} zdrTsSN(3fAAxKPlm~*&9V2%=k#H@soBP0UTh!EsRi5w*nn?jM;wln8wi8v<|IYuJK zO2h@BNNgdQbG$@c5{fL4$O#gGZB9r;*vc>`F5+MWHVPp~Yz7!vED;#BLy#D!897-Z zFsz0kF_1EHe~Gv?6p4|Jk<%pNPWSPGbj(MM2k1B=GF$4QWIaAuf3Vb}kFy?<^@mA4 z$XO4``h`-D{>yqu)*mkQ2#EENtY79n{xZ@->LD@uiwOEE;~*J_zKZT3B7LMDl5yy% zjDuucrPQNevL2H4E2JL1koAzPM=xX^^f}f;vc5s;(Zg5|$$IoK#-l&69+LIwPpn7p zVLc@4(R)~rzQTG))}ybmex%exvVN1)qaQFHlJ)2Zj2|QQkgP}6vmQClcu3Y?BJ~AQ z56OCDG~XCP>hh#nSj!ND` z?m^?chXr1Tiu#Y-$EW6P6MV8EZ-)?AwIXl3d`#-^-NRaWPeTViJ+?Fkk@sWwv8Z`Z zxtGl6{m|Xc<^8~Ys7T%;?rFQcC;7>mA`hGY1L${;3o(=AJwnVOxz&A~Y2NpRm`C*o zg;+%LULlr}d_ah!NZv0*8Oi&ESWfaWA*x6|iVr8!M!|AAMS}m_B55QMc{)jS8fqlb zRX$S|oq`&LS_0cret;|>+#)$7k>r4K%V4|6ErXv3v5(ux)I#_vw@^=#NJ8`WAiFN)MsYFfll;}q91X|yw}&en`ePEV0t#*$ z1&|Wsd=lvR(#={mWmu~jA?z6znsgEl=~0k zPx*?kVlZ$^=!;Rz^-%{L`hKfou8$$eaXOA=mYq=O{tcfX#JO;e{Oc;gP@P z-rc~dc)2fvD=6MifU}9W6n)bs-p_$c4~W-{gD#`7iQ@p^R2)23gv2oqIGZ?b1kNUo zMVJ&NB?hWg%s3WJv5VtE;8Ywmv7i#PKCTDOCXT-XXA{TG`@@jM`nY+jT^ug~r{WmK zQ3S{FE^sz+9EgdcO&lKpN9TFWvKb0p{o!=GIL3d+k7JZ14O$;FfU}9?65wp&n1!Ub ziQ^y)BsTqHBXDYcs3ACL+_>11?>UVRLl>8zcKHKvHtjO*Kz@|HWby@mp0wWn3>=+_ zGK;x>(`VYX%PinjyA(^(pmteo$#;>)M?(XZpmw=ord_+d58OjUVidDoZkZ*-D2sM^ z_8_}bM2E{-RG3!0ah>*Gh5INFRup8==h zz}L_?vJbV3qYOBk^^toR=1Ug+WAHq?IFd z@i1^Ij>FrCV<&JnarDEEflVCG0%x;6URY!oN6*E697nYg$57yG;%EZSCXSIHqZ62B zG5g14G_KA1H~~1dK8|Z6j?;m&iQ~7xJ!CqEy1D&0dnxv+naWoxc7srFZsW@ue zh~sJCY~tv7tX&*CfwNg3FPGWHF%X?X#c@I#af}AeCXSPUvsoVn$Gc~NrAbei{k#+h zJZ$pnEZ|feYukw9D&TD5*bSUb99x###qmhoE{+d@Q*oTsMjQ?X0h>4u2hJvrj7qyW zhF00daU5_ej?>zRqYgNmIGzU1CXU90T^tux+r{xLa4L?o+KA)#z}dtx7K5Hm9Df3i z&VZZ6>>uy1w2NbGjUUJPZN%{%;B4Z!2soQKX08g3<3;!o6=IEwZ}DpPz`Uv}`6j;T z&2R&7ebhotnz*f&xSv?!es76;&l2ZgqY)Ix!$!P)f$OEkslHA$aHD`5sTOL|oR`c8 z?s;vw`f9e{;5s}1CIDyS-z?y4{96H>jenEtgZ(q(D7C~LXNfz(5_gs*?pjOS{g${L zmbe!zac^1TzOcmgY_MBzBP?;#EOGNKadAsrizV*75ZvF;@sh?h;4q}NDs;@Ui>A%M zh>0mLpRedC^;wX#t6nzNc=vH(fp(7r4yQ$171QoXz^Qm~ZkJ1t-A$U^p&HI?=RaE7 z{ZzC2q{e5qv(xCutH#Tqc0L+7oAtX6I2G@ce}=C#=8f& zLM*5JyQU4?geLdADdCDVoax`yz}d9Rqrj`((d0hyT8`>OuL^0w;anU|K@8r)9$g@`Lt>04Zx{*f2Z-8 zcFzEAIhK>%B^u7O`>Lf~=S1J`|7v`u-7df_$8xf}Ov9OWYk;$f_gvsqynoR6OuLtA zc5w)U3;oWt`+=q1+;zU)mo+}qZhzpGV>!he*KnrY6M?gdce7@9m&Rw>y-~A^T}UoL z@&41&?!N2&cwf`_OuHk1TaM)vFLqhD1lc_cIGcEH)9k*X@tJn-)9hjj5NfyU20OcB zfm3<)md0n=Ee38mmQ%dw%3Olty#hG3y_YZ+L!IP#*u$FLcO{VcOuJ8McJY-!ips)jT3`F2aYKh^Ahr16<{pV#c3so_k!1t;6ZyAU`P z@5dUSX?GcL%dwo|Jx{}#b|15}`=Vy||1>_+?k>&l#Tw4EJM$E~c;mpSct6+pCL{mw z-zzo1spIFK5?@UJeg)hB=&4;YC6M^%7<_L6*Gn$f#Sy^i`yZcae0$p9>$TC2FCRGN zANfut$iD+D`4(t=^y-x`zG_RpbsC?#zADJSOD*|s(fBgk@b5`WzMpG+bZ<>qynnIe z`@6yAOiCSPK}sr5_uNQd!Fwd6Zg<5TAegVsldC110~M}0QTzY8t-Zq)dC zx54+QCEqg|A3aPx%)echd>?6i^w7^RzUaE~)o>}Ouj)H-&_r6@{Q5>=4m*SuSnxNOv9Oc2WfnZHJr(JsK&QQ!c9yIkWd*Kj7^EgBy#S>QtJ+vK}PhePTf z&g6^W;DYKOIHcs^OulU3)b?$ihBNsFX?)lP3gsK7@nKik! z9?s-DRO35W!f+5S%Nyf$1q~Bo6DJiH;$PAJQ)98h7!~(( z%A`qLn^;slc^ZFPo_%V`yt$6!yW`*vNxy~%vE{Qv>61hZU-lEFw6vgUWm9uwbG!oI z*4MdG)+-z8>Js(Mq5zNhENE=5Yp6=pih`;{Mazov+M4==`d(9C-5?6^m{1p5zP371 zzA_Q7N;Hb{@;UQL4=P_&vSjHYOG;+VX{9Kysjrb1nwsO4t6Q4lD?H+AGy*D(iCSq@ zD##z*4FZr(XPC8QenGssxv{3Ar8&{0T&-=WRA1v26^)6tI=dT(OT4C~q8`TaL*Kfu zb1`-CO7|zeKbpsZ${?>zo!5{BU|Zo@P!W0>UX%KVzM|8_hkI}WK`S5bQN4haQRMEG z+R`ZQUA$GRQ*PgZdYY%p5)XLa>G!?)0VJv?5dJ4g%LfQL>(kZKWzikaUWuZQyyMk~ zuM0n_%~y3vx~n6KTa5X6j}j0SIbw_!a8!&E)066%;^3tEaIrM0-a}L*)pJF2QoW}* zBdMM*E=j8Q6E`H)`-%sX>ixyGr1}W)TvC0ocrmFyNW7g?-$(pkQhlU+T&>#xQ4tfp zX(*B1?9!bOQuQnq$G6_s*IkT ztOlA)`h(&$kzdtn9GqC#O2tFt#EFW{9j}T(=MES&Fzl}?7}ttDyEHaWoZ=4K{@^mv zTTZ{6nDkSCePB_M`O_lL3HoV~*BoTk=YiEC)ckRe{3!CM`KMkR(@*^hfW@t>7AILP z_N@Z#vC1)cauJ%3QlYY;sl2+lsC@0j@dTE+4s;jjSZ_3mCdSemSf9V5uaGTHeOhsSQoEbQ=2F&S%%=pFDaQ<>XgxCv~mu2`+8YPT}7g*Dp3_%*3h`RB7s3Cwtqok zS-i2XtaQQbvdXeK^FTP83CkBWG}gsy3sw$`jW7K=+o7PKtfsyajqr6_fg$`49!cH7 zva+tJrm3tdv9_i%@o#o0U}#4x)wWc9106HBIo~KpX#@N3%`Leq$q^b=)t34j*GfUo zVcW72+nR573t}qs&2$hQql{)_7+}XIYOBlX)e>cs3Z@nm(~$Sg>C9$o?%nV4)ivu9 zRb_6=FI>3+(+qbbgAGVyym7stEs4mn+H5;`>;z&B6Skrk>nQXT@1* z)roj>OC$d{^GU+IP8j?gm@HO27Ns?UwGK^}EyRD3LmE0L097`t1}aT?V?&*%9pY(~ zgD$$#(_@7eREiwtn6joe-i*Dxy7G8Sa|7*}fT-*J|M>jw^amXAa~%7K;*Xw>VZUP) zJYf%*ADdYnaOmSp@2Pr+@1oVU4e@4y=k``Lv{c}DNmiQJ^T|J-*8eE7D7J8sfY1G~ zY!6CQL{9GBs~fJ=%XBg$nbEAYtn{pmtjw&etS(vESvi?qv$}gmsip8oXJP3vzmrI! z0T)2JhsNkL@(ezxEOx{TxIo8A6Y1-E){gE@TV2}V;vdQjgYlXtyz1$VVkgp{-)H=4 zao;grvNvW#2A_BrejX7Q;}cIV(KgioqP66{TShxX<+#~|!)Z8VMvpy<_UoP=$%#-X zpFknADDp4z&&c(!mPNECRms7Zw9oKC%yev~S~!C;bDgIYfR^JgJq~^U{-k!)xmCo@ zD<9Vw(7{H!=Mobv{@}|$aZLfyixKkRGmcQxK}UL2WKWX$^0J_8_YsU@tPuT4Kgxan zW=cs42RKRLP%264yn`g|$4R*!}WRE`cA zFo4t^ppdM-j#`z))5jZ<|Wdnoh6a}Arna+ zofb}hX3xO!E`Q)aHQuG1Mi>Y=jWE`wl19;wy)^o;Bc;)Z&YMQ+`Om+OZPYu-Vvl5IVGSD~w2Gz5Kcw0&~k2;%&{1fS6{#BB=4wY_2qB@(0 zt?g7t%qsjms^d?HFYu?#tI>`0>;bNXVTlG{4=@t32axOmBzpjv-a@Iq#9{g1DM!K{5JWS2;6OotiPSNv5 zq2fMzo?!3-|Cu6II|$4n6@+_<!T)1viP^1@x@;?NHEf;T#vN z)K<@GzaNf0qyJE12Se&+TCmpAC{&{mw~kwnWN?*IAp4bp zp=znbDT+FbGZddHDx`%_=zOOD$Lm4@)pxr7lW^*Hrvs-K#jyrT8{F9vr#OfYLtd-0 z85+e+3$F$4fL18MS2UqJc;yA)C|>fHcM2hC^-!#b~$w9pE?Rc{+aO}h#qJY?*`yhyx4?s35xf8;B4Z32{@a0&&390 zkF=)?Gmdl6VQk{~FW^)hDj$O4_$_cYag4x*%qEUs1NVgezuSyOS~ zCtQN!7zCV69JRpN#4!My&!9M7ga;Vw)<{=G-|ws$aS|P5w-<2AE>fFIklj_l;eV@Q+PzS- zJ3*p@>|UwaMMzwN?7nMhxBGBE-Xe|9yobCWaFueoF3k0@9yl6TBKX55n+y48-a~#F zaJ}SmU6}Wf->UIVk-#9nA6oMLLgSm-2H)$Je4l81)7#+7#YDv>-l4#$_EX~^#koMh;OmR*H6Qld`D}1YMvFuSEKP^Q{ve*`A*RI zu&MKKX1u3sd@&7Y@|~ygss2v>nfD-E3moxK`_XbLB+Yveo&ZkG@fajO%zF@?*7y#V I7~;$Rf5=km-~a#s literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wlan.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wlan.a new file mode 100644 index 0000000000000000000000000000000000000000..0f21559909e0ff3243a4836487fc0a4f18811438 GIT binary patch literal 7821634 zcmeFa3!LOfRWDvWvy*I+jU*5t&&?*iqv4TZXLj}#dG&Pn>~wY>o$j8UO<4WW)7>-E z*?y%TJ2O!)h!IgD21N;PuAl@&h+GWvROBHbdif(tt{_H01q}-LxabAF-|wmV)$doo zp4pv+giY?>e9}9=bLv#psn@BhQ>RW{^Ymi7wDQdJpSH{WZ*+X`*xu36L;Lsb+O|IYcb7pl|!&b{p&>P&y< zz5f#RQ2);Vja}+Ye;54xirUKW!lz%Sw*0&Med@xq^n1+EThzn#yXfS9s7K20apiIK z|Jv{I3tv`e`g=m|E_JHk6E9y=_V?8%t0z9nf0t!b>P&x^zoMlcX}_KKjH{gwKHd3U zeZ#Hl`}Mo#wI5KY`dwSQPi^#z|Hzd3e*A{N@L}~|zejH!JZa~r)g%4)l&g-a@8$Q@ zJ8n_Y-}R6Gg1Y`#>LK}!oH$2)KYn`-eNmmw-{_BDsUFJT*xSFY9?IYN<*!ty{r%Mw z)cB+1cVK8yJ(RzLzws9JNdDdMcOOvyfA~G?J2}PQvrGT+ef>?m=@PZoU-F~3s{i_( zojRDxq?G)A@$b~sDSnyvY*(lI9eLV*^>F=W_J3179KX4f?^KWGU#|C5^+@>5kAG9y z-zyqweha^&KRK(m`dc_Mu1@zm_ME%afBoD#c+QvZQUCROWY$6Hs}HI({Z)SC9qN1i zt(5Oj59P1=q5IVTi@(}mPN=Q?>JN^n@AlUmzf=9!Z|il??!HkyT7JFKgKA5^e^9D- zmVT>Gd7Ia$bjy{KUUg~AYqly4dliCv@XE*Nw3oAw%0ARQml0?Ku(w1 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%0DjGYQa70Jb3+9jM0!8qz?Z74LNH6Q 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

Ob? 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

;pRlvy9xd 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�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

?BQd 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�aGV^*5KJ2!Xwu4mGk9{;fP9Q>|*`^2>^ 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

    1h(8=P4XinD((L4#f)BD~yAo zLO!N&T;a_MAEWSD3ZJL&l?q?0@NEj;sql*mzpU`z6#hivNyuEXXK!`iSKSYwJM2G5 z;YNiwDtx5ES1bG-g@2~-^9uh%VLEe8`e^DS!c(|S;dKfhr|?M%-=gsC3X447N98#( z+AgR2Qu^bcv3nqC=lM=O7et;!kqvpWKSyOh?jl&U>Y{oSy208-T@)8I8lt%1F67aO z3;Ck$$d6a@xC{AWC10`~`Eg1fcOhS@)`4x;sY%F<1UK(!Hm63{QVgFknSipA1jQL^a5wo9ejE@0`Y0iPFRHhH5LB;Zuk&U z*|rdpcDCVUkAA`jGBSe>a5Lx~@|ee%sDHQ%gK-xIKTer}Dsk6SZFTVuxit+5lWvAEUvtlhca3e&4V?)Qxow48|m zpI8AuUDmQm*6cCDOU?vh`>k=g_{ke9WXDZ_drpqx<7I5*W$so4g+Q&`Y?}ef z_FLo6mRykLUk)LBHxSN|W2`WqXD!-njXD-^*>b&Rj2UEfA)#sVT7eSkHi!f!nvSm?KYAK1-ry-lu<$n`0?F35uGk}SBcBiBvjx{F-* zlIsa_{g_<8fXi$he^{8ljEdd>&V?|tz5 zI=S8_*N5af!Gh})3$F9XbrHF)g$r?Agr8IR*ZkJ0e3{?+CZBChp8|#RkU?|()~ul4 znmr%sItS^Q2hr1bncq5tm-wwS`F?)u+kAJwwUw9pt+V(NzjZd>-*276i~QEPd~ZaO z7a}me4`RxT{nj?Vhu^xA7g#fH;#Lmj#DpJEcHEAiiC5ug(v|p`d@eHLGD`d@+W@Cy zm&clM0e)s)i#L$nNsL+F!P&mM$u%2uPAl(1{LHxqKl9GRPr)9}BATQw+WYhc)+ z55hefg0U0fyXypQJ>vT&x9W00dc}7VbMw-T*3i2u3I`B9mA`MRFR_#f`?YB1G zDDM*P;ENBXV3HUV7Ahsf_ae5Ez39%*(UlB@|S){HuF z5E88pg{KxpVU^#?UxAX}=(l$3fQNH=0}2dp^IPZh7Rt?5zjYz6@mm*jsJM!+_FGr; zwSMb%UhTJD!*QSQP^$fh`K9XwdEp=S-0*+U(T_43Dx)>WKef%GJM^oC{(zj!(Fk=rk{FYUErDz$ z0X5r^#E4Kx;zvke=`5QuBSuQG6j2^ju;2`1!-jCjtMp4()Cki&g@aw7i7BO!;o zEoa*$_{b5#$6fG|BZ7|f7j)bO9qCW`G4~+Geu(>+2CTK9F&l&QdzbDjuv&!?G2Jh> za9@c{GP>8`9G=4W)4f*iyb=Ey!Uv)wQv46;4*4u$I}#ah0HYzzuMQ@Kesm&vr?G~L4u$_kArMati*)|P;M(+Ph?qnrr zs~~;*+d#XY?~!{4a)@%;bu}oWOexOpzqgMxisB}yRQB$`zilfKOCWbBIXss)KhA^P z4IUYS-u&4A6}e{T6@458-M*$*8G9HsN+(g~aVPt{@y&gWv1-u0mp8s`kfZWT`lt-k zl_|b)$XzGd#;-TNV_(PN3`0T!Z+w3XIhw_LFK;|XzsVRDMAI&BJPw1L9S^(sXNt!F zMcl91b(cvzq8Ts-!G+|I<~Ey(RmJQjTF#-mt9BV#-YA$Pvw z>o;$?`Z46L(gl>8Y|*RNhX38Ucr5;G=lYL6$l3X^uL?6$ejEe2GcwrcjmPMJF!opm zId43kf*dVEdoM~Wo&5gijMaLDfW76a>~TV-_}&h=do$>p3Yt#t!=8xUt|;@w&6_Tl zL(Y!J+8xB>I>vjA-*U`KZGCn*AbsBW_CSv46z;)FZ>Ibm zfZU=C@$km?eC%}{uk`3QuiT8;oIR)uD0ibLzAr*9Q~n;}iSPMyIO~B9ipNByi{jPi z(f2vz?EYDaiQ6$AeS2cB&Q2G59zxg~ZWwY~K&No0`wz2Uy4g&R|n znZo@O05@wt!FZu+xY1dDHI@9JAO1bnoR&zmp+n zr(drU&Sc-2klO-2gDV3 z-`|~!cioeHVr>ZbO!0Udax=gqIeWg9NzS*}m0Jl}x-!Yl(&X&%L?*egCO22A%c$=_ zP0nt|GwJKl$mp-^nT%fP2W*U zkizxqdtoQ_y`||pW(WE{-${MBd${>~><;uThTN|eU%z?dYwgKd5CT-Lj#Gk};=2HH zor zek|1Fy!!Ul^zEt1dG%F7E(ku^XZLfI?p}SJn!bI&r;Fsg`Zj3#3WSuD^XfZY(`S#1 zGU+=<(^t3yeYb1+$}~BzefL4mPQOx3&THTEn!f!tIj=r^-PkFAH20({Q~JH5>8q4t z8Po5tn!XB6?r~4Kw+cAB5PZrv3Xd+jKLa`ZIoBnS!xl)|^&5}A>mj!mQl#%1B~AMF z35SY<+7*fvh6_V@1wv^sxKz;PZ*e3dP`J3Lv{*O~8*^-R-3pwiR@%s}#fEJ!(Q!v? zNkv!?x5eyoKV%CAdj^M6L$MCHHuuT9!t`k-e4>dJ;C(y=siD3k-mJ|E50@|e6HMlaGmNX*1zElt!8cHQP@a@?_Ru#Rsm|lelY{O_BUqM@K5YQRmAvlH7sej($YU@bA*3Okb>1dU1o0Np8ngin%l= z`3n%=^{!uvvO}TlH!ioZ&=%JRUmR(0KloxR@Z+ezR7|PCq$S8FftM>xRU&()>@tXd zDew$nYAOkD!+k#u+eIB@EC*$?o5-Oq3l-;HxSz5K;6Dm?x7{BB|J31;BU?PqX_G8$ z_`GTZ+%4?5e`sJFz(<5%c4*mbtd;;4n=MX`eys5^dvGp`1z8BV-&uypX3K!vHCzGQ zq~QaBJ2c!3yj;Tv18>xDC-8|H?gh3l3nvARGVq?p5|xFIX1nYUzCm@Y|YxI@@7i76#mR82C)~xPi}MFBtej z_J)BkV|4bzzAS7w;QXRQmY(&%*K2q+@WmRA0-vej z9^fq+J_7i04Ic?Sq~Q~Q?aN~Kv2zUkFuU5oPq5n!{1f(=fq%y6dy4jD;bH984g4$i zR|Ef!Sx#N2_T_bY6wA3RJc6?T_*2c^y$$|*Y(L-%9EXutHme5CcE(THtN}Qp@z(&a z)-WCASghd$aEFFdz(;EMC}4bA(+Q8-TEB+B<-uEl?aN|+5&e&D-!_B)cX5;^sOh`M z;D5$`4BW5T^K;-C8m91d{A=JmjsFI4P{Z#5FVwKx_ELUu{!M;eQoR0B_a!DTBY7A8+8V^Rt0p)Z{NQ_=oYEh(EL;i67N8o9i{tpa(l5?jorSur$6AiqX z&j%jR{O@V-zrmLQZ?pFw5I)(j<4W+a(fD=1*K4>9_+||s3Vf@E*8|_7;Q`>gHM|-4 zehnWF{IG^k2Yy_`=K?>e;md%Z((rY__GRG}Rd)fuq2<@Zzz=KqC*)tlF96@6;okv& zq~SjT->l)k0Kcta8r$S*`I&{j*QeoJ;BA^bwJ*B;v%v4w_>>ptv2v_-+BX&VnYTV>pR}S<9z2BIG0^% z;0f$@15ad+7A^-+l&O zzz#HUK5IAdLUy=;7qd+U-kqIh;3e!r1MkglG;o;RZQv05iGfSlO9n1xZyR_&_OXHY zWn-MN8pXd{8w(R&&h|9;D_E(4tJ#4Dp5a?-;DcDVfe&Px3|z-fHt`?>H@;z(dJm0GZZexEoaI1Xlxyo^it!5LPu^h$!U^d6V>%^-f>;hupNvN+G z{6krzfxFnD2JU172A=EthJk~=ZyPwlE;n!wyUoCdvxg1b&wgg$KK7b{2iXS(PHAf` z6yHs3vNQH0Ji-0R?8$n5{)SCA@UPjP27Z;58Tb`eXW+Nk*A4t8>o@Q_>=*;T&9)kNPv7MR z-rIMJf!}8j8u-ubIRk&dUNP|B*q;sjA@ez7XDUCRvWW)%ge^31fiG;}rM^l7f5Dm! z{5gvom~nS5LE-uN2?pQd=Nou5zt+In{B8q}=RY#=IR3JMC-eU?@Ff13foJfs&e)mk znavt8OX5bZkHv=EYBL=SJ zD-B%fYcp`QuiLKVNI$wZ3ixAM6`3@T2@>13$tq zGVo*kdIPun?lSO?_!9>HA%DrhPx3z)IO_YGfn&Zb`|Pub|5JRbfq%*u8~ABnV&Lca z3IjjO*BSUl-ecfj@S_a;Ykr!6U*?w?_!WM$fq%~*GVq)H83Vt;Up4UC{Cxxe54Yyn zwVTR!r*EQx|IC91{u3`W@cX>Nz+Jv31AoY)2L3Bg8TfDfI0MIh-!||k{Bi?-%x^RB zr~F|9cl(|(@aO!u2L1j;;-APGjRF?J3~0 z@gW|(%7Z&SnD+E^|8Wm4_uvr^KFNb=Pe#`_TCKU-_U+^0@8QAwdT_Z1zvGhskQ?u!N+;<6CV5{4<7d5O10)= z=htYp24>?T5C7{PyoU!L;=!FByvBo%@!+F8c(TWyq=#>*H5=RBzkB%A9{zJ4e3A!0 z?ZLlPb0}N?n;!g#2S4q>cY5&OJ@~sG{DB9*=fUrI@I*DovF)=w_)3rbEgo!-X>I>s zQ)5aSAK<|u53cv%Y7gE`!*jLvnwDPn@!)a~KG=gtG)zkwCwcJM9(;ud-{-+U_TXQ7 z@Y^2zcMtZ@qn1uyWY1&|&iCMbG)(cY&@lO5I|=>UHNd}yo6I77 zEATlQ|Hr`JM17!RGQ|HK@F#HBfI#>YV49QQn3lj3f{dMx@^cuS;{ty-;7;(DDgM5| zj{w{H+JMhOec4a(hk>V~{o7yRGl18Dzqi6y0{<`CpKP4dBmEBo{}TMgivJ7X`Ka$7 zEBqJY!+zX^eC~X__C?dT1o>zZUqrzK6d<$1b!L`zfHw&EATw<4_ETn z0so!SkIV4x1+GVbO2@v*-k$;2qW!*9$-fSK2>J_p9We1f2EGF6ldt%b;5Y*5fz#E3 zUjPiJI8#dU$a zZ|_PaHpWwf?IXRhe)x;^4#hXLdp-!58tTP&`HEug_=agy&*tXtD8cfAP8__(N#N+< z=E0$O-{8W9ILtlV8)pYZm*PK!|1kaw@gHI0BzLKO2wI$;4i!o&Bm_5#ZwV|^$F}7` zWGPlA&x&g#e342&yB!WI1qeo$DpBMjgN(0w$>8v1Efuc1+wusI4n9YVZNZT3fLASm zLU_<%#N45~&0$Bz zRv5OkENn+6;wUs@6O9y}bV5{iG;_jK zWDM-#d$K4@S)|KT&KTIkcQPbmmW41?U=dwPP#JGKaz%EbEpjM!p)ImgqS#3!n}Up6 z*ku~y$=fjHtqc<>t6NQ(EEvUhk{8+f9afQ@BmLhf6&1bh1%2IwI9dJP~yYVkqQ^ z2_?Mr;$@gwSLij*X@^Ta5prt1>#&Pgsoi>(+J(N<&OGWCgdL@J#VECNt;{aCWp=@J zN=KO|`5i4GXA}@}`r}Z@_8(em>kfr%OF~QSfJ4*^3R^->0SJX`Lq$t3xMj9jnXRMD z7II2ynXR=}hc+_LL*8X>3BgbM8>4>>(a z$mvQ#)ThdvbO$I--3>V%ekkmuHFeL*hojl4)FEda5ORhFA$&zt`EZ65A*Z&73Z49< zv51~_o+dBiX-gxX3K#LTr4dhC8ga{h#H^7Kr@Dl^`QemPJi2LESL8IFAsX^%b{A0@ zqT#vw`+@K*%S?ZB?iYwVk8SVOYD}gf9&1njn%Al#tMCDLPV)~Qf>r3#s~DwJNWLT0@RrB|$wS+hdvRV!rHtx$U93Td^Aav;4z zh1?n?5-`2mglIzP<^V-jB9#NwHEYS0UR6S7T?x^kPVNA z5tXYWOoJq|bcSgJrAg30&?{l;NUsB7vkrvQ>p<8Y$x!-+)9XN3>uN|My$*zFG@)6V zUdY34A*a^NEn9XT8A%>aFZ5v=9?2l6Jqx>I1YtETop{p5tOMcnIuO?C0NLbJRAm#D zcr8-tRV3_2L)b)hF=4I7P>!Zo ziLhHGMEa)Jfv{Ewkb3TDP|Byr%&ie3v1qQL5{tZh2TSf$m-H2;*PpOke@I^;O-3|* z&UnbqOsDureQEX;y0uN}GwgF`y-J@uaTfa0>t-R%(WF5_pF4Dv9?}gkt7>6-RV~!2 zD$>lIB-=JQkrqLu*W^OCCJUR~rA^@>yGi(Qt@k2Cn(nw& zq%ggn7rOPF8j4bHxuBw|D@ad9x892o-40pAF})cmG@Ai;eb1UpJj^D8V&Z*U6T21n>~6GeJRkCNNskVGWC4JmZn z5Gj$SBjUCSLL$AMN8EZYCDL`66+4n%u_Ic=CQH+6Zp5v*B9!#18qumM=}51O5w|W1 z9qE-XqE$Z9k>0CD+}edgP)tdx7tCUJ!A1m;UfUvWZKGU_&|2)dM)+O>@Wc0_y z?uH#%Ta-T7EpoG7dUx6f5xP+>iqdOgk(<-PyBS+YmwMAv33~6!%7ZGaTVox)ak*_v zgcX&Q9!kqdqJO=lRrZ7;uBgd~f?24TH6zWjp?GVuo^JLxBP%MJL4N5jJJA)DmE(}gd!R;KATA&rfi81xq$$~ zL&Go^afS^esn!%;Wd?)b)GRD-s*Lp}u=PJ==P%h^yDAg4t}+u0~G?uc|6e%iG{<|Bu`UV+r7VFX2&qBMtK=dAnV2oHjq&YyBwo6=fu7CL^rR+gdQuZL zJ*kOGsay^pc1dt)&6_}#=G(-I41yI=k6`A^BNZ))zTw^&s>J3>9Quf*?BX$q@6aBx zLFRy+*WwsQXJ2$CF$~&!$Z;4$R-Hte(IY;M>ZUB;QI2KO4mJ{^U3E)Kw7#*bx}df-wzQ|Lc-3aCgv(PWWG>Z><`vQA@~Ya#Xj6G>O+jI@cVHl~ zacE&@)Rn7W*#Mc=>VhMeZX8&(qUpfCemx{&MO&&XTWcE|3XSrOl~q`pw_ zhx?+5LF`-%1(yhc9_)L7Fql~1AMb)hH+(eU;1ir(Xf30i$^P!d`e1(Lvi7D_ay?!N z8ALM^9}F(uR9d`8J7r;uJk``*-PaNC>VlRv$<&69I8ImvOA3~@$5MUmwGEZ+o$Xb1 zP$)kkA8kmc`eMBWJ^8^UwO?g26%@25`a6+EUv(%52!HOevB6}gotgF#GCF$p?>8hl z*xsFr$5TUa?Ap+6Gol+y3ql3q_Lk&usxwac(>vT1|Iajz8ZsIZQb}(|{fk!C7w#HM zlY`N&_`ohqExklj2OMkfOAL0ZgOR%+P1M26*g&GaKRz^qMrRi$j%LiizmjQz5A?={ z&|UWJ+WZ(vbSM4;EJzKFL`McR5bjEO+mrm^N!VDT9GidzRzCM{;k7i|;*+QLl zdk@Y$?{W(;z!}=bRt&{b>*GU%B0Zc#+`BAc?AC0T8_+wF7-*-T=*Cb%q#)Ezy`eLz zvAgqKmv?qTrP)cfeAf*tRCByj)(O-9^DOH7 z#~Apu+KYiOm`m{RtB>uDUwrrnZ(hLnf9QEv_GPS={) zrjFskuSUHb&wM48OV!TQ{GSR#-!ZT?ZD-B?tJ0rw+5Mjl%(HU-PpQ$n9J%-{CtP2u zGBs!Vl61;x&zGv~U#re~8mexaw#5Er3wJOC_@L@F674Ie~K_~`>oP$p$fke{5*AvEA*dhGI zaxD@XUbi-3Q#T1M>oLsgOUMWc*0Rlbokr&d$8YcE!NieqH?W1+BGvl*u` zfIZHjF`P8Qy6@IM+z>d6)2lQ_QZ4Z#D*L*i-~Ur0pGssLkU&a1wD0lQ@KBQWtBLfR zTmNxm;MYF!Vvj^8C;{Q|_gwCcN99hrLyrxP#3<@g>MY)s+9$Fe=)CLqPqG+$2FmayXP+ z%z2`QLgD#XBGZn_V7zmfPRlqZt+ShjC~R}6&-zI?Hb4ZC_T_|~QJF(7B66q9ToIWo zMiWIGX>gQ1X%Q_RMEiAg63t~6M_l~yjNZLlcxrJy#;RS-$;}S~9AEmpSnNtLb zs)IE44B;n{?4)^xD9KXSK_u53cT?+AA=ua3Cu|VmU6MsYM53(?Qu)zYpWG1Gl1=1+ zL8>M)kVkxiGLq^XN{NIM-v277M= zx@|&bb-X^5g%5Qi_T3_~!t-esZP+7wdurCmLobe@?zeMfo1(LOYl znEZx9<&{oItCSy?+2HW*RX*)%DE0j#OL{@wiKn`8I@OWCLM0}SL7_#YN-q=nNoAO9 ztz--u5I7xL}TlVOw?zZ*IhKyw*Bt zGtQ4z)jJk!v9nCE9Y(d2Te36>&kpyn9`qYsv7s0!S)xeXZ6SCFg)7N8k!GWRBg%cY zWE7QyQ7L+<(JvF@4(e5fT91!S4qf!b2ts=J)JJ`S!*sNXU`m}{K{oC(@SmV@6Jf-n zqC!jRDu*bWR!LppPzU;kRTm)xJ`Sy&(n74Em}pcb?;bRHil>@PsqJ;wk7zd~HRuQr z4#a7kAjvPeW~#YSt_3;yeelX z(T6j#4*%6`3IzxHQ`ECfIGgNymSgg#*6Tpb-Fh2{FZnVUoR)d~1Nth5}9!ey>Nrn?j_?pQQ9` zjA0-xl1~zL>p>-sb&OonnNV2vfRG77=?|gqBDhL4!)E zI!V6hbeLn~T%z=MyLzS(0fjXY#bIdM-(!}bCi?q^H#x-p1Wlc5a;V*cIzod2X=)h} zFvY|OD~cEPsXkTmJ{Qp#97sgFdt-w=j*@pIeULI;O5P$OV;2rh^KdLhlj z+)?$JAZ^4M-ed|55{?WyER1kvg9OTyPhWZD#=s?RTO_l4ipcKYxvh@OPD#GbC9CX` z^tarWZoSMjNq@zampLcNPhrfc8b+CYlKejyrz^6|A4$H*HPOu_$$A|Fbme0}WPl{^ z=1}#7mb~AMa+xBvCHc>84DfD5SrR1Y2@IB17p-2D;g2c$KDjZ8yG-vh#<-X+-rUzKrt!`P{wpnKWw{ zs|@0#_+St1fcQqa6!dD$KgyrNH#w}0$wU`pfxuhbQu2ZuaHTPgO);Jw_=5ZCt0^v{ zH`%`)dMtSl_}Ak7qy7iaVJzzd?*DesD;gAX*`xerD1XPB_<4?8d9dF9 z3H@xKpOfh43i@e>rT$eUQbs>ppxZx&esUM$XMn6y#!F+f{PZH)Q9nKx+8FDjza3i9AL>*V9jc7@rVhB6&NRe&_>$enzx|h#SI>rHsvf%^!H%@Bi6>pkH+Xex7W? z4}D?F--EpIcM|PQipy){3Lw4g=&HO8_$Q&X`qz;3r33ikry@_c`uu-d4e)yk^$Gg9 zntr}VKbIYcpDW1I=Xfh^;5j~e7IaS?Mv0F$RMocN#iikBC=_j|Ze7#Zd{DG?T~jqf zwoYs4N(@TgiW+>f)`e9qjBAjdNGoDK4+T%cS{kWA9tNfdZt7#3kUmqV`?q62ZNrM@ z@|M=-w#wGFX0Iu=6)i1>F_>DVV$S5(FsvL)Pi_4h`jaF5hIytJGDo+xyosI)BMYx1 z3oC2N8yc$X@IX&hYmG=3gA&DK!fcvJ!V|mPL&CG8J`agr)na*QrAz5GvVIS>zI-hU zu&M9C9Q)a&%9`3H@6$`dxYp{HR@hvNay6|1kHp}KD1rF26-_J4u`fDxHQp_3tErFm z4`aqi%Lb_o&o-&(BB<5X4ONZJ(bkswN)cU`)CLO0)}^%6*H%(Mf6)TMV@{Oy+EYhD zH<3}?qM_DMGxWMJ+zh2Q47URMG+5Y-lykX}W+a&n3tPbrXb{{i2S!=}N3p4^@KI~= z5Ewm!Wj`(^OrmGV#Dkixc+{<7MI+3zNloFVXiF;|w}NdpZB6Zp+9+xf9znwMmV$|> zpKYtJZD?H)ZEA_GXltl+)VH-%;DN0=JhBySX=`ah(yBm^(b3u}yq$UKuSr2gc|(<3 z9R+G^h=!vPw5ro=ShNz>*r1{jWz;7i6&j08&BL3cu|OD2bw>y2K~Sva((|rVzd^!d zpp5Q=y;#MA9wK#jLjxRrR4>@nuakmiSXx=$;2I-PQ+Z2E?doc%5U?DNpaF4->TB0h zA-EsjskBw#;mC|dfYMr+uj1uk5itiL;wo}BNkx!8hYbZS)k2B=Kp39>Y_7(GVJnfj zb#2wI;q+Xn;^3jJalhO0=P|fs)cBQt^T^lUUJ8 zwLw=GUD?z^Ef#%?ngVKStivOat?Qzdt#!gaomSVlCW_d}upG&n+LbkejObGLPHK=) zZAc?je0qw}R)mO+DJ&Tk8Zy{aMWV5TLn3M6g-xg7NjhCI zsSls=q9sy;y7HOL7gAKIe;3Xfj|7}QHl6x@L3U%N0;lnbD zO|xlSX0t6jAK2{YG;#^Zlv=ZC2qt7-<-1e1ohHh$4!vuUI&uu>~5l_&^!(B?D!%h5`m3tA~Q!qRydKm6w@Ub^?Qw+`}tKg;&EDLMlUJbXf7VZwX zDceY&_?Qk!~jDuu);JzFGb8=W7`4jRv zY%b;X*4g-&ciw3>74GxlzJk&Q?yKM)eTi_-y%}z#(*%}Bx`Yn6ABS6nIf2c6MoP?N zd6aiTVh)@8hNRA8dGFg)xIeSq3s_z@a#u(%XL*#KA6p9%!l|5IHfcmo0rph-+po!uU>vj@5_pG@TR$R(EAFoW?e@CRSz zylZ2!<8XlN{MWt)KJ9=G;IkQ^HGABr!saO@8#%1aaTZy-v1 zqb>99#Qf#cK}jT)$Y&GtZ$l~LwDPS;*H6mdfQEz9^0^{yo0dOeUyv{awT1HY&%pGZ z+gqfH{QiRc*g?WCy%{65S&_Gd^0yU(JRtXG74?j8{$K&9avxd|&n?WSZkqN^s<8jU ztOeJU$n>EFN!pT$4ju{W^(FbgS}f&g^%#_!OY*5Q@Qy0+ZIPCtBv$Xcz__$bFC2IG_Gl^X{o1h%krsc@a9dVih?YYhSUzGuzEEV?;oO2 zZDOBByj6Lv7CY?aU0w7xgBG0OfQLI2uZQ-`6GOE6jMoti33KjLJQao7!Iequp0;8x zOs^b)=ev}E@EPkG7=n%{R>9X(0O0??R)FLis_k!bId|Kf70DFJBpo|Z@g=?Yl;!^% zC734$Q}T=qX!k1`%9BHTLD8se(Dnfgj}D1LHSqARVp3U3W1hGy_>{$IuZAWvSnNW+ z=p>QC+m_{97%A}q9M#6@Ey!H6EcgNE9m691m*u~DAh}40m;H#zp})%2A!I1y3#u{>{(r z{OKFU`yN1jN@;=bRHBbgI$H~#RW#*SR%mO%bG9h$>Dt7f3#|h^;m&CH2+}laD}F&T za4Z5Z!Lf-iIm8Z}eNZO+W?Ar4*o#9$s0OOOyrRPDPHe&{drT0m1#c*l*lfn>ijDDJ zP~Tj}=F=*iSpO9pB`hcZII3!r($m$8mW2f`^l?%z8ll^Kyc?BEymbie5Sveni-S1x zf}Jgl7Uy3NNLwz^9war(4xZ!V{b-Pc9=Nf4O|6xm&8NZ@jfx#9mJ|G9PcEI*Cf-UW z;3)V8NFoGUMx??+2e@!7g3SoN=jVceV+FDmeLxru8Pc799-)!ghWIe;!u65i*^-Fd z>`V013yKsyN7mmC3JrzDw;)#%a16b-gU<4#hWq9z@{?3O&j%p2p5TGP|-N#4h9Z{v~oD|s|e9y;m2qo54 zN(Kl2q7dF%j^;i1cLnk4a9A1q3{YwSoDw|FQcy_G2%cClkYJaT~=NxpOa3P9>%lz7vRYNI=kbXri%8! zoY(-H6?_P$#0JnBis-W77o5lX2D*l$cmq+ak6zIt7{1_D^Eho_M*BLklNl#VEb@#} zi$h!SE~U|ypG7G%*bN%?}e)J zN}0mUPddsgYoq10D_RejuNS@@Lne`uRmm1R zp+!ETYs2QcsINSDT^^5NPm(fK>Nx^>@Jg+Ua#SWXDuY986e+=OZEtdYbSSwYjwNJ7 z1tvg-JgRKxibSnn5-*6t5z1jIu%b9XRP<7mn1k^n=s+1*0X9F3ZwRB%iK@L=jZ215 zWmFT%BuN&%cs~s%;Lk7nsg4mfy33Xr%{|uaWFZ_aNrUSXkRKyKZ2fvP7^EZGk-$#; z09a#{S1igU&?KRENp!i~9HnKbqm!~6^5cSk+J~!J;#+_^v;h$?b`PjWWGX5xgNd%X zc>nq#9CJmQy+4l=1#UFqzw~;PD|T&oixu8q6~#`2YE4}QGBIkB12}z!#+Wj~@6cLA z<;#}1Y9!v<+lrikNDiC7CeLG}5f%$dPu_q~13HuaP1poa3Y(=A%&T9M=arv`P%qy`dfy-z?tI=SB_`M<_dZ^E?g$-g^eS zVpNI{zdbNRkFKf1x-6|+ip6SGMq02R80&2rN;$b8vsBVo4db1ALz|(5OL8L$ULRgU zg>!|}7=huFjAm77@JT!rX*T}>C&Y@%1{vZMDTzbK7^l-9l8*bzdXFL3`cx7fYG-1g zM@(`2Gz=SvB~l|8KfB~?BIAWubXay1#f>VJEN*rk&ZX)Il4x;6Ya(o##O6PV4^PTe zz}8T0RkV67`z$Lhk^hGVg!QpbHHt9HyZ!QSA~muc3}Y;X9c_@8=dU6UwZ?9h*l6-CI4nO~ zge;$6i+9mcJ8CSu^4}mj&V9if^}je6=FqrJ^u2?4F$-Qk22Z)-v z7~x@MmJs0_G8V6ALyHp)>Kz1b6~ z+wKqn4sn28zz@x*U3D6M(J^<*j>8Gz@n%^P%psMC!b*T*stglptMebWJb6WSQvJYWRI*&cvz8~5ND*a`wcrJ>4K#Gr z!+9xFI7$fq-*CfniGTElXDgLqzH7;0d_T_JM+XP{q2_(`LUa^lurHY$>WSjv>x7(C zU1|ll1-Tj~Q>Lmx6#8s2@kY{$j&~?IM2Fsn`=IPxgNC6SXcr;?TJff}3DFMFIsw%= zTFnwi{#6d1;SXN36Ai&#L5>L&rMKv_*J8Cvs1`N%)SOH;S5yNbWVaZq`bv+oVmehs zRpa5#=-^0K9HUwf9}~IlR@KFVC)DaScyFx~5L2(JOU)V?k*V5IJ6PVo8Skj9sBV&V zs#Mk$io2?I%N>fUb~O$`Rl6pwerX=pN^?L-AW!5x@i2#mC-B$D>3JDizrm{u#Wb?Z zVGrVsmc3qfkHhXubanNndGGb4nMfg-hXZbsNy3m;H*Gm5b2iE3adXtEF{)afh{j_U zl@Qe`oXV+>O&P7ixdB-XQh4~ayPMuXE0zE+49Lbpy(yH|H9M%axLDEYoD`kDRHHz} zy(ExN%i5f34umhY;iiF+t_~0`%j_GR%T3M^7)645g`)P;V-vvJ0>SUkqSelZ=B^6h zYXZTo7-ww~11GGu1t@+i@Ss_|ytl6sJvXukVz&l@x2)vKWKu0_HJUe_;l_p5mauZB z=0F&B(!o|W&!MHrchwZf=Af^Q;*lg`y*E}4iX0XVj?l7O)rPUAdsTP4B;`?r8r)vh_zbC z(A=A}&j>yzybXy?N{-?%XmmmlV+PSM(pr-(D=L+uE_7sd>f+$9Ej~OTXR@2r*lceS zt%Y?hBX#jE>JgR_r7CXFB1G%LqjfSGr3yN6*pCQe8LSgI6x}G_{|sfzi9xoI1;4!5+gG3q&F`Q4esX_Zxe;3xx60hzjkOea-&$mpIigLOW0}7 zfGD7aK*qa*7|WgD=a@@I#bZ&em>r1$-Kl<#RY*)IkYHk|7h?>e6SEq3hvPy)!UU@` z-s@5?<-u?HQKAr%Sn$W9AFY~0@irbj8#@W8xl}3P$9UUor z!vHNU4_@k5tF<=mRvx@kiKS4dN$d`+hS_2^?QR~t$mK4VMRiUlu@`h4jdivVo@3Vice1uEAnY z6V_qFkW~}vRCgVSRDvo{mO!6k3+wbITECgW=FeQp7|kn0&!U1S?5@JH`L&RVHdeM) z>=aA^n#CnRe7UO60l911)HIy2BE(F%eMHk?@CgF}^a!H^mpsbeLk7M-fS zd3hd}kbGjFl?Ttvqd39+N6fLotru>eu%`j!Q!U}7?f5gKv-P0nI4lKWE9QGs?Zneo zUFOlj1U0;DZK8HhINvK*Zy zKv*rK6An%jBg*F1ak3b9C5P~&FcP!8cYSK8hjuPhH4>aAxm4*gRhibA$SHf3IXn3M zJT7=>uR({loZxE)Nv==TX`#~s?b?;80TmYfv}fQ#n^-P9%n8EwC&TGVqYt4Q!vv1< z1AUuqnC&a&a^K+jeKdO!hQpAdO#`$laJ|nYrzH7SAFadvf9!n;c$C%E|Gn=!Gnq_Q z_OMSDLV_$IAmUb&Ofn=ANSp-0iXD;=NGgyt2@x$iyq+~vK?xo5lg1uiR=85a*tuyco4lsP z>B($YP%-WxrKUJMDF5#vq~O>e7Nm=wo6NJDp1YV|_c&k}Gl`=}GIR%}I?iH3>!2^e zaRKJyogQskL6#v0&u97)CZv-@xy|bPMmb)DQ+oD>hitCAsOvMI8W5TRgbf?t+Vb{CmfCHUgz6|2qcCF>LqLL&A| z18%HtZlz^XORW_TuIl#ADu|5C8UQNJK4uU}vHcnPsglPx91lF*+ zy0)$wtAY%#bzLx8BWsCd?P_XkhHh&K4_>(;Jnbv6e2pLwruj!yf`wy~hmsq?!M2CH zI>?q%Ek#vPmqVe29k8u#B~J0LAnVkeNNN-|S73FVDz<1uW#Q=$UU)qem ziAh>!S`HEks=wU$Cbi4kRL*Bv3>IkO^?wwFuIi{IC3hG6N087V2`56jSh9*&DWIJK z<>l14x`nLRUH+#>YMf2t@6i#*4EH!Iw;O7!BaHotxeu^c@%m%DMuhxDI1r|OY+!Se znfwtsqTCJ;`b$*djnSAQC88`9WjKLA{95Q{PvYXU{O66)4iNEC)W@I)Pbje%UF{o6TMvaofOQ zua5$MFF>5wBEtrC3yRnZDU&>Y=^#&a)!f;T$Li_GS-lb>I(Oyr`tF(&v8Wwi2Vp0X z5Q&YVL^7 zlhH3N)E%uUvtMd`OzHrYy{k+qzWa&i^y|q9W|urtq%OfNT0)8(VZ07X8uouB0*p73 zJ+iAUOjd9Jm^DC$gpJ>0D)}z9$JoweW_n{MNO>E(Mu-ty@FMfyPiuu$t%PgV%0x5X zG^}m#c}07SRlbODDJpnkhjAdxPLi7fdyq4fOlstw5QQ+~X61g*I6j#9iG7C1Y6&SE z3cV`BQcUI1TjtA zF2!*1=yJ??E@+de1{jVJ1G_>=DCq&O|C6wW5YMBG)gl@Z>OERXE85_gM@u6H4oBv$ z)pIIO#`e+%4JsHNAUqDQi0N=Qk#e5&=A2EuVRh?@6U&*4 zk+HbttB~N%+pwHG+A#77IPmYm5G^IC{*L}h#YM|o$Y+0(bXnUn_^0uBZV_bU2iqY1 zb$8^wjWykL*1ZuZ*edSDPS`Mb@#2_Ez@JX!=paA4~js-{^_d>G&H=se-7u#23j=*5b z)?1{!oUF3x{10dgx$n%RZPX!ntjQv(W2glCX*t?O$EAhPiQow1n(F`c2raY$3aDF9 za&UgQ4$F)ehu{B@+4wAecfSLf)RIgea+-4LHovm{h?6H--1HN$#0d| z5Osm zKz;=FlWE#^|V{Fo%`dd+PAEVp$e;h#4Dn3fVJ(hhr9Wehji4>$Bh@L-(3EH*6Y3 z#U8j($2swd4wbOX5;kV%{;qtoMYEC1EJJB4u^>X4(1>(1?FkrhjLwL{Vx6N!BSEOn zEou%e6Qu+(&{Q;W(L|>P`p+AS#mPG}XaSL`!Txv0XuiG(uARJXXo8TjPXJG8|E{w@ zDsfT+GWSVX_e5>k>U7IFnorv~v99E0cnp{clLOr7F~ose$ds5;Q1acP z?PFL&Bi#XnGhPBDhcnaC-wLFI$t>25$GF?n*9Bq4f!8`1CJ(9MA$=25h3x>KC#6>0 z5<+(~wXbZ1!VYIbXiV(fr=_9TXxhNPG@0$6$j-LdMn#g5097L|Ch;Scd`Ngj5}=o* zjmwk=_^Sfv&WKHTO%fJ2Gii=Zg6fhhK@t40yeqh^&EnGbK1>L7ybVk`6hw4Gp~AwX)09yNi#eS* z69vN*JdccX6wP-&wuK23g}4NV9JJ^dBvw5^fD?tV({PNtR;FM$eqZoIp=*wy@q-y1 zRF#=(2+xM3#(Px=d59Vc1#Ma5iNbb>p#|4FqA|7+U4#n}GUYfpONNO~MZAoWNi=SW zG+?s9i)C@97?4q4$|gl-EpEB9d7{Lr267VJQ`Xeb0k2 zCy074M!74>J-Vq2vyaAQ*>WQw^4l4PJ~rV<(`P z?r*tp;#O{H^fl-lGumk^3|4MgLW*O1uyO-V!H|@iyIS}tM!A8lTE0B`suc#d5_{p| zC^Q%gC92*@y*Dcs+Ry|~E_T4KOaM~@fn603hMP2lk4^yN{1ai%Hawk(2Xr|fH@Vti zLokU9POSkoHLjYD&xkvRdyX;Cgd;y81I6O%#97ZdnB_zcVq#UmLJha1@JZF@InRfbD>#T7LI19fE0HA_q7Z znOLzzMN69CyRk;dN2qye1^0A)H!D%A>85F74d2`==~kPX3VR93Y`7Om+&gnUJfJscJqH%<&E#&U+xCri4?ZoVn}^%Sm-Q&WT=XC0C_q~lxs8PBxHrhivc+LlE(OyO zc-;0KQ&nvP44MX{o!XbKz)@KPvjkRKR~~Z?Jz`)lZ(SNT0~-z+jl1jtoB%ef7R(UI zq)SOSW7kviLxtyrX&iuj{)K>~b&Nno2n?3SqKX_CER92z15oHOT>lj?T#M6jS!R%G zJDU5a4L51X$8Hb(5k^EZSIsgEnw2{;k|_!kE0@vWN5P{FYFYiXqA)6{npK6j9bjGz(%g<{mJKbLE*N z8VGWv#67h{RmT}9`Y%BDZUsttfRK=H67XIL3FBjP)RsE~Q4otC1s4i9FuS%q(jgNJ zu$uhJX{>2iJjlSHENo<{&GhC(vo1nlR^b>bx)Wg!Fn=S%(#VAqOzyXBf*e% zj#6{We{;B={Y{n7m?f>ST{-+XsD#RXiL%f|Y&f=AVJ&;;zm;qBdjyBZte(mbuyhMry4`l=5;m`HoC`yQ&PvqkR~SI z;W{=?hvAUMgqkN-sZ*Uam?u>PD6Iu%DCi(cQ-K(Tv0qWb^c7-6Uc#V?!A z!E|-7X=Rus&I#9dv@TuBF40+Z(i8f9HXqlP;$lH7cR0c7VP?YdkU3E46T2ZW}bnT2A-B?db1c^@X`Lc8Kp(HNN6*(Y7(qr_xv^lW{dWX3@lr|2NnI+wzbvw~TC z@Rx?;DqON?Z92+Mk)awxOKMxqR*eQ184{H+@|cZ$fkt2jO#mU5jUe!?D*}~MX=2T! zQ6q})lzu23F5y{KMd!SXngvNO4Gs!j4N(M1I`RNX9(gW5* zD5xlKfYQQ}R&jtdu1V5oIn&(C<^VedkeprBZ=zJz0xaT3+-OzSM%o$juOF*12XRom zqqQ3g7S8N)Oj4u-?hu~9(9yVLCoij81!F>a>yjnKv%pLKp+6jp#?F1#<$rdp=~B+X z+eFpk?r`xeba`pG7C3+W2AsLT`QU4D%D9~K-CtK%S(wVX`D<|kT+S2UfJ5c{?Q3z$ zxtudRU)P>;ZqIdJjbl2e=j#xtC2xE+g6WpezYc*)xGL$Z+sQBT+pj~Q5?=Uf1k)8C ze;opqaEbRT+kk!u&zw4QCfN+`{5rI`5wt&ul?JmpP&6I+-j4!k4`nkfTT~chCII`Q z7-cgE&>IT~0PsZ=FcbSO&Wwnm4rR=wo0)TAzjX?oIfH6}1EQgGSYIOOU`r=WA$$)_ z3igE>;(k2%jpPM*2V&5LQ*uyZeVKwLqDz(PwbRzI_L4e}+){(Hxg zsi2glLcZDxo{I3`MLIZxKotCZvVZ*;2E#qS5o8@#GLS+PJS%E_yMHRZtk3`a2wmS@ zbxa^U^N`|cbd>KlpZ@_m+7}CZ-Di}@R4Le_4#&i_CM61Y-UK8@3{Y!R{7;V1rM0th zWlH->lEl}^dA&ve=}106*T#T^-tkUd#quE#u1k?_$8HwHX$gR?ck-0iN+~x)A@ud8 z(i-aN8{>gx4E$A!edQVhzjA}Nr@A+A?ehZln& zN7D6Iq?-Xn0hU34XoB08S@$UXGeIykOWA-SJn7uZIrHHoi)?ZLw6eH5ysQy%k12+l z$MGcT9H4cA8jKu_}Yr~rxI_PkcX-r=9 zotRs}v_X!7D3n+w0~Aq}TY<@C>J&DNfMxT;^XiCK7e^w2RWocR?$TL|J`V(I!h8ib zUH%-AG)n6OBp9(}Uo=R3CL7pBcy<&!rHg-vqe-a4k)I5U8erA7%vBslSYQAyI!8=3 z!rzcOh!r&yCl6cGwbJXxToI59#iYMufP$UnOyoA45?=t`*wiyhz#%!LD*QgbYh`#r zJ??|2yRp#%;rC-xaF;d`gx!|}^ao&mmEA&pMqd>(_sFa43ji=*P$_Pb`%px@?7~-- zkO`voG#G3mii28nM<9=(2-g4+b#M-bnUzJp9}Y-LWRR$1rN7HpwukEL96af8`O=*O z74i_Mj9w}w2Ug|;*=|Hr8nSf(Zte?*7KBNL)wn7@^#Lfj4;9gC>IA!`djRN#csj$2vCdLpbX;W(gqW>lGIv3nS_ zq(;g+)}(MNBdTdpf!EiD=a{Qg7vu!UOBfBLEsLbXg(`c03tqYox2K!kJPan7jtVMK zK9{$_I11MUIDy5#K3P`;qRke9)|2uB#X23nvO`J*OdR5X6om;3zY9(5PKU3hLA zk$T$q*@0V@V{B5{?65FY5klBM`Osn_bh124=iVc%(O-`gX>ns%>idNFUCEHt(eWU1LK0JaCzfW_S*tk_dhf$a(_c)-^9*+Ovh?q zMo7JEn+XX<^cLniAx%VHnS&;=teJ0HV>hr&|BF;}#jzw*!)koD(?HzY#ghz{Z}Ke* zlSzBta%p`C-|1kWbQ$gLeDqaze2UXCZN zY0#RDfy&xIu83Mg)<$`AQ)?%jC8P?`eo=N)r+AwX>BPbn;6O8uq}PnV^ zJIKz4Xt_BAkmWyfv<}rDhcsay>T9rHAu4`k<)Io<8ik{H>Zjxc2qh=c&q%ZvRQ(}fj~ z{C3|nnAB#&71A~@_UkB+rekR$CiQZe;k4@cN}>7%fRszsA@mA=A%ngaL+hRwZe2ZY zudIa&R?WP*bi;SV$V{Kt2;;*>5&aI3V+uNrtykH1 zv|)h;OY7>xpx_mA=fVIo=4Ggd=$a_r8*rSe8OQ9q!*qCstsmm5laNB@ z4`GKAQb>^U`ZvLM9k&eN7=zhkQw2g%kayq|2&D8RXCIc43zp)jx9n$&WO#|(Ld%70 zi~aYGp`xjo4T!hI#2XRsh>6plc8Y)N7~DG8x}p@j0@?%la4l{&7=y#l-BHjc203W7 zr?9tJ6wQFv2yX-d90#|M9cVcCY7`7*kmr{-({{|-g`4Fz_&KdB<}~nq)j+;Y3cyA# zUukxh0u*>i-}Ke#tp3|9g`#SF1Q~h448B{yBy7mif+Y%yX$ZpyNNQL~u2Bz23d=1C zxgI`Hn#r~m;@`A(XWuG?7+ns-6K>Xll&x+P))$<7JM`6%_AvyChJ>EM^B~Gy&58gX z4{$^@90Y)H#qN*O9YATA`W=j)11Mtvod{Cd^zAj8*mA0-qTDd~&OsFFB0ea#P(*e~0ggb=j^KICpYjC%0UE4}XE)n5iPBXb@w017Xgmp|e zJICbEm*a3XiMfWH*4ANk4+T42QMEi>+90{Y7n)D$dvk8R8VE~pKF&A|kU0NqI(=Y1 zI9$7w?G$3vlkG8*6uA#an7@QAzm#QL*|23*hG&xSSTm35+hf z02vA6%=(vvQt`E>&GjkLN(h_{OLSuka)^}eq&Rmx6-mu2yKplTaRxvY^G2|tF$$CI zGIFDnY?Yy4h4j8`mSt_YdS5`YVb(&ehb>cpvS(6kR`pz(Z?bDLq?Q2Az6q34gW_zP z1YwjTO|vVyy9|QtmL|?D_)!rI&LBG{5X8zRyCwiI>JmdxW-?%8np$}K4#*f*t*C73 zA~^;~j<*PX{7OI-f>w^3!Lak!(II6FWRvk>L7t!)E65xDhD$bIL$#!XWb34L0QqDk z#vG%cSn5LE0)KpjCDqexsnsrz!AMpR$OHnJ?dhmVy>DkUq2~FWJI?y5^=pi!qF$ zOmnjxU@J*E27tNGj&jX!#RaT@baA%m`sg+<2qkN{+*p)P@n3`{H{wDcCJjN{zE;x( z4Jh6Vq>CA z*O|kpH7lBFZIfn;Pgl|rQw%R@*m=I-7DvfGmo5c#KI(u&mcVrU66ug7zv;@UAj?fq z!Y^&DrW^2XGozMMhsB^8UC8rb;^*>HlwNoCi(W_(43+_IfB6#S|* z*v{Dr29Qkx2V(hpR|YVVp%W4buy7?XxA%iq1Nw45Xlo~(?Az&da<>ffyG|45;-W)g zx9!~b1g+jd+a&-qgghQ6LCb9C?v{jh_%g}XLpMPQi`m_KJNFi8Pe8oP>&VO8@vyaXZLE?(LGXJU~^ov5V;)f*n&>vvY#$PN$+;7JjjNp?Js-M-eL-%>W>T zh7$;AY&gzmWFE5o*N@TdqauJ_6>-I zN4`Q;yP_RVDP~{i&(4Bc8BAD1VGZm5IVyZz%E@BeyM+5whhY}jv)Y`qa4;4-Oj4m30nO^iAf zOo&w4NSZ~yRT4#AJ22-j)U~vO$eN%Ut{a%sK%EFu<;aN6EEt$mNaGd?I*xg`tdy6= zy#vW5NvJGz5oCKrQENm89-lz#2i6XxpFXC4Hphj1jOi4~8if0_6na)dCWu!k>g;}1 z0UoVsZU0PC(YcZlmc0ht??#n4+{yXzNhr`sxuAdROfxPVL-yg)#?H27_?#7cS@EVs z`WZX-qWDy3Bw`ZRaUyPmS%D2DG3TXmiSZdPA7cnIn8DU1tHbOL5axXvRj!wo#wAj3 z!u||*17KYFwHUk?ek}&{5b$S%XOCbt3IoMUh;0F648pz7(woem#w8ksmYsXGQvz`y zOZlEZnvmCwF&U>Ov zOh^;kgTt;xjwX;ZO~fD#)k6mf(Z-pBHnqq}1Gq1e#343j(cTCmB*;>8FQ+%>5hCog zt<`te;$#RpQ4+P|<6NmZTPU@>%~YD1b16mX+bz|B$bv_egLAH<1c+Dc@DqI#q{037 z@w3n&xVNX4Tpv_cE-NFz$TxRPE1q3>GI$r=03hm6CzzL zvdL=zQo0lefDBL?AK8!4X(mA$-w1^%j~nJ8K4c=oHA<6L0F08GMHWVayhlgpNu%(tmzGm2a#8QayOFo$?g zTL4{>2GNl%U7|3j!G`FHi!%7!n%KVprCSE*Haf?!CEC^ekZ;lDgbky z-H@}WvT7{5uSSt$4~^oe1sX-38hBIVQO= z!Ic{sMcpb1tl|e$l&y*Q)aOW*^FGopUWFhrG8^*AWW)_-F2V6{v~tHAi$3pF`1*vX z?M3OzY#?<1B(p*5O2@omnkz4bmjNAK0qkIvP2bd`8=hf9F&7~}T$htitefa&#h&~E zX~p@p7Jn?3d=_@GqPz_bPuSH0UDRWvax|&nMX%V_Ar!=KCUe~F%y|Zupi!dZO?*oe zyjPafiP@-zSnRq92_gPK=7ji6bt_{u%W{pBlaIdxhRZZ)WlT0BSUU@JZx(&73$P-? z5a3`_Gq_?5fw}QvdQJnqu5f(`>c^QmKc-u>BvEu6Yz00dr#2>AjDoVnR2`oM)r&1| z1rnwg&1pFqau%)@B(JK9_#7Z{%^gA9XtWY%ip|xww2uT07UiEApMRF=Be15eO~jUC zZw9_-h;dCH-PjMn(WU?Zx(xYnjIVK8c2Ode>Xt0P#^8Y5VuOv<($6*Znwzta<>^!# z3#w&MC>B&_(CXu0<%}n1cui?>6H4fwNx=+=OO)fg(ujy9LQe1+aAek!Zuof94bEa^ zl_#u5r0)fGO^7zKg$Bpu7KX?NaE$>VJVWXCx<+>7#s~Y zcdV`zYkABU{Ve`@j@X-loAgkeMi%RQ-tV`W* zN~*K^o8V`SQjH+DGd_`oMs$4)!dvkOG-UAze@R4`o`CRILE!7L*x44o@DmK-i@*ZY zrsGB4O+*!^NhG~L!T&Toz>RE6yBH4oiF5b|lB-?1D!?N7M-%+7!zUn6C%wTVUIVvC zd}DZ7WJ>IlhG5oyE#BR7^@N=EB9B(6bOZ>qn;dxq@V=>{1gTpUXjwrT>EA^l_%H)N zpw*a*4$wg4jpUre15RS}%)*0HO^=l}(PjM0!>lHHc&h&&qe!s95S(?e!ekou?fqyR2az~Oy? z)rZI}1mJqwcI@2q1m0M_a(O$<<8YI+bI*?j;=LJofdGT6LJ&i0A;m)I?|`<2zYL3$ zC?V6=n2dabEOaFy(@(9mhm!r;(Y>sbqTq5+(as_xxlTaG6${6pG_p?l3MqJAXJd$& z!@71HXknfQ$`NNrb4WScF;UhADT}{RTn@>NjFC`b2pBS06KDYqPz~9)u6D}d&r;AP z2(3jNtZohie-Q^1CPXe(yf#G2FxjZcF$n5?sm}nT8#WaA(mUWmS9F3h3J(OueaQb1 zpC&Es=-|)4-{t^d{^Tni?{!{6s6uq;w>l|M0K~7X$08{-8uUq@Iju_}Z;-D5=AM%( znjaj?VOsKp1E1C2+E%%0etTyvZpy&SlwN@ILpSd8fR>vk+0!Ba8VJ2Txe7 zxGm)W(?J?n$aO7irt3ds-o7p`KOdkuO)CS-7nikmk()4#P9eH?qY>RfRuJ|z>XjK# zJ7-=!dLUE^NgLC=j*4am4e9|Y_=38ei)e||)`40$tK>#;A|n$pXMLbf(9V`C^c?7P+?hhE;XBCqrgcz$>S|VisKOy#5D9(^Xy_ z#KcfO|5J^+rH`pIBAGJjYt?a?K8(vmpCT}srWHpr`RNlBaha}a zh^^^zWNKeAeI_pAV9Vu6d$Eocxs%?bFEvM&;x;(si?a`hGnxJ1<`x6gd8Kd)R2oig)c&KX)e& z8I#9(Io_j_hmOvpWV}_E`bZ~_Kr-$hr|0|i_eT5k=fYi+zSbL_IyF4EqJDl&Xm+^% zxY`P}&mB129&x~QxQT|~4H%jiz(ph#>Jv|3v7MLi+(tLQsvIYSuJj&$nzqu%SK`n` zRe*~OSE$YK_Z>XZO3x>7pt7pgN%(d0;OSN-V+ZEdhvgG%PL6VGAm=!?Vs1H37FDPZ zk_T5?X$%VmoibkZ4PGEM;)6BeV@pF-rDfF>Y7;6QbA`4BFg|JYB1wBw#$4?rV15Nj z85f*4H=uT;j=7c+%1Y;!oBp^o&0lEw#?P+`Ra95h)!~>^d4;+YMf>aRjC|ZpLRW+H z=?SBzc`a=w>KvGld;01E@Fw9-y(azOHak6^6ookIAFiEczz<{upZD4W@*|qjITiJl zHRbBM%<^o2QN)~i)%8`TX*)BUN1_J-tgXSyAb+_)eYJXOKy!}4MalE3OX&y}w{hLT z<^p>VPYRv|J9P3$ObVJ{Y6oV)kz=y+@!4DcAQ2|X&+f49*X;x1N1jA)b+Z-79%N?b zjOoC9)Jo1lLAA%$fMEXvB`wb!oX*CID-fz@x_!M~)WqXh{n>^I!yhY!xdprQiSM(|KS;BLOho^)6~=@;md zf#=-i69)|AD64_h%kJ{Y14l4`w8+o8%k!Lcqh@}}U7m|^i2Lr8dc!s5dYv&cZe7w5 zQ5+u)VOpwnqXYioQ{s|IIZ{U-)4qOeAb0r0e0(T~KFHCfKK9?Qe=uRHjG4M~!s_~w zewtYLLOKZTof8(kXrTkm_33>RRyU0plP_0XZ#-yqT4d4r5X$Bhkmif(x1zvYy zT{3RuYvKM_sk(kc%|d@%-A- z@T}^ZG8D(I^N%(h(xc5HjK8P-mDLAdp=~Ax4w>W(f4fl6_`tR@^Nqn)y?&jp&b5;A z$yKsmJ#f9Qs<%A(WN=nr-mH(AV7c>oHpku7viTN$Oc_=@=~jL8c%WfQ9q>|- zJ8##uwN{oX##t2q0C2koL1iDeekWbGKddWC?O~iJ?u@6}_j^5e5{TSa9O1n4QQbJ+ z8kWz;=|ZJAQjKd(Zg^Zb9*VjW*4w*JYAiS_Bfqwzl}^dwT`qV^2k+CCFW*?=)m=~P zAexz!Z%pB8<2D`KY9UU2LNv9!Y!(j$^6uZEgOAzPAWowN!u@^ab2_-qM&+g#9pt+K zUhBnlA)oPj^}?$W;Ilw)-s0%M&k3B2HUpfkuoj%#UYqw%=qOvRBH8?8frLGz0fVi=Ws zk??CIs7debDLtsr72Mb}A=`RY6;}4B>~OD3)%Iqq)6z=zS67tmzr6DByKY)>RQLG% zk+NumyRv6mK!0w3>bd$}JG|?RA5eUD;A;C9X==h@J3M=ivIe@!cAa)i>8^EkeV=~W zcUN-Fp7yfRAU*dEz{g1Kw4DPt>j`Pwb9dSdRk=|NdS26!iIkc>eZ?jF*OpQ~gHf{o znn-L-s4(acKKSk)g7hFfjqq#sRW|sk%%1jpT)`p1{1(c6X7Bvtih$c*qH8N1=L)KiC-A6_2bGJDIdWgB}6gZ8UcTg*(#p0csWKOXaH z3g*>#yjzhv=ubPSJylyuxZVxD?a4{IpK#B-pP;u7|6zB@{#+WNzFWFYi+2Gsjv*6P zm+a5yryt^Jyun&+-e=cpr~M`S3n`^%!Ul z%--_zGS{nBBMG~8i+d-vJ?#aGsch3mGdcvJ-+-O;*J=H*jLOXlqcwEv@ciwS*@0Wh_mg{@_05$Kqz85vio38KGcsB34 zzSqh&Z@a$N&Tij%xs-T$Oo^9AO1wO##LHt!yga7F%OfRTjuL0>R8x+90WCh2TCDCb z?xyy3L`GzbjL6=|h!jUgq$4sSTVzD`t{%HHhf8RTlyJS2@RvvlIgt_?BPCofCHw^t z9?L!&f#De{msi6EGf&MrcIHpkebm<5hub@yasM}FlGM6^>MTgzP9r)vLkCUmW?d3& zc*e57W8}vk)1od>Ioo^J9ojxC)S@nRo_UV++;T$6{v0!tKwArF*7s<66Y~zHT!?o( zZ`J;pgSImjvarTo1@`t7ghLBlJM6tRetYloTzjXz_oyO!?+Meqiv}%fpOu}h+Yibv zZTDuM*6zxFq+Mt0AC+aFwg~Gq^AY~58|b-||DM*+K5No@y#bant%E;6E93L#=7|oFX%DWt>?auo-q4O@^^bT$r(HnV=6l(ADG>V%(xeDX?7@lSn z*yCr?cwVghpTC3BF;kDnOl_Z~(Q5NX?X##>8dq>X!b{LhBS@(q09Az{gXqHo&n@7rw+I!0P{Nu6XMKgE1 z_Mh~i!p}$hP7k$-I40*AyhG&esI`~J#cq>W^i7Y5OP?7Rn;sLZ+1Dg7XFX6E&U&Dh zJ8$<@Vafi5txg)oU`?(dKCrKsXK>BF#ezknGnD60R*x$5?w>hq z)pk=(WdrYAeP^XD(rR7F-o8WC29$a2n&VCb?|<|zrQDyZ&uey#jG=2adsG?qj@oec zaLlFI?JW*&MWANakO@v-KG8ezo}|4K=Tp3Uk#EmYcFn$122u>1`=WanX)}J@uHHo` zHS}&WrJ7x!yP;(N;0oV{ntewvF4|1+-X<=oY52~&T8<7HUbg7R%9LAX;s%2xj6E$6 ze|*LuikrL-zWdR|ivN+^z;E4iWRqDbCPuw9B(|4^wrz(@GK=!Ncb5;T*|qMt0-CMY zI`5k-eS>~0K9qA3F5aY(!j{$;yw7TO4WEE@IGa-F_jqslJ)XVg$I^3qq<2gyzvbO^ z5A{h$&8}B63CHZ6?dX;6S-uS=`$v`gH{Z?m>}edf^VU{s1K|~%>huW39E_TM?Pc$_ zQjeG#4&lCRZ{lA0qOZ^NjQRUCu&4)*3%1Xy+4Wi`)@3z*o z?;W36#Qj>)J6@eS%AWGcbMdf`9*mYWuQ$KQDJ@!3eQ$mywQDBcCN)Tudq|Ke)cg&> zjfbfG_F1N`dA;NPRQ8{?P#LB!2S)N_ZwcT%aU~qPafTXC`3)2^W07}3c8lS$s1!Y( zN;KtVPU(H_qX(z)oHv|ByX2$xa4wX06z>}as~ec!6`XvX34i=ni`w(1W#n3FhuZTg zV)WF0Pjok;n&?-I)!`3Xkn!3{f}>vPapr#3BH9Pvglz2R{4*(kF^?&D7*#kJyN4bJ zxqRh8+}4_X7Sp?fP@+bOMJ3dm;3hSd>d<<`o7?vEc~q{co6=}K-MQCAJK|2Y_I60$ zHM`1&Yd<*8ae8+kD|xq0Rd+1x9oN2~cigo@cIru4J@zDfyBlHBq`f;RCA{mL?^9}J z&)i)}N#R}R9Zyd_-|s5!?zw5VFX@)uNlBh5W1h2+$1`czj=S$m-F;RHa$fryc9J{2 zBX|3J-k$sCrET{DP9+iCKz}#S+OGcT{`82z@=@MNJ<%#{-S35Cl14%ZU zu<}j+)#gU$%Flb}IXC5# z#f@Ify7pO_xhea%g=hx0&q~>!+UVjZ%tP?o==W&~ygk2JkID@NtAgg)j7a5x1;}Nd zuW5))xm;qPRsmPAWWPOce2d{FJi(H^ISYpFX;;I0JbNy-0L>9z4s)(=_3U64`f?`E z#gcuhM6DwT_4Z=o!uw24qKAs}VGkkMwTE}Vn$vjo-npr^H(A;1&JWTW9!6`}r}8~9 z>+#jz0#%}qeL+o`v)#9UW~N%F#?xq24yfgt!@HC>i)gkNbag{kw*K6!)b6cnd+)>{ z+k1-+R)ygC4$?dN#=Cj1T{LRz7JK4*uc>ir19X*{YWHKnT?pJo{cu}GJ+=k3?zLC3 zHrh9{s&aHK=OZ(`El%k*& z%(!`WuivkCuhAu=e2u=v-U8o}eXsT9sqyQe%T>7(fKkbL^dx(S(Tf(`XH6Qp!)Qjm zn^oTMo#f%|;CVOeywsg)T*gMV`z*D5lv;82aPOo~UsIC@_t<%TdGt2+q#bzkR-JLn zF}*43ysS%F2DMt3Eg!YM)ivqk*BW)ktZ^|F9fz?w%c8yM_r2?efAX5nUekNQ zaHogek#+6bKx1Jf&MCb5u*53WX)f;|>=lDr+^K_3$FD^x>>lc2oAwh0o{#nyb?mkF z+X^$L#6M15p!Ay3hcnc=++fBn?X!||efvi>dN2yK9(nwF6!-R*?>MWfR#ufsoe4d> z8h7mvOSJ3xsSg(ODkwhq`Q8hZ>dxGJ&nN$E+xHxtAbTIf$Ly_aJTJm;F@DrNgONT8ziRxt z@mqu6TKq1@?;8BB#gG2k+RD<(La4LouZ8DKdT?!8NJsvd_*eW@gvWpU zjM4vZ0b%eTf8?ovak&xNJjedcB0N)2f5g~K|KVqD9=9yEHQ(wv;my7>PZaMfpT2bi zxYx}<;;-oo`Ada!FGl`EM?N8qs3I{AWai}pc7v3WBAB9qvrtCz?-}Z+*JGnW|UoM<- z$D~li!uy*p;9*mYfWHM;mqhJsWu1~fFij*;e{ozBdArowu z+dB9qw|7LjyKV62KKtlv#=dyiyPl!ppZW$2Tjg2x<6+-FWa#6wKC1rN(8qm44!QN9 z-+0nSt{(ZLWaW0D=t^%&*D%kI3Kx641oOCXeC}-RO_^QdYnhOq>{~kG37^N8VrP1m zlwa+cdCdEgugaT})9UFgY?3_ZaGqpeLs60^?U;83x)#s{Z^WRT%YbCx!lI;P-|@D` zbKKhNcTY$ng{;q3zs&pzYBnik@;&%&B{ z^>v}ZLgP|!aKl1ujt$G`W~(K*d;ZXaOD8uey=~JwH@mMK4Rsffnp@k4r@K>zxJUZj zR;t?`>rTpa+aui8xHPvt-R;VA+bP*@*H{E2hqztoDTk&^NO9YCW{%sHJ)$sY@?@0Y zIUK{M^{tozHoc#6S^vLYvEK_;m4^JKbL*=7fhmE54)O=kfvxxqy8qZiCQmDxJZ&VN+DTJMb>J;<4&SCz>x$m=09>P|q-$=ln0FoqQc| zZe1UiIxw^WYIlHB_y|TTo##;c@RY&i_ILxqLwtRg(nq9ZJ_Xnd1Viz-+YN`v!wZx? zGGz!|+M0y&V-k*2`geH4$$B%0ztcJumS*$97~{1fYRWV zxw3g$F@Sj!2q^!sf|07!offDC{+(wL6Mzm-3X7cKX%qsEb*ogSQY6=?%PlpC!mBJb zl)`f@HH^Z6pa>LRX{k{ZqTI0*o?)pR3i09IaTH!?sFk|JQiT*prU?`x-((6==oE=h zqi~3!IFvq<;>dp}g{br}3IYEfg(&|h3I{7yLLuPg6r!FA3WqBNS&ZTMs3^l{Q#b+~ z4~4+5r4ab|&YYq~hbX+lQpZx5r__83FSFF~6y}3Vqwr=+Eu!#DOEpl4muaLB@Ff(U zZ>gmef<(7a2>3Dz(XY7oRjGsUJyQw+@1PLyP6|P8yC}THQr#3zS8BDye?lQT@23J50 z6axNB3IV^C!rLr$BZYwfib8buuPH?Nw@^3{j})SvcT#u}c5Mm)CvRA)?;Z*P{|$wJ z|CU1Jzn?gFh2>6#20^Ua<@NHVafMe5B zv^d-p0zX;cK7prD2>c9zgHKcH5TyoC2>c-eA1ZJrbif}e@KF@bQff4X=>KdAOR+>K zME-mV@t)%aUO*x6CJKBKg;*?;1zs%hX%v1RQ(NFODa896O5t(n8i9XL;73uo0P9=e zzk@VF{|B5bw26;wMmu_i3Q;V61OR zUqT_;yHwyU6asIVz?W0F6ziM9Ut)bzh<+lntbT~~E%0s%QO;@#+m$+5;6J4h_@`2M z5+pJTud~#d6oPo1MIp+?2QxW-o}^zy;fYFJEa{g?{BlX(Kw&FLxTODF;=hpe>nU7@ z-AmGMq7d!dDDaypL_2O5IBl6k#O|aJ?YWCWwEJ#>-$Nnr?-Tg%DC}11eu4K0{9y{) zlzN0hl=GOt9~bx&6r!AG1ip>Jvz7V-h3hSNb0Y-N`xAvXTIxj#(TlB`Y^-SSfV$%^`jP*?6^;pjo;(h;4;rUq46r#M{5`UjU;O(LCJcY9aCjAqM@0av{ zQV8PuIfXZXQL+$X{{qhh`URGWLbNAI;$8}YmrCK4*t#kFg{1~iconu-i4Ua^?Hfkn z)!0HQyv9;vD7*msE`?Vpl1aF{Jb{lBcs_-|pCIsqCd+RtfxQfzPIJqoryDUQ6Lk&{|OVb4$&q z5ban%;n5%o6r%hSB>n>mQEnrJDM~d_2)<(}g~-<;@Ky@Z{^bH+A@DW|QC_FOy9BMRQH!<3}(CX2d;_q~fLMEzhLxIVBNJfE(n z5aW-j&hrUF&*O!xJnm>3j}u;r+jAF%sQOlxLR9uBg_l}tD}`H?dW=F8 z_B4eLEA@=Tw^NA5?x64?tQUd5L?JfYmnHrNh1hieEb+fmc&$=zb7-k|CH|hI|ARto z+8;`MABAY=Clo%S)P8|~P9fU;1%*9I{Y&7MjW{-Qn?khPOCjARrei(%q=kHO7_LWhH_5~?Ke;!RC27fMvc)t*Z7>_y%cPUj*A?i7fLNE#6 zmw1>$Fb|6)-b^9Ja|wm-VG9xXG73@uathxC$rX4fg!gF-M7XHvMqQfn!6YjrM#w}W4#5anD%VUkuCQ;11>nWSeSs_9u-u5@js zM)eL!sbHH>wO@mXW%PZitWc2^nrelLtjQKi!5Gx{WkyjQm4Z1y3#%l3_#$%g0n+(QE^he$U_! z5oiAAJb*p;I;}+x6YR?MJua6DdVTh#JuXl2$hAg^(5y&1KqV9~TGd^Ib^_4u1lm(- z4l0LMhyKRt3IK3(Q>`TQB=NM|)!#i4n+C%92P)lvM88g3 zHO9$_ke*8iopPq@EcTO#2VGqAkVw%k7%^KqIrc$GJ`$YZB?l1FwW`&Lg(C&A@^zPjfko$gK>>9%kx z#7@b|;%U`{N&g+4RSIN5pAzeT7aNMEBP( zp}PM|`q_1y!~Cgqk$$%8%;dAYXD6NGS?fO6b)J1bX=%?Lw5m)LwNE|$%(LYOMEd@> zLHb8LQs6)(@fsT>YiuAmQxIpSO$rT+A*1mGH7Vvz4)pj9tMQf;s1{t7Wu5!5OJ8Uk z1@T!Ai-Opb8OkhusQ!mTreBX1~LDA7M~wD?Ym zKFd*`ZF?px@}=2sPsjSDzBEtnj18Wq$9`)tfsh*P#@p>k29)0wco)w`Ze0J5|F{f9a zHDb8WKBCWm&|&ZTI){vLSsp*B!?8PA`g)W(c$?SfvWh%uw#%Bb*_%qwzD!$tR-{k# zj0&5L%+`NI;|6b&w%SQAAv(Jx5`8(g>2}g9h@z{^d)i5_#z$X^kG{@%lYKXzT9kB= z=9bwom6N7E+1F8&4DB3*~Ur z>r6`VrP^LkYvIYBuJNAjwwHy(cJoJR6Tf`46H{#$ipLo>2Jfk9|J`-Q+QyldiU%1$pZCghL zJp*QJMz33*mhx*oBju%R`%gfN%n&>24Mbyl{m+QTbn~0>sXODNZ^cLd!g-T@Q;U+0 zsFO~A4zQTo{u-a-ZO#!H)W5~2{yjeWPJHxT&YSGZ!EokbaWoz>(Bqx4!I$YdwQz~Y z8+qU7ftS-7spCD)7K!dcG^TyK<5S;{kA4sz{Rihw_GK0&P5zC%?+bv%)b?R~4tPt% z*7i|+>fZS1$MMm9@p(Vt)WSu{zDIuQ8D4(0&jmWsw0>^P`q~bZ!P}C3uA(H{6KUa# zfX1|NKcX??|0$v|{n3kP%yjrHKKjr2=;!fyzu;8gTR-)*6fTF^GKG0z2pjLV(%q@HE^*tblYu-UYs65udt_c7o;~B;$+o-1?H=m( zOqo0u|6n8AuMc;7K!^vslkC*dZaXkGpUdJB?NqmG1eZPn<-xLM>xpjnxFLmO^YZe> z9zFtjlCofR(^-Wh-7Y^WLJhw02yJWPs3EpB+P22KJtIdA8H)O?(Wu(S-Q%8-V+->N z3k&l`OGxda#4IF@$m7DZBy_v;N7;6X+cR#6?a3NpY6j5l@sFBfxgdKi#a90vWfF;j za{dvM==&kwg|9P6xYX9+8MkXa!Vv^jlwZWm8BB)0+t3dwxD>9X4P4)!^q{PV*24pb zfR2^#Ih{6;SizB~IYqAR!G$yJ_$r4n4L+~a#t=d?5Tg9zOFCr$@rzdyv>nDxzTHK? ztOsQM81=qIVB90b7fIqeSN^sH-~6ofE852e7P!Hf^~0g-JN2tN&CmR)eBD96rhOR% z_!;t~2I;~sE{whdx`IBR$VSZ9b@CXpV(yM2(cN=#IB)3W(S&mxIKoU)@R3r0*u_-3 zV^rx6wJ(isfP8_(`2nPfBQ0X|y?=#oas^cu<* zg@0puQL96)aBj64(y}&*8V9I=tG)bf#I?ayEErWW4b>Z(_Zb3cw_k?<(L4Cx` ztQ<7*>lI1g@!_OVaQ^*U!%4Kp4CiPW&LJRN%)(IlL^I+g*>6cQ@?evNxmI6^fmDZr zWL$=aaU(A^!}O8Hh~~74auA4L>J1L!=^7nxFgt5n4=}n>t_kO64+1UVR|g`t)2wr z7xXKHTn^+H@K6+s{6Q4iyyJ`dpM<;s$ouiIHLe|RIX&W`9>LmtNk8NC^b^w4(N%GS z(<2V{%X*uG{bWDvTNANg(SLBTpX!HwPa^iK`dJ4XgU@3gZAnie_G|i&4mMsO0sE;$ z?AP^n2OBStfW0FT`we{?^&pM=Mm(5tcL>%w&PY0a@@IX!gMD*9>`M}{-_&&(UOT_+M zuX$A7l3pMl`|3pOFZ5{+HoZVR_OA@~H?wR1CkYUf$LF3z5*RD&+F*875y9@iEeU{K zb6IMe{_AE=fiCN7k2Gt;_iz)!ma`H54w*qe*nzSf%!BuU{f|5cI~dHEP!}T~F+P0Sf`wL+IDGvKVgZ<5OfK+?mVYdSC)RBMx7Rw%W6!FU(7|w2c|A9G>1m`P# zHpF%w+0(IJX=Kf){x+&S!l(lC8NC4rDvVLR91qVhgX%boykywwZyweE?am_KRuGGh zYPO8(i2vZIKB>>4QKc1l2F8k3pc&Qs6G!zaeHD%BQea+zhv(u(^|k*S)o(tq7OVDw z_bF?aalM?8uCXAZ4nL%(iKuyrxDLm0u&+3Lc7&ydTb8}yQI{U=g|Cm-zWh;gE%3OL zJcT2#IjD*}0cO1B8L{>Ua0-AS^w%&o`c}ay=(zIr4^c(`!a>mKZ$ukF% zOxMugZ@Y31=9S~R4=X2woN{dY9$Gn`;UiZUZJrdKZcp+REj)Mu98Oeu2X9CyfJB!LDr8T3I`3t<6B8jaOy$#ajL?>bV{|YsXs6P_Z9$_oGk7x%6%6{r<@?E zcgLsRW1>Du{Y@l_1Jbr$;53V4$8km4^W(xLjPBCs@@snR8Ror|-i-V?t<#Jl)6&03 zw8%5%ZgR0(u!-D2+^6Q0d0o+1_6J^{^;lpI#(SygnLgV>Ae)HVm zPIo5{bq`2&r=?=sbZ5;PJF{@g*r{-@m^N+Nj2VTt&KWi$d)hSHp6a&CafTYl7qq4` zRNqxf5n1%c1~TwE0NPtto6kRo=|+7%Ob+K(w;rU)eZcvb(jIYqy~>?FgbexGVsvp1ROAwfaJ|T}8gVUvX;GJn>)Zpf{sNkj8abqWgF9^yZSxNSu|L)NuL~5Dgi1|-Wd}um z6_w)JP~kUFrCb*N9?|bV3dx=h6ub-lPVvZ}ngazVxMMsFASC`?U$9m1)^26I=z)Uw zG?2;k;(1{_>j@s!?@*yfq0qmg(1S+)G!1T?rr^Q^+^rh*T51k(-v;iXvABmP;6A3` zBiy;b{Tpx(i^ZLlfcv=K<>3B35qDk!?i2b0!X?EqDu7-!roAUJZVvkM>uucsC;!qU z=X3m`*_tfVwE&05{v*>BX1Y5y2Gdf9W2F9qks9wz*KCZ^kvGtc#|#ACCjF6vhoL1r zV=1JIIyzmct-M7}H$`ELCgAs>m7KLK(fHQ6cohD79h^gkWs{fWqTB_jVuf9W72SA5Bj z8su-D-|p|W)(&T|BLDs^tzFb5XNt`4gZ?AOyFiX%K3{M;O+}gm7&00u!%6*-M#-4j zF~5O#+!+pDpEIHnBm1`<(f{qb_^*>tvm=v1fmV=Ma8m!nE-V^A3ZD*S3z0{_V3|#q zw%CZ+it{P|(SP5tZ^Jl=6XllWxqholuh&ML_{CPzA;pRlNl|gaUIWHCMx6Lnj5q;; zXz6xGn&8-v9AScE9WhM1r8`-aXyC2f*2^egL@aUXUndsa6^Eeg)6kG3;l*&!VlKh0 z#}P+wNc7aJ|ZrEiav_scFNSdxV}_9h44w3LFuXP zsT9Kz4o3t@)8{!Nh&WH$r?bpKA_#KW?iF$MrRy@MK3iu$$~YPNXa|=4Fher+T!Q!x zW{CuKkwoIj8QGRz$ms)gF1OMy-eOj}eFj%b;vXut^hz26681QD5)A>#dK_C7H$2^O z!?T=<6+zFXZ%Q1T)s%eDZ>7#Nd1$6UbVIowT|kM&rj{fHhPb?iUHrJ}bWa+qmYtx#n~opwejaP=<>fr2T-=(M851xADzi=-3J0JzVA1?(au@qQjTNQT7 zDPti4X5r7&5w;B%NIJvNBh(CWA-2Vw<2nmW~vq#vji3SPo{12WA-7_}w zt>}dBsI*Ab+WAKt`M@Uth)Rp0Zpe!wF20A8cwZ$hejwuFbaX?!xcFhBxVTru#bbaP z5f}H0xJYN&BjVy-w-FcDJHS#1mV4dV`6NT$3}D*I>0Qg)3063XV904?%8x~+e4LP? zGUX@zWXexPru+vG|NAm!l!+;m?G1a}IVkV|+ndwh->@wi{^i%!qmG!^W5h&WtLg-g zBTnWq|8kRc&SPM7Na8x0Bk)w4V)k^<2hcy}F^c0QOOpMlJ7_B@<(zsG%xVnakBn`ZhD`}yUL~1eYRb!w`1m{qKO$!Sk_Lat$ujuBE4tblP|N}| z_)Er_!T)P?@Qp}1^%bYg7fhK^zweu}3ti{NJtfW~E zxwzk%F|4FZcB#TI9~azzJ|2&&uhU*<#z5}|(lX&;;A$%O0fW2X&vYS*AuidBd<(zx zrSv!MLYx0zH^Y9L%Wp0FbnjGV!JkAsnTPxCU99tsMDFV}FYtzn{JREEF&FCu2AvEo zi?VRb39DQxa5X5%nwiH$-y*wR?0rj-(!RyRH}J3%!6}6D75MlhQbD>w{y&WEUoK zRD^cx!(-As`be7jB0&E?_WlFDsv`X#$4|NECb>5^g^&WFOA8beHuz>>YCB7ymAGT^in{ z<|f~zMtk&iV}mh0F6z>SuTfp6s4b?)Y7XtD<6C@pS!?N>CqdeF($2{~WgeLyb>dtNu*G!i7-#(Oqe4?tvrh)nGnQ7s7rkm2#JJVMXbNlDF{)d(aXkF6BvJOR(N|$gShv*XDgz~-27hkT zx;T^X3XZBw^-_$gNYy9)K4nyOJsd#zcSHPtL1Jp9g<^$k`_7HxP|(*o1T*1RF{~yN z2CZA8M_^TmC=TvB`)N&k#?t{>ye4yTNDFmF^G2Jya~o~{h0is|YXKdHBU&MIN;=WYnyz72rWh~#%2B$SpGhM;m8pu^l0b8e#TrW*jFT^^G)ADiehQ`0*JzjL zsW8-93@75QHSu_MYtsD-oCRINoqn4#A=b8#v%B^)D8Bfdov1mxj~t4;gE_nZ7vwBI z*82pU?Poaqq|aH-gIsIeapWxNegbDfhhyEa8BXzbHJ>!?Av<7G3`U-g42^@l$i2ex?r1h zfj=rt)>1I^zaa&K8<&FhVJR4L7CFn;YFJuT8xBnee?BY)Lq9dN9|dVC1$*t1g6))o zYApprEbFm`Bf@s;0!-DQzDpZQ0k~zYO&at{6%0JnFm_{*u^c=e4-3JdPYmscL0Sqy z=`JA6bng zu`kry|1Nqey!|f+E7R|(X}{f_OVPTc-JOeGSdzvnvaFJ)+o!7?US>f$!#>Pp)&&dFkms0v zx@_WN`M4r2s58{Qt}e19-Eaq8-0sS3-E3V%+b*GBZ(nol$;?9+%C)D^r?;;~>}i28 z&pt$arfM}(8B?`BDOKy+plWBBs;QlN8Fnor$XdJFpBBUM?rbbUncAMEs>UsAdC?VS z%i6=TW$mH0?w#1O_ANx{m%rHlITzdg50`R^Tz6fcKG=oUzNl;8t3a;8La)C+f!L|r z)4oazea-Z(UX|x9QnFM(0;}@h>Q(tXC?$o&s{FTUYE@oNLgmL$<$_e((+)9|qV;Z>x(W{2 zsXXO-G19F#k_&FbG;PPWp6c2 zTbi*AU}~^Hk!q z>Evl< z)K=^Ahy9*1K(^wwixMpwdxwKb!7~(V&okWLq`8lEpmBLh4sppr%UHT=Xefu*vG&P?L)wRX!G zx@$#VG1TAkdYESDfjf$!QCz+WJ}OQ<9%SgqWFG}XL3RlEsK|~p3^hC0@PU2H7`m&6 zhI?qOX6W=C#ZdInsoXgxJJR&dF*Pe17V4qXPBaYtS@GTfzWKhEGIUoD4KsA6X6UpX z$k6J>8QMR@&?zU7pOE_kCIj*8&59Qh4e81sXyT4+Rd_g6t)H9Qvkt9{NR^QW2vZt72jji=^ zOXXzO=5p2Xi;{9ON6X26mbDScknx;c@oeFI2-{h3+w^XKQ9o|ZFIM`UhgUz;6kTyY5CNDVWvJ6 zxX1~MOVX6HFKwv;_+KCSTlxkh9UZ#qHy}r7F*@ABp_UDlg3$$5x{=i;p zlknOjdWp|vYd2H`Z6@QHlFB}vt z?fFk^rio1cjhq^`I=Z0v^1g?*daZC#G_!cITCuZ_Opkx~^WbTcjALIftNH3J2^Q&G z?<%C*FB+X)GFxIUjutQ`f+F?}puOZfpEm7!DMP>8quGORlGHtWCS#K%dUWyAI>)29 z?la)lXm-UIoMP60T%MF}A}B7GlwBL&!QjAzJ?@BRmt;w#sM}Fh4;NylM{ikM{4`## z^qt>B1={UZAoGnbEq+R{Y4&U%i^~S1*RL&pD(aQsqw%PRn}k^hHw`8fa_W^RZl@79 z0x@?L4yaQ#NVDhosYE9hKi#H-BJ{2X;q>ky!6(azJ-9Y{_1fZxcaAq08#1!I z-1a9TS?nJy5`_wJlvXBTc{tj{uy$$HRE{}NoyRt+Zr(h6Vn4pNesSFj{h|E{8Fss5 ztiYLU>oWW^+2S)1Ya;H1+mO+!kjJs1!&`ClZNMoRiB_K@AAVX8pDXvm&8l56yKc5z z=5}gELT*Lo!Eo`J)hi}0n77ihH)dq#Ga(+YmN{|W>}9x2uAgO}k&%(Z_Y_|cJ)Qo% zJo;%1B&w(P;}NQ7WZ;xd;P7q2;q%^BX`tX3{7<8AD)i5 zVv~O$#`B5NtF3Vv)DG0qu(3gQ)SpPUKeX(3ARAnKqJI(3CL6RZdFQa}L@A0vaPRn1 z&Co@`9mW)v?Rw{+W<#@&e#vCWcm}Rgb>lnzZGyDg&C{s)-%?xdZ%CzS zTW+})zGdJoWKMLHe0GwY+ohROGy$72;ckHmx7u-H7e5oRrz;EYf@d1D;2wlQkz&E^ ztt>cEMYcUs?65;iW=|V5`vkGzmWXsZX*VHaeW5ntT>HjU6VCCjLAWyEIz2jeOySYd zf=ORU)QEi(Xw9Y=^bIHNW+_C92{+4-C=*V|ehsqe@6m?S2AXY;Ql&>mi)iVQ%)Sz2 z?<~e8L@}fMz301{Ls!}w0+Tt(bfruQRe*Goi>u!O^*^n&;^M`chX*--)yaW7Wj!c+Jr|%eqNA z1X<4hFQ!n^9ukefAUw_3KK&gl=2#EdFFqYf#M*&V&psXTyl8&$B^cN3`Tf0B(ZyL2 zHP?!RziO_}^nDscTG1k5&|Yny%Y?Zf3R6z( zhdNQwvJ^k7Ii~)?4OG|IVf~g&*q)Ru1QT|xYwa(X@ZL!H6C~U-kg)xX*smL6Iny39m+S5D_^Qre;2X&0x_VK4Ncau*>82E?m{ORdyx%d^GsHLAXPQ!M zG^H@aU;D(E&0}ZiP4*Kc<~!3ZAnwKbkr<`0N9wjvC3dZw?eIwN(=JTXqA8H>uWc|` zE$ASUEa)VUquA&-g(AMi{@xVg>tG?kJ^Ncl+y(t%mmPsyG2Qe?I$ltur=_Q-XQZRm z(=*ev5=L79`hEUH-RBor*2U6%q}ow^e&!GC^7A1#-=NF?-l8$V-gctj=drCBeSX$& z$y*Y>1m2PmUzp1R5M}Hbk(*uI-FA(BlIHG;9dgenEI%mnkq?qryaW1P@CvshYn#74 zxTMzguw)IoQE!lr>v~{r^ds^#u&B4m^l$5RMLznEBwq~jZ$bWki2TpHdib{Owee>P zCu0^pNIgndta8>W?Le@fW22K}aTkW5y{(9EK;}n|L;X=j)CUk z(OdoS@8CGrjo8n>wE)tx~+ zZ1>?9CP;367FOhKJB~J>w%7?jiwhp8#wk)>7V0vwVth^c5p!l8Rk?8aoW+ytaR=rS z%YHP5&*Hd5XdcGRPTuqNSS(xa`D(Zpx3$+G#U?SpPWT^@x9X(m$=I+Ob5vWwY&@lP@ly2Gd(#&TS#?S}z)1S#oM@mA9STa~vGW>p&;+H<* zx82Vm#;%*_;dOS`_Vlu|J-bjOdehD;(VfVD{ZOJ~JgQW-NldO?Hm9tjqO5Ap;dM*u z=PX{LcS(E{vx`Bap1>}NPh)DA#2AFcbwtblJeJk^8^{{gi4}H4?X07g;_iSiVp)X@ z;6b->lgEUm^DX<0KwHw<>10in;d$x)r|G z;78|q(GG*}MfXV?zD#$YoW3W=R(tb1UFaQCT8c@H4q;bXfo)xTKZNrC^1ISHT*~XZ z8STAZ?%Lhim7GG)-a~GStw;Oqq~il+$2`)TmZL7o-ZZ|f+z7hqOee(cwnkiVvvP8H zK=a7(K+_?v;$h2CJS&e~2H%MZ(sZR$;#md6LO0|Qa((z`JbVMU3H zIrguUZ^e1ow)CSlUVe0CR;51-9fON@_Srdxp=+e?#Cv$1+%-MDEDS?MG7M35@1z~4 zlL&|Uk&bhL={Qw$<}Iw9-Pl01Ic~RatnXZ{`%daWbZxwef#|w;Xds%y(N0F9>*I-{ z;7D|n?pUFb=+_N8)~&i@H9it;`>M+(-IX@($aiyCQMuW<`&mqIag>L9$Fq@WAG7k3 zotgT!_1DgyNyo3yz6V`zO=st2?Rwjq)hdCV;puiuR!IvW;p*KS%pRE*o~rDekaFCf zcXPuWEACkL*fi6N6X`Kyrn@ojEH4>`ySW|j*b_d%M(g^lKI$D^2KcNFgTSryao4)n zrfx0Dl-QzM1F<9CvC-wLzEP|68Q^@Ex-F>BFWuJ2b-#?|aW{0XJ1k4xMm>6fYiGGb z)pYb3)L|#pniMDVP-`~$qGoGz$<8JwU?2Ra*u0bTtS6p)>gktc2?LW@k=1i73zJGc zkW=$+kC={itOCc${ww6H%>c1_p<0rY$@`7f+kNo8YVh_R0c!etm^XIlT^c)^A@tQ)a&AC5P^wH~b_ zc5XG&OWL_lXghbMMjAW!Np0ss9>vamMi0ka0}I%>&ovm1U(mzxV<2qUNm{DBTO+Pg z-skHMafW5x3FHn?-cY~TSaF}t$zTDMx1J2T8A;{sLjA6oKT#>}v_wF0V{^s*Hak=S z6*q2lW`_#JT&`olFT038g)k z`;36RR6xfGzbr9OSxc3HV@1v=`>#^o{bjd!?qX@rrdeZnTOz4VTwvKBLz8oD9Jm41mi>o#RxZ7XMaYifpT*S`EnNQl zTx zs9T?lM0Ydut>PT>rn|em=|21(Lk4=1`Bz$W18GY>OZs2m;JP)_wgcE^p1t7+T+TH^ z3|nQ_UZNR{Z7L6gYWp?i3AST(aGcx+p(@)|h*5j5sLHf~C*n4mUnx~NPJQxVd0WJy z#cPk*UL=}Ud?5z*Sn*i>I^`i*#iF^33#Cu>=C}H1O7O!H96Q<&WW*MEwb4lNxZg?* zxSF1Ne84a1+Bn-?pNZ2o^OILuMY`g($k99~{KuWshz+WNYMM!%kzX?JW)zoOy9P@aw?lZNssnQB28AKaxZ2%nm% z7KAxlOF)I5o~agu1M2h4%YT&hlxrM;K)Hzi zU;l7)YZ|&MTR=Y?sRJ4s*C+0uw|YcCPhYMj>9XMWr0gU~`B4*TlVG!!E#AE0g1lDm zLS47n3+g)EeM{7J)J(Rt?Yf4-!~+mE=`gv`a1P4J5h!CWumt^BP`T&)u>1#Sq%$*HchG^Vjik zU|!{O0uBdGUgcw7qUj<$vB-6-1&v?%V_yn7e%oQ_7UGOyl^$;R3#50M(Ct!%EnQ+t zhYYCE6I*v=@-e`&_JVd(f|0!NQDj)OpZ`ON4DL^&pHDNhd`vjNx<%nj?-P#s=%FNd z))7dLewe3Sjfd>qkJGM(apW_kdxc0BKSI(8AbkL&dj?5Ihe(%fF{BTMNv9-9`MrZ< z)l)x7YX1(Fm$c`EX;++RXnzjVUJ<5Uxt_GF93tVfM%}th_t!ox znHI5DyHn1_ug_t2CGI=&pPH$G*@_&-ghupcx<;}nv>tkv;s+^*6JHy)-}8K z{+X8{8}mEOP#RN@rfVsS*D0D zbHbX(C;nNbd?Wh6FMXNGaZiq_clghsw91-^IRCGvZewcaRUWajYHp2;l6_}cW6P>K zl}(sDwo~~Y<-L1%DqlE%QQf>bi#tsn(zmo<-_pKA`wbYl$F$M`o#rlF+^MW;ynOwU zay-^HuWJUU?E9b%Xb`$5WB1k6yko@{R8#irwOYK+vX%>9@sqln)(r2>sJ*=%tE1!O z{S78yQ8`S&4EHZ$0`e;t^|BO;n{%f|qw_neiMh8?X6<mTA)Gz$_9LGI3t&4k+m$kFcwkaUyX;itk)Wr1X9vB$K#6)s`gMcX$Gm1<^ z(nP>~ne$yN1r5>0o@SIT3=B*@*5Wp2N0wQ>hS}1Cw#e`aYQ{Y+X||~O%x33zhW2Ge ztLPG-^Uv{$SXVed(7vIBe;)qS;;fqBWgQ^(+ z3=@AFCO*kN#k9l8rp;JprhJbvpiIgq+qidCrMx*z{8vLP_ip0q^mX<{kHD1V&aU$w zp@ED~mh?iFd$gs@y@-4d9JSWlcafG~CEo_xo`x2saL=)m#ArGsyurSY#D{?RHz0mi zK89k-q`twYM$>`%6#IUY^xfg4@9~MjLCi`|wO?li2P5fMQNb>hpycA4mLOHXG;5=M z=JTK}2kpk^Bhftqw2_iL)^F#q57$TA%O202*gmA@F;Al^Y-j%~B>!b*2doAz$B?NPC` z=!%-RV&33GznvJ1xPURV~X|Ct3?z2}c9jVP467}4| z(cYS(9}y{Iuif%2-yKW$mKIk2rxdQ;Tq+r{yVrWlbARU9NxyVU!FE4@z1%b3i@E7p zUOXS~sS;kRnCIa5)Hps~bF(sWP-^19d~5+Yx{VhZ=y@H1$@IK^-8FcH!Zx9>}Qccl^_OSUxP+01cG}PKFy59IEy%q{v+v6B1FMxpRrik#k)${c?_| zs~=yBPozH0Ea*n+cSt>R;nD@OiR;;ixXcCzmHja@R&)uDWJsVEnKP^AELl3Q-m?Fc z8JDdUy@5@r-4;J)#H`|ro&@l4`Do@P>sK5w#p~!OrRRhX||@OOBg{v zo6%H_s_BZnYIlsCw_LU>VHc-Zp;%GhA7&-u;K|0nZ*hiPQYiTe1vKUrIwmP24jnCg znlWWJV_JoQ!pXnzg^2y8qxMIg`$BT#9Z&LhBhlDZoAey(Oj!y~9JvZSUlNE8j=u4KqRlv4yMLcyMr}>yAXSuQj?c4ky zSAFLawTqW2lgqx{4>TXaq(x_B^+-ha|C*n~#3DSKF{?-P1xtT8W7(S!M_u$8i$tfC z95#Gv!rS9^QcI!{`wmIebqCK%M7^l`#M4{eZC?rY>~oXx+dU(wXxe$Qj52tt_MQ*# z+*N%K)!qxlTZ$v3U?xV>E);JpiWaCYnTvMmUZUo#Y4)9db<$Lk5F9UFC@~%TE(_sJ)&eb&L?~HMZ6o%kLHxL zPGoq_9>0$zPo zw&WkPf0JwkeJibHpX1XYyGPaz{-;~7^IIbMwenVX91|lRrvxW8_ZvqLT7o{#K@Uqzu$w#fKo{n=vm)o|GhE6QRh9z1Aq+px6Q_@=))XsMGUHo##1{XRIVzS27z?n>G0M;WrtnMcMbz z%(v}(k{KluN`1DYx%Pd2ppNHeL=VXtEO8_D{Rr+-$0{bWRoub{ z5@~b0v2n6{t3sw-j6#mvum23~y#(Ir@5MZnc~-Qw{(w4~hHD;zRF?@=sXE#}nszz7 z^@T}k-7KLQv6t7NBTOkdnEMfb`9{|#2`o|;bCELBjqK;et6Z;-mw^N6y=*t$AGQ#} zp#WjJbo3$}VIZY-0n))`ySbh-$aCAl$Z~NETigD9OG{mEFnq9{U_$kAqg`OMmG<33 zj4njU_q^VHy+~iLqq{g;S!8WtXu%xAwZ22W$WR2->ws2y$T7Da@B5A9x=s(o?bp?d zbj2rkh|AAa-Jy8}o?GBWK-9%^JA08%UewKiv+LjzWRruFTl)|7Y}fAZw1;Vh-LyNa zX~luuJRC-2Gl8IKHFlEOz?3wqK1kU4{+R0wF0 zpgr{%{mNMRJfXIRegITp`_k8m@b;x|6Kea?Pb7rt_2)!9PD3f7Hl5I^g3pod!aj~ZP`G`F|bRE->M*?&#MTipv| zWjWC}W3~ocyHv<=S@HatI5Ova6EXs~CXXClGi(43bv-#Nqo4rMe#1bPuV(Vd18PQ$ zuHO$&hh-}1u%+-ZhiJbB>Em0tJdU?4t44KoR`T_9-j3)IEu5jl$~r{7ZJ)` zUAm=R+UMn4nwJmm7+&7$d_i6w1r~h*UcQ;+Wo?L;_c+gym-9jR6nKf_6g4j|*u~3j z`@DpU;7UFJUh@*4;`tvDQW=YSrV?nmX2x5!+ng_7zK0{vDd)b#(}44`XVbNl6IOpWV=k=Xq}J%akd1jxxdnM@H`UxC2ED`%DrK05PUSSX0)U z5J>#A=|YD1eETdC^K+%sK-@n_{AQT=0(%pQc_`26Anp?+{ya>4p?x-q$Ab6_5Dy9x z|20f}k$n$|hl2P{5O)m{-xRLkuk3pb@m*o!`@+N*+aJ+X=#P}&Lp!%O-%hbLUN!A( zTH_M?V-gPn@%td|93=iAOnj;R3E9nd{t(0+0-H{(Z^Oiw*`Jb_J3~+&>`&<+?BVAWPWO-tfnuoJ6E(PxtDhhI|vD1PFscMwiGjmw85 z#4?f)(&U)@8^zT>wsiGq*BwHtgZfFAjc{A(@PP1-0s*{=egg##2piE`LozM^@F#zaDum{ z^}oPmTVEo+w%wnj?o}7IUdcOO#xc?N=j0Sr;jp9q zkd8h_=7AjbvXC$CaMG@2Kb(`1#kYBrL6fK&Ra2%u?sz0e-K|a32!qORC;O@nD6c^Y zEc=n1&fSTai>R6Yd(203GV?wLdLz&c-?(k9C(`IK54)A@e)?EW1i)K$BX9(x&miq`{t;aaN8DjP`k>A3-^X2k48MzS~MX6I9~1A7tCKyv&?_Ut+H>TB`RH#e)*0}yO2 z_~+#+!QVEC3~tst3`H1`xBR4xlhfCwt&eW-PKliAZp8RdkvGQeX*TP9qQ&(OmQ@%r zqXTQ|zfugj{AJBhfD&~?4X#hXoANXx{O7^)yA7N0ithVe#C}^g;o)n8->I$I_>Qk) z32ujpxmjMpAs!Att&6THzQRjHGm0<7S51qfM`ziw6|Qsm?y==wM%#;G%e?ume~K-2 za|(Bhb&K_h)>T~PrbT0eZ@`|hRat$oQFmlCv-mjddtMohPuh}jqKhh?;2;z|ys8M> zcCdfaJ(9l=Ehcc~YTuEsS8%lq!>#`^vN z2d1@I+7EHpM3+e1&!P`E61Psr%~mmEX|XIE(7GIDT!&3Ry(%ij-nH#@*n>3uD!nzx z#@!HH+l@_%-m|vkuvmI;?4;Z0kEj$@^v#Z^2mDP#{ zkIpE50?0GN5l&PQ++-Ld$ko#?WyG@DKLz~7Do&4*ICn0N%ZoYWB)0BDP>zWnQG5lq z5Z&7YyRW^B;hke^_IlGhc8?+BEAn4-M|y6n17e5wt#F5XYx)lBQPAbc>F@VTLsg@B zn`6iHt%$|2V|>lP(rEUEnsH^UZ1k9{UWBb*#3n!2%l#c2HLd34=>xI1(TyHlGB-N7 zBwuRl*$+wKWdoVs*$ATa=s0Z696VTk^yAqN`$Xw_`=gWnHnt|l)I|YI*IN>;&5HEN z#S-kKuTYV;(aBY{_;_FuJ{~wWS~TfllppR7TGlS^uUMBo19k2xn{i$8asVS|>}+s*2M4QNiD*E#0J z<8=1%+GC$(!i72Jc7g$xR)pPTUUsI}D$YG-UA#znu9scvMX)ujQylkmxAF?zrLDc3 z)?T_BEAnCp?dy5@I;@ysty?21!fvilh>@{X4K^LpmVIq`UHc(BT%Ba@rgpN zI})~JYa}$B-yh_3#qnEATTpSJ26l|cGvo0%%u3f;-4)-lFhS(d--{GT!Yo7&+3ePl zrylOc&2!QyPJ86v6`S0gK29fWa=Q%X=uT>pasV`sSfp2=B9&tfY;2R;aruF(mE%@aNm(5wc(z4g(tK$x9HGWC`!o~Q;csWd-o%< z6(*thfT90@CF*+)%f324Qx1_Nwlsikvl|P>j~#;8?)?Nib25%yn>VL=;Q}z=dXvJ; z^4i6V>u`C~(nX8Vwo7mx8$&ynGLCHQ3vS=LAwOQk?RyJQq}1(uw<1AK8g5Ss;r6{- z^V|1e$SVlR-Hf)p9>SKnaPHhCbLuVoo%~ofL;uQnvqcKt$YN8-GAm7_`#;}L^bc(Vezjxv z2tMR|#F3awckjI}OLux}$)DXKv&Pp=7IzA;|M&Lgj(d-BrN2f$HWx~jY9qvUAt zke~I=>EtNS7&-+UO`b8t*J!G6g55g(83*CgA7^&QIV~q*Y+{<^By!>u*HiJ_r0#$1#*jj1!%W z(9{w`P0b(^>!Gp{18cj{;p&q#>q*Aj#pjmu+1J}YYd(LmW8RxS=)Cq}p4ca<@Ci*p(~IS;VjfKI-fA$)$9*4{R)9YZfvS{t2lAUo-&mURXp{sUds z^wgGi*)Fn^<%k=oUAbrC8n6?4Ce+%~vB`egL&)mxF=TftGIbIQHTDfH)xrPkTRgY6 zq@ORmQa^hFBo8fFI4Z!IsuzV znF4=Os3*~lPJuruR8!zYeck*y3zuT1`e|W2m($WrVrHo?&^|3JmQT}o48pd(foSxd z7_^V{8`s|c8?*6FtnF*)Ep5`}Y`<67Tlh*Wzp&@6DB~|*z%MJ}joF#%l~|jdSAf-U zCW0YHFl4pU0^G{WqDZ{}+kScLf(%4YN430R)owO$D4+gK zn#;{-=l+hB>o~=yy$N~iC-S!8O={<~&Y?s91o`Eg6{iwX+j-}e zTJqIABDGyn7a9S$MWivQ{XQtQjNIDazw&1-wRUA|EwlfpW!9bq19mW|YvWnqh^Q5g1)KGV2v!K`^pXU{qKh}wBIbqng}_B~=dPcKGUEf*=^Ajf>H zdssDN$_}Wh7&)qDYFX9TvJnT2#FQvfV31QLRE(@DpIB8^J+fl*_=yv%$Be9~oK`Wy zvXUfYC)Z4#Qdv2%s=B6ZLUm2W*r}GC3@o2gRW%Y=gp8apq0DlUAr)0q$4(eEv1al? zmYYP5FPo0j1Ink4m@UhDAbtB}be9YKN z4lV<*Bh7e*4#klW#>4X=9N$v`Ay9FK3@9xF<8=Z_#xtnQ!U_bN8ei1l5YYdA2i>tveM+FI%a_*IfQx)-7jEvdn(`^tHB(1c zQLNG8BU1cm^c25SP?6$y3r42+BL$;U{9eK66hFFHia)JjY>GdOIT2{i)-j<#|dU z5K&$M9Tr5CAxX*|FAPE@7e;9)+F|hp!R!9hc zkHbHZ1Px>CCxMd*8V_`=O%_AZeUZv!f>c5V5~Ega;IOO?_*W>{mg+I_Y-_q{E$mz1 zU=(-;!tDUXR=}+VI9OXjk}y3JlYZ4A3K~3{fGIWw8IMmU{+Ul#LR!{@@_($r0_Dyj zWL*qD;goHwIe_350fYUARZ@Xd8V2%h9m#ELs)>R6VF|&qKF0nJJ+=O?**tY~=FGtb z9{v085N+})N<4nFHW_K*(9?fBlmi_D#zP6vF{ozHcyvoT2jjXBaSR)8wGu~v&=)8d zA`~gt(D7DRaSa$a-s&f=Qbc{m66Zh!f2f>;5dMvFjxV2FI>7ol>FH-djWbUK^v7ZD z#ytQMW!$AWiQTvd;>2j<9%P-IbPtAwWbi|947za-#VO~;Jq%}<8+ZSHIHNr2>5oIm zlb!+AX}+h_I^FjSw9fE7gK+k0GR|P@EZ;N4+T?qN;%n$6*)VJ*GoAtc@HKPN)89JJ z_YAPk_dTUJ={iX^(7Mp~46-irJ%eG8B*}(Y7yF)})+N4Yn00B=Q`*nE%=h%SE?1tZ zW2*bpLMWfy&)OVD^|!7FqXt-4hEb)~RbkXX>*_FSkabNMHQ2f~j2dEH7e)<*o;N40PF5Bs?@qCj2dX&8%7PnP4=daI>*7dqCSKgVm%N>4TVV+ zq8f&~J`HN1DgDD?RDayw8KN3sZ3&}Ftw+PCf!1ST)FA8eFlw;%L>M*1dNPa}YCRQ3 z4Z~RgQZ8frJ_A)xZJ!5izX*>1wzj8{KWpK98v3*59==0V{t^^hmI!caVRBJPgG#)zZWHGXz6{@(vX>Jc!F065M|<5d%swL@307fkc${hn+YT80ZPA z2@QwASR@wxPp}FOgT+WJq)f014ujc9EaXM73J$X})Obs?z?giz4ifHugl(0(D`6Mq z7Wjs8_al5;xw{g6tlR=01|yjc;bY3(mGBwm7B~hOlg5}SEDg;AmL=#8hKq@!!;L!_ zPD{hY-xhZ;4U;a*u*R)HRcY|(mXe`#@$>v<&Y24z6<>aCN=@P zlKN#w3e1s6w;KPpbr3-xbW8O+0xlp4z7ZsUe^5vQrsF?M<}&seFuo+nPb(S3)($ zjE3oevhidpC7CG%>eW7f4^l{iL-DVEj{vl-DFmwCQ&KCzo7xag;I|*k4J6ax(-;5! z#Km8gcV%OFS2mV+CG$>giT#n=!IJSL0%op$=PD$@Ny3OZx2@n2(bwuizi8PS8cl0Eavq@asT&t|!Ed_ZdR$aLNRggEu^T zBU=Z{4Mz~R!M;Pnww5&v!w%!|VX;hO(QlfFFo>si?~+Ldf?#Ldf?glyk^8-#t;j+Yv&(48_FEr(+=BNUH$5;RPi1f>fFwwb@e~M%fyql+KF{G_p(@3XpR+)rlBjH$ z#D^eIfinnhmeghtq-MoZt8}a3l1CKeKy48~F)%}CFRafI!c4fHaIm(Bp)kIne_vSd zg!{nCMJCR04Aq2tB0k|TwZ7*JRpoYusBE1w+{VE0{VeM(!a=xwm#`G^X<-aN+Y##5 zQ7meZiBv><2*dcGpPiErVGvJ6!-tSe48kB?Q}Nm9_z(u^*_oU3GhUdE`LG<}a*~cu zn0{OF;c`+zgv;4jKI{m52wA=$492H8@%euNO}-Eh-BDWxrGWU6K@4)<6>biIaEhEC zKnOXnCxo1zM%V|Z2oOTf?;?bpKS2mNf0qz){ue^Xc_!r-a^95?a=tfV83ta${nR9! za?U9*<@^%D0WdrQa?UzYnG&)s;m#qvM7fJh-QjI?FeRTEKS(F_1&)t#2g4~#;W#bD zQC_z+ISrpnBMN%{{NelGs89zZ~P|5*j63Y^+7 za7{9>vSFafkmXbT)nH5!0;X$E5K-CgO$ec@B*d$PMT8K(lL#Sv7ZO7FZYG5AZ6Sp4 zy+#P(`+^X{_Y)z6OO*2dS}37RXHn?je~l37ey05(mx`MrM&M^F89&3rN`%71hcHOT z%5IB4oX@t#XLAPg3#Ug0us(!*45XW7XS=r}?1RY#A=e`m7tX=kv|DrTV7ZHQydbWzyjU(iNst<283KucwP#8|$z%!q=L9$vQBr|5B>EGGR5q0j zv-vC;IHh4Ai>hMIAjp#9%pgcDE32)FQEkdf)k=x-#AG3 zi39b(IbF}Zr(qyQw$4=aIl>tjfC=}rut5S417{axNjO@=mk7&{2_6>CluZ2ik$wgk z#G4WmHic*Wrsyc^dR_-&^0up!_0kSFY`gp@eA zQ*tw-OgYNj6&td-SkubRL4yAdsdCb z_8$^)pumzU0R#13NJjM^pKdDYz(UYXC1{Fn3h5dT#9KJY^f6G-O_)mA6&RJrferKM zsmY;4;gY8zk--F1BkMqc@E=n|SW%|cebs0%rqKeXmLG3gLn?@|dfEXvN9sClr{kBv_Upc8(s^v;kD4R_M&hjd>EA!(m9k6_(g=kpRXZvWAmA!&~NHV7{pUx z_z(v1tYpw14yRxQ!%5E$7=&zJ`ol0BFX#`J2h~X#x|kO0O<{1*etQ$5{SF{R`$>DE z9p=%GpQ{NM=s^PQ#D`^M2PgcSa;GQ@$>aZrogGDLR8a)msIf8{Tub7!jl&9$@q&2z z$v3u9V5Y-tTBge&kveS&_muhuI?j-oqA!?lE<+eC! z($DGy{gkPoza@HueUs@#n}NY}!}-$BCkPEaimzoo6-ophhwUisiFWJ*9P7U~;XJLU z(GKU*kM_BXFr;6ANVADtoBOex%>GGizu+<5BDR&B2D5*wE!kJ8)^f~wk2M9>!Kb_!YVyWb$ z32>k%yiE}$IY8kjAzI6)hhLsf_)Sg06eaNKgH-km%Nhz|2k>wWPt))v4FM#vVB-aA zC5?ype0GWQ&uoTYuqabls9*^DD0fnH;T3reE1Q_6V>llp30oEQAW=xb8wRC_HH#Hc z0*1N~5thn8^!7#!=R7@JVvyos~~+oLA{_G zFhws&94Ud)IYAx4^waSV-9W(T{=-0aT4(}-)IgR%1x{-i$h1`8l!k#)_KQG)hiRI+ z($BK;G0?ajO&_eQs0q9qAe8nFS-~;jOD_0P?Ur%mXcD$e28n^Pg}?pyl=h6&Hr93HQhTLBhCRRM~7Kyx$(U0Zco+WFuPVS4g4E=;0P_a^ z@wq8MY9K}84hgI#_z?fIY zeiC>f!5I9j-y;Aqqa#R-L9&DKuYMKTK@G`B5agOjFt|o|38cuij=jyMV>AB2&aOrb z7*P|`nRExk(}AZr@M+8)q-`#(=EB)6Ov>8Fe zz{4bPDnV)>^&l~@yiDmSrR1XjGz>8q#FW~&)~46jmKjoCq)?ko6 zm@nhek7g0bH0b|t!KTU)#It^U!u1T|6@@N4+2Lm~{MQb5p;aJg>6l7+9ov=g zU=iG7KeXvM4F8bsZ88ycVv>t-hkKIgF~J^h+|AJj!%h0k%(#Q$G2l&JK{x56_#cRW zXcq!538-JHH6RJzCQg3e2LzJf6XN7|KQs@J1c%|@fBdwkHkS5OrrlJGX^fE?*cupO zG5p`WhK|(23i>sm{gSmpaRg0AYGNI9Vz90nCIuuD3fEhvSAObD5R|5+8>(BAhuA9( za}jiEK1@>3$X%Z4_^ro3ra}ZO1j*l%6p~<_Ao;r)|9DSDa5etbFDHsX5=5w%<(Hr5 zV?t)W(pO)PjS}QR1pMw({c>1CbOQhD&ov53a2)>C?}Z9Uz|A)5cd#_lK?JD`Xa}-= zB*uXRWALwjnKi0QuuPEr<#}#E5~LC zFe=9M)EKG&(;F7xgk+3L#^7Fa^-DV+r6Nep<1}DY9+OgIs8UU8SgNyh42Z_{Yf|I< z8$p7mBQ+lr3*RQE#!+>d(XcL^P;|;EVi2VEFCbq7@)P{4UnMRxQpIHriUCT3)Y9}p zEGCM=-rIQZy>|`M_d6Zq3pKFve23rt74oh%bfo6+6M}V|+mch12#~2p&=J7Y;~z^i z1b&y=tdInL?_qI3X*xMo8da$O%!5-UH@UIoCT~V~IJwD9Cr5*hQDCAG_%T&Co=%{e z2o8D3B~^fF1j_`;-_7`kf_U+GEJ z=h}GuOi!x5)RU?&EbAjA$lE!_fol%JKk^jphXy5nt%e{I9-0QCpFLY_E@x0JcMyj`PN}vhGn(!EGAn7fJAfRNEC-Q zL$&47IR&x0BtS7B?xqg~`EI@^o}I|fMBbf%$~WPWY)Q@oKyo<$*-h`3o)^!FmLny{ zc$q^^^s@=k&nKX$o%5t7tVc&W=_t&H!?KDASiZL6K$1NQP}8wF$+}`iN-+5WOt->h zf?erkg6%t7djPmy5j~#04;H;Mx=T^h0g!G%PXx3}fQ@}6ax%Gnm!_aauJR##o7gVU}fvFl1TUASq-N0EHA&7AIy~_rUF-Gh%KoP}cCXZMizZy30{t zIpEWVZy26yTmK?NxGeYppAQDp&l@8g_)D~ZckSot9}MTf$M6~2&%>7KU!eVqwf}hS zKUw?F)qWaEq`yJ?v36iNkS?qpSWcd8J*~^tjS&A9&WN(C=K*ow<=CT-8edz#xNe0$ zW}m@m5`0~6r#1w%jx+?IWPsX91_gcw5)Tdc8RJ9dpqB(nL&7id0Y4_b&{m&^paIZn z#ZOa?4N|J%5RD(F{k-ge;k6ns2&2`MSk~fN z;jREga{#5pfKp;WVf}9$1@G1nB49+~kIb>yw*HS0)sS5$C~2G}1>V%GllJ!~MED@> zuhRah+JA)h&(r?3+JB<UK+YcH#DGR zgjN#K>g;D^7$x)tnjTQ-abktg@74r8I<+toQ0TjA`fg3o7ixMyq3^Egdo)4cTGImx zeNRn~4sS#K_VdfzoB8A4 z!s&!ca7NFmub4Bhz805$EI7QncILb}VX;A{G0pfRqr;N;hlWy3_@$bFet-gkY zyhA&RUy1|uBH}X8FHT?5;~ztmE>8Kv1#{~TU%Gfs^@>Fcm(Nk+94-;MuPKp~pt~*s zpy)p2{2fysY^xGMp?NF`@Y3+Wqc4abZ6gqc68e$aKUVt>(*8rWf1&m-(f)PXFM|Qn z7seocsqs)<<6(Y{QAAziD-n}DmshXIP1Cq#yMeCUGgQ-Fx~71V!(KXvz5N_gTBfI_ z2Ne3@ntq=q=pi4%OF*IDSJUs;1bshE5BN1}h87SJFS9M_5*TZvx`kqdz>6*hU5YLS zew3!;!U5@KXn&3NFVg;c?LSHT&(rWCjlWs@AJ*_OjhFhA>-tpq_34r1$TOf!T~WbHqN5PnfU z$p1!-zeVF8)&AdUD25{FrG9m~en%n?Z)mZN#Mydoiri_jXiEx>25cVYJNEZ^N z3qOh={u4F*nc9Dj_Ftv_w`=&2h9dHa|El)CP6+=O+W!v?|E=+DAn)wD?Fm6A?Epr}^!$6&8)y8jDU3u?S!Eh*koWtj^F`p?-}v`B9o4 zQ0ULn^qZQXuhaB^LVvcVhiEsN{ya?&DD>xQdYGP#(l5~TfI@%1roW&G`W2cUQ0Onz z^cYndO@E=L2Ne2WY5I#B(u)YL(-Tv`dnk&4(!PLS(~sWwJs~D98M=2$x|iy7FZ0vw zK|1_nSr(M+gdVH?WJ!Q;uS?ch`v+-`$}%S0IGQfq|G8BJB_hERph)D;5!3t0sl z=Wm2J>2)?#2!Dc?ionxN`}ZV-e;@6iqWuSI{~YbF(|!z>LXQqF5FJy}l?vXhEBI@_ zf|zz{X+)RtD_AvW-oo11TVvWSp*n8Vbp({WZq<3+=I15ioTZniPu28*LXSaE29Z0O zpg%{`14@s)Qx6Jvg$4!SQ^X$rA-XO0B7}dm_K(y4L$yD}Lo>Ywcl+hqBiUv! zX2NZ@6|uM{)MRJqDgjDX_v)G8fYI}?;tX1+;*9zStGsH;C5YJ!V&l>jA+ zM|2ij{47$M;9^Y=C`u2e(m%+J$81ZK9(qbHiwt}hU4`!2pVE^MzP}DXM8ky|N)sSl z%JaA`&l7%m1|+#p^_Z+O_>MK>dMp8!*Uqb(xww|MHHK>cWT^J%>DmBFZcpjlp7wJ~ zsr_Y|9`IY1;u%{h$Y*WY(n39v&RPIptZTEohW#~O@_A0@^SqxA)>5<{U~fs*N^C=n zNDF3cRm^CNJttUh*?ZsSMgthEPDEe_7MN(ggi=njTOp|EjM1Z$p&_ zzKyQ5j1KVk(SGR#@Rw_UiZTUW%JDm0j@SHh_>EJO(l}$sFPuF`-Sr%5oYzB*v%fAp zpk(rf&g4x$la$7ZYkEMTe@oN=Qs~TtG1rAQ{qM-q!sC{*?X! ze5q~}(U$O6X#W^O_^Y*ly7tf3{v))1x%SJ5gY;ptNj!|&G7_On3q)5EJeolKNLu_~ zYX9Ff{7K{EoYf$`Y(fxAd%UCD<6XZ!eB&cu&u+LbJh^WE(s{Luv3a$8;rvCliyN%X zzZc@veY)iUzhR5MZ(BbQeqdX!E?F8O@F_|b{+>Eq#0`EKZ%}eky6{)&@RXJVzFy2Px6XJ(3h#yrD|DU!0uiBrZTTV_$MEqXb->{7;95t_0_F4Yv4zJ@G77Ij+kPBs+b49}0!ki#(0P2~=aJG2wrF}l zq5o9Vf7S&3!|B$8!6#B0<{Wnd}Z`SmHLjSF% z|E>vobXn=O_!0W=HT@4w&_Al_0fqjLn*L8s(1S?Q2NZe;jMVopP0%Ay=mCZP|1|xN zP0&B4=>dfvf+*>uOB;hBg}>Kmdce4a2k)h&&;4EZxqpNV8{qS`jCa?t7a{P&wci*v zwc0QCF~V19|7uMy<@={DA5@JgpD*JlfYQkL0p(4~_`iG^7d`Qjw($W)69Y;c0!rQf z4SE}8M!##LVTp!AHI%abtjmTT*RX8+fYOw$tg^|n*-q-h%^!4G0HwWg$cdE6ag>g8 zom3qMys;xXvTX1V&}AH~{bRL%y!O{<|19k{x=q?2@lVp>8+7=U+JCL~r;HMye^ZA; z;YcrQvskNH^lDa-S>aOOR&;eVBr9KOSpk$h(7=x)6$g7}jXb9G<1Q@U|D~n}6#9gw&uW7H3r!Cw^e`VJeGH0?mj6>t4=D7xnjRbH z!}QY6ZClOd0i~Y07v>&-C{gWfuNUT87z+DI-Q_rPu|alX772mU6?WG3D@e z)B>bnc)JxHg)QQSS5kHuGO`W`(|&b}W*c zviG4p;~!rtf|Bhh7UP`ys!^QxD8oLybD~42{4fS(EeBA_4=Cjal=64<%g+`fzok5# zba^^AR-SIUJnXZWRc@_3U7)kA!`%F-Zqao2GN-NEp^aOZ?`CYVBN4aFBm5oupzS6e zu-&2zHcmBuZvBgClMt2S4rN zLYv5jyM3k(kGSwg+zw|UD`e{Cq?-U>J8l9tJ>q7atwMq7d>M##dx*tbeylqn7I6oy zbko+tyXSrpcds%yR`hZ^7P=jZBJQCDOnc>kh`Va3`;D{AE!vHU)%&_dBO>naysi=V z1Fu`e{fe$1>G}s<7ewK@I11ObblpVP-E`eg*OPQTOV{ggMclRhBJPQH|A>3CUG9Dy z0sdbR;@8=|-NL;hZqdMq+o_BDs)LXZ9K`(tT|dzE7rIV#;acy)bsk+8(RB@6sB3rp ztha|p+zs}g5%(0k*ez_01UrI39U^YqP7$}bGupL1+Or)P{cjg?t@C&rO3?Q12we=N8HVJNyNRv?&}uaWV@N< zMD{&o$DR1ey%ImIuE0;;xnRVlZ2Z=n;Vs0K%WlyH_-S*E?PmXm0d23d-K=}*D!u^$ z?JmSm`>XNO@jU$WJ>5oSGFa9#ZTChe>K3HASr8G>Z?w-~v_Tgkk6Y;4^H+$m7q0ho z_q`UlyNRo~9-T!zZ(FW^mckIdHj=0P27s{*b)o#yY+)T)6A`@-826QFMBW|x%5x4gW zc&@SQ8TTA}l-u?IwAolR*+jJ1B=m{>(P-l!JYygVl@YhgNJ##Kh}&%@BAjcFhrrm? z5%+w%irkzWaWAw-N8Df8Na9L+O2oa&o)&TMv@0U+M|N$*{n+m5wpj=U&2ghsQHP#L zrQKp=bu_Y?hpZMMt0l*Ug_t!++ zhnyJ^_hIMYi2I0hXvE#(927x|&5F2>IkO|~6V8FCUUC%Kj1?>AcE|cH;T?{Ze?H)C zj_9ZVcj(%G!>SBLP|Y5Ywgyu?R&ZfFLd z#c&Rxa_$R93K>t+)u%8{hT7iGoySuVW_zTB3YUk_VH7?F`C4Jr%5q#73v;rfCq+;8 z)?T*gixd?(yMd?#Ar-HfIyllG{u67h=>AS zJBlcZ*tzy=#f}ZJTrXZ5cD*+2*#7sMa(2#~J)6O+7ysvBA2{F6%)9S=-}If`{gPOX zhKbd9)V?&5xf)+NiPRA3KUR@`x*LA|IaQ>u4NT{y-5{}OA5-uProraJpHpO(%(s7LNa9`D)X9zCW!PKt4P1I ziu8x7NPoJD^cSm0KRaO&3jNnsk-njd^c$*3zq5+;hpI^bR~6~+R+0Wi73n`!k$!f< zCM3(Riu60HNPnP;^e3xGf3b@6*Q-c>w~F-dt4RN?iu7{=yO`g<)>o0fp^Eg)Rixiq zMfzt|q<>pQ`fpXFpOcbkh!X8*eHH1KSCPIsFr7~ZSW58KgIZN+Q|}q}b6yG`$!|zF zr-}5xSCRfeU^-W`equHEV}&4=U3Vv(P(=Da1Jk)0jsHM3{t=iBHF}}O>w)Q9jV6iJ zNU#)aEf;ug}!seaPH#7UzPCmo$QX@Qx%UqO8 z1dDykauAE0R%Q-ljYukwOF|Keq;~>}C_*JDhK)1glKr&xA!*lyq@fQ7)R@Tc(YQ2T zPS~@I8Dg|@I@WTCfk|S$`9fS4WRghxXIvW3zDwfl6LgjyvSIg>S7LK9*HY2uN}1!* zQ-g1YgswNZLFruI-iE%&oi{6JH78CB+QZ~`n9Jm1(wugC-QPVj1=60 ziMU&ioaL>+@WD%PprJSP<$6z(dP85n|2qPI@H0;D;CptcBkppqN~o?#e>b7Jpb&L& zeL@P6{%Ifux9sqE%T(LAInI(QY5)yDl<=bP3iwB$I=q5->0cda4pk6x5EaxaC&s_xnf2D&Jj_9RiZ zc=3wQQz(AsqB{&AiIN;d5@kIeGPXMhcMF&z+H9U?N;GnTAITLQXDA4Tc}A861AUQjb@m)DlA}lzU9og(!9U?=Go_f|ZJv3;S~pSHivnkE;%fH*Z==SQp1xtF2y zVfH%nRz$TEJ0oHI0bw?^AK#>c#!8>%=$yL5XO#3q=iU+Bym|B^M`05%A$WFEkyFYiM5#d2WrtXT#MTi zYcb;w)S^?k7T5+$)H?BqFlOq}TIaRs0I@?v!agGIG>#6+g<7XbemYPKdh!w-Igd@m zAnFwN+KKGIeS0E13ghgkdZ=r}2lB*YQ6oOoZAh%oU_&3ZehL_iw(hTf(c&r(Dh~f9 zr*U9%bnZ-Sow!E|v96E3bqS`VxpGRHCz!`PIlbD^dG&IXcT6%O-6134+kJg#1`y?V>z)C(R; zDOcX9ng`{|TP;rx9?<>tkXwVrhSnIV_<)ZQiawQ(UFK9VLZK4H2$i@Ii}W)BZ}jA= zCc@%71KWY$Q8!nS9=yu+uMh48ydq1CA_-|{2_~ZzaxywwGF_GM!Q4uTJVzq0O^94A zk>^R|4GEF>ex2XcULcXTIPCcStxCN3ChDOU?z#0B-zb51)OxWAyyVW6Ii|_?-e3*y zq0oR&f3TKMf6(yvXw;B5_Uf@2(>r<%xkg3;*Jzc!MhWV2y)4ZOCDYw-GQYp2*#NU2 z*S#8aPdY2I!aJLupHei}JSLj!y~fZRwarTsx$}}lYv3gb?GosHvDEt#iFgp+!Q=D} z9(JIYsQjj;x+1!r9?jLg)KC|h@lgayBv9{isooV5@i=@<#i>`tc!Qlqv7l{}9#Kz~ zXj@K1cJU)={)SOvhH0Q{PjE5qHTB`#B)l`rRab2p3dYm`45*?8u9PNTC7JLk_U1Sf zgKGc-zcAvML<7G_-ke|{<-4oGu74yPC`I~jffT$5@Qrv(5#Uyh*D9Vu9D%Bdog4Q1 zxeaVouLlIJN219C1B)PbwA$kR6|a5<=@v~tJFxI*mJ!#luOj{G zD$;KbPDi5^tHCa4%kpt5wPd_hk7rfXz*Qu@gTWt0jSqUNER;;x#zb;(8}n^U+`z>3 zAq@=Aw#){``xu@!wL(vE(PP?^#MhhrF&G+NUkc+X6n_kc9yW-==vE1and@aA+#(V9 z;(t1>h;6!64>RKFYnoQDC~fgnLyP2{W~^ZoY~eP+7Cv%AcRp@F_kW@D$7+&9*3b@g z&<~;+0fG*H*qafnq!=eOB{`DMI$EUMYZC3JRbZQ^F2kEw;N3584m|nqiMtF>4_@H= z*WVr7%R|7rx6|t0r6b+L==I(X2Hw)A^}gR5~N@wFhR=QW{Ngr`2DO7vPeef zi*&*?;+>^lZ>W*DG4KYC5tTRa`VQ-YYIV^(4ZjUtI= zouI+rC>#7u67fdDW#YXOd7nhW4L}~aV;f}f^n}U~D*?Py-ZkvqnNI`Lc|Uo~C>mtX zCl{!-!d?wA#GsB6s3Fqv1TP?6-fxI$@U6s}KAc$7TuV*Sc|?Jo7^Da_evy2ABq*Ji z*Monc7CAw=_*&rekbvUiTHK#li{XJU_(DD_Yyi ziPN&`(O=xO*3kx5C@Hpre+g`Pyr?!BN?-!u69JUr69M`*p9s+4ABj^cxZj}}iPqXK zuxO&my#kY=w741%yc3kHNM9e=7pUY#`sToNZuCD4qqUOngo56$!3B@76R0UV%PWCu z_|5qAzy>0Fy=Y?_0<)mqw5YMz%SfPwC=5K{3{C&AiuCWRNWU>qMPI*rt4Mz_uoV33 z->M@0vntZ@%}=QP=Ou-wUmlpw8xy)kqHet3GZprW5~wK})9ry~a5eBD_#dE#7(_l! zSX(0ftH3tR)p+y|R0FTvBq$A`#)pa3*q&I81WS_5a-s3Iyb`PV1ju(Ja*ss9GfD#F zyAt`HM8Yq|em9A)z4U%ziwtm!&i_2YH~F` zOsvKV%P9rpoLD2_<%EE2NDYyWZyW;BW7beF22yZMKS-?UeiWF{1S4L6CcdVR2c~m1 zKK?`05F-XYdPty#NPi}IIAvc|-N{9BM*>$1)~%vg7l_?VaPVlCGRj7D-70~Ze^Tob7W;(0!hDj#ka z^5yn`ho?gZrXxR|4yn?)p65w>6%X{_ILH{>Y6FLhISw)g2Rk!JZ^Z{YI1Vxt2Rk_q zG7*l~M{9|LW|})AI~5Cs~zFZdO`c)*Fo;rbtngZ9c0XP$cN{Ld;{}C`SR-^ zW3E$rq8$ZZhx*~yLB?EHRl7s|^6Mb?>pD~hzYfy&x~r&sQE&V@NPZpanYTmq`<9fB zcFpU5x*Q*7$aXkWw!>p&dzdBL!?ChGpk1MAnVTiY2lQ{=4hPHnN0{de8KW=q=k;OM zziMCLPw7JrS`Es=lzw^Ztd>$qN!G5o5&kT2H*c#eaN!KwOJxP=l2 z$#HlUg|}UZXKC@+2;B)<-J z$J9Ugxg8}E2N}Z;Ual9eGxg%vt&rD2^6T)H3y)tZ;~{xG-U5lqca<#nb7lUkB_5LF z@vZ~M!?`EVf2Ay!H4=Zm#6xmC-Y(&I)$TNYUhc?;mjfinD}8VsZwJtmw*&BT{c%0V zLt5e2OFSgU;~fp&ZlP~Ws>i=cUW7R>B*!T`f#-U_ey#_2xE>cvJS4~Cr3%h_scf&1 zoDWi^$CLxE<2;v393;m*E_t9AFCR$G1F6zE57z&@{GlJOe_YSorPA+8c^xFb4jbHD zzp>Kpjk3RDoyzm+CGn6PkMWM1+guOK-z}&-kq@8Wu#a>og|XfM zC10l;MiTlAB)M7gUN3neIWHcv@qBB@bm+_Th2-hThv$oYGKfdvAUO^)2B*eh;JJRV zi|bckZuBF+-o!IPj-${IOH{7+V3H~L`@tlkA1{X+Wj>HRA3Vb2@%PGjNFKjO>WTKu z%Lna==dWJALB1V{7yY;!Nt7dsmbs{ZUQSp)ay_8$5Q>NXBT2&kaU{_{CXqz_A59W= z=8*gcWBDZSXADChb01&~Lo9P2Wb8ze4>49r@?pl7kbH#UkH151WbAa3+Zlh=$9**|cKc;ZdgY!TtTnrEN=IM}u>9B{VLk6aA zl9p%RLKz(E|*mI@q$Iyq@8_J9GK`K6uN4;>o(i`Q(agZ@MHLfWf%9rCH6)wg; zlq*k%ROx~Fp&mF6G7*mJkAB2)kQ|48$NALwjB%m`*?ocB1jT*Gp%hmA`W<;aB)=ZJ zx}5i284tR1Xr)&l^dZK@$9(Np^Qx zcan#?tT##IKR~9BmFW{?dNE1xEhPzk&Xn>3DX)-nie3&Gzdsc>Dm2!`ipG&z< zN;ntg{4Gf0x{fm3L&^bC4wrI*l+&d=fh4Y*FT-V0o-E}GDKC)n5-B%Hc|T+e>^JV9 zFv|aBlHhw=rhg>mUMYW;GC2u2luNpljil^N5`4!)QvJ^-x!PrABoTj_lxw8?H)P+N z8QVkhR>nRec^hM&lf0d=y66b0Xgm!`_H-E>-muh6m$irNi^knmhWkl5QpyP=d%0{1 zWZ%CtmL=m)pzs}x&89GT=TZ1hv_}e~aW1CtUGV85!z(F#H)9vd@Ff(!hp}sAcngJ} zz@KoC;fG1~aoJN+K1;GMJjRkle!EFRua8J#zWPDRlxiZJ3E2YU+TkS8DMm{~oUCT(*}a%4Hu(y+Z6smuT_j=egCvpP6C`2p4wA6}QyArT8HLBgUXl}Bc8iqTNW#v$NW!lB zNunONlSDm!0NDcb<6e@miylC=KtD*AvYC__kkrq*Qy93zr5r2eOetqWQhP0hr1n}t z;ZGS`OyP;>9~9of*l84=gmIk0sNa^UP(@eog}GVNzTByPZIUGTgq=pqTT*L67B0}NXno7SOV!2fh7G>C=7jS zQuqtTYEu~c)Ti*5j5VS#^l3&CdSsA<9_>j&kIp2aM|YCY<8YGDW2}^=kW|hmOSy_9 z%Jm{Cua$C}ln+a}Q_7bi`+fyC1&|Eo{GE(vHIdIu%(o=L-w~45Cxa<`jLU|ToaM5k zNFIyvmSh&jF_On&yd`-&`W;E&FCht@Ye{CKACWu(?TBO!+7ZcIm+d5p>))30JsJNE zN#yebN#G-CSofWe_614yHX{lC_K>7^KazPa8wttRRTKvAY?9EskR<(X1m0GT=)^+__{WtWiz?iP}BTy_sh++RFPawXa)N#Nfj3H(PS!QTW6 zPQrI2ITzzBB+cU|K=$2>@r2^%q28oiP3gGqItl}S3&{fX3n`zXbj<&|qna$Q_cYaGbbb*L8#V=dB9hMQ3s zb9QSP?ntr_^ApKyUDktS5#}k9o6){V7NbAP^wA`*blG&0$afye4bY!t3Fax1sJF$C zeZR%{OyN?D!z531**c2c8QKA44UDIIDUAHSqwtF;<9Z^TO7aHO2g!1@7n0DgHOUItPqGr@4#}-9 zn?(|K9#3*1?3CftNN&XVOA_UAA;}wY|3MPG*O6R=_CylA+eqU2dq{%+DU!hLCW-QU zlO*b456MkvS0rKQCnT@Ix`QO@VXsX8fh5ZNXOd_~HS0s7d>WC2-R-69E#*)tM?&`f zk+EqqoGayRkS)$az9dn9dr1;+0}*Z_Wmib*hoecNU(6(Vfy?HQL_04eiTUwNNa{B? zLH5BKX&c3(J?G- zym0W>r|gdip!7#Cg07H}lq?SpRRQCgXf!|Z`9u&MhhasB0SRhLcY7F_FreVpM(i>$ zSbz$~5W}I2p@y+I;vanC5-bH#LL{-JfJu$9CX8Y7zz`(fHli(V44RC-djK_~S7b1l z@kda|f6L_6$_IXZLtg_SuA#$*+I zBh?sN&G>E^J72tg#Lg8@f7vRrE6vvPqGfCidMn{C7EguQrHqZH@a2q+A^A7P#*(~J zyiLNeYjPBYdkGhXwDmlJ!qLwPPl;VAhS?*B za65$8Df0i7N8x92*G=*{vD?j_N6)433yi9ev7NYUBl)7(1!ud2OAq!EqbgzSF?}}Y^`diaWDf}64GT4&fO@@EPkBuxwf9C2Gl3y`)D#^VVJxP8ocI9bnZ8?R% zW$X-+-w98#?0d0WPk%mZ1;zi!*x4jM;Z2CK55+D%`v7;L6#p@!CWNgK-n1BdpRw~O zj9o`GAq*}KH8l(>4!eLPb`95(^l;ZkGQz0I@j>T8lF7Vj$w7xA{G+#j8gu3#1iI)! zxGyNuIBFp5YH@84IIWAg7K>Vb)W&Z;{MY%e>BiXkZA7~M+Y|vko6O0mtA;v4oVPV_ zH=sd{i-+-XLFfAhxSoyTff&+a@@;n-A~wY1sGkxaij*Jinmg=-#xekbqUXm%{Ob9h zg+C*y(g>i=vn5WI;W_l@CbdI_N-c5JzSK2DLwpzN1y7MUF2a8u*Bno>X7iNTpKB1T zu0@>=9Mvc3nvp%jk++nu{@I8hrbp6H+ z!e9BVDfENj2kG}3{_B^oA1gj35(xj-#N7(qP(L|(hAtYD?^nQ4K2$E$*6EN6l_>eX3FzvW{t$*vZJ%iKgf=Cz+DiapJ_+y^L;UjvU}@#?xgd*x7Lna5i?7!a&@ zJ3FQTXJbc;1$K5E3!Ji}e@J~S0PbF+)Y1YTvz$rVXgxK*VaJvKYHSO4eKl}cMnJL1i9oH{$ z*rotJvp&Y0WY<4d0H^Gj5n{(hz@e$e9i|=0%k26`SKzGrIdD`4=3&-HpHqB0_?`nU z2%0}e0C%;89Uq)(X9xa}u9Y3H0cT@JgVTLGa-`8gcC-V|X5D@da5m%Bo50!B$JXV- zhgnti@e^>WUCjxxqsAGS2R)N5ejePp!lCVoxWnupubl0$rvh+h|JZPjU485ZPT8>_ z#EwsZJ2y_dcsnXr+11D8z}eKt+;i>hxE45NM`?&1cK~P8u12l4v!e(&8$0@+XJ^Mk z;FKMeA$F_)?n#UK7{A8Oj&k5^>^Sm#J3CGWPT9ft454dKKVJ>prxtcBy1>qk4Zzvh zQLxs|j_ZI^cJMvT1a{m7+&30>Y+dKDTLN%qKcBhY&W^ShI$}@U;56rtV&Ie=XG&~P zeVhW^B?04$IS)3x$YF08c~a1{<6GcX2Jo5V)iPviGk@F%oU-Gb5Ic4Nmm83;X-C(~ z?Ap~7;B4Aei_7ioI2Jf%2j8=XEkX6M0Jyc%PFfDf^n^^;g*jfm1e}c>KLTfC$KzY=?mrsd z=-Y8uh#l>L!^?DWhiS(dH#_v$HtwLdp--a~w*>FYoAdeP+wAIN4RESH@OYXZLF3hx zz_r&Ekyz7?$8NW)k57QJsgJw1+1c?maLSH{rEWoXcz?(9MwzY)(~gUQvuRfk0B2*z zId|CE@dR+n4z+(9WXG$(@n`9}5jZ#Pn0Ke09czHIv18UZY}cH9JLLH zdNah1Pl2On`Q~Bv^OqmAvtu7{Hg-Jmkewag!@eEwN+pB(c|G9xJ3)parX8Drv#F1# zfU~jV!bj}vco8^NA0LI-@eXh{`wXvax3l9X;B4%8@=?2WNu$SnJ3b4sqa$$5E!HI? zp0Hb&^nDUv#0T)1>yl%hwyTeefK&CcH^h$XfV1i6{dU;Z$ML|~)JNxM?Ch8eoU#M2 z+3_Q&e=GuSgG|?j*+1$(>(FbFafi81d=fYtJJvsE*RI|JPTBFZ%st4CZ-7epJA z2W3Zch#lR4v$5l0;B4&p063d=we3|qJH7x;*-<0Jj^BX0Rj&wnZq~;w|FnDmYTj#h zcJ%(2ogF6vr|hU5V#n#g-D%N3`u^LlKC*zbsgI6t*x8W}oU)@qh#i%{U1d=p_20C! zV*qeAb|k-LXU8z$lpRe%?3fCiy3f+hG22z6x9#j01e}c>sqfg?F$y?kN6Qd9W&oEf z)5GkzcaPotQTVQ%9sS<3doQB`IAurM5IfEUZkld@JU8p(HQ;RK!E@h-$BKY*G1n!R zeIPvIm|2_8aoz_`+0iM)j&Fh6YLX#r+Huu~4t+KdcbN0}aUbD*M+-ale(Z>EH4H}6 zj)9-}c61N1V;pcc{rnT)9x?KyplQdApE_)@g&l1^v#XC{;8cC|3bEr9;I;(lXVypi z&+Y2tJ>V{}u;b(}?fUt>z$rWWh1l^la5nRK)>n4p)pFo$#;XZ??d&)gIAzBXA$D92 zoZ4^HtHrF3>0dkanN8ebwyP%J*xB&`aFgTM;@^im>|5WCp&@pR0&aBxpJ~TSz}fWk zwcj~xsYSc0@q=A`j0R5C$EXlHW&(G&g&nniwCm?PfwO5>SM0MJuighv*>O~e9p3_X zrG*{qe{$$Eueif(SGhl9-@>9kzWT+kKDz(v+c7!BjzPe!jngiE{&)d6n|1rQz}eKt zBfr^wH&F{;lBoKa9%4sJ;I>=X@eXjW2H?#4xZ82r5{vri;kj1#QANNhJB|&pV;OKZ z`#vW|@TIne9Tx*Nt^S;^7Q|h=Z+oD~~ zt!q~w*8->Ps0gv+4&ZFoyHo4I14MwmrX5N3?ds!2;OJWOFzX||fp3Qz2ZQ#nS^)R6 z#XdumMt1G$Dd24S$AyhutM%3Uz^VE;C8R#S1up3QHM3nUYwEHa0@{mNAMLT}jiQM= z%=^KCEnV(G(gd3IaUpQZjx$2+xE8nxNhVwTe7?7>8@S#u?Kr(X{u+{n9q)CtYges0 z`F5!C4r*6Dfm83x>DHL_@ept}^Wgiy*|e+MJKNduIdG~z&I_rJUx9l{H$a}7c0AF= z&W`^8XJf}bUG41n7C2?cx)3{(y1C-pd&3sfj&;D<)WC0Z!R*aflr+ z0(YKa0|iYxjz84Sjx&L?v13vXJ3Gz;PT6rqh#eb%v$>C&*3-_8rNG(PF*4K6jx&H$ zc3d4|$9mu{w5X2`z3l8551fr1jeFbKF&#K%$8{lg%mJ>8OxMjZ+tu1WcJtto@Mvda zM~%bm>=+H4vg5`OJ7xk`!(yG7H^8nw&H>KGj-v%{59;Ym*i$aB+DoD5vkNW3AY9fjlI=`cV)vp(jGciEsrq;=#Evh3 z3wj=4wyVD%ZD+^7fU~h<^K?5q_5i2sz$5a6^|2SY8$@!9e8A%(2m`}t&Xuk1WY^T9Ajt4YT#__m^sVNj=uq??7*Yegm!EO&gMO`!;ZDH z<2c}K?C6x`l3T;L!<;{gfm3$;C&Z3ZfV(k{E&e{L({V1jv5Py*arbB7ie-K}*c^A~ zXWPw#*8!*Oz@tZg1kE3J0T;CIZMG}&Xl(NyzyrWFx3FV(j-4G1bA3DT$S$EB?SY#o zl4E{NJJtaAKny|z%=#FIO`jYKJ8qh7XU9jtsrtYpF@6Nq#}B~StV<^3+tr6N2Y>M^ zz>Z&`0jBv?`Yug>UG@F z0C9SBgHm`-*CXT=8HlT{U48S3@NkSUWm!Y!OXYN^!S^_DleFv9DZOK$^ED{*+jE-6 zXX@7uxFw22lV-hL37o38J44FlI^aGC*xxnzRxkGX?h4_%5IFUmLQ{{KcBCwY$Lnf3 zLY$lB-1>Bv4Mq}WSzqQGRL;4;ZItPPN&K4SOn-4~wB&23@tOLK1#YuWB+pIU7r@b& zXdWgm<1ClGZqcrOS^5#UjwK3nrVEdeoa@p%n7*fG3%q* zYV;4OUki=T)b9@9CdqWYY)t(k=lgsa8lS1(6~O&sQGS=Mg(q#PUu%ue)UWY6{AB}O zF?nw4S7*JL2Z^e!#%Joc1i1HPx=w4B-*Feg<2L{V#iI~@1hwZ^fEyiPuc=@2OYj#3 zz(Q1bWWo>PGxb{r+|4pwpv14KU+2qxd-14&A3^%91MW4QNS>Se&A-C$zg;yxQ@^di zy=|f2s;l7HA9)c}&4)qy?E}t6zg-*gmjxsrw!rxjRDR@%y}n3}`8Dlm0bGlKdNa%U z@J)Wcy|jGIc3J^kkC+@qz%1u$fvb%qqQaIiKZ5MI1Gx7^a?G!(-=)|1;|8{HqkN`* zj{uhu!z2Qxe(wNB`Vdurjn6E#Lb)CqUsE*Y5O#N0%TnrfkX8GL@oYHTI#%Gq_v%uY9k|AvBciQ!SeGJq1 zO#M~@*D-+4)bBQpZ-mBY>UR%tw*>H+`pwzm>o-c{GxaM5&Zd5^)cD3|e5QWa0r#?n zeoJrg>la&r{Gj$?>bDiRNdbIj`RxNP1303>mLWfa+Fz5c_)8WdIp)_azbAmB>xpWj z#%JpHJa8E?Od??F_qoP5S>rRy?>pe`3K;)P+)X#(FP~VfHy*@Ik(w{Dg~pGda_)OO z{vu64zNQ^-Zo^*+0Si%0*Z55R`r_tfi^wkK*DU8Zfm8i*rp9NM^XI^2#4w3~sb4y7 zVpP3hYlj~cHuY-=T+n=EuG>3ne0NK}p!>^A;5Nu~k*oMM^L^ni{3R-jb$h)BMY|_T zY>DtAsNV8``$#0m{F>!5?je^A27;(^G(J-s{H~gzaoQg(W5T^%hx&=oi8)^HUdZ26JLQ= zerCQ~fTQu0AWiU}4!X|7y$0M$#1pP1{?iep-wBW5FB*yDm|s)B<-jTZibC{T1>9iR zMv%6WF(}_1j|bnEnfd+%oXQus{`{b@X|MN$UoM@rd`(<04c9#cm#5)+h2Yj}I9jLD z5v1Rv8jkjk=m^4nt>K0WSRk&=lfJ#9LU2=oyAA17bVq48vz>ZRiNCxoAyHx0M<#Hp zKCnf=k03h+0H?h~r?!SZh+P~=AyC;Nix`z8O1h+!N{U-!>7jP+ZUGkNNGuy#~yIlIS zV{wN$PJRWPY6sVcw1XdkQ|;h8Enl<0b$ZFi{S<;b1~_GhTDX!PrX4vi+u3m;aLNwc z^6?|6-mU;n*^z{Dq$3FTtcFVs!Tqe^YK7pszT)RwKLnSh;TnhFR%Ic{Ti-K z2;YBzQ|p|L8qTbb;jh|_FD1aK`oOJR!g5{&+!mw~q+3Y7E&pk^zM2f2%J<%oe2)R{ z5}9viNWNbI7xdi4>_@F%^YgtwB;U@!DSOp-VWh8F&bxr?jd=4g^G*JjpD%7n`9XYU zzf1>Cwfn<0drjOR4L2kNR}7pgzmXcwv?F=9UH|P5oT@k6dL^`D2yn`daaz8n9VY^} z5iCSCNyC}?)p^~nJs%F7(ho~>egu{CaNv}FM{D_-`c(jTfz)r7hBNhx{M!|O^HO9P z{auD@^!xY$z$yK(#N$VhenWv%`l;{7f^cQP%>xTj%?{CTH*noVb}_%Ee%0Ra^}`g) zkDz>O0jKmU(DF6Q????-9D*wWZY6S~EXy^VS+)B-)b#iv;1;3+}aS_It_PG2yVNEyF3K9SHo=#!L{Dw z+p#$WH$}s34Z$tfaJPowwrRLKLU4OD+`S>Vy6^gSJQ#u-3fxB2Dpl>H8qRE|dw{cP zFX`|3?d8*u_RxATR*VN_f_Cjzx*mB z-#39%{bQGwuh~D+KlE{M{l$-<{x(R%!DUuLT(O3OOQnRkD>WQk{UpRaui@bOCL!)u z;8r4)iUclRqByf%t@y~UoVNj|%6VT%Io}7IS{HvCl5gFQ?eZN2oXYo?kbFl1r|ebl z|I>A*z0U(j?Z74A}PnEJ6V{O9rN@lcQ*{eWu(F3Pt~NI6f^aFGz)JPnr|f;&yaxgoeq zG+asu?luiqF9i3LhC4n4_qK*h)o`z&ob>wm0l2zGT~P32{MXl=sbBi#TuZB8ldm;! zvn=>d1}+0QQmsx1-xa{w=y!{zU%e2%CoK71)A$;M@O=r~Tnl?=V31e#9umU02sj(Q zH#NS-A$-3A_oRh>kMH&MYZ}7$K5(BU#S@zCW!DcrU-J;Yy}+FvPZ!tzmi*}RwG83A z6gZpmW0(w7eY6VU%K*-X?`(~)O$guhz}d9tdx2BqTDuUw7cKeT)%ZGu@cm@Tm-dsd zU#AehcEEjPQNL+F`}NT!gs&TLw4Z1mX8Y@dNjC!^8eh7F@Er|Ywn>Jt$#;jwcW4OT z%fQ*x?+3uC`t2FQ_bYI7EcBcGt8Z_w5WX{kQ_tOWTGQUofm7?wJ|TQ9eiQ2#or}&* zzUzQfe20bby$ak`lPrMmG%Rj15Kr~nKZI{1aJK_v9;SYi9WSciz!1LEfwQUK^}wn6 zJtBnfM&S0E${}p(x54%JdtYR)@{>gCA#?uy3^?`tTBkMn24Rtw0RZt03DNI(;MDWn zFuq5DQ}sJ6gzsxhzL^o9Z$t>+O5o1Y6_e+ty=h54->4A2BY<0J!S}evHztJdTi|T; zTbk_aH!g&4D{wY^hgS3X#)t4_0~hpulUaW6YJ3wz`0AzD^^bPIss1rJgzpI8g8UPj z`qixN+dDOc?{MI3#?wu}WuP5U|2R5?Z@VR5$5fwhMhM?%;B54}MdLdrgztGvzTR-E zq3k_2gl`&fHu~MC@f{b!x7(6$a7|yo>=3>afZKvPGY_-9q@?+LIU#%;kynvoB8M-;8cH`8^X5}IGg(Ii<`gcZ%GK>W=p<$xan1VCx-A1 z1I|Xj+cm!O5WaVSQ{NlwHkPq0dI;Y~mVEm(zB5Aj>SFUFSFZ?pZq`QyaH_qW6~cF|C0}wQpYQAtzP`ZO)bD8E zRQ;Y4!gm62$6MI@pr+rsA$*@(>c_Atr1U#4gs%~BM_cGu1Y8Di)Zfkz;X4mFoASF# z({F7E-`&8SW1-*RCVu^{58*2X&PKoGz^U^4TL|A}z};Y>-!N>dWdKLzcX0?`9&kel zWE^JyxE7mglYk-Ur6GLZ0B2)watq(y%R~5D0B5tmyBIjte>a5iZ2|5L$1oN_)84Z( zJaz)&$=<6%_}&N3#@_FNQ}ugw2w$yMc6_aXQ+(Hi@bw4IW<9hKxD09U<`BMzfm;Qf zd6?xlwzbc9eF)!t;B3}^J2k!=LioP4M?(0P0cXSap2qiR2w$X&-FUSIxD50gYWk0d@ZAEOjeZTf`g~7@@bv-C zhVKfE@97Y}`+>6=Pg{5M^?N3SZzym!?Rgq-sy#m!!dGa?cZ$aMLI~eQmV8?@z86FI z9=7D$rSZL_@zFb5)zOC3@dDNKSnS6URKJ{K^P3b-$)H-@@>`l zMrb&bZ->S=PQ#geyMUwqNcAyR!k1&gA<<n^#|DFk z^qUsKHxxL!o^a|uDTr?@a2XPJ3?vBbsf%XEY#=oKvzEv7uxrQ_KTL)Z6 zq?3MS8qU=3K8Z?DF;Ov9Oc`+&JYx&z)}AuzH>F4$=A7;Z|_zRNY7$+u49yG+BGeA_j?jT+A6 z+oADYrQuAzeH!0p4QKMXy?uMH)o>j-dWKK;ye1gm1pacdLdo^()i( z)bq8V_P0snyCZ~etH$?t4QJZBTjRS|!EZ|i8 zdqBgPe5*9RM>U+uw+^@rq?3NzHJr(}P2+n?!2mw|M$_eBk7@^#ku{;AH|*SsLH#8qVZf zs`0&};Y_~e8sFO*&g9#q@qM7-Ounre-}@TQomR}HJr(}P2>Ab z!h%1Gq*=qy8~L!c8~u6dghRBUj_g({Lu=eBf03%hhnEe(N;8c^b~-y9_vG z?_3RM@@>=j=$kiXhsk#zaFdWo^;@XnOup2^{rV`=a3)_};FNwRYB-axv&Oeb!fa3IFe1F$)Cf^?5RQube z;Y_}L8s9w{&g65C^zFS{!L% z$+t@5ds4%heCvQyUXDx zGx=s|e6MOalP_1}dqu;Ud`mSx_4gX6zD>U6z^VSRJA`kO#`m^{Gx@e^d~azuQ@cl*9D6x% z8F+wA^AY7o2g&Dwlb7+hO_MyP_Pcq6{2?C^I$yuZ9@~iT_lfTcNIHmbbWY)yL-u@89jUV%Iwv%XaCIp85un?=n%hsd-vv{o`?17(}$m(kb3HfqeeQ8uZ@G5 z#}6^~%pjgkQg4=llaBaBYWMPavIDsk3$rG3O`e+&lMh0nZ#_@vW zHKqUP6&Yb){WeceMEDdfOkMq~m~fi$$Cp!L{z$8brnyDKQ&UyWIw{&?sZ4F6i*OC5 zHfKe+0aKftB3v!zV*!s`qH6}wkdPh|uEqZl0$odo2iMx&W73nEy6G3`joDFJI=#qq ztP##P!sSNz6oe^?HjnF4*Bd?{E4REfE4M7CIB#Blc^2wDKc_rDYi@pVepyao*1{gW zvKIF0-YX{y{i3v_7(Ii|ib{&JW|tK#%r7g?T2z=*jO%g=EAkg)m6zpZ&BJvuJvt8^ z01YF%d`Wpleo=Ybw&+xqg=phc>HSpG}=QxS@` zo*4~Ovpq~DqEBS@hz8KFd-fGw-fx&a`>1rfvZsG3&;ZADhs-|ECMQ29w;;PHzbLPC z2`j5ul#NoE-H)GDVid_)l3h~HLyO7^D)Rfq411Gu%E}9h@$%T1AyYDjOqe<*W7wg? zx^&4HhGH)$%rDEBepvTjJ-YYm-mgby??Y$w%FLKkQkH@K{y(b1jBdj-hE1F>a?Ggg zNko&d@Pp>S>E+|J^smSQY0rSu_5m8<+w>W<` z5OZ+F1T=M=&Mqv-&CV++o>MS4qxG->S(D02=3;ox7>Oz=&*-?gU*AqyRAEy^%g!26 zl$$?$Hdtnqlr6~3N1w_#tb32FoU)>&3WUQrVfKWQvZ9>A?(2VZZJ@ zyJu!iEvYQa%cuGQe z8M3G}hfmLi1$jAGztI})09H^bsZmjY73AEKlDQaG4wx=9S@YHE0oMQvoQeZn4i!0N zbMq_8d3lso6pGR9fEA1y%?`K%g^LPGv*;&#VbAWpyZ6kZ*)Y1QQPcT>t2~fuBf|+4&0# zu(|KGPYy*gzd#h991UNM{3razEPoVOGknU&@F*?C>M zVAqpB!^+MUJC@juEXyfd!f0C(8`ylia}M@4vCUGRS5{D3QBsB%#Z*FNVPQ7*&2x}W zyS-QlBXJy_L%3`Ne(?}4DHXrsVcZ|FTgv`B5)=ZqT>qO5a@tslZYTUVM1dofs-!=S zICn`!en}Y~N&Z)b5U4xNN%WxS57(U&V^aCkhzl3zR+j%c^kS0!Q#2RU(fiHcm506! zu9y;RME_a*fqTinH#1{9_;=Ch7xg^?Z#u!@J&MJgF=J3%%h|R3HrGNduPP zt$@4*(Q6Brlou?`_cLpY=bn0I6P7H@DfE?nGD%IIv@j7$c>lQV!yA&;Ix%ku%wv&f zD0(GDPtPwyNh41YcZ-u#Sy4g{VJYh0xBap;>2;UCpHPs8m?VDTTaI|(n=KyyM&onJ z7v+%Y0=3qeUAB-v&n2oIl;eDgsaR5)Pg;wp=aVt90aX-@Im?wX*`@QAc9*5$H#?;0_K0AhxUwkS>(_@12{JcuMnBc2) z48`V@5&sZm9yisyfB+2l5ohb9u_6xcq$dMqaKw@=AwC&cW(5 z%67eoTZlJJqJ$UoxOslWVRRi8uwQHSc@(vKq3mjYO+iT>t%`Y*6}3B#aE1B4w);4+ zsIZ9Zz$@qSWCBqXZOjwpcWZyVA?dUfiY_gu{va}W#N{!I%JM48@FZV0ps#uCi6vY) zUUIDnR+gW)Fp7DNXHmvm2+#9Yu7+L*{K^M-xh+Poj9&UsGG6(D^>etf{Nhy}J#LDs z^+GBIsi6Z|%=@#@_YH?9U;w~)Rf*R%qNFPve$SVigVDzC54@K4L_|_Kwjlg&`AyR3 z9PO>WJeK@#YUrCC>JJAGL zgd)CgG`c*$jCR^UFH$zR(Ii=NB7VT4vYgV=d>Ud?SFQQshNRm(zOGVzwK~raZwX+p z1&v0Lis*AAZpZLG-|6y6BkO{M;(7+Wqu`DDK5G=tvtV|bRJnFBA!aF$l;As z1UJ>JC@8`kTG8}(QYaU^z*j~sr{+4+Z>NJ*l)RfmWZDI{Bl0yDk^JIl)IWivQo$Wj zH176DUP&45D~oy|D*alz@|T)2YGh?GRd@Pkiv3vu$Dl!yz9RazBzkiQ!CL&Ael3p5Vfx&<_GuyER?Ffkz!6Fh**V0QFSP8b`HOcgYM;* z$=n6g0>b3Wp-50Qqo}#Fi*ktQN+Lg%$CUEvRb2KThoTBHeeOH?P+yQqM0IRIF~xIyo`w{G##)eF;$Qcu$72hMiz*jK$qwgn@~L+;b`FnS zM5C=xwLe8*y^9en8!zw`;O0J>_A57|ytE*DPGL^@yeP@1BEFm|U9jBBjZuw5W3J38 zqjoN0*7ECUgCt7zGmlz`H@r&9viS=o(Im_u;(;S#>g;>_k*!IWV>A>|vbyW=>V6X= ztBe-aDWb3Qqh)o8_zy66O88t+X(Ik@A1`W7L_do$S;9s2iRh0b(P&XWBKlHaMZcCr z(z}=dB~dA_01@3T8mkwyNPDxIpU6vFM1SF1fVU?_OAslKV=|I?sLGH`zFx9QMErW6 zYSC<}DG@zAcF3>G2Y|0UOhojIo-dXPsE^iNLml7?BBvcD#MHZtV$O5am?vIqLqh#? z&U|!h(nC&k@uw}|>fikHPd6mp;-{6A^7*O$cV9oZHE9m(j(5X7dfk;p9Q#)X(cgdn z%c=YQ#$`>eAAY<+c$qd*i4{73br4IqGUlZC5#{q}kIb#+HHTGxG^RHesTNsSQUE7P zNf91Qs!m+llV(hrI%0D6u<^sQhmSaF$T8VRPZ%>To9%TXsWM4MPh~7NX8csV*;``* zkqj9=WYV+|Q=njt&nZ0PDB{f?HgZ%pOQ}gO{5vIc=3r68+%~KxL7cqGitM8D1&DCd zc*LBXTp8gxk%)+cqWNWWa`N*16ed#8hVpT3BnriHP=qDF;7$^m;U1xq;K?k#5}f_A zE7&%tdYxvid9Y_+gdcUPH*DUPhkIoryxXbX6yX_14Vj>9Vb8nuO1*T}?c?tzo^cy% z`^FFbZaZyc^O2iI)_wbdA6RMsk%LG6_WiHFZTKN6W5W;0wJ#V}XYi0un5*JvH)C6B zeEOJ^(psQAw&91`wVlDWTGtuc@}UhsxV1O@P&1>>&<3B>Zu!y94L{&}h3Zbn7kBP? z$?Mp9r~6W*W0#%({t!7`fE-R>9mYiSORn8xaL>Ujho(Q|bV$u0_DLg$>^-OF%AxR{ zpX$7XZz|qqhkmg00lFHwCfBZWFQM-IaN;*hPaHk$hYy{Mq}v|&=*#n|p`rZy;m;YW ze;pC^8xD`TC#Tmi7$*E0GOfF0BlIC4; zDaCkh^1}2EUYGihv_8YT=G0`T>5NGAxjR!&j<{Y*lO3t0sVAk@N^S7*(O#3})(`GZ zo|4|V$&;*_x9Lf*8ta0SYv^Q>=gvG_Ws(;0+>^fKZpf zNnT3*x#V}lWv9_^%>MYFHMkjH)8sKnuFEmh(04a1$?oNvMl-v?#}+Pn+bJ?*3V3kB+?=`F&%$@~K0`RD+7@XK1c7 ztGD!>%t%9r#z}S1mC2AEQRuY1oxuPT!RJbjdDCihd-;oWp7z@(Q9NNUDbDVRPo<+bQPsuic2| znxPb(JE^1)PkZJL#kWSzfRwsKvziiylr0%EdogncR!>g@_-Tq0W3qF23Lm0>q9{2> zI7bLrBS5R+5gbttE2+dB<_xM{p9r$?Kcy&VvCMB6B^_D40VT-;wGT$d8lp@|;%N~+ zK4Gk$C};Xo215s94eb<=sKeEed|_&AnKscczSlCO#Lek7^>5Z@MpAC$fg z{~b1s#9cg+eqR=mB=pvT%0Uc4UA8vh5{eDIW@dagxMpVJYaDiT{3VDCzM{U+j0#%X zH6!Ti8Sz)s6}`pPqe+g$fAK4=9UV*b1X7{-Xp*rdL?3Hii~kNgmIPgf)7Y!;7m*}l zFQ(=qt_Ub8hXoW=2Cg859X5$XEM22$;xUxC692`o$m5tGt&R!PYI^JyFN4lu(@Df; zTs`7A^}LiZ4QimFCMPj3Wk9WgwffikWJ0YmwOur#nrUoIz04Y3Nw4*&q{AY|--n5Pino%g7O3$S#X2ZPpO}rM3y+ay!)i1!J+G~71{q|e! zcxmT4UZd(>YI?-0QOBzRG1~PyI1w+?1LA%+jmOojgTRkoQiKj~KwYnCD!-&=ElQ4f zX|?gwtOm!X)xvr6nle4&AsF#moZzJ)Pp@{0ibrJ294{>;;-%;EG(@y2j(Ba(Bh?m- z_cBu>UY{{uwTTE0I5Of59E=mV&~JH&*P>CxJH8?DF6kNZmX7fL#dIryd&d z&Tx7}ytAC4-ku2Je~D213a7Jo$bg8~s8__xXzjh|BIPX?*Sro9CsU z40&RSm$uyTYGrwc9P8Db<<*?zjYbQa0UeHC<9MU11Cui?;?17wJ>nkY9aR(Ix7{hE zQTin&FL|%yn!$-5YZ>okhHbtB_;FyT&=n>p0GYuzHVuZpV#&3yeX+r(-Jd%i4 z>4@U-^+{q(ZcGwOm=+`v*$grj@x4hRekjShScJ*=jU<74LQ063gRToYFpS9cv`8hK zas#8|F-XagN5npd3rq1ICPe=X0J$lEiZOifYQxr3CdSbeVLf^$R#y2~(wPUXqI6sz zTsY1IA>7lzod(=Ql#h8FVc=fq##pAmxQxQbNF3D<9S-hk_1RZlGLh0aLj1e|988HG z>O31Ugj3hO1Dt*+j#5P0;8sYS;vhb1L*{W2A`~~JeF@x56NKV5Zz>U@;1T?NbHfEw-1mMj2xTcqw+ayg?Jcct}zm0uZ)a@81Qhtzr zv;aqwqhjR8PT(e}fF?h}xvox&zKpF^m80RlHgLE0V{DI>u1@JiHC?~8{ZW31BT5Vm z{2={k<)P<$^#H~?%XD3|GWebWj;<#@x<8?V!sPN#=X(jb>C`EW!_=?SAo1rPh_9N& z5}&_SBO-?Y7toE^(T09gH9qRwbP(Ts1GfM;We2q{Iw-uKeW&G zeSRDmKOo$%3H(9eeFk!}LQ2q!G0_-alll*;GJ24oko;(fPQLo>BrQySLYBvb)7V-q zOl|Q(gjLI=vs)478x$Ou!tU1a9_CNN`x9XbpV6B{F)8f%=sAsajX1t_G%kg`f%EoS z`2FZP;hS@O_h?)ScOp!)zBp3Y&p6S;%Gocalb;hx_mAp)lv82ybJ9jDKo2)#17pG+ z*tnQ*HKv?U5{JjyFx~G6`8m=3j(8Xg&WgFdPR#1UV{N%3BVV6}{2t5?H((FPTz?36 zK;);lW3R`gH)YBx6k$Bpg#8qg-i%eJAxa(|O1@!CID@r~3Abc@W5TW2@R)Ein;aAF z&2qIc%_Q>?rV){5J@UJ962cv{@EU|kMQUS|z5!txkrjSFUU%D%M`3u&)*o^A+ewS? z>5@}iTvEY5=WW}zBB!7*FQ*XhlE|@3b}!{4MR<1UFQa?ML}wz%(NSkB9G$7qeaZI= z^?uF4QvW#5FSgELdVy99aWt~{9$iZEdPVc67v%TD84NCsI)kB0Gc!#;GO-?LGNZ+U zSLu8CD$u0}(KshGwn*l#e0icF`u0s zP%RF6v59qQ7OkLzUTl0%kjjtFK`%DOTZro8%|zVQ4ly=*1?+n`FS!PxkhN zrKr1zIq1cP`{eu`r5Mu|4_H?@=*33!)~SC9d(ewbz=BzPgR9=aKX5C>z;E-FUx5SG zmjbmq=*32ijr*}dd(exGS!D6IuCgZ{^kNh4D?9PLe9(&x9!CEYPib8CB7X7E{h$|{ zh5O<1U(Ggu_%kX+9R1?__j#0%i}-|d5*~2;dBg{u&5+Oa=-1f6UeAA5%h6~P@ds}C z{@%m=pd9on z!=0ua^eXdL^eUsjk(A8+!eshU^O%UzJ<5m}!=sFFGZXJoM)+xo_b4MhxL`2l1j0kETWkf`*N0}Ny9%X9Adz2ATu^weaT#QGVv_OwCX~7<4M0C`njPORI zdz29|nnxKCp?Q?473Wc=R)9wtfzmw62vCei8G(uMC?in1N10l29%aO3xrItBjV#d%82+lk1`@U&ZCToj`JuZ zq60n3h@?1=G9o(Iql`$4^C%;tgFMQJlmL%1k}1xkjEIl(C?lc`k1}-my$e5dRN)(>jmxURHOO6$drlI$264P?kHa-cvfFe~ z>ay{dR!=>-dDF{4Bf?-Q`C|2+#+G^fl%0ZDiEocq79%7`%}o!&!*i|3=oqWy!AV zJcvdHmk`v*?xRMQdP;I+@~5fG8lHq^mb|Fx{a1kfuhh_*qoK7-@@gbGwbAk&=X2=y z*Kcb2KtW9{=_~yBO-;U0?zBp7AZik+bV)KF?wHf6b9ZWk_3{4?Xgg4Vw;i<4|EBG@ zkNIuqVcB-7`)#M}uh(|k7;T3>hx~uocD{q!e%sMMEpR#{HwkP&9S-gF2R0y#fxH1> zK>KeRkhjBcKu^d9lZCOw^5693|IY?Q?cQ%d(XUsWZpnuPww`WXd-$zq z|J@LIRk$IdDHSe;@arEm`ss1>%^}6XXN?aMQGX09$m7g@d6cQKoiAk5W(e3jxe82gMQ)S&s4!dwj)&dZ{;uLdo+ zgc{%DELIJD1Vy6I1$wI@`6peAv5waVB-aI!>jKH^p)F&d5DvP0P7-0ROFOAcdtVns zNnO6dS*$M6&WIGDKnI$mhI;iI!&eL(t9aG9!<)uy$gxO2xV%YQ^}lg@lkKG;Ps8mE zeBqGW8+gAVec%hG2ySmylWL2O5^irsd)3JI$bdocn{p&h7I*YoHuPE?0yifO=!zw9 zNwah~+}@1v8nvc~laKHk4JNlYZOH9STXK7Yv+w9^ADvxHK5Z^f#@Tgrb|anLMQ8WX z+2eHfG@ZSIGq}Bh=a@4b_`W&I83MOAi2nt?Z^-RUC+`q=t7!!HG8wJl_68|$k)NA) z>Fi57`-aZWAU`)}dpP?Won1<2n{ftH$@k6KZCCpK#w_}e+(X-{r%;00$b`MFs~{&UtlaKdw;)0y1fz#Gt| zPB(IU16Md3$w$uBPFJtdQm-b}LM`~7@oF!}PaU|tsS8&(_42&5LS$3F1a5EO+@{eS z=uzR-f+HFzv(QV2qa0|o7#hJLPILIEX*nBiZ?crzo8#l$-W&&fpLxP-&42<_-zji= zQ|YB~M>@k7d1>5{&Y1aL+NnTJh5Map0echbn)O>VB2eiGz~#pDvl zt97h*2wdJA;uzQ-Ht znS2cJ;DG_>`}s zMcU#W6)kv00YyX$NC7P(Dz<>A*dj$mOO+}rw%)K*)LO;z`+lGEocES2<>#gL5AxYf z-sf`ex92?PInTj_@CN1vxw5(A4Qfu}f=3<%kH3OP9t4j_1dqRhM~Z?+{DQ|{!6SbD zEMH;L<6kT)0-vmZMfy7hfSbV2BAHl`9sH9mFzQM2yqJGAdhx$PIP}A47W$zOB5^5^ ze4ijP_QRc&nbZy8aqv6=1j90S{6X&Lx$*fIWbn3dK;bkO%frcBjw&2FMv0+6nA>&7 zZIDl5-P;kO+t0Qb-;1#>e&cXU4gS6;#&_HXI9ChE{#UQ^vAOv34e-H(i~l3_4rmbn z!{s;<8L>1n+iy?#Y*jpu84mr}IHrN)_y5HAI_0xbFh0m9^M2k5jE$Sb7l7}~d0g|| z`0NEA+mrb~w~`->w0UlPUI9LCOyWO4N7DT&5bupM0gksv9qxX|n&>hjUDM!Hs$yf_^yd(4sg62e**VQKU;wBVc;;=ad^tl;2F=(J=E3U zJIV$M{rx_^!~N$I7g-*cmjIUK03CGht8GL(`5AweJER^n_gKwewi>Ld+ zP`amv;)|#I*P(P*hWO&;U5pN7<54#G6Hm7de73wV2*npq_jK^t@~#f?#nZhUe74@6 zDcw(o(#6y&Kg>tm{ntI*GN?+A?}{3q`I>qVjXFxAKp^A~p~ zw)0uWHyVl$L;isA{l&A4@A6Q57*Yp}?}ld?-={+Hp^N$P#oe8K1$?%@a316j;kdiA zZ-CFvlN2fWL%w)?KMKWnjBqU+-;<&Ejt=p~<4bk~`GYhpKMUn=Irwb;jt}w0<2xY~ zUvr2rUfxtFK1kd0!*t{B-8O=+1^5L1lg=Ohy~2&oyMF>ch@SI)J_Nq7yfGAtZ@Y3c zKJ$bW9<`yC=d`I8SZ{F~H#9fnAAZd5Et`ywxVFt(ww%6&`2X2k+$F|&`QlAZGg4}C zPQfY6@6TIYxzs2bw?}c;M)N!y%|qa=D)@63KS6lhYk2QWy);^}UX8?el5v=3oCql* z>QyN4s1}YP;0eVt51GmLpH^z%S;CWc(&Q-F1AlTB zW9aIL$VOBe8md+Hh1s9Id%7SAdQb|=8hBMQIC`Bj^@#HR)}H46Ogp6c^B+&Y4IejQ z@5v78LOgyK{@#sVej5HJ0N;eapU3|XDf|Weox*>!@H9D(;d?pwZ&vy*@%OdZ37xEP z6Z+vlVzIahql@va0{m&v2b50FZJz+l%@65OPi;#b5A@(?sWTLU=_xKsU*=RJ^n+vc zgPjW^^yM-7a%X>p-sHS80w3*M7J|8*px;?e9OkD;f^Ii<dXC#?XDPss0e-KafhLEW?TbV7UjTk2M1LBv z{YgUnI>M(>Xg%3k2lzFi^!dJ#{YheCc@f|TLiz0mJZTw_pTP+KdgnaAp9|4PBlLD> zAK-C6PfgBWMCe-`dYao8qQ5IbKhyaTU{2KLr^&e`LeDsV3z)BBSo-H9^ls;Cfbmw1 zPyd$x&HC8k`~>iIq4NG`gr0MjfOegaYjO_rc^Q7EvkLG}Fh0yrlar3nU*>E9{Bj@H zzy9}z9SU=rxE(4qOH3#L|+~Z4&?uG=LEn9hw5u>g#J!v6W}{S z^cO|wS33QG?+Vd(Md;T!BN6z6&b|nIqw~55e2eo=z?X)`)B7Uy+niefUlyX@5uty= zxexH8A^Mjh^uKk!4fs{Q^w8w|Fhc)~^DDqBF+bb>!}%@@Cl@7zG2>t@#xe)wi zz!!z!ZvehH1V0A&H6i$Mz?X#JUjV)|1V0V{1_;A2igy7==UloG!D4BCj z2e2Vi} z1b*20X$0O7>|oHyN#_O55&qmmxXo#f!2j%=7JW~yXPvVmaF;U>fxqjV8-c&)Ohn*b=M@pS$9Z!EeyMY11pYVYh6wxv z=aUh5z_~91_d8#Yz=O{BBXGg_MFh?}OZ~Zk_3?CIZ)-*F@l7J8zA^zi~bg zf%iEdjleU`XCv^e^H2nK-S0%;r2Aw9UhMKkAp4VambiyU;Dg-e2)xvNK?GjrzBmFO z?Dj|Cd$dHxc-a4xX>_f08(PJt+bo=B|&xf9aeVf#2c`M&Qew^CIvHcRT{W-FZa>ew*`_ z2>cG`>Ii(e`{4+Dqi({@&{xAAzrS z)<@vyxi5^s$GR_#z#nkViNM!7;}Q5e=M@q7IQI<^_yqSo5%_xNh6wyu&YcnX2In6l z@LxM$kH9xMk4NB-I!{I5k2r@}O(?1Xp6lHcBk;$aQzP*0&PyWj9nL@mUge$}f$wzo zM&M65e;$E9?YubxpX6Q{f$w&1jKFs}pN_!yIQK{3X7}q6_;bz=BJgLOry}s@rSU7e zN2C6QKOa_^%EF_9HToHT+BM$N->3Uk3%_31U<-dcMz=DgrB`F|e?ny%OaGTx_*ci` zpN+w<48gRMus=LxSRI46#NeJ7yekI3JO;l$27f39-y4JfIR^h%3_jkTm?5vs?-?<; z5QB$e@JtMTV+_7J2Hz5c?~TD365D+V`W@atpn)iL-ZG5AX{_y;lA4V@4( zf5*n)O)ggAcbSMOwe97<^g`K0O9^#^8Y% z{IVE45`!;@!G8w$a?BSw$xoAW8Q?bqrW)o5%!lt&e!_=COMY7fKG^vJV5%26MUnqo zfH|F7_~(GRN4Zw%OL3z1@0i~%QTTa!!e-)M=Cnp&yleuP?=bQJkn#5eo&Zd-mT(F1 z=}2ru;k|(OA_F{tB>h6bv*2H&@S6ZrN4ZPk_X36r0+P4k|1jVWf_{p^pCXL-c|gke zz6ki=0JkXoDByP^{<6aV0r=I}bG%34gX~E>;1$l_JEuk9`92gh~3Z$$e28=v!rX6pEZ-=00OJJ5?GFYz`w`AVsrF|R52;^hnRN9lOIIQklZ zgOAd9cC0#m_Dp4_lB-pxYSqU6I5A%t4M>cwYhjhM{=2^FTOj1KX|9a{KexOJb!A# zvmNGKO3vKyQpj4INy&RC{>vbS0#MNYs~>{qvmb)yt0U$E4Dw+gP~pQSf@rf*@Vyd) z0Z;6Jj}MZ>Q8q?CPZG-PCX~e_>8HTbRfc9pJ;~fAAMZa!BKXZVV660;eSZJ365-$C z^W&|UQ2bkbetd-RTYY~2ofC;?tL5jLGa>$MK7X`ww^{y;QU22{Kc8|jH2_Q~!qaWQ z;N=&|K--2WU)zRg#@nQO1QpoU7NfWODMw4z)*fTr7)!|S1bkN~l+!l95#u5J#tYt& zF=g;uk9{v3mEU~9TQepE+b_(>b_>I7uLumfRBP}$j)}-`sNn4zCHNs?#q*nr&b}VhOgJY8&Z+`u3wlU*%9TU%H8}nuxvtMVM zZOof(%$se@n{CYgIM`xi-eP0kVq@N7WA+Ef78~;x8}k+$^A;PkKVr7pn77)Px7wJu z+L--ev=uR9t_VHM)V6V+xkB5fc_^7KgRX7P`rFx5riFOa?@u*6!$&%XqE-U!+x%uG zFCS|PLbmyXSZIDk0{P)!n$W%>fRG3$g<$5B$Cps_Al_hzF>EkW$;rVJ3+uMF(^sV{cwP}m5q zJzCN2Z9Xq^)y}C(gCbk(`O$zpnvEHoUeeqAq}fM;r_gL-eq>B++q}fK`E({03{G;NJ}M1M5GESK_h`lY4%1nUyB@F~nRy~Jf5WGE* zA|yBmNG!cTU||N^f@#zQn4exCe=t3+jV3+epFh*K1yijgK>0xfFt}e@6MTMh6e@xO zwM8di&KHKL4asFV;BJo&_;$_+nl`)P{Q0*nIsvzZW?%_0KYKw}lG^aQnY1=Vvru~T z=iRpG#M>5~dAIp>tEJ-{6^?IycA|4{8)cbLfcevFTdV=*&zWsO9kPTJ|3fj(pEcWp zX;a#JTco|W`NKild;YB17M(WRLh~k5_dA0GpFfSZ1@ow+etI$zowz{>M>4w>KcTFyU++Jz)l*T(NBfCm9vSl1%X0hou5Qw?iTA^}5cA}gs z)#}yBQ3p6rHsM56vSX9ux$)AZ=LZNAXX=fqiAt@vb3t-fS1v!;4~L!$QhPhIQ{_r5 zxg0EIVJBPMKV4ZcP;sn6wL*UdWnY+KE;luc9AV~IkiMsZ;ss@)rXsh!lXku=cYkSm z8g(!@)5uk)P2Q-J$ailqRVN2WM(UM@!yvJ`C#r9*bE1@2m$_il#_1_dmd7hhu;k== z&ha`rvZ9zUSs?};9hiavUEi$qFnQWX9dX(D!d^Z|T3qZbncbu(IX>@dinEH1z zd)3mo14^tn5EJwU(c5&))as4g-bPgQmcQh6 zS8EfqrCOzPYIbsbs#FdrZd9ixqvVcC35ucq(p*=qHdR}QhF)Kq zVD}0o$%t8V6VU*DeZ?XNT@)M7F9W0Ys~1*m&M_E6k;@(Xa-}}()j0gDwhFo#JmXrc z4Da*AznG6_Ym_I@Al^9Mf%VZN76$%d!Qvl4QYiOtq3tP_J_^23;rydn0V02gpgR3?U}_Y1Lx zfr~9C`S=N`!hUX(uUV)g`G7F7@~s^5ZCEbERD&f(Ce6q++7X>cJDh>Re1E3T%VoBA zc>_E9y=~hdKF&vC$24!qZ*9j#3}XATp@~4S9G(da|ZqDP#{szE8`&tg*Id z$JUn?hPKY<&(1VFEL?RkVjA+p^yk|y9Jt`6qnVy;5XRfxoA2uDDipl_!OpJJdW)qE zW2bL9dp}lgKObJtZa=4m)AG9~=Q0;=7}z?DtCasY-d}!pCjrmB%e*Fg#<_6fK8i)6 zxw|@EsW-RGZQIhy*J_&!`|FL$ME%?@KD1b_l$&>B$P85`hsTJPRfjyYZhPjO_oILj~^KJ z7jyO8yUcOHYW~*sxp$edbo?&T&2#TE;|-91gv0NI^9B5NBM-p*KD4FI?-mKQp2NT0 z8>Tw#aU(xHUTW~o%HJuX1?NxueDd$z!rw;xhklnCv+VCZ@7x*Spz++noy9^v`v;HT z9ya~q68c|EA#{=B57REsJ;V*OhjVeLEV~X?w~@=NTK{+IMG1>TMMkH)*Ov)=GT*)PodmJuU%zJ6r)C5fw4reTWcBj1Zh zP=VwvQa`r8GPr$}$%Y@{bFYg3Uwl;@BRDsJq@teoxKUaDmvTTB9hoZDV0;FPG}5Fu_LvZzz1xX;kgU(ZD=YxRvF&wCvb)4r)Re@03tz3 z`nVDXrmFQy{}im$rfNR1&BU22HO9h)yIaFZCd0*h`X^7_xcD6j6DnV#Ikca@ztTXk z5f;-n+Em>AZ5&YvgHBRGv@Xh3L<7(rh8MYh)E0a>D2D$RP|!3fl|yLZA^xx?m~ z0fh9zD5ZP1A5~A30!u_cP&!7+`GM9&Dfw=navmr-_GDwL?Z@?c7bfvuzSt*)@?iJb z+3yoCwk@M%pU~wuO32#Vr=CAXlVI4e1G7Y0{*EM)M+)s(=0U1v*(1`umm0$63<%i~ zR{yfElW-dsPsvj(#GH?pivoUtUnOV&?i2mkB$;hiCe%6HB>wjaA1&k7V9)lE;}y>L z2AlG+(-Si`7Rl)=gIBIeYLx06T&5JCz=|NHD2e5?AKQ0>Fg4CCCXHsFxjtPP_R$Xq z*`z(PANfBg<9-ni{=2_?;Ks$jI)V?`q4(o4;xT8r^s`Q)G**MTMMEpHN!YgfJ1HCd z(C@M}-tYPZnr9y_HRsth$lxjNyKG}8uu#%v9D8Y-Mg4t?WWetE;djT<2Xt zhZR1@yAG*WCZ@+&gNA$ClfRC2ICkyCJqW}1~E#+#7lY!y5ehI>(UIO~qzLKXkY*^NfVa_R)$#Km4 znE0m?f{|`;%r(Tkf(Wyv^SKTuu3VB}!t@CVrm35l_L9d6a3-piM2(gwIKv(DUPk^G zf|;(eicPQ|yF^vngSbDo$Y9ts$4Jq?D5+Iz7fcc*8JHpu?f3kMRuMBf62x;15g0>N z4`$;w+-H)4txitN%=ySO1*fWFA3Gx0S&AwqtEUrzjjuOd_a^+6-q8MAicz1g;xY^d zmLJKF48G1`W;kz=!r1;3ig~6aoy=e^mvB6!@S}Q4Q2Q{ZrfQf!af8JtLGCv5Cos?Z z)uVUcxcJIo>NL&$JZbK429u;|PJ{kXfY#C#MNY8V&L5a{^7e(KjZEWSCQO-;)j zd;Bk+{Njy^N1VH_G9I1a6UzN#Iv@J~z8~Rxx=hSW)~lnFFvK4oE7cr#X@IDYVIs!M zvH=DQu(Rl(#J;I2TskdY{Bw87v2ekZ&22HLIP zTXs0^eDucl9Zq6CddsFA@S3*rM@)F2v$x=F+tBW{wRrEin zdS)kI#PwTgBi=;o9q29g4h~4RfNm?cqYIyiulC}mJTf6)z$N(AR&2{7Ma6mB^Ics7 zNDDCbV0&=;caLd%B=rE2ve($MJ-xa3MK(#W*i}Hk1(oe#?jj20p4gq+p25-l%B^s2 zYNP2dP0nDW&PJ*&aLr9?7D?^u8t5F%d&NS3R%$jt?F5Du8(<3ky;&yol~6(`Pwd*A ztseGlQwvf@3Zi7t}51oDx#kHthdyeR07xHFtyL-EPJq*}fU*;SoqUd!SeeXcA z+shTa?wtc!Kl+`84zB=X@NCg5>@4I^wVDW9ySEcQ{#M?}2s$zYox#W#XmG%5_cm<@ zzXdmMM{X?8F^J)i5Rm;7Gs>E!?4PDGB30hsJAhc&&)VTNM=E<;JG@#+J>zSe>6KeH z!0&|AZm!U^vvUxW(m<$snaUDphImduJ$;CXF4;vVi!88 zohx(s-u_Jf97|vfUD?5b&M;1=XcLrC<>9*#HDBnGIt6=o?{;cuynGk_ZbxV7+u0RL zBcR~!XNGw*EJ!UBs%sDOtpqghrZ3$-;#R-d09*P+e6e4e?bl|L_9OIKHCN%RGB2qfV51;;F(wVHrOL2(A zr4kioAVNNuvx>qHQ3LNpm`fpvXsnc!h~Eel(=tV&gk-UbqNwDf$SpyfRg}i&ZE^8vNl#zntE-5tW3i%*J{d=0X{~g#0cWW2u}%8KK;Bh7tU&mq4V)4y(Q_artB7w zB%I0E8j6aN4P-~^I+CX~6c{aeqpdM#4dq;m9tE1^T0_}TkQXOR&VB?Wg-AB5EpBr( ze#*+yx>_5iG1gG76m&Q$3ci%GB{7Ph0-!6dp+u{I5Gi+C7_U67p$IOpMrSg?>`^X{ zP$X4SC~!w8(lJP(=qvz}ru3|U6k2{cYbgHDLo4k@OA8e)bvuyP!NK`j$4T9T?~!%p z`vH6(4`~iWDzgOY! zWBPpx{(hNhf`1EfaOlnN4t)OzU6bF8k4?sJ#>Yb8dtDjd53F39a8lEj1K$@}j)b%B zb@;wAu@*12U52msuEbZ0xwszx`OUv~>Gy5;`%UHqc;YT*PVjv{zCWqo599l9@wg`O zEWy3Z6~3Rqx8xSzbdfK)#rIOg#|9+NQTUde#pS5#ZGD$z9eRR58+#4TjH!6H@pWq>-OObt%I+N@pU)9QkUZEJZ#nY zeFf=|7x>MVme}xpE50S4_`Vb0PhVkp?!&jFvea3}Rzi%+oD^&6c-9EMe~5311>e80 z-1ts_{@>V1k>^l+pGW>CC&h5W-{h=gTi?EN?IBKTgT>pxmM%*FZNeR%n&Fw3lSsia^jD9@VyVK2^Zheg*&%SKQY+J04W(eR87#C3 z>ZO><9^ezL%%V#d8&C+sp++LLD1C9lE$@Tw8^F@m)Js607@xplcXb~yO|9I}3I_g( z1({5Je_8AMnt{c^xndqZy!D5~RQ6e(A0OWO%4314PASV%hqwL|z0BpYfugQIqV*bk9_eyBF(IhR+z&`GAQ-`dJ;JNc*v z2j-@&tsD&QknwF&XiVYgqYT1Trz@c_1HCrRSM!yLseMAa#*(sVDs`>3d$6a_k6_oG z-pU@}=I0m{lRVvIq_DMOQp&6-)Aiy?#l!VWnk$%-;cY0{1 zT;7l4jM6lRb24?`8R=gxa&7xe;0ax9S5o(DEC^kzPLyhl;6cS{Skc}B{UysPzQ|>i zCsSXRSf|I@hrN+m)Tw79e#j8u=w^7#M?UN$hh}}`BWI+4v&fwup5idj@$#sqHBy~} zFD~rAlBvfOrGD`$`zqtWe(wyYg^EJaQp<6U)71JN4mE=rE01Glane^};-N=0PC~aO z+!5=e2lJ5ALj7lbFWkgqD~DNc03Z*ByfJo}rRjGj+(|yz%XsjOlYh=ui<}mAF3*#r zHK!^4!BbuH$vN>0%mVx@?FA=Npr(_Z2fc)vBwPVwTPoFj7Co@ zv)O6kT<=w<#A8=W`r8SNKlteaLOKt#{I3buxV-WzlQ@TidJdU?BH@bHtZjgnrhl#w zyk%kKN&iYgILu-ho(8B+vyk8PYmy2I_p#|WSXkIsrr(yt+E6NcwK$JQVdAB)jNq_bQShJ)Y$hFh*0^Bu`^?-iZofYZtp%LIgOqOKC=@2no8}{HM z5#S=T@Zv!c0S}dRDDeDp1uzi#vww zz%SnM>8~IwrD;smQd}o}kL%(>V0p%yrn9-aB_z`KHM=|{_9ljLS`1%k zm9oZY`Hy57QDYt5Ovhr6mgu=S!3|#By1}#5(t%Yo3j-1wi0p0&iV8078O&iq#Hs~M!fDB#;D~EAmeSA25sc*Qse-LaX-B%st5K3* zChkAcE#Xj?jcVfgDB{UO_T=2--T6n`MTjK3WC;!3HRej$*>#;juSVg|)*pNXrdr7(OqJAIL_v98jAy zmd4O9n;iUyq;F|=b(ADh7(+Lq0FiSDF4m3~rBJVy;gxcbeK#Y1l$gzU8^K8TXI|M{849>xae22vIDegg9NQ)@C8v2k50lSL-}fYhw*GaZs7= zX23qyxfB|J(v{^ zZlwmsc2$)>a@fR`hFMjcTFaDP^7T5$*0$CX_b;IXy57m1b@sb6TX!)U(+_5h>qVf? zYJDXG^hOTeY zE^=Swv~W);o?anlbBpTSbWkf54K4*gusEz|r5OR3LIZG+NM&YhqDU!gZyj?34&)JN z1sO^*evjhk%dyYFEXgE~ZT%E8Xj7xa^Fbmyh9@P*$U9~p64c#e)8vWaR%>p!w=keMM)V0x7TzAeEmG^JjEi_!{; zM21?$BqszNz~n4XO`<1aWtAaCH4YYPaEVt{3eI-}MR5UW@%!4GzBcL7F{uqzU{s-Oz*|+G?5zf5#<^-w~5rWLC8o51)PhbC3G@% zv75d+i9IMw%)Ray39@gcokrRMi>SzX|g{g-j6%` zGi<$N>eghee%L=vZ-SCSiP~UNw_*BS$yi%4pBx6*CmT|LcrY1jBu2z0N~Ru)=YgVQ zIGu+VN=KOx{;?pf0QvoRdNxv%=8uy`jGw%KMw#_VMDqkzAsW)uC#I&bf2v_RHv7G+ zlIhQ_c2!JfqpA>vHCuE7s9IUH8&h;Fj(r^>dwT>2(G9pOkO0;4RGY{=KothZ92ISx z2e2KI7QA#(`mdhFMDWOJ7a>^RGQqsNRil=Xdr8wmBUgrjq;F>o9lnCI?QCf}jAw?u z`fRxZsn!f!e+XeCcfg_AA~cCMJc3UR^#T&ZLv=%RL~b&*I@DXwOzt0;n(XL;OLA8K zHZxXOb{%$S`4}B`Jw8H*T`n}fLIL-f>;O|mo0xs#`95x(H)p4DVT7us+OQ#)eR7?y zq2uH+pFB}5!|i*VJ|3$xsUg#b(?OLPL?bjdEuUZcbEbNN7WGGr4y)I=>Dy0W57AlS z5|?XiEix;-eX$t^HN5sWGD2s^a088=y4Xw>5S(aGMwROjXN&hJUXf7Fh!m*QD;Kv? zW(} z3dR@&jqc)=&(_5Gjl!a<9DD`v?1hJEr z!Me|(M)b$J#jzBuwH|Jok@Wa-BNX{03P+e(mYcEYqp9agrhd8HG@(Tg?e%KIR6Rbm z+_XxVFgG&J9acNY7h?lLt+zGh+0~UV(CX;e*1JUz%=#w{4-@A5i;7v)`tO3-Syac? z7)?uC4}K@Oc42H!)s(NPbro^>ZiQEv?qFVWKp5!7sI#Z;g>K-VaMOQ}WeSin0fE&A zB|^nf+XQ%#h07JiETt}3`mZQ)jJmOjY+$HSnIQpJ1Q{$|phnTFo40x?Zxvs201i`=Q7|3qp7F_E_l zAl4HnUizgN+Z6Q6a3&F=1{+2Td2qx|SK}zlStPLVF<<_PRHvapdoDqS{r4>ssUANz zm1*qurjG>OrOTlc-wQ!1IpSebDBJfip=hcH+d*>T>Ly>WpaN}A`nyRS^*{s`Jwb?a zZuHPHLmLyL5L!>fuAWe@~BudbGm#3(79a{j6!pKtxiPU)dr%4xEbMb(0 zUe;jVrir!>n*``a`accYy3h|ub0jgM`J{`|PuWng2g6ehHk3$YpqXHC8YbLVE^>ED zXUS*!p_&fDVe&;`5zgoU@)cYVz;;Xy$k6ZsBV@VPEpoA!^yIc%5xXN1=-#x*g%%PU z3RIYAp&?^PoY>U_hc8zM3LC89%6Ndi%1vLk2t5i-DynBF{ix7K#Mij#ci@l%BbNgN zi0cFqJj8(WE;s$2MW&w)4dFx&OgFjd_bpOwt;OByrms`B8pbr)ZgbN&ST>8h!%g43 z$Te@v&|IuATSdcz`A#?eH;b@o#_O-$m`#DX%T3<_*6y8!E;OtJ3Ec35ey^MUv_=Jb zcBY2KsG-Me_qpkNl;-6|`Jzmv!MG9ZemDL3IB7TEfdK76=_-hsW)pL!^B-81iS^BJ-mDd!@Ld}l=T*9 z=NB@yRi#%A?=#@?9nx3f+#smhP^Fha7>&P(>$Q=Myvn9e`XJ+GHLi{HK-rUn0=FH3 zbwgbn=>w8IsDqDJ@JvSI2;+0q-yG|-ybSr}zCu_oq$TAIPi(RmMEaqWB zhl4Xn(}TkeS z>nd25xJ--cpe{Ya;G+BG)jqmIwnk@Czt@lFo{^<`BWo0foN!DW+p)D6*7@e*)vf^< z5=X!o%_p)<`2Ifj*r4{ycS7@QPx&G|u(z4#9DxOyg7oCD0W6DqN$dL zECEw?V{cWlU$xj_x-LLI75_DhHyBx$>SH)~M-u>7TBa zGt_a6cx(fuMlLzXbSF`~p-XVHFBfkS;u1aF8e;Ly@iB}BDy2GauwY1==Wv`>}WqP`!}iv{9<1A%oninqm}GAos75x_eIsl!de5I%7m!Jv)^P}Ai}J>5Lg zBqKa%$x>Z*UAm$}0^^n3aj}=-IfPl~CC(6D4KRl6g>J7@pJp93n*;+qAG<0~$x67| z(6DQR1wsk-dGHUQ!PXe9Q57j*?Zhn#Xenip4DjQX(mpv(kfqW@CG7J2Tq1Rf0a#}tWKf5MneO>1d- zcIUVk!wEEH{R;x@Yz^KT$}mfW305LiQ^H>CE=Fc$lmc5EiB_ zJVHr{8J2sA)Ev}SFE-~-B7H!-it3M141fCbB@mhYr4f@YmLI492-lfqK|le( z^TD}k>}(7MoBv;|l0x#Nm0NzZ!($1{7h2N{Znq5{y){7Iqe$G<;=u;11yS6KT_%l@ zc`vB1cVGwhV?|zRD>^!f4$Mpx=LYv;P+MCE6(>Q+M+quQ3SsA@i0F}kOIweo%5tb< zroJEEjYdYQJRNOPY;i7%KSc5Q82(VjbMsWGMZ+96kCvYcQd%*OS`1MScUqnglYAOw zo8|M7WwB$IB6B}7>rX{?GhTpbBBr^VfKeh{BO6m5Pr-o@(Et_V8I#N=F{KQhorik* zP#shGl$b{7A-MAS{lA%GMO3ZiHDl#e)81^>DVXCTKju_^19=H5pPs{5jEIvwgb_V~ zV0olQ^Mn8+f?Fc>Vq^GV`X|;IPOE28;b1Vxi=f8RZ#c{4!W$g-Pn$(RqW7mroGC0d zQ>^xQnOM=KwemF9{g~t#F%(sCf*wu$wMHY+qsq+;4dF>o?*)&(4snGf#B}G<^zB0~ zHt{lZVYH}5gtf?RTiUvV zvSD9!a&MA=Af?Ku9rp95PChhrAWkz_24O(Bf`Q87&o@Ual9q!#Nq!;sG7Q*32e%3Yy~ z^5fE4x;I3N#HjT9Li9+yO21#Hq9F*`dV(QBJ+^{cOvQo$Wq(MK>=-nE_V!gvm;q=M zj{(Sv`|7gvC$QpTU7X&JK7s>NIlTqt;bl$>uO?w`q4~}>E~98zk-bEd#aYM95@M%i zwTsrIh3uSgRB&xd-*bYia|0AmKSIpiyS)O2nGBBR^xbCR$84hO3KPS2Fi`f*c{m9M ztOL5nhh_mb9l#z6sho_vG%}CVZk1w)bE&RPeo5?JK-OO?9Vip`5nP2WS`)^L@Oyon z-Oq9PHdaNdUX%!-M4-A-mr$Z85ErLLN71AGoe1{7IM)oRXaxfK!y8@HGwT%a*WL7!n+VHr zNTz<~rY}9o#ZbhEu((TMDosx|lm)HZ4Y3zhg@dj>knhlk9kccnP+C1%R&i)rhQB$_ z1RrjI@M;_Oa-z?Ii448M_@h(S&L<9v$=s?#C6^h(=Gb|@DW5LUa>!+dq2`q$h)zw& z#G$6!6X_d99~R)k#?h*gAglF`XXi3iN>G4i)Rdw*PaToIwHbp`R%XlvoT`pWzopp~ z|4%UOlx4#uhKGHM_=21M@Fgxx8DMNS1B-Lc!fYp;I{oMku3QAPa~HJF2_q^grxkIo zT4K;LFx%1jRN%Ix2uLy^hH3wzv^rEZ zWlqybIApH1LOHD6 z7wk~zBnlcoxo2v&VAwpHDK%WfP^fIJixiKwB+G4`ir$(TPq;h7fCM`Z(R7fB?%=1hHI6IJCt(4v4C79}n-Yq@f zKAM``Td+WnLXZ&(?}V z;|0053ss>E9x?8!?!z4$c8+A~we#^ZE|aO(&8L(nXb<`N1qj0)&nb9j5*V3Jj&U#m z@dl{-PQ)}jJSC>(Lo3kf4+9RMINGUS1&tXa}G zw$nBPgrp&G@{j{RJ;4GcVJOA9ftm5~4Tjqd<9GlMJ|~RZFvWOXzYHdHo5gDO^CPe& z>BG8+=^45o++)h~lmWrC7{Fvk(OnEuGw)yT7yp3d*31v}m#AqJclEgT(rUA zCSIzIW6Ry(t#;kO=exVI<_ac6l6P=ibaiGk9vmii40^EjFgon4sSJ4$fOLd+l+;@X zG2hu&P_2n;CjDt7*DzXd*m|)y!5nJ$If%bg^@22@p(wWMoh02 zc-$bARIY@BeuHvt>Z6mm-EMFR#bI!UTu1nZC!N(%&$PSOZBC-JTL=N$j~q4$)YXi# zQ!b_^i1yjAC^DG?QtpKjNo~B3Rpm%`Cd{hU+ntH9>cv3R7<}^S(6<$LtZD6Btumi> zoS`9{m;=pcINlH3yWMPD5~&mXKmkrqf6d8OfIiW%b`&>m@Q_IF_Fj~Bl|cgV55i6| zcJhQVu0H1xXTWts$`?c()i1usJcU zGg#DLHoL>iW;4=2Ae?LnC|8V-4u{Bf+JB#0Pc`Erk4+p5rgb!{*o^+`4-5D>lv|uC{l-b0-Z-@-~aF1BRrwZR;k$I@18oZlwfZ=Wx(B zX924Q7W9np!=iIL%hh}K4tQmSvF09Hq88xDvsk3z=31=@&~+_#(l?&q+6e?R_6=pn zk+$phvdGmCglTF#aEn^qSShFGhg7PA9x51T&9I1GvMK$>;ANeTAv* z;vQ)PtCT)UbE;@7U2qLSn4=e_f^v+;W<*>Gh5@6C~B~ ztRk0zr_w~~IO2*pe#Nz^ejIgi8>*A5Vr`A-JQ~J&ec2u0*U2;y0;q0bC_j>``Xbr}GeyN( z9=&mV2&rb}1Xm}^Ax!Yq$!t#>JJvR~8fSbP`{}%NCB3Sb_a7nhLVbyaf>r%O(qOpL zCXHMY@uA;N}CJZ;gz! zZ^s_%i}UdqG|AKhi_$-9HdQJCWSQtK&w1_JQRNvg2cCcYA9x19^Y}CJbVxjJTKr6D zb$E>DhG*o-N<5$cA9xtg_nwibQ{s8`l4r_Kr{w4T2j(%A^LNk0z?}Tcff-D-{Q8*~ z7{j{{I&eOvARm7w2FCE!12dSac=DMT7{ldD510qk13Zdv+s3oBTb_w+z-RlSo?fCi zXE$s@xE}`yuAyR{ASaE&u?>iy1sok)i1=j~kp<$Z0I?10F78t40XE{;M(69aYp{`S z+rm^}m}}-aj4$2jU`eM29X7d!f^{JzU5P=iAwWu_`y*piAz|KP!wgg^Wv&{@)L&YZ zB;Zf5I6cu_ry0A3`SNAyZ#MG?EhE|z@kR#=URbt+4#^-JpdVV6zVjqO=h|r25!=DhqpX`d25o#kt7Sh7W^y~=N@#k9mME9$UU>*ex~c%`nh;9oJ6WlFWq$~U_b=CK z$7l5gD6@oga_ZH_e+u3Js zU2fzYpk(32iPUmdI9bd}52iboJ1t+tVT6^6>Fk=$+HqH?H(P*zJGqry?D6{AdoXQR zMM21$6^>8AZXDb7o<6UP3E8K+2cVkN|gzObnU^?=FK&WzvD3{@t)0 z2SP7K_q6wU<3oU#m}rs&Is}@O0@jYJY5+22=(k9P)AGQtxvWP1B1PVF8J}rYX$J<4bNCuwaq1K*2&4VSQAh#P@|Mi%SEFIimf?< zW#CR+eB|5uReN2C5FRcfQ8Ot$gHg$7CPDT(z2oDRQIU_-W`dM#ZFrkpNRu-90s3*M z!E0*3s<=+(!4cf2RkTX&5j0ejl&@5Pm*}AZ)BtMAO)R7yu&Mn75#5wTy6gLKDbhEh zAfE2}q3+?OJ(HQWL77BaLAODOmH}W_Z+Lo$+-=^L~TV@;xBT z(@gEd>L*CImkqq=hCtX_Us^>Vt53@rx1Q35y1535vxB`bX|@ftiLxk-QBZnJq4nK4 zGo3FMET2}mKJ00+Iz4220%vQgPwnsam8RHRzw+fQ+a8a*duZ7p@O#df$xMC^1kz~! zpuUq!zEB_9O_`g%c{QxEJJq}wafy7ZS;cnSGddPM8p2*%5qdO4j%j!+;;EvFw&by> zou*Y~(h}V*P7lm3Z<~-GG?`i!B522AkE?L|ip6j#>PN2{QJW^m* zbI;rt_8dKw>70Vm08DmZ92qS>PaPHGnk59er}6{Q+6tekX{^Ah`WZ|CPDKFG^Av0Q z%`FL?Sc`Dvr}?%Eo9=!!yvZnHR`oNEaVoYt&<#jeVzeAVcr zusc)m2BtXGbKMu?IvtK-QpXNY1uB1F&L^MV&WTQkXJ!Bop1^+uXGD>mfaov?9hu0s zY~gh{0>q|;gU~1)Sb#bVVL25x$8a6MtqtLZG4q)zGYCr@`A!X-w9tGU6C#is0whhx z4N_ge6!&z%0}N0SrFwx0kc?A3!Q>;H>J279l2Z>RvEQbVc^~%GK%TEkf*KX8JSPCI z6vx?~He`BmVK&kSY;P{ZxfXjf6+v*k=5-E@iajHy_4H#W`hHFPbrUQ1*|q)P^rw1V zHe&>{^;Y3JM$;{?_CZ559thw|2Sr(teu^n~?c$~yM;C`oY6juYPC6MZLTGd8(bVJS z3zV48C{nA8t2=x&`Z*^|_Xogemh%~?35f=RXvj5p)a0S4dG-!HpW&DdVRdXE zl&{v%vC&x>uJWW&lThs!gPSzf3X4-G2CgEHFzraarjOIRVFUHyom?GsWdqo0F2@|3 zzHybCFP;OMiVwv>tXBZlzOpkvNTtE4ChiPOoA$BLrE^Nd?shyEd`W~CenOHRyW_YLw80HD3H)*!vo<06Nu^|)YyY>6&1mvrXB#LuD6*G zsCpbX2u6)Oa``S3rEBejYXE7+LorCqBSgt5FhzJKP6VKkPj5KJGL8r`x zQRwCfs|$Ijh@Y}2X0R$CKiD5$;u4pcnF~Wxtg;a{;JDC;;9$draHC-8 zz>FrQ}0uPbhdSJS!|30FTB5Az&jxe5} zZ&Nfk8#=Ub5(ULHHp2^$N;5PWyi+N%Z;6GvYayBmb}zPnm$GBYx|P8;x)TRa@HmBC zwqtF>A<0ieUya>9n!sU`@EbA@K&fg`1W0>8B4XVWfz6ee1#hy5jK_M1;AasXg5XU# zr{(9&1Uk0fHtXs%LcYDA#VV2uG-6Ard(UzQ&JRK1nxadQMRCd3Mbn}Nfr4)>z6*&p z8ZOzr!qg0QF8=1Aze2O6YJCD7)*jtFx~i4$QNm@9c{zY%>qT-6)V$K6$7fU6x#Et1 zc&}L6`pPR2Vg&XU#+ZiN`LFR-j(K=JH!9~8dZ?$%qfhevM5*u>Tk>IEgJS5yMZNce zblnW+=W@H@np&1975?z(CC*VrzH2rU6a|>N!K~K4pB>)IwN!Q?NNtT_%y+X@Ax>Bs zUr(R)tcaR+Y7Ptd*1;2 zo35II$q`}KO|WDPi(NK}5tbL3YEt-T{$vJi4tY&pH1w}pM`==AT}x-$DCm2-XCE|K>lhsD>w=4F#pcX7 z%-P2P6j0;>Jr3Xp9cZxEdYbm%u8l{{s(d@>x+HqKMYeZ#1 zcHr)Y7II7wh01Vt%p8m%PUG|i{m>8Ib1PjK;oGBobtX`+F?z$*_ zIo~LOqbr#jy5W^@umKGy%7v-@VmJLxkUMx=%g#fbG)*9g;@F1|j&$}HcwB_#P~s}! z7DjLbm6whUUIw)}wFftD9P^s>;ADkko0=`Tbxa(Zml}5IFYpkwfb+q5*gjw%o`o+h)RUYdYl2c!kPv+n< zl|Fmx5mGplZ(k@ZGjPQ-hb7BZDvy?o9d^xhv8xq}0Wb3$9JVS3RZF>6vG0q9R!dEc z`Y0Zvycw16SNuSEx|B0!y*N+OT=ukQu^YvPnRb|B2bD~HAYr!AI7;Cb6Z;j+9Wh)R za39ixnc&O^NC@usASiO&0}^C+OR{G~dd8DX-KhD&+hB70qCoRrYk8hbeOSv7oW4+L ziVL?WJz~ZNK>BdWKb$ejqGal?HDG6!Nll6A_07s1imYO9Hr`?r%E{&zs~4ij{^4kf zl0g6NPV7*r>4|-fa%wp9O7$%AIFHUl?d(_+L znz4LI*LriD{OGdQ`hqj(7 zGC9r$G*{Oj+S<WEr8tH3efv~+o zRIi(t6!1Yxm&^~O3B5rB;$*J`zlTi3N+)lMk`SS#Tsl8@CL3>F#q;6JPcCiBh4F2oWz6BD^F`VnKok&lr{*+5 zWNqU78RiDyc-J#>$XfV}9MD6+o(+~g$~B|{lnfzE3K%x>dcu3OHbp*-5RF1BnYz-( zKnY}2{9m#|UbV_dAyhgJ((@)f{&Av&OM^Q6Y;X z*T<;@LLAl`Wfad3J7F`30%LfVjsCJyrUnA>$YJvWWVQr#Mz}~IqBF$r1-LX4c~Qo& zmF5o2Ca=vzBVq(-MzJnDmWfeya$pv(8~ zT@VLEs*O`!ip=g%=}~sW*#mDiL*w3=!Qmlo(t+FTwD1WzIy8oA)cF0qtI+x-`e#&? ziyjmGgvu_tgdxwi@+dM%C0cF@b`0pO*#>KDW$W=os=8pRrn~NDBLqgA5wA74*hZ1J z7B0D910fFJE$5+6{kAP z)fNH<_*L-(uXJ=!4}==e0@0u^Pu^xVl@w8)scwiG86NByJtCO(XeF$C8!+h_N71{a zJLSTtoS^byRKc(nBe+iC(<`ecwn&H4dCySL10qDdt*V*u6|#C^)u8=e%Ztl@5*^&4 z$$&DLY8-G6=6T+AuHiAE!`648{i%kBsa}|(c6o`^c}B8``{MRAN_yjlm{i6l3%3*X zAnE+{jkM8+4R`yd?OgDz4O=Nf=-7?R#A@C4XX&>ZV{nF`?K0O$mq?j1LfH2Uwqtku z1}}uYL9vK2gtcy9VroIrgh*!z2y*)INw~G7Mz1aZv=F78lqUm9PosTp=>$Yjw|Z0C0NuTF^~Hxc=nQ%pnl!F~>j7Ij+Jw7;cPJC=uG zK8aY3eD-LRNLdh7A7c1+lz~kaW%y26JhBtA}d!h+}}}~&&M?sE^^zUcnyG=bU=p_yss$r+5NNCf=K`2!2=#_1{w<*kK$0n(0`lOG+I|M*su0~(9 zNQ2Q5srmAqmKc~Kf4a@oSY{Yjl%)wzc7J!ZrQeRFy5}4W0aOj5y|;}fv-uGq7%^mG zAa85eEaf}5`}?Ll)sTQfNKb!P#}uwSa|iQj#ltBfE}ZbzDwG3?L#(JJUJwQH@7nF+ zHHy2&yx(e4yDMPsp6!_}&YdlHUy-+ffQUA4YDXlW4RG1KN9D}t+Ra1^mVCaD2j;%^ z^e^6o2d5`Q;>0D1_SQVNYd8;3%=K#efG!T1m0D*6yCiQ$1$TATeOdSnz+H9474tmp z1-i&(Cz*Pq;)gnCCZ=)f1?!h&>P=xJ$}PxC71=$T$35yCPIw^8*RY9_hfrZ)o;P)k z&L|F3u?5PTdV>9*d0@kCsV>u~aFW`C75>?SVG~%80q5K?$!@@M| z)J5jOp2x0eb}Eoc@hcOqhfd;9zzS73im4hmTiy$^5 z5nQ2PknfEkRiaQIg4g7^$JK?3=l#&yX?g}3EZES`Y3)D;ovdSKR2c~RxP1Bvxg?gU z)#M@en=AsS8sfq54qFJF6@sR}*HXl8U*-xOS)cVOYz@9((_bCMeglV1$mZuPRX237 zwwGP|=PiEwbakp{cK38W2geZTnOX}t)0~@z;9JC&2d;_o>G!tbp+>wq4SRCY7Tljt z|MFB99@84*6?(hSe*1_Pem7A4!!y~5a!0j6>p$p{^K>&YgzBJjQ_dcKsXZXqzq5!M z$YQE!neV79QMeD z+J^h}av-r!Y|EYW*G61<8O}4MqGoqTWq*Sc!3x%Soi3M{aa<%A*KcP%M(;MS;d~^y zY{iLd*3T$-kGt&fm21`y(YxwB?q}SctJkjAb4P)%PCoBM21QvL&NYjd9ldI;gcbNJ z?y_S}TuU-a)o@;$Sav+faJS9x&$ur>v3dOftg!evt%tY52fB*82lG3;;yJl4=Z8zO zFH9c4ARX3Uc^LWRcV^*Vpv(EuLD_P0?Rx8cZFg@c4F2D|?1is%6NjGG16%6etVHQ` zIUj);-%+O~4qZ=lwx>5|39ny%)TYD=;m+~>nap*d!e(-xdl--7n6srhsyza!WN@w z9NfXq@Y)sIkmeEV;g+tD#UZg&>UURco=P6No^KlC2CtXfZqSb(-u=+hZo!{@tXVT=VAd29(Lq9J&CzLDBi-;_mM(KNM-PKZ+(n zn&}IR%O6XwKVyC5g(K(DMXRoL4?7_;BzWlx7qar`5zg(avdzc6AVQ{IqQGaH?`zJc zjyrWdo}}T^4-MztYd+(icS@VqqVv_~?=POv%%;X$+?eCO`usg#OQ1-`QTGQ=-9LQ% zs`ctj`GHgS4;{BEk_;NH(|K}Rw%uJ3!Oweu{HD{h+ufB1=5ek#BYTc}T!euydtdRw z?5h&!bkP^NfBC}f1B={44Ay@&`-f*{A6bMUA-w*()&KpX?61hnini|szPoVLP@-wQ zS+YHOXv@pxdv`gvd8^)$TzTsHg`ZqPyS=WovXD4#edPI~uMcf~i;KavJD2gc_YHP% z;u+{3bY4^5`gg7wFi~IO@40C6JKUsbZdk^?JYpZ`;dX}%R zeXsXOoMAh|ai-%WCSls~NSxvA3>q+*5D1B}Ga(_+Lf9khQlM-otBgVm6aoRlhA`4S zQd(L{TbibUrZfNVSJHD=>;(GueLt_?|L?KybEKmq=}0<~&Z9ZT94#bnzEiGz(y{_$ z^lXWE{60Brh?O*k>y+BWwfD>VHp?5s*&gxzM`iO!%N;}UV&VH+!fM6EkICk#sGbP4 zO+5T9Iimz*df(o`JNhqtLN+y7`8pWv*+*L1#LGbLlxX(U0+aeV3ERxR@v5w?vh$Rm zxD8g~&DUka7&Jw_?4P>u4Y{zy${VBhvY4$t;va9xg;Nk$LKS-c9f>+*rHyIoTFxyh zVCzGmo|IN_jGhJ)m%bek|+Wuo1b=MW6VxpUAok1CT5(*lZII{ZiK5A3(-8VND>G1Oj!zN3!m%09ZMA zmENVlm32Q4AQK{W|5IQ-NU?)TB%^izkt;1!{(+@q;l5Ew>1p(!(`Fb`{xz*e==A%s%~_hwRDS6NoMPpCAdR~qcLPEa6CWhj9{cYfo( z=NF#3Smphm1LckTUSuc-8ey*7J5-xLJFvDK;oqrk`_5TZx%YNuE=Nr7E0ZdN15-bn zks%7Fw{||Gxwufdi4bB+M_#uy?$em6z3C{~^Nr7EK1#2w-dko>?j2dh6ij}+YH!eW z>P8}TO*0a)AE~Hm%5ElyH03H2)6ux^HswBJZ{yU;y{Qp=xK;h{-UHPuudo*3QG1sCYH10c3(N*p_2kFvg?WYo9ze2Z2 zu8U%C{x$pm>WE?1i2QBN2zzG$Fnxr0$0@GdYpZas+dos3Jz|aUcG@GXoeqB1?j}gd z4Z}v(G2G6|%DoE|A5k7e@i|50o2j|s4BvE9#B`Q2>2lJ%kt?Qu58@rFa;b5j=&IaX zqs*Gdh1+5k)0p4>W13SpZEsl4Xkt3Io3wm}hWzprcbF9PlNc4>bPZ0aSQ`)h>uDM$S19pAJoV%qtR9MZVwoG#`7O+uManzy#r@LqzWONIp32HSVk&Z0x$k3MGd~Z|G%R8| z!I&ENT^ylHE+XAOKsQ&p(p0K&s3|2=APID@++$f&l*!MF{B5G}ER!GApKb1H+;@oz zGXtgbWo2?qs7DiK*$3E=+oVFV+bwKrXARm{$`dsPysz*eTxNl~^ix(WZ8S~pcRl`g3x0RGS zbw}=;QEKg+iJo%&?q@#Q!!q4gwRb>H`uef`8b3R*4t>cf`DouVcDnBk-92(XV|nz# zoZUVjgoS&a6P1W7DG_VY^~ky^)e`Ek{Wt*69nnk|&}@77oMi4QXYs-0Y zo-7QDAQn#MZ?j7Cac7^mGpRIrXG&?x&eYwd^S#|uKTF9-J|UyC^O@3qudd2C34I!V zYCs422(^+}%B1`3$gFNVbX8+?JQrz6+efop8b53|* zq8!GyyX?uveI-rw{hemD4$BAP2?c;>w1k$BtJFb@$H~_Z;2c$*X-9l6n{Z@7`s~TGC3#-Rfj~Z22jR={TjAl(7H+MpL;j&d- zg;ki^Ox65@`v%N(;#;bz3gd|l?Xx>3Paa<`_Jnb9NV$_IU6nd1{!b>fe7sKRq?m+$ z(DzG}*GKeFoD-bvNOAt(OQm0y6)~x_r4-VCrCS0<{T>lDTlr`-lobZj@@)ltq@s^l zEm$>iY-k{kgsjav{8+rEkHu)SevgPU5EGZ?#jqUfRgv4kfESHd?*YL7$N!NsgvskM0aMDd7!9_W;2=PAxHTM7> zTUZ;znIVxRl4O#VWCxsp8}I^2fj}TR5DcUwr3S*0<_~@lufX-}!pae1B4M@=^8h7& z`fa_Xj6yU^v8x|s$`bDSj1{G5B%#(&$Q0Z)%z~38uERC=!SaCZ1nsM~J2@kRXWO1v zP;ho~aFbh-bIW50%o12lBrGZ1pz_M_|0nwoArgBKx$TO#?sNZ^DTK=m5w5gf6k*p6 zy5&LeDdjlihw18^|4CL88U6RVI+Z4-3r`Au!Ez*rBX2mx3%w{$|6FQL_>z?}N0&Y5 z=r5&HEnCKfu&fE^aARr8q86+hLzle)%hq(g_C&kwFMN1XGAprN|`&9zh6^EJOhz@B;{e$3HGH7x)~& z1AH-I2|lC<5h;u)c$`2^xJrsqgr%Hh!~^D73rIJf5RSNxUWl`$s31H;iX#Ze2r-%P zTq%wwJY5Q0JBe98DJlua2~kCOffTibQ>CaQM4;0MH%T#*5dInn!ABz@?q6>r+$=>K zA@Fkv5iV<^7%cGVf(Q9=0wLT_Bm|x(VKBbMguvruSA}0ni1=`pkiwrz2>R0q5zi_@ z(0397e+D78HLoEAek~#J>j;tGXAuIwkr0`&iST?W&Lsq&UnWH6e}xcyUO)&umhLM2 zMTFqm&L^!t+g8nwbbELS7 z5cs4(23Mp38jzpm;M5BUwML0z=D+LpY zB1E~P{tzB5#ifKu^p%9iN^uP#5`P0B0{EqAtMN552tofc zA=6;YBkw*nmmYr!!FQwugg>jQ+22zTb2ZCjuvU~1; zK*}+9BnLa~vEGTL)xov)Lz~MU4z6^h7pizTIe5CaDm=1TI=dbO-{WlCdIWW=3tP%s}nMo=k^M0BfJk55z6KbwwU)(3Y zf*zG&*Rsdv(i{)4Vxq_T6yk#o!S9#$-5l}|_~P5xCWubMK{xzHTI1GoM~ z{BokI9mb--HA}}=2)RvqsoV=hMK%q$&k6awbdwlFI=W4zU!vPq-z6DT{xT^w)KW8D zIPZWV>&q}?@&^l>iY#Vx9)8gG6e4o~EK0)zLZIiukPuCl6P8wLbeJG16`C!IsStXd zm3H7?!fGkATE<%~ZSW#P6S~VW;Q$<_ zl`FP{gPQ!pcE=IbfuOx@yW@>3xKLG9G=FTT7{YLn*|O!2Z#b@~0om`~aJ;gD%h;MI zQdy85NXdSqSz5bFFiPZ#)<@rPEO8u29|Gy9g0n+*adxsmqi1b((@0yal9SGN-0%kw zdgQ@!Pzp@6PEud&MOlvi!^(1yKpbjps>t^VI1j(7tTWGZ&v&-i7oay>mL*<%X~)ZN zDIr1cIuQg`iTabS@w@#bYw=E8nycVi|p=_yb&F@ge=z? zvqkkBXE~O;KC?ABcxKL--%=5w<|Y*4|Kz~X=NiMh$PT;SP}eF9qh3mRH8Q2|iWQ~7 ze<)VL>yRAP1~=fSE}Z0y{e9TMfpuuBxM5Ss^KA*u$vpHOii~7!vBTMHkU91fTC}uT z$P4VCYK)IlnA3o{&|q4KS-TvViwtI|!mI-3VuM*v%;_hu2I>+!7-Fk_DN(1;+ogu+ zR$$U1t^XCaZvDSl^~gaO{&P1BtRD9HzM?qNREbiIQXq459l$4kjG`EAK_iXlVQNUT zC0Z@Z`QRl-=F6ZPObP~ro7Gp#&t76pkr-bd@SC^Y4dJ={HYY31a9vW_i8R^S>yrL> z3k**7IgtS-Z*?|a?l>M5{9P+^-Fdzz@1ajENgbHwnUL*J6D0r@yDw0E{}3oa8ti3 zcdd7F*vTn|`U=gi-${1Tt)Z$ZQIZN)j+Zzg;F3RX5lN8Z(Li8R-CUPjFQq>A|k6LXq35-I8O zR)_7*qMTye&OR%*$lj3sM)r{GH?!ZgWzLz}Gqi*J}WIf9n?V#!1qr{Pft(DRBx!B@II;-zUK#B_=Mdan_}DH6njH3B_+j{VZ3!< z=@lTe!}c;ge*afwXWwo5SkwJSHGTg;auS+8wVNy2_+9EqjBS~R{-cfGWsYwA3V}`Q zoW5{{+BbuRY8hDK0p*heSKHyX1;0D-%h-cgwhe4{0bZGyZYqQj+f7pRu8@9L_h1Ec7}{pC#^Q+;1(wgnSR;pHFyIY$%@<|-3#q*0EswUTfSBwZiM3?a6{!P5`O(=Y1oXQU(7fVQD+MB2)Tc$10^mPNL|Nl> zo4*vdo_C$5_7ThH94{v#pY#(m?C_jZSmJBBde~t?xsdqQP35gN)O5d?eC8=1`n3dr zW0cPM_7-3L#AlgmCdUcEE`x%p-|^z9@yhXVUuXAYSg#ygMec;G9zo?w+P6|xZOaQ+ zJZ1;Rrh0C{8U4GwGsC5MuVr0y+{gMG-W|ws!%^Qcwm}?~9RDR_8%&$GwsfhNTyTc| zke5dXQs(^>Bav%MmwCYm_83LEE{^h8hCDJYWEE$#o@B324lV&ZXV!der%G)f_$+*t zwZxYBb|4@6Qi>YY8!)_d6cniODWg(`_lN3a6{}&pvOZmhD5UbJsqtnCk<=|7BMT|6qJCSVuwJtz`5SeOt~cIsOwb=W4`*=REOL z4Y|ejawDBZjG%9Ei$<`cXhR@TiM|kDag^w2*KrXr>^e?Xk$n-hy1-`-a2bB=I@Gwu z3J&RM?FM~^>OQa*W*bQU1EPMGB_>!_@;-=qx>mdQC{3qdX->$}pWC@>wSdb?w(Db0 zGPWw(%94$3Ds2<=oRoBnDEMVbx*E8}ake3}B?niPC4E~Zp$}Auud+g9Oc*|Vcxh>= zsti;t`>t+J@$w&48TjR@6esT174l|`IP_ErZg%?>vJ#x9JSPhG=bgmx;9E~(VBMG! z=fRlwfh=or4!~JyLs69|YSpIgJ_-3?aJ@Z;8MnhEhi!?Ye)0mQBt~FL;s-A{UN+WY zs!7{=OmY-2HFBe<$e}kp2av z{bET=5=f{1%P0L?BI)=z(j-xR<0}YtEJD5R702rz6RJ}X_8H)eP(apfVW^Wq`elSV z)F<^E>AqmA86lPdX_H0m_g^=(@4cQtd%u%FTlWK>_WeZKZ)@6Ohz*tq^WDo#lZ}4w zb-4H4svcWASk=GKNKdTlH5q=5>eU`my?|5|_y{axg1fH3d`IgpEaofxuoF$ymKb1J z!3$o-YJlS3c?%~FBo^-!*q!G&^ zEJZ}s)nZBKs8}X;DVz|E!EN`gfLsZA*yr#)4JNPqzx=M_iD_u+UVvdj!Btv(t8%D3 zF$C>W)Ovb?mKdQH3v#GFufNNhV|z%O2Kl2w>LeZg@hLcNI}-ilbx zM<1q%hlAJ^1GllQoq~{`Mq4`!ZLLpg^)tPV!#RDB4%$xA(I9;mr1`O=!xBgbqeHYr z8AuVlHz1aDd>pAK3V%(~ks$p5q`5Jq_F&V8X$F!+(YY^!v;w661Zi;$sdJ3!ZG6(= zFZ-nEq2fvF6G(@kNztJmNF;5KBMpe5mymQk7(k1JHB!;catp9cB$}@QG3-*3Qa88| zq|^=ke059WNR!3zFSBPH2GW0lG$V%8%L~PZnk-7r_esx1CmKgult4P-LXt9H&jV>} zzIvk~r0he3vh;};4e6sV#*m`FO?uffeW;`Q+Y~W!5BpG-!SB(BM#_M+zx=#?D2KxP zb|0EJ6#j4bp={YqAKDP@9tw;8R9IboL^`pPtXL`>k*n8#;P8nqdAYOy|bni*2@E_HCa#UV@jkPSr zwL?5?Tb+a`pK?7eT+>g?*u+zAvARa95-xfqVzQ9mb=@FmSXvc%VxM}b^+lX1^dGL? zjv2$&tBdql1O~^I++tPSJ)qKZOZHSZ$^d*`57RfUu*4|KO1&8(yI`^s*{>>*wJP4p zek=dMnN>loTFn`T8((hpHWs{sbqCo6r|Qm7j^RQytXD|#Hem?*hvJS~z3*%eW7g2| zx(ar9N0+91%fcgRP7ba_!+a{Ymh=hza62SCkf#=t+7=fD45`gjGyX-cQ<<8cnTq-U zu#*0hki}de^mHy}u^ee6&f~NeZb)LTB^rcJJ zWBur>ZvNo6Agkf7r6*5a*Vw7Ue$5SJva)o7IE)JfT03!3ArGgOZy1;R6pxA3=!*|| zRGM0Y(Y}EZ)ii*>t%%C0s!gIUs$BWv<7g1~K1?N3LmhI|v)dh8sR>VEjw`p|E4msL zBn)+ozTVe-t~Q#FTBOOLj(#axN6|wat{C&7Px^}lQfo|VEU72PzV;fF3F=cjUeo%N zAF4er?%O>{j(eCo3YE_XprcUv^jC@6_xL)B9uo-2ve!t zRlW6o)m!IqkVfwz$vR6nEAH^vDFU~Iw(*Y|w-L12a&(wgTd(Tt%G}szrUY*DIDAty ze7G%32B_se+;Px%yJ3ot|EO-5BT}jxuJ_O67V<97;{rAP#3-P5d4q;=USOVbN$eyK zrEBK&wL#E)}?YhWan zBaC?!+an{4&luIRT7MO7D0}2vjIJ*%K6?s6>E^lC&{CxsdVN|VDGvUlB$68^^=DDd zw8+AQXFV_5FZ|erJ{>F1!fRA@Q;eyXwH*#oYXVNWMoFSpZHjExh=$y&DH=z#f<$pWnG_vU z%vJC`J%_1NKld#0J?w`}yX93!K5gp_kvG1|B}k4JtXYB;Gczh@i6r|as(+dGHL~qW z43CuuH?Y;Wt%@s6g&V7S1aCZggmG`v4Igpq!YSU77oO-fhD))buxx>MM8$*Nv~X$h zw&5KiM|T`qCG#$~W!Ackcd?A##d`W-Img`@@`}q<7$>ED%XTNZp(JBpke+NOP3i~* zjD22uviFjxco-WQ!=<*{Zznf!BB12#b&4-3+sKzOx2r`^mN%~A>!F~Vy=*q)a95N~ z3^;D~%q(R)-J4%070QQY%7J_B;g7lcA47oOVe*)`*XPj1HN za%o8bjD)k-9dl>Uf+@|gWXl>hiQ^wu*z^dalU2Fd6SsvjbpM}T63R47zj8xa+LSXS zlxw|IYp&6T)NCo&wVRYWT@S|H$zx=f6Q7X1DiAN$tyGkb|ih<$(~#Bkis~2 zD@QHt3~znuY2z2zGGiaZJY(;Hv+!Zp*e92LGwo8^6SSCu7#K|AevcuUf21D~Nc z95^JQ^>0w@7y%D%EL&jg5OVG)KGR!RdSbKOEq8H|NUEK79d@D=>0qwnP82s8f!vt? z8+SeSjab~T5yXz2f(zZVA3q*rG;4#Fx1#1oFS`N~ z(QqatDi&x~MZ!Sk!XlVJsCZ_W@6wDHs~d8tB^C)`$Hr+;a2eCTzJ$b%W_3OKJo9 zNek}wi<^Sj3u8ZJ4eW2b`TgmLx08eQo;HTxqd7fyNJNpV6JrS@vwfnKImqq zaFIKv>>66W+ORDAm5pa}g=F1~JF% zl2n{gCgrn`hratgL(R>9s6D1W4}^bGCJ)BMB|x}zQ@)&(IzVv2p8v#r*1VilGL||d z1_n7^$kD?jFHnmGYriHYI@b7Fk{)ZE3jHdKJ2@6(DWSUaN4}nvSM(lYzJ@Lh3)i-* zE5_y9NnVJN@&K0bn(>R;PVW?QXObSV9{~Ei$KY}$4ENIVC@j|)3%0?lW5ha^pK)48 z7w)E(`+`Y19K6J4A08019fs|IZ{U~lD_FAsqTGIG+;~_G-7|L6?a#^$1+M%(3_RU4 zexcB#4Z04f+lh_@mzf4DdKZz+xfhXso}t_g3#l{I2BrP5Eh7=v17r@t>npHja*6RX zG*rXz7rM8?kiC?zb$DgsNE$JovX}lfxNe7m!>`Zcm+@1)MEY|Ey$F^qME?k{OdjLZ z0B_mmb1+fs|GcBML4>0h64V+UzebY2^ja< zj;Y$gTyBOjI-DhhF;Kk<0Q0nHxjkKR-G&%Gt^s+%U>%mf>A3P~B`h{ew4fV2bYffA$7Kv#tJ9urFcY zkuB)%2S@Y_gj2jcxWOx`xIY>i^pQt!I!iLngc;#&4(I<^rLzyLw|y}MeW`8db1y4< zwac&*)$2!IrrUZYrL>IQRw2;o+&)TK4&zshU&aS`Wqyhm+BT6Lf-|0fx20sLx}}_E zsuOn9-TvewH8g`cQI}Cf$-<8hG5=m-cK0> zn#?3y&fddwNH&1(_n1=nQ1BpM1G2H3 zdmZ>K!_TTWg*zR)i(%I?!*^ut$S+2x=YwwttK-}l_?uFW(S+F6x zdlEh2|K=la&!v`+`FM*5HgQ*B-)(SMy8c5k333y!-{34#>T$;seY0esQPSEWtcLCWi9I%50=iTVeA> z5lU1~XE2|HxlGH-{1~EGGD(f3{F^z-=zr@p7S>I1#(6_Dl395=VW zo1gCTPzc)@n3t21if>qaVorCRVR<~c%n7UMEgU1+hlQPzr zHW$DltrJU6mUm-imv*~8HGk-f@DyG&cX{1%TrCR*guEd&MI92o0Y1K1g&L@Q=+{F4 zRau+#KC-D>Z1&Dx}z^x38|VafFvM9ylYm4u$A4Wiw5)THWLuP=tC903ngA^#;U zG1s!P_Pp)L3Dk5Q`}c1vP1jBJN>88Rt|^#_nzaNYvt~naUa@~#Ax0U`2stx;$2yvQ z7RIVM{C~VTrA*G@M-mSq40*a5XRilIQuaFA`p)Q3Ko7S@#=_=Cs4?`fDt!Qf2X)}# z_soddya*MB_Eofc6jjLi5f_Z%(*@%yzOUmWZXxX!Z0ffrIoRk;4G(LU!*`uxiCoXl zdKOnh4r3O0_ET>$3%JMSD%U&yn;6wuuIK8yp~WcKJ~2{wLpbcBX}3aX#0CuJfZ5YHp)o1d z&A~ty+POT}Np~~t&4b(!&L`4Cir?e{>1;aPU|cS#|*oj1sm!KH<^U0meKREz6UZbdHjUG0D^hySp_%)JB2>1e?Xw6(m=oF{oq z9zSK$LLqMq%jkD@OdI*NaC#mU0xoI}cj^mEh5ULrr{D)@ZEhs?ly&MRZh31sDfK`) z&QAt1eIX*+4#V;Ib>Wxsd%h0vl@6y&o5jjJbakM+5p?A~-CdflSY-gpQ9wP8(c*#leUod=AsCVhSkFva-Lw(~&6yl%D(EPSvYo z3jHzfr0^(Tx6NIbjH!5a5?!+laOx<_v9s@jzwE*AXWO^YANyjQ)V3zOrQx^Z9u1!9 zI2X-_KlD&%hey8zcKSqz@DFqdJk}$Wi$=XJ=whbF3Kix|p_0o}MRBHjA8xZLQvRcq zT#ksb$u@^N#x&aJq=gE7QbB()4fi=|qsrNEj|Muu0zpvX*y52cgAHotUc9K}X5(|6 zPhvZIs1b(6%7=bk2rw1rGkWK{Tbv8*3(ZkZ_~d)#tpvO}1)&H0RQ-u~5WUiJ(JQF> zGzF{6z-bSyv+kjzEFGp+{Tj+@jb-Kh4%OrDQjg32M%61h(Zg3^UNAow6ZaeKSLE%V zVP8T@Fn8K?uiy>;2!;L}r-dFh(dN=eJu+q2``V$hTe3mhCt|}hmt4zfv?m7xfgq0b zz8@#)oZNDZ@j4^HYh!}f>Dul&75AgjKHKXoU8H;pS)cR&~ zrHoY~IamTWeH~77r5aW>Rmf}7dS0A)O`2Yuxf>yb)-LQ?zHreBeBNGzTFzNRP5=Up zqff3$n{pJFck%>79&v(AMAxUe0X?;tSi`^gTw1HCi`ZWDD&o|2G19joPT<(V@zIXj ze3%+Jl#^wm;Z5w6m93ERSM`?J;mfp~&(86WsvfBb8@abu5t_ZV!)Z~jn z9=p4?>4*N=5W8MbXG8G#x8kF9?VF-@*FLQH{a-)c=v)5=&KFjM)W~G;26{4)F%+np!h4| z$ky^Gafc!HcA>6dYx&HcwwAkDgY*eQ>tTi{wN75~d; zdA4R?)tEZ2Sch1eT78hs zKtg%AHbW~95-NA1u5c)M#5wGd2VcEviH12abFtFw->ObHLUW!G*11C51P@AaQ16KL za2SnYId4I=@~(Z)k)`QsuyyNu{4(bF=YWduVKjAP=~0~F!Ln>D%Fe_}R2+bfPD zjjIn@?nayfyEZwP1q~<*v#0AzHzWr$aBbH0uxzM#EIF9wWhl$4nn#m^Auml?&Y(;B zq(20$mcJ(B&Z!h&L*AO2N3kp@x#Ifz6}k69=}9w(TXc@PqG>DcLOAA5TMk(ivRy0t z>zt1Pe~SxlNe-TnmoJg#KJj_`M^?;qRWY$dclcoJMt5`4Sy$ac*GqSUd9Ih^28+}i zn232;6ca9VlW>Q?$iZ0JjD0d#Jrz>l#C`j~e+s+*s7lQP|M^XvSgHS*SgGkVTB-M# zN?k~7Or`#lsno2fkxISSRBFmkOr`!5m72%ha+D!jsXsH7dKIvFJcd6TLEVC1-rr!s zXRul+6<%_B$1)u8A>@IKK$wld^$ewR$?1A}_?*m~f}i4hq#VC+=PDn4Ze~XQ2S9xX zzdV-YY7<`Gepoib!iFcUvu63pZTf@%YnkEP-+)>_U!;%j=X(OY5x=-48mg7SytZm( zzJxm}K>!Z$u*3M`t;|-mGP5nK-&fvuuJ%FOUG1G!aEoV`W_hQFOTE<6q`GPT=HmRqA(yjW87JV1?h!3Il1aDhYxl+xK@4+wmqnA3Rnap?I5;Pq*x6Y z2k+a%Wt36>QB6Z71kzvZv8bba>1wqBU{zZR+) z%$^{y<8OAd6_RM2tbE*x)88tVqa}OuZQ)kuU zI$dX=k8E$KscorlY^iFit!bUz*w{9`wx(%r%~TwZ;*-?3cC@xPH8r-hbyPL9b=1_) z5i;Uf-QLnt3oKk}8yc#F6>+I)nN#0T*Vxf|ys#t4*;Vs8T57B3Ol_~DLzIt}c^yqv z)iZ0`IvVThT5H>c8}U;!by`PF`|PHUmU&Gx+k_YIFuS9!y`fqpMX)VZNNsg}Ykgxw z2a_cNu}-bi>P2z{(bU$_G`FRrwXLeHy;TGUPM^zM>QG$H>!_ODRMXy3S5@8C*dkKs z$ZSOTDqUnrq(*{@aM`A~#I1ppnm6#n1OJN5g$`gipIy3@>-OjZ<$oZ3$2;e9J}7q=d${un5?c|v$_MN z*o{0+%h`vdIkhbmYvlOUI6E>u&Tb8?iL=`SYvb(Bz`8iQJ8)W@9aSvOo;0vN z&K?*zBhH>YaAus{l*mq$$c_I4CBi1N^OOz$5z)UH{C3Q(Z>gPC+uGVOyRoKL+zWA! z!*sMa%xq|!%W{avC46+R3_a zCx=W%Z+6ngjuxvtLcA(|gg+YYjQ425LUr%_e_J=x-L;sC$pT-ATR_B$^&N}WbOYc` zg^u0B*v0<^t)(9qJ5(?1T2i}GeKK_)pOP)qa8OF*Myu}Q^|>Nxi)Jre+_bKXw?^P@ zkaX3d^^qgFd03wE&u~jo@sB2s*?>J<&UnPH!#kbLW~n+Z-;NWPhk$W=c#bsp%j zn-30q5U9s0AFTvs_~~~!APSLSB5~?n0SF|)Wa8AD>;b9lc&$Wrv6aR7kALI*&omRs6K`YD^bb{EzGgO!z6?!s0)lr|x_vbT@t z_}x9^__|dI`Tbp6qWenmZ#eFL#@uimz2LL^Ia`m;CzinTaV4-lt^~H*sbQVx%+~F8 zdZxCq1h(7Rxyq(XV7r}PpscC{w%Y@Bd0+x<&C==1MfXeQ!ef2G(5zMKME4HcQ+Fi` zQPlT~Y3RYl)VMOST6{I3m~K3{m~QrqN#&gA{<`g{DxsPU)r-X{bHrNer+e7`+8iJ( zHh2K_ICv8m)5B8l(WoXxVqUQ!vSmh$J*uN5_q02W5wov$5yw8(=eRdB>Bt?s3~)!l zXrm8%=SBF%^%lPK02rt=PA4qVSbC4;f?V>6g}ysuCTEF|^n85fP$`8b{MZ~~$AA~!0-1M7JHX7CB`y}_|H2REyk!e@)g15D_8y<=CebNVg`*uIA z#PjjfH~?nfe{33$CmvEjxJ;WPVZjEvNL2Fb*+L-@-Cc=nEg^kml7r|@+@*&RWoAy{AieEz)D^2D* zAf1qvJuw@5;@7kdjDin?uEbA@IRtc#K1^RRep0j$3{@_C>p)t3e#uiTzq%e)0*i)* zU@Ct4T>}UtK^<}Gt%Hkb!G88!17|;bNa9CFZ?Vz!mOT~pkK!aeQk&z#h?T6~%P6%ks)YjJe8cqXfYkfm~TSrSEHSuLW!kF2hm-BCNcscn7-CWKn%i-K5(`Ub^KN7bxZVnD3J+!o}n7#NE}>T1Lw zAA|AArm7Z%J*QC&J{Vb#@lcb25z48xqUd1cOi>&~&hDsgYndg6L@;WuXLfx4e$NKnc z)lRj|!%V3-&PP@?)y+mZV}i0;V8GwVeM8k8cx|d}5zj|()#cMMeb-QfMq+ye3As@X z3}!rQ+uG_Irim9KZf%&Dg)^r<5jxt97b8v-rr9xVKMBQ45$EdZGdrf&cQm)xqPSE8 zAa+C?Yi4z@DMDV0mm^3um#ni}yb{6IG3~fJ-a`oYUl>?`VNL%N zm<8l>2BX^0>f0F~bBGV2k7uQ3uK9Kt8Gj8S`1FwWK0eYK zIfTEhYvIc7WlOu77M{Gcb@`b~Mfaml2x*NO&wkVyJwc6Ut6{pyqWcL)pKyxv0Q!DT zL@vl+4oqFOX62HFT^mIAeNG4x5R(h{IU~lYT5k?#OV=!o&Tri1gfQ3=lM{D2`d~s-jJ{v|+*_|w-L`(}nq{a6Q4#pSN~j1Q zSf(PxAN|z*E6Ysmpn{-!{MypHZPQk-?>uewqBXv>d~E5-W%RIpKi|IY=bMriY=BtW zwRY(e)*;co%?cIANz68D%tSpSh6QFd3s)}`-OpS4jIG2en(cmGu3U5abWFXxXa!Qa zt}_kxS+KtpV{d?cM~uB`%_3l4wgO@0Lp4_u%0>4paTt`)t5#B~n&yu^tE>ApD>XMq zO{IO(_iOW?SevI#ZClf|vT^0q6{{AXCb~cNLeQ&XYVyb4u+c0RWN+V>$V^`%Tk5#d zP;|fTh4SK(`?jYq*o>JeeAhEGh0D9AlvlTl?)SW4nic|P3pEU?@Pxy)nYyles8tXL zZQEBYsAPndqyNf=0d;{oXc&6UhCc(O3UV{A1PI9Rg zockCzBlVDTze_bahXoyvq2v*4m%WALb>SS3>nQj}@7O zg$7T!UYqj?H)^wj=NLTUdD^Vtg$5tna++(!a}_8ke|eK6VE*P zJb0#^kIsX5s9g*W@ec(ecHuJJ9pX$r(#|JVW0;!H6n#IA26zi;f%x& z3mOS(6q0We#;kIT(lz%)7g^LwI(DSUTY|~>>G!dKKoV5qr{7Znfh0(bIZ_G%IO-7F zdJ3_PA(GkXLmvzd-7-PEBgtYj>UL$1W(eJ=%fd4Oiru_*OVvbBu8@3d#+nGohki3T z@J=v;IQ3@c8$Dr;Lh^mQMiMMhNWM?eNP_t7PdVH? z6`m4vxS1K9n8VFI<#2O+4zGqkv>XIF`7o$wDBha2IX$s9rzh6t^u*enc-C||&xz;a zN<;?DoFJYHW~~;N_V~nHr+l;%#QOjuPSS5w_UNK_2lkkB(K-6iQHR_{aIhnD1>YA0 z?6CElWI!rAUbg7Ab4S?u{p~^h?7SR1`4x%7p8LJb_v9C)9eP39`N?)L?AR$8b_&3B z%Px_QUCu+bzl&p#iA&9Z<*&Bu@Uf4~v;Ow(sH{f(yU zJ(#}c!E`rG_tW$QO;6GE5>2ntvl zbnL=n`yC4|Keyobmo)96X&+5rv0=KvhUr?GzDm+-^4-iIfu%nH-0d$_mH6RhByTZE~!g|CqE>SrTd6SR7B|m4VHXb|!Y%+F3WD zAns!3XJZGMjZ4J9&e!qk_n@@Xo}okkZ%RA-98Cp}z+u3xcny32ufaFtHTG(W#3Yl~ zYo&dk<=MGOb{HZ8`b*?B^fu`>#PI}8N8Sz}M|~Tw;~oO;S>kFQhUs~l>hFN*J2bW4 z50bX;;x+G1;GQJzgjE(iKDY0tQTNaVq#(yqTmnH_uW zc13xI+-Q$D%T9%yhEkEIXM?VEonwzY6W0)}hvhE0nqF5Tv#gd|OeTr@d$aV@GrgJTa{2=CX(*^nOD=GfQC78Y%*gTuq8Irfbb zLEJ9e9s3SB*RdayHIDrYd6Hv)AV)a%)7A;B!X1wNjP)hQe%4yx*xRfV9s4=!cn8T| ziD;#_1DYlaZx?v~Vc}oF@FLUgY zKBa?ZJU<$hV`oEU=BNwey+gczm_Iq1%C zU}=^esLx5k7fp&Q?7)#}N2N_k`?w*kJ{>#j6EGIKz??)}GLD&b#dwexx`Lq(20+;T zQxGI-UVY-5RpKS zODP0Z@E~lZr!y(H2bXpvx!*zf`gDer-vizcE44>AhL$!lvEaiA7#6%uTuc@nmtrM< z)PP8{C#urVBp_j6k}9ku0OYKig0;2?@k^L3X#77Qyg-Pj2rtwZ1zOk!SwcI0Li`rL z?f5k`w#>%Tpe8J%>1b%5-7%S4zWE>$VO<;zdN=K1A$Ae=5H$N~9rUkZV$gGE&+aYU zOQPW_mLW?26_&9xosVMq(rD1TfwMCI3oOTmds#Hxy$lx`@Mi|9T!#HwWB-KkaugEk zBeROf#}bY)keOp$A+RO~@JfA<1>Hx`j-TE+`W7G;KGio@V<+dLu7#_Yc1*@r{i%vV zrFClpPZ*A^W58Nzj;b-| z?ZVDiKB{=W7R?%!RE#^Sqynm-Z!$sO91U9e+oD&nSS9*;214Z@Q27Vkhd8WTFhxRE zFTnZZgs>MHJNFgQuCfH~s%T(WSpvJt64>V$dX*Kha}bc>@jN*CKg-xR8~avcSNwn7 z@PBKR|1snXKYeCXBx|DE{%|~VWImi^D#5o!GwN@^S*1e!%RtVH)6T(Z!Y%{X8u)Vq ze`%n~D9}A?{C`GxyI#k`gmH+EcHWsmdl}&!LLF2^IMvvv892+pCPMgYH+Gd_pzkpD zB?hh}ywhYH;5o)lNg@6sLWFazDZ{xK_s6kutCz1lnOteM9#3FvykQIQcc8JJ5aN%7-^R~G%e7s^ z`)sNE29R5dE#MVf-!W`G8D%S~NyM?GCTviYJYRrpX}*w0D%-Z0Qk!7-0{kWUd0L3y z5PnyPzY!w-e;K@r{}~fM^7u95&VeUgT25@X)Yep2!NtV4Eg@d?396U?Rk+WYaG#Hc z8^`Anh8|GmA3Ul2djWAX-@vEJKj2k(FPiXhfU3`DG@nvq`D}rrP`7Z!ibV^tP$9za zjs$*-O#FZ<{FhDmuSCO-lGD=~y!h5xz<|BYz) z!|0z3iemVTh+oT6Tm;86#nj10bXJVKnUH{E4MTv6A^2Aey`8`i@cj*gyqKHE?~ONh z)y5;EXBzxMgI{Xwit(L>@ppP+oSH*~@y5=jP||Qz(Ck%9mXdGH_`3;=V-8J83ZRPM zJrlwE(Fo#l{wPBa_z}Xipn$6ce-HBjv?Bqk1b|oJecyx!h1!>tBGTcfTdoMh^}OV- zYfa~B<)kA0VFI(0Oyq#S1(o$9(=7iO7L!}7B*d%8e_|s4X*BX;`cp9kV{@yeX4*lG z%+I2+f0>~U$NE25`*^|+F&$xGwSm(JK{w0Tn+?a%h3S&RRz_kXRW#DE) zgntp?uQ7yd;H`weL4FwcsDV!qg8pe^-_CMGJ=#GCk@l-dv@zWdN>&m7WF2)fqPk}q zNL{5rj>@T$iyBi)09E)94i!G?sUN<___i2j2a)}icEEwmf50LGRd}!~IRpFy=^#IU zB7}uaJ?&hbPCHjVS)X9|#Xxme1MEt!;La|T?usu6r{W6@rO(%JhKrvNM}hL7bekKt z>8?@$HWb`qY6SX9#VMeQ0UFc}&{}_v#^BTcjri%N`vp`f1yue4KchRoYcj|&QmDfF z)Px5|pW|_)!%wF@mg63;=p~_I;V)6v6)Q6gD}ag>K*b86Vr74n6<%v>!A`xBtc^DI zDr2uT_7jXAVB%Ff54gq9Ut;iA8hDMtf7RG;Hg=Vz=z>(1N*T%0;mlfQl(-X= z%u=-)BH@Zgx+|f8uBG4>lT!^QrvOz9=vjAw7UzyeV({tJ$3l}47C@DAfJ(~%R5=f* zavty#hT}^8@qio;F(t6)5_%FN#~LajgIx(3+?BQhyW$5rmf{D+!^Ka3BQwQ4 zUT{VN#X3G-{3IwArx+FiKLU+~p6d66DKcKa0A8^Q)>TqrkE;)`vk8e*U9?n+Rix8r zwTR*3hXa#ANmhA0bMK52ja% zZ8zE@pvn|LRc?SPQ?g`4&Gmb5Kk+L3Y!g0O3ZI{7V#`co6RwzuBo^7~GmrL9SaZ$z z51?WOP%#6jm_-RFwF7Vu=`kLL%vU*&d{cI6)GUeN#(spcs}cges zSBXMORi>ej@e>u**;j&+CCEf2955b97~~`_Ve?JG0MB4VfJ&bMJloh+djR`e#{RB> zKQ>T}_rP6c44}#wKtE$th7=@Z2<&kg0()GBz#f+&u*YQx?5Z6?2HikDV84$L_J@r9 zd1HUc*#B(oDkBG&j2zfgM*b6&epTo#BhjY#8Tl8M_?Lu0AE2Ph2-sCd04fC(jQJ9R zo>*8Ipa>g=Py>{Gn6Z}{dt8QqUNK&17%z%4K7!%l7p=p-q7;9QG$|C%$a$al?*qq7 z^Ofe0jUDZGRC?j=cRDI<7n-zPWWw8GMn6HPzBmEZ_)Cnx<4kys1}-$v@0ZUr_8SfC zEgqG32p3JE;!m}Eu$LdkANoB-k5)=SpTFMXNu)0^{w5h%ZD6y39R{v6aHD~j8+e<6 zy~T(AR`G>~KtW#?CK!8_fqwp-XzVKt>@8eX`H(-ox%;xIzE=-H=jS_mUBzE-;rTMI z;Kk98FdwfzSrLz)zpBeUM7(|@a>)2DlUf-^IOs~_^07C6e>M5-x6(z%-xvdZc~z}3 z!u1=U-ojILA9UzqRs2eIfW5-ld-L~+;RAY;@~7GY`0I^+=e3|izH;)!f@W4rc##yh z5?(5M^>;htLA|}3Fs9!2hQH0w-<3e$8~#>9f2W}j6S9#}N3gOnRV_}oUM*?m=&dBy zRs#>F%rlhvh63sCCCq+?vcIADnmH2FW$egS_1Ji;dmf@M;)#xZ-cR#H0GIDV#|t~> z;igxGz&)r^7HCAXwB8@!2@kiFNNcMb4xSe{KWU41fqS8Ik$o|IsB{iAycA<<4B=bg zX#gQU)`|$x%#I-h@8b#aSviStc(17}F{wmjukrx}(Mx`m8p@G|;@(~>$>a#6uWr2vOA22~m)<2qEiDguRRV5vIr|8%}oMs{wU6 zzQ_X%qqovdZ~$p!%MH!FJ@4mvBB+F*D(uDH`5bjv74Oh29VIzRQ0Ak^SgaHhRrLLU zh?N|L9M%((rR`I)yp^B~j*%QC0LOqTDme;8X-z;;3Zs&vM8TIF2CWb!CTJo85G5}! z5mA(g@a34&mkcjSFI&CHmva4OZ}J5pQdQlTFKEK5c|VerV29gS^$zK1HtyW^>v=JLIl~5OOz%5OQ}sA*AX=!lTU5cZfvs;Q<-nQ5|jM z?-)bzX0MdT8p`7g#k-i+I!IC-#@d6VGG4kRKGCMgbURTA>t_ljB4J}<M`pzL;e|$*S4-e`3$3wdQ^^mS#>8+hpJYRN5*E zVuD`ko_~YOR3)g*@yw7*?Qrt{N8Pu;$5mAS&)wamNt!;BzGyc|lL7@wNzw=9Y12HK zK%0b3`T!%_O|og$CfTsNX<8ogke6B!5D=A@h`dBB0t!myAu1{;ASx;%3MeWn{6tVd z_UYZfkWE^7^rU#l1SJJBT zPug1))do{k7>@-33+r1THyPw+gM^9aEs)0=B*u0@z$W(=$oCp#kDU%nzFFdtQ7JmO z(@-Q_k6ul8j-kU9`o)Hhq7yt^p`#~2P&wiVlJdb=EB4s=I08vOdJZg|@cTAqorfwS z6PQY+4k5h?$K2>*OaMf1A|D=+f2ooGCv2^e|9(q8-s1XkfBHdON*8^h3?us(J+WF8 zIdDbJXN(+}izIzePWW(zf4$*jxi9ftqdyYk@jGd(2ruC<8UC@SScypn#%lO~LzQ_Az#CcjY*M-qoXYv>+iCZy2&DEgsK zJ6#ky`Y0LABp(kp@xzt)zh(3tO{Y)vgubFDT*0ABk$eD7^n~7`C-fFQp|9u(eML{m zm3r}0r4W@I55NpVrX8(x4>1ce2$KaF^zSAe`lI=Y{%A5Xao$4rFj&Iq?qkXc!bCr~ zqTfR%9U~?kXa|yBWRl?E3hq&y)T12yk#xcpK3w73^&avho!Fa@bRwLj6ZtObeAcA% z8I#UmdCCKNwJ;vUE9J3|(dRG|A6$tK`IC{a4W=C<-$j3vpXdiy@J|{%^s1tK)FZ(` zzTn^r4tk2*Pn&dfn{>R_41&Kk>1eTZc{N$OyjrmxLb<;&3u>H;YM>wKZJ~?wVuGN2 z$Kv}?x=+I*Lig8Zp@Vz~(~tHk<<)Mf82xCMBXm(;l5|mDcF;wAIfd@Gu>DIH`F;*v zJrzu?<6agQdPmceAB#ql@}*C*5XC z?}}V+cHHOdjTX-S5S8=;KcX7&>Ie4tr9Ho4>N(8HQa_Hc^e+Y+YU;;fmaZSz zI+FUa+SHG2mi%&$51o;u8?NXH*VY3?A;%U(PgE-5!xcWJU>Wroo8N*v#Nglx4*iqJ zS%@7V%BwQ%6^&Q$a78X$8!vJ(E=hiW8s~8t4)P?wL6mTiBjMm?gu{4{GaMR)go7*L zFzXO`n+zSU&@sml`mu%%SLm2gh<>QQ63@{F_g;g8E4XTdOBh_AC13fMU_P)H(o=%% z03=zhe4oBBsbrBQ28|MI^Q|;78&RrK&(b^a1*jAKVtL!1!<#+?8S6(nC7r++p1Njr%BF#P_6epEK^C=we(B z__VwdxGT3PHJg6O;juG@TMn0cw9pTi_gj+A<79OEjXMZ;Wdz@~(~t1Hbd>ICr1#=h z1>?Vv{yuzm3|I803%NIwkM!{P*vfwFjL?tpU#CBc6FkQMF#QAgY6&jG|Jt~JA|K`V zDqWOAi64CA*GjnbZ!!J><0eQ)_><`(p0ka6zHu+1i}B$qxD`i1Kl&l>t8|g>?~)Gx z@9F=brT#`2@zBmwg8sjnF8co-y6E=@=%U{rrHg+519bPd)Vapp1$X6EY;)7U)>79S z_XWC;JG}rd^xTUs{ELjc)VQmRyT-WdjJwgey>yX3L&l$=i*e;Ny7<3~F4A=gUC6(J zE-KqKblcJ2(>>i%yXl@|sV~z##Zq_CJr(tp?pc<4fbQ9r`aaz=EcF;&j4Myj#klfo zx~Ey{Il35EUZRV5*?{R5((Ob#>Bi9S)5SQm2=2;3EUoBAKCGk*JrAdg@u!6@(ibxH zsX@9Jk4}SI(PpVn(U10c1>BVwEU%^??esSCF%H~87u&qwC%psuOoAWVgmdAR z90`5sqW@k(7yWlF+!ZMM&GfVXrMm?6jV}80Gw7lpKikkhOc(w6C3MlBf6CCWpo@O} zYU96_F8cQyjsIr4=;!aJ`$6P0+{y39@e8;Eq!mHW<1Mv6+>!$^zo5Gm`9$|1OC3tL z7X6Q*uQT*cL*Gmn;RfKcJ&w>_hxS1i{n5E}(eLb{yWCPgql<0pEyW0jeBVJA{pJ;P z(OG?oZXfCi+{xSUiFtx< zN~x#ljw$sF-R(FeM0XtdKo^so7wGQ9Nd>wm;@At_lduy_S2|a?r=XdZ!2JL=3+SGT z9bLMo;d?H+r(+)vt}lfAqI(SbJ-U$5O&4k3Om_>)n{E&Cfo?DQL%Qg+2kG{sz0r-J z-P3)arBZZ{Lw?ciLjKa7tkh|A@&8P^X!GaNtw(;-t+UkmbQ{s{(>>8rm(opH>N2`G zUU3!OooKIgqv${AZbiGYeE{5%eCLYFD8o83 zN$mQ_G?~6bAvt)7#L6>o6B|1Xy~b; zi$73Brrp(aF_WmFd#qk*s`u)djKYWy6=XWTFWoJAMyDcrhQpuW*`M@2z16|#`V#u1 zdS#;q^a@{X)n`+AQ%o)SF>O)c^q`)8v?OGiQfNub>7pee3uL;#k}g^jvQVy<;fzC} zB_Tv%8ko_KQhbf9TKLb{BLfO9{#4^=lWoHw5-?{{v2=G}{XF;CpjN-g=2+AGp4 z@17~yal1@9?C{r4wbY;NMA}5StQv0eMbq`_&;^m#8}PT-MSK`Iwg{mKoP`gzm)^?CWF z_f_ER^s;U9$(G&{tO}3Q?2NzO^!5XnPkQIix8#UhywXyb&`)@t1jS9hvr2F4dx2iukAPd_1;PJk z{CC%bISVZ*C)R_#4L(~vs05C6&HH%suLn3g{}vl?w)`6c?gw6B@O$&`uxd+>^&YIZ z-baATCeK?BYHM7#8oFe9({Va*b~4bBbBydDC$baCSOmPJ=Mn(s2!N_hgCJn~r{1QLs>(@Oaa4D^`vF$b$2x zWA_31^)Jr@XQ$&}6MMFFybfIV1nnl)t4^#MZ_9%7rXzq=8j?KW@up)5R)4J%P)d2r zBL$qDj>8Q;TRP4J4%5vEkGDKt1n$cdP!q?6>#!=tLVUvGO~->+waT2*gY>4O43j=P z9SsJXEgdz$z2uQauQwfYu=;u|3(i{}e*g{(;t7v89hCs(QyyD^v(wRHq-IOUIB=Iw zh;?E*%3;;GI}6U6jvoS-PdX+UtFu?6w>(0?+3B$7$Jx@+3*0xn!r=Fo$KJ5Ed@T#k zn~tY|3*=EA&%lbqu-?a;j)jM&)6r?b+0s!5T#AF1=kccFbBE_QetZwOe8!JY!J3p$ zz4|q9c6o3P&L>+s{tviwG&|$3Hyt}+4a;XfR151Frp6N(yL=GX831-Rlo%H#E8@=He(tgm)DV78JcTROUdgPCf=<4wmy zz~wXFJquRZl04G!v2M&eC&W5&e)S}9b~<2IlP6m`{si1NZR3ect~VXGVpnN;mT|`0 zKEClj?8;=pdE5C}Tk>lk_W@_8G&yd8#O!QuQweB z4d%E0cosOAh$cMV@^~Yb-}=1c{poaEV5DYCM;N$#%HuWQelkJ3iR-9GF$kWLM>;yN zd$&G|Ja7BB3^=0jAUS-kG+dlj!VD~Bu&RZVWCGu+@&j4qq zebYf)9JV|M>;Bidnk*3-gNu{xP0tBSE7;U)9;RaAis2c z9yq&wd@)Blz6RVuS@O@Dj^Ce}UplThEx&XOpPt|NaRYF6I_!MR);{h8t|yQ3m~}>e z^TdaMgK28QZ!_#l3rcmitTJaOZh>2%y_@Y&Ka0NhnsXq*>^wl5lin$djxv>tKS1> zr^B{SWh;+Yfy=gD^`_&YUHR?1-2Kt){Z#;Yg=OccqxQ$Lug}YYo8f?P!~r%KH#-S7gx-+jq)M-uDuH>eC%3-@UlQ^2Bujm(3o=Ia0AJlJ|ZBc7FI- zBzFB`Il@evq2C9A>vV;)Ut!kDkoQC2s=%bY9~ltkIrYHM{sP>=2D~X6+53Ah z?n&T!veYYYyoX+f&$_b2>&4x4xuvel1J`wB_WcE~emkzpuY7j_7xHEb{9bua0{5jX zIB)r$|Cx0EVy_>wmG4!+dxa9rH#7}u%{nXVsRhEZ-yMV)S8mTW&yX1N6-!;J1 zW|8Nue~$q--N<{^CC{thGr)Z!i#%`so%GqXyx+RydG-4UaQWol&t39<=aT2u?+?J) z_FA`^Uj2^3Vutz8s{aRh~&e zc6t2SCC{s0$>(&x;g-dBuYUUjXQ%f?mpre2^}yNX<(BEyZ{D?O{r=*T=hbgN;Hun$ z%)3{=HsEai{_2wF)$bVKwq=pGkEcBDa>;weCC{th{lMkZK3;Ok`@2h?SHIVR>&ipF z4s?=sdS7$N^Xj(=xT-Ajy!kiolJ^gnJn#5>stbpyj6B)K^K*bZe$vE*YOL{%c_GX!QnSO72&jW7ID-3>bdS3>v3UtczyX1MtquIA+w-@!~4KoPB-n#clcu)(b{|w?HrMl{@mApZ@MnOW~;2gvVQMuYWcB zy2o2?8@`sVr}n&v<>uAzeZU3ti1)<1@*B_31J1T@xfA0}$D%#i?GfH|gub3m#|&3} z@z%fNfLra3(7b!=-=|#i?EKD_FINM1c$R$e=HHWd<13>qIB$Ae@6EnW_R6~kxVd@A zyX!uD<&*{I&A;;d)ANma2A{3{E&(o|_Ir^_9@EH&^1SK23Al3MX8!Tw?g4JUEa~v# ze)mm$6~s=%^LTMTd;njEcrf&CKpgI9HhjlY7qSEOJpCTrZy&N0FYWd|oKw5!5$}6A zy{_Q+&xiSPhDY96z#Smm%s&@;I7b&OjW;8*kAg z>3H|e5$|;17DEd4v)9Gh(z^<{eA3(Jig*7U@osa)TV_&`E#8kCoRd)V;!W?@T=5>5 zBi?TSw*@ko-g!n=ws>nE#aB#*?{>WLb^yollvkT0-c7)9o=sdO{`1Ke?{#^``$Jc} z%W}l~bKqV;T#R=SBgK;~-tYZTe{HJ~>Ax33w!3}&2{=2wD|5vAGH{&NF%$D9>;P!V%Xx_c??sCPuI!C-00XGO4jCZw>l`Y=a zf!o*c-Htcjn#a@W<=D(8TRtBEoE@)y{xw^?9|taSJH}kJ8N4&=YXSef?9P!==T$SOw9dEqfb;Y|bN4$>$XScU*SG);?akpRJ z&(iwYcK2-M5dv!yL%%Npw*`FWbDs<69e-#39P%iIhVxJ#oyxwwK@e2#ec1#SnF zqkbpni1*50=2uU@4V<0cNRD`a0GwSOr@P|ymdBgGT?#(+JKKfx)|XE_m0v%8FL1Vg z(H#1H2RK{5kGSIX>i2ixI*oqkyKr9p+J2Sa_;xaIwtlf3`ke_J<72*DoFm@yU*{L^ zTHx$>hjPT*0o+{h8SkfZ#JlzB{L143;OuzgIpX~!aCUiI>5A7|9^V1Z&X;RkIB$6z z`%HfG>@$J0^-Jc^Zx?X1cQRjY$PsVg+5GyA{eZLM-JT=fI^gVlx!D!3H(w$y+^soq z7XY^fe5T_L7tWi1F9MfO|2O+L>2#cwBOMjMU2o#ulOx`50hiBs^k-MRAIK5!E5O+UUJ2IW{!AY2d>q` zYoF_)zTW!M^t<$YvN(sln}AzoJp-IwPoIz!WS_^D{~os#7fjbkp%?9_g z9JnWdt1`GrIrMuSxWxunm;<-y4{2OM4%|B6I*q)^IdGQ&XX{sz19um2c0NzdfqTgn z@9Z47rsvc0X63-044hqVY$trO)t8%G@@#W@HrzA7&4mp1FAq`}o@}_+UAXUSSXNy5 zAJg(w4*iz6aQx<%PquiQT)2ldOmN=sNPB>@?MDVC4)?I!?Q+B=@6#^*yzwXH_qpUd6xi}PySsGoLyf&n?v3`dCGg# zCGT@NBu4PWQ7{_AaTvtCS(AGhR?cNTE@)W1&vXP3vVIplp2xNqg5 z-`HQ$^V8dN$SZg$KmFzaXY2Qs9P$pzQ(l8h-kmw*9ShtWd8BvK%jt3OYdPdy2V6eo zaVKzgdiUgz_eh@do_5K*JBPg2@{~8_m9&2M=8(5HaJJngSA9MgxGDhHpME2Uyz7C> zC;z_c((ju&SXXoD|IplplPkDP>^03t)Pqy*x zN5E~*BfUqznyxR{8hA^2Cjs~QJml^3T3X&ua>&~VTt4|X2%MdNKg}WUv^?cq=#uyI z9P)0=Q{G)Jc`)6*W&S;ur@Y^|5oE(Enu*uJHBrd8s*q z(m1cY*}&QIDqJ|Pyb71RpbO`fx6~!C#)b3BTM67|@R^Qk7tSm1JeNFsKQ>!>F9B|~ zk!SBC5$BcnW0yRxr}$)(_atz3{X0m*1m~6avP<3y7tWjBH-NL(Z_8acue_!INSDW< zE}U21O5mo0&-^>Yh4ad5amjm+3+I*B4xF9dBV9PJyaAWICKt{tZv?o-;8VXw7tSm1 z5|_L-7tSm13YWZA7tSm1TP}I+E}U21_g(Vr@8Gi4mzQ1g>~nV6>dPCz?Ld4?uRUI8 zleZm{HPVS={pXWS-UooI0*|;&_|GSsyenMt-lt(%(}eT zdGoIYxGL}|FXF;^)(J2=aqN1OWu$R=aqLJaD(7ezilp@SKiAm zc_+AVUU_c-x7f%Vb>Y17_Cbs+C&p`!!}NRQEd`>=;KtzcA+Xyp_P&YG+l~(~=75LQe zY8TEc??{)t>s>gnycXaV8+q5ca9(*ME_q*Y;k@#;180xFH@R?LdDpt+eaVIM%DdSm z?-m!%EAL5{ysx-$UU|O(&aQu7cHzA8UUtd5%Z2mGd&4E~Yc8Bu-c(%q&U(ahyVr&D z%9{;bmBHQP!g=Mbbjkac3+I)0q)Xm6UAXUBne*M_fxD_u>sRW+Jq8^9InNuovfe)T z;t=ujYmdCv$v(9jiDh~h^A8@T|DgJ1%d1>Z-STDi%j)V5Syo$Bg@1VT-<8WXY(@Rb zm8*olrR3D+buIX@Sdqm)94XR1+)jO`unLx|D!eR4e+ur>zsE)f;%aFsx&zQeyg!nPNRaUMfoOOz z8tIQFR5;wUu5C@YvpLk=8ftE6nm`E0Mq(zWWGd3PZ7dnt>O?S*hz!G%hz^<94S{;O z)!CYekBw>^S#CjTs7u%cVnb;C(nu5k5`RnBhpD4w8;SXFdIC{nIp44B)zoVkr{6H7$qX9E#Kl z_aAqa38)v}w5J&w^WvNKu?zbBsI(t-1@_~j?8{t!R%&j~uX1Qb^7~`9*N>klW;bI` zwTS<^&4la6UD*%8wTBw=f0V&rsGfm8;3!?Bo`?S>2U8^P_q+VBJMWagPsZoIejK)~ zaAc9c#JRSa!Tk8itJN9&Wpd5qT!&VqHsRgvKMwu{E`A^0t6cse=biG4C45@NVl|e* zFY)k8)Hxaa$?8)X{!;a&41bIKmaQ${uO=zm%%szIn0g`uKT}~R)Oq|ktc(?;^Z0R* zHGibTe*Cx_eNKjdFSU1uf1Wxx!#`g&XZVj$p$vbe+LGa~P>BqGmHe1TI{qWoM>F`V z)#VxfMe6ez{u;F>!@sxsUWR{-{HRA-ev^7GgTJ48Im5qL75Qy6%X5Df7RnJ4cMvC*W=Ij_^aXP609EoXF{Jt;HT+esqweN&nf5<<9|Q=9GW?8BK{Qk zSq0gr(0>8^d^l~Hf%fxBYfZgj2TUb_Vgx4dwS7V_YFpqJ?KiKkz}-IYjh-< zhz#{?uU*!&ec94wksgeIqwx`pfqWZ|kM#5>V%wvMWY73eWCY=05ZiQNeF_4V# zvJ6f=yh2`{Nk(`fugD-Tv(YyDU;|s32?IMlf`|so5z{z zwO_#0gU}(~2>~$PDM4M`1d>b&>gsK+^=Nu9#Otw<0~(U1Js6c&AR6Pr?4&Luc^n|? z956%HrP1-em5xvX>ygNGHVDPZVFs;t1R>UyNM$rks1#VMux(%#ArAXO-40kcpwpt3h%_>$Pj0DY`oH#!~NUi zz3&hAM`d>EC1)ijq9=?+lPSb;f=dpg@Q~O$gp;ZGsKDZ*Nz+mE^(_szHmnObHMfK} zHiX(5)~st@+C8{@`2BUE&bH&0xnOOfV@AT=J4d78rS1LE<(WtK`s9l1;%MPhJFkY2^$32>e zzaN#%?xMq(X7@(w!rLRY;pmRY@aRyqr+E`neqgA1U2{WMb59djH9Z}@@9&`!^uUdd zK%J2zhGMDIP!#^Y(Xk^M;|X-R2d-Zx{ITJ1EXj_(>OcVuqNfKT8QVG%?FV82A=)v5 z;I)4!)*J4Nj|{}NR#i71+S8ecZ^gQ%ss%-ptXjNd)yn;PSa4l>py+8H?v3{ML&~Oj zVq0$%LsiwmOKW=~iQ%5M_QsyRo~CsW*eC+S?ePTa?$W{Pssr2J&TLw`v?n&whdg?_ zV?jjtmmYtU@jkmS?Xj3+_O4(67-Mzi;e^bE(6eP)LF4&;fM;YLPd zJtNW7I69qoFmqhW{P!D~EAr8yND71G@H<-`B7Of2Xo7JUIR>UQtz-9sYqgLG?kS6Na*=}C7|aXj@<7Zk{KYAjf)f1Ir&<0J)>8HF z2|*^n-o{!DB4--|4VVWcOYy@Z?OmPcFE6b#{X9% z^H}2lB{A+HOdHen0`5O5%&fuwLo)R;>OU&%-)qk9JT%h)Zn6E_8s?Z%{C~*KV@H=` zG*0(m{^8vgRn|pkpZ?i`DZ*=t4n)R=Qpu%*$|vOin$o7x*r5tk1O~q^|5gXa+ zFuR4>J%}?v{b@p3pel z4}8J*h(DL)jnM?M-=W=XMaEKbZi|e`)Cr_^{rNcj%Gi zc!c?(Q5RVKiS4q#PEp@wjPEBUh2s%SiY9%(5EBESh8@aTK0^tQ4({w5U~Mg=4B9wGk0oerYU5OC-ujjD`Aqfwm1sH<~y`CzF&b%Mhn zPS}G-aT+?@i}Nod4sNEG;OTszy?Gd>3Y{E;ahJZqg@f97X?V813;`GP*vFuNC6#<8>r5aVYeQFw6?R=+L!%SZF(yo!+3xVZYC_u0P|N#Ap$>w!6HN?6 z`dH_+)>oR$#91r!h-~ya4<0az>E23XA2zb>?o=cH>O+l?qg$a!Dr#y0BPf1|rFB7? zEgfI66zc;e2-Lc%ZFsAM46j+k0Kz&flhd-v5!IT>Sua_l1MTZ!bvl9e&g6tz9M19J zbh0MvgJ+Hc=UAbR_2E$WI;~n~>#tICL3s5rq!$s*hd2!*rxc zG^?D>U-#iU^6#P#lQ<$cHc3H(J;ovG9#m6n9O~%sm>CIl#HXWEGg8P(MWpZ4ygSje z8J_7GwX~)do0Z`=u!!Pu(Q6hN1mxqvp7CuISi8!O<(DdlhFh>Y@mteT<&vNMr%z! zbUYCm9gVUpnfl@L{&qvbZoe!)?J33-i4TX(V6w-cW((Eij|7t2ddCLX9c#kM@fZ)Y zCz%79^5xu;^RILC9p&K32@967}gxxErGsx0_Mr#WgyMGh9Un^YDg_(BdkR;KS%28 zHn0r?CiT_i-32^=3U8T&y&mIk0*PigO&Q!C!R%VHPZRbYMC*?96H$I8CGU}WqgJZ? zDpF1&M6ZGz)C|)4BN9wNcSeI4smlf_RLy_d5BjgX7o@AkG z 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%0As?dK^Ec9#8>BNTvf7%7 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 b**?2lLNzi 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¼kMLK;$To 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%0DjNRX+{EO>}QDisB&eV_FY3j*~MGFcfEhN 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�NemAZk z&qT9~)`Nq&t;ZQhsYem$&WmP-aI_wKK$ofCqV*`opamBDT~WEJ$i^~6KdSzI#46C) zdaN?^ndE8{=ha0zK zVXmV-l7TK-kLN&lJbuKBaAN0Y$S*2a!}I-mtTXhP`eQQaHf6{!T92DRcOU~@v>vmd z9B`fbT~WEJD?Gk>^nlLxM~}%hQ$2Qr?%aN*_ScU?pd&BQD_W18MfwnyC|$H3J;ldY zj{~5y^{{f2sUCNO?s)v{p(R)rjxvXEv>p$GZuoKN`M;Oy*EgbBM&)W+nO_eq2g#MG z9*aPSrG|c2RIakhGuwk`J-!FJImc0tH!6;=Kjsed>(OVp%v6tN(AoY9Rwi1HFM;lO z^n4|pI+(0}*Z;@fm&Zp{o&DcCcL)Ii*+g*-1cq1v$-?SVLJ}lOLP&y$jSiEUNis6Y z3^Nl#Twby6JE*N%H(aaM1+7|X)z+m}ebuT}tJPMm+S*rZwOXrI>+k!V<=%77jPtu) zEd7J~fysTI^F7abp0jWF95Y`XHqoCQ7XT;o)nE}jt^v-E9iw*jXUC1e`Dq95-i>eX zGYvNL6n9!$oV4RKp<^LC&IOLb zFdx&7=~J=XWP(7Oc03E5ul78ID=!PkGt=WG;G`Wimq$+_JKhEkD<*l5X~$_5{A4~8 zXxfq4+h(^1m^d>%_SnZ?IbQ{wwBtM>w2&PM;M&bVL7R5m3tXXeGwnEhy3I}qHbt6t zET6$Y^D}W~dfWh|+t7*p%XCXfqNH^1tzgD8XEKuG|JFczqS3Z6V zoU}vM`-SZIGjQjdky0Q%PM+;=9Q8fm{FIN#9DjCP4xF^(N}*>VJ8l8)93OVIUGX9ddr9PqahJ92e=yQC@4wBvr@WP03L#Ez$d zTUj7n(~gP;eulCcu4zZfVw>GMW8zFZ?mWm}IsY?o(vBY%v4hTHe##6Kv}wmP!1H0z=g{BIN*-)VaGYk zZELF;C}=Z14r=vh$LGNLv137-XUCI8>^K;>P=S7gnI7pC*goaMjzx$1v*Tmn8VkrX z)1wxZkjz)VC}PKQ;ILGZ_n3Ax9%fszoT*?lJxW)hKVBexFM}tQy$X#?lPXBOn|53W zoU{W=^Xw^v_gT~TNeYF%gX~+9QY9Tv1fWuT~-ecO)*UL}P zH-V-d4So1Ls(?Jxj_;)W+3`AX(vFXc*zo~yh33snJM6W5`~LsWjt7C;;KPo(tiN); z0XS&~mY~^FC_Od;x4=g`_*lQca{eB0e#-fE>-gtRX1ti~$M1oYcE|>(kR5*oPOdAc zGR^ep7~r29?HD5D7P8|g;IM@#?=kH->}YIP&O_zf zF&dpUR2_MbSw04j^_Q>i0#4e2r8D*vvg2XkwiF20v}4`>_{&$90_P`RwI1)!j&p&N zcI;Thjw^tpJZV0r9gQfwx0xW&dHD)BKXz<6!QXu1JHSahWPM&JJw5}@&wOIbH*M<@ zA9lPA+_(biYv!xJpXhJir{N^ej)_ImV+C;7ijw!3>Ctwwzy9vnQ~14jra;q56vJ@zbO#}?pzX9fz|wBy^S`CC6;my;=Bq86Yp{ zRUJ(`E&%RI6(HVCJ7#|ipSSq1W7W6$XZWT_Gd->VPNv7)B6i#gT%mEEX-DKjf9=QE zi;(Yq*s<(lf9Y`^aMF(bi`a1$aDLKb%y;~y$L+vP^^FY#Aj?RlwZM{N;1W&(Gs znYf_M^!PP!e%77Oz+$7H_I%;x{?g+_;ADDW>X|`6R*JfWdWR7J{|(D zsX%&AUlr3CX(?jNm4)l8oq$ulhk+X}Jt==pT-K+&`+eeeyvkxp*Y3!zJ?v^fh#kGfu=I3%DV_S@=rr4Ie$j9Y42v>DDKV2#QhpLKk@gzPu!5J zZR^`+prFloxdb>FFNYS1m+t{LtAIQ+US3kp`J z3|)Cegk&eQJ;p;s(4FDXTyn;t?yHE=JR!a$qxw<8Ao$NI42%-g*3n^D5u7w@X~tASfs zAY9Xq*A;m=MV_hO-+=Rz?{~Z%<4chj)!@za7`)cRhF3eoZ@` z1CHt|k~gTxGwm317wk|YN=P;JoA?9lvlQ~=dR-y?mIK!!WT<~k{jLPAOvpQ~h<^72 zC+8bgd8U5%-0kJ(;}v;k{5=jF#f$lv`hBLzJ3*0W#@~pom@oE`zL6h!`Se6Zo~hp; za0^Z4K%4q)Rpgzl$TRhO8n|BwcU7zzf7jgW#UG{;*i$Hd?*PtE{Jo*b!&HJR&(!Zj z;H3T>#eYrx27m13KMeg{d1n0G23%SdB;F5zAGLoq=_g+KI9ritwlm9tqj+FREYi79 zJF^Nn1fBPo_U`gP;c=sB@BR;Z`fXD5Gwn?R=f~cc6?qt1vxn?5?H%!uXRlqv-toZA zBLT)^+Pm~|+q%C%yqNZme9|i)7b^Oh_D%(Ei4i7|X8QIj@-Wn752bJ*l{pq*rbx%KZwOo0oepdqL zN53&|c>3L;$TQ=w7Pvz5)MkHo>YKLpDOXGTHRbK~JKK`q1*kwX-(LkBl`l#UbWzwt zv}x~+z?E@l?e_rqQQN`C6nQ^XoGR};;Px?O5qX>;?_S_!zWSLEK(ra|Pl2QOBNcbUe|idqJNq5< zbGWniYlgcDI2rC^MZ%2$*8@ouZkdo+DBMSZn=IVbKW4aZE8#v-B-{^xTMa=J?o=VL zP`GV>wApc&0^>=8W=aYcz0iVMC8bcM1=XZRDyVJW~xNL1hIZ!6(GU&P+am2hi{g!`_~aCiQbXYY$e!rcqF^$3sRd47>_ zF9dFya997B_THz2`*M+RA6CLWut>NEzUMF8IB+swp=oDNq4ezqZWdh0-o+fo{F(Xh zG6g61Ul)@11aPuGpnO9Q;d-HiP=oOfcpv>C_NM=s@s|Kj+JPpPJ%z$u3*2hBk{v4; zs&KeZ16Lv3)jwvqpD5v?31d&8aP2>P@prfoSO`}M+-kU!eqjY?#>*z){OGq8I0}d4 zp$cYCp>Xd9PL|&ohZRhZF(3E~cRp}3Tok=84Yw9J8E&FTxSM>2yHyDnN%N)Q-VfXc zxKh0IF;wAvQ2QZ2rddGTKW0AY0#4eCSN0T&=RP=I1Xl_-%V7oMx#TZ}_am9%P6tkg z``04j)&RF!giG}nJ><{KR~PsU_lHWjpB4%C0VUkySg3`=o&1r%{zemU(%#RDgu4Q` z)d-K``I|+;y%RV;_4CV0xJ)Qy7E0eYfO{2g6z-|`PfwwEeh7&r`vn}uf6es$18~w_ zhlJuO6z==L&4L?2&LR+=LbypE^Q(vnw~3(&>o@EZf9-S?a8kdaMf6($++hfhAaebW z{F(XhJm4yXUg{q+UVf;AOUFOcQ^?*2fZKq4LJ+y1tWda%{^qZ|^#DiyN#4jJ;iiF; z>9HADdJ5^cS;1Y)VFhtd0k;wE6r|iARLG9ipZUwrrvfMK7+b`SbAj6|!j=0K3Wd8b z3S$Lu=3}PYO5kL;I~55x4BV>_Mvyy%&_dyU{qO$5?FLSUyGxO9Q@}kX!j<0*6%6+a zeoco^&HZEAn*dIRyIYZP*8(T=%YBNyM6NVIumyZl21o;d8(^Dwi_>e%s?@dj6&je1|TU{jF^MRB3Mb6t53iptbfIBYd+VNjA+ zxO0kxdmL~wzkDGClD{L2dgEr`(%_S*Al&FFq~9KB#O3&%qxi3>-%{YDe)7AGLgBUp zcRfTAtA!oAC^$1ezXzP0 zcLCge%=p`Nm}keLB6ds$ZZo71WRD`@J^<+=e3Iz75<(BHYPE!aWh4 zFF*5yw*V*e%aS7D-UXZtcUqBfn|BEK>OW_Jli?mxB-~?wllf)360VtFz6abE5igYr z&ddkj7~wBoP61Bpx2%YM=Kv?m@0=px{v9|!_4ssjUS+tgMZ&EC?lfWVenrB)2RMp9 z^D*<^J4(26{{!h^#`A~3NqZL-2{$&%Uww5xaMIqxir9MzaAXHT8kBHN{r(rY^#UjN zrIUO!AJpv_VAm4DNqfwA830b|*P-aQ2|5ThnE&H}TkdG07-8!76gu@4a3N761iX-b z9|Py7pSagJPrucQerCASfRo|wSR~wK!0jd6)jwvu^eN#+i-fxlIGJv1i=^9i3hoC* zaL)r*2E{1Ip+Z55KhxfifZIjjHWraLYP^T*E`kdwIBczEPoZ!RRB)p>jO8Cwze5$= zw-tG>z>grA?>^wh3OVYZ_duxQj}3~vUL`$Dc^CPVccUUNRYcwcKIJ{D$Xi=P-a9_! z{aullEh29M2D5(RuL3xkU;2y4Tj*0>t0Hfph`ghG$~#t(H&{g81wQ3nr^q{|h`jrJ z%6m$YcU%#9Zvl6xnvuo3nV%1w;ME()7m?QooF91)De_JzBJV@s{N$ICoxS{WVi9?J z0_R8GEJfbQMdU5 zh`$-TdhvH<5qV9(`H^>rBJb)V@}33GPkQ_gIGG;T7LoUNpYldyQdi3RZV`DE!1>W{ zfgZoI9Cyr+TFI>17<**|!C&w#s5{QV;GmSgf<&I_q(n(`850e2ne&LZ+Y0nShS z?J&uUzaJElR}P#Xd9xLHTZ_nR0uD<vtV|jqh|B|R56_Gc83Xf+~peb)FaI*j+ zQTG;+_Xpto*!wr&Wd8ec5qaaL`ja;mI4SS`BJvLKDQ}q~@4+JSx_!zUROJ1%h`dd} z_4r8NPeR^&*w2c{yJ4EY`1=uXGX5SdBJUSI<-MuM``;q+J_kDep?)Wd3VbaHhPiioA9OXUe-@k=Lf+OnEOV z@>VH0Q{HR9mBF3T_b>%#%Cl#B>2aijGvy5fPUhz$6r3qyXro8!zJURX@RNfjDdA&vCEm!0v6`ZMGM3I+KaHf6<;ADPYtKdv|7c24x6r3sV zN=3hQ3eJ>wnnM=!I|11HPJ1_fuz`-38nz8$5fQ2p{g zaA~+vemFC-sx_4TbdkM3MKc zBJyl(1{w`_viAZ7XX-ZwI0}bw-%)U;eiMPaTi`BMaHhPc6?s=EI8)w>z?BJkmn%3^ z-Y1H@YZRO*&#v^+<7x$G%9{wBwD$%DXUdxd+&s8byj-u~OnIvmdABGyQ(i=oce8>s zWNz_uxN0h2)*C$a{dp3d-B0$h%*`nfg7g$orXsGwpp5I9c8vR&b`g#%j;r z#}%9@Z#i%>{{C0Nner}H5 zXUb~?PTKo{f-~g}D)K&2aHhNyfLjQ6iocH)oGI^qMc&^PoGI^7;I;^PpDQ?1-r!s> z{v4YFDIb{fP5^EfAupidOnDb8@=6t)Dep?)Wce7X;7oZhD)L4uI8)whz-aD^qZ$yvq4r{Dl;pDQ`Y-QopGR&Xl)Sk++Y6Gvy5eC)49=3eJ>w zgCcL1f-~ja2Apg^_Em7EyiXK)vlW~v&tBk_j~WGM%9{url`ksi3ly9wZxV3Q-uVj7 zl($@w_jLtl%3G!Acc6kZ<*ilZ)hjqt-XL&g!j3uxXUe-+ktfeFE>yo#? zJ<->dVxaK4u6Vd79*M;>RybV0v~f|mrJ=QbNozxGeI6s6=t~IM*<2*Lrav3$hRE91 zrkRmkE|ci&&&9Jch~7w)hr&aTbarOq>#UiXcv9%YT$y?5k7|9;djAW32gLhd$dNAj zagd&8tDZq`RA>kJ;VO+DZv9ZBM_G?)bcrRu*dexKjkK)#1s$E)bVp|<(iiQCXFFg( zJd%xfbjSPRnMkr@-K^@4b=5PgBOQqRbgB>0Pj9`czK&QXu`Zs;b_^sVeef4a=HhEQ zvY9B{Gw}{D-$W79NU(5rFq@0_W~WR+y7ea!v&8fgXr47CmX{{;^EfP(Drd=b;1OS0 zBWcP8b0i@WsS@*(jkT?1wasmfWp(@3?Xyo=9gImN`>w36 zD(gyR%8=y$k%%dqQD0WKta)+cLE)CAwe5?SwKj$4p=i~X&6p8OM7qP-{&YGM&t}V7 zBC}@CnvUP<`DHUwW&Tqwok<-PkLG0hgd@H2&PZi=U1U}`zCO~MPR2VLR>IyHtqn^X zYTFt*>Zz=EEbBa~gOnfwf8%{nr|*DdB9}|XK}Xa52h^o9x%m1SP1Vf3zc-x7B5iVI zGZ>%;X$wX+(cKr10nr5?&B#7@jU^ME;b^L_E74sxxo$y6OD5HwiS(8&MuKF^D%Q`N zvv&t2R~yeS9Syym@mLH}R;Ds*I^)QvWpiiF>WE}|I~tqoI-(u*OChk12@E%PEG)&2*v0x}yg@$8D|wIykv)tVJ%8 z>5k{JEIu;1B(L4JS;WZNY?~{P97v=)=qJ3ca%RoU$_{D`-Cm7s&bM9K$%x9+lS=uv z>sE;B*q3%p_5Z$vRwFQxURU#9n8sRO+-5O|sfKM)dFA)TqP^L~w&>u0nI^ILx&%7+ z|M`T;SEy?MBia8VO}2dtQ*cUXn^%|xwW69Rm8Fr-e?BXTiu_CU>WOZ{UhbD`wJke! zMYUSCqP?xPZW|=zm(>ZF&ccLrr#>}G^Q2*N>5Zg2GO2Ail#)G&Y&MCmS2B{p%Zy5z z1>N?&oBmt^(~JpBMgQl^rJ9_gjG|5rXLB)@nqEb=tmkMjardUzy;}!CX9*!HUwq z6bS`&r#6W#p8VUDC&|>Rd}V@@>pT0iUyXR)N`EDqbJ6aA^PdVsod=hazGNo)tCC-E zGWnkk%$N`Ur^KigDWCM)WN!8Gu1J3}mz~*T1sMC!Lm3~-g0cQ+&SfW5F52Ww zqOaR^Y-f(`Jy?d0d6Iq~VA+mVp9y)=wQpWE18Gm*>jhKYjo%L9%OhHOFIuE@GZXk17Fo>o0=o;5=axj}X zI_?EF1q+{QV9kT;B1uo#pAC_%C-qFY63&md{Qia^o9rc#_~qu zMa!+kdU?y|Hx79zz*hGYQE-MZ&?`1y+70uCUY8%q4n)XCj@o3$GV9pNEs1)Jq#s3` z++aFR>T%YSC7AsMs@LV591xsvx@R!jMXAPhyUumU#69k%qX~}KXBt0F!E6l@E6p%0 z+yfZhGf1KsA}SbRA&H^(O2p%Ftn61-y1JY=g0)oPRGMkb{LW2dw@0`$-ks=kabp<| z(cyANqst!F1TxRZ6Pp$UvhiqtCXw@0T1DJQngp;&t|i*-J-F=I#E$lQ0bMbSIBe;i zKrEC;9OA{}D0VDf8S`?RF}u(Yb)|im^VVUX2Ju)lT*!Dm9%C-~qX_m=Z9HR92ItD2 z#rz~vQR@G)yvb!9Lbzny)Amya>`nGE9awmmln{tmGzCv0Kic%k4MR4R5<8uxjL!qP zKfpKxnP@J9HG84%2aLNm#gt<)H;G`GcyyhMd4UCyVL60_c`H*xO#|lhUKVfbQT$x5 zKQ6)U9$4SSjAfqBaA;x4RqN-&iKL7UWFITIT;E^Y%mF1G6;nSpX1GF6vDxUZGlFWx zD-SG{ejDUYSxh!~#qz@;WD`&1s8#YBx_okU(~yAC4$IDd}I&c-t|p$75X<+6b5B%+P;7Yt+~>2#bb(&1;E^YINs zwm7W!Bn#>=79KVOU`ho!MhfNZGlSVRo&8;8K4;7sNYGBBEX5$_+%;@5b`!L=FKYv9 zhfSpXr$ds7EF~N#&v$y${b8X7dF1SiZQ1>CSvNY=JR!P0)RAlZA%bA?KYD6y4i_J~0`)j2X3N{pFDBFxUkMw)WMqfz`+Q1yS@is7D2!P2v zyR2UVN3nu|kITC)NF9BY`)aU8T|+)UA0b$a`}-)lM_o(Y&m^!Bxsmkc>@7oRgAm9a zncwS?sWUK|{Bcgtx(M1G7JZyCm1@XHj1Z%*Aj$W#+LtRedNXm3AqeklxTrD2i$$0> z2HMKJiO{*EHH3+adWehKomkzm-Utc0oP;0GIBC|1a&Pb0#7b0ovhQT|e4>iteKa%d z2^Lx+vzDr6E;>cNXp4ZW+>1PyzT838#r9aK?RyIA`0k&f~k+;4uF5lODWr z$d7Hei>LJ7#^3ze->5_9x~0;raT@>82T$BMq>Ftn#&_-pZT~w2QQrUAeVzaCrb>D6 zDc_Y^TZ6xmc%(C7*AF$y{j|Xo^J09Ao}x` zy(3-%fuLO)7#t;uB{`kRE5jSZBB4%*%)!$vNnZ9F3pdcVO6&JK}wh z5yCy_fDAf&hMkcJ(-~bV`C#s~+8J3IbVfy(8yJ&%gHHKbq}sqzr)qf6nZ3jr+6;2R zfk9`#uj6HXg|pWLXV0C3&XGHj+`(Bv=jeLpoj`-L%Vc64dw{dc!l3huA>~2ml_67t z&i~QdNA&h7yw@XU!b{)NakKXR0w;$5mPwDMZdV3CULFf3HLFYt!-=K4{UF-Zd z2>$0m;-6yg?d&{1=a$?L&Gy(ZSoP4&J^+Zx_?swRnT6 zQ}J`EyVD=1HYS#pv&GxjQbEQ4q+2v?w z1f{}`r%+OS6F+0t2A#3#pffJ&j7%b!@u{G*^C0x-f*v_%$CD^Ac3tO;I+b2_TMv!8 zp-~oz7YjNwJD~kAQtn9S#z2WPVW=|-*#XEi?IZ9zyT=)M9R4myAo>0$=zP838F?}Z zsvmGho<=W?M>!*p$KSS0&}lyr3LUn_8M%R6j)*%Wjz-Qp7Vn*%&ZuMXcWs|D@&w!2 z@f*(0hdU!yIU|-iOOSz9LWLvGww)!zfQhsPomi`Lf8a1@=?KuT29}dTqgLT(xz@R@-{{{urUi@tFU;KP9L(qHw11km(QnJ)Cu&Uf)y0dpqYlLde~ zfJfhi$cMhM+6;pH4i`-NerTNF36^ygaPs>&$)mq?x1qZ0{!9puPEZm2eTsp57aw^Y zk`J>g4cvSjGBO7f_2xroPN?Cg_OYy+Oc3Zs1Gn3B+y|iG;;_q$gqwA z(+_Tgz)2aVJjtgo4iNWgNfU0VB$U6K@h){B_8Q;{or`^|fx8FafKYmx4}IgSX0OMA zLy|E2kqy}+zfa*^PviTPO8I4q@qNm?S(US9)2Ax`|L;@w#3zEWW$$#55z7x7_*_Z5m@&;%Gq@XurjKs@z3m+nleKg~a_ z$rI-J7Xk}^6oYjvHE>39S@^u9n?9cLG?N_e{w3ef2&L2goqaG&^Y8Jv1owj+F9_XM z4)b_!^v{G%nEIhlhGe|B9G=gVUp0%<2Ruo|%>w;R&@+%P=|dj+z>SV9I2YxIiZRjG z;_vyOS#=7UKB2z>c&dKL{bBrl0yKS6NVNRK8Eh28QtLH(5szbya<6jXtWt|U!6n)J zDYfXT4Jsy*mY-Y_?cf4z!jIyP_${^Q6Gg8$mRd7F%g3=Mva6P)|)}B@y z@XfN0f_n$(c7=|EK2V|iK#!%mojs-20O*S(3IE4~mXBkVTjzkjNiyIrk5DI;gNr_{ zh5LPq`^}pFeXV;yAL;T+t)JpOsnAd2-IG~r{Sx###r;*hpQ6xz!2ACwG;VFQMk}d@#ztTh_lVjZo~E3DH& zU#jSLp5}hIb)`m!tt}cIvwi~F3$xUERC7;QFKTq3^#_g4TAzU4RY~uF`%d<)XL%vy z@=C4U+;?(6&e{j`JeOB$)wu8E{!ObM^uaE#)M|3y$^BI82++s6yi%*neJA&`taYHz zae2sp?mM|}VrBU}mse_CweHRvQ0<+eaxVr06kA}e;)My z3jGS`g$n%^=z4{IAM_H1{uH!)9CW1K1EIPP2lhw>KhAw8d#<(0K|dwiTj)C-^adAG zYRz`v34f#20QxhRS86rm{a1?rm7qUV+*jlMErm|t{Wl7o!TT14KE{2g@NTuv)aW~` zOEh|`^*zuRDdFFtx!-3!q|pyr&w?I1M8;UD^=r-j5$lhjPg3N41o{oP!Y#GF0DX@_ zmqJ#Ff*%9=&x-r5pdV4_a?sB!^mNd(6#uh9k5cFZK_9Bn2Z7EhbPMR1Laze7r$Tpv zzFi4#4d|N`ItThy{O3=pb)5T7@&AN%wnjf=U8d2`S~qL-i`Kmw{Tu5EjegzwwMM^X z{ZXU;Xnn5Hf3`-tx{^MBwf4~H&#W05ZQBQEbcx-h(L2~jX!K~iSEF~bkJIQ~?Xxv{ zFZ(jkAIWhF%FA_{`xN^w(7#dQ|Hqp9-uBNyPj?&aQtJiHeWv{u=%6D1eb9e)`^lx& zr=YJ_{5x(~6hGDWIM5#}_}xK2uHdI?_<8nh&}S&&9jLh@BKt^6Az1sdQ=nEA5+nRga{+mW0 zW$%Evsi!e1dw6}V(&OE@qxc=h>T`*v0nAj*eYiDGqj#`cH2P^fqS0wPrO`vJV>Nn= zb%sWdwl3A^vDS?mz1F^4qj$C**60b=3mUzP^*fEu*dJ>2?%EW9gTeNAH*ZmV>|ssT z=ze>aMi1Dv8a>HcrqN|qM58BLDUBYqkJIRob*4s7wJy`>Y1a2N`e=KrM$fQ*rqR=_ z7d3jO^?Qvz#{P>&A7_`id5i3?vc_w4r4`cXYO6-0kGB_T^a*yGM$fe(8a>C#YVb*@GqXkDq%_13K#U1!~|(THF}BlnnpKUf70kC%XZ5O+23aEsL`#~B#l1I z+E=4jTD2N|s=Z93Pq$ZV^buB{Mt4{nH2O&E9F6X@F4gFWb&E!KTlZ>om-UoJC#_dB zdX4pGjXu+M@Ag6^;8xb|As@#&#hRqi z=h^#e^cmKH8hyI8T%*slR%`V6cArL{YaOT2=U5vxdZTrPMqgmxtkc##8hyU?bB+F% z^&6eG-_huct-opXMb;>9aE<)%9cvGb{d@#5?V~jMYHLuV zud>e4=xeM?H2Na@295rX{X>nu-g;D{ziYj$(KlFcYxJe|CmMaZJ)D+l#N${uTN5?< zCTqGz-(u~j(VOi?jlRl0T%&Kd5*mG*b*x5z-#SyHe_(w_qwlhA*64ezdo=n-*3UKi ze(N_H{S)hbjec02JVAQ@)EeharhvZ2o}|&&*;N|-zgDeAKW4RS^y5}kqp!EuYV?iv zH#PbxYm-L*+`2}ipSHfQ(Z94F(CFu_=QR3d>kW;5$@;5Czh;$;k*Q7f->cR{jsBfg zq0w(z`)Tys*1;P6f7THi{hpQ7=s#Jq?FO)Y_uapI8rS^cU7M8vS?c zO^puPA8NE?4^^iA(f_k2XmqK)w?>b$=V|mv`w)#DXCJQ7W9?pz-o-vvqj$E?)##h- z%QX5H`xcGf!@gIecekI?=soRMGpKX_E^jv#ijlRb|K%?)on>Bhr`v{F*V5c>DfBSfiUTB}A(OSm4{G$m_A?sYXuqk^Ke0d1=m+fJPO81j>~R|1Y=<eW=d1q~zn4_ij26|Q+^w$k~g+Y%KbBxk|Sj;a=ny%obN75G>G|l;|?xz~GTmz8) z2Mzb9#h6{Xe=g?iB>j@%{ujgj4+i~;L7!~+e-m>os=oIc^iTu;f#LqBLI2XAD-HiU z8uUto?lpdU2oCk^^npl^oV zWcyf@&ksQV81JoucCaS?SHx$tpeKTU1?em4uYn$e@*?T|hz52~fo}s%$weifR3N?}HAbyiFE# z3AR%-f~IUh;qMIkG6Zmyp!WuS1nQr=1ie4#i}6m)6ycYGJ_O-a2|5m%=Ih9wF>ud; zz76tc3imTWzlJ&G#|3>Q==~6%ErPxi^tV8F3i>h77eoG~f_@3~36M8S(C>mC2YF8l zI`OHoE0JH>MnATxf*s??w^w}GF^IGeu5nJ3-{@O4Cd9y?H!ZIoqb95R`W8$1TIX3_%vJb9EO+joFt z9;f86BLdqI!jZndRE`~aG-XOIl1SnUTAT$+e!|t7rz)Hk$=L20kxW+AY{AA~zBPcc zt0lS;K{Ica^X1E2@lsVOUfhEwxmPI>I7MPjl|bvZ)JSP`mL(5JimjA_Dy3IC-;!fs z8+(CLN*J>NH1=rBhJ2HyYcK}^D>CNj5iwVHovXXf(_QDe8tYLqUx&@tg;dUhGqZ-$ zNKpkFbadJ~?~~^PH-@TUxa!eO>?%DIRJzGsrDuamH^B*^imH}2Rp~ZW*0@^h30+y^ z`p_e~a<=P3Pqxb0(g(D_>8Ct0s%1W?mdRf&Q@vW|15!>^meP{NWVN9zr7Cln<4QHO zri5k)H>1vx;nqk$uF2K1FjN~R8u_Er$RL$&4l#0FrIAH&{-0DwPr)iTv&@z{xY6pC zB`OEZmf5c6(34x|`jP?6ma#e;amIZ~Fcluokr7@aF>WH)=ozWX$g@>Oo~@$7tRz`g zrJfE|Mpmw>GTbRQsQ#-BcS=6hogzi0DQ+~{aHmvP-RBtYa}D>ohWk9jeIDFd9M09V zWYv5CnZq0-h388jbM&aKp5^+GiAc(E8G1pep5;2ow49>{U+q?^bB$1p1gSO>q?!_h z8-x<0#8kCgPAQ5w0@+VPz^h8TMQyGg1=U8PRl6D5C|}i-Lsf}xnWbc*_R%|7IsXi0 zWKHulY__ei(p8)9$E)$$1nlLjp5vBz+AZfQKwIi4?!4B9aolJpFtw&!T_y<0C@A=) zs#^E!#XR}-YHafBwM!IA733OzUA>rJFC_Bo#x~!5Snb6s38QLO7v_~Y5=OPJXM~sk zncqA`yzWX;LfpDox(ZluX7V=AVj}opcNf>w$z(&FBf6XT}KC z(_Ye0`=kq-?ZyEU=4pnYsXXgwuTCM=DE}DlZeo!;C6Dgz)j7;P&%d4qWQ^|LtCPt8 zygdI@M|qJk-;Ej(v-929A@4-#;;AO%-YGKXyHUf?d4W;&=0$DgtUPg*RL`l1JQ0;t zy(tR2i6s^GoS|?f4n8+DuJl?VQn`{kmP&|JOm!10y;_k<-+VVOk&Tu4MYmEfyOmnu zt)Vtt2`<0%R(h=-)33@4FBPqsIJq^E3q|3^imhbFjg*X8O&|k1nt3;qpBFs9@w*MU{G8RGD8HRVuX+$;hv7D!mGa zsWm@eEv1ryUEa7#*yWW@5>S<22UU56gzTzPaxKYoqgl#xBaX|eqCKq!qebkduL8frkeMigF+m*Sj+ zG*91ZYP(F~Zt&!xT4|mrELVGJzZ)TxDXVF`sLQMN3LBT_N}xK5G9Xt+$n#noCXcFA zQ=YD$*S%-*+{l*lyx>T`{GwUy6-}m}S8Fl_@=Izp&7CPxm|s$>^^#hhUs9{}E&w%~ zS}ahRQi8}Y$klqXR(t6}vEVhDA{O-GR-Iq$tG!+Uiv^k_Q549p1*(-=fI@VmS%&DE z#G*03lBo772^ON#g{OqduQRF@BM_oD<-!8UuR*H48iWNvb49`=^6pYXVQ<{d<>hB& zufE~(T$9OzONdXl=6D?|LZfY8QoFQa@rt&FWqt8QIBl;LlgO>hnoRaenolNv%aT>$ zws>yQqSkmfzdy&~6U=OKnGWWmBNsUf$7IBD1Sic)>l{AmOke2t#(SgbK{hAdiwmYA zgW*(`Q@Fh-p5w1{ND6x+Nhk>XgqlbUHH>`aQ{A|ML=nM45peb{@a&y?I2!lYAZJ=E z2Eqr$a}9K`299*15C3qifN-ElsifEubX!j(mKtb^q*w6yckZ2Y2`gYLDbj>Lk{hmz zB%}Q}|00gtbz*fm?Z#r^QNVTmnGDVmYQy1Fg4xpDf}@h+IWCa-mnU zYY>9N1#^%qUO;pen5EuwMK2DCVF|pboga9@7BMmuzm)76cYvarvFY@!M7IuAcpi{z z6p2Q0G7j^}8MLh8hT*Y1e_TNq!b$GTS4*PNnPHTwaDBt#@S(M>jkSxGHq31Asp&nc zvbCl0@M^KB!eOoTxDl!$+`g)%Vdkns!z&MOnzLqYlrBs|`w|8whpVYbT4PtXfxK(5s5RVv zU6*!kZh@|!DD7Vwi|Soi+pp_lmk<9Fv8d(6ZB|saU)QBvORS#wzx}$d0v%?4*MK~O zblde53tmDZFHqTLWvQT6+pp{5b))~&T5)3We_76Am(W0~?bmfhdegY0x8Wb`L2SRS zOCL_DGZCV*Me8@X6yra9UDtj1=p$g=joI)aO=jPXptdR?|Q|Mu@# zq+s{LjeqXu9FeaI>Q40;yRM6`6#UywkoDzPCRkL&U$uO4BpOmD$^-C71R zZSl2r{Gu>+xz%aguY3FNy6#QfDT9$WZgSxlw>^amaJJt9xBV8lVz*J%g=uTUzOpbkSG@WPMY(HWo?e66^RKgWTmZ8&@jE=$@+5Ul8)n9T^?2?aS3C z=(;=(+sI+zx~^1v20Qrp)tUV2xFy_2ed9p{e~P5M=?LB+j@?g07cFXGJNF{%gg+M4 z*ELN0dWLZoS;bu&V$n!@f2K24CvLpt0bC^XS+df-_=qd|N8!+%N@U|r?nPVNv69Pd ziR5}zi1pY30p?lPBeThRa@uA*K*;BSJBLVI{{tC1lGRH97Jx z0ptkkfYi2PpQgwKbeG+*Cn&WxdCu$>&0*|Dz(@?npl)@Pty#i7T42M-KBN0>vAMqS|}&;UAe9Tr`p)I8*Xi1%2mVu-TEfiaf3`5 z;p{pzxQW6u4fE`}uI|ANZ8&*?E}R?6QvPlS!2?uPM>?>m8U8ZM-lg1Ob}bz0*nOnK ze?<;=P=m_E8a~#$eAWmf2=3w20NwuSI=a$WF!SW7RNU^#1AolBKaG`J&Ry7UuYqI% zzv!rURPK;>MMXX1_*(j9IyT5!!^x(8d$lg)HT3G&A1i^$=~mPDVh!nw{1 z@dYy1*Q`|MQP{8UiMhqSBMz7``n{N(%)1wpv$cCMIZL}2le64=F*&K+i^&=8y_lR- z?#1K`{a(zmh3>^1mwzuNXX*E1a-McC=JeZPXsb8I`NEY`ra*lR&CKpxk>P+sWUY)sf;j1%uE_`(+XXjs?$@%$LXL5et)tQ`~ zcXcLb=Utu2*#)o8z7=exvRK3^F*BYg;Txoe1%tM=6z7`kALjI4{U^* zfqvXz-`y9Fm1TR-sNiZohpu(-*yxB_!J#F6IP5L3E|rMk8ndClwgbWwa~~Lx9R6kmyW_}j^_qa znKkq&55AwWg4S+ZzD$9}=EbeGZSAcq>e^SdHW;cjE^2Gjl);D#$Ggef(ly0u`*W!k zeQWws1AUr$x))MM)wH&yDHV&8hF`~-d*Qkzwav{9OT#N0>)V$wWi(D0U!+>Q>nwcF zX&Wqj$s90PmH0-|F?jQ4AvLU^!ELHN%o<|tb~&j-pF}OgI9+~V#Wa%4w)WaEVh_o? zyJWRgwS>WLZ-7dYx3Y0@V;Cvlva}W-H8RJ5wHuvC%D=d3ZeJX3X$vo2(Ol<>U(vQG z+}5zPp{_mLwxX@2p&6Ns350Ut#(JF3xLX;HBn)SA;Y?RJO=Ee?Fw!A?l$F53M}-#s z%_igVG?XD$R~KRhFTIom)^1@E(24-+YMZ@m$f%auwzkGY8z6#_wRP=8*_>)>JPb?Y zyHT|f=AR_eCusC(84H|V1fP^`gAI+#njuPYXl+2e)-7vpZ)k20FKuXsXy#gbXl>(C z`jAZsShT9Wp)I_qw)qffUIfv;ti5(Al2-|$skVhl4yw$S)@AJ!r;6Mms=J~R54FVF zgX*9?-?Xhgs6yEjmqYDAl@DInJ5@h;*^BBdYY(bWB(>+M0c#JcS0qDVsJ_uy5Wd$E z=A9}u*NLi+z32ymggezkuKUl)oq`o|sa_<<{lK#JpvsUa7iH~1wIZ=B)KzD4f=8r2jiP{XN2Gn73t?4w0S`O*_ zCdD$vm^FzSJjtHOBg~pa)jearU~|o030RY;70CQDA+1T&$WXk?SkSGVL~SWc zbP-8BjV4jM!`NyF)G$&jOmRDjnkPmehfbmvPY}$J+GR=OI=V^JL^E25<4Ta$)NX4o zEL3u#23d1q`Gs7lX=MZjO|7dS)Yzu(K^|MA#C_6DF>dl>U~WV=p&SRvK9Bb|5Q})9 z@+#invde92@;i9j6Xw%9b(AQw>7DM?0E~P zzSDaZ-hXije?JiKq$K!Lmf-y(8+YYRY2|N2tSKFMyTieKZj*cP#=-+P!`+8kQ;x+O zbi>;zc)K%zx5?+>?KJdx=>2lM|IhyX{YJbq3EK-{HVVu&@BIzGYB?EyQQ>Zch~D}dyhwhuaa z;oADzmiC5L>#YS9l$|gomx&ETGI8pj5R7x(dDbw^m4Y9rAQf%u$b@^TlYpzqLoe8N zY#nA)K$KSOJ{Ru2$=-N6lUN7Nh>CA66dary%a~62Tf-_IrF~q{-k9LLFs5QWlEjX$ zlbCnLRP2xQknKcDKt3B&u{*k)HqDd?*7aj6o=1kXX$DTPHczN%tN{y6uLMo5cz7S; z#H0QASbR{h-z%^95KTDzb|=t#lI#tY6~DsK$#!}m6U}9k0(@3g#Ss*rQHh?AEIx46GA3eHV)E2!Mso%M($e662L zrPJ{k94?(N9NPLb2qlYC8G2LenA2wIREMOhPh{!y{gum>v^7z<^D8KO*{$nI6a_h- zE7F+FU>Y}v^93kW$?R2_TZGpJW?5}4mcdu??CXA~{0`yPGr$fm4AabdGM;1V+$r3I z360rk^eir`=Dsq&$r~^pG z-n4)x5WwMRGL>af_>f?BE?(J+vkeA;`>ABLu5U$u4`!!vt_2((UJ&{yXs0{-W3fTR zCYGpBoSpJV7KGjj+A@85`*AKNxQ`1d@SREYMlvMeDZvwdd4YDypO#QsO_9uJnACJn zRW#f+fH)0H!OwCBoX-*MahWf;%+3Lq`SOC$`_MO=-hx!;_)5~T_3Q!P`KFH z9jJ$_X*9ylt_gScqmPYRkFMk)N#PzkMxr!yM!@c)tHDVQ-mz{(wN=oXMwu%dUfUn< zk6R-`e=D>3E7ge~kqS69WDSrAfyUC5d1yODe>7+95_%(OGX@s)crEHs79keNu6T*n zosl*1{xr@@=_SQWITNYbo9IKFNd5?3emf~NWR^hrXwIMmxI3}77|!(f^`T(EWxO?w z<{!EeT`APbnK)aYA(JtZu>vhaAAsZ%K#mW6EJ=pmCG@$Z z8GQH9sSYaONGzOLA0`9frz~`q!*vMfT;>#O+A8QSHLVD3C`ZG>9eww()|>he@|WAK5lGhR6ED=?Ly|&q{|t=*gYzY=1f(?u}-V z3Y0S(@{CM^sEEgeM2R04dc?M&;G)G8j^ysd#8ZXa7lDu@78&Rm4iWf7YuXejx}=@F zWW#XorJ8xyif0Z+Ht9!*mNk{`)?r#pn5ubgQ%n5{ra8b&<)*qeUYLZs@I|MyQviT4 zEf?V|9@_AN>>~M0&sq#;B?GEa_Y1x$I9Ig^WRIvs!kA>~>q7yhd~)A-;Gt}$HOxVWvE`39qICG(01Za-{U3ko@EVsHZ1v|e~# z+0?{*yNZk@At z9VF!4P zq#i)HGm*o=HE`NNc#WXkqErL(r`V#D1jd*^ZPzSR<@y4-^f{D&wBafKUQ(5)iE+rkKtg1@)R% zCi-xo6J*Qa+W4|Jg;wzy)!vJjxSY|}w8^*`M6-2eLyNV0;ZP5W4>qFaSWmIU5#YG0 zm4;r}$p)hdX_Y~qed!^bei4#ip`E9%;iwbR^k^leJ_j@FeB@<^7Q)`nC{a zbM`W1yx#s^+~B|}BRB-OL#rq>VNA%C<8rYIx*LRrxTvm~hd7=i<9$(di>c3+OGQ(> z+@punok^knin8TchzL@zCml&-2GCvg*rmiyWNB`-l44*46)K+HWEmb!#Stu4PEqnSMWUk9%5-^WoGlFmJ-mX(s4}aHHnjzfVI)dX zRa3EvTIAMjmde(=ij%1it<2Ll_N~CR8G-WsE6OnBvw1$~N7sc82n&=ithkmuG-~s& zZ0a;nzPjQZ7BU(XEF8pRw5NnB%UH!K5`~=)@J7vDj+@ok;AV{Vq2Erh8yg6HtTm0s zNdwpz9!A%ZhF#W8Ln~-tYO`(;l|{s}aFe4n7We37v6^uNb(4~qAP#4vRDZM11bK?E z@;jbhk3!!awIl_(OU22gpbRZgzI#QA7>lBPOk<}U7rjrTu^M#@S#3AI;(1btXs@rt z#v@?mdeFGLeU%Zc7c2X(=r35)X!QZ3J}zo>#f>Bi{lGrx8(#~Btk9_e8WvJd4rEZ0 z?EE&wDk?eVAPO)cW2rvmM6|4|%Alb>8fu`zv3NF0{UBQQpzZ?L8R|rpg}&|B%n{*L z97mZV#=2)gYUL9#%D|=c3bir`7EdF%(b8a1!>vuSMrC(v8m-!(UrqxI3Xv8|&~EUo z51K}@dbNf-#!X*ZR4bSp6RVcs=}ImjN42QV(o19wkJZk`Y;E6Q zbEG~$fK5KqIj7iFB0Q_7~qxrwu$M~|1HZix}a z>LfdK-#E%4qE5NiJUrwR~G0v(K0+uBX&}4@g2bz zOiVbiUZ8NjWJm?gy4zH80KQa`>(c|VPB1Pj>>DnZ>n>~2iv;%y!R@2{!Jsz}2|bT~ z)%v#P*4QG@*9-~0N;K;@VRCB-*|#{7%*Jbzy>)20kvxEH84~)={P>a< zY@w+fOjL<65KXiuvQaV4p_#vT#1Kcipsfw#z*2Jh;|{!2~jQr;6&ZzAH&1tSUHfzyO4%foXb|rcmBPLZg)c#g}_ouMlHn z&ZD^%zGYWfI4mUxF-T_X7@7Vw#aIjRv9W`Vo>hl-9z7V15i*R)4%8x4 zFC7;8rGwQ2^g!7#U2*9Mz06(2)QIA=I`o=!>Py0>;uIqdSkm`~2O^kCjiqQFucrVT zkr+<0nVe+kcaDvb@sa^-vR>lqmL@jN={4B7(A%7irHOdgp^=Q|V?8;MpnRueL+?pn z*kPggVjUv5ac`y@>bt)WqALI9l8lnI-0w{ zxztYiLztfuqKpHYu^wQD?hcT{DE3)3hJhYmnL}OQ zjibwfc*73;JZML&dfZ}zMMJw{6&q)Qi>WOx4F@IDipM*)HErL$F_2`dSiBnCC@0q< z*abz4QsFM#U`cmqQfae7FORozHaZqlVpPZ>oSINskg@DSRZ=PE*7av|DXc|cKFpni z;|pfXGAvEO9uayDeN}c^x6ne=>?3)T-q3)pE={eA4~iuEmhs6wr~GW8qw07lYlF|h z#9A9HqHZ#)sl^l$(cYw@HSI)T!prK~8?bAry|uCVAgpbmbGR&;W9-t zgyDg7B;1`$bt3IqJ&o>qFZr$WR;Z~naZvjq*t^z)aaJN4p>yS_^&&nd%<54awDC(x zsR^3G_|yW`!jU)?jKk|A0TeFNnai3uK2)LgHnd+Achckrg+;R)4Pr(fQ$wNaC%EiI ztf#*}O;uYsxrB?hgasz8bP zKTy@6#HbWh4JeTfL176Upb7|o-Z=R#b)2bspLZ|u0!w$-% zMOg6DQNR__*zDJ8HwfvEhZD===g=%*DUQ+uZG}`!`pS5&Z`c(#9fCZ?6o(?Y^=TU9 zULVle8P47kplKyw**s|^N$tnoj7KFUjfF$xFqhV*UXjzJLsi&tc}TioT|=LlMQ)UC zRRtU$4^TBWfR-{rdHE@pLpd3y$OcQ~ht5PF@?tUx6M3{eD;?0r<<=ww_c_VMfdE*x z?3U=~C7*LxJ)iBt+9-nfWeMK}ksUybKy%R4LcicKF(ekumjx3uZGy>$1Fwq7Voa4~ zQTz9>-79ES6t)ly6LS4D!}C^2MJ=27gqH#A&qSM25_)6;TM|s7#G_-tV&t@;JUg*@ zBxDI1yJD#Y>k}x%Vy&i=%*SpCw&|u9Cm>K)6$M*!9*Cf$n~Br(Fx<`_#>E!$D#dRc zR`D3AXx5?V5e@SOw^YT#Dym)C6a})LnD}N>d#bY-blx-~^vrlB)va7GcpDMgNErc{ zFp|xtd)N)q$UI^piLykxhi;X2%1<9@O`|WDttXC{!G}=cC9TGR3$0b%o)`_`V zHXGe3Hs!J}Y!Sd{9zbUpxhE5e(RwoN$1NYj`RL4HCogA>^;qdxpIEFL$Ae)Qq+}$^ zMls_#7d;xNh%!T48faRb%~Wxj3V(O011&uxD}*E2G{sSw$)`pCScToXa#0LU10sYxowG=UuycmafvGev zKa-2>Bp}AibeRU9yPd^NC4Ma4H^_4@Ew->kFwyfkyK`wgLB*tnv=aM&mZr0f7=p2` z0k~qT2v0=nse`wl@V$6IT%&@GL~UXTW`C|nJ>3kmw54q+N(HSWw&kz}3$F`>*IYCe z$5c%hOT2ns@ud4ueEGdBVak70OR&MFumR4U=n}DatWvgBCE=){iee z`)IMH$#wO3&4Sm)Q=N8s;cghVGXy@CRs4vGjvF#Yy;OHnEkiw_7V0XPj%<`e)1B1d z@tBs13-2N;tY*G365{>fSVa|0rDG2|BuYDbIFmZV-EqW?;*{jVQAMKGXBV~5)H*L; zOIEV64Hi_X@4VLI4rK6|W(+&WuuCRTo)Hdf&`<6fV5Jn?tl+ZZ)H&Egi?X@UN#kv7 z#AlZdEkX&v=BhyXdTZLaIea;V^*%7@(WgUUhFkr)JrGv=vGpS9%&I@Cj>xxUVmb~% zlqwT|aB=7U#M~~iQ2F(Q#b%&gLN{Xbh+x9|?i9zU4={fVCSM7lbYbgcZ2y0liqLA# z7~ngCh?6wd$JX;{Ehb7V_#G=)Xx8}FD5v}+n@@AOFs>J^PY4GImJV2`K(~XMMpU+_ z!ZD;KI!h`u1_sn%#Mh@2*ighc7)*aQo*79ChctxdVr$JFXqx;!0&#iU6 z7t-iF*LKBq40Z-#7c%Xe<;~~I!;Dpg^`w2*fLj&G!EGD5CT#a4yUmDlE-Q<J@!=TEdVRIM(g0X*kG7sl!d zeZR`4hSyuYPV+{9iFd0=Op%xJDpurpRuA@>36kz94c*#mV-U}3E|eBA!$7e|C8;lkeP&cN6h5}W<_4RV9F68B zg&9e7bO3m`H1zV`*gYZSJW^WmTk0DwP4um)XB91tXMuZ9s2Jm?jG!%$4GHQE(S1Tq zjxUhdSOa}bmVYAAUdh@dSaoO(u>GrrW=5Ry^M+L{rG|r<$T|wL8_K$dnH1Kl(NW-? z=8Ht{)^rj>ic47@B}X3fuD{KSKh;V2yJnb|^?2mGU=Zx8MG-XfwbmBe-{mY%l@F#Mz2AmZx|*KWQz z%p>b=my2!{i^VL~1X$*AU1VfPm!BvR<;oJV^pN7FNfhpnDDIkE;r_U&iaOEB7Apd* z*~3s!m?^jb!1iYalQIVFPz|q3L{J0JzGW0Zin!;7g>FWRi*_+Rh&+PzKeiYNmluXv z)97mq)Gf3dWga!7NLbeQvWTEshu0Ep+mW2DlRGpBP8E*mxQ+-tHqI7x116x}faJw1 z!)+*LtZntJ6wM58{v-Vf^4rGWJM^Gy)*&<+E>A1$_$?+UD-+Ysd*nYwUViJMY zKSUEsEb0)qr@FgY|6YtlF#g3d9Yc!kYg7yHm310^F!R|L98*H~Ou)P-woJ0^(iObS z@lL??qFtxFp;WIo3O05cMmiI0V;aq#i>ig$hi?I~$tQ*Z4N8)5p~BhD>&aUMOBO|0 zWVQ%CZ4ec^D4p`#1hb*F#be&CFdG{X%J&5Wdt6= zFuVE33R|kM7U28s(6tq|+_6i0Fwi0YX2?c7Q=EeS6&6IM5zUH&Q~seHI&UW%MG+-J z^3KA}-zS~gY~kgbh5VdJ?sj`mkQp7Br%6dC|-p5GH(9PG|@z}MI&Aeg|5^E5mghE z7{#+aG2MN>J9<2IudkSqAm{)(-bsw9WnCsFC8z^UqNx{6_0-tV13S5k7MOPfYkcUg zo$Ss?HW9^6Mb>O!QD`TdRwD%CQ9E?adMwOi>r_AXPg5mK$%I9mHwM|aTe9ZD+;f0; zlmt+Aa4s$3Y*sBv=Jh4KYGC@Ygdi6PzC+CQFn6!I}>-hu0;gu{(O$pyS z(%!ZhYa-Z!jqXwjO~PaBO$^=OU)LS2mY@FpGAi1@RD$d@tR(B!o(#D1Wv3v zRNa0p+_IRciBTnuFn&`K`WQn@#-W)ODy6R|JU#{cg@_A|qY9nR*=`ndX&#?iOsD*+ zp)|?Ixp`B4R|`*QK~kE%Z^)TBBmy=rvo1e`y*adE9$TM7VMEo)>wUiZ|`8;AZxxv&BSMQXt59r z8+(6B6Ygv`GktZ1-alrZa28SALW!r3{5%dDKV|W$4pzLdLx*o7qJqTMz>pU`PEcTI zBNKg{hw6klz<3$ZH&o%6VO_Z+{TxW^e^@u8UL> zuw>^N(Vr13I5-;&t_~j)iSCVvJu&c+mBw@#5bL?albFggE0>uZ$I7P&YhTXd^P-~l zqxCYTxonjov66Gv;eaz#_ZjSt#dKM`kL{bT5^mVqDF$-YyoNyQ$5!dH^fiH)7>vYv zXz~FmFhG-BbGYAZsta?RH0v{01kDas5YvBR^3ZFz-MK?$`cljrDznC7){st7LB^(L zG}+5Xp3FKvQcxWZ;A(eb?!eRgK&}vdjFnEI&Y^yxm{jZ)UHm|Kt!D;(UW*O}U%CjC zFEU)@qDG*+P71~lES~FOpDfgK-av1!?4tzA8#u2Yjcm;0E>^kJTx2Qs4pO<1D0V!t zjmk?5HYo^nqt3>5V#Yq$VBLjOPZz5~vx;`)E?z3<+A`?j-%g5jsOCUTjUG>oeHt}t!Ti;utK!%h z8k@8=rb%N_xmIA)3O>^uB*V_Fh1@VTQzP4sTg^tK1xw`h`VG6^EaM+*hGe-Ah-b%$ z^@1&+=4ce}P(((x@;hchvT49u%u0ZRH6c3R3zr<K^gE>!_nNg$x;Z{IP9Vd&HF~z^v;cbb@|pA7nq|)>#vrBg zh#n=!r;<6GerI&Vi-d9dZS3f>@K~-JJTryEf)-AIa59E#iu9+JE@ou_;w+3K9tSbI z;5b=Lr==`JKaU*HX9=!A^HRWd2F>kD7I3WqmO60zC5X89P80D0a1MjCkXpu|v`zK^ zvpI_6^vJ%g2J?`no&&D-wcXf$H8v=JD>OQ=d}v4x8YwjMes5Q)AvM6X99Z2nsU8jC z%=$^#kB_Yz1=Q!3*Ksu@LpiP4TC}9KjqMM-tfm)P?lEFa%u_CR;3asvKEV^`qqV0| zBV(STT&0lU>DdHN&%#st($V9v!ax?=>$wFr;5V}^S^nFiBgCfnfY!Cb{JG@I6;wqS zam>`WnRhi<)~D)XYg%!YgXU#%9`oZ53H}ACiWjX-Fpc!GGwK?)k-hwRnDN0)Rj2;A-!ks@=DB1@%?^l zzh2SGPLF*0j$6Br(=(-e(uqvxd9@3j@=Cv-qEANJoVL#?mYqG6hYSU8fLEKLhV;JJ zvhyna^M_(*7i|sq{(7LNLZzXoL$YLjC%l7{Qu01a?t=w>~uC#0W=IhKKGa8h? zwA0HFP~+_BSo`z}d>xxxP^rf`_1Zlk*G|jnS&2p;J7!>+o|9i`wt>Uem;-VdE`ntS zo%9}jjz+hTd{JI$C7XPqdFcn%xTQHom5u)K6d#6quBseW$=NvR!ELk0eC%X&t8DQH zv|kOKSd92_rC4lDn0hYM`zkA^B%vg6$}SGQsB5an=~<5y)JA&@PjJRwMI38{eP&M; z*7oaBS&2nQoZMfj-YUA^I%4n$lV){ebnVQND3Zhl#-BykF6i02(u|H@-FNN$9=#Jn zl^v8n98x>d%1yv`*dY1aA+?jN(#~nreU-HbTRjp8xbyJ7p|z*lPF`i=j=`&j)jk!n zvNSfftM8)WwSNdXIR@+RtGlkM_EXa86=>#=r^co!v(?Luy)Sl3D=QNgmp$7w_B;!m zHm9+=WzvDuYryN$OaG#I>{bgA=`fS?y|%qBwj5zszq*KrA8oM4{KK(xD--(x>dZ~n z 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

      rfLa;zBl_8~p~ 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 K>>=Z29^#%OM|A4pfX=iMD$kYN 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

      i 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�*e)fEu2xs!^&IrK%1!+^gCkGU}t+`oxg9 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 ctR1T=f!L_Bxc}&|KazzgMmB@iTA_w!1$oX95 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

      O5ZMv~x!6)eCEM4}CI^05 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

      {kwPU!2kG9|J^q} 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%0ACi6i#A8|!-n1zG?c+hP8OeOxe?gJv( z6Wvc|Wc^O?Cor-nxu3kC@&>|B7UBfLqa3aX!e0#91@_zkS_QecQ#s=CPFW5U@sO$z z4->tgzsezo<9PfO_~B9aQgGITLcB=j(DQeq*q>%@r0<`^WB-2<#r_|I4v)E)-e|vX zDIa)R!6GZmore>SRtC#@60aQ)eHc$Y5Zwm9hyqWSMalj(#3O#!5f6Q*5f3>RNP0PF z7s$IBwCWE++(Yype3b)KfPM~|wH5baZO~^iz7a+Hb|DHq-9brDU*h4%V4{#W40Lz| z6KmpAO7f% z3%kS*bVqm^_9)_L+LKQdJ+uW;^vIS((F0o%MNgw1LQm$jA-X^AO%px9ol%MLIFTfN zqT7S$%P<{@M@tnDMZ|X^I*m13k~a=I1=t_ z$}|$y11dk6sDzjQT!fy0elDSli}12+YA zeXt+G9I}1#&PPTLH-aBL*1%nfy1qhpOv0y29LEW^M@~gA)NrV3PP6e#v7>w*iXFG9 zLgcH>1p&B^%8@BkIvmHQfeX^hF=XK4;PrYY`;;z%s_hjLr(7IK6`&<0<0RzK_7CCA zdM0^vJIhQa?uLWh)8!w+tw#{NV1mG#@)pi;ubi6DOOc_QQ#_Nu-DJYVjRx)$;Lv?j z5)L}_4WeWkGRqwksGhjbgHk*G5r4YgaljpcJTsZ`d>L>zn;`I}zmpIcbeY3UR9Cuv zi{}V&DU2igcx(bC`%Hhoo)^yF5%WENdCZdXOn;{Ww;g3>GW~6b4)e7M0&n`;;t=xh!7T%5w%e1y(Ktr+QYf)O?e;Qo30bZa(~n6DFn&A_+@k^ZnSNY)lzTDWguagqy_qgLS{|DXpo!Z8+(ZLI>^Jz+`{gIV zk$)6_L-0q5c(eY#0gh}olNkr2%Dnn3k>H^En+Du7rZDhk9JE~|Kagi&&HCFPxIY;f zVog6H@o;`j08Yg-rd6DR{Fn%zLDL>S4 zB*>5Lz)iPI>r&gJW2F%PNQI@0FJAx`W_&4H!qd$ZiKZVbfm41=kmw*kP6h6m6ltk` z>;mqt0G!z#zo~ZbEt^nkbG^F$iNQHcdlWDA{J<2CQ;;7MfICl?>%@$!ZNP5`v7pNJ(S-;emo5v4(Tb$^rLI75VxhEQjc3t0Y?w>n920x_2uFG z=v?RdF-Hmw@?#Ki=$a|X^y56>ev^Vq^R zOq!udW|)~YDT1J~Z-N4Xf`EduDkumj`~U%e@Dmk(g4<7ppMav`Uq#$e{@?Gp_r1CE zGVL#}#rJ9R-aY5sbI;w*J@?#YxYqA~{PpqwpzPgo&7}2@ABVjwmk-yDE>s^!f^H)H z<9g6dq&}KYSBv(hZ2b1;qwkD~<>Lm>x%B}RCr_b#+yy!`g>jF+e;oSmiS_g6K=#MczQCk=Mg#P;IdGEyR zl23xp%?G-GJcaUcALuUgCyKB?ADiEo^KY5t{(M+xTNd{;_#b~h-geH!@^Lok+=VRBo6Px!OcAnbw;AiR2$H4g$Ygadc&dmpkT%JPtxCeAy{zMV> z=cD+7iS_e)LC2kN{^u3|Oz^`OPAngP0-c)=6p=iI@-g|MiRB{-x{2tkvWwwkHU-vc z^S)=tvQj!|g)dR}mbzj09RZygJpnqHp5q=r-Ls&(b{y4syD0@_@bU!cw!(P+MFBd0 zy7iY8){}m^FMuvyfFExdD>2}a%X9Vi0S#xp@sb|1TvmW?g%QvF+~VW6)8Kc7!Ot&W z3Um{Z?>U3tRR%x5d@oN>zFEj*1^5K;|7wGuU%thlE7bn{<@ev9V|kI^hYfyy`MwLf zh{l^QfBF5<;ODk4^7qRZ_@MqldXrfJx+LhDd^8OD`P~n?nG>LU8g#J<&@D&hrwh>e z%kO&7%>^CH?_-+2Q2V&+qe~n@B%D{>q7!%gGa@OM`AA z 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

      5RSr}^9=6j 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%0DLfTnNFOsUhM)%8l_Bx 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

      &MzsBF8+X(GukrTPB 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%0ASLst*JOWi01$v1_iXx3`1*e2*qz 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
      s@j^Nk&pX1j<4sylsC-M8&gXTBeEx7^C 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

      erA5&PrsXio8natZm)jN zyYyS0LcdpB`W>$2`stVRPNIEob>U8eY%N~69l%Z1{%+#c?*iZ$5A%;>2OfU$UIyIh z@FQ*mK{$x>#yj!X>BUf)I8z=cy5jYg+g9K%hClT?)rIrgOX2U*YsdEo&gi!;g??qg8U4<3#p~6t z2RLId+g&)Xem?{*o&N1pmwx6xiTZlu{X1}skNI*zig?d?KfQP#0?y>i#uWMTBj9+y z#dt4C5$_uxq*orO|nJ+h_i1&El(rGuY0?w2Nw!+BaX9qU{XY%D1SG?YQ`GpJj-4wX&KP38vyInYM z{v8EeI_cOBoJq&ADbjHa3?JbWV0Czh0Oz-3P@gUBd&x<}z?>?myI1}$FDdOD(TqWXRyk`Fw!+G_4 z0JserH!y{~NuQ|K)KFe#3f$qq89RLiSRQ`yo&#Jr{3!1i1mW<*{T(?(?j7LjbiCOqaP$6@NJmx*+)Ci)YkB!8 zaOVJL;vJR(_o_?Y&=k0#pC!t7j}*AYz#09_^t@mB9uM4bosOqLdHCTjbm5*+Fkjp) zE}R+Q`pJ9Dg?lQ6yjNW~K5NW_^1RQbehQqKXA+P;aZSv9zmWOoM0ziC<)2qxF>vX~ zo8^-CtrYT>rYWz_v;LA^ddq<`_Hs=Mc};1` z>vYMxE`_{P)0B6yOWqAB%O(E|<;?hX(ia;RVAKjlq-Zj9b zqu=*|Gx|N4Lf%i)l=p^9-a{$m{W(o}*-k>gAEuDECvfXMwnC@ZUd{xr7<^{+qbcOw z2wXb(cb`kY$5Y6A9=LkM={>yqO~N|^X8=U~u=FU0Uw^q0xODW}0Guh`AE%JFElqjn zy5#*Ng}fWnly{#?-cM7=dmgy2tL#WRd-Ja~BT>HpmqOlGflDXp%tm!GGQcN}mxr6F$$KJjeq@1+#-Rs)w#{v8RN$-iHu zkatp=^3He3`(+AwH>WA@0hhejQpo#Rn)2Rt$$P^k?^;~D$Fc0(Sg%>nnLj+}zRQEl z1g-#l;)dbJgE+6e;VyY&1m!QU*d=eY3+I(L!zFJ|7tX8SeBf5YpZbk=;k@#;yX2Yq zB){}t4BSF3ƒrT0abJlZP{KY6bMXY6ku1@q0n&s_56x^Uj~S_2dHV~z{wP45ig zs1NgHKNrp`Z$5CQd>6QIUU`)+c?Y|2UU_xEne-my!g=LwaLFro;k@#?ftvz%j06;Hfa5ha^bxC;maQJ@;KUs z^UBKvu2{?KcHzA8rnuype%LR)GhFg|Q^>1x$veh{^Xj)6xFc*;zF%?Sy!zdpn@I0A z7tSkhCve5^r@fru!g=Mr?vi(k3+I*hE^usbDeq(#&MR+tUP8Y!T{y42V&IItGh8^Y zygHXWGY<4CkJZ4L_Vk<-@;12So$tbV_3H-C*zG~SKfT!is4WFu5{tN@>aX#UGKtqRj?3 z6O@1dw%R4{Q5Wt>J9)gj1-P9d6>p9U_dIa;k00}g1$^IY^`=MOSAnZUBAAZ7c*4PS zWbHF+_MBqZF>}uBS+i%(+;{fe;^G;?SLb0TX3~?qoUf~+O=_Gq%&rfv^PgfqOpx#ZIKSE zq%G3YyFT2}-Wf61?Vanotddw{3!u@iw&qx~1PO0i7YT2OG`B^fRybU~tZGrXuA;uN zvc96UybmMX-r25W>WMYC9@X2^ygnYmx@hx8IHHjb9lK^wFSk1Dqg}n-3P&%upaj$< ztPiuazPhA27K^sG^u{7RCVd@Ut?tOhsiKWGcdjQcp_$<*|K^sKXk?SyU*(nZ?e1;q zM8cB7CwL08vAI5W90dF)7P+;ypZI- zr+h9Z7*{&e`W^f~j$<$D{QZ0nLFcaC*OlDpD+ zILST0GV@l9)4`7aSCagTtoM@Kg%+mNk+GBIvUKZq6N)Xm<_V9D`2N?jzx5-^_6n@yEeU6J(o0_YSyuaNJyo`tuF#R=gqB z{Yma&*4s($1Mo8X{hC^Ox|>?icDHVb^faMqjWqW}n$|};BhluLrcE;> zuA~)RAKKq=&(@w;WMj{S3FuaPJJ7qa;ht~09OPozh9c9PGmxtcvEi9LIU zX2V-`A0lVAK|{O<+gHsgf@LS(`YqwCVD~{ zA1v?=5PbXHQ(cu_IvY`BtRogEj=SZBvekP62gY8Wvup4TF(j~26&w3wZw zMZ8>;sC`igiM*K8N2@tWTFr?o!ieW0ACuECH%Z!DsOj;K8z95SQ=tbl3*wY zy=-4*G;(xrq$h?rj&{jm6do2^hj34L7wqf8RF*CamsczfuPm*vDqXa!qNH)doQ+4$tgowDJKF`Ts$bq2Zrs`( z376EgMds8kKV;slC5!N7h|>Dv(wc^<;g^NhJOrr|t$LGkpi;`AH$?r7JMFhtXghcPs7X`UJ0)I1{`+0wkRyCc$6u?i`l z&axxjVJ+TD9#SyvQ|;`Hj-;@`V5+}^{^ zpm@3fHlQ~Eqo;j+XQT~?bqG;|UIv$K9qldQ)~?QV?dyxjm+jY77wuY)d!gdRD4L$) zDO={xo7%*JYfzm@Q^m%XNLw4Etm=v$)e=E>R=jV?jHc%3#-^&8vZmIi@?{WMCIZ7X zT~XL~$%gU8)2qJ7Y$_>fYVT}C9(~cVAR_!H`skjnR#TX!zh*!G-&dror->Cl8pFkx z8@o{>ylH;P%#vA64PCv_)(Fd|qqi;czo}d|YEN?uY9B1U3H2{rRz3U6(X9{~yqRL#)l+&C)%0w6`|n)`_=AUt$4uQ;k?VZr;{+b*)FU@}=p*&bkTX&o5sC z3~*v!vKF!C==w;kNAe@8?g5&=Je5pq_NCc|QJ`aUdv_C0;Y~A3=9J8AVs989)tK)5 z%g?Ajda{;(dBY0b9QT`xZtX<%|Gk2CtJvPXY0m#(87JA|mzINAsrfP*ufAik){Q;w zU&a8xKTX;qo7yqB|L+$}Uxn6f#+~f{kS1S#592#?{G~UTzFM&*>gwU0&wt;PbVJ^+ zUYuwDAJpp09MtunSJ0|s`v4C|M&e-{qk%hC`0rQ`jEw%nVA;<(wx&Av7<$e<@|E|3F^u)g- z$hVC#h5m&JZrK`(bVaeB=-MalP_#{X9%^UTEmOJdwzn3_yiH*o(^Vfr5I zKO|G#M*T;H{d?8foriiDz$LbSTf-DXihn|O8Y{Z&qp<@B;}7q$sAZpj>WNonxd~?&e}N6m9QZANOn&&&CbdC)AdZ^y`3_9XACL zdGa;Cx-#R;0G5Xlwx_*SfEQAe&jT1;wMEtuC5(A+0~dFnNl-pM&ESIrMWtC#Ja*WX%^h`{qYaUx%Qm(FpYdbjPbYh2B#P{h^KP-5 zdt+T(6esJ^?|gn^#xDZ0Rja)foR9>3%~lKWVYM6|=QsCkZf1Tc)DF8Xx=Gg8De6ha zcqBP7Y>!}46zk~_1_nTFjB`#2Xim6$!`9YyEMcYZwQ-MVB*Fb=0pZ-=LEux2=AIx~ z-GY&L4;1%?t#L%FW?*AV0yVZh5{Y0Z#>|;lNm zRfQ`_{?3fxQU2MyH4>oVJ{weBJd*NZAdWoWjN-0 zqA&*wi*-aP`!xyE-qp&R7cp-o>k#5PB8lAkivTxvY!n?NzDq*{k;U~Biu`oPpKr+6 zmMQE+hWrS1VRN{JO++`&p@0X0+f+`&k`d|nf;*lNeB=r*bOoz*aakYM$MJFo+ke$*PD1Coviw9Z{ zGcP}eqWXTEQHh$qVQW2EnNPDaG{ae59WHHa z?vAx@QWmFtF0)a=+Gq)x&k^~(d~t_MJ7eYT%oGLNu3+J^bzO~7n7(RsGewI^6{5Uq z3E|IB%C8*F+luY??X8Oz)pc!-L>rnn>2MO&!AY3LErN+FvPStVMGCU^8WF`-)_vf zAfQ6kH&>~GRT*`i4urjMXghJsJ#%JhSv;oYI!wZ|ChqqG9n#cGCGYc)3WCZMiLPsI zWhGEre@ka3HeaEMq|vK9ctR_tnlFWYM$0zst3rMffJMk&uKMO!L|XwP82A&G){ca? zw1fktSf9v3pvqNM8`n$7@S;TwAYS_?`z&4+7gd?;Q!QDd0!{XK)x`r%O!lc?9QQdK zK6SKYwG%C_z-e}UZFRW5ahX!Bu5wLzb=-5CDH-h@_o#>$iZU7r9QQq91NsX-S_94i zDfzn{L=VucGU^~%ll6+2cc$`~TMCad>7BA(rpKM^O+~CH#FjW;dSFDM0{kVwz98-y zmnPB7kM{ai8f5)XvSVk3KHz{I8!yLio0i=+r6<@cMU4y6Va$?DfEgpOWUdZ z?m^3@{dCKyq`j30Bh91Y7RSLo-4Tut6#K=5n!1ft!e4UCMr}56*`rR+Z=7~?vAi2$vg9$&u+-L&5`?2Gv>&Z_^|s7bNJ}xj8MfsE7)^XOYb^1$BHp; zb31py_b>+(=dOW^v3;$+v9qdv8 za`XrZ)Zy46+sjm{Ko{lqL^gJBU}_ZOiI6q`<-9T{*1i#&?&JP%WHJ_PE{?KiD>#?> zJ!9h*QO^>BS-AanFkcM(f7E>meB4#F|8JSe*0fEUv`yMhk|qVpR+4mQk!G1Rfo#l7 zX{(XxB$H)qk_j`DCIx)p6Im1y6clBZMF9niEP{$``b0(l_}rfesHo^e!R0-9fb#!- z&%M9j{AN-RQLN4<`Tg!a=iGD8-Oj!D+~o(L1a?8+108b(mNpl5n{OzYg1vHLEkKK| zXUIR5YEZ}YBx}*48wh>LA?!kjNqH4`pPxHY;jNIcH=^H7A<+z{AY*%C7;a1UDZoKv zXx*_mDJrif=X+%!sf4P$hLF=qqNYVIRXJhtm;@7|dnA~SU2)n?nqfV5Qmj@GG>Waz zWONmopDBDbCP%V- zmJoo-nusQ|;U2ODnwXrJo^_cWD=@vA%7u;ybcV`BiMp8pOhhrlisDR$?o$=+&ytMv zR3bVu9!rn8O#WNJ(=2pF`6)?^Q#m}Gr(-GBa|O9r!qM>VvU*8Cdt?hcG!-@~x+n~J zm8Jq@@e5BraI^pNtddg2y;zF-@vJ&h#i_s_%Yt=rDfrK`DsWU4nu0%>rB@}Vz>i?e zs5Pc4J_Y_I2JRZH%141eoE0%!N(%K;jM#OEDJcL2KFEc7Gg{#uFU%q(w-xx=YzlA? zM%4s``4UFYIu55WG^eawx=0lK16fuxah8+AdK-1{Yyz69Iok_*`Y zo+WdxTmZ84BZR!ybab9OhO0XN?1ygl-)Fj$SkFYX>Xw&YyvcuS7MGlo@pIMx{{HJX z`$vrJurjD~QaTf=8^jGifcMdok)}CY{ZWw<1PScu(F+w(|V`7>z%pzY^ zIX=mCK_MQ$!og_&t({nhaf#AnT%EBBvk>%shOf}lOvLB6?>Bu1;n-G|YCJhPN;38K z`##|Z|GQ4cvCnfY-&=4qP%dem&=nQ>*0zIg9fe8p@n_ss;(LOw!*HtFSBa~Oe0%Bo z03WyTVNv|;Bsrds2bSaU*=2YfQ;)|IN|lq~0^bWbpjP|ZEP*g)6!=(?SeW$3+@j|=#?mx!+tksyN~^KmB;&(rmFx}GM< z61rZbtAdD^>0QcTm+#2R81Qi5pOPRQzJ?Pg_{fQY1v`;jYU^e9G>y%!&KQh<0BF|z;L7`irnjiL=LFG zxjzbUBnXil*!HfDt|&?#$7ptHB<@0c(qZ4#8|jGlhoc?adfQy~+rq8UFiyR+MWW$t z;r?JRnwv0$aM7-I9LHR=67>>hqiAX*I>qTUEPR;F2?v0ZlL&x!dK|VL2t&}w2yz9E z2^N8|XcrlT5J6i@Z?+nWufHW6?m9UL7UFGbi_m8(Ur*N#EC4T}riiw*xAaGXp=|K} zP*+b&Xs3f9haip-wR>S^1yiAef>kBk@?zXhS;>aE_-wyX$wmx^xY>;1SVB!;RI*_@ zzQva!Fx#gC&@^nfIUq1!@*@dO#o(fT1JDvaz)0iSo?U!=2k?wm^QC?v@=;+$r9z)e zWmHnT5OgU6?u8oAT&a2puLFp2siaWlI8Y6u?r}k+mLNtYHU3<0PiHr(fJgA#h>@&1SknHCi|1WK$id~-gH9I8)#bZC1 zgOON?VIPr$k(xpbJ3H~9eN?fh5)b8IXQe%!ysl#3I|r=Fq3Tcv=SgYxdI0;;2cs{p zZh`wh-z@IsC&GQMwahe@Z-eVz6RzqQT(=`v=$?R^B|!IH$88zQ-wF4X)-v2Q@jkdt zF98MIm&1KzrE-59?h6z_4i*nb(EgS$p9PZCBrEqU_;H5@&8{Gfz zEr8EAmiNF7c@`SgI~@wRhaGphQGKf8t}vD}rJ$}hmcL86YK`iP9X#AuI_?Ie`X*3pNZ@|U97;~pm}poHhEemKg<0)w?Auz)nh0xEDn1iS4RhE)G6i)_kYa8I+^VIB zCQlz>{)f)nYVZZVX_|6Y7oMWp$BqV`cKRtC*9WAuwiO+iKw!8Rpxy9vaqR-sITO|_ z4f%d~?M_05_c%1ql-IrkS0g67%)?np4zYKaD3 zvbJ`j7r+UehDBQKhU+)hQuEXF*ADX`AkVa^4CAz!7R6}#G(ewgt9|FeijFo8K>pBH zOC4Yiw#y|4VH{1K8jb_;R8S+r)39s6k{ueJNbV8T(+;W)Wrx$;oDOgA>kRiW-e4_j zff<@5D@GYqf^?-*m}6yl)evHJfD<0z^Ezly7KU2lah~)IV?7c9UedTRF7+fbw2|Pb zZbroUlg8;Jbfvc@VdIZrTsWSLF)4r61QIe9pTarq$tY|!ql^IfmmLPkzD(C-e-?9A zhf2+0M>2&<$|Y2tUh;iMN9`=85N0BsQtQ2dovmT0U@q(m4P#}Ykqd%HGqOO80C#94 zE7u(4BOQS!Y?Bs6jcrqM*`SA+VY^H0A=Yae;$aveP15zcui(&z!;nIGTZOFE{GAMiR5w zfr=5V-S9~bk|hx=3hu!*Sa!oLTa6k{6U9m)<~*ac_FOhK1sRKvL)938D=m}kMJwH9 znImXiv_McX88w{!rm<8D`zuB>HNEiCFeuk%8AXB1EOQbXO8CHyH5RHbpHaig6^)AB z)F=(yP-iM9mE>I%0ox>Zs~9-XW#!>!1T5YeRe|r>rT}>N6gk&EBtnCRylXF~Zzi^T zc$&822?}1MfGEw0#3b^>ftRZGJIL5jS*(UK1;7@~@j)&@r=}+-(J+9iG-_xt7)gvI zp`>A5l{`U`zXYhdRY3}1Rp4IJOpG)kh@qU#aQXsYTVzh@T9F2zNL9*&REWvx3Bt+( zzg}d9GJVsT{)BWG<$(`WnkUh=+^iv$I#}w|sJhv~YFLp44Np>CF&zPwi!g1XHFD1> zv!>3dq4JL=l2P8yusZNl3+)dY2LO?sB0p`JGgveq-lTUPAe=AoJIll>ZY;hDzM{Y% zwXbOdyrqGcv={e+Fb(gpP1w=N0p7~MIgVGTs{-$F{6fAWaH)-sCKivTW~0nogsBUB z#8xsyGcItIQF9e!cO+dM_)-HpF-ZrcHfpw3;M+i0Yj(E-M8}{5VpGPlz|Tw*4p}z_ zcO!7WX~rg|;?vQo7GQ7=q;;cWGD+HUp#nV%NcNTyu2jD*}Hs zO$gZ9!FU|#Vqwp|U=sZjRw0zNPRCJg)FdEiXM{o0QK031x@EQRfpx>2rg+ARpMXN7 zcF1&Kk-Vj+zkQoX4q_3xr!B17xfU0pAhARM5|OfXI5xopy{O3!T;#+Z5Zw*{qH#Eh z(2UL%Z3)w(^;;BXqsd9=4(8(R6{tHSN!DAFIp-0hz^Hi}ShcjN95#I9(bCoxZRzR= z_ey90+O|tjWN>6hU%%SD1BhkRd>Vnb_w-0;SCBS8xFhVX-1fk0wB0qCBzDQM6CC_UK>&D@Rj6%~L2GQI02|oY2oC;#q8|=8_p4 zVv&P>e&DKuOx;SXh6P5=@+D9XUYi>GMnfkhoKD2MhbKof*t~^2yLhol2;5NS=jI1g z7oBViwM4r5dZVzvX}y|dpfaN~IfdN31<114B_L<@F@d!!+B&9}8;nF-I zbK)}URG|Eb&;o{%ll@qGvMt+7gpwS##Z|)jy*a$T}YWxGnya& z_F%uUV*jz8IJ~zDnqxMbI6z#{iUMD+Gy&*AS*1`XRr-EF{@c4@Lc+4UGKc9p5{CIY zHXhET+)_|Qs^BM0$FO5;FPNAL+=q%cfvZ}u4~EJJ2umSCy3s^o)N8=~gcDUxjIC|G zDn{MSU~3(Q@ElHZznW_I7<|bsmC>QZ)R@eEd>oKX#S*C*jHI*RB7$)(40n4m2TIwf zRCROOa2cB;K%#L(n}|${Fg&PsWnv|a26q@Q70k)xx3YM8Vncd7jYxcfAEOJ+YK4+o z&c2;(GfK&O3;;zlmU0A##luWA4c&k% zdJw3Lfnw5rE9rb2rWMeVjIydWO#z+QIKX%-0j)!mA~6QJg+>kMl^nB*wyUiDA_by9 zJB*X55r_!1M5Krk~93&lie1`48a6BBq z`5X^NHW>G?zmLzXC0EC@4xTc{gJlGacr+OYM^%nzBRne{4+lndj%PDG7~F9AhT|6q z#Vxl+UEq4#6!9Qa?QyK7Snmd{$drh)MTtbnrxZ?5!CR_I!6^Z)a!(zXUT%d^^C+Ug z9EOuIW&szYrgmqd>bh8CTqy_cYyvMs`O?#a8laWRV0s2h0R&o?#K(r9A9l-FNm)0B zGtdf7J@hFU$`M0cgep*eMmrWyDancy%=MS3_5d1%*Ns*xaAp=23bTFWxe`!mC`Rv$ zjQ3!E!`@`EB1jB5fF*$+RGMNvSxUtV1gH*GRH(&C?i2-Nvx0E&kK=L)Q4>ci7&d4` zrO6zl{ChA?)ZO;6fp1oracPgo(TPJd;MGQ_%$&ODO0oR=nNNjD!+HQ@MZ;4!-cmfk2B~1`vC=+&{Mj%D({pY#+;(O zs8XY&!zob}Q3W~`gINUMrF_H5G=~r|<&$Zq)i9`hv~OX9L@GI(V3`kTnqioOqB@#* z;30Gw7)AjcB|G&&VT zX{AmTg^P3bW;gX?Rz2$SW=+FjFr(6@ix5C$pDsXB=%#u~ZG!PqC| zTtGYpJB!sYr2x8-xdCT$!4leTILi!NZNnVG6w~Z-o>mSDf@+JB2KWLq@FJ?eR%o!I zn1Nr=PyHsgQ_x&wtJ*OrQLine$ab5!CRi;vVe3^8M|dh3kzP&EnA_mjsX`msw`wCh&A#f9ir-RS~!tM2zSUr)dtN*qAgJK`!)l;18Fh9lym!#rz2BP#CJ z6Lj1~r+KAlK6=bWPbA{;@j1cA^YTn`h)oijC!1voknuEqht4gWS(QE65_KD-ZnbBd zftwbwhR7(2x{1TvRF2ZQ#IIUe3fCKtj9??Cvcq5I*Zmioo=ml(@j)u1k7&3C!VD=B zFpa(3U&{qNlMNHZS2*6;shRj709WomG%#1?Vot>b4$!MLbdo#l;lIWocmPwH*>G>eutse8fd@UIN86PqjrCOe3V-@ccYIzSB_* z!pJ&ABl}D9^x)8gm>n6XaibaNv(VVNTEs;kS}&h71OJ6i2tKu2Lk!Y}1CyIx3I$#+CDu^4jQr0Wnn|3+6e@cil(gmsc3y1km3^F1R zy~OdpLo0P!Lsz-9Ug+w;Z)|gVO4)E`^(5d3l6Jvb8=mPNj#J+^64E}L16xN}9H*i{ z2$D_O>!#sY0>s`rG{!5rSotOAwh+Nkm=noKLD%Tjt}s}pksvUm;4)Vnp#l3&mzy53 zs1cmi&`pH4Pswtp+*WKg^b?MKSz@^IA|fh#UQ-QvxaaiRerW3oP*@t@~s90V6Tdanv-cVLb5fJ`+Z% z&KVq2=yYY9B6#Z@a3*lB0%O5)c;w_*DjQ;!C^DLhIwNp_6ABxoJfUQ~1)K_&kAuFu z*k^8&$`Wen!5WAYo9I3y7U7ijD>RIq$S_3co2S!RLX6Zt=rdtOiPB2Z62T-#hHIDm zOe~{eDvb=2`2t1|CUXk&`Rpp+Wded}VF<&1ckS;AGjNR$H3~&4YaB3lbL9w(uQ3BR zU^NXbmkk7f>jf||3gx9CH=2Q)eX5@F10>YKpELuX^66y(2e!`)+@Z-*XwxLS!wlTx zkU6ls&A=CYW{_LRK9 zu^YZ<2EMLY0pB*ALSK;4>$ne_f&bLFX#Z%x^q}O8V2_xA@8zSmbBzSJN6o-v4i2{- z$Aeb6=M1klf6jo)$Ejl#Bb;B1()=@=Hm;Ww4$cN#JlV>uDlhJm(e+$oOZ z5->%(=5Mu(-j|XB0=P8r8w`b6!20N=>g{0TR$Y29*pCxUp^mMw#AKhct78>d%jiX( zEPe2WuxVEuX&4-Zi>B75Y5KGFe50lfG||4cND!O;BB8F{tyuBI;IMBfBL-}-%ym1~ zTA#5Z0s10Z<2npGH82}!Vc53pMivexwnk3G*3&UmY~C}93vAeX5e{2*JG4O< z19-FsD#ld;XAjf@Bq6H>qV$m_)R-d#1G@*?1T8I%wdd2W$hc@&4C=)&SY-nbEOx=I zGDh0m(r!Sjd}jsI1ozXJkpkNzZcF6p z1^7H$oZRAZfpSJp3=Kn40I(0QW+67Zs5@Y@Hi{M8{h~)pk#L*LlvG`Vk5eT*Cx)*X zK5Z_8Z!LU!(hDDFOS&4u$LW$D{=r8BVu8z(7DU)xxsAqcvQjrMny&^_@ni;PB$1gd zvFMkttmW%SM>b)EJiQBux&MN!~#7_`U|`VVk$D-C=+2c+z5-JaNK>ff<7 zHKpT1n1TIZ!A-1ayWA+8a4Meo9C!xZ0_Gzu5A+p|U{0u17WRMxRm;`0-*{jaPRrs~kD6P*o=}Q=v+5 z?kdOO5aK+&OghNqV-J<=&6~hya5z*&!_Gm^#4ylJ4a*H&=m?4x+x-P~*k4p!dlf}w z&G70_jEIV}lUr&Dq>9>AOcbbk+I%gRLoZrSIzL$&_;Q7CML4CFF?qw14J!gFVJw}- zNkV+g2Wfa%ui>&vOpPhn`SXn$Zq3s>>r{6IgWUIM0@V9HjKG&MWW#|cG)N!aL(1)^ zJ0s)Y0tbv`arV%KO7DQHW8v1i4%2_^k(W`ilcPhETw`?TwD}&3k$d#2ge(LN>OJ*x zMja7o`~6@PwI>zBJsfH~eM7m57=t-%#FriR4U4j%sraNexL2qm6B?9cEG@H`N(IG; zhG*&2MklUli6Kn!^uYDbnL+26Wi*zaVjk702(xf*1&Sx7CtRs;m<3@yEry+zI9jWN z)hi=N0x_WP#3Bf6deTW|;rJ}m9$A=@u2QiQa!!a4^AQR}7CN0r=0cEEE{UyCEC)%5 z`SlR?`YF?Jqe?2_@!`q6s`hf7QwkxUdXoYVb`K}mO!_I6UjOKxN_SxjCPM>2^$Lh8 zM2^$})2kqZ9W#0yjO~SCz502qlA|3{j=kOe;cm1FF5!nWScyQ;Ejr@NP;wZinh`0y zcBOcxCeeJ=z>MM=c?fO`Qgy2l8KarA)6@HEaZPja2J$Hiu0 zy&4u)OS9bjnHk%}cHl93z<^>e6@kCa&st*P$vN&G}k0(vK%V+zQ@@aQn|#)HypbWoAd`V(ru+mudC=XROfU@rs5&ZlOf zGpT->gfrNQ1VlLLLmrU)pmV3*uxo^}g<8jyn*KCJ762yH01VGg;TXGu zU^4w%EGi;BTn6M6TFsaUiJ#_d^5OJV!Ljor3w%%maZHKJ0L&J!_#$R)+*ZyLOWj?) zC*spf5gw0<7+Q(;PESN;`*x#IJG*A|a1w}6ESI8vt{ zE6!U*n9o)^f(wPTDsm>bVHiDRPc;=VQl$IHOft&-6adK302zYSZrR?${jgJ>T^OF4 zKA46oPs%16y`4hSx%Iz}ZADC%q*ZO@2vy!J)+ClzAdlML(MVc)HJx5oa3Fe(e3Pz4 zq~ih6$ry&8e1x{N-=f(lS(92rWALQ+fe0KWhT#gD1IJIFFA6-n z)5IiRN*S$1TNs#oY)O+zZ1H5%@W8P-KC`!{^E7lXNmwK?&;h}J6a_wd7&b0xJ`Wew z-o>$DcVcpPyC_=DXF>Oz7BQ~w=|NZGbR^IldiVrQjst`;*T5K4y6M;u?+q5v#TwLx z-T*r<`)L`m8{S`B`#h_pBvA$m&M;JlhN&c$!ZA=#gXYWh=r$jvrxaI69VJrbyc_Ns zPdG11hr7NwTlG}tO=39CvHOj4Fi|;8jkA+Uw=8acB_eDQk4V^v?fM)3>$QQ-QlKNtv##Ey4!_fSS7ib#+ras9v9{KFo64quIbM604f46^l#fDGy}71YVpP!J&P-_}4@4DMTx z?n&^ofjrHC2FQ&2PI2IK&_U=Ir}mrm=4k)qQ>j9 zdS{Vt&?)X90lM@RcGe-5C)%_g=MHENLyQYFGdCrg_ptQ8&gj625%UQ4!VXwRj2oYK zyD(=y$L?G0PtzN%gdvOw+|#GKP=YuhjwDC%ogTpTOa${^EYk_4-Z@Gwpcd{q{gC9# zR;5*e?<_Wl#;{vi4pFRDZB7jWZq)rc>xM_Y-mJmcr7?6|+3*LK6b8m;!7M`YW%-S7ns z)$3ZS8MD8;Pb0>#l8!}IHX)|~usZSSLrq77Q$hTI8Ti#|)7dP~Egl$*Vs;8@T(yDtavwZTF6J{aGj4jZcP38Sq? z)Mi0F9Fl4iH+NI0 z?xJPUNHs$(D~BLjH9?34x?})!_slKK!o2(Dv_*p9o%^F4U8ao^96*ybN72+%ivpji zMB|j6ncK4fstkOh(v*u^WKSIH1|eKL(}1s-fv3*I!aR0nP2=bSl`xA5i#XT6!;}-9 zj_QK(*-|4VP03*lo9q<@3M_ib0p6(hR*Hb4h=~F99opC@u-G84HL)A+(m?Lc;`G{X z_>6`qEsJJTpf-<0!W~!?!SM%-mkMZw$K;!1D)a^bJ*;Dmsbelv(}S?r;Vch}#BTVi z78LzV62q4v)%!CEk9dW0(Llh`C~Zk^NY>$i_<=^iEX#gN@dNz7G+Z*ni};~N#FeV| zqF}j{NPrT3@5TK{;YDaSO2LcpN`8S{hL&kAdwr^%144Z2;8S)SIk ziDA9}>lv&d@Jf{^KB9}eIC|T=gHfzC_0ZyhuNULoEb6dPTS`vws|K$-N3KW$tNX4Yx4yv@T*K z1T}dCFXe!eb&dw;D)eCfCt4IEIAs3Sb0#Z)P4LC;jCJ+()lbmoGZ9PI4r z=YiRGXwb& z2R@a-^8D^DeDvOPj@oKiT3gNL(*~0zqG}NAgU2)m^M9a2L`fDbz707 zz_lS5aIWQYl^h7^&^Egs<)hiC<2w?d#nhK6-# zF&_9C#zf6ytzycZ2;8*T97Ls}*KT-+Ru~ggJmid>5rAP$9yAJ~sJNemmqI+U;e#5DwQXKgrN$B;ngfh-^XX+8%rTxK{II@k z#F^WH=n4&LOW{WR*mxTbhhorT>8U1`Rd|#cnS%Zx4~PXAyWwLx4z*6gLc?(&S#y`{ z9)uooTD;wGlO_gPoU@giEt(DCbF7A#s#>*ju1`Z?2^*4aLbOoi^->7O4P)7@*4W;p zXatjkc&gZAY}Ems69u?AMf9lj={Zbk@4%d?xb`I)_|R(GBqN@Ot%MiSVM$T0IZ+k$ z0=IZ`wpQvnO$yt{aFILemL?k?+^wsy&J^6n3#_f|t=e>==T>fJsOw>lP*vHfM^=Mg zCd{9>tWk7cyUbAvq}E*Z%$mK16qZ{ntcE59*~4okhm?N99A{FFLpEz%_RPszRa-E# zmK}g;b`rX?iN(i~SlB>AnqlMJpu(n;BUnG>AZDYETJBuaON+Wcf>F|yP_IjRmPYlO zq-RB1uSoJvMU0s_MIWDFyFOOO$vYi$5XUL82eB@ZmHSPK8d@}a0!(*ps2jS-TC&OD z4L^`mZHurPTJtc@h6x-8a~Q+Ci(_(f5?S|+GZPcpqy`n;G$!!zEVRQ5Ufa$p5GuWEo)D3+k3$PjN7I&9k_1QXXco0*(q&+G}5l8e*CcBicS$p-;Zr%pY zv$Xoas%%hlT5H#Ce*Of?3Z1O9<$QeWUXIm3@b6gy8Hly2oh0w{fM-&X!SS%%5`*Lmd9CpLI=E9|E*bV2*#l$CgCgI%uAcmscT)~YV+^?*nnjB+e0N{L#1b8#X zP*V1DDlLmGkGxNHS>TtBnajN+0z-qqZ!5L&3*~4SD<2ju?V7m(c@dCkcS~q%5NF*Z z!L7j%_GJF3f`*)wp5q#Uhu&Sd}>pH5qRGzrkwkUDuH<7Q( z5KKO{=#6W)QI8I1Q#51TZWosY>g%Vq_kOdud_{fzAQv0&H}5sKtz1>#+Sk_|Z0QyM zmG;tr5F@b}<7Z~^lE5mmLNYSA8m@R*&8m8AH=+Ap^G$)edheAHIoGgW=no!aFWWCZ z@0Qf}6$InYkHSE zh@6{1uP&~=28f33T_M~98V*N$`r3oWr@(1t#9mm>jv@Er&nJtIMih(cVH3^X==sr- z4M}@J1I#Cr(Fpaz^DTERZbh$f%0LixE_r9PwvBz^JD;o3QIe0K969TT~x)7PSAz_it5@k8^stdbcYQ?9o=Gka3r_=?SxJRXz8S zyF12>=X|x-nhTfL)Bc1DT8;DlSDV8N7w1$FeANP%nYM>eNXESt!Mf_Ba?rR`BpBv} z5D!%b8>)|}#};L71%hXrZLVvGPu*M&LQ9xA?L0M{<~$D*XLgC z@ryOVZ}`ml3hG|J@q?p+kNHq3g%&pks|zrG-5A6zWb^BDHk^Op&6_VY?FIGQ`&*)0 zyZc(vRK#+B?^`zCXCk^eyF0#>-f*dDOSN`dn*?~`Tyx{EZL3U*MwteUhu>#jZH`;> zq=+4}&Ha~{n;x>P()wH-`tU90<`0?>TEz3{$rpjRilPX@qYL-lYHt1*5JuUZzK+)4 zUDRk+cb#S&T63SV(^yT&({;wq9rv{UuCva>g`q3%tLr##YhA~^PnT_Z`Hz3Tuk7Bl z?p##|*UM+!_Hx$=yIY?#trLd$w(u?)YkkgVmFzlUto09PHJ`A{0(IGKt5%Z!cjY3^#--Br|Ov<$WEUiqA5whWo~Hr{#uK3|7~Kq%jhj*D+Y zd}dwU%&wW@eT|^?b)3Ji?o7zS7+!(isM|GDC-h}?=0(;e3+wEQj7yChr?xJv^IueO z$%4AVix5wb@6su(m6Tb=7Hl%d|NeiE0Bj5DU+7Bq0)BIZrr0MT`ONL+%0o=QB*HXo z(<(A?f3a^2Dk+**SX#88R&q=|HsRG$@o#5a7n(aNj4ytpRk<4hwtR-Mj&BHeI^HJ!)zf-F zJ9y3VNv83gYSS<+!#`U-e&_;_$On7@!tXonma)RJj9)(BGpo!Z+h^K8d%(B&u!5sD zJWz1Pc@LHpRDQn5KM%?I=U_7Dr@E%(6MU~P^$^qGhZ_x_f6^!_=*vFbMh2HX3$x3{9u91?s*A-t`>zYl|jmfi^N);}5L=OEj* zg+f6)A}eOpg|G_<4t#E4Oli|A2T`4_s-Pz$C5ZCzpBEyrY`&N#vX72euq6uSV7 zP5ZZpi*Wih3@l0Q#wqfUOG%9{9GU;Q_ycgr99wz;rI(ZW*_d_w=DCW z5Bkiya_KQ&W{>G#Sg`QWP5x60-oC&pSibz6`If)3{@9|@#)6Up|9C-i0s7GT2X{Pv zLi>r&R6bIYwhOmDUR;t|@}9dXz&~l{dF%pzRk`0R_4`Yg*=EJ6m31ZPI}7XKm+tdV zko{k(`(%Z1e(XNyvle8#PjyYMEWDdrDi1Ng>CocMpbONE=Vn{g3Wa*T6|))4vK14} zP%0*xZylLiKHW&YxFzuz_kC6x!&)%wlaf6W0LOWyhIX8@;a zpx)?V#tW9K%773ISgPw^ym1}tUmgDcI_0=rLZ}CoZ%f77&$i!%*+ol5CS37$QIn8L zY9sXh7eGPZ!InvEtp+?^*{5>{SuSS!(Y5uwUBwz zPS8TirG0e#tF;dqMZNhfs@dY@?}c*jWJPN(P!(++D%x?^v7)U(0NW_|2`k!a z1bAF3TJ`cPoFI*FRaI>vs@iea{WDj!KN?ciYNe{3Qi-Zox4@bQo$@bN)mTHEpH#IL zS|?(|IKyO^9Q?F3uW8(1_}j3DXL$C=p0!3nNnua1HQy{Z7g<$iwYkiyH;*uB#Y^x5 zuQalt*M}A?u@;!r_{ZYE4rYDb1Yg%@Y;inn04Ef&6?OLjzeFD!7@5IWcL!*C#cptZ z5c+R7>fxITvPEC5F-MInVa~%1N7yI5o1195aN7n~pp8@ozcAJj!@Wofkgn*#Z3ZcP zQeZMRD&H1S@Jt1cOMB?1E4HOU;DPL^2%p@BWBQ8z^osW3!RgT+^)YG%JENi>8_UBd zE8o9Wg!iT+xRpnF&#VaV40HJxCOL>kgK+y+ahVnP$U-pGC>~3Bn#5yQbTzlF6{k#qp+cc~Bs6wYzpY2{n@@ie{Yth472C1rpa+IP5HF+$7l=jr+WN4( z3P@Q+TWDv0M9EcNv6V~KgfFV-4~Dw>JA)yv!vauIu@zhzol{jRvdUbbig9r+5L-qN zd`T`)W!BPM;5wnK$_1_$V6_07a${eX3skW$&jqU3SL6a!?15aMioGTmsA8{W`KlDd zCJ(oY>H%@97#_suO#8bO{9$n`_*V=mYlV1Jc@*ZG$`kGGQ$_uKqoSoGa3V_CYQ3!|{*;=q!_BJ6&s zu#37D&qJ44>No77qZb~taPz`n^)Boxvyhof{00Lo#tY=-^FY20;M(~BH)B|}2?o{x zJc}KVZ=nRR@j?J}(00*5MHTbP3fd5?(D9<*Hp>cr-dpesel@P9LZqM!E9j)7axol+ zQN~U^O5-TJtW^j;Z{eFT&hQ^BqL$zV zz7?y1Sb^M{w?+cH?2ezerGWCn?y?^Yb7TG-cvqPdX2H7LOvSV-55jCNAk34f;gFP5L)&l${P|rO+P%(=(k7&zet4llY}FN zr}$oF7%ytSNP@*Ik>ud4B!>`>f7GEll6ckw85NV-5v ziuV(sv6^&UYc`DE(+|o1sNv9!h6On7=!Dnu4SJCj{xSw8doq?37-JU z2jI&}?3!!GRpu5AeC^3#L8)Uem5Bwo4ZH?0Mz(JiIzB`w-J-H`}22yft57rRDJapY8YY zXCGZ*FE5cGr3;Dn+4IZrSTRp%=P!giP^$6R^T!}H??+UK)GjM>5P;Tx0MH_zz2Fjs z1!(0pAi{+KaL$}*H<$SA^{3l~@1XzaR-b)L8yvHT+qG48O|{QHWht5OUF)+?+hadt z&DvF~2sop~u4?z$-|`>iv!C!E?6ZGG*Z

      usJY}jSq1SL3hZ#(cK-SspY;O_ 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

      ^25?q6yeqRd+B*FE>$@>OC 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%0D@8=3Au+TjqJJ1;VkFBOu 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

      yEp(8ewgGWZNb4Q^W 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 {eaFx0-08dw+3&4k{2LkYa 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ℑ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�fb4zG8i=BDUXw(*w&yuI6Z9MNW%Ho=_h=xg6x61_6xHtfpm&#ARD*V-$qhwmY7EJi#CJU z3P#Zrq=?M5AP>{!e+SAMQ;l*UJjy}iheG982*+6A7g|;do20mlz@5SZG^rdCi{L_% z9K`-FHtpMvkC4A1ETTg*S{g0$E}*#N0xBhPTLVZwNdqax>ctTk~x zdL#nC=mjW#N{9xBWIF+gU$iz?jBX+Wq$HSfh~j#h#yyb%Dz1FRq2el14l13o%7MiR z3miS^ z2&ow!Bji&G2fE-a5~ZL@K=wl$;=Jph!2+}{gV_^J11Sq=nJl?J6BNGy!s0>?(mpH5 zV#1Z~@00{X5IWNX(3Aj31smxJLuumfTBui1L82L`O4T?$X07h)r;$pYa8s~7k(7v8 zzMz@zLZKvp5qzo1c&p9~qC&yWK<$OsFQK+6c&W!@8~JT_uE0bAh=mnllK2KKY!jk-kz?eyfDv_0dg`MWwh}5YUD(%z^t*;YMGT$*s1)Y;%j+ zAuhSBZK|Rax&wE*wfrI7EfgEFD~czTlsoXWU|EFc+*%=#AnG{W zffw{R&|K3V&db@tks$bg%L>aPzN?3)go-dfluERZv_U%E2Qk|*Y>lJ#r+X8LOb;&2 z#Ep-!?lgQA9D(GJr%N#V!^+3A_0nla!vK{i3 zRM>XV#iY>fnH?fI%np$pW`{_Qg$SH?h~zLkL~`&Bkp*ZahKr}{BQqg2X|irpj|XcA zuOMf7{8#4C<-IWxQIjnUb0ji0bJ1KeY%R*B7Jg*pXq;QP0s|y=L#R|YhUZIQR&6>2V(Miha5=p0hKD2RA&PpZsQf~<@BO25& z0lrK*2a^MlP7p57?i-vdoSY4q)q;AZr1sH%f4Hym`d`F6Z?LVoHBt@tbzc7;$jv8m zFe-Z~ef2mp6|IW*)?m<#;sNYVum6l%Lq?fI3#9?=X=hkzA=9!u@SL>Cp!23ZnbPh; zD-j<`lZ)bDY!<}f8e;uJFQdEaubm?fRD=e?m zl3HS7?bNycx7r5gh5mP^+kmC6VE~6F8qByQ ziM(P6IphCIkg-%6?K(7+vV05@iv$g2eaC--hYJhEvF*y{i;uU!6W(+l^xdf*V>PcV ztyN7}eNmy&xQtQ-f5If#8pY0NSZ8p?4?kv-N?SmT)Xwx6FfCcvb@SocIUWNGc$i8f z!+2A{2!hMReBN4Lyoi%vZwX`AZ>~CCX814gphh7izTAd209vy`OTNnRUy5}+G(Tz} zAY8`@z1>h=l5&&bzse)(DgBrvY2n)p|MecZR-$Nk8va`(EQK~ruzL*u9SWvs_Zj|s zJVqUDY3C=R+6H1Mcz_=?{P%lcLkmUf>cIr%5ySseKe`ANh7q~rx#x?6Y;O0wbYX~X}F&RRn^tb_NQ;s1@|;S%OZ9k+GY zLLn=}3x@wCg;0VyFIe3U0WTYVEC_N1BA0hTeAV#3Dl)k*K?|6;BLyV^#_PPaDEGM< z!?Z#i*@Ujg*-whlTZaD)6nsUG*=(?@!6=zZb{V3=)Gnk2!(m#1{m@{62CP5JINF(q@;>-% z82Yb8if9zhHMO+YliOP4V1eg>39hPXuftZt_SVMc6aeE6BG-X&*n4%=_^?{1j#8D5;iv4 zl+15c?T#lpQTALYmo{MJ}oXlO98qWxfOXzfA8rfW}epAPk2M27*t7TTZ<19-Fs zQj8^>PCZZ+m_(V&DMBAfpyceuIk0?MiEmGwqbU3rOG^;jbaY%yOE=%_O=>6QxbIzE}AMCrmF%jX)XhA zAzX6O3)dpJWHp3~rb}}82NxL-^GqpZL4+}yHt?8DmeoQ)X7hyHlAS zGQG$dA?LJ+l>mz=>$IJTl2VH}`Tpx+&?1r0f1pEJk^e3SNtowW+Cr!GkF_gONf{RW zbY$T|8n=wEGlNk6L^zQl^bEQM%txp^&{wF4DPJjz`<79%8x6*J#1WCqV3M@IKHY=88VL2JJh3Y)jWhLpO^kZzmn|Zu#8@aJ;;WJfo3bSG>A>r9@d}o>G_-#Y zw@RT`v5}=EU`GpT zhFynZL{wl+Zb`)=Ris_Hi2_wmnyhlxx z2teBJJAH)XEDWsiV+Q+ z?Gsz9Xqk^2!h}p4eTER+sg8XHL+K>tkx!@7CZtG$_k?o<53?XFGKR3n6DNA5H@`4~ z#1I1dPWp)&+=l>hB^Dh&SGbWcO!2N#h~jX$MPe^OA`vp3^PLnVm6pU72$7ux#QZvp zU53ImTqcrA^hmUClc>G4_{$4HM^_4R=gMe|nn?@AO0IvbOr{$#1>-{lP~{4UC`1~m zgCi|cE6M$$;Ag@y_`lh1O$q>s zgv2;FQgQ{Y5a3`6@G6}>f=Gkm?kMuc#*%AvP?6C3y<(?bQl_S14WHZ4uPY%M8!YNT zs)bC#8Enr2BPAK@FFfy#w1QS(DVL25$l1VQdlBuZRcnjREpEiOuK zK=_U%p<5GMHQj-e3}NOnVIp3%{2>_%tQgx28g@|E2xSYkex7irDaxFH2{i!G!6c5= z3koLF=ZZwqG9XQ%#f*tk7}>0LKGF15(6JrHB0eUGIQ&G*0F*5d@iWZYXhS<4WLnwS zd@z0xs+|s9wKJ(ioBMm)2Uo2}qgGpq0l!w>p zR|~XNgl8!BUAW#zsvCKC|&&;S{N)oxM_MYIz)Y1^7& ztLgYTI9M|I&Lg>rwP2Eh@Y_|3)&~;26en@?+z_0@_v^K zGgTx^PMhZ~s0^HN4@kroEZJ!(mP3G92NreULFTYhF!Q^RI-F9}#AG0*|vzw-T;&BJak zDd)-jlC3m0Tp8-(hdBm1crYxCDKeO>15+(mh8{Z*U@}HKfs1t*1xgx;3Jxm24d1=7Sq!A!qlklZyY|`4iMR|P+*u2 zl0ig_*9Ya!BH5r*w1b4S=r?RPL?{WI$5NN82^-g$Qka<%DLcyhUo|>VAz~guT27H= zz_@^Vy-8E^IqJT}{xrGKiVK7hf!04{7fK`=5Vt3~@v}jo%b5t~zgVW@NV%Psw18N+ zr|AbzKHsWT;{RZx5$?ejZhm}Yu4r>&5O9<1*QsvU)axyhjGfQ&cR^9Ow;58k@bp!+ zVC4sUfyo~H1IeMr*)H_t9g?J)qG~dCN;+*=m%F;%fx9HJuC>J?-fbf`)*+N1N&@!W zn?CQcXyS-~*eLD}+-ouTP}21ABa4B?L?c>ZmH)A%XXsuv%5dy6N>^aR64m(ohA>l^ zG`3c`1NTc#TN6&9!~KAC58ZL@4*W#&)FykrnTd=H@CPd-*W0N5L${;50}n~I*o@!K z_u{()Kb2Iut|gi=jqM(mj2^6{W094bkSYMIPTW4%P*K&BX?qB3gu^or#kObTtNz1=WoJ{CK}KILO`y<2%%0 zCiq_$mb7L>ywKCnSMq2Hba4#$${cqHa>+@6IUA9T%x**baOxgDnzaB|Ap?$$n%(OiVq+I`2>|)oDmt**mte;ggzwUNbg(9HitsMQ3^ij zhp<15D7lVoe$?Ec+{K{fU5SlF zV-zV7RU{eO#1Hw>?CteRL++NQ9U`wSQ5&?squD@cZpN1?FbNai=&N~b;lj`M@&m`{ zk~xVVAXLOV4gcA5jHcvTtc;tdd+)(iolav1oA8_1IB(Qmx3--<=aQ3Jtof%~aD8+; zo%*6wiMe)DGEdv@mP}_ecy>uD34(YzRuW!icT{2m8xwM|7weKXqEKhpQe)CuyXXp4$SW&3 zl_pk@w?^J+D8_GXWD%2AR4{9V(r-_2x-_GLPAj3b#ta7U4wjiARAX$gCWgZhkRy$w zz_&v6suefWCfby&39oM#x5olqE9zk<{qT8L1U4CN=L4gT4gkS9kXp&l61xKRW^k=F ztw1zbnUCLphJDmJfwt0u!j@CRi?+o^t4W3HQYk@imtqjD8XT~1C$sBXNuwy4#KRTg3JRHH(G|-5NF5GO?O1k(k0T`!IVW~WR8fr7{SQZ( z(RYy7Q8rbz931Qfl2la0lEU+zsxZi1GM~AnvurTDxD3Q@omdHCk49Wb$e5G3xFXnE zAFKl7=xmH?FxCqO8f0s9gBV-I{eQ=ZqN1mo8$x^i4^K2YQK`u34jeBPMsG5+sy~B$ z1)#la5Jpf0igshr zw%Ccio3-eIFy5dc%a>Ap%MlkS=gDRDPBg9o-niU>^Cb<5$0gfpbS6)jx&s$THdcn| z>L}3>UpRyqq^+A5Niq$P1n^>cNr;*m0d$!p)ueEdeJEao^QP$WU2>v^MF~0`jZ8s* zpb@cr;SSs&!w{MQE&li;IkIa4DM`CVH+CepFDM zlEW?=zR8B>_gXInf3)4o+Ng}pK?1`$4#ryrHdCeasP^OI#uKiYxoOfNl&l4qaTJt1 zK;|*D+8Uk_J7H;{i{`K(hn;9R6~Zej?BzVk+QvjVBS>Kr7A|E+-IB0)=XzO%Wv1Y6 zU2xsPS}6?=a%N&?hPc*cFBK3Uxxj< z+$cckgGH;57D2ihl}CA{K=b2Wa@Ds*kiwkzl!2&T;r z!yOM1x>})K5aS74uuP}p2NGgurFq&6OOBX9iHW*Aa0am0N)7+=(3!ZwAD3W*Bu^qK zg(~;x$@6D0i$`$s4!>&wq&$EoX&EtEmj}`M(_}?7(s4;+W8b$(#LCuB}d^e=fvY74BO`Wv=6VofPO%gP^ z!ZX;~c4%!&)rz{{%2hQ??n7Id2xD^58O00$MJtst5z{RR95gW1F-Xn$wLRLl3ZhiW zARdxP6CE_U=?%2%c8iLgmUJOVumN|{)8sGMn%)>`31Q_M)4;c>+fW2}q!>^q)+&$n z6He^21xHT)+UdBHAkq*C0~fX%-3&X;|CTa~nHO!4xKk%eV;Ko&yKe;))&OM|2jwRe zy_Hq1$QN8#S+@c=i(wsU0vUc%GP%tE{1HZ1EE=cbCA@4WFMMBU`4ZF9Sh_mI;puM< zPqcAOcv@Itdh*eRMTe(rJM1CR-E+ylMU~i!$PfKt-b+#NUl}y`IiaA45S>stIJ$|q z#j(^((qyq9Ahz_y>qhQ<&e`zNzPJUrLXsn)7Qd+%g z)ylf6X70Y+J$WVvBe5CwfKf1MW(kQxGBUWhu3*}%l2U9>q3;Kc17`Y4zwt8IC%v`% zx~FC3Yj?Fil~)^amy{}FrMNTogAw~3Z@5O!rRzs)>*}qmOYek{W7heGYh0H(6ii3%P}ag6@u0E)tU0AP*UdLVBe$OZA!x~Q&MctIvDkI4 z+Bp+MnJifZLAg&#?2_DK?lb?agVXAZ_K397In8CE-LNcYhfwv z)Zy0M7hFqrx<`&L4XVZN4?N$ULh-Zf6!DuE><;hxt6fv}C~X;tEH}u?%8;yxHd3#uTpGsrp<&IcEzLzjidG`m-)y(n7z4ux)0ez2bzB3+Z>)Y zt5mMWzvbK9IdzspD!)PIwLNMpjIj>-kPBii*t2$pG2@#t?1a5)4>P7Z0Ce-p3G-`D zaJk2oI&Vz*2m8+`Jc)0EVnU2bb4For2f5@Ae#pfGtUeQ(i;~qZVYApG)+clOKpE6UOeV(!O zahI!zx4haa+#AfVeSYOOBYLlU=DgA@_2bXC8K@xc;?kk@ysIT&)-qR?)ZzxU^~wA7Cq&@DgU#FyNVuRzRE3me)r9lGr#`)A7AHx_T(XX z{`1R6>?ma9o?B0Ujru(Qh#g}&WyFq2{9U;^!QO1!UJvK{_aUm=n6q5Oko#HbHvDexM}%R_%l|c-<2uK6 zE6H^E^Ej_y@BHUQ7;*QFJMvu>oN8P&x9rZ(*m0LVY+U49mv`qw?`}WdsCW4*N&dr> zk45!t&YUe1>u1%o9YwB!i(NaC?s0dm`gF@7bGES0o?P>C;{2t?T-S~>^Igw*=8k@D z_Upc_%Rk+c^lcGwq_oigJb!upG|pq-5k;l}x$Keop66dypZ~h@A^3TIquwJ+!6Qq- zx7AplchQ_J#@sQ#uG;=2^KBV%(VR2b_A##fi*U4%Rlm>1=GXX6oaS@CUbVde{3*V< z>nLp$3m=vF!v@M@jA3k9^;h?Xl49RSI^i;cUs%;_8)2b;px zgW*pMWUdYQEJHo%5%S%6$T#;N+cJGNiT((=; zTuyzg*)xrfz_A-*WK1km%zQ zmvV``P1GD;f5x3CxpkDoUUtlLW0A_do5;m{-fj8kdvEdXm}@lfy5&X97`y#AgW8nw z!E<9Dx!ahV|FFyVgyBMrr?L?rc=Pjj%=JKCi*Ji>OTmtd@~ZxF9rF7Aomb^8|8)8C zbz91~B$8hsTbL?ohL*#&8L-1w)r;_$&$DgIQa5BzV-Mdge7ftI&%MpHjj+c~_1uzY z#!mQW0na7n(!Xa&d`Eon$k;7+7i`PBS%iR?hD6IAN%j0hYTu$RjJ?;G=cym{>w84Z zt5<#cV#vqi?yG<3-LcDgT|hpfWj#!FN0iX`LH60@Vb}KA9`^^e6JOjpqxHzUS_?)Q zuI+|vegXTwaem>L-yJiD%USx+*Zjc%vuKffTqFn{ETqWsZCw!}}tH%{R&7w!Vau^8sUHNri38~88& zy8erQGIof;UYv^TaWU`UxcI!$VDq2-1_{rLi(Rb9M>>%<-0wG`?W_s6Ze?5 z)c21<&!l;U?z!VCI^3?jV}ide9CW)T3=|X&6hHp}guB>T^w;r2;{TSP>%cTc{p<xGa*Q%7U>eGVkUW;yo^A`I-fEdccTRU;89R^(U_3}wuio0m_a ztp`CqsENE5-imS{(kRAo&pmz^1mfv^B`4udZXER^_ce^+8cD{kWo#0?ufq-a^u7@j zS9$}48>_f~+>OKeK6)bwbLgGV(1YNCTxw0^e;&P&@IB}aKF$hqIu5fjHb%4@iVw9M zaxbPgaLedDnX!GO`v7`F-huR8j|J!9+WjYNg$eJ_rg z(i{G|=#9j6)BA@q_i;f$oZNR{T1;;wdIP=hVXU9tWsGg4H!fP=OmC$ByYxo7x6m8u zIFa7S$5ZHi6|PgKH`0G5y{~8Nd-T46vG3FSW?T?YZxq^j^oE>^=neeE^alMBdLz6m z=nXnVGq#Y6X6$xO;(Qb}?v>~Ncws;^?uh3Q#+LG|VC+twrJRT)^TI;X7zPal<@t)N z%2}JDILzk&gR$xfn@`F+*?WrTRQFcb*+$KTjO(Han~`bEHCpOe)Z{0Cba8*kKE`z{ zD6+<9`y!_BKwtl(HhvHK=}N=pGPXSCF}e({kG~{+>~?!6LUMx^|=Q=`%*Ty~VT6r|(fNZ@l?ojyQVs z9tF&cLA_C&Ke8+@zi|G#{jWXv{3*XEekLz5dQbe_Z^`YWJl>fL$_US~hA}OFWbxD9 zaNa@l_5)(+A@7XxYKZ9)v5F9dd3j?GdHbl-J5E+HD=3_lw{G+v=t}mTS2QZmYjib{ zc<*s}>!)8eYJ}Il_bc4}-MrGdZxjErM+JY}*ZCfWF7yI_#Ap}#`VFnsd5f3aO1!mq z60*?a_IQuYYZ~pzdxkB!ix}zmiNVRWqdomk1Z_12fr_fZe|f3NSO7OI|+_!x!36vHw**3fVzx47hi8Fiz0 zPr-q) z?A=cQb#xcM!Ob{mcHmSvi}>(LqG}0(a(w;uP&}Xci zU5XutEW<;PTm7&+ID7YVNF)dH&5B0z?9vNBI)(h(j}{oKX3x76jQfazUEX|!(`fOI zq@UGzAH*>uz{Q}96%)dC_pNcJF=8ymiLwq4`bUgz>C5FAZMcf8!RLq~>i7)9XRuMO zF<<|KxmwFyt>v!P3RmkwSL-5I>ta{y5?AX|S8JuKb(y=hybS-#@xKE97vldS-sQV+ z3AV11^I5);@5&!Cz|F@17y8wF3h}j^Yu^b{`;KL78;BzE^w-k95A198T$?VY8@;u+ljJl zi$#+VWlQ;Fm8~jQ=Aj5^&;L-^*Pu6zxm>QTxVWT&I@1F8Nsm)!T8PecUTu2PFN*^c z{cgj%KJU2Eo-xOc`EH@R=T`?c?7y?Hu&_9O>t z7oY!z>>$UJyP$A<-m&w-dFiDtcq^K|fiqpkBx2?j^X^LZ$Zpjm7Zm1?nVWa)Qnv(8 z=3v*7TczX)US99+#ZT|Qb7}?y zg?SU_6bEj3y`a!LedeA&Ci3KsdHvHLFDR^=vHopj>3?)L_-li@L1X4fbc5&vM;DEp z=XLw0QkUkboi(y#;*41%Csxj!II)sQ=>L`%PAwW)II^l}5;GOG zq^6@D-t(TOtTk!))-Ml-5|#H zwr=n_BY!mYgD(*k8``Sqsv6_h#)wgb{gkMkxTBvLzcKO(XxjMy$omrbx{B)mx$nOB znyu-YZt0S?X_5j>o246;C2JF!jU;UfR30zMYZID{mzSnxM<_eUzAHPHO;HgAWs_YI zK><-vK|oM(K|w`X{@?G++%>Ny3Rw94?U8ydlgSgHJ&!YX~eCSIJ{KWp^qZMmSSN%V}vJ@ItZ$3&VA1CPP#ug^sH;mufH&6 z&*x-zF4DOt*yjy3!^-*5GjYCP%vpF9O7)D)$)0!VL`)AcYoO{gUmDVD;WVUQ>&F0T(6_2$W`Ys%Lj2nIzl zrmkBb2!>~8pDG6l5eZl`T%Kf``fZEtn~F_MaJ)S@3atO1&u^pkR&a-?y_FfdDY2?> z5s=D4?533M$u?AY#y<3x5bXq)Zt|t@L;?%YoEO3qnVx;@nhPg9rJD2*_#sQeG>Uw% znj-7&2f#WBtaqYGO|ni}Abl3&QUh(eB#b2lXmA#PKaHz*0rTf#UWWN)jP>%C<+JHL z(I%`bsq-{i(`$YD{ef-L zcPzHs72DXr0e?{Tcibq)iSTFE4`vHkSJI~3l^^U*%x*6o-&wjPv8dPtoEq8rYmX2g zGRV(@M-vE`w>%s)CegDEnmZnbXPcUKtT}G-lRGHs)7!xf^Dn_nGd&Zdk6~b@`3$;c z?g5-8F@6k+QNi<4s)wy@TYL?GZ;lvvf<8qxv1}JCy_e}vyBU8Aj?1E3Hqwn4txcsM zt+c5*+NQ8Dy%ji>A%90}Q&j;x4+#YChfR&EluM6y)27mn4UG*KWM7tcOlWvwOWM&> ze~@-mIFQ|ucI2eov~5$i;IWVq&dg5W!B9Awxe)8rpKjaxLMSsd1xsofxSz;KPsc4q zdLRfxNnGxiGO^iU{^pMq7i zvi>OEq*B`(75$7*XgWUt`=1#;9xB*ZE@EYqjy(yh^<|TemR(@3?gBaL8HXz6Nbl=I z#@2Goih)4r74(BlwFLhn`$2fmW%q?QE*IfB*&SK)Cq1#ItoUo;f@QaakI7s(Hn8~X zTxw2D8w}rYTxRa4z7t%JP7unPfgX^NJ|_#^!I&dQDOi{61V^D5zD`gd z!&~UMo5ev3%{XUSc7+>5;XGv|NKGNGRpT2&MJw19_9D8MFXG^Y2II!izLmURt(Dkl z2i}JT?tlj3_*gDOb_H)<%HVGfg|hjz*9Za*%Zdf=;5ewa0k$3v;v@$feGiWhH^PiFeV<(Ugo)A}al-2+`e)t=@%ift(Q5odOwIxeMoD>5eb-cBpVEEgUGmns2-vTD3nHKtBd!%h$%1_tA_k?7@@XGx;0RCsj zHjN4N)qt>U(lIBQ+ogl7wz4eLhF745R%y+ckM$@w+My1;B0msF{RONK=eEc@#b;@L zq0DgpvS%j!G&D3}M|h|=5em<^G?Y5+@{l>LI&^UO@XSDXS@xDR(@e)MjlIlq2VXEL z*9?aX4*b=)f~IrB;mVru&~mZ&SIqswb1K(l?qPY=XJS%$PC5+;3I}d~fAi-1nIr zcxh#Y_EF&U;gUEL!v9g%U97s zpwpcRhvDjQIQ$0U8GKKY-#j<22~VAMJM5S)xO$RF>a}rYPfj z+cK_+p8?~#3&u4;8Q1Fn8^(3ONaHH!c{zCNURl;XV|MO%+%rZy9z5g2xM$3Yy*Y@b zpJ^j}9)#7M%Ip{q*CY%dCb@>LpXU8mF_MZ=W?uEdK5M4fL z+eu@#hffZj5ncKTl&FLG5I6Gz+E8taSwZN z0Q9=Shd$K54m*-g!vnmrcl{t}oDoQoKJ*CHf%%4vFk(m(=Wgj+W9(ICFRJ*0q2Cx<}0j zYpN~bljn!GX6_eWR9QMUc)%aH9v413bFXm!Q4?xwrcckF86M7lVR{4}cTHL#C7cnm zp6eRZQ_|mEdF{Sq!-Lo?DTjqY{%Yvcm%g2rhhZasDbu9K^PX*P(Y8DFn*n1YeJT_hJk`Et>{qSks9(e`P%q43yHV8&i0A#)DyI?_Yoi3n!k z6{vTHXNTrwGFj%>^z`ZJr5vTOQ(QU|rHHdl|(Tu{@-oP~jjdmqi{+RLF)?)tb z&mZp9&)S3^Tv#9Ryh$|Ly*kde^NpXU^C6F%v8i`bAdva!<3VGK zn&lpRoX$W>c-F$@Q~!uL?x^rkrZN7TlYTlm8lIFroEE~XRM>V=*)%qwrsveOIG#lB znRbNvya;b;E=f%rn2`4v75OwTmz{RPJYQ;&CPs?ptaR5M8WqdJx!n6lqi>~S8oOHD!=HBjnZu+zwP9K>(P9Mi8 z@k!o!>FJZ?JXTp(-q9yd8WqO*>3V5wfCpe17o>+%IqGI2%?e>$m>x=@5+g^?UG=l1 zYhcTCr&_AskyV|Fzp_&?ix25f7$)IekGl__o;6i}`rePwpVE#E&%{Icv}3}ZnYlMp z-lw^5oREPh(C7}z`uRTBXV3cGQ=4?Vrn;@4+mrZB8+ALlJtNFl@6^SFu7`2kGZyUS zei*ksqh$FsHNp$TY)yWhcfRGd6I=rSf(>tuMSLGxt%d@@XD~ zFF-GtkR=_I5RVWEXLDB&Hw2k@?K;ymGciK1mM?j}gWCPyjh%yC{b3pWxlE z2#^0PYo%9kiF<@*!n6T@$B%@>nU4&NhM&p|zQGCY;*!`+F zcpnXHx*Y%2_ew!n)5`AS@?kE^P(F-$$xA-fyF5erFqa~^+mE>-L;EqR<9$hYybNo1 z($yIu`6k-^Os94yU7ayj-p*#q&vdP>#k0kXNzc!D${$WNpKmhQ>BL`1Rq-gyF;Btau-0 zweU+TTieb+TV*Ci~($ylzhs1%2z}`1PnF=Z5c)v{@OpBl}4wv+u6?!9D%0jMfsH|$PXykd3l`Zv+jjc6Rl}&Axmz0LO_ggS)nyf}jm;vRL>{>cA+lUlNo3ffY%N=*mimkX&f5WX{tja5qR(K6CI;?l zT@krY-w@3VMiPUP*uua|L^DfQ#1IEsVi5(>zL zq>M95UgPoa6-NvYNHWAa22P3LY7@y|WC~SBYrS=(v|2k-t9cN-2joDL4Gg{V!oaXn zKwS?;d6nWf0M)2?fO8WrrgFD2ZGaa;H$GEh)eoDNa&j4JIkYv}2E(7I_pLplKR;PV_80Xr93*EZBPtVVq`mx;MM zB^8T#Zd!3uqz-ABs9wzXQ9!Utn(HRPzp5x}i0Eow;3Bo4a*#^oxp1`&a7a+`l92Br zRJSqf+J*@CU5Ek~1&4*#Rz@nSrTgpDRqOilYH`2Y7I+SO1eMZ?l8f)VDNv-j>cB|r zdRoW>Zc1%KZL4&&@ZGk$I(%Op8=EVtBIWo- z`+7KL;x}#@u(hij8k?&kJn|crfJcX}j4I)=x3saz)|J(*t%A?sBGPefsj8!Ch*#Z& zwz^1TlWhLqy3xoB4rgN%&SZbhhv%{1&5d<%L|pR0-&R{)8>wh*t`ooWQSc?wrn1(G z8u5EKp{HQpkanb8_ z56i?^Lp8&oqNgyF!@D>JE{D2^!yD0@=oyggTqI7!Kb&F)BZ-_B;C3_c5;?OFgXx+R znSHs{k~1CrW{XJL0r-b13`lk^5;?6w6$T@T6a!Tlj3n+Qi7gC961ARE-;q?NK`DYG z)3S`@2ek``NCz7Drp0Fed_zraV4ouERZ7`%V z*qM+PM~EC&VQQgHlS4-%5EoWO?Py#)<5cYH{Laj*MjCH0mHQW=d^V zI&N9gL&8kid)(*7tsM9JhHm68euMCF z8opG+d_2VTw`ur39X_Q)p3O+Qv=*fQqlV3Zl81xvBz=a43w4NdDg^VB_0**63G3Y7 zXL*6=fD0B}>k)WbVZ;fV}wAU;Icju%6wXMKA14Ae6046^^ajl@*|Y|Yc>D6 zo$zBVO7bI={B4?l{YZW}CcwY^3v>-2l=&f)=@Gt8JRZAR?JVUTtmPfz$XiH0{3~Zu z)=TmutjHn@x525DH-QV_p6YEKN+keGB@kj%72>;SMAEU>myRqOW3~>L=}-Qa4X{M3G<>Lr+cmsJ!?Hx}x>GKyZy=OPA(ToXlzpPt=@TBM`m|F0j#7^8 z??frjVFR{C1CBBFzox@`bSP~ku1h}Xlzb5iqip0sa6Syk*X#9bWnTM8I0cuT zV-IZ~2xY+$%7P=56*J`6hg4*XR%F;wq{voe!cG+Nz0+&A!d6F_Hz@?OksLD-N@WmA zWe`fc*yh;96kR*k$YZ%XRqk3lQY(9uqpV{uYmzJLc}LXN^k_#@sUgI?LyZwiO%X~> z5z3O_`7Yn2pqhRkPPY~h>4Zo(tKWIF@ zvL*Q^>kyy#lKl9}mc-ZSuukJUG`vZ}$7=Xu9r9BT%zlblv_dfhAFOwwxBt2}sX2vw~cYP-{5vO%Wk200bii;*XmH}d79SqbVpBEyf)(-!Re`xBwP6H8aczMkv~wvQtg8ylqEzcONj6_ z($B=d)b%>?pi!o>wKQ0GkT`h`OsuKkJx`}>wdnYyR65Rx*icq-4RON z5z2bJ0QqQL!VCd>?r{Kn?r{KDYyN|D*rP*fF`%EM;q!HPoep;}1YHgdsEfxm{%1OT zUejOK@IQ1Y+x0?SM;AGDnXx+k&g?^YC+uv2V~k)%aY{Jk7{9r%r}IeEM?w?4UU#CunBRMa9lSh0X-) z>Binc^Gst!&^+7NCun}r$Pb$57%PM3xyGWPd7iOf&^+H*8Z<93_Cg_z5@cp9MoEoj zLGvnni_^T?C^V28`wDR1Ca&^E#O`9O_6o$l&RENJU}^mpe%5~pxNi{m z`HNAETYwt-Rf6poK&x9BJK`#^d=uK9WtdaX1k)M#Kfy3-U}$Yn>`;8bxh55i(d|gx zc@o~q2%K)1bs0dt7&s9kV321}(%930KGiS}_yUorgHsH%_GB3knp?grIj=R2F$<11 zGhnD=Gf=0;fp6i~pt;9UL37V-0IoNNg63_;a;EP`?ZwP+n>paZEzWDTmZ4(3z~<2W#A&Z|aeXbhSQ+L8PMqX8yltPPqM87*wz)}VQbQ4=&TGa%v` zV_nd^)>t1jziCtj&6kX5(EN>25H!CVIFudvu%LN&U_;QnC-C{8d2b*RH17)>5=7ZM zg68)Fok8=#z`-o!%s5mz0;=>tl>w+S2vs&il|HD_8#Hsf&EO>PE$A|*ZbX>3DQM;& zj+DwXD-(EHj$seO-QRVXqK7r>PG(OHuhLtV20`U!sTaOPygJTWkf-T)2qg zoqCv0!gn?E2mX47uJl)F{B=J3S2bSd3;s;P^g`q!5rTyVvo{JyH>sNN~(Sj`DG z$+ffkMiQq5bZfg?xHySft^THip=inc@EU;M3DA!ddtRf}0|B8?SSkAU{?q z0?2VbvcIBch!Ihu2> z=IGoh=RD1c29vpV?UWO%D>C;5nxki@oOzlvUvmuXloM?uWnyVk=APIo=N_7KPtCD) zr<`bKDRYtLz-{hM>a$RDF4i2!@01g+tdv=#IkpRd%J-Skhg-Jl|FvGTd3rkfzqIV* zGADY^uaOotMdxXmHY`e-Hgu4tjb0{A8#?Z!oVcTs+LvmMGj_^}I}^#dOmpBaVka_j z-yk_xXb$-7JKDQ`H`*PRkK7=uVqHD=e$_4C~n?pMClX@XD$%jz#p}uAQ$XDhM zJgLojvg=b#V82#kh@J~et@MMgdRCaFY|*?nIDLf5256<%8Ps@AM`@klS7x3 zi~J-%!d=RRkw`v-l5c~~52J|0Bb4~VG#;5rJVJ@@)a9<$bc_^|j!@DuQpkF-=ulG9 z5lTA72U)&6jYlZ)0~!zGmHY@LKH-zMho&Qx^sPD_j9sQfDDg*YJbJUlBb4~#H6Hy! z;t@(b`i<24M%|v*YkNYy%USCS+Mce`?TU6U7N~!;$8v^;>D|pY2vJ7(Iw9&AqMyq4 zK`8S>DD$)I2Xa0310|&WKrdNO=%q3pLYWS|RO-1y)6q929igOuQRAVM#3Ph=^h7BS z{Z8T$O8muI9@-66E|!|=)7ELwzZEdUgBhZ{8#Ua=5W-Gnc(rHhWG*`}48JU}TTsy- zX9)ghHU1Y2!S|x3zoF@GF@)YL0!n_f4i81R@n#{o@Ro*<3uX*+P!|kWFoa!HGlX5N zXE+95EoTV*ZeR%g?qCQ#9%Xoy5HD!@-x;Q0y(*~idocVqcAqo60ricLpT-gSk+0cqr$CLxBf7&bchpFnTf^3X@Rj>t#O z97u}X^s-Nm+D6asl|z&qX*i5?{|}9ZmNK3pIw9sCInzvJh)y<%A&i$s%I|+?F@*6V z3hPqdDtio6ks~sVA8QxtJMU0Bn=q7~$xtfIh;x-XQBMqQOELCEJ}$HPuXG?}U1`Fe z0PX{lY0;MQV%b;@x4b`~B2H&w-$i}h*$kmTaamX|W3G?ywZ-}y@m@ALS-9mDqb_fd zmE~pJ$0zT(8W_#zy*z|!HJ??L``&=}i==TVUMt$D`4m=&zuu&6NFyx1h~~5E@bBPr zUzY9zqvSg|37@nbE!4{QY|W?9vSZ$90j?0^f~P$mKnGssqFYg*`B{4V`Tz*FJIf_) z9U=S3_wa9(2~jcb7WEZr#@!buc{krt;Je4opyU2_TZsg{NXaD7|zn8#g z)t?-zkRVz8y$QYx{K5eH>u)oEJITive>v_3pH+^zT5hs( z{0Mwo-4(BS{pF~-3*R75qMyGUi@&Qj;`mwpvWYCHUaWjJW*eSb3iiUmL-tba6N5 z>);zrIlgxP=*sa1_^fg)(fKDU$GhMgO?#a6{n53@H^DcWavb@<=;nnxz-N^M?u)#V zmE&RXwQ9OM@VCc|2l2$(&7k9cyLuRWTu=31{{HdAL!&E4+QUvc_SQm^m173@j`n8? z*k6u|z?W?P^_Qa&jgf5J@R#F}N8w;6$=5$#jeX21$9}sg$1Lz2n?#o3PtOz@UnRqmo3`QSS%i9CNfZU-Msf5hc)k1an^JFWbT{`ScD z@#yTT27FdIYPHa0cC{XSqv;>FfNwPYWBrqAC&!1CW7|`sE62CMXO*L27v*>ee4{DH z^q-8b91nqyJ5v0Yzde5YQ}u$BpU-bsg-<)>Xwl5c+GAhv!8I9i`R(d0@cnWG*U0(f zsb}zd{3yzC;m=0b9#4VKYLE3g|77KO34EhzkEPFzu037?A2)aTFMoUd_2;82N8vA= za(sRl<=7W|qbbLE;2TZ<*!Ow(f1@bJX)lbf91nrdYLE6^l;aujjiww6VN|0j$1~vL zHW>fqZ;#i0l{{a6|M=XmopNm2MLE`hPc5o=bN2U-#+TFrmX~W}{}}!aUT_Hbxklc< z9rEhv{HsgAXSGMqF3Pb3d{>OjcEo&M`P@YRet#I9-~K1?O&diy!halHIm*FjmE)*gl%pAZmfg9F<1fcO;KQxnh|Av| z+x~KI+Y(!}3$PCT8cW$CQ`B_vIw{`sJPQwj=K@$l;Yt z-f7_Dt2O>>7V>as^X-4A^UpqvZ~uQ{Jtv7gF8-&w5^wmI({5*IJj=h_mv0aFR=Cnx zQSOR%%e&4c?<|)*e>rXk-(TGY)4YEDZg^KcC}B~)=#uBx?+Nf7rs?j$uV3MNLgavf z^3K(aEU#a`2JlVRba&uy-*)g>_G0zBWbJ!6_>Ky>C8@YS-&e7RGFiTUzIVWP@knta z%5jcq$RoS_tbTck;OO!_b(DNpfNwPVm4rr@@9I(V9WqM3e(=%O{MSA}xX~A5AUVd3 z&`!Bz|`@xsYe{O@$Znb}sY9#fqVqkLj-)GYe z`{N1n850zseGmEM_2Fq^o?kh@+kNsb0H4)AzM}aF`}4hmd`QcFInBlA&-c2qqs#XP z;Is0*$tBO9?@z$D1a#(`vkN{P5USom1J42%pI^T{@PZfR`7eLH91K27KRjxXSF(C( z*Yc>}9=qT>&&9WR7kuA$@s;j^@2}u%0-gD;bn*GiTRz^98!t4A9r(-J4?e5Bx4Y!| z+hZ7fTR~^O`?~o2`TiMv#hUI8{Q1sB=dkkqwo9Hr--Y121a#(G;o|e>dq4P+&4d1Y z-*M%8mrI^MU!DMPALz`t#>F=xU+^u}ba&v-_wV4d%6qptd{%jX;*#gj_X6-O0iF4>zIi39mj}VO12pmtkqH<1{QC7w zA6F&Va9(&Ai^2L&zypol-7<|Wr%JLp3 zxssRn8{o6%AImtVi397EoxP`|4*Z?gJ(27CpelW)u}_}&BGG|hL~F8H#r=xyhJAc$a`ay^4@pJ`^PTiP01adeg)vO^m}I)^7bF4yk?iYfA2!x;iHsyv`ZeA9ORX( zJuVofyc=BdKG=o4dqyemNtZlB4*<#ZdwrDh{^gQq?n2(g1*2=%UWZxp||jzh&UF>JPWp zJC)ZkN_mI5I^Zg=Td;NtVkd)g(h(8cGM_X7AR zhx#pY@%iNi3!M5Zaq;=(Wq>aSbjq{tyOOoja-Q9+bKGcKGx=+9hwbi_b6bB$vEu7oT6=6)t&o zE%eE#-x?R6U*7F5c?Y`q{PMo%lE;3>D_Q$K?vl4ou_bMfr@?2{-&z;n4qyGf z;gWZVi_fp$Ti~YSb3v~9kZWo_lzw2D``dxf}{cZu@5-qRK#pjpzf=gb)#pjpz3izz{9dz;e z<-O~Yx6Q@pmlxc_X^*WgKEJ%l;A6eAUXFF~`Q^=Y$veiy=a;vaOWuhtKEJ$D@LA>k zf{X8QpFP)s?<(M^ANAzL@LAySXJ2pZX~-A8Y=n-y;**!Umm!`5jq)nB0Ln|VkGAUU zE$m;MlT%b$Qe0A0w7hg_PR^1XUg~#QX{m&YN|zRsmXmW>+HqBN)p!?LXGz5hJj%WL zf~18?MGklhL`48_;b7NbBA$r0Betzqg&OYY@9mA@t6qipJXv8p(c9k{>k);WvG$=& zk)H0pm=*8t+t@D(@sTsd8riZj7U_ybJ7aMXiB#6rmPc?}errv06~6D_M?~=5be#jf zmDsU)XpkSk)tRWTrx(U!JzfO$ajQWF;@lD(9+#96$g<}8LiI`Zp+sy@6mE+54-Ht^ z_w;uNSLPB_*~X)No5=2HW?>XP+TI?IZE@389VzO-PWP9<@&P*rY~ zRk1lZyZQ@J-c3$g7TLzwreQ@KW8pr4H$Sk(21S4KgVPb24ulf+`ctu8uWHnlDj zUlyzZyv{{G&_h34L;;`aq8|?USQk9#;XhxT=z%X0=L5dT#eao|ewnxh@I1TBDdJlm z`jz4#z`u3TpYYJH6TbrdhKv51hkm2@2jKTzbh#NxSAdDnz>N>07vf)CDPlU}Z`m?Z z#5^Co0PsIu^g_Vzy5Qx2ojg*+{(y0$jV-^{j)8H^dztcn|TQ z2VN+i^}u_IS3GdB_^Ssl62UR<^8ZOp^uWtSjt5>QiaqcOvA+lYvuNk5TI74+HDaj;K0vJY!2ia#T`Xy|uO`vqfg8n;2R=}o=z-rA=X&7x#Wfyyt+>qt zw~Ft3;C13j4=jvddEkKYXAgX^c+UeLB*wWG6-{x7nB{?k#-1KHY^?IY5pjSAK2&V* zz=w&$J#eSk=7BrJ86G&rxYPru88><0ZgHmv?h=oC;KRl99=K2Z-UIiF_dIZhk)Gym zzd

      1INW851bIIJn&fK01v!X9OQwAMUMyGCXVvJnZ{`z_!x1C2R>Td?17IJcX{A( z#-kqi3*u)U_yqBL4}79{#{-Wy#-zK;e~Osmfln3-J@Bbwg$JHsRD0kv#aa)1hUoIZ zXNh4CJki+hfhQXmdEj%!^&a>f@f{C*o_NFqPcfeHz|)N1df%`3+5#l~b0 z{E(RMfgcphJn+M!$^$PkT0HO%M7sxmObmJ8ABvMaaFKDI2mZ0R+5#P{u-56ge!%M?&0@iLBrY!tUh0F- z^1-j^`vr?XRr|Dr=eqLe^ZgZr^AjnoR>Cg<{v2Qyk?>yt?*o{_F5&k9uW`YtSRCQr>H43Mp;41)M?}EPy_+r3T``ick+b;Miz&{3T)&DDit!q41cvGl|zQ&7R zh{YcG1rhPUzY^zr;F-pQ9(cC#t_NOV6iu?Ev(B)nT)Fp#_Q8P-?O2cO=!y+)z;an*9n8(eGVxGPOw?jsH_}*HAF1f?t;GVd{L*k#tR3i# zCSpkl(6(c9WaBW_a1&Sn=5po=Yav#y;1-pLT&xt65GnCeR%&#q2VD$Maxe9wEpCXH zDVCEiJavE%D+lb`)L7^De@sf*{Sh^H@ zOH)*2DO2oAQDmp^*#H@3&9d2D`W6-2DZF-2RAQ&_woy@uodOZJs8TzHWkbb28)f^t zQ`ik#;%(TXrIwkM_)5Oi5>55oqL$eyd?jCIr|^}0xs{^SYWtFR2i@>KXMbN_O^%7$%%%n6>B}k^VOG!@J zs5h-+_e|@`iTT=cWm?GfrM2rrrnN0m=4;c`gQ;T2P$`VIc{Ouvj+&J^nt^*M4BN*Iqi|*BWKC{VsF$0) z&{1@$qX|oGOVWxuicryIBeG`Cb%ZUmy9(RGQQH#cY20NaG-Gdf3G-;?3^JB5J7;RP ze1v8kAsk`L9nHu+B5MvCikq4*^>D8kA)8}^BYTCz&9wHYpcQrMn~JU&ku}E?moQJM zoz}60c^Y@+2+cSKIl@+YG-K;2nij_SQz59?$`P7z+;RzXGy`|hk`Z}xG}Gcx4^CrR zx%twM$gYT^o|9kE5^n+wYd%XZ;%Ml~(Ni}?bVt0FUsUAHfWw+Axg(6_qwnBPZga3# z7CGY>ZH5-8g~{Y2b9W|bmE4oN*FcLn{P=P&_U7&kUNU!2Qx;*)^Cx%4Aep-}T*>5~ z5vGW9r!RMBWRS^S;{wZW*Q;*pry8H<=HK%<*^I8DtF`%SXAn* z9aj&AJjZ{dp%*P3F?bg_9ZeQ*sjUEO$MN?pb?nTfJoY1BbvYxUl()=VzjRRj2|N|x z>bO*xZmN&pu@{-aawh{Q>~vhM@JdIzls7Ver^S>!rvjLO73fpIJL;|U@sG@4g{^|j zV1<*QzoTS{Jy4NY;<(JRe(7NQa(607N?YkJe#sICqVjae{mH$`xU{D-wrMi=Vk+ZJ zUhGx3*p(lX!`K~LEvA#@PwvURc!{?vY@*DaHwC`rjvvRe7xOmB$BSv$EFq&q*jyFe7U>@pt7D1Qt?_|G3+{xw2NT^L zqG@nwFeY)L8T+c5afe(V9o!5z%nh!PkYr;Nf>n~Xcy}V^O5=f72np&`_U^NqXisIY zEKns1lpGC5I#Sov;!s%^GNFTtc18yh-CMd7+ai6jSZA!$A$7*OxkS*}8yUdDg9{G? zYtq56-oS?JdIkm^v@iK^B#wINMZH9@c+rWZUBz^1aA z3MUV1(Fbcy5%xcKf)%CBO{HaxZN-D!B8W~NLHU>o`)kY4z(9XIkxaRkp8hpWwac0t zYL_|bJc-KMyL)JUt`e0F9JH~sczA1bx2r^vnl`(RS_+%n`nHxGxujuv$4V@qmNn;; zHMG>`R4lI8bI%+sXbp7t#Ns*YmKT;RDJ&^mv81?kaa&1o&c^maNQK)Sol@^}>O8 z|KYKYgtaml!4AguXi;QKbV($(HQGDS6WdVLhSD#hO_sG(ZK&i%(G895hi{+~1Q5ph zpibXDXp%%v3~wBVnfG<$~p+d=h-1J($F98jrJ6F&C6L-`$<+)Vc~}E zz7EvUC!Gs2!k@G3JJ{c0HKw%$%%$W1y(0aC8#cycv3LTJ-J83mMr6y1!lJ_B4K4jc z@s1eVrw8-%r&Bo&jnQ`WK3Mt&^uI_&eaY_8w0|(t85`J*wdJ(1GYmtb?aqBoJ>%+Po z+kGloquFk=4NHNZ;qHMA{E2KSDl9E5+Q8Y+_G+x@eD_z>2tC=$cb{9KhvS{rqFXzu z`lqj<-70nuY$^Q|wz0<+cUukOy|&#kc#ZVMI(i4YcZY+&Gfg^UTe{)g|L+@Sq(U2q zaVPsJ(q#9~FiAHf?e+mPsa70``UiRE^S?JGJ&^BIuda^W=;iKQtKI3;?NY18wXH49 z6}zD!cQz(qIScPvw|h6`afcpOwtFMJ(SZ%|{@r*hWqA;TgFSG)dZKYe78P+#boYBV zLkX_@c4HId|GrjK=vq}^K$0Kxkd0w{0~coaly$aTpJoIX?Wxw9qLI87Iuk%ME`Y+ z#N#n-=TJw&Mt8fh@R8!(eVc4zt0cB|aZiUM>9v3~JMI)9=SkPUdQIr`03I46?O=C@ zWW0!)ycdAa)fwAJE{RwM7<@K-G8wx2ZFmP^c-%JPuco7sl<=~-6_1YlA%({v^>(WQ zO48ojjLqwFIg%bTI6sFX0jd)sR_hR!vxv8HJ_ z-V!^aqPG+BgFhttRHCu}0yS;pzG_5=68(HCPSiuUzIRjT`G9==r@I4~kWBb%Lp_0y zsORxEJ~}uYWziMaxkhJvi+sRNQ9odghZB>)77=Q#P)~&6X@INO#%vF0Ok|*ITgOH= zrqcHYn}8iqHuj3~WJjY&jUQ(=cLs?XkUTQqeI(qqjfo^jhelvSj>9#lI~I#!J4R8F zt;>lSasrF=4@iw=+T{Z{X)4kl!!b!V-$aQ+b=a8kxU-6Q1j@w65zXf|gRzbw>>zSf zI>;IpxYNs4^vNKZqXR_9x-TPa`+hchDbXFOab=OOOeIEcoUnyGY7!TVp_9t$mKuH8 zkjRa=i?F4AMd7yK{Vw99z>6iW%Rww>I@Y_>n3E-n-9agPrcBe_-@&^VX~{~~8uInT zoZ9-AWbEzfl{(1$E)SE5C_X@;$iJWa&W)ktQzY7{kjFwq3=D3zbG<(xF~jkWL|oRS zl=nA@J)&P~E30xY+2XN|Ep{27lX=FaCCNlzlX|(^N~XM9R^(RnUOVZ%VZ3{W>ewht zFB6|`@;RZc){oN&vO|EgPxgCNOTRQE0RsibwIS@Yv4x&&$eZ={C`Kix-^gbBO;C{r zv3BHi-?u_F(VnKRZOufDd4ioG3J}|%%Iv*#HswnOIv8HU1Lsf4`K@shj+FH!D!W-J zO29daEmE`zk4tER?n!!c#u9p&Xs{ll?%OLU7)C$rkm z!_&G|^4N+Tq^MP$9nsdIczb^Z-|n+BxJ2ty)28;?*oyv96B_!v2V?d9*n!s1@m-ao zNXcx9Cc4~(yI-e~3b>2+!Jpo}DRf~#rD~3H%bBg;S9GH0*d~mf+!s*H{N5T}@tdFK4QB7NI?_vvlTeSXl|^JG`H3%)tYJ!s;sw($6GC<$u^v%5I1ro1y4OCW?F9mFpR$1Zq9*#!`24WmXGEYDIpErhXGv&>p<#3FZ`C;!FR`4;!Swa>1 zjNstr_Mwd&ixpzoa5rD69^?qGFn6R@WAj>bYhw#g{|fbBo zbfIfUJIOKmN=m+4dXY-0$yX6`6j{{mkIglKxX!3dCYkP($#m{Y(q{6+wBw{i%_M45 zXD%7dx`QCH>XzV%Ga)S+F0;+`8jO(8W}u`A37}i+We0wqW0I ze;mUUwkg^q_`7PD0w(KUFW!Gs=yGR-Qq7$uoBLPJSfrX$p>J@|y15kox6Tl?Ni~|n zzv%F*mQ(1*;V|mqQZ=7K{|;WeMyvKw=t~_HomNt$m*KAK6a%sW6ncS;byu{a{c4Ou zl(ntUe|AcMQx%jYD9S@{opl~oV`xc^Ub;yX{(MK&a3@Se^7WmsdL?Z8bt?W6~83f*hicd)Lz&}p~6`|le=Uv+5x z1JZe(_V>U2=%&y{@q=r5dJM0iG5QVZbf-R+11$dI{r&bKNamLQ?oJ^>!8;98Z&1fH zCW0Yz2+I(1!#eI$jHLK7I{U}RDk+|%E;2`BKGDh!4GjKjx5 zKf4S+kCfr(_6Gc%IfS1Vrr?y>2aQlxt~&!J%aLhQ#)M|C0bf5(uL|ADpTaBfGZEzo zeV#vi<5;86EsQ)qBCQ8CRfz|UV0J@i_n@Lx*9hSPD_ex&0voF< z>z9gv4}+I!O&6*`Z8he5JoVA7IPG+XffIa^Dp1={-CWkv+Pt=+b#1d>mD=)_7LPKx zgNjG(dt8rVWms}r+qb!|f4I-1o;L~Aacf%ERF5;oso^=)u%f1{p`oe{uQgS+*2sGC zU?O-8S!8=rcstkdq40iez=tZr>)xghw_*ueLj--;`m*&RBxd}Z>NK}TnyY0S@dc{d zhT7KJ#s;Yt&_%7q&Cmw1;?~k;C?R9O1sE%8Eow%N8W&mJTvgS8ya40&VtoX;sJ2H| zg@RFfH}970hTgP6)O1MR{mtu6L`f>Y-(; zT99?5K~#^dCQhB)q-AYO6RK8c!q%>>#L49|mf$qeNO@U9rPK2z)YuRy zj+Ek@s%#5fx*FP8KzSp&LqLMGPgBe|f`afyTzq3>fNL{YF6OC897=%d?}H?SgFRR% zgdCzaZiECxdf5fVjN2#x2HsLp*5D{3p{BBymfCey5Fx>`idI5~g6eD6(+K{E?9mEFWGF0vZ0-D1={R%g`ISckVeTMvp< zwAQH}*c!2Mb&YKisHgI>u{E`;Ya|-_KCH!{4%VWO7!mkBsU@hTt+o{dLBnd+grX~1 zb(M`Slp$u)KcNZM)iJl$_03JKEX?}ywbj)qLaU0HVitXY+4tj18JyT>pBNIe=ma1l z<8%WMnL|%P%;LmuL9<^*8qV()Li2Fm_n;tr4rg#Ni;jg&;#59|KM->Tw)fz6QY)X32BDlEoiB~)%ck}F)vjVx-8@-R-@><*eHqTkC;XG zXe3^y6tn2p%>JXv+Q^}M^PoXi=3$pBhpwK3_YvvPfp~8~x4H3T>aW)lsu0>qxh4|MH4_qzrxpyM|6Jvf*%zFqiwldPQP5aZdYj|32#myOso+;1_SkN85k28^>!r8LC% zLtN^K_!`7#v0V{wLtN&PB68U>kceacSxYzKpYd%7r4+<@o~h(U{9MGxT&sA#g!muv zKR-jvV>-#7A#&NiPi4;^E9Tv6VG;i!;@zwp#D9kP>nfhh(@arM#7_|OCRp)FA~zdxq@0fOTi975w@6{1Sj20s_v9w%}yMeG|UV)L%IcoF}G75@N+e6J0_Hh9XZ%;|%W zSil>}VBUiPqoaR_PlH4NT`&Jq+%HGUD$AN$tD41as|#o}c-#>09FE3gbfU?ou=X<~ z`MePLKmk=W`2Hx?@NmNvm>2E(AKuQ7E0}MWBl}Bj==4TY}7F1&JUP7#)N{KrUKL5uUX!mP_R4~2rj8>)Q*V-O&CxN zuFGrGRZ|My+7~Fep%&A;f+Lm@qaTuM^k3!|@D3SiH(`l4zD=W#FDl^evN52Z_-gE# z#Rcn&fmM&YHS&Uzf{Wl@7`-^p0LO-y^RHW8z?qlNZYuHo2bec1RE2TrlaGWVgGU1U zmx_XW_ELOYrv~P&iURf^qrFm|eA<%e$8&;C5MHU$gq9(!P2*8ybF8<2izK~nkt)!3 zmED6}(rjz2X{l$vRRy#Hqj@VuG0Q5YNbO)8>tqRjOAEPL$X*xnpE_xke6nALBS-Lv zp#^(Kkl*BL|oS4FOubx;!s`H+v=_1q)uI|S}+E(0)_w+|ul=n4*Od0LO0VX&` z1?%mbm;vY?SOQReiQ2v(ropE|yt~0t8QR5-k~RE#bwTJ#s4;+z z=-4#|o;RxV9yE-0e8mg(UtREU1E{<^VqxFXMFMlAqo;pRR^geet#Y>UG@WhWJIg{f zZ*9iS5Q>deO%l#top)l$7*I$1$g^$Gzs>pQtj>EUXjtvji%*H+CTso$S_)FfyL-8h z+nj%i#_6=Ghnw>+wOIM+*g{?=r4Dozci;Le1#%lJ4Q#Bjl9Z6wqwtQR^htU zdG7~}0c=)6H_-iMht6wb_f~m24z#*OqvSJf?CRJO>jCywe9nT)d~&5(K9Ug`1t+qr zDNI*q4+pKhvoI2|<8f5FAz*Ao=i(|d2PTovHBX$#!)IvlYx5_Sv zk)vA!AzO%5`bNM|=l|&aS=2l4?*T)eKD2@g=lxqlI7SHLecth=BH|<>XqmSipgute zs^y(-fs%V#-WM%cvd_%B)WnD!?To~?Mpyud$jSS%sdT_GM;0d2dDx2>pZA`@{)+(%6Cq>@B0Az7kyxCwVo*(Yc=VJ|x?)Eb&?zI( zy_=Xi&4pK;SMg_z#H$3EBHw`H$1OtkF0M!y)QJv=8F_zzD$xOqhO)RK?w<5}#IRay7K?Rii5CAPmJu~pUd?LO?Y-&q&SeY}KZ)JbF}% zV6nfi4@Szx}wnsccU7Wug|gea>}H4GmKGRi7yBW1PKEe$d?5EX4Q zDJr;iePff_UkOY=!eW}yo4bMvrBiUL(R}q673yqa`2DM zdv1xLdr1JFugHG}1@JsP;EX7AQCbGOJL_V7n-bVwi#oeI#~=oA4B=%neF@ca+uCNV zqhZApPdfBy>IhJkQPV$wOiToL% z2XyrJHQ{l4ztmZALB0C6?!M0cVaT?!Yf%q6nOi{Ho5_2P&vY+vMH1VTU&nsuj%;PJ_PQ{?B*wsI`5Xizy$R-D-U z49A^LB)1u>eh-&RemXVM(LK;5?|EPfx{Hv!QOR#j<1Jza$6!9n(|6$JA5`G*xeMeSPD`*|^Wlp_br&VpMh3j5BGjPq|o6|1(&HjJ%#_s%tH ztH^z{$mgA$+&&6lmN$m@@>v0w0I;tcBO3c6>RHpQ`$$>$aQo!Qm_ffy&b@;;b_PeQ zA<=jyipu^wnbE^>Gl5Z(SxzhXIi*{<1?JBzcz}rV4tbU)UE47tpLdh=RHY*`xnK`+ z5O$n5d4mL0q6^-J?L1{jx7u>3p@_&&Kossvm8kIre`K}6F@-yU;Z+v!WlX|gfNKdb zn5RK_W_Sk@cSo9_J1{vrv0EV0Azcv+8K)ovOw>p&U#ArCU%2K16yL6dVA57fb`BTGtTctLR*J<|0wBWzN&!gshiUM@7A}}hLvL-G1uO?J(*o$$ z(4GDZl;TSfA}4Q$X-M^;Q^87FDRwG>H^_wDo$OIkBh*tIZ@vP|P_1CKfF5#>=#jJB zOp$*&vcR1SZ^2jv+(Iz0IT2AGMHW4(%X332WC;Zg4z+899#q-~hcPICL60S!T^$%7 zHcMYgd1tW%Mg_|bwd03&>>J{w99BHcwyQI)G}B(74X3I803JoxomLufVk-;^_xPxD z_Xx%8^^n!Q1$Q-^O|le2cZY?TlK0LW1M3KZ`BORYf`I5k#RV(w=LUaZRw@i#e!S+~ zEr*G{6%JeLBC^YTPQhE?C+cZ?Ro>5M8J)5}?!zcv0EDaxM&({pPml;O9> zu%8aTkGx3>Y8+b8#*J6FE!{5MOpU`Wnax+A)8cqq8&}FvXTTY9q-J{H*W%EKyL< zCyI&+;wNz>_&`8Gwy>zE@C5a_KRjztF8d z=;qZAWw#M*uq7!{8%OFk8-KT(*H$bi#}2ppVS~y59&z&;i5anp-0H{j%fM+>_IrLZ z)_ar)>3^GfR|fc0{`?F*sb~ zhX1q5)+r<#RVNtGXT>Omsug4M$iN6gPa%I{sLFw1S_9bCcmV^wXt$H-0@zhB#0+6P z^9Mve_yZyz`~i^<%kgp<5c%K_hT> zF$YD{9)_{~bQpRiYY`0lOUDun(#wR%8yi(Rn1rT}`#n&l#O|K-@Sfg2Oepj4GxE4A zTN2a=PO{AgFV12zBC{pI(%cTe!ZccU%W!1ZK5Ty55KovcTW`7|zg>Iv8Xuy)y4eS4 zuTErj->ifiO>qF>=x9tdTYMO{_Sn|PC2};KVoy}D;Pm?FVQiJVBOkrpNAK+K?cFgd z`Hs9glNw@|gyhLonE{Msb$yS{Z=AiRdNM8QcS!BE7r_oNogAVad?aXk z*$~#dZ{5m0t;PC&bx_AIbUm4BrF*+6j6R`o1;Y3!6EMYoGFU@*lg*9^;%mIey@Pvt zy8-yr*r|c}bS~zdn7{%08HMiR!YDjn8w~$xIvow|$=(J$e=Zn)6jPkNGG~E7H^}d6 z#M;un$c~*+D9vadkR1$$Z)vx+$jsUZ4M@*yMCMy))^)2NQS$=^Z$_`HiGf~#->Y@O z!$8SOV-+G%e<(5Y5g)~Q2?SS(>5D#?c3ijmr4rTe^cCGOt@hBg{&I;bhgpcdTX)d7 zQ3vz|=dQ;JU(^KM}Sb zeS2U?=}Kz~J_T5^QJ+=C#0n;)LmJq9>7=6BllI(a?eH%#3gXEGW{Dl#@?bRdB2B=1 zHhbXn&GdB$|BC(8nA1y1h9pC@BTB-G48%`5N!%o$q1lVMPYXXK$wp)hG!rP!GrTv5 zN+N=h6Aq77I+a4_`Lx;4n(*_kz1y7Mhj|SrNlx`*lD439oqJmQdO6@MB_!6D1B0Ff zAcAX>9#~>XiDkXJAja708^eEb;oHRQ zQ*`bu%;xaR2BT)tEYz0pg#k&_vwhbNq|8F~vINrfJJWl-b0naVjR8g_$~g#5^^Wi* z0UK7a<~>;6*6f#UkYF4lkp~_LUuMAYllE<0-!+s;v6m=jG#Yho_)0Go;^?wciNXh* zNtTa;z`ZJ9o0Bq<5gu0Iy^V??zY6J$`7McaFECxlCqP1=;WLC#6+aQMVa7?z!p9V5 zS)wm~CSbz>fVn&>OeQQ)VI)r&6f(}ms|5txMi1s4zRU3%JN&r-y%J`bCYQy~VTFvZ zwZmV46AwL?9Rz^S31H_oNKb_vu)|*s7(VUph6@`+U$(>l98gDz2RmqoZ&R`%^l6ga zY=`gk$UN9>cKF_a9p~yJS-aY~r=Q1OxYc*q;Rge-S9SHq8-WDmZae%iNE_F6#?i2n z1Z~LPM3a+SeaH?!s#yUa-93cCXhhw|582@#DK4GZmXINwc_Z1w7#8zU8+a%NaF5vG zUwb%gm){!~yK)SY$)kA84nN^hco_>Wjfh3J`X@HMfg(WEw+rCU?eOnRCGQ%b_Y%8V zp(TL$wX_y%U&I*ZHD+-=1{XMuiehyMc&U$@=wHVA33Yp$VTh)|fdO>Q_kg6`Mf z+tz|b%V9(km%0&yS9*qZE>59`JiWE6e+QR8(S>JmI?m~Q7mxgKs%;W z*oX63%b1N^FG|1%VO0Mqz6gtX8F<;!iCB{C&~&vo4}meTq#y5B-QNHx0M(do02GyK z9UI!Zoa-933^W?aBw=W4GwiGhxIn1ctLbQHYb$`{pa$>~;FCP!Y*Yi2!0I?RFxB}nQ5lzJrkDN*((iDzi%&R+kMr1q_Q)a7Y-VRvvQ=l)T`Lelx z+jf+WB3#_itpj`X5me`cs-oti>%kt`bOw^4EVSky0A*FpCr42VBtr?KBv-h|e%;%4 z40MB6Wn97Z>1L*lX5xLQeKn=lVpYU;bVP2bjh)&!+7b@qehkv9lgImDCjr3c37jKl z1V}<^1Y+c;5-Mh~U|=X>3(z#Rv}W#gcst)k+i_gIhcIJ@FP`Ir8$_)>ji&WB45KL0 z@JQ6y>M3wc9!;29^r*bKp~^OoOzBobirhDY--O>!*Hn1t~X zWRS|!*+{gM=in&}KRLq|9OO0N$gMQ|F&G8;76USd$=0m5(>JoQX=qU2g*2PTz#=y> zJ8tn`;iWV0=^X_h!7zpi9NPn9n^!O|)ldfeTf62G&R(RHD9Fg(L5?y9oLumb0UvZ| z833*Lbh>tMyt_?c+}R%6df@pE|2fA^gptid)TQ$4G(Os86<<#K2W#vK=d_m;IGRHLEGD$Qju(6dGJn z#u`8oi?5<|6`KxNPe&v%wPvGrmj30^@DFB6uD)==Y_~N0?J5BxyJ0Mv$3f`$hKJPY zO_mtkZeWOiUcd-O4L~%*6)c!Pq|H=gjjZr@F-gJNUUbL+fA^T*LH-`WF=>!99vE>1 zd@e9@2f2DK0hM*Imtgr0=8K5VH8kBlyXchl=+xf<@9o*@#u3k1w=VaQy;zV~XQSOX zQx4%C*jmc0^r;?iGKKPqHA09P0O^DbXs~yedMGBECKDR8WY@6FS9L^(21fnZ#DmcV zXp{oUGYdXLKfv2jAl)@Q$U3UZhcjZTcR)tMIR=MW8-jnj5Ri(^A~e}tV>Rl>8!&dl zYXl>x43Zf*j)>z;ri`K*0IDt{(}dh}m=QdKyU=~jL?BK6 zqm+=zv3>*4-OykM(<4LlH??Y3Ev#|`6kr4#=-iF1I(E@9+~#95uy%#1FPkBUjzRf06Sy`B77usR~8cs>bjyzxfUSkK8tTZ^`Tm@lRGjXMe9kz{K&?htH zqQ6KH99&C*R3-zwSAkeTNxuoJ1z8-zER)gqT<_P~(!Lh+u@skprYser?YnoT_I8|w zPVI#msX7TnG8dt$q#)Mq5*0lsVPOpyKiS<4yNCB-qpPi3aWp9BY6hCxn+q>fcrp)O zu5eC@`i64Tj2neZ?#;ZZViY!&g&l7#_(3Dq<8Zq-;pv|j<+)u>r*U;FHqKK~NK}%>pD3Nr|5fZO zVrnI==_`v(d$U;w;EO_eOh625nYPVGrBgqQ2hpH{dgUV0(PPKRAX(obbfUO9)n}NP zBg3BI4C)HH1NEmn3d29$W@8dBt&HBH77^ASd(y4} z#Kf{|WWlk9U}Rrg^Ent^24F#jq62~l3d28JiAXQa=b^%y4(f)j{kzVJiJ;|t7Ie>Q z5qr&kbQns!fdq0x)h9@DtZkOL26Rjrrn`E$LB4=Kq@XB91B9w3XvuM_KU`GvB@P_& ziJ~ZYYN)7&Lj#B%fTCcO=Brh^%}42!;u_&mB2CTv>8{O6myObNpDW6+o~gWDP{*me z9~gy6%k~pk);-|2#TO4pgh=|XoeU;L%jeM`h6GKtjkEf%jz-lj-MO91We#eU4eUT@ zsI=$Tlo;uuHfVj_?0>g$)|<+@&4)s*l3E;w4?~#8$9R?DVeZbVC_gW)r9)Y`oE#PZ zP!>KXU&TMHqKHbidN5?z!&Fc|Q=uThw?C#pG6uu@(`Wa0K?3kRXLLYT+~Y;zFCkV1 zo*uGfQXVdPvrhfz=9i6x6t$4ChYk@T#KUho#lDM(atEe$_8 z-B#HE1Jo7Bxp70f6WvTYM^#qHjE*aO8K#37W8Yp4#qw-@47mZi^e~}_c*}R*ta6+o z<5WYe%XMt>zr>ph$oT7N2TH_w1V>>HtS`pV250#&Z$8K2+XVEfc~LTi5rO7P9YP7B zLYx}dh6CvU*O>_Bzwm1crJ6n|1x%y^rynw1k=U+z;g8R;d$uFITh^M?m_BEefCDlYP_sOT1Q$<%HFke*+J113ifNoTf*A=}kb5P+7X4i;s zxB4ap#*>K*@M~GXmN>HfX9X~t-^7a3wP3^@U}pJ%hqxt!5LMDo@%0P>orxN)@gm%+ zaBLjXJ-`Y_VAgOQBD~1)w~ay$jSM3e&8@y&F`aGL69doR(C5+7=x+5l73T%uOZ1m4 z4-qS*i4GkrRNbw7mhOP1WOn$Ivut!l^au}jIaVofpC#&o z-ZnK7s;veG9etqRfrl+K_H?4Jt7lo&AyI75Pw3AC9oA5BEhpx3V$VW|4EKHVHq|wn z*0Tk9I=2RDlFD>p<8~e$MXN)!IJrzW)Ur|qQPG4#lp7W!&<`>kmw{ym8mdKtq~2DE zo-9+N1QlpzO)Z*oYC`y~+31`yGBchHph@9R&9=L_XRZVhk~avUvrhrvw!=U807NC6 zRhV<1jebvQD_*B7uCQh0t0%i)eRhnhq?FWS*k`W^8L$W@4|qVMQKf_;iHQOA&1&rv zSXVbU?{cfZu0Y1KaenPq->Q%y5wqwwAro$s>THA;2Mb1^FokG_$LyO*6@~+V9@4jB z*Bi6|PmG6kgmH;!v)~`*#ka+i}s- zPH%NhcV@kC#*jc3GBMpcW*f_)>3uq00|aA?EOA1VO@Bf%AK7a?tdf=1BYut9z#qb4PnBzAb*9rM?0!v<4#GGg4-Fuw%Hc=fBmdM+z z;_ZI%bHH0zO_9XfbT=p|qIxsANZpf#0p6;7G^r z5yTY$29Gvq48%||dW1VwK%N={tu9fL@HbYwianjCsnp~8bu8BREnJ1+2R?1f#vMi^ zbJiszW=g_Fv@dcjI14s>vr4)ueEny*gc$G8F38ct!^0Y)Q?VY=h`Ek%dNgjVfc91^E~Ygo+J(tZuLhM4bL$5 zpECpT$3_9ujCH+Q!Bmh8;m37H6K`e&qH7cs9l{YQT|1&!i;5B7(TN(I5?ncrO2K%L z2ZZ~=t^T~e!+06kXjpwD9&4%YZpaa@$Gg>EP-2kvQrG_?9L8s$^?jQgCVEbdEF)Aa z9PCIv5){PwecmXAbPNy6M0O*X$^cFB+dlr$*Vw%j!l-2^wIErfC zL-QDVZB!}(yQnST)}?UrA#x7OXoOK=FPBfw3#8N;K_)gx$#!&~r7WzKf^FNud9JZm zs^LLrCVpj@J!lqtFA~iiiPXuKulAp4=tOVUcb#NF#$&1TQjQu@a-S=l>SYFUHuoRy zG4c&Fgk7@wvQBZCB`(Viw_si>@qWV`By_Op>fKJa06NkhcHUJc?eM@>cps_6tkze{ zW{T>V)A0xwN)yr}$sz)!&pR4{nZoCVHlkKu&r?Cmk*BKjLHsW+sY@D(NMVIv!R^G$%3z@Y-m>xg12QTXwCz(fT-)1Trd`13HZ%b0g< zs6~*QZcC9{4YjO?Q?GBrfO@+3tXPV)3O&<6y|r!%O&Ta<=rESP<29YxWZ3_qm)kC+ z3~&m^|!?Ys$->6H!w(o&s-{TEk0S4F}#D@EhOI^x`Z~-x7 z%eSXx<>OY}E@g{(s?$exjOK-FUZCOwKFzq)v`W`jo`(ftg+W%hJs;n&kGdKN-jg8^ zMXbTZVZ1jByk{uYy`xjMqJei%vXgl3{;z=X)EUGcbJRe2%oH1CAtv9WI(nOwQSpkjcE+boAx9+V>uHEjj44S^cN z_PkJoY54FeZkoX*hWbY1PVm!7q~mCU>3-stCT|i`=_ks-G_b`u?K9w+hO{BId}$hm zYiVl1*UmCPJQ#9-W}78IdJx#ks+S=-13*$J!1 zL`kZ72E~TpbROqVP^!*1P}E;Ex;7n+Mx=p&2pa^xD-W?CJ4GVqHAGD|eFnTx!O^ve zm|r1_46v0;$=KE$6>AL){=sfB)SBAVDP$r$QfE`ZUCmuQ*RJi5Ug8)C%QXPH`og2MovdsV=XA^ zLJy|A^N#N7OQ$&gq%D;(Tl*2Nf&qk_jTb|x+{h%{uo#=9#La$z+tP@wv1+ zOEii2PIpQ~S{i$vh@ck6ig>S6z$BD7k9}1b$pQ!j-@p(= zON*$_7;)-XufVfP8azi=b7rYRhWM3HJgajF&k>`VuNJf6eJA$x zPNf)=K2K``c!qBy2P^!8>9!|8AhBz9U~5vgU1K_mQsL}wG3H`#7IP|`^^mS|<2$iw zKgR{xfDq5|lldITdS)35GtUj?n|%5P;wwyTr#4C>cr!?~Ep;o{I}?p#2-0{araUY# zQ#3bY8j)n^Fe5_}O)*uG*wNc;1Aqk6XcK@-Un0g55o}uSRG%Q`6hxL_MQ9tm_*A!5 zxTXx$t1*?M+0b-rbS>yrnC4(m?ZC{|Qe@?njwOhx3g0lx&Pfqj&+^O}BLy(Nn@}ZH zPRit7j8h%_YPW`uaG?ZaflMyHEDL%#ayfx&rBbp8;?TmB=e^P>n9F2jGPaMZ+orUdjeU0eCqB zsK>aAWuj`=LZ7MUIV=p>@-nSo#kXM1fsu3QUpmpjkWS|ymK&&65(UPWjI!3iAfTnu zG-%?*B+UoCH0^zTy&N^%>MJ~uydWzZFI3F*T|@k#qVOMPGddt5vqFUHN>msY?EnwW zAesR`Ruum3ECFLhUN^)#b`+%#6gn#s{dP1K(EY{XtLSfNIWwBBUtPDHtM|WL9R4q^ z=Ff)xyx91S`BJDyLta$L1tl4{_azW9DdVKxUlM+wnCanz8r;p9_?5auzZDAJZ)zv^v+3X#SdOIl!rGnke1W+G&+)3Ren-!_@- zs#Am*_`&LBt*v~cpP&uKTdu$bRMmA7(;4Q0Rcp@y!vsV1TNQZ89%fR_N4=heLhAvU zI#~#4q$bihyifKjQ=cc{Jh`W@Yl!QSO<~mWLY!zTU>oFAllfArN(QJ?Rg#jq%sW*L zBd}pZdR-@Vb&VZXSgXM5tpz6yqRyj@9qCju0wITER{WVpBo*bvNP_v)f+X9pfZ9x+ zJ;P3<_Z?V`jCXkRr;HQD=T$5=+(@qqCK=p1gm1(W16Gh2YWU1WHKuB{)=h+hxCTL| z^_>M&>S-P$4`5@Mjq1H&#e3Q-eMU~T>ihDo&FPIP9LdB}Xn;|DKO2KnYk*L7UlFJe zz^beTJfr zG0oXMtZY2o$x=^gaw8Pd-15PEC;Jz`d?EFEh?tlX=7ti z6FBDD3Q!X`DF(6SAiXE5+LFcttuw7ELQA3?U?nxPy!8enrSwhHY64Fs(rw0Tw6Ug? zYZOsH4q2QXZnhSDe*_=hiF1n7(Py?oNLy-rjtw9lie5G@uT4TtpDjDe6>fJYDC8NN2izfTEt`zL<%%J7%LA)}~dV@;l~e^vQKS z$XL5)q#b9UVQU<}V*=1%0NOKAY?;Dqcf=zmE$oEG=s-i*Fk<{E3aj#9bi~igg7rZ1 z8IhSuOBm@EXZD}s$`}Yl0GDQfnMexV-3kXsVRI`APG+eaTxEdFQ#ZN7SyNe*y3thz zNJ@3UV7|@uGp#%&fvcFrY6Tz+tBkqp7`LpcoGgl>%ayTwyWaStldZ2_afCVK(Z%PDT=ll*_>^SAh|c(am>Z>&yrW z1zxn$&_KC>08u-HI<Qe^rWhLUAAS|Nvd^vRAy41YEIMpdT zJT`iTO<4;rc^5ipW27^ch6Indx5EL+T9In8urmVIlr`Wzlxu2u@4^adY=Q*O5!b7V z_d}y|^DuOOs5+E~;Y6x1`~;S(VPhezTr^{BsY3V&@eZ0Ag+$Kw#$j!_#t3$I(eZ4H zHZR33tXxRe8~(#AX3b)D;dy)3b2pxcvYt7nog4noS=i&de^&&f0?LE=;eMO2$nL!v z5W+yS$T-kQfOv+?fG$wo2orFu)kboVVXq6Ly*6Mzzq60Su~-)nJ0!E0w*FmhU2-}M zh!HI$Y>0uJDol(%d=pOx10L`P504k~-%XmSrmbsA3;1R)CJK`cFTb5MOk*-U2C8c} zBcbk4jLci|Qr$fk&Llg8>0imY-nbRPG&%;y6E@SsEZg#yy(IgF2VqY&F`gEigukKl zphcvr#SkFv0gZ@tPXH!Yvf^Qd2Z*Fu?@;_aK!X6d2pIc*vk+ixT}T^`871Fad&(-3 zwSi*G)sd*T0scc^IHu@O!9g@IsigIGmmS=7>yXfbmzcs%v}7*L0ccgGx~>j zf?++^&97(G@HA-bM%FcG)LrZlE-T!53Kd@~>@t9vE0#aW#yo{J@5d?8_Es#3H$elO z&fmqjSd8{i-V)oy6OuAh2^}7Nl)nX@Dt~!HenjTBI&jhMJtkf!!^ypg4Y3AvNfcCP zWjHVdepJ!#stogT#2Pk`Im-KpROMJIvk)YmYY3B#Y*nZeUWko)pVe1HOS?G^WS*hd z4Fa&}a)if?Afx%F4=Ey|%d0rJ8fk8J#}~&E4@5!=on>o^7H?Ze9bTimN3a>AhPgxFnQV+}?dxFKFo1)u6r=-Y0%ko7nF`b~lTS^p?c_HdH3gC&z#BI~ z5-}v+ut|uJoNm4|zIVhB=xAx4MFoG8Paz3(uzd08U*h1PvwuTBc{a4Bt_0 z!4uJu);JEnP-w#R!-Srt=Ydn= ziN_b919>ye@CR^K7y1QO#&dIAT~d#4+U#MlHa-`#MY72bS_e5cXM~rZq38OM*p?B1 z&c2@LcHKdEC^ANw{fTQGVX7@C(BcHtKU^J?;T?gQ){p@w}GklKq&gn`+l+`SB9 zxd%r0ZT+nb1jeRQEKR#J*700TU7^wv=NvdAnmz``#tgO=g8 z1h*m!qQ0tZ_s#qx;C(C(gUKuF#-hEd~G9d1!eb0^Dt*d zAjiTFLH0fa_G_Mb57IGz)w`@{Am5uE! zmlObf64(PnHjEd52pOzW%ZH_#LHWoiXe*#U83pYh=3@R|s~HV@6?4+Y*n2HvJN7nK zb=$3eTFFu#+3yr!G%;z9vt?JrXB07rSSB5MxcQ^1n0vdn^_V^?J&K=-0D7sDmf=(e zn;5`SLQLXF;6>4Ap(Qb^<)tGfCIQ0hrQUX+J9_2~bX_L{_3RtyqpFWJbc@&5l#?ak zfg{p?(!%9~z!ozl1W4tD7#DDWp~DVyt}n2ln>%s^U+gL12{*9d>OQaHuL zd?ge0@4=PIkkDM_9kUGzbPZhHy4pG#Eu_#|PAR(q-RqBi@C-b5ky%!}?ocv<+LiU(H z%%UopNdO=w4W|)kd^p~rjo-rQZ?hoCq1j@#Mf(j%4{3$Mj?3fTJo5y)i4(qfwvE}N zp?g*fmh958-nf&0T~zZp--MwR)-0ukck?F5Y+4IU;{7b=4HJ{~cKx%C9NPcr!%`FNOyRt`nM_J9yngQ$%9`tkN zHIqdrhi^bjb)dXvJu}GK3mw^hxx8k#W*gl)Ah9=qKIPG(@9Mtkd1LM6<7)2Lbg;H! zmmdiyhhWr=b3|A|)!<-sTSd*sw1|uX_G#}>g&wQT1>Qi9{h^AQC0cnLfH8vv@SzGb zROcU0`@;BaxPRtrVi#1B5i_puz&sy0yksch4TU#s^x>~K+*jDP8)mzq2&BNaU1kj& z1lEK+J3SN)wWOILB8BX)oS^Ap&F`msUdOKwWx-*%pluAht$4r~+D4+8>Ff1%fPc~# za&ac6CdQl!B}8A!BASh)k=3;mYVKjxas<&n!8cqtp=J#$5u(bQBYIpgp{9`SmTdF_ z^RQk}y2fo27?Pwy6t)QZ9+A}rY6H(ju>WxRCc@WejZWr;u+K7;qDdR!u+U5|&P#;p z6|%Zy)T_8a(0|kZ(TVg$@6&|v>~UvwzQof`F3UwBLl1NL=+c>Dd|(#+!;$XcfgLzJ zR@|O4DA7KHgKu-OFh*w~uM{LUfZK%uB|qhdbC9_SKQhY*WGI9ETlb|U93U-2EnfLi zEolytwF&<-!VN(2u_IE*Sa?JV*dY+lhR7bJ>Q)6xx{&w+=nTS94_cd|pXMNqLCdZF zq?ZFVkZrQHFB$ThS!Rr;(Xod?fneQPMiCJ64BULi0(C&4C=Y#q26%9mc0nT$jH}7( z7q=-ADO6x^0z9(M!yi&SUEUnJX==mQc<4uFSqlzcVOf}_$*M6m@k8|L6h|$Gu{PE? zrZ=i&vFG|Q)j$Ztc%y`}sm(gXDB^?BJ$sG)vagpG2;?Kq<}uK03EGUX{f(&3P`}5Z z(oE!>Iz!gA_wZzLhFxw(%mB?O(S_6aGMY{fPomMx-Rd?@naEh*@S&Cy<`GLJTF<5( zY>rW_{1mv?FP2wrDEaiPbEMHQel3@bV`_x~GE7Ubu&Z=_FKo%F zUSJ$`!h#7Lx@N3#o~wdxKA7~VyTSLs4I5x_k44}-WK23>E3E}Qn3j9B!!&BPH2-z7 z=0E7xRg;UliGD1Lz#*IZ<-~QmB7ZOTuK~m{fLWs>tg#t2E-h3fy&r9`v$ zS)XTMc@sshT9EzL;FR9Ug>V_ekGgB}5J@>@dQono&fQrCBi#_rOV`PhU$89>O;IPR zrKJh>-=?7f#E1D|w^*;xhrtd_V3vffFw5XonCDp)>T5p25FAD7)J2>-r`7bcmK2j^ zcTh7>ZW6p+P3*gXSh2Ki`#Fqm=#u~wUB&06z~I$4)QG*wc4Mia*`%5#e_hqb^!2&v zn@k;H#Od$hXbS}c^lJFw>Nn1Oedh`QBgUHP`BZX@XKEXwMTWYqu_Ge2x>mA{Z|&y2 zW)J-J!cK{7P(nb-Y*42mOXa{~LZ?gfOl~<0rEcW+H7b{>g?z{;NGU`5vrGs(YQRP`t3%uh|7rvnPUd@EIv7P z0BTY%CRsgbX|pzKOZa}q=;QRSx)n_v@Vpqdt{g29AK$RU*XhO#X4RmY+Gq=5(IND7 z%Lb#sd~-@Jv^jbBP~|N=?)Dfpc2yD7H=5XYitI0Djd5}V8IEGBZt*bVaOG=#08=N z-c@Hx1YkqYt|%zY+wRT~H|}ZPliGVGaV%Q~fix>u)B$#X1{O`GX`FeW&In?VB$J&< zKpv_KzxW9U$3Th3iA551HE-o~4gLUy9MI4QusC#9IiDIvvZU!1oYnQ89mU#ctjSea zDoJlIz$p$pZuR>W-W}V$a}ZuH*#zRUY#_c{fR`(E&Ux0s5q(u~&)cPIB! zIqV!noVw^dnDcle8V?1cC>Qqx^152{iokETE(Y5av?qW+q2O*hKK2Z;fjVfm{-l?W z95BN{5R;J{SZA96Kb-?qB}BJJy*9}?t`1c6_rq$Z?=!&6fDQYc)(&*gUBi%!ssq6u zmkhrui^L*BLtQNMmpuTy8nQX-ZQe(yRLC^_ArB*Q`ywl}XWj2(*cv>XtF3<<<{RAg z2E6BCbwY<|dzrOA=)s!?`v;o$Y#1C)U;#WhQ)>Y}&AnKr591Y?>^sTugC}8cs_xy} z5Kk`Ff`^mV!XHETogTj4+Z-?rRe2{yTMXY;_(N5?WgwrdMNfkit_g2*P(WtDs+&e= zbyFN}?Nq!8u_}e(=VsbH+q;ISE=Vq4-EME~@7o~(9M+;UnM6*egR1yBM?3tHI z^jJP{aMk2tF&!fBH0qFBivw|*6h^}6$F zy&W7kw8Rive_`Qi7urtw;$}oGw?rk&blkcL0dtcUJLRnwQilzH`mZ2i*>CL zE>@7jyfUG$#+@jwFU|d5c2Ol-pPXd<7n=L@S#xS}`njCe1#YxJWYbF<7d=zrx~I7*qzSFyT(na^*q+r zdxO(=FJ4~D;~8X2&)+z!UUEw&)TX^1&Tnzfo5u3_5$<0PcCRU$TsxQ-;=VuFbd^1E zPHlobz-Y~OE+eqM;WX_poG`mK0Vpvk>zl!*-;|W22xL>4%1i6r9qhZsU2;k-O|H^W zT2k@0bTe3_6>{nG&gx&;74vGbk5=YvHS2}Itk2rxDs%b~*F<5xR1$1t-9Ix{b;3zG zXqpW=)A5bgk7viKPgq=w!?1Yh4BBDN9roK7Ez_#EK3;oHsLg z!5k6rbnQ7k(`VJ{Htru@d`|ZXvvOX8mX2B1td7;$6*+M1^w-$Zv<{`f!Jz5JX6y0Q zu_k-Q;c2XU>SLSi6LJVP#1qy%C&%97fKxZLg8j+z5}$e9DY5Sd>@ovwh_)N(Kb;!; zc>w)LXd75GR=^FX#r{fKHiLIk>75%VbvvcCV;|{;w)^7N8J*4vwK>P5y|S(TgEsoz zhD0RY)Y{QNnS{2t4)oXGWt+Z~dltdBF0@x(WxLXQJ-H?+zH^nm>JP3{Aw1yiiTL!j z_Uii_6w3H5tebDQ+p3*{T1E_|tdD%desjtR)za@`{pcP$xyT9D8V=0fvfzF9+Q|lV zUr3;ob>n^Z+65ppE9geAt-ths`^;NWK*NP(FPYjd@dr*CoUKWgLU`I^TyUw7(1O(qjW zmUi(d5T6IaTC=^d!G6T98f>!gZ?LK2_Kp|6(^Uq?f8kvnFFf2;_=qFFzTf5Ipmo9? z=}Ny8u$=7^N+Q2=mf2gFuKt!+|MJSK!DlYAfU79nSa+ufIbX zqh0mNlNGm2YGnGVzXqPOpS7)uI|u#zUTt~hN$=^9^=eCb67TczfePZ8&lJh&rCZGJ z*^-|<=cYAxf6Y2+(p`no{n6kvf$^`t8PV2N&-$fYYu+rK2_XO+IY%M)|?C9d(V6Aiy{|Yc+rL37hdensgiP8&)#LNdG-Q_ zUmj?5=kVLtJiErJYP|EQiZ$+>SDsw*^s|xD#=vtkLRU6E`)=YVRq^k$?+yud0iXr) ztLb-E)w5fQ=NKx&OMLjX@3j|LRR_BZo-47ReRtrQrr@`~##|1(_Y0RDe24wF0_)u~ zf+rp9{^QS{+g>>BV0V$`vh+O-jnBQCrFk}D$`Wd9c>eblfw`A%DtPgs)PWWFyRLPm zrQZ|Y5}0erTZi^La{t&%tyRVHtK$VL8IWJEHb3%`b%8b4e8EG1awh}@zIVG_Q+k&@ z*P3f;qH1oXz`t?%B)Lca{1a=+ocFwqCI8qwgH21GeyJm1zU{?_)+b&H94vY9 zp@j9^fkNxK)`C}?_ihQi_^EdUo+~XXU1i$Nu5lmTyM4_IR$}j#$e-^C*1XNG3EaiE z?%i_EcD`?ov~l3i`zOql@&{gA)jdmo6+buLnJvFco{Kwk6uUq9Lfu@2o(7i$zq91I zm@`lDrOC_dmdfu%CoEGectwAOo%!;s=($2?q5KLxSK%y@U)DUU`m)q@||()OI-rXXwLrPkTjoXFsXgXZh; z#g_H9s@sV>u#_cjc>c@FUacxzx9m>vY{z>K2G9?Y!{iS@MnwMHI%VBUfh$pemUU&r z^HfEN5K|cP-NPw=#)mQx_(Zx)O*dQ)zgr_g?E-T-&C{ll>VjD?EdLDop95K z8Xmf9eB;So_MDBT7uGm;)%RO9xA~Cz{@c1vne)?^rd9oLs>#j#z4NKRTp_YiYiHwJ zYm>!y&uNu9F#Q&C3glE68_wE6_4FLR=*E-%0TOQ@p-5SR-|KmIH!?hiJt$c8BsbgjTpIuK5vXxgN zlNwaytGHatIk$X5nN?sF*ac33TNo$|7KRE73JVL13X2O%3Q7yh!Cd}Tj$cdh&nd7h z929FgAq#B#}Dnvi;A64Y2}`x;`3JDSX6xOQWw4-6dPgimSbDNVoA#v#7^1VJv_J>+Y0UK z!TP4nL$Ex+%)EJ9-!6m$?$~_x(&d}aUVh^8uFc%>8v&)eM)+&zz^=``2vUdtV)Gsx ze1mjdICS}}&HcN2cI@u$+YB4U$euLM3OI4QWq;@3lV1pa65kj^qu{UY{_j9}&?yPJ z(}T_w6yLU|;nQ6rt$_1J7ixH@XEQvDn~ff}vTHK|-UGI4J+a4Xi6pBc?VT-E(UYRJ zwN+7kY(LHvs#?GL#N|s*Tz(>E|0_<~u)MBn>%dSIRsnruezD#4GUN_++Ckt@gd zm?^IPWMIl%i>Fk17ZVi^vTDRa34&E;EJeCWg!_pa-^?`2yhN6T+7s>wqW%ai5|Cwu zZL65ict49^EwQa}yqDpmV%|@(C1*ZN=JScRbpr1bU~%Fdub#y_63ylv^e{6?KJ$17 zd_M0;w~%+_zle9hYk5cdlXwT598I{CdZBHtR5*^~75Hl27unXSyo3HU-cgv-d0%E* z5#Ets1MiiV73F=oZHc@>;hOneXIrhj&#|mF-dEVxnY_=mtR(NCU(Y-8$5zV%-^e@C zfhz_6Hr_%1cHWV`n|ILn@D6w{@3pqIop-?dc?bL*yf3z`UAzN6!21H&w|HM^TUc-` z<;O}$%bH?YXY-DFIEVMCmh~>)@%``S9hG|l?|9EeyrU5=<{jmKpFY2zci=zBI~w5% z-tqo_;vJ3j5#CY$tM&QgyaWF!-q9%VfTNLZ>$AM09M|)X#`zrYVATV>qdZ^W9cOBP zk@s1)^;O>S-Wz#GBOc^^wr$jB=8?m^zqu&jr8hm3oecfh~L`{|bTecnO;81KmMaoz#{5$~Y?Iq!h~ zf_E^_FL{R``3>*L=Lz1C{wdx;{|xU_ZR_{EgSq~|yK7m`@jl+RUf_L-ZN12Q+_L`6 zJMe$yeVlFmjdw8P%e>FCO^0luK*n=`a?QXo6ruCkRtfLOx0Ls_=#spH4j{`ax2*}h zrz~qC@6|SoCU`U%`lrn%vdGN%6!5#vPnFIOQpp5eUlOBfAwFz24LZ3^*0d}zJ-RIT z(!a2Lp|>`=!d-HJKw}RzJCqxZ%38#KHs)#aQk~kIS1(|6P#kO z0q;eYmB5|Zn!m4il$(hm!g1`U?g`k%wv2MWy(chvQBm>vZfMdYpGR(vbvgh3w+?P& zjw2lDcGNFBc_of}~Sja@-%^2QDghTzmR2i;4^8zw30XIM{YY)ljk9dgYuMtyj8sXkO)((D|#aqT-Rz zme{B7!;6n)R{NAw9t@TRizz$%9E_o;fP`68STeWYbwVpw-3z@x^w#952jp$Xu5i4S96NKqh{xlSE!o!I zMYbI0vE|5KGA5lk%j+!rn~q(P!?O=tEz0yhEZ@#>w* zt4AkFkIa_il`SjY7`6;taL8xNm$TTi_PArqd;cF`%Vn!9`+>u;uPSrKz50sf zB$qnLWlplrNiKJiE1cv?C%MW=u6C03PV!_oxoj!^EyKS${9BHHD`=Ln9LU&wO`*^# zvsLB>0yx5(Hp;MUf7o zevVzuJ?MVJndTOh^Z(QMSJd=-QRd^Whm)hdi~7*0u5lzIIRw9d872Dsi#!#2x^@s) z3^^#EkKkfA$#e@sUW4iDM1Kx_luTpv=MR%C(^*@}6T?h5iW!g~G}3CD?WYYUQ}&nEWxJD2}hl9TU>ax-I+Xjy*nSRA53+jsCea zWjY0Z6=9wl{d1>kK7n-vb|bbvgQ#6WL<|T|zy75^WaJC;BXKP3y8eYpo=p1!7rU2u zTsuR#w$ifR3G^5fKLawEyf*W~+f(ovFmIq)CVyaBfATkh5d~v`RS?9+vIMd<+i77s>S++_^+VYvBQKXUN#sK9|Lv zVV^r;nAjBi^Z|L-u`5-7CyZ(AWyd9F=8M&FV{j&?fPXw3XZ}$+GsiOVN99afCJbl( z5uC{h;Q7KMe=lc_B|GOTXU=)!WM|P$8QJ-jEbd(3b0_s)+pazyWapnUvXj%O<6m~t zla(3xMRsB~_?N@kDE^{0ie~bnH?;pnZ4?>N`4?x=Vh;9Wi_X_OQ-c#rXKG<(%b73} zqrgUB{OHxE~6}-mdes0&kwS2&AqRrVv(A5yB+Rnxz$bb10Gr ztp)5AN-n)3hukSB$|et4HUE|&{{=fy4!K)UmQ7w@Eqo?J{^Tg~KtcKOFhBc^GZS7s z{=c}~4IRhxGxD)_9rgSSb1(m*q>oHI=c3@nflJ&=J$)BYF=H$+G8_E^7rV$&&C*VG ztVxbj{0%Tpxltv)E{t{m_KUbW!o1(1zHjzb3Cc zcGZfPIa!+*44xb;Dk+1n&L|k0jz{rL2JkL&Qqkto;#($e7r6F>oQWG#diw7L?$pXr z6SoW8Ibi}3?U6fiyTDz%k_dz|sQPh%d+O;a@)-FokFjrH#%fE}FyBHLX8r%0X` zZ=o{xlsCXGUFbL^v~{T$Pi$T4#alvQp5?_m_Lb!ixb)&xi5KsJ+-jy?r_bxNglCVh#iG zO^4GEZ&D30N6Ou#8e&Gu-Q+GgiNf%+!mFGsz24~S*N}22DvMUX0aC8|7Egx3c9bQ< zGE#2de|QvNi<U72A99`S82H3^uXGyKZQD<^E%vH*EJd3b8Vo=FUAHg_xNa zqUCUL1T4bx4l9We)g-35+&hV`%5NZ0JV#4ostRleCE==WX$6Ioov7&0nCUy*((wX1 z({rVN>+hQ3A{f=bXr+uyJYS=y;lCFKTxML9h5=csqcSRDMQ6})DlWKRr?vsIo`inv zzh7DKgoX2IUp!6%KMs)_Cy4Ky?ReY`srjbpA}mJA;YJLm54d(&4l^=Qu`=U>%8XTn zdF=R*vLl$rkL~!*-!-!xQy78~g%`14E{Qi7UgY1`D-36gFdTn5*f6vNx?Ru(`94!DJ^95i9 z_c^p5cT3ATO|`)wN29)t$!}P|BK)@UALF+PA0@vn7JmD1hToi++lkj^ z#@lPicu@Hcc;oGq<7&M9ZDzbZF15c=1GiiZ^Por3jb7 z*5kS7U0W8)3(I8>w0{~c`CiDAd@uf;k}o^VoC7pw8h)7hY07e^TGoD`%m?sq?p(tY z)~_-1ZI(cW$+v+2rp*BVh1{FSf5nAvaK)DS_bx4%`ws6vEOc-emX>n>h(FP2S|&uAd?D&sa4mK^)5C$7R)M8V0}qr$ddL8qZ4jI8E}eH0cl@dkR=DTS z3YLrmZ2Elcd}iC1qOFd*en%$tE)EbS@5fi*lwIj{*!X{>e|V%H`@uXCz^WyWkcmnM zxqm8-gtH$FjGMd;V?}24_?N(>=^Q;4gRWRc8#7Y;Fi<*);{v3)Ri4^8C`&?q6qq2x z3Z{KlnE|G;56h1O6J$ey@_x(y*T94cOm`7UxbZ(v?y!Fvm>{cY%NqJe6i``p^utbe zmpz0Rs)#Uwn4drS-R!Md{&a|7+YtP_%DBC^A zUK=b8RmFx*dfTKL*9q>Ku&&Ix=Hu*HC-YgR2NUz zr#e1`b}Z}-g)6&H?HX63@a0Uiv_PL$2vymtFX{8j!ji4$ghm$j>hmg6yP*|t)#ufG zUa?7^>Z#*l*S_9`eMQB=P~n;X89Hxaf3X{KW6|P3=)B5@p&S;KxkeCSsjd?mDxX$V zya9fbsj;=cW<4ImqToe{g2|PWgHuX^LDwm7u9zKkW(R}w=5rUoU}eedc`Mw|+^SmH zt!7HFXu9lNQ$gazkRVPWq{KVG6~~>FV88G#Q6qQaD|n)dY@?2S26<*=TK12!?Eegu z&Gm?_VmXBp=X(IWWchDlPW&=(<%7*#JLFUc`{lso8QUN$9u=@BQ~oP3xso#Fj{?^lFJiOkx#JLh%bJD#J1cKJdh^_h71A)+d_>^Ut`jTmkh@iBefn-ng!~HblD*e%Ot&9NYBrZzA2Bi?tbR_CXn6((%S4?Tk=Sk zf7dJLgQlFBTsP#Au6WoZeJG!_%O_nRpSALPBz+4C@NJMjU}cx!oj$Gku2nxE?J1x| zQ6RXos$ic_YO-DZuOw{(>Ej@UquvxYgbjr*w0&Cma8a}MKO=1lv_A&z-F^{B`~gEu z7RElR{F^gV1ptd!2xh3;z4&q5_2z#lfT)z;uGa+IMO$vxPSRw_Hk9ZmxcJwhcE{^A z#z3|Nq*=T3P3&Usy#bH8`txTZet1r-dZau%L(;z*COvXeMBD zB!&6&XWSYlo_$f##f6s?TpHRRd|%-G?q%K*pEoPdV(ZIu@j=q3xvypZSYmaTLvhRe#F*|o)3 zw!bvAtkn&)JQJ|b`xa~w(<;yRG^MF?RKYr#>Y1;sogz;+DehE0)xAZZPLrn__33mf zW(#}t>5QCQ-Xu?(6}N_zbwPcKP`z7nvpzLQo*pjBrxS}57vt0NfIh|f6l#UCS+j6n zS-|Lf?s>tY&^yW##pedkt-89XxZSmb?}CDO##5~IV_5II2fcaL^pYU{pIovqI0ZW2 zRLlu0mId8;xCiD2-Q`g7?D9qPa4p3DvvHpvESeCU?w(f@EFXu>qOoUpDMpOzz(&WN z2w+e0F46x!hp$*S*o$zRu{6N@gJttP@+=MT{@}9J)BveXmdoDJeS6W_9}Lc#{{wWt zr+u*5Qu?9btT6WDP;j~AjhwO^fXUr z3PKIq=aU+h>9ntrbOT7g3ex*>RHpaZdBmsxGl|n6Mv+6`%^|)bLu?ecGj1j^bypNT zqvlc<`*=R>n{M%FQSw~c>+@;Xe4Vtd$QMNq)q5Yud~eLBjoeDwBxq6c&>7jZx8~C} z+~$=51| zD?>b&%=Z5k*ik1KOlH5+3tqCkwUO(z-=-`Xk4*ohymrJ+$v>NwrpiHzc1 z8~{tq=1#h0HZ37EXuufy{ty-kdz4I-F@Rhanq0}@uL{sC1IPzMX4K*$;qpOme7YiJ z#wV^2F7r*TS0W3JWur%^BW>=K8H>&C@(mOBjMj-G6Gvn!w?R9=dds>RA7Z|x0B@8I zAdi_}=J9r54IO^G#z^)cToeP2Gvls@0``8}bc=63#BMPUJ#}ibV*De)chC6)tZA{9 z=i3eh&pquiXo=@iOAMWV*MiWL%JYgV$Nex=v2gsP@0B^Gi(tA~5IVnnaq(n#QpKVr z`*CUrwuv~Ug577e1;gXq$}RA8j9d41LV~+P{i}WwI$?iPS#Z)GLPEpkiwZ-fp-Fo} ziwmCoTv>4AegVIwd{J3}_Q_3d@%YKb8z*BO)P~SVxpSs{iT(IK$EqrDXZ$<5_&W0~ zztJvUhId{w;Rkj16&JW}a5!{sc>unUQ2I;LJ~rV8#ZED%xWT>io(YwuKkhnbAfntI zA3P_trSiHmckwK~eACLQJ%xeA_wlz|8k)Rt3RayxQdE2vOr?>^>o}7$;t{i`(*N#FPG zVnANBmVu;rX0WI%XqU?TdlfXW<4*bhC3=;2iR{6)5}En8$ej(`yFNCs&9W~purvGD zGi65pTvA}<54|#F-Hm(nxb&7?iB36W+5U3D__?oO)?E%{qBF9m>#RP@{z}34=>%dx zw92>d!Fj*1`tlf^9epF5?0>bOP`ohb6Q42b-dJFy8m#~ssdliyNHq>+W(Hss`kM-- z&!wvWjKI5h_xA3?PUu~OIKkMmZ!Rboxrs9&tO-xxPdli7O3Lom{#|FW5KkABO{C}N zbYQKC^{5}pq)YJc0sNcvGyIuMQ}PsAj{ER$3Qw!pi+@vBgP3dmKLYdt(s4o5WQzAG zZ^xgj@Q>Hg*dVe^WXRo^$go0LCIlP*vC|`Fe*4=2yV95y?*BIACQpx;I_Z&PeaXrD z3(MUp=J*Kc-^X7kWIKA7bix9Bg>MAIxlA}W1AQaCV{q8AKUf%&Q%@uTQ|a&*rBQ_Kekcs$y+m?twybTyo0qfb89fjWuf=Py@K-p_?5}(WaXjHoVuVTSh`GRA3w)4kA%yy=VE4ci0S-ga^rj9=rtYXvYC320& zE%_`cgFOVB8Ks{u49THRLSBWx8QtRf!kQDwdZJrow17Vs8ZDsApV$1Q&}aZ}fcQ%* zU%vex8&Rw7151`0@pl(lk|S#Iu|J|-R8;O3{f`|{d;4;EZ+}rp4iFRGlOfd?d;5zP zEj3~lOVS6^2*47%jRXx)iRS6RX6z!1)-U(&YR3KbW$6**-HWt!kjvrn+6<4oJd zHe&(%8Oe%G>n+E@x=N?mDW|Gv{RIDLBG#eBexna{^iGJmD$FBmPuHzzvMNSFS2kHyc|e@^4Jgc*xrGFnSzM4`B)~h`TQ^x3fDzbAoD5(K z$p0&?gi7A}hBOoQ8Me|q?>-i9Os|h5TOtjuaqA0cDG#`=Jr++!JCc!9Jl5IP(UEG7 z#}XT24QMovq@^?6xh|3DNT$+q(s|2zw4aBJu1hB4?ShH7w@1*99wwGt&-2yOoo}&R zAJ`Vzm`=u{>l@ZJ5|JrKa$`CXiLQ;O(jASBo$-_v^waPJ_t?6&L^`=Ku{LFeauIFm z#&zvctH6gQBly~AOJ_?*dzzmGp7DwVRA*BQ4j=UZiBvkVA(`$>MN;cJt>Vh&4XmZK zlyYM_(w2y=OE*TMsg9&oLL{pZnaFpsNLHzzm0!w~loBUbkZXR9f9d}VeH(h*`o5u9 z-y6d=-6-)$*EZptmS{S)Z?G?oyeTdF2XvTM`V$s_O)&=tF&d6rAB>qF@I9 zAqFNe53B2zN6_$onmC%;Jk&3%L+k4Ax@BlnKScvtQB#~;7e@a! zxU1^-WmR<)(Pn7HVbpPcbw|?Cb*c3Fc#>?58gIyXMy2OGJC(7VXSXt*^Bkyb%y|x0 zHsw5{i{(5QRJPQZ$#%_26^V#NQkmg zxp6Gce*@|82Hpzs&-`VK6@a`kP3~nMfMDR12i)D47cr@spQ!GC7 zVoALG`E2TpzcB5?KaY;3{{N*vX6oK=Ch`Q^FmoLqlEpV9&u&uft2qqGZp`+4zD%D# zug|yXGj1Y}yqEHsh0lIApGS=L4CE{CMZUbUt8=XW?Nz6tHt#bn=e=c&Dr37Gj>xa7 zBN2_Nq6QWfMHeA}`{nw~V)0F0Vff4~nXdk*K|Ifrxb{DyE2 z^2x8x8mZp%e2W>IfI|zq5pP6it_*VB*humxj(O*8bV`HVFd9jSd1qpDwtRbb`%@H9 zB4qOor4uEGcacYo$ndBFfRHLnIvk0}XLT0sa6=j2;N_2>uWKkfzDqBCGOzAEF_T>r z6fFNkr8cV|`&0S(+0REKwf`qoCqzptTOIeK5oLdOb>@jOyxPCArG;`QyS`k+5tg|| zLx}V8$j}`b=H&vee?#BJd~2jU=N0C&8Qg>3CR+K1Ti1m zO7fOVF9Ps%fEANni0q0KURFA<@_5(b`lt%eG+E`#E&@@nrwIHe)(P+LH@~Oxe!0SN zlZzirSk~(@Ab%8?3yQE%ZQ$(`{4;;gz}>bs@Wxy+S37&54mFO;enMif>G% zTVbp$$$_qHN_&XX90Z|>h*g#YRBFJ>bKp&YjLU&U0IA4qg9iG?2Ja*t%W(DM8}3WjM@>{-lAWC`={D5gDLG&|3!)wHADlW0+!|jWZ?#U# zfy3pHYDQt2J6dDu=9Z>r>-59pTRS#bXXK(A>A0bL#>mMx3BQ+LNV*^+#vy& z=1$6ocGMs%B+B}@XDlLqr*-crOh+OP7Yt3-Sj>^#{g8EQOIr(^PU%=n6IScwV`D9y zEK@oj1Eb!bi$+CzE*q@u&CZtcgUtYE&V#uaN*BdEl#khHeOojG%i@+>U&lWe&z2Q< z!(FleZA!rHC^S7btc=dxgM;u!r^oQzc)CC`)%t&^dk^@is`P#Q+&hyHIu?pZ2?~e` zkx3)iLkfwI5Ry>D$T%bu5=mo{5Wt2FvG?AvD>m%C_qyupu4NZlyRNWce+-gD2n zr_5w_e&1b}{ohYA=RW6s&U@bXw0rKk=gxS~ZoI3#fb_tI6bM)>F5v|ELmThH%cTVc zCE}V!kRIG*`bi7$=ti_8p{ax(uS(5OH3LsoeOwUAEo5uF*#cKq5CT?tK^^$s1(eIP zvw>B1q3L)AwB}J>W2iHPa-R91cRqr`Cq=wIS4|gj2)tjX7b_OOb-c`vY{KGK;C zl(jl&K2Li_F=?fM`05~`r=eCxQq-OJci21%Og_JBDPimZNL++;5JP59SZ4p93Q6Yx z{LB2rvM5FShj(*PMS-`_=}U0p3Fk^79AvoFN6Y|&>CU5aOhm9=hB2>6|BMlUuO$&P0L{DkI<%Tb$fcWYdgVXu=ci2G`n8MA7@lw{t zr{t?2vl#n5NG`&^i*PgsBf>L8xKxA?bM?|t!c<&z{MI4m!N*_NMF&+JwJ!?y_ReVm zoFCLqX(0vmOaY12k%7}J{Nu_(LA;O(8|zyXm_n2Sn~aY|zv0@7zQgT5ws-1aT37Kl z1o3TCHQv4{u|ue&xZIzFVyjeqYzQkUA{yJ7!gH$ltq!(F$t^}j80w0vX>XJlGXvu@ zO}PZ*w^-G+HQt>hc0&nPVGV|QzOM)S;%Ix?4t8YG>}obrMiv!Tgi9)_Y7fM%Rc-ZwSm_WdNKjtMtqB*- zp3O$-f(6wmU956wTzFTEB@PioD4j!C%9oKeEY#YfI!JE=%qL2IjL zV`W38<=ftH(;BY98d?d~Hw=N;4>v)^Rxd2BC=3_QoEff~iQ81HYX~|u`|7F%6 zF|_!>Voda^6s $!_1%QauGfXi6N5K2T6YH(<})m61v#}J~@1$ccS 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�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%0A86~N&sY2ZuC>>> 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

      }peU^X_%y54kzL6vvbJa{j`|j>7>A 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

    1. 6 z#+2dHBNdHT*L4UzF0d=*IBaO29+|nY33?$rqlz{S<8oE&G_l1&%dL*xuC}5UMW^(2 zK0dh)F12shy0X_&8=z0B5U;kTuXsmAYhzVwtAy97YS63-MChp_efy;fMoo$VFH=DA z%3{#3*iZ;XFeS`y#l}zaCb-Wl;Ca+dWi=PoHvp4JR8wCqqVH)41H4f6Gl<)YNKr@QcrxZM8VICKu+0O2V+nI{ zf8h8r6ri#U8CRVygEZn-%M47&+6Bucf43y`b%-2en#+X(cWLOgbl!kLMVET+Mny1| zM%rhtXoo`yU~pvsj)X`oa>JqMQJ|+%Nvt*+3fC~|c18AtC?1pd(DRT+`?PG%(BbcM z5ll;xWRm>Es$D1iZ`53*wcu(PZ-^Bq?~uFhBifHsH3g*eOX-Zgx zXh;!N&?)e58vumeO3%Gd-9wt@>7_yJh8{ zC5;8%tt*VZRf4Eih>nOt+d*-HVTl#-&E!CZ&R)IVejcexDrtrH9lm0Lv8B+KG+eEQ zu<8_iFaf!`)R-#ehmwwBoguA~h4zhv^ngOjx;jww7$x!IH7lbfNSdmZY)q?$XnAZ@ zXt~7_vXUZyH%j^jjm0w9>b2Z4Dz9&w&{#QIAR7zE$u?Rf)M7KgshLa@m}skL@|MW5 zZ7mC7b|$WGg1WT`|EesqYCY-`>tp8JIE%6}<;EHEl_8fxVuR@=A$!SEn%C#4dr}1M zei@Twv++4FSeOy6Otkxai47{+w-H?}Z5SKsm6i2928H&6`ayb=Xu0JBa%?{n>`J#Q zhJqHm^+iAwwKlF=y42QSXt`AqPi|I>y`t)PsNCI%N1GfEo!OG52E@X9ryRzg81 z+Yk~5x0xM}hPY5o6ppD*0++Pls~MycS=C~T18UnoY|W73q_RNm+y}Dsp+|YOdtZL? z1Ro&*NNswlP(AC(YY}q7RGB)Be zz%oLcg<=ZIzgVa$67cR!SL!RgN5)28EvR%%Q4Pp<0I8o=Ry(;0A$RJkCL>U%GxU!% zQpL1Z(vB3W<&mrT!o>@g5VM6MuTc@<9$Q=zD-@vMTm1?h>sN>z^<<&3V`7DZA^2_f zD|E77Aqp}OSvGDWJTiqbos_cFp1&{SLj0_k4Wi9I7cE#VMrMEpO&jSD2oau*OVl|l z#DX)izMbvVU4bDvg)7wM)7+Jql~dS2=EA4>P5k2Qyu$LCGpALR%@Egj>4Sy{WISv; zdyBi$hU8IIc#L*LhjnEP%`1eL4xQh`&mR&jjE`K^FZldZR{6Pl#%c2h#D@Qoqr6Ga zE40;zGDiEaoab!hn(r)%L5>*deeuipF~)}6&b7osJ3M(TwD}wEA$Qst{eqG|nJ{D* zGR~SawH6`CXU`T<`Pn0=X;_`^FQkQ`e+=?y!UWtRsSsMtzEIsCdB?WsKG8d(JO0RB zndlwrTJ}eD$E2azg$NfTv>xcKN1g>NlGwis$m4|FqLpVS=M>iC`!LfG?00HYdX}iA zWE_e;sJU`bYt1Dy!K>~=6-xstg;K7a0!9(WfPH-G41Y!;R%FDc!Np8}a-j+biK`kv z(1oQ37qci!?q(oEay_9hOw1~z^;qCM`7=IYnLn+Nyp@Eq_2=5SsGg8iSZ~{&H=5JZ z(H;`s=gh3VsD5f))pSIapHVptljnAA{Chl*SJ=;^p1o-dxtsel`@Q?=2uug{b1izG zy_;4Z96Y*THiDK_&8C1;zaCVcJGihAF56`GY-f)SzKLHsqFD6+yE%7iU1ksuNL#+% z$(`DmF|<&bxi{xcZ5TYXU#%3yihX`o`4}GP2k#fXj3T8yIjVdz&-&*$?4Hr(m+`^< z5GVxMJw@f$X?kK||IlIA7ni?caIb{M!h4-4E`P_sXcM^j0D=s#wRLRy-w3y#1OCaY zO1{Q1)aKNb)lZ%_vmA3kDAo5YDe2_6UCeLk#0Ea8TyxPl?eB^4gB$tS+qo`AusyLv z6^HNSX`aIVr17J}yyROPO&&=8zVkFM`3^NiWWAbw&x=>#>XzcP(PU8=^;z8>3Tn zSm`CTmht94Rxt(YIp=UBXv1JzTEpTnx$9lJ0 z2cth>4@C`T^{#X7YuVN7L2zug*UWbIdYHIFK~^W*!NNbOc6DvdVz>I!_qVV+Tp6<} zZ_e0Io!*TRzPG2D_4*81n#W28_G{ZWWEq3IvrytAr^A`Sc2s5@sIJ>DTWYfOPBvQa z%BenEJc}80MtQ=_*PTRW!sGc zZRSj4gqm3umWP|mZv{xGlVCx5W2oYk-XLBpAo@{zi$dJdsKXKtu=p4f6p zLCNNQR_``9%cowmdN&!N=A3TF30LHB=KUWr*9FaQMhms)_LmNQ9%;PWB>N z{5Jks!3SNXL+R@IuD7GdnX91tHM^d--=`MY{cG+!^g++pYwM=bH)G2#m$9Xmx?Ybix z#aM8k_}l6g$5_6Jce*WlDG%;vJ?llUIx3cC1vA?Yov!|7@(D$eD}ISkDEf zyEn?pnw{Z{SJ@iYGmMq?Y+$#bjeL~RS=tj6=a*Nry^}w5Etl=(qnw>pJ*DPXqbqM+ z5IOWg@7LX4#xM#iHdbH1&(*u#L3Nv~9<7a9pe*yI;2#Gpc#+h-#KC$6Ha{^BD;F&+$yoxB2dF-BLANK^x zs%xN)pf^l(V%L0m$BE6m_D7mK=JjrkZsnzkJ@fooojm(sYwuQP=>~^>?1QxvTMO28 zwDMs0JiWVP-htMM*sjik(bkD=Q*v0gQ}rNI)a4Ddue67==R`Z@o5H)fo~XmteMBFg zsc~*&!-=Nq+t8mUw$$_#IkwI0;ck69>%G}@p15nCwhfed?cya5MbiWOtF;|}UUF-6 zN-_`oIv=gp?zk>G}V`6 zo30N3uP?C7-Fb1jPrdj zCTYxjoQuj8uQ`_)e#+;U(NSVyvE!HYy@1$(AcZ+y7hSnQcsgk;G$Z~Gq z7M;bK_eVO)9PVR0k9vd`7+rdSvvX#T=3?F7t1kUyqqFv$yyr8%Jo5Bf9!@&4EgIOi^09-x+^=^X?A826 zSExJELE}Lasm>yiq1}Ht$nw5dJ$b)5+Inj!+C}-12nX!`UMF)DI9`=d!sdC@Mrza9 zic0^v$=EBOSdV+&q2hUOj9B|8M6~TUwrSgaMU{~aQEi*PV{6OP5&1k~4xQwxCZd~W z7t3Wirg}<=CEtqkh`a*r%$cI{U4oViIQWN;XMPPMngt!(tZjT-S~XT4?rwHeJ9lGr zCus9%tee{sdT%ypl(XI*tcXVMI#(cDt|+`9aO8;-rR%-#+_z}v@zPeCLfqZr+aYIX zaG6n1+OvG4^TczrzTar&++G|ULL*k@mUXa>(hqrXUQ_?;hlp#S2 zcNJjHf{zlTgx|40%jgZr^X=X9@|&;~6SeE23@aGg$uWj*==gn80Hs{JDUbfS$S>H> z@7S0nq5Qi9A0yxs_ixPNcer*>Oc%J@e?E8&-krqz*`vka;v}u5+!3M#XTwD6WPcw* z>jOOja71{r#uuXJ&C+kzZhs+KIqQY!v|09Z6{$AjxzNUxF8toEJ)rO1nkDX9kZ^?3 z(jJYW2Ci>Md9LAi8t1pm`btsXlvytH1@AsQc!MbCV8gnM9Os?`;%eW1^x5qjv#4H4 z$$5ca)yre`mWq0(aIKqq_{}5y_7|h2btYzA73Qm@D+lxp+&V&GBuXU)|%h*j{_pz9Tw?AGY+Pci_nmTjnuiyRl;f zW>gm261fXKsTY_>j2$O~&qY|!*x_7$5bq}8<4a1Zcj$kT>PPvmF^)%RT?YR3ZXVRS zwIJ)(gWZiUMmy#uoY;c3bK^X7M^*6)YuE^ItY`T_hpwB4C&-(M`S0v}dY)s4e2bP! zceSi53eL+`GY<0r{Cf5fIS-2t+Goy#ESS*U6^#bUdIJ0~QdtZ2&KBkuvrY@XK;s&} z6ZuTOEp*ynpAlZnf}4&1%RiM0l+3;U+oyUy`& z3wwa&+}G>j4mB6*KV&X&_jrS2y7qkJaDeA=vmHH|)fvvNBEkQGQg@B1o+ZxTh#pAk z-9M#+Z96=;D1AfLKFs38H`w;;vgAx0x1VjFQaPqNyIPJbwq4@wUX-=JSjrz*)&Fwi zL`XH>YJ*0En1#5vr;eK4l<^Y_C@Czi%abrv7OTGDgUKl0uy z*`SIm!2^9(X0f52R;<^<;E+-o_3-!qZuX zKo7JQY>Ye^eGo73OUNv-f|peXBnMotJ$c8 zuF9T(;pn!n^&>Ms`7?0StB-DAxv2-gxw^^0kLkIt?yYOLg0q>m#kC*Xl)DpG^Zh*c z$QItC8#~L*u9=^F)}CEDxZ=o$(yYj#D7*xX>LEv@q-plvY_UDEpDvEBTkn8W2@UkxV$A8Upow$9`u8vFhe?M{ny?0>4a`dW! zCkh0--hlH{f6Gj@9Uj;9rJ)QFVcZJI!>vhy8}Ezusae(ZgfL% zbrG(D%Ma3Xptee5-fGI9w|wH((xDSK{_CeIz%%vSDBsLUa2?|5%AoszTz%a>Z}p(+ zf)X#N<+6BgNEGz$qw=Jm zM2$f#2ffn?JyfqpW4YHwkB%b^(S~TzMD4`w^X`uB9#^s9`VBwbaCfwF9Lv!*Ue|iv z#)9HNHUDUXUYg}B_-S-kF#2|6vaPL>nyO8p!SR|Z`6Tj}-iG}zA(FcE9%6e;sR`+% z_hFyYSl?K^eP4RDGuL(EnXmBMTo@T|MW+=7qs|L@O5C{wxoc5&ZZN7{(AJ!=zc^Rl z-~n_)7Jq=kQ-SG2^aem&ZxI|@Qaih{vNO7^_0lhas~XKv2o z**wy-)F|MR!x_SZW>iavG~19-fLMSSIVY#eei%0dZyXT5J*|juqZT<|PZmw99Vlxw2&n{K>2Bk?$F(;Ky%OQdKXV;<)niy?8-Vhm3mc& z9Oq<7ISeUe?LBnuX7}P3-?BrSJS>EBpMdxUB*4l6Nn#cFO=edDCxtcOJdKUP?;tiB zkaT81E~5JR_6+nw#yFaJp63&Gi}bMPd1Lpd4f!wobK1!fbnsqA7=2938TgqjT$=&@p8tEo-=k#5FhCZmeo#Y;^om>vkwq>=60je z(!FirDstsp!1((HmJy$T=K zrW6-LDFuEGr8DqRQ%V8PqjWi3EGPvW@qPt-1f?4|8$~JLqbWrp8psT^nFcbe;|y`5 z7@No0Ig~bVHi^_0R^YN&$bGQow&gDeCzd zrGWpO(sLPmj?(Kmdx_GsIr{~rxX-JU0`C~5c%C;X#dEw#DF(w^l=>O_6{Tp$uPMcS ze@AHvXYW%wfwK=OoyggrD4oPv52dx7{e@DDvrj0!oU^}Dx`?ySC|v?=Kc!2t1keOp z#zD7{V$d0s;=WiTL^&6wWthg4uE9K_6q72EQsAXfTEy8PN{czmptKtEhf=)HAf+{! zH-lb<}`FL-We;U z6z!^{v|3C7ag8Y;o*!M!XcD2T1sp?5z|kZzsb(oSh8U1U(-=zFmDKsd5S;L zE;LEZQ%qIHri-SbGena_IdrXf4sJS4kJ7n}-LLW=pcLcpK?Q$^QsC`Z@JA^Hk$jBO<(xf9 zDaOeUDFxmkO7XG|Q(Dg1Pbr;(`9dk4=Xpx;JTFk%z}SnFf*Oa23-%(o6+5e#wMDF*LqI3U9>2;VdlrCiKzbM7a`Iu7Bv%gT< z%-C^C@e-nx1{wP+r5iZ=2c>8i7zMFz=#)<9%%T*7%SGvxSbr!Tg4K&sylg+EXct%_ z0Z*h9^`$C!8l{<7!YBoPCZ%|uECnAzDe$uuJcrVySXwAui*<<7i!q-ly^IrEF2?IP zN@*TZ+RWK`lrF}4LurJu^C?C73Q95WDk*KjJW}urDMh)9C~dZv2l1S?o_Ot;LHNhiJ9=>1Mlk>zr)(X*^;eT8?4 z8?B|^|EsKEjO&uTpy0dsuZa^pfQOH9g2Q}fN}8$ZmQT;h(M+&{I-Xuf^Uh>eklv|% zB;dqjq0zLBv0iLNw!d!6bFH-4yQDG+vF~E>+??<>jI2UHy>a~E@RY1!qR5vSZiDT% zAiWU(WyxzcnVXFp^cykg#CXe4;|-Ya0fC0Jl<|9*0Cnb*C24k&le2G8zH6S zYnpZt<8de#b@+@)GcWnISIbGEu{pT?+}&PXo)22( zHF7415z9;iQci|5A?2k}x4P(x!wUpgmR|CJxYpnih3m6CUeg}s3#^5{{0GrmO)zX{ z#2Eht^O?zM31&)ynVyqJfCNgC0NAhdhld`=BB31F2Y&qPq?lQk{Zsiv{6QT`*A~V1E^c=q=IjtO(UQaj#krLCK!1SsHNNe_M(V8)0rbJ5kB;p3X8PiA*` zNI>B{2+2zGpZuI@wMANOu~s`qs~xM=j?-$#Yqcd>?F6lMqE>shURzv*|6=@)!T(tN zj}s!AHVoQA)D*u|IKgx}HRmaM0VnhVSZQL16KADjzGkcWngxCVK+$$)&>6Awee+eE z0iuQvoyp@1rYaU$Lt~?kyogmk$LZEzei0Nn^I9>hu!1@hiY%%D>IFk*qkr42(BA77 zXkLBvemNn>4x$M-i)+V1(2$s{=%ZgL)c1d-P;*jT%rdyPnI{o$4%!F(3H|n4e*ln+ zf@a_?&}%)@X9T@1`=A+d%4`2u>LBU{`^dteFHi2+s69iA5+9Sv*f5{)~~hN!i~CS zuC`YC4AX5nGw(3H_}y+c1T-seY0#@}Y$HPLwyY_>)P|yQm*rk%C1tj{+#c&IwdWPP zh9AmkWe)v@mvnDmKjJ!>TN^a=O=$^j+uH0$+}mSvl33|E-#oSMr+9**)prisKD$Cg;1gqDx(F ztKwd_S$FsN|8g3WW{5kj_J!S!ahF*BaEc8}8g4HbzOKwHm%B`NSo15t^>^yiGiTZO zGc6mbW?~3ShOU86@|a<_sryDuGK~>wiJsxC88cv^-N!2Wq~lXD2`#=g1K{IAW;J^f^aU#Xbb%H35xQ z(W>B1kMD;w-sbJ&hX_Az6>7^bw3)8B6=sE8VWy&De7}ouE*Ax` zF_EH$02)Xm=nlIYY+{n|!Px{Snb%9zBc?}RO?gF>=Z|lZDmAu@a)hM$<;Y&&n~_AV zEP3q!PIb7VcKNc~(J&nJ8(ZqWfU-d-%Fs09*_U;m;So&e(=S6P3jIR2tnADMx@pb5 zrza!hqSBO?+#RXeF83C$T1&Y0F%)%{C$HM%+U&f+aig`x+-iJPze%jNt=bLA_~tl90tyJ}Jj8_dL=%PS zLH_BAi(2I&r{yLY7^RH+SSJs=>pedQl%~dnf zC9lTwoe7ifu~vthT<$@muEYZAD|yUnt3B79lD%XSb0=N!Y5q@aC4j-Lv#{JQ_KlR> z8!sS^tmsj%uGY$Hd|`X3wT#T=UQ@zTbJfj;6TW`c=;>aoaE!Z|)>l1!>^N0hm~t(| zi?Gr&+f<1Vv68ZRzH|Ss7hX};*O)extsOW3;yPA*M}A? znL5?$2G8nVE^0ACpQ|R1pe7eS;Z3mR$H&)O&A$BO^sdhwdJrXgkYy$(B$!%uekv{R z>G_FR&NECiG3ZUp8_0%E2 z8JcWbK_D2RG!jH}L0qoFI6l)^0Gh$}L37f#6aYJ}v5NHrTEqBfnk97i0y&Ne_)NDy#tVfS3*Yhz$karjZGA_0hNS8MnWOP z%n^UGQU4H+iW$#t)dX%1HTnMyq43MUx>tNtInSKH*88- z?Q$C_&%;+GwQ{<3ZD&Zo)^yF8>%PWZAKVVb!?k1Ha~t}ntYlB^hZjzTXs=(Jv3$r+ z+=k@_{;JHk=4J&7(gt5}kJm`KLsaW1_y_{AF{+5J+(k-^dGhCcilLGMBqlH&AWoNOb5KwD@m@uZ)#t1`^CXx9Lwb zlTt~MoCsFHYwBUF-`posTw#sRGYj-JeN3aX0xbh9%7Z0*8>}*o9k+jBH{Tg2+_D$t}kCi6op% zA&~eAw)_+i>N$ThaRL!!_!l$&bpx6%oNuRV1drQkZ6dhGr9JB7xc{e$nKqvW{;Ud7~%?22DQG1KrJ$8#VJN_j(FPpNqG^t!T{>@) z@>*9H$Z5@k>7!F`hbZ+`BB0i~@KUcPX_dRgj93dZznk~dnn_8+(^n*~wNf)%z53n~ zqDs1+w%Ti$E=f?$RTeM!Jt&*wqGZZ7SNkuC(XXq0VUR{^ZFqs(7=MM8J?lfSBaRYl z)*@dGDDg^3QY~I`!`dZDYX;>cqN$+lEncT0o;rx93&O!|;uh<2cX-{BTEm>Y+E)i- z@;?&Sp^Uz!6V8RLx@N8S6?zjS83nw}7aDun%ny{@HD}ljabiUM0VaGo4P-9J3E0TYZW~+aCj1im%YO z&u9FWCwh|78w8iwI>MvXora1I~}+D)OoG3j4UEQoW|6bbdR4og|DOn}N)y zf%9^2kF0@^2_QI}_3~RlB~kMEw@8!(Dp>czDB-XIgjAAb9$W*;I%t4uOB~0zW8H(FCH#fmdO-Z7@jZGt_Tuk+Hs4qG<-se&dL zg4Sm~Q*420afK7ThOAfDtuKh}oUZdBi2-3%N=}8XSl0`Og-xC8lwq<3i1(-1^8ONx+ZG}b+km%mF%ByH` zC*zXhUEZ}M@A4IZ;yH$2S(0P$M(poYOWp2!R(jsUV1N9=n@@Nnbz9OMX&KcTX>KP( zw2<-Nz)yk%tefI91atLUD=CX|b|dG@&1k3xW5-LV;j~t+vgBp@e6Gw~aCj0z6CQdW zUOvj;2;g5Rm^5mF=)y1mBP;lUT>;X6R|owpoPd50A41m)uj>2>O;U!Ry$Z?zHB!JD zUK=>~mh`ddfA?RMngao3oY_|WBE*peiP+cbz|_`gZT^CKB zG_%O<*4(<&@@Ofml!P+Xnb3VwonPooxb$6}-|(HoT7xU6sJf;pSd%qaxhWz-^l9*B5NzBdR?FHX|8;PKS*ps^}bZg zS--{Y2>Y$}rR&^?=nlP`Zr+frugzI(EzA6w&QrSd1Z!;OJg;j~gWI5zL-&#R=W-W{ zi+8=QvzEHUbY6Tlltfi<;?znu!=Ap}YV?JzdAjv<{L+1HlS-MFqx-&-@pF50xZLkw3laB9U(PHZIaaT+EM0f)#6T{T<9NFm z$JnU{d&$n!mxL!Q>~B_bW_xGLXvrhC;Wx4OpgoOF-Q$2zRm`d_--ZMw&5 z@`ZL|UYZDMg83}JmxLIRdcV>EeKz$9vB4l=Do z(^-@~%ydDg35#kV(X=v62dW7MOf!j;1NOxrG({^6Oe96C4xK73r&_W%H6t}EOLr7Q z-wJ@I5KFD8k0swKGoKX6ZN7UIH>YI-b8R31V+9%zQFVaT?d5Nsrz1qbXo)H^ubq2Qh?(#S)*^`E>cud z739vW@e3a%ybFQ%Fz`yqsHxnUvAqnqF)KGbTrc|OYrriB?zY!-Yg9b$y905jeapuE z#z5SU2I5ZNVdLID5cku8xHImwalbtfce6I2y)*A2T(a$c7q|uS?R{V%Zq0p!I}NyZ z19xIP?qd=+2c7?~H915E{}bH0Y53X4mlmtzneYfk>S?XJWy#I@V>T0BuEuy7V?T#- zM~?AiG++P^EynkM^G8%Qpe#KiDU${sUod~Z@6^}qv~bLv!Gj^oTlEj7yqvhmax4v)*I4tGPS@Sz?;Nz!s}=s#?RFpLg7<)SWm6b{26%nkL+6{t+7S_z&fiVJRi!h#f^ZoKR_KwX-@IOaAt0#5I8f6|-*g~n1L=pPq4k$HFm3oj$C6V8{S1?!ok}uD&umu}UN)e@@l+u_ zi}fTr!RxY;W(h-lYBo&qA#4CxTKFVf{CRl1xibHo#g>&H6J1KE2i-35;8t<{7O%VR zPHYB1zmL?#-86o#yqk4RctOpi3+~p#)}oTX<~7GEyr3A{548~uW@crJxzHL?vPl%w z`Pp_ev18z3vX}-E1L@O>XUiyW!O5)>l;B zs-JIllzfg}_PM>Iu0+nv>2J$qHc5ffZI%@+hD^)xbf0++`8LLYlA%ZHPUJ= zf$V0WnTamwyB1?~r$feTMmi{LjckOsr#|cV?gZWNA6ve1^c6+U=~d8*Ec5!LS}Qfz z>$kU6qz|!HK}rh9YfG=+>`hdLO3S*;XIUdLc9TXQrB?yjtloz=`tY^c%g23x(Hd{E z+Pe3m%f0(rJj$vG`gcKX&LG)jyBj9HVV+boIbcF4aU{Tv)-)lZ43`$auxX736Fod6U}u>5gRu`ILr5RzLy(~CvBd!hE_9*EEJhm`)BPLJ~Z4P!)dS9+ zUaE-aWSxupu`k3(G_^tDOvoleBpHt7Niu3@Zpal{LhXar;z3~mD2$5?yV4-oUGWhZ z#&^2S!JMpr_7NwV>~zbGAH^7!2~srK=^k4`;s|z-vjoBU?{uGEO63s?fO&<>`FB9* z2tvC%z6&Ee;gfrTKcYT`EVgm%a$91HM>p1Pkt4n@O&dJEr*hKJYGp;d+byk#j%T39l+*OpTK6J$ReQxQ7@Q_?Hui#3}J%G$~U(;7-pYnQF7h>n}S! zh&!6!0DSyF}dntx3t;*g|evo_hH{Ly|MsUxsk}mFKS6w;mu?@Y6(9axF}QE^ zOi`n!nz36^wH!V4+sD__9skWClqZJJ**|_A*8W8p6hFkEc#YZS{$n&A+6U1vgw+>g z?VKBb3E{RCh}Zv8w|>Pkw|KR-*X7`O+yL^wW5svW+wjBoRV9lr$f3e2(*v?5gmdnlN!nws-awouq7Zi zqCfsys~AQxdh?P~2B#dDZ_~iMsFdEDM4WTVz?6u8ABcGADT7iX-bQ0AzQ)T=8ITh3 z*@1{(Ic0oF#9!0cim&nVQ^uA={5g%Qi%R>(mG)1@mH8hsu4p*e$7#pazw+m$Zm^H< z;N&z>56YBB?=&^oE`kOcgH0}O;OycSJpt?cp^CqNeR>gICo?rIq5DmpkC!&Ncix2V zs-n@%hm+EFbFTFjA2&_QpSh#-0&C3#W?nO@JLgjHSgqLLpe>v(1mI!jwbtD5dDhy> zyW!irw0Q2g%b;KCn7~hsUtq1t{En3tPL3IqSNn3@i6fpI(r%fVOWkgmQ6Ff0(+*64 zPz6THY8Wt+orZL*9W(?+-o>>OEDLqSqTJNM>KY^s{29Zuas3nQHncL#v2GHtX@^2q zhH1f%Hpz3ou=aV<(#WM2awtUA9EhYyAd%}Mu-O10y~xyt6Oac5jWs}Cz_Q~wbH!5{ z!uCOP!Q()qZBtC+GVwuI5=8#yi?f(fp*Yd>Z$4=?D+Jgdp-;e4iO+nN=T$(!J;b2( z2&qLmslzB|oydvbn@@SUqbcuRk$2jF0-yQDRujt8gd*=Igx`Po9Db55U6@Y-ZifN* zhc9IaWn4fR@wZdOf}<#R5#`2(vHIL6#k8*rcuYjw=$EE1ilHY&wCntdNz|MVB|y}9 zJrrkTmP^u*@ui&F#!>-wfN4e?taY38&DbVz83M08`%3JxLR?naxx&?LhN3sqrB@Ex zgM(VopUM6L0y7)mp2F7>cfO_bEM@b53-pED3es12wMkb=q3+uL`jh#hH6ZuATwosUodQMlirx#A7b3jk>mCW8n?Px_KyvTP#Q0FuT5R$UXzlK z_3TA!sO_wzpGBT2ZZJdEqHvQ})AKCXmW8xNy4((I-*3oz#t!TT6}3MM)v*;$)YGgr z+K>}F;Hsl#US&Bdr%KJYBafsPmwPe%TP_dJ?-UPs#*F(e8uz+EjvZLI5P3%|V9?F# zd8K-TKQmcRI}QZRmq|V=a?i)*{{hp1hMIlQbnsmnHyxBe!BM}J1gJdX7O$iJq4_lY zX-XwPO-Xyxj`~NACY9Qq1QS!@sJ~<)O$mz1mek%sDCb{5D9Y3Fb-(nqq*#gZi^wni zauFfNOzD9syc6E=OGk+>w>BWsX6)F7vuU9I#%BEnduCmtX4b`sYlCduQvJr}(-+L4 zgP21#n&$i7+d8jE7lCK)dYfp!0ULq^59S@`RbwMoN8;y*(yp~fV9J5dLDCU}e`7Bk z^5E3mefcQIBtjK344f#gz3;zf1|b57q0!fdp^n}hZ9`xdDe2@$Y0@6 z&md0n$pARXMB*WnU?9P6s$xvGv(ro$xWptlSS7)YELB_$CwD779dNj4ftSn|zhF9( z;pml@07FfZ7XlaOhy#1ZuS>gV#HoWOqWfAjnv5MV3baHJkH39!%d&d8P%-{gz>~vB z*=`@Za1u8)p9+i`Pg64pu$O$RS|at*HTqD%61D~C{8z<8lZTMS0nWgPCM9uUW|_Zi z(SkO{e;lw<=w`HOBYui@2ILf_`O?qB`EWpbI1fR_mz$`Asen45@jOj1(@WJ1Y5V->Wo@TE*Ih<;`OIK0MozgY-9t0TTM_|`wavrRmzRDV_ zt@1m$SJ^8aEXVH5tGxyyl59swyyBykOJupns6Huv9w{r4Z*C*904ml z`XHDHljEAfx>;!Q95IKkM@OIOyd4b~`w+a{4QTZIHhg?}F*XS{)i*9#jBi-9#b(i_ zL~E!VKPW|o<7UvN#1X~z3`&sG4$Ir7#0isV?V@=S*o?AHoIoRHFJ@a4T^`!N`4rftQHo@rHM3d301>VLG4;*^%}hLJ%aFtpUyLj_$~LcFg5i(%gs;RW=Q=9~?_t zb2IXOj1>^q)|y)Bl)?A~q!30y&zKqRgod|ro-LhD#hjOqfycI0bw_SS>2$Yo`Iy2) z_@(=li$#TCTTR_JdY0Q=`(fO@N}ryIpHE$6J01|NMP z951kd7R?{?6}M480$10Dn~OZSa_B}yIc&!3=o-s6K)UXjd>duz))fU_EqGLVkwXP^ zmugofwQICfT~*N)MW@t8TLt1hoH5MsE>H}_nWTuuwDl+fUI+8xU_crka)UM!6EjV7 zIC4CQF98?r#7qQ|&@BX?qBAoMx*y#a5^fT4g-~1+GSW>4{1?*T-GF$>`fwzsNoT}< z$Icz7jlwCz0*Z>T*6UhGL=gW^SnO#w+K1q}X|X4BHVn{;I@M#fr+rOVwJe59@4bnZ z3=B@C;;7WUi6h4lmD(o2f{xsqSUr{K$UOl0m5)S>e%*%%B1FFqQ)ZmT=Ke&vl8d+v zeOs7zCrW0U>I)z`KKlUW@{lT5h4~E-d6OJIVjb!M#_?qc6oMtk4iXVgqYxHy@0E%o zT_JD&zd(`R1G~Oh(-PmpzMsj11U-8F-8gzw@*3#TaBR_N^l5Gfq8|Ks;jz3!ZUm?+ z{lr>ZTc&6U)5`==aA2a(o%DI>G|-yjezc}q&>Ba0t|StoJjhq9e7Pu(Z3Q455=TG# zZvt1JGltT#DW|0sll#$%DdH{{>&EDPE_WS*F^u>>=*}~U$LL9t6eCqo3{Mea)&$@P zH5`-xBqAdt58h8W$5c*DRd5PXgDX?Zk_ptq>PpOZBjgcY0sjd?HjQojpy@t{=KtxZ zJHEiR5*py+NygBA!7=0J{P85onQTH$DkRPEq^c=2NB019%AEfsNzVD7QD)qn|1?R? zc^Sbr=Kdc|l5>8%n9l>J^S|rA;Ka?O3A|oS-pgfVhJQABCH4418xCxOunk;r@EzUc zqhZT^{2ki1ev3VycrDEYlGP<*={Me0h+liRKjLhOs2$6JufNX9K71FB4x>=x?}3<+ zxkaqPjMw!?D?e$Y{kNJbQb{ zJv=~kovM+zl`p@GK}Opxp2xT;$h^J}_eE_0Zq+Mv zqQltx*v5uBb{)%D$KFOAM;&L>adiIgyURUi?|T!jU->_N&u>5Xo^{sR?_PVY-OfI3 zpV7d632ZMuo}<^;@D{cUn~)-=UhtMS;XEwh5H`Wv=Q-GE7ZOVob^#kU!FwjaqUWF( z%I{$@urJ<;V(4O4j64!v#a^ZXtYQ~>qJ*6TO92Z?;N76KlvkB*XAHcW;8sqBWJj|;vX~*c*j)gaf-Qghf z(nW7Rm*MB)(=@l4_jfz3%}fgx-RfDG*+RcrfpwAP(IDavl@H_N12pEXiO z3%glyZwJh_y72t0175hrst`QkO3TIDw*FXoqBM?YQ@Mk1&%JM-&i&*(YBj3xmwtVR z?bZZ2k6H_(amFM+L?cgr9_WA%yYK|ZgwVX{fevaZzom%N^QH$nsHOa_qzIoeZQ$#Z z4|F)ZluuW@iNC%_@lXeKkAlkt#?7xD?x5yZT#hfiRK&gxYJMf3{ZDS&nUiRVB#C;k(|ILW%9z!Dx@{x6 zZGI%u;qU0SgVd{Fe`B}B_t^2~P3J%4ypVcq$I`UpcQoQUsfTyxBje`d-5T`Dk&Wgp zEWXp!GNWbqQflAd$DWq)gQXYzV{*F6g}>EG;OV%i+Csgq((xJ78QW~mY^h}Img}<) zypLZ0au)JFEgN?>)I#3)nnxFuWv>e)9>u0zD_Q{O%U^MSr@Z!bdmFpYu3E;D`Q`=cDXE0HyXP%Yatq-qaV5u|He z+hn_7K?EO8jAZY5+xL17kXJDp-{yfcW#9nbbR7L+>IQzl2kW7Dr>2jY>w80PUOu4R zak& zenE}&T{KdzZxX?WmGTZu^}0lOoB97jgJeyb5NnW_LdDwTqPoTSK#^&gA7-bid$>%) zw7J34PakHhXBWM)T=mtK53>uWvR!(!LeFb_oUJY=DUn*#;*)IiMZW)PgPa6YWP?O2 zlsBs|spBEw=Ql_^vzHf%bbJ*Ja=dDgFUbapmFT)$I(TlU_t7Aa%Sx#}W9U)2>#I-i zQQvdruoi>w?D_D4ZScl)2Y*XytHIkcHm9DDcTvlXRenzD=E7(0h9$qC*7^lnD<&LR z)|R(xvifyw-M3x4j=AkIn6|}|;9GEjYlEkja@399Nhrf#`lN%^Z-Q*a1uz;-7>m`Q zEvp9|zRnIkwKNIn(4hKtwt8|Y8)B~1{Y|!7S9Tcr(CGOOJ$mBtP{?DGhkxqH!wQH) zP5oQl)PLSM8-C)U5DMcz-@074@jM|vdyazvLK^;Z(3Kn6?wt)$sv^0k!hWXo7h_6~ z&%t#%{~h03uX@AIcQ8Er9$x2fG5SyHE3V*VpO{eU=gxkg7hL3n4qZE8MMnKH_y*@Y zu3dQY9s7>!5OSgKxb~F%@py(XRj2QX1s$Z;mKlru?pW`kKM!udpgwh74nEI^?!CXWdy@LzmyVyiQOy(z0w_9i?A6zfbk<)pQf&5O8D zN4hCT&D?7s*5}H%xXYpax z*ymO9@tGlYZvt02Q+?6%gLgFS9eh}o-%P=WxYW)6L*$25dm+Q-uUw{L*71Mgb+rAy zHk51)a#E@;?XT1|*hPp-WI16Yd)j}m265Xc4WAHv4mId;{or zhj?oet0H%K7j-Cse?}cjU{H%gWG()p?g~Zx4@C$6?3^t5sKoz;rV~C5;o$VM&SCHg zzgXZRXbB!LnA~B$Kh)D#zwwBc92QmyaTefeSZDziO&(-}V=gmN2~?K*In>^S+n9G^ zsnguE-}Sx6y;kz<{q%R$xzNDqb=l9fe+~!Qqw(l&N{54%^iAv0WnJ3*@^RDi`F*{N z$he9jnQ7|>Mu)Z_pBbs%&4VyrAiJ~tu(|K$Y|O>#lbssO0lEEg_@G2YeOMQ-uWjh^ z>$Dy_=5>5p-{tJe>)JzZ!u9lt@~Zi~to~{33diC#G^G`*2lx3l?dZZigXaxx(XJmJ zbjt41kk<12AJlN=O^^rh(t*)F%g0}X*ba!rXL@w(#BeND?+mW$VO^oDflQOr3u|zC zhfPl#rBey7TY9U-o8;n6Ice2h_Tl-VwuMz@9n>}h(%KXrZm!U`IbnP`R=fEWOTnsT zbcEMEz15?SGBxf8%L4*&az8}Td}R#=UF9_YXc zdwA`#{c|0T&CS!>P1(IWJ=GrDv~FCJD$@Cf4`it=${ILf59+LS{|`$(NnMv#U)~-o zJ>g4==_w~giRiT6yy^#?km_}v=4TD;wH%9Gx5hF+m!+;-Ux;_?-;lNWugz(tS?Mh^ zzTcd^c?z1^FK7a^rz1{*``{cH-z3F_naqJ%eR^c#0WeI4I_6~J0S`RVfp`1-OiYga zEUaqdkH7`buBmC}k`8FkoE*GKkuru0-E_tdmbc+fpZ1;kSQ%H*#KoR*cp%m9)~9P9 zJRXME2ebUhygpnxCn6QJ8qyi7i?)vaKJT2`3CmmKD@CwvLZ5+c!SZ_?gHye6I9-<) zj{b#i`b$5<;b>(7hrLceLcq(aOX}+RFnC>5E^&!RMfP?~?POhCZ53PMac{@o19|Oe zCls`5XyE%9dpiyt!N*ZX_Do?+1>efCVvwpEACwV@6v_Yawk* zJ?FyiYiN7j+BNE;!$%#}VlUefE84BWwv0Qa*x5*+T6~ATU)m|J|2-XqxEfJyR@LH@ zNgJcJtJW-9A6;C#aNTNL6TYlddS_<04+zn2HMSRN6)LZrJ9X*#6HYGQW4vg40mzx~ z95V|zxN21tE0ES=3DSEzb?Lo5M~H6;Yx_rRi6|s$1K0I=vs0%79tPJMt+fr7_FTMd z2`|*}Yf*ph)TtMvda@z6{kKlGh)K(f@@G^QO^;#;o@jBA@ILL-sm~t}FNO`mUcgp3mQKP0nY$ zTr+9LgA|xsPyEQ>-cvZdR;ic1d7Ic|)+0&%e{I&xYXxf7yjE8oE(d=K;eWsC(AT;? z`Ys+8DMr;jgenlb~ZunPjPFKV7>49|ny?4*32(+@4Z5i2v~(Cb%OSI(?4m&It#U zWJbo{u0Gt{bt$fx$>z}lK>YJ?k9ZTqKX4s12(j+|q1dL8gYW(w{h>sO^$ zVEQ|^pr~kfTDvZ*+WxU+#$qfZy6AqI`^&kVbxtmBXXT`3W^l%WdB{O{)dmk=w8iRy z?Q<||;mm{|8^eO=ty^`BOu*2z8)dA9>R;~K2W&egaC(xl2Szv4t&1*QwrEiuo?Mrc zja|8^9jvJdleJp5xW0B34q>};d-Z=1$?_<~lHlv5?BHQM-1GgK1ESF>RV4+L#ib?D zsCXJ<6&6pC1>>nZz31nuJH4697U8wh^K&!X@FF1=z%xr+Uy$3n1Lr^dqL{jnq$VjB z=Bf)!p^Hlw<;LHpxHwncrf9RM4l|oea&tPJ1Z59F89ENW%Oq;+Y8T+@@gVFL3?Dpn zv`8($2g&L-9z1h|NXuw3vt=aHYv;9(f zLn(-l@V^8*y6U~caWxAX*3>LmTU(Fy4jXDXEvjv(t5Flcl{FiNj;Pr%V(^ICnuV)Z ztyx`1tQ5t6NdCtbXCj^^59ime#I}u338Q;H9EC zzdSF$q@p;lU`WA12jvw&&9aqsYx8CwGI+$$!6OEb89IFAkU1lU=Ph2nHZQ+?rih)3 z`^)OriImiCm&4xZoZ|AL8ATNp(V3-%MPjd)($$hgt4gMql+HobJMw(tIg-gD;z*_n zS6hBnWvNJaU?tJvBd3bCaney!MV=$fFNi|=V4N`pQpJg`Nr0D@%$O@ql+91ghY!U5 z{~xu4LSFnoQcYaGFm!iN<~qnNR6c_oIEvJST(aO zT3%f?y;7vv5i{jjAzH+w<@xZoptypg2i-!OF(x6ZVrsEy855LMM$6`uM=L7xE2}C* zM%QU`SV~c8Wpy-vW?5lXbV`0fWofx+#Yh$-lt{m5l4u=M6|=HZTA7JXjgW%>dGs zOg@@sWSsodfiRAvLD(r~M}48}j3^>@;s&h>Mg`5F3QyooUKI@76MXDP1{za{zb9ss zP=64ZoToETL1IVpZgm73IkUlaekPCLYVoO>g2)uc9`ejB%+u{K9~%H6m+nKq%m?}h zK}P)ybc97is_Q z$6swXfTDUx>=eT_0EQ4vl|i6nt7N%VxmKF!LhKOWi6cm;T5Wko3?YfFu015%W#n9v zv+-A3E+6BGxg_x^_+}cKQ6;GzOH-6%)g&k54>w3`9P_0FTB@-t17$QDGU>Q-i`zT3 zl=LqA)mEo3lLBl?@Mn5YRFg1MwIvWj$?FXpJR1e+NVT7>qogbk3=Ah>bP$*z7k^L8 zB4H$Uv5f_`Bd5LfVe{!M-92FpX?L~XC)}Rd-kFXe(f-fDN+sBE%;M8bXlEqMHVBMV zhU}~~Qg)!V_@lT;f|kaHz{(=E)#SyJ9Kj@R6H1VR854%iMDhs!YO74GN^I(Z62>j9 zMcX6^a^ty%Cypd}6@Rr&gS8Nn*hXgopqlvXc#l}A5o9!9~GbJ#G<153gaOh`tRgy)v|1>!+l z{ZveCVsl>Tmnt&CqI~Me4ofhbVMeXOvizwfqPr!S3`q}FTi>C^j=;UB|Lxl1`N99# z%=0K1997dPIq9rWvrav~Dk?0-R7}mfFwu;cVhM30#$+&BS|(31Fu61nw1Q}UVPQFE zmP#_Yq5_qV`J`ftONuL_<&_m`28g+&M&(x&7bqE~kxGX7T7FeU5oeGZR8XSkl$baw z3FgO`v{ptdE2ePrsD;yt3Z_R@zQN3K#>^tjkShvGOG=PJ%^KyDk`Wcfl|>a*<+F?C za^k4e%w)vjrNt#8(}*~6EhxwIQ)Ix}sZH1n&ybue2vPqbtiVhwj;>qDQ>27-x(wa&nRA884hJdIiFn z(SpkI8KQSgB2OA-7MDaTa6(fg`q&ZrY@)A`mRA?f%#Y?zof<8h8m*ir`k4qe`|`3m zGev(RnQ9~hjAYvIg6NbvJcW_nV_;ZbRuQeNDk)Mfivz<^NWun(rdwWCDGoAGQ|8P- zB2;c^i5O&rCX3-^QAFb0Y(^={^k5TRSvaF2ItMLN47NnF{}oJ&Ruq+pAy!N|vH*Oj zB`hq3r5ZlW#85{u&RwVGqjL;5(#rDUsZ)!}qf^U@OQS=@2qT|diOeda1*JvR#TAue zWJq3IQBj3nSu|sEz8GcX?uqMY6QR#!d7fKTjvhNVI-|H`x;Uii_zGMFL0=zZ5EXNZ zE8$F0*|fRQIjiT)$S)CNO?B?i8Zucemvyrr{s2tb2467HBL^6qIu}(|7MDyF`(kmGIKxLY&!M3mgYqM>C^n{NefG(#j~c1Cj!AtjFM5b zZt-MHR#sj*6XUdaN>7x+GilBVF*_chqvXuC4;ajBNq$nhBt!;*b4`V-BH>G8n0Ash zg_|Adn;oyOACyeOA7@h}i||)lHK9F%B(Y}9p661vK6#o;Y9pW5vCthKxd)MV;jgxw z=HmQ>#Mbc$B+ALT@lJA5N)s?8CYhpkNzTJxZNC9UJc+G&9wf@iS@BMCN~<)_A>j-f z7XnDe;jgwVObAJA&AE`M#Ik^B3#RuZHZI7dn()gcuA0QgeFBLt?n`8}~=nVh%lGc+WTbACQAi8e?=H9?@_m+i9@zq->lMZzCNd0JRctPzwBhDrFNt&uP~ z2#nl=KkAkwK2mAhIUkWSFu_XvQCuYP2^Pu7*(C9iC(6hscHa;9g^@gWBk2nn+bLOsT^O>$^0Bbg^7xm}7s#wijz z1=X?F`eNr@Te$NWAd=yZjqM2RBU1YulE?8^TRu2~wo78C7ze-(nWCEHbeV$NESaL3 zq%Hnx%ja3p?MV7aA-98-kYt<`a=SqZN$d>S_AZeb&Lz1*X2|VFN=U+$wbfQxF_%Ow zt<9~nVlGKP{MA-jF_&bb6mq)>e>7SW+X@yGi;q{(;){_*PAN+HzAa5hBk{EO*eoPw zQF1sVwKHbyi7Mkl()d_bG1AJ*Q1P)lkOm!xBtDX*jii&}OP$%S!5=M*#7@e>*oa(l zRhES9Qf;qQLXx|rklU5gg4ratn#U2znnNBW1O$P(?!_NPde0Uk4hip$6XMWna2TpY z*b{(pKu+^jM}`TTeG=_l!&BRI>_JG9 zD}~(lQbLj$Qpjzk6wV^qDuvu$B!y)pPfH=U&r9KvB!7~-Wh7rJDajx?hHyI+e~btu zqvdWHNu`pK@W83I_bVX@2Me|RlN6SbsPZzE&5@CfE(jbQc%YP{KX*3ZueP00Q&P+w zyWFXV+7W%%7Vh*?*t@oH$Hq>Eq!53UC&>)_)mF8VGP{+`Q%Dk94Xbi7N(osuZsCp{ zO^awg4i_XF@mE{C0YV{(or2|Xmr}TA3wQ3pUu}7?MLv7BaHq3W#VswBV%ub@m0~7m z2x-1vYQ9fu=2ok?eoI0X*T;?vAr*V2iu{~-G^{^E=88wmlB9&mX$k!8gMIqNsWScAN(z4&Qt;>Tj}&fmKaZ4S z0rpw7GEhc+7-C2m9fU#H9f?0Cd?Yn$m*ix%OJXNE0uqIrMY2}nW|7#qLm?@bk@Cj6 z9Z929zLg|&3CBc|8ThNM%A}Iy0{jtAl2{K9kH!J+GtnjZbDz{sH3kx;eGbXF_@f7q z*tp3t+}yy5x$#!8T$MM{2$m4ejYw>LY8< zH01f#XIB^|Bhq=#xb=@cy|ou=g=GO!{kL+vPmYK()WsEUNz z)K=lDB(FE&MtnVFY{cQtldfb|hh=F4r!}bjSWIZcBrFL&RRS_x?UDpmj8j6AN%*TR z^F|`cV?(c$$~l3`IaE0Ye_a7g6NDg1b&#Ywu%g6syGfwWQWw*`^G z=)+SURNJVedb<>K4gQ{}AXz8H+-_Dvl9Q#7+wDq7vO@~Fy-W#78u1STGai6H#yS!< zKeeUj5R#0cOm1g@LP%0hncUWZLP)ZhGP!L8g^(;mI{qqg1xX|RL0}{ugYzV=f3xUc zBmQ=vRsRShOU@OA*{JhdNde8kdj)5XocX`GBt3B~{1 zeWp*tpZPkY*5?po)FfeS5Qv|RKSp(ui)0M9Zz>^)N@1)Fs--%#E7M^^eLvxk!XoJ- zGvk)?O$bTiEl`;q5oCs>GSFaNEJ70{QR;M@><X=xwAC5Qa8UJ(lnTF-fd{N&aA2^B@TZ5!Ec_l2qfbwwpkKBsrckxm5<>s}w3wV91%@6SZsXqMeCqS?gi646KlB z{84Kpxl+h2GYM5CtC9MG5TvY1l(NcL(p<_pcFM)jjsrglvk!vF3uEkjI|*@=8tgjk zPzE(d)E+xbgt-Sv;Ns~@NWvsR7=T?q7Az|t$7CLqcuYT2%OBGeM5Z*Zno-E`jAx;R zc~*c4vNDhle)8Bqh?Gqq!E`}Vx{nIaCFP+F`vXW%)^ZRT7{393)Efz-gTP3YAb7NO zIArtjNBFPxAh8g^9vs6T_Y~BA zI_YFB2a$pCl!fLBfhhLGD@|Z=dZL_!34*{#7Bi+tBz7cWvO+@BU&eK?+On81DNlldY)Hlsw(?o%h)MII?Hi{gU(i5_=W;?Ib12F%bj zBC{WkXPKDzWcC4dm<87ygBnybe z6~8AVu%Y*7N^Mmn@!r1<0FDbJ@saCf$~h$Qk(A)_+GVlJYcU8iQ#H@sw#ysWg|);8+NWaEvzv)Kt72I^b6yVCe^&2I*E!_h~#MV81Qp3@P~2PxK~|68wX}NFGZuUX%2bG2Dg*agL&DNSy)-B+21wm&De1 zAS5br6-kg-rKpNe!Dh|OVIWCh164stV&~D@^Slnl9fasBcXEX2mI8^NUYygajSyYP zsNMFd!cQO9W_+8mZ9Xk&Q;b4s`xwy4&2 z+^s_5ftf%IZ{??tL8|^>atxE&My5>xL0Y_^>!^Lm`?v)8 z4d@^pp&R|U%bo5y*umW?5*pJ}?m*?i9pPPZ+n9eizDP+j-1pd{W?ec zb#5ZRmIKK$mfvki?AK1mFU-`WU!YR|d0PLjK))#m>347KH|R*(1v*C4M*0h0>H~e6 z{I8ty0WZr1{$Q|^@f}IQch-D=%^#%sLd{Rp{9Mf+rTO)mKUVX+gHL^zY5sQ2^L8=q z;Sx%We@63fYyN%Bf2;W)G~WS@hIqd1O*)JedSrQ>ugeQ_i<{{$;`2!HN6CQP4yqrF z!2{NKZA8Y%GQBXU3)$BDs_S+U&_519=cy5e~}QMk%Ir5=6}?@>;s6Gwq2}k zyCkp;)#CJlg-chcMXDR3wJWi_0RY;pO9S&7A&d0a=77FH{xbC%ZRBzx{z4w+{8h`X zDh95tF4AmL@UrOxKS1;2bo@lkTU`Kn(*>65cUr(EKr)KTGpF zH81_RP5XheGo5@O^T%I^3xNz3DhgD6kfL^tTy>8SSx9#Tg?l_wMkWaL$Svq2ntxo= zMxr=H>cPLB|9`g5j-T#gXnTWIGvFQdG(=DTZN9^HW(qj_sc z240Q~h+n1Sx9apeG=H(?xs(m{->CUpH7`d7q?boy#Q#~xzpLZF*Zhx~Z_DxkKARMH zEL$h}gEW7b<_k1GTk}U~9&2Vv{KcByswp4pp}q%6@4~D=(-$=Tla7Bw^KWbZ1I_!s&z08KtvhD*+7&WZwvT&*_R$C_p9J2c zsnma;*8lrJzv+B`rycof?L$(T9#p0WeVKYtj;6g>G<2jf^4EyJUq90H0G>M2>A%r( z-&6Wzdjc*;^9PWE@2~k0njWI%R(k?&iH?^eBKS?3->mVxmO{JF)%`D-+P zkLLN%C-uFo`PVf6k>)?ud`n;1mq`kI56$=1yle*#>UQu@&<+N$Uho&(XDXUR1lTRw87ZdDv-dMY^qk$~^YzJRS-1Fm0_7S|U?49#rBV)%eGnz!zvdsKoz4 z;~#GVU#Rh*690t8KN*jgZF{;ts|A(zf$pK5pmJ<{N{@wGbz26{XCK+N(n!Jcniu&z z&C4+m{GpmZQpfYt+{9Z44)9i61uthsNH51g@DJF`(cCC9D}b@hu@uZ=dWsat5- zhjR>LUn5dR#_D(N6GkLID0iU9N+*e6r@JXnwBd zrC(@Vvb_Hk_?1UH@K+yc5BpVDi)CeHjP&7^z?ZinjZ_KozNU?u?$H!bS;lXWzAD5& zHUBTon;B<6ZJYD~xI)cOBL!ch`T3f+&XN%?eL}@bpD+V6b%m23?b8;dq)!|_u!!&K zHC!Kg+3I@p5hFyY8iJ{&rvA*-b974r?Zn(cWr>3}>Uilbc-gYvbXpd8tH!`vH3fd6 z_Dzu;iT_;V|Jnq;m&Su)T$fdh zaa2UU5CY?ri2TjDd>|Kh6L`p_yHJBMTN{~;dna%u^_ zgRXNqn1DA^N_l}0yy*;b=tH_0#6vC*&)^qo{ur(2G)>Raa_RXu+Vg(|p6AgH{P83c zkY*_4NuB!sCS#=U7^6)qcoDM5A|YPUv{BPNn#$V!*132MUe+dT^=K3LcG^Zcod!Qp z^9SpAY4dm5W;6-Yp?fhO{Dl|+ByEnJgz9h4F-}^GBZ{#aBczBdR=1f+8#OPj29M)~ zi2NV~3RTvO8J-RZTLtT-RsYsj!6jqWK}?5#U==R7#|*PRjYp6)>!-jx*-)11a~IG? zU?QN>JW$!7&}u%R9A==`i3oU`CXbf=JSoai&I4dG#xN10;AkCk4I_ ziypi+Hw9m+t3>*cs(rvj&G^uR`QWdHQsqPFi&Tt|rnhih=%!r&m8x-(<$7Qvu+ONr zT<)Urpc0QGy2RspgNy%wCa*&EX}TWvkv~?`OEvwqrt*{=@vm#Xk#>NW%>h*QK@^cW z2ED@g)}A^x;Xw0O@`%SEC*`Q=pj87W#~H-SNelP^I(`r-_(ILg@do^S%`ejNW}G=r z^S{#Z7is=x&EKy1hcy4F=3mkL8=C)@=6}*WC-+zZS+vtsp0dK;0pwxN6jJbqYo0G3 z62Dl><*6%hXK8+?#@(R#TQn~(K|#;cnwOUr!1J>T%tzKM4nMM9+XVI6kL8NL5I=#K zant?42IFOmL9eu0&Kg~lpfU#xg)#>m;M|te2pxP#V2`E}kHdw;qnEpQ*%BJ`ECsYX zOVrn$6ui^}Do1H}`XcpW2>zTD{Fj=yS`6Yl>Kc-xGjQWIU!tj;o&vX6^T%p>mZq0# z`fF0)q#sZ!{Xlaxe)MAg_~RQIK+4YRtPRdMS(>>Xo^vViJ09S7&-FWH`z_D)Qd0Z_ z&f#{cN&-C=MGQt^LqM!j3DOi2-3dKy)U`<4fjs* zv3IJEz4N(uG54;<9$f8<&8gm@DgF*`Y>I!H*V*sX0SddLa=NAXoqMGCUGh+-T~U@@ z06pCslj5K0jZX2;_Qt39=Xm{6{BylSQv99XK`H)u-h>o?mp3BC|CM(job-kvGw)z{ z>WxhC8@>K1{%&uOpL?9&nzhj8R93|)*tA=l;FUve%^eoi@RaC9q?? z-)0*tqw_I-)(-A;ITjWzg+=R7d37oN;GYw>?`+SX))JW7$`pT5h5t}wu0NwS z_;(`Zv?%KcY%;6;8FT!@XGt3mnC%x=aWBQ+a5{2K@vrh`_yeZmGtIjBg>vT+v9m>gU; zA>E;AJhGEU=4!;5O(GA_$j%zE6KCYm?WJPFF;xbP$Lhlv@^ct+LpjCa@KR11E_3;q zo=wO~+nTy9WxIcB^YE=C(q2t^+gp3vMN1STq zz3n*ZJzA{gJuH-s9F1Exa!in9BZnulk)v_TMm|=p?{3#1dt}f@V-1etaCixxw3Z$9 z<%FKJmY&qw*3RE+Ge&SDxG@t(B{lq`DpZIM}7L&5?xw;s`damQzJ_>0yp!T(1|09oMu*N1dc#WAgeTTwnn_sByx^MR%^uD zO(N%NB{<(;cRq;QrO z&+XxLKhDAW6xk)N;!!@KcPbt|zuEYg6UW1Ke6SYt`i1e%(RCHMFM;bp>@$gCG4WM~ zHCZbv8Y5yNyJL1WQ6j$=+A48I6G7$V1D>CkY|S(vK1W? z7R%1jP3LAhN9Y7lb`G5Q$<6`m{wvm~De%~EwzNjgeusp`(wZ*GThl}-uF|DguQlO1 zYc#GD(aRH@Dxy27!b6A2b4L<(Cl&j4f>>$%8pn8K-J2P~q)U@GLCurVa}t`MV$Y8> z7yEv*v8VX)X{GtNqAbhQw1klPO_X(mF6(tVC9b}nMu%|`MQQ|XLw<)^E{3nSNI9sK z%pxMsb_(< z3%5jMI$Yq8=|E*Vv;rx|@$K4p(RqUzr)FMy5BFc+1 z(qHJs*&FT&cmi{C5y6okQxW;7{wz{BC(8p=<_l`&3;lV-!5)blsBxec4t5!w^kay| zflAz7?Z+(b#}V3(eC@|!+K*~oKBzQl52#FkgHDh7mF2Kdr?1lKr|R@5=Ywf4@)=GF z{jwaOUzP)&0+Qu`d}KM`WL}m7PM)QI;AOs`R=y}ttNdTl?E?16c7gUF^Bb${5&EPa z(1dzWuCiV1PtSO*2UP00Lze^mkoJt#dQcv+J)j$8jmt#HduCbhPLkE*#3}|>^lN5eck)qysY=|tu zy&h7W>B@2jmE{8}?SehBKO=wX7wkmWkY~Ws4^*1OgG&6v8jnbcH|@dnvoh^Rh4usO zL)OnS?Z*)9N1gVgR{Mb)Qqm8!PwB@{tTjM8hN&ZN1a?KqPkfa~rr5~Wik1oW)FIg_Y$#_r`FUtkxpBKgyxNH6ULwe%yM)DOL~UO^=e)WV@$r2W9l_6|E`c_4pT9vD|- zI#8MJYi$oAWx7ea|KirW^n0>y_t1AR{X+T3c8~IyNFL=qnH1$cg|w^AXMxTKROa(f zCm-mM;{=XFG9TD6fa%R~2l{0`u;&oUQ9siDMOrVY)QfqulrPqDP$~aO%a>|7sFX)^ zd&<*tP$|c8y4mCZJY5bmJ-Lu0=K*7A7yLbx6qO?D1shqupi-}yS3tikU-%{K1&pj0 ztOE1#HWI554IHZ#}Pz#6r4Nm&kpmCrQhxwbdvryZKd4{wT;EvFru|lQq zzQvR6D+AA$mQap)StTis+qn23GP-zjA%hIu3!G2BnF*+U-V-CLTX zX^4#ZI5+Y@;SZpaZqoE*Qp~GfC&dE{DG^Y#Q$7`v(ZiEpA;>^|bS4k|d^#ip^~$G1 zi02D)8ED6RIhFWHTF$3MS`EXifS{vNv0fPE&^wP5{%_KBE9udmIGYrD_@YC`22VaE zl7V>_U!0*{KAn@%fP8&CXMy~OlESW~pc$C2oJ@-2&f}n?OR=sA`Fv0OoAfaBuM|+s zTdGM>o|{S0F1C}Rz5I$4$GIy>QBL=eLhnnY(DNoK^n6N+`9m6fqP~u#d7kJ>+7tGH z(yo!DuyY>ez;7hQIQt+e@_n2XxHn0GTZ@L3(aRIZYkCPO<`-9zVt#QwDURpt?pKS;hDYXfWkPnv$9X+{f$Yp-cnQp`^VkV4;~q?o6akfPn}AjLf80#eLV8a01l zy5c920=JkH^Ou#Rn7^ze#r$PE=}DeAle7btrzKtDiEBua->snCK_3Sl1slJiJQFJ+ zv;;-DbR)0NX)XLF#k}Z7 z9siMz{}OccdOX2Q9=P_cK#@-_DfEsZMSEUH3VS!|_{}>0c~Z=;UM0o+>LXGNHs6q9 ze)SV6=2snCgJOO)1++WrtwhrkDaSnJN>YpuHIU2dY~un*L-K_%m8KpDdfX7od~)Zr-Pg14D|()FIWi1cveOF9V8W|6{=yFf>wuRTv5dfy=h?mJS9n-P>H$H@#*_}`Hf>GDXC z{{T|>wHCDd$;gj%4W2cl9LJNdNpU>+juhkHze&4b&_^HT@g#*5#}oG13>^R4lH&N^ z6_n%H08-eqhH@P5PA5hGIF}UT=U&o-;fIcYTGRJHTOEgXNq#WKO&kbGvq4)OkJV$y zqum`!iu5I<=-2Z|alAfV%P-LKYc>A}Xe;!e$H*gnzZ^w}gO0+OGL95|wh*)x+GVAV zpQq_^P1k^qLLWSnJp8%>v^(_eAq9UU9yKQ&uNI&e6J+}m0w=1dCn56jMki^Jn)q-Q-HCjG zTEvL!q+zZocox~2bS55~B`sBV7I?LR6EQAf%n1}{b3I6_gvcYEqaHjEb1@K+KT6$M z6-TNQ70#aeQjW-eqzk0WSP?=^$a$6xR|RKPa9t`rh!mL)B84#rldjUMF0T>tLYY{t z7R3_{>Olvd%?>9H1Lg=)43r~D(GXEocpX(1l@Q0N3r$=&l@n~9^};1NdmKlK${A0J z%HhPDl{0}9l{1kPm5ZX4l{ASI4bcrUUsnhoO^nH7fw*rebOP&Nwk0j+8`5iXRa%VG zGO+H^zb$>2q5@x#m8~#liz1YH8-CSLA?BmeyP*#RVRN2ATS2pk3&R{-?o@Po3~p{< zxrO!BppCp-=c0rt*0?ksHS7VbfEKb(_9MihNm}|Ye_5>-rjrSSi)x%H!`;AzgRu!s zN$T4lZhHcp$%}f;{@EHw8N=1cmv0@r;R*vLYfw!S#38dx5`lECV)?zxlvnIEV|bzc&MyjK8zqSF0{KTvJg#kqd$ z29Eue<#K=qC-UPK;E;7(aQ*nzhw8-~7wFbo;YUKAe7d-IAP86IjUVf=-T}I}CxM&n zV90)jf4CjA{zTOi{q0TwLZWt%4IFKDgIj*HfHURSQ-i6`ZTGdnJ?AO|@0Q=tzvyF; zgLTVqHgI=17_x3VX!WUD;opV2ejEjy@uQyxC-P$zaF`Fq1=o+KK2xvSxj@&CCqKur zj+NmA*AKp|SnOcPx_(Rt&iHYV#wYUQNZ^(y(C7N`C~(Zf4Xz(+zD%xP9)kv1?J5KB z`tcla#*d*IpU985f%|6?_3`rG)f;PW)~+AVd?N(Tsbzobh8+Gk)v< z?)P!I#`lk+Z3aoF7L4XZ(hlcy%CwKG%W9w(!K| z32?3-e*!KUKh8*3H>q8fZhgE8objWg89zP&4ox90xPE-v5*Mf9Q1R{Ru?$Zx@a00? zc6C&1tiJ7nz`K6j37ql69B&fU$78_V9zOBYx?Mju;pWQq3H){IqhEVmtV`l}-Z>|^ zb~Oh$*H?Rl;K)UZgAVx`?xsCv~Fak0lzMs6K`QcY&)6yz9qfz+IC7 z=lZb{7r~RMkDh&!^J5-xrd_RU#*cd7&QD;U>xbVrxpp-WxMciz3%F$Jqf5WQkE5IM z;~?M?wHMcqXK^t;nd8Sdz$N3yZwDmj$49`K`qqox>lk;N+aLM@L z9fS{;#N`@4?p6S2{J`Zr84~r6g}`CxhzqVCn+B6&YQGuaQpduOx6~< zAn5&F6A+Vu%=t{+*rd4)qwTyXv9j*I2f<52N_ECbH? zu}$L>`Ee|8?pY_sd1=yKlTA<{5W0X6Z!EX za9<_R=lbyoZX&;z0O$H~%^{v#y~%~Te)Pr7I5U3@W8HRD51jGCw3|eJYzFRkVMTi1 z^&J7z89#od@rnF+8@L?_@^$@Kf}8v3>T$vK zV;3Ge*pdL})<^q%tkx2zExujV0%!cVxEVi=1`b1STyXukc(NzgGj@TlA1C9cRxV2VS<_m8uIGk)Bl@rml=3gD~@*Ba~kaYd;o9!!w0+pgx7 zd2(Se7wY=)6&^~O=7PYx^)a?Q@Z(mEPvl22aGMh7bN$!{+}#Oqt{=x&cyh5Y7wY

      )Z{lAHC-%cO1MQxMcijs11DxG?ckp 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

      o}*hk+EO<%c&GyQ!H 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

      !f^ct#_pxSk>kuS^&w>oURq_7b_qOd$X5UX@_ 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


      MMed9EG9{*bI)EdtNh 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
        9013@SVSV^rsVtnl8GiL`tI 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 literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/rom.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/rom.a new file mode 100644 index 0000000000000000000000000000000000000000..4b7947ad63d3f8505c69f68610ae5c2cd2c8d763 GIT binary patch literal 2015854 zcmeFadwf*owLkpq*?TUz5Qd8y25|1IpIfrz3WvkP ztBjuxN68h7OBVC;U?yXx2N?VOZ~uAylL!Blfq%-tKV{&bGVo6s_@@l~QwIKjCd*%DZ@rlqSR5@-)>ED3l#Y+0zKt}E2uSrzW|)pfQkDY@#35UaVd zzK$u)H-=hT+M0~b>pP?25ov2_ULM{UZqfT!)mGQkcZNE{Dv&jy z#*Q`WfbDG?0-U1-xTX4L~dGMwxXe=HrndzY}bI5QAK@ib?L-n z--hO!+M}J}o7&o&S$$`FXIobb1K_5n4Y5E|TWd#WdskCuU_-Pi5Z$Qa(GhJ8tXWi= zBE7oQSKS)zRH&ZZxv8@~5Ni*&w1t{g2(iihs4lJNh%72q+cc%Kj0o8vntQmQIyyWnPsYm91-|t>HkpJF3+-0z?-O?rLPs z(T#zQD;Kl6n(G5?%^Lzf%y)aJX??VHt-q!1b`}7O0hqOS-X4gB+B?F5+hd{9Kr~FW zRW)x2`6A_zo%N6wbkS*1#gL#YA~=yjU9n~oox0BTn4bD@Ycs*)x1GM_*XeL#?Heni zp|$n3?a|iGHE7e`><@K;Oocxh?Hj@yNFq{0X*^pS2CqPEsI#j*%mBZFFkaE7Vej!p zN)7CuEFC0Tk#MLv++Iq3G&QOSH+HSPG1Sr()=}+TAB_cKkx)k%G6ManLoa|1x3_me z^7&+!Hne^2#&A<-TYE#O5wi<`l?|1Xq`o7HnG6%>JKI`XIP0Swfu^SQ?O<6)TWiav zK(w`~rK>s2xrVX0Z0dvQC@m{n9w0RhT;CXOu5Q0gWm-T{1)9>e;Z9{jo1(EuxE&$^ zZPS=jlFl4z-w|#CM?$d}&DdFl3}UYd8OaCiO{~k;vYsFUP5jo8ygt;iK7}ZsdtXaC z&BcV$N$erO5IkV#q2{Wlkhy6NbBQO#D81rJt~{Dju5TvkQRWZ5Dxw`U1Nu-#JzEgu15bV@Fe9Lsv_vnNNbE3e_F$m91s1%>b{1R#gG*RQdFW zS~|jIEgO8ToovPR)w(DHJs(-gHUm{k=k{>AwNXkt8r#?FHd`%1OKlh;iQ6Ku0Ne;{ zH!BQ$iwl<7LnhYPnyLoI08q>oe|i#7oTe@mz%LTh!G zCYF4~tuF2G)U?+&Z7}MQZZlSPgxl-FYcYgA1!a}>fp)L}Gtg3rX|5{9b~9GVxgvRhbp49#ziz|i3|xe zzcUbO!NRctvkkk_3FV1`a8+(5sv}|15ME4L!f_@u$cc?(OL-LUc{Ky>13k zY750jq5Kq;(4dnufES~)7ZHpIBXVk|77lo17 z8^}?Hi$yjCz_KPdE}h}>XlH#Sx~9{(d~ufn0bjV2ksvQ`TU#G#yS*GCK|PsFm5W?H zC(@=^{pBzL3PO{#SegIZDWX&7mhe-9L-N`XFKQ+>yn)+a-2f3anlnccR%)3gdsNd} zvu2G>xuOh{hSym=VOeWO#0=(kwyjsON_oZ|YNW%BPg!#)20>VkNUr@ zMEW_cEEK(ARaM3HW!3d{s@tmC{a9PerjkXTE0js9U3PN-%XWD=5al9Krg9~E<>JmZ z&B5d2u4&Zxr#4B8`Dk6jOd1_y3WDk;AffPUlC0{aG}Nc9RVY{E8=d1Rjw!*RGSg|e zaG5erds7N^n%vuC9auy8!UyQO%1tnrX8}wEA};A7^$TohYu02-X{tz3XIr2#tS1G? zVYw+3?JV!=*i_Nhs(EJIDN+I!#pJ6j?J!ELLOhm3Wd%*oiKrU0EZTt>Zxd9M1vYkc zG=*B%q(u%J;i;|JWC%=ELryVlvr!q< z;Z(8?ur>PJDSRRyD5UwqXa|EbDQ|I8E&M+v7SZLrp_}w?qE09<5oC2@PV^)cM32@F z5+)@CRcBKV-PIIB6!vy)SPSl(OEu>}4MNC7u|SNgb1d2xQ1y~=$3_pQh-s;%uT{^M zP`T0UG=~*CoC*#nuIXUK#L!ehyoKTzojN7Jt#1i!468yLUJF5h7pZ=Pkg`*oa*$rv3NQKog5(}9k`G8c#~(mgIRkcK2qDuJVLpgH9MYsTrO zXl$Zwg8pb(NKJ}KRoYuaE&3_|suc$v3y3izhjOy~@DY&1#E<&wNx=noO2|QEZ5@HH z@EAvvs&E@5QBN(XvleSYT-gCj);u}PYq+kOhk2D%4b|6A6fROfL#~E=pi0U@)U<}l zpW%)SB_IqMjAToDxVZ}sh!X5W2K9L@GsNj3LuwL}bA>^CRpSh+T-lA~LJxFEtMurP zGIyz{nn&7o>5;Y)y*I55cOny(@}I%(hE1`sD%ZqdBuFL&V7k0(i+J5cSMR3E+QlB8 zmhHN7F=&RTr-X*Oi=ZwFlX`NNEBd3_23-Y=`6!^q)q=c7qr0dIVk&WhhTM}O$8>@T zQx^7snl#j=QZYM|0zHKymHW_OOn{#LR77+t#Z*x?;!pVs%$&cg zRe`Pz7^YNxO-nPa#vlnjYhpwaSAq#?TAfTZRMp#v2>E3}Y2SFm+iavt&RwEkmTHTCgmXm#wU* zD{H7!voH0X+yqusmanXusDNed?V(M?1SE{2UqM=vtW!7~MmQHhaz#n>hg)EG^^F0@ zH!LH(Zp|)oAu@6YG(maL?tDuip8+1Kzzi9(Xa=)B!Q-oIxG6v~RkN}>&~S5Yr80>f zB|LLwG`_lfzp|C$G{}M$PIW<`R<ACH;w5iS0tCG zK+3v0+bTLH>IgFFx~r~cvfA5_bL!BtS%F5{I8iMuW?1(njCK#nX!wtaEqU^?5otss zfd&VvY1Hd+hk+_>qi2E$rh-mR8l*$z6pc)NI3@YC-`orkn?kx=<+ zS7HxGB@DUVu4u|h(v~oz<|>v)i&YHaAeT#h*NH59YLlfzg>PJz;KRn?|EM+PN|sJX znG0-9EG5yjFVs>G?uM^NUK@)g=K|6PTK)sZN|81n1gc|_u98?)YzU>5r#yKIw5mI3 z(6%lu7Y=4DP}y0=Dz4Xe;|Pmd1P!!D0~3~V$<*P(-LXzY zKGX~$7c z@!;Y6RoMFC8_>B9xX85{n zSf&FcxJVd)f9*Vk)&x#-I`y{Neu5_H4b2`EeTsoj(%9#LgiEY{ZE3A;_30*r7* z#!2;33@Lsk?^xGn*ih2F(NsLbx6eAUHQWt=$_8pZPuXK`QYm{(p}xzhd9n%%A>FMM zA*Ks7IZM8_E|@bd$^Zn2-dTKBqFIZG)D@g>CadA7p6}!U>Pawln7Y%ovK5+ZjB21R zaAC?;HY^KVS9x>b`l{<20=4zm1!`_Y%Ac<%t5PmoYN6LM8qJ_6e4Ea9#Zabp&%v5H z)#q22SDpY?rwkmSq>Yxj*O8orqUsAk?tGE;iV;cATq-=96!01{zkGpeYHvak0V}$O zfFY9Lrb&UC?x;-8LqSWmiRU|pMq@~_hXF`Hv|H@BYp#~EKZ`Zrkh0W57$`|)h0j-} zpV!bWox0DdlSH^6hI zMmH@lZ}8Pru7Z6rq>7Yt9UW1WZs_WsV3yDeHqhum=wc2JX@ZnPfB9~pE|6qC>(cg| zl-}gb$2MufHI$F9m9YA{jY;A=YbsJL;v3U{*! zsmGMXN~P)3wKo#=G)6nMZ5wTB!Fif#S}OFEAa{3hXhxGF&Sv;`RIZk3##Sc^GgMuh zZrlTE;z<(^?zq-<*WTWam81#VTC}02Du!+zX)Y_mEul>-Vcs<3$#ou0R3dpAf*%Rt z`2{Fz2!|9PGwsoJt<9lu%XQ&Rkj1W8Wowfb$mpV2dE@oXjSX!E6sRYW7S$qIjcyW< z^fLo#TbLnjDQdq>b+~nev5a8121c<-Tj_MsYK3&9WD6PkNjeYL2L=%e7z`yw zu>}H-DPpOI92~x38=QXAQdO^e6RM{WB}7Phe5rktT4d>vMBsB&WAHjCeZV&Yv<(Zs zXiSPnwWB;|iDXMWi?xjkzR)Z9lEXyNS{bodJ%Gd`UGQsyTV3CtS{X@D3_S#0yq@5{LasYY@&BDXP871cW>gtK}Qr*EflevDjAxu@cq+5&) zZ5y?W^o{i~;OyN0?lU*$xc)$WFKAxiETYHd6qf{jImj;Nl41tS&T|2%o$CghJ&NAT8i9YC~2 zhxlk7uv5FNt%HsN88VAFPEpgEUPQ)e(&9;`f+zX;enM)2stet&&)oz&G0|}9n@m{0 z@K4nxI0w)Ro>p&YYtsm)^<+a#TTg&JcBdzq3>5PP@cK#l9vb>=K7ldFOdV>_ET3u> z5NvB6^Q2@CKSrUnYieo3u{>2QxRXg|v^v6_*ia+>@aaOFk{0jQ*H;HB%Buptnic-) zDu&&YjbY`OVRKt+D?dR{Ur{$vY`AZa(`h(N7lQwIkp7&YCVDGf1wt*LDbgHotQNh6=u$Ls5K?}4)<6`Y|}6*dym zrLPLR42BYYybhc}Fyx#R-jctz8CoU!YKm!M?iiV;3HwbZt^b+$Fp**D#;X>K0nROtMeig?=ms01TfZIl|#mC(aSLnu<& z-cA#U^Eld>XG#vGB5pv`mNQ)nP=>RHnq#7HhNqMiQBL1@qZqX-24QKAb~DgtICh|_ zfzjsZZkNg;br6-ESn}(QS=Y(Y;>s2EwUs_CRwU$cN@J4cQPK*KGW`r+%Kz4;&X7+e z9x1p|go)F%DxDn68pGid_1rTY)`q%n$XA-Qr-`~YnQ-HA9u!A^`7u$r?>HvKf79&J znl(xEQLdf~s-A386`PvM6%qq@bO@B-bVCgcXF9my(h`~20#iI+eU?ebjH_LmB2Yq7 zJ7kkKXA}H;0-~h}kbZ1#>q6egn1$31!=5eEPaYqfFfE;n<0HfouC(wX5mlB1Ql=SF zN?Y(c`h+?uC^3>25gzL+JH;e6$SBlT*Q^MX*HEXk@fW$EfBe%ky>wlYl%{6){(Zz2|NBTr zoJtte>gfA_BCnFq`Ho3u-?41zm1E3|OS)%|^o-@-rZVP=yG`FI3c7=!eVpxz+t|^# zDNopQY9v|5_MI9Tq2Es)Ef=L?^fv*H=sRW;YDT>cfMX(9xI|pSwqC*Wn|*9<--y*Q zKbRZ5AozqlrS=Iqn>``tcnf?EpFJq-%nV)B->Kll_2IPk3b(*m|e9a$~mM*|AuaXD@mX&ZZgNN35sc(OTtp_w9>S5j=?~ z5pyH4D*ZkftD5Pi(%BJLGG;1IFncb_yeOIJM7hxEB8YwXoe`NHIWS_HRxdl*qvF(q zHKX>XzGGhyca-1Nd+mv*-M>BYV>kH8o;<}I+j`?R8nb5fbcY6g+*F=4nPvlesysKM z;D+%SE*gj7GT~la_;1OY>9Zf)gWn=^?x>32b;4evhfYFA*+&$HDoc z!I>2)8)YTbmlgLN%Qo~q1XzOpdv@docKRKRDw!M#KGIiePMnURgt(sxOduQC-MF{a zB*#k+oEVRxOmqczRJ|)R4|DoPKI#yvFA?;zzl- zqsr{IJW8!zI8Squ_8NZ(uaVG8H`-AdA$Aj_$hxr@QH!R;E=tpG9yE_zUji=|+yxxW z7{{ENpPMm17vGhdA2Av8{dAZGSaLMJgwIo6q$V*V^4iE<;%g&9?%R8XVwQYAZYnkn z2%K}wEjkW3%`Wh4#qVYNAh^WL4_h7q=RDw?N8tiSwzx@JEHjdEpEvn@{P|&FDoZ|% zZxg=n#y6Xq-S6Iumc(23RMoO$yMukl1V;3n88X%Y-`gA>i+q*x+h|K#1{>inmU>q% z17G?t8T`Yo50&Q)2#XHfI$kIbxFf5X#Gw+*r>dAU(|?F_acN{n)dNGUcxyl4sPunr z2=^-2Ze~*q{T4+mgGTPSBe$>=eJbI($%8Z(%z@CoSdM%KC$^UdjaO|ksTK05t~9+BCeKgq1t@{K>^lVOdpe#6$2@rZ^W#$mgwfJYrL zC+lLyWh)^aGiQ47X=6qI#--^7F2PUfZONKsdRfuCyteurwQulR)8l}IInu__`u>vF z3g@W(39l8;QTrWUE1jeE>%8`=b6^$cwKq?$^~?rE*NooAW!}PJbMEkY1!tsE;*2!I zOPcXz-pV?m6=-uP^(%P2?VR;r}QK)dvIn%n0t@6#J9lbmhJYL!Pqa!ANuVJz=pUddVV&=k9Kxv!YojW z8(shmzVZsr*%4ukjk*$&G$-gmzuylD1@`A8|I+?BZ<`8o`d>g>2XBj(4p%aV`$w*% zfN%SLR=AX;l8=(R&7S;(UDMAT_Wd{fR4d)JZMOCJK&Id1n2Po0Ie1$dG%0D72wr)j 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%0A_1of;Ujp`Uh(L4Fr~E__Z0#O$B8 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 z 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%0A! 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%0DVm_en^fTcu+g1+wWuu&+ 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 +#include "hal_api.h" + +size_t __write(int Handle, const unsigned char * Buf, size_t Bufsize) +{ + int nChars = 0; + /* Check for stdout and stderr + (only necessary if file descriptors are enabled.) */ + if (Handle != 1 && Handle != 2) + { + return -1; + } + for (/*Empty */; Bufsize > 0; --Bufsize) + { + DiagPutChar(*Buf++); + ++nChars; + } + return nChars; +} + +size_t __read(int Handle, unsigned char * Buf, size_t Bufsize) +{ + int nChars = 0; + /* Check for stdin + (only necessary if FILE descriptors are enabled) */ + if (Handle != 0) + { + return -1; + } + for (/*Empty*/; Bufsize > 0; --Bufsize) + { + int c = DiagGetChar(_FALSE); + if (c < 0) + break; + *(Buf++) = c; + ++nChars; + } + return nChars; +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/driver/rtl_consol.c b/component/soc/realtek/8195a/misc/driver/rtl_consol.c new file mode 100644 index 0000000..2840005 --- /dev/null +++ b/component/soc/realtek/8195a/misc/driver/rtl_consol.c @@ -0,0 +1,333 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#include "rtl8195a.h" +//#include +#include "rtl_consol.h" +#include "osdep_api.h" +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) +#include "freertos_pmu.h" +#endif + +MON_RAM_BSS_SECTION + volatile UART_LOG_CTL UartLogCtl; +MON_RAM_BSS_SECTION + volatile UART_LOG_CTL *pUartLogCtl; +MON_RAM_BSS_SECTION + u8 *ArgvArray[MAX_ARGV]; +MON_RAM_BSS_SECTION + UART_LOG_BUF UartLogBuf; + + +#ifdef CONFIG_UART_LOG_HISTORY +MON_RAM_BSS_SECTION + u8 UartLogHistoryBuf[UART_LOG_HISTORY_LEN][UART_LOG_CMD_BUFLEN]; +#endif + +_LONG_CALL_ +extern u8 +UartLogCmdChk( + IN u8 RevData, + IN UART_LOG_CTL *prvUartLogCtl, + IN u8 EchoFlag +); + +_LONG_CALL_ +extern VOID +ArrayInitialize( + IN u8 *pArrayToInit, + IN u8 ArrayLen, + IN u8 InitValue +); + +_LONG_CALL_ +extern VOID +UartLogHistoryCmd( + IN u8 RevData, + IN UART_LOG_CTL *prvUartLogCtl, + IN u8 EchoFlag +); + +_LONG_CALL_ +extern VOID +UartLogCmdExecute( + IN PUART_LOG_CTL pUartLogCtlExe +); + + + +//================================================= + + +/* Minimum and maximum values a `signed long int' can hold. + (Same as `int'). */ +#ifndef __LONG_MAX__ +#if defined (__alpha__) || (defined (__sparc__) && defined(__arch64__)) || defined (__sparcv9) || defined (__s390x__) +#define __LONG_MAX__ 9223372036854775807L +#else +#define __LONG_MAX__ 2147483647L +#endif /* __alpha__ || sparc64 */ +#endif +#undef LONG_MIN +#define LONG_MIN (-LONG_MAX-1) +#undef LONG_MAX +#define LONG_MAX __LONG_MAX__ + +/* Maximum value an `unsigned long int' can hold. (Minimum is 0). */ +#undef ULONG_MAX +#define ULONG_MAX (LONG_MAX * 2UL + 1) + +#ifndef __LONG_LONG_MAX__ +#define __LONG_LONG_MAX__ 9223372036854775807LL +#endif + + + + +//====================================================== +//: UartLogIrqHandleRam +//: To deal with Uart-Log RX IRQ +//: VOID +//: VOID +//: NA +//====================================================== +MON_RAM_TEXT_SECTION +VOID +UartLogIrqHandleRam +( + VOID * Data +) +{ + u8 UartReceiveData = 0; + //For Test + BOOL PullMode = _FALSE; + + u32 IrqEn = DiagGetIsrEnReg(); + + DiagSetIsrEnReg(0); + + UartReceiveData = DiagGetChar(PullMode); + + //KB_ESC chk is for cmd history, it's a special case here. + if (UartReceiveData == KB_ASCII_ESC) { + //4 Esc detection is only valid in the first stage of boot sequence (few seconds) + if (pUartLogCtl->ExecuteEsc != _TRUE) + { + pUartLogCtl->ExecuteEsc = _TRUE; + (*pUartLogCtl).EscSTS = 0; + } + else + { + //4 the input commands are valid only when the task is ready to execute commands + if ((pUartLogCtl->BootRdy == 1) +#ifdef CONFIG_KERNEL + ||(pUartLogCtl->TaskRdy == 1) +#endif + ) + { + if ((*pUartLogCtl).EscSTS==0) + { + (*pUartLogCtl).EscSTS = 1; + } + } + else + { + (*pUartLogCtl).EscSTS = 0; + } + } + } + else if ((*pUartLogCtl).EscSTS==1){ + if (UartReceiveData != KB_ASCII_LBRKT){ + (*pUartLogCtl).EscSTS = 0; + } + else{ + (*pUartLogCtl).EscSTS = 2; + } + } + + else{ + if ((*pUartLogCtl).EscSTS==2){ + (*pUartLogCtl).EscSTS = 0; +#ifdef CONFIG_UART_LOG_HISTORY + if ((UartReceiveData=='A')|| UartReceiveData=='B'){ + UartLogHistoryCmd(UartReceiveData,(UART_LOG_CTL *)pUartLogCtl,1); + } +#endif + } + else{ + if (UartLogCmdChk(UartReceiveData,(UART_LOG_CTL *)pUartLogCtl,1)==2) + { + //4 check UartLog buffer to prevent from incorrect access + if (pUartLogCtl->pTmpLogBuf != NULL) + { + pUartLogCtl->ExecuteCmd = _TRUE; +#if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED + if (pUartLogCtl->TaskRdy) + RtlUpSemaFromISR((_Sema *)&pUartLogCtl->Sema); +#endif + } + else + { + ArrayInitialize((u8 *)pUartLogCtl->pTmpLogBuf->UARTLogBuf, UART_LOG_CMD_BUFLEN, '\0'); + } + } + } + } + DiagSetIsrEnReg(IrqEn); + +} + + + +MON_RAM_TEXT_SECTION +VOID +RtlConsolInitRam( + IN u32 Boot, + IN u32 TBLSz, + IN VOID *pTBL +) +{ + UartLogBuf.BufCount = 0; + ArrayInitialize(&UartLogBuf.UARTLogBuf[0],UART_LOG_CMD_BUFLEN,'\0'); + pUartLogCtl = &UartLogCtl; + + pUartLogCtl->NewIdx = 0; + pUartLogCtl->SeeIdx = 0; + pUartLogCtl->RevdNo = 0; + pUartLogCtl->EscSTS = 0; + pUartLogCtl->BootRdy = 0; + pUartLogCtl->pTmpLogBuf = &UartLogBuf; +#ifdef CONFIG_UART_LOG_HISTORY + pUartLogCtl->CRSTS = 0; + pUartLogCtl->pHistoryBuf = &UartLogHistoryBuf[0]; +#endif + pUartLogCtl->pfINPUT = (VOID*)&DiagPrintf; + pUartLogCtl->pCmdTbl = (PCOMMAND_TABLE) pTBL; + pUartLogCtl->CmdTblSz = TBLSz; +#ifdef CONFIG_KERNEL + pUartLogCtl->TaskRdy = 0; +#endif + //executing boot sequence + if (Boot == ROM_STAGE) + { + pUartLogCtl->ExecuteCmd = _FALSE; + pUartLogCtl->ExecuteEsc = _FALSE; + } + else + { + pUartLogCtl->ExecuteCmd = _FALSE; + pUartLogCtl->ExecuteEsc= _TRUE;//don't check Esc anymore +#if defined(CONFIG_KERNEL) + /* Create a Semaphone */ + RtlInitSema((_Sema*)&(pUartLogCtl->Sema), 0); + pUartLogCtl->TaskRdy = 0; +#ifdef PLATFORM_FREERTOS + if (pdTRUE != xTaskCreate( RtlConsolTaskRam, (const signed char * const)"LOGUART_TASK", 512, NULL, tskIDLE_PRIORITY + 5 + PRIORITIE_OFFSET, NULL)) + { + DiagPrintf("Create Log UART Task Err!!\n"); + } +#endif +#endif + } + + CONSOLE_8195A(); +} + +extern u8** GetArgv(const u8 *string); +#if SUPPORT_LOG_SERVICE +extern char log_buf[LOG_SERVICE_BUFLEN]; +extern xSemaphoreHandle log_rx_interrupt_sema; +#endif +//====================================================== +void console_cmd_exec(PUART_LOG_CTL pUartLogCtlExe) +{ + u8 CmdCnt = 0; + u8 argc = 0; + u8 **argv; + //u32 CmdNum; + PUART_LOG_BUF pUartLogBuf = pUartLogCtlExe->pTmpLogBuf; +#if SUPPORT_LOG_SERVICE + strncpy(log_buf, (const u8*)&(*pUartLogBuf).UARTLogBuf[0], LOG_SERVICE_BUFLEN-1); +#endif + argc = GetArgc((const u8*)&((*pUartLogBuf).UARTLogBuf[0])); + argv = GetArgv((const u8*)&((*pUartLogBuf).UARTLogBuf[0])); + + if(argc > 0){ +#if SUPPORT_LOG_SERVICE +// if(log_handler(argv[0]) == NULL) +// legency_interactive_handler(argc, argv); + RtlUpSema((_Sema *)&log_rx_interrupt_sema); +#endif + ArrayInitialize(argv[0], sizeof(argv[0]) ,0); + }else{ +#if defined(configUSE_WAKELOCK_PMU) && (configUSE_WAKELOCK_PMU == 1) + acquire_wakelock(WAKELOCK_LOGUART); +#endif + CONSOLE_8195A(); // for null command + } + + (*pUartLogBuf).BufCount = 0; + ArrayInitialize(&(*pUartLogBuf).UARTLogBuf[0], UART_LOG_CMD_BUFLEN, '\0'); +} +//====================================================== +// overload original RtlConsolTaskRam +MON_RAM_TEXT_SECTION +VOID +RtlConsolTaskRam( + VOID *Data +) +{ +#if SUPPORT_LOG_SERVICE + log_service_init(); +#endif + //4 Set this for UartLog check cmd history +#ifdef CONFIG_KERNEL + pUartLogCtl->TaskRdy = 1; +#endif +#ifndef CONFIG_KERNEL + pUartLogCtl->BootRdy = 1; +#endif + do{ +#if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED + RtlDownSema((_Sema *)&pUartLogCtl->Sema); +#endif + if (pUartLogCtl->ExecuteCmd) { + // Add command handler here + console_cmd_exec((PUART_LOG_CTL)pUartLogCtl); + //UartLogCmdExecute((PUART_LOG_CTL)pUartLogCtl); + pUartLogCtl->ExecuteCmd = _FALSE; + } + }while(1); +} + +//====================================================== +void console_init(void) +{ + IRQ_HANDLE UartIrqHandle; + + //4 Register Log Uart Callback function + UartIrqHandle.Data = NULL;//(u32)&UartAdapter; + UartIrqHandle.IrqNum = UART_LOG_IRQ; + UartIrqHandle.IrqFun = (IRQ_FUN) UartLogIrqHandleRam; + UartIrqHandle.Priority = 0; + + + //4 Register Isr handle + InterruptUnRegister(&UartIrqHandle); + InterruptRegister(&UartIrqHandle); +#if !TASK_SCHEDULER_DISABLED + RtlConsolInitRam((u32)RAM_STAGE,(u32)0,(VOID*)NULL); +#else + RtlConsolInitRam((u32)ROM_STAGE,(u32)0,(VOID*)NULL); +#endif +} + + + + diff --git a/component/soc/realtek/8195a/misc/driver/rtl_consol.h b/component/soc/realtek/8195a/misc/driver/rtl_consol.h new file mode 100644 index 0000000..c27eed8 --- /dev/null +++ b/component/soc/realtek/8195a/misc/driver/rtl_consol.h @@ -0,0 +1,133 @@ +/* + * Routines to access hardware + * + * Copyright (c) 2013 Realtek Semiconductor Corp. + * + * This module is a confidential and proprietary property of RealTek and + * possession or use of this module requires written permission of RealTek. + */ + +#ifndef _RTK_CONSOL_H_ +#define _RTK_CONSOL_H_ +/* + * Include user defined options first. Anything not defined in these files + * will be set to standard values. Override anything you dont like! + */ + #if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) +#include "platform_opts.h" +#endif + +#include "osdep_api.h" +#include "hal_diag.h" + +#define CONSOLE_PREFIX "#" + + +//Log UART +//UART_LOG_CMD_BUFLEN: only 126 bytes could be used for keeping input +// cmd, the last byte is for string end ('\0'). +#define UART_LOG_CMD_BUFLEN 127 +#define MAX_ARGV 10 + + + +typedef u32 (*ECHOFUNC)(IN u8*,...); //UART LOG echo-function type. + +typedef struct _UART_LOG_BUF_ { + u8 BufCount; //record the input cmd char number. + u8 UARTLogBuf[UART_LOG_CMD_BUFLEN]; //record the input command. +} UART_LOG_BUF, *PUART_LOG_BUF; + + + +typedef struct _UART_LOG_CTL_ { + u8 NewIdx; + u8 SeeIdx; + u8 RevdNo; + u8 EscSTS; + u8 ExecuteCmd; + u8 ExecuteEsc; + u8 BootRdy; + u8 Resvd; + PUART_LOG_BUF pTmpLogBuf; + VOID *pfINPUT; + PCOMMAND_TABLE pCmdTbl; + u32 CmdTblSz; +#ifdef CONFIG_UART_LOG_HISTORY + u32 CRSTS; +#endif +#ifdef CONFIG_UART_LOG_HISTORY + u8 (*pHistoryBuf)[UART_LOG_CMD_BUFLEN]; +#endif +#ifdef CONFIG_KERNEL + u32 TaskRdy; + _Sema Sema; +#else + // Since ROM code will reference this typedef, so keep the typedef same size + u32 TaskRdy; + void *Sema; +#endif +} UART_LOG_CTL, *PUART_LOG_CTL; + + +#define KB_ASCII_NUL 0x00 +#define KB_ASCII_BS 0x08 +#define KB_ASCII_TAB 0x09 +#define KB_ASCII_LF 0x0A +#define KB_ASCII_CR 0x0D +#define KB_ASCII_ESC 0x1B +#define KB_ASCII_SP 0x20 +#define KB_ASCII_BS_7F 0x7F +#define KB_ASCII_LBRKT 0x5B //[ + +#define KB_SPACENO_TAB 1 + +#ifdef CONFIG_UART_LOG_HISTORY +#define UART_LOG_HISTORY_LEN 5 +#endif + +#ifdef CONFIG_DEBUG_LOG +#define _ConsolePrint DiagPrintf +#else +#define _ConsolePrint +#endif + +#ifndef CONSOLE_PREFIX +#define CONSOLE_PREFIX "" +#endif + +#define CONSOLE_8195A(...) do {\ + _ConsolePrint("\r"CONSOLE_PREFIX __VA_ARGS__);\ +}while(0) + + +_LONG_CALL_ VOID +RtlConsolInit( + IN u32 Boot, + IN u32 TBLSz, + IN VOID *pTBL +); + +#if defined(CONFIG_KERNEL) +_LONG_CALL_ VOID +RtlConsolTaskRam( + VOID *Data +); +#endif + +_LONG_CALL_ VOID +RtlConsolTaskRom( + VOID *Data +); + + +_LONG_CALL_ u32 +Strtoul( + IN const u8 *nptr, + IN u8 **endptr, + IN u32 base +); + +void console_init(void); + +#endif //_RTK_CONSOL_H_ diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/8195a.ddf b/component/soc/realtek/8195a/misc/iar_utility/common/8195a.ddf new file mode 100644 index 0000000..c00b2b0 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/8195a.ddf @@ -0,0 +1,23 @@ +;; Memory information ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Used to define address zones within the ARM address space (Memory). +;; +;; Name may be almost anything +;; AdrSpace must be Memory +;; StartAdr start of memory block +;; EndAdr end of memory block +;; AccType type of access, read-only (R), read-write (RW) or SFR (W) + +[Memory] +;; Name AdrSpace StartAdr EndAdr AccType Width +Memory = ROM Memory 0x00000000 0x003FFFFF RW +Memory = SRAM Memory 0x10000000 0x1FFFFFFF RW +Memory = DRAM Memory 0x30000000 0x30FFFFFF RW +Memory = SFR Memory 0x40000000 0x41FFFFFF RW +Memory = SFR_Bitband Memory 0x42000000 0x43FFFFFF RW +Memory = PPB Memory 0xE0000000 0xFFFFFFFF RW + +TrustedRanges = true +UseSfrFilter = true + +[SfrInclude] diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/dram/EM6A6165TS_7G.mac b/component/soc/realtek/8195a/misc/iar_utility/common/dram/EM6A6165TS_7G.mac new file mode 100644 index 0000000..64aea1e --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/dram/EM6A6165TS_7G.mac @@ -0,0 +1,41 @@ +__load_dram_param(){ + //DRAM_INFO + DeviceType = 8; //DRAM_SDR + Page = 0; //DRAM_COLADDR_8B + Bank=0; //DRAM_BANK_2 + DqWidth=0; //DRAM_DQ_1 + + //DRAM_MODE_REG_INFO + BstLen=0; //BST_LEN_4 + BstType=0; //SENQUENTIAL + Mode0Cas=3; + Mode0Wr=0; + Mode1DllEnN=0; + Mode1AllLat=0; + Mode2Cwl=0; + + //DRAM_TIMING_INFO + DramTimingTref = 64000; + DramRowNum = 8192; + //SDR 100MHZ==>10000, 50MHZ==>20000, 25MHZ==>40000, 12.5MHZ==>80000 + Tck = 80000; //SDR 12.5MHZ + + TrfcPs=60000; + TrefiPs=((DramTimingTref*1000)/DramRowNum)*1000; + WrMaxTck=2; + TrcdPs=15000; + TrpPs=15000; + TrasPs=42000; + TrrdTck=2; + TwrPs=Tck*2; + TwtrTck=0; + TmrdTck=2; + TrtpTck=0; + TccdTck=1; + TrcPs=60000; + + //DRAM_DEVICE_INFO + DdrPeriodPs=Tck; + DfiRate=0; //DFI_RATIO_1 + +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/dram/common.mac b/component/soc/realtek/8195a/misc/iar_utility/common/dram/common.mac new file mode 100644 index 0000000..d79771c --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/dram/common.mac @@ -0,0 +1,4 @@ + +__load_dram_common(){ + __registerMacroFile("$PROJ_DIR$\\..\\..\\..\\component\\soc\\realtek\\8195a\\misc\\iar_utility\\common\\dram\\EM6A6165TS_7G.mac"); +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/dram/readme.txt b/component/soc/realtek/8195a/misc/iar_utility/common/dram/readme.txt new file mode 100644 index 0000000..2fa0238 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/dram/readme.txt @@ -0,0 +1,4 @@ +To Change DRAM setting + +1. Create and Fill content like EM6A6165TS_7G.mac +2. Change load file in common.mac \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.board b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.board new file mode 100644 index 0000000..a2c0b37 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.board @@ -0,0 +1,24 @@ + + + + + CODE 0x10000bc0 0x10003FFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x00000000 + --head + + + CODE 0x10004000 0x1006FFFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x00000000 + --cascade + + + CODE 0x30000000 0x301FFFFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x00000000 + --cascade + + CODE 0x00000000 0x000FFFFF + CODE 0x10000000 0x10000bbf + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.flash b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.flash new file mode 100644 index 0000000..9660e0d --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.flash @@ -0,0 +1,10 @@ + + + + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.out + 0x00000000 + 8 + 256 0x1000 + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.mac + 1 + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.mac b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.mac new file mode 100644 index 0000000..1d4dfc9 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.mac @@ -0,0 +1,69 @@ +setup() +{ + __var tmp; + + __hwResetWithStrategy(0, 2); + __hwReset(1); + + tmp = __readMemory32(0x40000014,"Memory"); __delay(10); + __message "0x40000014=",tmp:%x; + + __writeMemory32(0x21, 0x40000014, "Memory"); __delay(10); + + __writeMemory32(0x1FC00002, 0x40000304, "Memory"); __delay(10); + __writeMemory32(0x400, 0x40000250, "Memory"); __delay(10); + __writeMemory32(0x0, 0x40000340, "Memory"); __delay(10); + __writeMemory32(0xc04, 0x40000230, "Memory"); __delay(10); + __writeMemory32(0x1157, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x110011, 0x400002c0, "Memory"); __delay(10); + __writeMemory32(0xffffffff, 0x40000320, "Memory"); __delay(10); +/* + __writeMemory32(0x1, 0x40005224, "Memory"); __delay(10); + __writeMemory32(0x2c8, 0x40005004, "Memory"); __delay(10); + __writeMemory32(0xffffd000, 0x40005008, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x22, 0x40005020, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x09032001, 0x40005010, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x2611, 0x40005014, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x68413, 0x40005018, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x42, 0x4000501c, "Memory"); __delay(10); + __delay(3); + + // Enable + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + __delay(20); + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(10); + __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); + __delay(30); +*/ +} + +execUserPreload() +{ + __var tmp; + setup(); + tmp = __readMemory32(0x40000210, "Memory")|(1<<27); + __writeMemory32(tmp, 0x40000210, "Memory"); +} + +execUserSetup() +{ + //execUserPreload(); + //__loadImage("$TARGET_PATH$ ", 0, 0); + //__writeMemory32(0x80000000, 0x40000218, "Memory"); +} + +execUserFlashInit() // Called by debugger before loading flash loader in RAM. +{ + __var tmp; + __message "----- Prepare hardware for Flashloader -----\n"; + setup(); + tmp = __readMemory32(0x40000210, "Memory")|(1<<27); + __writeMemory32(tmp, 0x40000210, "Memory"); +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.out b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP.out new file mode 100644 index 0000000000000000000000000000000000000000..ff364369b95a38a680e6b5ded0a787fe0f082ec1 GIT binary patch literal 107376 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 literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img1.board b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img1.board new file mode 100644 index 0000000..7e5c606 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img1.board @@ -0,0 +1,17 @@ + + + + + CODE 0x10000bc0 0x10003FFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x00000000 + --head + --img2_addr + 0xB000 + + + CODE 0x00000000 0x000FFFFF + CODE 0x10000000 0x10000bbf + CODE 0x10004000 0x1006FFFF + CODE 0x30000000 0x301FFFFF + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img2.board b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img2.board new file mode 100644 index 0000000..80cff48 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aMP_img2.board @@ -0,0 +1,26 @@ + + + + + CODE 0x10000bc8 0x10003FFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x00000000 + --head + --img2_addr + 0xB000 + + + CODE 0x10004000 0x1006FFFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0xB000 + + + CODE 0x30000000 0x301FFFFF + $PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash + 0x0000 + --cascade + + CODE 0x00000000 0x000FFFFF + CODE 0x10000000 0x10000bc7 + CODE 0x1FFF0000 0x1FFFFFFF + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.board b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.board new file mode 100644 index 0000000..2562be5 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.board @@ -0,0 +1,30 @@ + + + + + CODE 0x200006b4 0x2002FFFF + $PROJ_DIR$\flashloader\FlashRTL8195aQA.flash + 0x00000000 + + + CODE 0x30000000 0x301FFFFF + $PROJ_DIR$\flashloader\FlashRTL8195aQA.flash + 0x00010000 + DATA_Z 0x30000000 0x301FFFFF + + + CODE 0x20080000 0x200BFFFF + $PROJ_DIR$\flashloader\FlashRTL8195aQA.flash + 0x00020000 + DATA_Z 0x20080000 0x200BFFFF + + + CODE 0x00000000 0x00000000 + $PROJ_DIR$\flashloader\FlashRTL8195aQA.flash + 0x00030000 + + CODE 0x00000001 0x000BFFFF + CODE 0x20000000 0x200006b3 + + + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.flash b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.flash new file mode 100644 index 0000000..8613f80 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.flash @@ -0,0 +1,10 @@ + + + + $PROJ_DIR$\flashloader\FlashRTL8195aQA.out + 0x00000000 + 8 + 256 0x1000 + $PROJ_DIR$\flashloader\FlashRTL8195aQA.mac + 1 + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.mac b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.mac new file mode 100644 index 0000000..4d670e0 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.mac @@ -0,0 +1,60 @@ +setup() +{ + __var tmp; + + __hwReset(1); + + __writeMemory32(0x21, 0x40000014, "Memory"); __delay(10); + __writeMemory32(0x1FC00002, 0x40000304, "Memory"); __delay(10); + __writeMemory32(0x400, 0x40000250, "Memory"); __delay(10); + __writeMemory32(0x0, 0x40000340, "Memory"); __delay(10); + __writeMemory32(0xc04, 0x40000230, "Memory"); __delay(10); + __writeMemory32(0x1157, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x110011, 0x400002c0, "Memory"); __delay(10); + __writeMemory32(0xffffffff, 0x40000320, "Memory"); __delay(10); + + __writeMemory32(0x1, 0x40005224, "Memory"); __delay(10); + __writeMemory32(0x2c8, 0x40005004, "Memory"); __delay(10); + __writeMemory32(0xffffd000, 0x40005008, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x22, 0x40005020, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x09032001, 0x40005010, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x2611, 0x40005014, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x68413, 0x40005018, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x42, 0x4000501c, "Memory"); __delay(10); + __delay(3); + + // Enable + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + __delay(20); + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(10); + __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); + __delay(30); +} + +execUserPreload() +{ + __message "----- Prepare hardware for Flashloader -----\n"; + setup(); + __writeMemory32(0x80000000, 0x40000218, "Memory"); +} + +execUserSetup() +{ + //execUserPreload(); + //__loadImage("$TARGET_PATH$ ", 0, 0); + //__writeMemory32(0x80000000, 0x40000218, "Memory"); +} + +execUserFlashInit() // Called by debugger before loading flash loader in RAM. +{ + __message "----- Prepare hardware for Flashloader -----\n"; + setup(); + __writeMemory32(0x80000000, 0x40000218, "Memory"); +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.out b/component/soc/realtek/8195a/misc/iar_utility/common/flashloader/FlashRTL8195aQA.out new file mode 100644 index 0000000000000000000000000000000000000000..c89a9590d31352624a9c6b857b6ee0986d4b4877 GIT binary patch literal 151784 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 b_^e;AAl3QN-9N&Z@!ku5p6fUfU!?y({5g0I literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/gen_board.bat b/component/soc/realtek/8195a/misc/iar_utility/common/gen_board.bat new file mode 100644 index 0000000..c96cffe --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/gen_board.bat @@ -0,0 +1,53 @@ +@set /a tmp = %1-1 +@call :toHex %tmp% end1 +@set /a tmp2 = %2-1 +@call :toHex %tmp2% end2 +@set /a tmp3 = %3-1 +@call :toHex %tmp3% end0 + +@echo echo image 2 start %1 +@echo echo image 1 end 0x%end1% +@echo off +@echo ^ > tmp.board +@echo. >> tmp.board +@echo ^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE %3 0x%end1%^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0x00000000^ >> tmp.board +@echo ^--head^ >> tmp.board +@echo ^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE %1 0x%end2%^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0x00000000^ >> tmp.board +@echo ^--cascade^ >> tmp.board +@echo ^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE 0x30000000 0x301FFFFF^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0x00000000^ >> tmp.board +@echo ^--cascade^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE 0x00000000 0x000FFFFF^ >> tmp.board +@echo ^CODE 0x10000000 0x%end0%^ >> tmp.board +@echo ^CODE %2 0x1006FFFF^ >> tmp.board +@echo ^ >> tmp.board >> tmp.board + +exit + +:toHex dec hex -- convert a decimal number to hexadecimal, i.e. -20 to FFFFFFEC or 26 to 0000001A +@echo off +SETLOCAL ENABLEDELAYEDEXPANSION +set /a dec=%~1 +set "hex=" +set "map=0123456789ABCDEF" +for /L %%N in (1,1,8) do ( + set /a "d=dec&15,dec>>=4" + for %%D in (!d!) do set "hex=!map:~%%D,1!!hex!" +) + +( ENDLOCAL & REM RETURN VALUES + IF "%~2" NEQ "" (SET %~2=%hex%) ELSE ECHO.%hex% +) +EXIT /b \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/gen_board_img2.bat b/component/soc/realtek/8195a/misc/iar_utility/common/gen_board_img2.bat new file mode 100644 index 0000000..d02407c --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/gen_board_img2.bat @@ -0,0 +1,57 @@ +@set /a tmp = %1-1 +@call :toHex %tmp% end1 +@set /a tmp2 = %2-1 +@call :toHex %tmp2% end2 +@set /a tmp3 = %3-1 +@call :toHex %tmp3% end3 + +@echo echo image 2 start %1 +@echo echo image 1 end 0x%end1% +@echo off +@echo ^ > tmp.board +@echo. >> tmp.board +@echo ^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE 0x10000bc8 0x10003FFF^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0x00000000^ >> tmp.board +@echo ^--head >> tmp.board +@echo --img2_addr >> tmp.board +@echo 0xB000^ >> tmp.board +@echo ^ >> tmp.board +@echo ^ >> tmp.board +@echo ^CODE 0x10004000 0x%end2%^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0xB000^ >> tmp.board +@echo ^ >> tmp.board +if NOT "%3"=="" ( +@echo ^ >> tmp.board +@echo ^CODE 0x30000000 0x%end3%^ >> tmp.board +@echo ^$PROJ_DIR$\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\flashloader\FlashRTL8195aMP.flash^ >> tmp.board +@echo ^0x0000^ >> tmp.board +@echo ^--cascade^ >> tmp.board +@echo ^ >> tmp.board +) +@echo ^CODE 0x00000000 0x000FFFFF^ >> tmp.board +@echo ^CODE 0x10000000 0x10000bc7^ >> tmp.board +@echo ^CODE %2 0x1006FFFF^ >> tmp.board +@echo ^CODE 0x1FFF0000 0x1FFFFFFF^ >> tmp.board +@echo ^ >> tmp.board >> tmp.board + +exit + +:toHex dec hex -- convert a decimal number to hexadecimal, i.e. -20 to FFFFFFEC or 26 to 0000001A +@echo off +SETLOCAL ENABLEDELAYEDEXPANSION +set /a dec=%~1 +set "hex=" +set "map=0123456789ABCDEF" +for /L %%N in (1,1,8) do ( + set /a "d=dec&15,dec>>=4" + for %%D in (!d!) do set "hex=!map:~%%D,1!!hex!" +) + +( ENDLOCAL & REM RETURN VALUES + IF "%~2" NEQ "" (SET %~2=%hex%) ELSE ECHO.%hex% +) +EXIT /b \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.bat b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.bat new file mode 100644 index 0000000..985ccd8 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.bat @@ -0,0 +1,47 @@ +cd /D %2 +set tooldir=%2\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\tools + +del Debug/Exe/target.map Debug/Exe/target.asm *.bin +cmd /c "%tooldir%\nm Debug/Exe/target.axf | %tooldir%\sort > Debug/Exe/target.map" +cmd /c "%tooldir%\objdump -d Debug/Exe/target.axf > Debug/Exe/target.asm" + +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE1 Debug/Exe/target.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram1_start=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE2 Debug/Exe/target.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram2_start=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep SDRAM Debug/Exe/target.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram3_start=0x%%i +::for /f "delims=" %%i in ('cmd /c "%tooldir%\grep .ram_image4 Debug/Exe/target.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram4_start=0x%%i + +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE1 Debug/Exe/target.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram1_end=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE2 Debug/Exe/target.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram2_end=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep SDRAM Debug/Exe/target.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram3_end=0x%%i +::for /f "delims=" %%i in ('cmd /c "%tooldir%\grep .ram_image4 Debug/Exe/target.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram4_end=0x%%i + +::echo %ram1_start% > tmp.txt +::echo %ram2_start% >> tmp.txt +::echo %ram3_start% >> tmp.txt +::echo %ram1_end% >> tmp.txt +::echo %ram2_end% >> tmp.txt +::echo %ram3_end% >> tmp.txt + +%tooldir%\objcopy -j "A2 rw" -Obinary Debug/Exe/target.axf Debug/Exe/ram_1.bin +%tooldir%\objcopy -j "A3 rw" -Obinary Debug/Exe/target.axf Debug/Exe/sdram.bin + +%tooldir%\pick %ram1_start% %ram1_end% Debug\Exe\ram_1.bin Debug\Exe\ram_1.p.bin head +%tooldir%\pick %ram2_start% %ram2_end% Debug\Exe\ram_1.bin Debug\Exe\ram_2.p.bin body +if defined %ram3_start ( +%tooldir%\pick %ram3_start% %ram3_end% Debug\Exe\sdram.bin Debug\Exe\ram_3.p.bin body +) + +:: SDRAM case +if defined %ram3_start ( +copy /b Debug\Exe\ram_1.p.bin+Debug\Exe\ram_2.p.bin+Debug\Exe\ram_3.p.bin Debug\Exe\ram_all.bin +) + +:: NO SDRAM case +if not defined %ram3_start ( +copy /b Debug\Exe\ram_1.p.bin+Debug\Exe\ram_2.p.bin Debug\Exe\ram_all.bin +) + +:: board generator +%tooldir%\..\gen_board.bat %ram2_start% %ram2_end% %ram1_start% + +exit \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.vbs b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.vbs new file mode 100644 index 0000000..af44493 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild.vbs @@ -0,0 +1,5 @@ +Dim WshShell + +Set WshShell = WScript.CreateObject("WScript.Shell") + +WshShell.Run "cmd /c "+WScript.Arguments.Item(1)+"\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\postbuild.bat "+WScript.Arguments.Item(0)+" "+WScript.Arguments.Item(1), 0 \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.bat b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.bat new file mode 100644 index 0000000..87573d4 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.bat @@ -0,0 +1,30 @@ +cd /D %2 +set tooldir=%2\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\tools +set libdir=%2\..\..\..\component\soc\realtek\8195a\misc\bsp + +del Debug/Exe/bootloader.map Debug/Exe/bootloader.asm *.bin +cmd /c "%tooldir%\nm Debug/Exe/bootloader.axf | %tooldir%\sort > Debug/Exe/bootloader.map" +cmd /c "%tooldir%\objdump -d Debug/Exe/bootloader.axf > Debug/Exe/bootloader.asm" + +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE1 Debug/Exe/bootloader.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram1_start=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE1 Debug/Exe/bootloader.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram1_end=0x%%i + + +::echo %ram1_start% > tmp.txt +::echo %ram2_start% >> tmp.txt +::echo %ram3_start% >> tmp.txt +::echo %ram1_end% >> tmp.txt +::echo %ram2_end% >> tmp.txt +::echo %ram3_end% >> tmp.txt + +%tooldir%\objcopy -j "A2 rw" -Obinary Debug/Exe/bootloader.axf Debug/Exe/ram_1.bin +::%tooldir%\objcopy -j "A3 rw" -Obinary Debug/Exe/bootloader.axf Debug/Exe/sdram.bin + +%tooldir%\pick %ram1_start% %ram1_end% Debug\Exe\ram_1.bin Debug\Exe\ram_1.p.bin head 0xb000 +%tooldir%\pick %ram1_start% %ram1_end% Debug\Exe\ram_1.bin Debug\Exe\ram_1.r.bin raw + +:: update ram_1.p.bin, raw file for application +copy Debug\Exe\ram_1.p.bin %libdir%\image\ram_1.p.bin +copy Debug\Exe\ram_1.r.bin %libdir%\image\ram_1.r.bin + +exit \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.vbs b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.vbs new file mode 100644 index 0000000..cecbede --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img1.vbs @@ -0,0 +1,5 @@ +Dim WshShell + +Set WshShell = WScript.CreateObject("WScript.Shell") + +WshShell.Run "cmd /c "+WScript.Arguments.Item(1)+"\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\postbuild_img1.bat "+WScript.Arguments.Item(0)+" "+WScript.Arguments.Item(1), 0 \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.bat b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.bat new file mode 100644 index 0000000..da1523c --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.bat @@ -0,0 +1,59 @@ +cd /D %2 +set tooldir=%2\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\tools +set libdir=%2\..\..\..\component\soc\realtek\8195a\misc\bsp + +del Debug/Exe/target.map Debug/Exe/application.asm *.bin +cmd /c "%tooldir%\nm Debug/Exe/application.axf | %tooldir%\sort > Debug/Exe/application.map" +cmd /c "%tooldir%\objdump -d Debug/Exe/application.axf > Debug/Exe/application.asm" + +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE2 Debug/Exe/application.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram2_start=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep SDRAM Debug/Exe/application.map | %tooldir%\grep Base | %tooldir%\gawk '{print $1}'"') do set ram3_start=0x%%i + +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep IMAGE2 Debug/Exe/application.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram2_end=0x%%i +for /f "delims=" %%i in ('cmd /c "%tooldir%\grep SDRAM Debug/Exe/application.map | %tooldir%\grep Limit | %tooldir%\gawk '{print $1}'"') do set ram3_end=0x%%i + +::echo %ram1_start% > tmp.txt +::echo %ram2_start% >> tmp.txt +::echo %ram3_start% >> tmp.txt +::echo %ram1_end% >> tmp.txt +::echo %ram2_end% >> tmp.txt +::echo %ram3_end% >> tmp.txt + +%tooldir%\objcopy -j "A3 rw" -Obinary Debug/Exe/application.axf Debug/Exe/ram_2.bin +if defined %ram3_start ( + %tooldir%\objcopy -j "A5 rw" -Obinary Debug/Exe/application.axf Debug/Exe/sdram.bin +) + +%tooldir%\pick %ram2_start% %ram2_end% Debug\Exe\ram_2.bin Debug\Exe\ram_2.p.bin body+reset_offset+sig +if defined %ram3_start ( +%tooldir%\pick %ram3_start% %ram3_end% Debug\Exe\sdram.bin Debug\Exe\ram_3.p.bin body+reset_offset +) + +:: check ram_1.p.bin exist, copy default +if not exist Debug\Exe\ram_1.p.bin ( + copy %libdir%\image\ram_1.p.bin Debug\Exe\ram_1.p.bin +) + +::if not exist Debug\Exe\data.p.bin ( +:: copy %tooldir%\..\image\data.p.bin Debug\Exe\data.p.bin +::) + +::padding ram_1.p.bin to 32K+4K+4K+4K, LOADER/RSVD/SYSTEM/CALIBRATION +%tooldir%\padding 44k 0xFF Debug\Exe\ram_1.p.bin + +:: SDRAM case +if defined %ram3_start ( +copy /b Debug\Exe\ram_1.p.bin+Debug\Exe\ram_2.p.bin+Debug\Exe\ram_3.p.bin Debug\Exe\ram_all.bin +copy /b Debug\Exe\ram_2.p.bin+Debug\Exe\ram_3.p.bin Debug\Exe\ota.bin +) + +:: NO SDRAM case +if not defined %ram3_start ( +copy /b Debug\Exe\ram_1.p.bin+Debug\Exe\ram_2.p.bin Debug\Exe\ram_all.bin +copy /b Debug\Exe\ram_2.p.bin Debug\Exe\ota.bin +) + +:: board generator +%tooldir%\..\gen_board_img2.bat %ram2_start% %ram2_end% %ram3_end% + +exit \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.vbs b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.vbs new file mode 100644 index 0000000..4144da9 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/postbuild_img2.vbs @@ -0,0 +1,5 @@ +Dim WshShell + +Set WshShell = WScript.CreateObject("WScript.Shell") + +WshShell.Run "cmd /c "+WScript.Arguments.Item(1)+"\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\postbuild_img2.bat "+WScript.Arguments.Item(0)+" "+WScript.Arguments.Item(1), 0 \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.bat b/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.bat new file mode 100644 index 0000000..fc09b0a --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.bat @@ -0,0 +1,17 @@ +cd /D %1 +:: Generate build_info.h +echo off +::echo %date:~0,10%-%time:~0,8% +::echo %USERNAME% +for /f "usebackq" %%i in (`hostname`) do set hostname=%%i +::echo %hostname% + +echo #define UTS_VERSION "%date:~0,10%-%time:~0,8%" > ..\inc\build_info.h +echo #define RTL8195AFW_COMPILE_TIME "%date:~0,10%-%time:~0,8%" >> ..\inc\build_info.h +echo #define RTL8195AFW_COMPILE_BY "%USERNAME%" >> ..\inc\build_info.h +echo #define RTL8195AFW_COMPILE_HOST "%hostname%" >> ..\inc\build_info.h +echo #define RTL8195AFW_COMPILE_DOMAIN >> ..\inc\build_info.h +echo #define RTL195AFW_COMPILER "IAR compiler" >> ..\inc\build_info.h + +exit + diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.vbs b/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.vbs new file mode 100644 index 0000000..3873489 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/prebuild.vbs @@ -0,0 +1,5 @@ +Dim WshShell + +Set WshShell = WScript.CreateObject("WScript.Shell") + +WshShell.Run "cmd /c "+WScript.Arguments.Item(0)+"\..\..\..\component\soc\realtek\8195a\misc\iar_utility\common\prebuild.bat "+WScript.Arguments.Item(0), 0 \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/preload.dap.mac b/component/soc/realtek/8195a/misc/iar_utility/common/preload.dap.mac new file mode 100644 index 0000000..613b841 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/preload.dap.mac @@ -0,0 +1,372 @@ +//DRAM_INFO +__var DeviceType; +__var Page; +__var Bank; +__var DqWidth; + +//DRAM_MODE_REG_INFO +__var BstLen; +__var BstType; +__var Mode0Cas; +__var Mode0Wr; +__var Mode1DllEnN; +__var Mode1AllLat; +__var Mode2Cwl; + +//DRAM_TIMING_INFO, additional parameter, to config DRAM_TIMING INFO +__var DramTimingTref; +__var DramRowNum; +__var Tck; + +//DRAM_TIMING_INFO +__var TrfcPs; +__var TrefiPs; +__var WrMaxTck; +__var TrcdPs; +__var TrpPs; +__var TrasPs; +__var TrrdTck; +__var TwrPs; +__var TwtrTck; +__var TmrdTck; +__var TrtpTck; +__var TccdTck; +__var TrcPs; + +//DRAM_DEVICE_INFO +__var DdrPeriodPs; +__var DfiRate; + +__config_dram_param(){ + __var CsBstLen; + __var CasWr; + __var CasRd; + __var CasRdT; + __var ClrSrt; + __var AddLat; + __var DramEmr2; + __var DramMr0; + __var CrTwr; + __var DramMaxWr; + __var DramWr; + __var CrTrtw; + __var CrTrtwT; + __var DramPeriod; + __var DdrType; + //__var paDqWidth; + //__var paPage; + //__var paDfiRate + + __var tmp; + + // Register dram common.mac + //__registerMacroFile("$PROJ_DIR$\\..\\..\\..\\component\\soc\\realtek\\8195a\\misc\\iar_utility\\common\\dram\\common.mac"); + __load_dram_common(); + + // Load parameter + __load_dram_param(); + + DfiRate = 1<> 1); + if (((Mode0Cas) & 0x1) ) { + CasRdT = CrlSrt+ 12; + }else{ + CasRdT = CrlSrt+ 4; + } + + AddLat = 0; + if (Mode1AllLat == 1) { // CL-1 + AddLat = CasRd -1; + } + if (Mode1AllLat == 2){ // CL-2 + AddLat = CasRd -2; + } + CasRd = CasRdT + AddLat; + CasWr = Mode2Cwl + 5 + AddLat; + DramEmr2 = Mode2Cwl << 3; + + DramWr = (DramWr + 1) / 2; + if (DramWr == 16) { + DramWr = 0; + } + if (DramWr <= 9) { // 5< wr <= 9 + DramWr = DramWr - 4; + } + DramMr0 =(DramWr<<(8+1))|(0<<8)|((Mode0Cas>>1)<<4)|(BstType<<3)|((Mode0Cas&0x1)<<2)|DramMr0; + CrTrtwT = (CasRdT + 6) - CasWr; + } + if (DeviceType == 8){ + DdrType = 8; + if (BstLen == 0) { + DramMr0 = 2; // bst_4 + CsBstLen = 0; //bst_4 + CasRd = 0x2; + } else { + DramMr0 = 3; // bst_8 + CsBstLen = 1; // bst_8 + CasRd = 0x3; + } + CasWr = 0; + DramMr0 =(CasRd<<4)|(BstType<<3)|DramMr0; + CrTrtwT = 0; // tic: CasRd + rd_rtw + rd_pipe + } + + // countting tRTW + if ((CrTrtwT & 0x1)) { + CrTrtw = (CrTrtwT+1) /(DfiRate); + } else { + CrTrtw = CrTrtwT /(DfiRate); + } + + DqWidth = DqWidth; + Page = Page +1; // DQ16 -> memory:byte_unit *2 + if (DqWidth == 1) { // paralle dq_16 => Page + 1 + Page = Page +1; + } + + // REG_SDR_MISC + tmp =(Page<<0)|(Bank<<4)|(CsBstLen<<6)|(DqWidth<<8); + __writeMemory32(tmp, 0x40005224, "Memory"); __delay(10); + // REG_SDR_DCR + tmp =(0x2<<8)|(DqWidth<<4)|(DdrType<<0); + __writeMemory32(tmp, 0x40005004, "Memory"); __delay(10); + // REG_SDR_IOCR + tmp =((CasRd-4)/(DfiRate)<<20)|(0<<17)|(((CasWr-3)/(DfiRate))<<12)|(0<<8); + __writeMemory32(tmp, 0x40005008, "Memory"); __delay(10); + if(DeviceType != 8){ + tmp =DramEmr2; + __writeMemory32(tmp, 0x40005028, "Memory"); __delay(10); + tmp =(1<<2)|(1<<1)|(Mode1DllEnN); + __writeMemory32(tmp, 0x40005024, "Memory"); __delay(10); + } + tmp =DramMr0; + __writeMemory32(tmp, 0x40005020, "Memory"); __delay(10); + tmp =(0<<28)|(9<<24)|((((TrefiPs)/DramPeriod)+1)<<8)|((((TrfcPs)/DramPeriod)+1)<<0); + __writeMemory32(tmp, 0x40005010, "Memory"); __delay(10); + tmp =((((TrtpTck)/DfiRate)+1)<<13)|(CrTwr<<9)|((((TrasPs)/DramPeriod)+1)<<4)|((((TrpPs)/DramPeriod)+1)<<0); + __writeMemory32(tmp, 0x40005014, "Memory"); __delay(10); + tmp =(CrTrtw << 20) | + ((((TwtrTck)/DfiRate)+3) << 17) | + ((((TccdTck)/DfiRate)+1) << 14) | + ((((TrcdPs)/DramPeriod)+1) << 10) | + ((((TrcPs)/DramPeriod)+1) << 4 ) | + (((TrrdTck/DfiRate)+1) << 0); + __writeMemory32(tmp, 0x40005018, "Memory"); __delay(10); + tmp =(TmrdTck<<5)|(0<<4)|(2<<0); + __writeMemory32(tmp, 0x4000501c, "Memory"); __delay(10); + // Set Idle + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + // start init + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + // enter memory mode + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); +} + +__config_dram_param_fixed(){ + __var tmp; + // Dram Attribute + __writeMemory32(0x1, 0x40005224, "Memory"); __delay(10); + __writeMemory32(0x2c8, 0x40005004, "Memory"); __delay(10); + __writeMemory32(0xffffd000, 0x40005008, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x22, 0x40005020, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x09032001, 0x40005010, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x2611, 0x40005014, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x68413, 0x40005018, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x42, 0x4000501c, "Memory"); __delay(10); + __delay(3); + + // Enable + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + __delay(20); + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(10); + __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); + __delay(30); +} + +__mem_test(){ + __var i; + __var vaddr; + __var tmp; + + i=0; + while(i<10){ + vaddr = 0x30000000+((i*23)&0x1FFFFC); + __writeMemory32(0x55AA55AA, vaddr, "Memory"); + tmp = __readMemory32(vaddr,"Memory"); + if(tmp!=0x55AA55AA) + return 1; + i=i+1; + } + return 0; +} + +__var ok_pipe_id0; +__var ok_pipe_id1; +__var ok_tpc_min0; +__var ok_tpc_max0; +__var ok_tpc_min1; +__var ok_tpc_max1; +__var tpc0_cnt; +__var tpc1_cnt; + +// calibration result +__var isCalibrationDone; + +__dram_calibration(){ + __var rdp; + __var tpc; + __var rdp_reg; + __var tpc_reg; + __var err_cnt; + __var ok_cnt; + + + ok_cnt=0; + ok_pipe_id0 = 15; + ok_tpc_min0 = 12; + ok_tpc_max0 = 0; + + rdp_reg = __readMemory32(0x40005008,"Memory")&0xFFFF00FF; + tpc_reg = __readMemory32(0x40000300,"Memory")&0xFF00FFFF; + for(rdp=0;(rdp<=7)&&(err_cnt==0||ok_cnt==0);rdp++){ + err_cnt=0; + // try pipe + __writeMemory32(rdp_reg|rdp<<8,0x40005008, "Memory");__delay(10); + for(tpc=0;(tpc<=12)&&(err_cnt<2);tpc++){ + // try tpc + __writeMemory32(tpc_reg|tpc<<16,0x40000300, "Memory");__delay(10); + if(__mem_test()==0){ + if(ok_pipe_id0==15) {ok_pipe_id0 = rdp; ok_cnt++;} + if(ok_tpc_min0>tpc) ok_tpc_min0 = tpc; + if(ok_tpc_max0tpc0_cnt){ + __writeMemory32(rdp_reg|ok_pipe_id1<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc1_cnt/2)<<16,0x40000300, "Memory");__delay(10); + }else{ + __writeMemory32(rdp_reg|ok_pipe_id0<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc0_cnt/2)<<16,0x40000300, "Memory");__delay(10); + } +} + +__setup_system() +{ + __var tmp; + + __hwReset(1); + + __writeMemory32(0x21, 0x40000014, "Memory"); __delay(10); + __writeMemory32(0x1FC00002, 0x40000304, "Memory"); __delay(10); + __writeMemory32(0x400, 0x40000250, "Memory"); __delay(10); + __writeMemory32(0x0, 0x40000340, "Memory"); __delay(10); + __writeMemory32(0xdcc4, 0x40000230, "Memory"); __delay(10); + __writeMemory32(0x11117, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x11157, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x110011, 0x400002c0, "Memory"); __delay(10); + __writeMemory32(0xffffffff, 0x40000320, "Memory"); __delay(10); + + __config_dram_param(); + + if(isCalibrationDone){ + __var rdp_reg; + __var tpc_reg; + rdp_reg = __readMemory32(0x40005008,"Memory")&0xFFFF00FF; + tpc_reg = __readMemory32(0x40000300,"Memory")&0xFF00FFFF; + if(tpc1_cnt>tpc0_cnt){ + __writeMemory32(rdp_reg|ok_pipe_id1<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc1_cnt/2)<<16,0x40000300, "Memory");__delay(10); + }else{ + __writeMemory32(rdp_reg|ok_pipe_id0<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc0_cnt/2)<<16,0x40000300, "Memory");__delay(10); + } + }else{ + // Calibration + __dram_calibration(); + isCalibrationDone = 1; + } +} + +execUserPreload() +{ + // Register dram common.mac + __registerMacroFile("$PROJ_DIR$\\..\\..\\..\\component\\soc\\realtek\\8195a\\misc\\iar_utility\\common\\dram\\common.mac"); + + __message "User Preload...."; + //isCalibrationDone = 0; + __setup_system(); +} + +execUserSetup() +{ + __var tmp; + __message "User Setup...."; + // if use normal reset, please unmark those 2 lines + //execUserPreload(); + __setup_system(); + //__loadImage("$TARGET_PATH$", 0, 0); + // DISABLE DRAM init + tmp = __readMemory32(0x40000210, "Memory")|(1<<21); + __writeMemory32(tmp, 0x40000210, "Memory"); +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/preload.mac b/component/soc/realtek/8195a/misc/iar_utility/common/preload.mac new file mode 100644 index 0000000..96f4b85 --- /dev/null +++ b/component/soc/realtek/8195a/misc/iar_utility/common/preload.mac @@ -0,0 +1,382 @@ +//DRAM_INFO +__var DeviceType; +__var Page; +__var Bank; +__var DqWidth; + +//DRAM_MODE_REG_INFO +__var BstLen; +__var BstType; +__var Mode0Cas; +__var Mode0Wr; +__var Mode1DllEnN; +__var Mode1AllLat; +__var Mode2Cwl; + +//DRAM_TIMING_INFO, additional parameter, to config DRAM_TIMING INFO +__var DramTimingTref; +__var DramRowNum; +__var Tck; + +//DRAM_TIMING_INFO +__var TrfcPs; +__var TrefiPs; +__var WrMaxTck; +__var TrcdPs; +__var TrpPs; +__var TrasPs; +__var TrrdTck; +__var TwrPs; +__var TwtrTck; +__var TmrdTck; +__var TrtpTck; +__var TccdTck; +__var TrcPs; + +//DRAM_DEVICE_INFO +__var DdrPeriodPs; +__var DfiRate; + +__config_dram_param(){ + __var CsBstLen; + __var CasWr; + __var CasRd; + __var CasRdT; + __var ClrSrt; + __var AddLat; + __var DramEmr2; + __var DramMr0; + __var CrTwr; + __var DramMaxWr; + __var DramWr; + __var CrTrtw; + __var CrTrtwT; + __var DramPeriod; + __var DdrType; + //__var paDqWidth; + //__var paPage; + //__var paDfiRate + + __var tmp; + + // Register dram common.mac + //__registerMacroFile("$PROJ_DIR$\\..\\..\\..\\component\\soc\\realtek\\8195a\\misc\\iar_utility\\common\\dram\\common.mac"); + __load_dram_common(); + + // Load parameter + __load_dram_param(); + + DfiRate = 1<> 1); + if (((Mode0Cas) & 0x1) ) { + CasRdT = CrlSrt+ 12; + }else{ + CasRdT = CrlSrt+ 4; + } + + AddLat = 0; + if (Mode1AllLat == 1) { // CL-1 + AddLat = CasRd -1; + } + if (Mode1AllLat == 2){ // CL-2 + AddLat = CasRd -2; + } + CasRd = CasRdT + AddLat; + CasWr = Mode2Cwl + 5 + AddLat; + DramEmr2 = Mode2Cwl << 3; + + DramWr = (DramWr + 1) / 2; + if (DramWr == 16) { + DramWr = 0; + } + if (DramWr <= 9) { // 5< wr <= 9 + DramWr = DramWr - 4; + } + DramMr0 =(DramWr<<(8+1))|(0<<8)|((Mode0Cas>>1)<<4)|(BstType<<3)|((Mode0Cas&0x1)<<2)|DramMr0; + CrTrtwT = (CasRdT + 6) - CasWr; + } + if (DeviceType == 8){ + DdrType = 8; + if (BstLen == 0) { + DramMr0 = 2; // bst_4 + CsBstLen = 0; //bst_4 + CasRd = 0x2; + } else { + DramMr0 = 3; // bst_8 + CsBstLen = 1; // bst_8 + CasRd = 0x3; + } + CasWr = 0; + DramMr0 =(CasRd<<4)|(BstType<<3)|DramMr0; + CrTrtwT = 0; // tic: CasRd + rd_rtw + rd_pipe + } + + // countting tRTW + if ((CrTrtwT & 0x1)) { + CrTrtw = (CrTrtwT+1) /(DfiRate); + } else { + CrTrtw = CrTrtwT /(DfiRate); + } + + DqWidth = DqWidth; + Page = Page +1; // DQ16 -> memory:byte_unit *2 + if (DqWidth == 1) { // paralle dq_16 => Page + 1 + Page = Page +1; + } + + // REG_SDR_MISC + tmp =(Page<<0)|(Bank<<4)|(CsBstLen<<6)|(DqWidth<<8); + __writeMemory32(tmp, 0x40005224, "Memory"); __delay(10); + // REG_SDR_DCR + tmp =(0x2<<8)|(DqWidth<<4)|(DdrType<<0); + __writeMemory32(tmp, 0x40005004, "Memory"); __delay(10); + // REG_SDR_IOCR + tmp =((CasRd-4)/(DfiRate)<<20)|(0<<17)|(((CasWr-3)/(DfiRate))<<12)|(0<<8); + __writeMemory32(tmp, 0x40005008, "Memory"); __delay(10); + if(DeviceType != 8){ + tmp =DramEmr2; + __writeMemory32(tmp, 0x40005028, "Memory"); __delay(10); + tmp =(1<<2)|(1<<1)|(Mode1DllEnN); + __writeMemory32(tmp, 0x40005024, "Memory"); __delay(10); + } + tmp =DramMr0; + __writeMemory32(tmp, 0x40005020, "Memory"); __delay(10); + tmp =(0<<28)|(9<<24)|((((TrefiPs)/DramPeriod)+1)<<8)|((((TrfcPs)/DramPeriod)+1)<<0); + __writeMemory32(tmp, 0x40005010, "Memory"); __delay(10); + tmp =((((TrtpTck)/DfiRate)+1)<<13)|(CrTwr<<9)|((((TrasPs)/DramPeriod)+1)<<4)|((((TrpPs)/DramPeriod)+1)<<0); + __writeMemory32(tmp, 0x40005014, "Memory"); __delay(10); + tmp =(CrTrtw << 20) | + ((((TwtrTck)/DfiRate)+3) << 17) | + ((((TccdTck)/DfiRate)+1) << 14) | + ((((TrcdPs)/DramPeriod)+1) << 10) | + ((((TrcPs)/DramPeriod)+1) << 4 ) | + (((TrrdTck/DfiRate)+1) << 0); + __writeMemory32(tmp, 0x40005018, "Memory"); __delay(10); + tmp =(TmrdTck<<5)|(0<<4)|(2<<0); + __writeMemory32(tmp, 0x4000501c, "Memory"); __delay(10); + // Set Idle + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + // start init + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + // enter memory mode + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); +} + +__config_dram_param_fixed(){ + __var tmp; + // Dram Attribute + __writeMemory32(0x1, 0x40005224, "Memory"); __delay(10); + __writeMemory32(0x2c8, 0x40005004, "Memory"); __delay(10); + __writeMemory32(0xffffd000, 0x40005008, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x22, 0x40005020, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x09032001, 0x40005010, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x2611, 0x40005014, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x68413, 0x40005018, "Memory"); __delay(10); + __delay(3); + __writeMemory32(0x42, 0x4000501c, "Memory"); __delay(10); + __delay(3); + + // Enable + __writeMemory32(0x700, 0x4000500c, "Memory"); __delay(10); + __delay(20); + __writeMemory32(0x1, 0x40005000, "Memory"); __delay(10); + __delay(100); + tmp = __readMemory32(0x40005000,"Memory"); __delay(10); + __writeMemory32(0x600, 0x4000500c, "Memory"); __delay(10); + __delay(30); +} + +__mem_test(){ + __var i; + __var vaddr; + __var tmp; + + i=0; + while(i<10){ + vaddr = 0x30000000+((i*23)&0x1FFFFC); + __writeMemory32(0x55AA55AA, vaddr, "Memory"); + tmp = __readMemory32(vaddr,"Memory"); + if(tmp!=0x55AA55AA) + return 1; + i=i+1; + } + return 0; +} + +__var ok_pipe_id0; +__var ok_pipe_id1; +__var ok_tpc_min0; +__var ok_tpc_max0; +__var ok_tpc_min1; +__var ok_tpc_max1; +__var tpc0_cnt; +__var tpc1_cnt; + +// calibration result +__var isCalibrationDone; + +__dram_calibration(){ + __var rdp; + __var tpc; + __var rdp_reg; + __var tpc_reg; + __var err_cnt; + __var ok_cnt; + + + ok_cnt=0; + ok_pipe_id0 = 15; + ok_tpc_min0 = 12; + ok_tpc_max0 = 0; + + rdp_reg = __readMemory32(0x40005008,"Memory")&0xFFFF00FF; + tpc_reg = __readMemory32(0x40000300,"Memory")&0xFF00FFFF; + for(rdp=0;(rdp<=7)&&(err_cnt==0||ok_cnt==0);rdp++){ + err_cnt=0; + // try pipe + __writeMemory32(rdp_reg|rdp<<8,0x40005008, "Memory");__delay(10); + for(tpc=0;(tpc<=12)&&(err_cnt<2);tpc++){ + // try tpc + __writeMemory32(tpc_reg|tpc<<16,0x40000300, "Memory");__delay(10); + if(__mem_test()==0){ + if(ok_pipe_id0==15) {ok_pipe_id0 = rdp; ok_cnt++;} + if(ok_tpc_min0>tpc) ok_tpc_min0 = tpc; + if(ok_tpc_max0tpc0_cnt){ + __writeMemory32(rdp_reg|ok_pipe_id1<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc1_cnt/2)<<16,0x40000300, "Memory");__delay(10); + }else{ + __writeMemory32(rdp_reg|ok_pipe_id0<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc0_cnt/2)<<16,0x40000300, "Memory");__delay(10); + } +} + +__setup_system() +{ + __var tmp; + + __hwReset(1); + + __writeMemory32(0x21, 0x40000014, "Memory"); __delay(10); + __writeMemory32(0x1FC00002, 0x40000304, "Memory"); __delay(10); + __writeMemory32(0x400, 0x40000250, "Memory"); __delay(10); + __writeMemory32(0x0, 0x40000340, "Memory"); __delay(10); + __writeMemory32(0xdcc4, 0x40000230, "Memory"); __delay(10); + __writeMemory32(0x11117, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x11157, 0x40000210, "Memory"); __delay(10); + __writeMemory32(0x110011, 0x400002c0, "Memory"); __delay(10); + __writeMemory32(0xffffffff, 0x40000320, "Memory"); __delay(10); + + __config_dram_param(); + + if(isCalibrationDone){ + __var rdp_reg; + __var tpc_reg; + rdp_reg = __readMemory32(0x40005008,"Memory")&0xFFFF00FF; + tpc_reg = __readMemory32(0x40000300,"Memory")&0xFF00FFFF; + if(tpc1_cnt>tpc0_cnt){ + __writeMemory32(rdp_reg|ok_pipe_id1<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc1_cnt/2)<<16,0x40000300, "Memory");__delay(10); + }else{ + __writeMemory32(rdp_reg|ok_pipe_id0<<8,0x40005008, "Memory");__delay(10); + __writeMemory32(tpc_reg|(tpc0_cnt/2)<<16,0x40000300, "Memory");__delay(10); + } + }else{ + // Calibration + __dram_calibration(); + isCalibrationDone = 1; + } +} + +execUserPreload() +{ + // Register dram common.mac + __registerMacroFile("$PROJ_DIR$\\..\\..\\..\\component\\soc\\realtek\\8195a\\misc\\iar_utility\\common\\dram\\common.mac"); + + __message "User Preload...."; + if(__driverType("jlink")){ + __message "driver type J-LINK"; + isCalibrationDone = 0; + }else if(__driverType("cmsisdap")){ + __message "driver type CMSIS-DAP"; + } + __setup_system(); +} + +execUserSetup() +{ + __var tmp; + __message "User Setup...."; + // if use normal reset, please unmark those 2 lines + //execUserPreload(); + __setup_system(); + if(__driverType("jlink")){ + __loadImage("$TARGET_PATH$ ", 0, 0); + tmp = __readMemory32(0x40000210, "Memory")|(1<<27); + }else if(__driverType("cmsisdap")){ + tmp = __readMemory32(0x40000210, "Memory")|(1<<21); + }else{ + __message "Not support drive type"; + } + __writeMemory32(tmp, 0x40000210, "Memory"); +} \ No newline at end of file diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggcc_s-1.dll b/component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggcc_s-1.dll new file mode 100644 index 0000000000000000000000000000000000000000..1c5e62ca931785b324504ff6a35586cbd4787db1 GIT binary patch literal 103453 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 fDOc;vQI literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggmp-10.dll b/component/soc/realtek/8195a/misc/iar_utility/common/tools/cyggmp-10.dll new file mode 100644 index 0000000000000000000000000000000000000000..ef02f64ce11a7ce00b1630f2b3e82dc36db268e5 GIT binary patch literal 506909 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�%5`TQCbfgX4hBo?Rt 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�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

        9VojLgHy?ynX{QOFygP5&Q2p*Of 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 z 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 <(H%yHlM#3aZ4?m1Hcr3g^aNO~#XJu*c$B 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�TL;(V+0?imqCrm$yBw88})V%R=4Wy9wJ4t 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�E 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

        kIGM{r>qrf6sn6{<279`swWcxV-eq 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 BCkOxl literal 0 HcmV?d00001 diff --git a/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wps.a b/component/soc/realtek/8195a/misc/bsp/lib/va0/lib_wps.a new file mode 100644 index 0000000000000000000000000000000000000000..69f1de386c0954e9a9df2787dbd7e73a004d9efd GIT binary patch literal 966426 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 z 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΅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

    2. 0$m>}fa~%CxIRYLC+ND1uFul-CAz*s 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�s!Tc1o{Z^3&Dp% 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%0A8M6Yn^UnR_My-}t{#I>Rya&s=6^fNB~uZc53 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

    #(dNPU!w7PEpH)-a*i)Y$JaWH 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 !r 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≦WP3z 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¨^^b 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